mirror of
https://github.com/2dust/v2rayN.git
synced 2026-07-26 09:52:05 +03:00
ViewModel-First (#9621)
* Interaction * Remove view action * ViewModel-first * MainGirdOrientation Hot Reload * Fix avalonia preview * Remove CloseWindowInteraction * Fix * Avoid threading issues * Fix * ProfilesSelect * Try fix previewer * Remove AppEvents.ProfilesRefreshRequested * Remove AppEvents.SubscriptionsRefreshRequested * Remove AppEvents.ProxiesReloadRequested * Remove AppEvents.AdjustMainLvColWidthRequested * Remove AppEvents.SetDefaultServerRequested * Remove AppEvents.RoutingsMenuRefreshRequested * Remove AppEvents.TestServerRequested * Remove AppEvents.InboundDisplayRequested * Remove AppEvents.SubscriptionsUpdateRequested * Remove AppEvents.ShowHideWindowRequested * Remove AppEvents.ReloadRequested * Remove AppEvents.AddServerRequested * Fix
This commit is contained in:
6
v2rayN/ServiceLib/Base/ICloseable.cs
Normal file
6
v2rayN/ServiceLib/Base/ICloseable.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ServiceLib.Base;
|
||||||
|
|
||||||
|
public interface ICloseable
|
||||||
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
}
|
||||||
7
v2rayN/ServiceLib/Base/IWindowDialog.cs
Normal file
7
v2rayN/ServiceLib/Base/IWindowDialog.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace ServiceLib.Base;
|
||||||
|
|
||||||
|
public interface IWindowDialog
|
||||||
|
{
|
||||||
|
public Task<bool> ShowDialogAsync<TViewModel>(TViewModel vm)
|
||||||
|
where TViewModel : class;
|
||||||
|
}
|
||||||
@@ -3,5 +3,4 @@ namespace ServiceLib.Base;
|
|||||||
public class MyReactiveObject : ReactiveObject
|
public class MyReactiveObject : ReactiveObject
|
||||||
{
|
{
|
||||||
protected static Config? _config;
|
protected static Config? _config;
|
||||||
protected Func<EViewAction, object?, Task<bool>>? _updateView;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
namespace ServiceLib.Enums;
|
|
||||||
|
|
||||||
public enum EViewAction
|
|
||||||
{
|
|
||||||
CloseWindow,
|
|
||||||
ShowYesNo,
|
|
||||||
SaveFileDialog,
|
|
||||||
AddBatchRoutingRulesYesNo,
|
|
||||||
SetClipboardData,
|
|
||||||
AddServerViaClipboard,
|
|
||||||
ImportRulesFromClipboard,
|
|
||||||
ProfilesFocus,
|
|
||||||
ShareSub,
|
|
||||||
ShareServer,
|
|
||||||
ScanScreenTask,
|
|
||||||
ScanImageTask,
|
|
||||||
BrowseServer,
|
|
||||||
ImportRulesFromFile,
|
|
||||||
InitSettingFont,
|
|
||||||
PasswordInput,
|
|
||||||
SubEditWindow,
|
|
||||||
RoutingRuleSettingWindow,
|
|
||||||
RoutingRuleDetailsWindow,
|
|
||||||
AddServerWindow,
|
|
||||||
AddServer2Window,
|
|
||||||
AddGroupServerWindow,
|
|
||||||
DNSSettingWindow,
|
|
||||||
RoutingSettingWindow,
|
|
||||||
OptionSettingWindow,
|
|
||||||
FullConfigTemplateWindow,
|
|
||||||
GlobalHotkeySettingWindow,
|
|
||||||
SubSettingWindow,
|
|
||||||
DispatcherRefreshServersBiz,
|
|
||||||
DispatcherRefreshIcon,
|
|
||||||
DispatcherShowMsg,
|
|
||||||
}
|
|
||||||
@@ -2,16 +2,9 @@ namespace ServiceLib.Events;
|
|||||||
|
|
||||||
public static class AppEvents
|
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<Unit> AddServerViaClipboardRequested = new();
|
||||||
public static readonly EventChannel<bool> SubscriptionsUpdateRequested = new();
|
|
||||||
public static readonly EventChannel<bool> HasUpdateNotified = new();
|
public static readonly EventChannel<bool> HasUpdateNotified = 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<ServerSpeedItem> DispatcherStatisticsRequested = new();
|
||||||
|
|
||||||
public static readonly EventChannel<string> SendSnackMsgRequested = new();
|
public static readonly EventChannel<string> SendSnackMsgRequested = new();
|
||||||
@@ -20,12 +13,5 @@ public static class AppEvents
|
|||||||
public static readonly EventChannel<Unit> AppExitRequested = new();
|
public static readonly EventChannel<Unit> AppExitRequested = new();
|
||||||
public static readonly EventChannel<bool> ShutdownRequested = 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();
|
public static readonly EventChannel<ESysProxyType> SysProxyChangeRequested = new();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ public sealed class AppManager
|
|||||||
private int? _statePort2;
|
private int? _statePort2;
|
||||||
public static AppManager Instance => _instance.Value;
|
public static AppManager Instance => _instance.Value;
|
||||||
public Config Config => _config;
|
public Config Config => _config;
|
||||||
|
public IWindowDialog WindowDialog { get; set; } = null!;
|
||||||
|
|
||||||
public int StatePort
|
public int StatePort
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class AddGroupServerViewModel : MyReactiveObject
|
public class AddGroupServerViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public ProfileItem SelectedSource { get; set; }
|
public ProfileItem SelectedSource { get; set; }
|
||||||
|
|
||||||
@@ -29,7 +31,7 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public IObservableCollection<ProfileItem> AllProfilePreviewItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
public IObservableCollection<ProfileItem> AllProfilePreviewItemsObs { get; } = new ObservableCollectionExtended<ProfileItem>();
|
||||||
|
|
||||||
//public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
|
public ReactiveCommand<Unit, Unit> RemoveCmd { get; }
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
|
||||||
@@ -39,15 +41,18 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
public AddGroupServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
public AddGroupServerViewModel(ProfileItem profileItem)
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
var canEditRemove = this.WhenAnyValue(
|
var canEditRemove = this.WhenAnyValue(
|
||||||
x => x.SelectedChild,
|
x => x.SelectedChild,
|
||||||
SelectedChild => SelectedChild != null && !SelectedChild.Remarks.IsNullOrEmpty());
|
selectedChild => selectedChild != null && !selectedChild.Remarks.IsNullOrEmpty());
|
||||||
|
|
||||||
|
AddCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await AddChildAsync();
|
||||||
|
});
|
||||||
RemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
RemoveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await ChildRemoveAsync();
|
await ChildRemoveAsync();
|
||||||
@@ -103,6 +108,20 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
ChildItemsObs.AddRange(childItemList);
|
ChildItemsObs.AddRange(childItemList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task AddChildAsync()
|
||||||
|
{
|
||||||
|
var profileSelectViewModel = new ProfilesSelectViewModel();
|
||||||
|
profileSelectViewModel.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||||
|
profileSelectViewModel.MultiSelect = true;
|
||||||
|
var result = await AppManager.Instance.WindowDialog.ShowDialogAsync(profileSelectViewModel);
|
||||||
|
if (result != true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var profiles = await profileSelectViewModel.GetProfileItems() ?? [];
|
||||||
|
ChildItemsObs.AddRange(profiles);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ChildRemoveAsync()
|
public async Task ChildRemoveAsync()
|
||||||
{
|
{
|
||||||
if (SelectedChild == null || SelectedChild.IndexId.IsNullOrEmpty())
|
if (SelectedChild == null || SelectedChild.IndexId.IsNullOrEmpty())
|
||||||
@@ -230,7 +249,7 @@ public class AddGroupServerViewModel : MyReactiveObject
|
|||||||
if (await ConfigHandler.AddServerCommon(_config, SelectedSource) == 0)
|
if (await ConfigHandler.AddServerCommon(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class AddServer2ViewModel : MyReactiveObject
|
public class AddServer2ViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
public Interaction<Unit, string?> BrowseConfigFileInteraction { get; } = new();
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public ProfileItem SelectedSource { get; set; }
|
public ProfileItem SelectedSource { get; set; }
|
||||||
|
|
||||||
@@ -13,15 +16,18 @@ public class AddServer2ViewModel : MyReactiveObject
|
|||||||
public ReactiveCommand<Unit, Unit> SaveServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveServerCmd { get; }
|
||||||
public bool IsModified { get; set; }
|
public bool IsModified { get; set; }
|
||||||
|
|
||||||
public AddServer2ViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
public AddServer2ViewModel(ProfileItem profileItem)
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
BrowseServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
BrowseServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
_updateView?.Invoke(EViewAction.BrowseServer, null);
|
var fileName = await BrowseConfigFileInteraction.Handle(Unit.Default);
|
||||||
await Task.CompletedTask;
|
if (fileName.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await BrowseServer(fileName);
|
||||||
});
|
});
|
||||||
EditServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
EditServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
@@ -55,7 +61,7 @@ public class AddServer2ViewModel : MyReactiveObject
|
|||||||
if (await ConfigHandler.EditCustomServer(_config, SelectedSource) == 0)
|
if (await ConfigHandler.EditCustomServer(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class AddServerViewModel : MyReactiveObject
|
public class AddServerViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public ProfileItem SelectedSource { get; set; }
|
public ProfileItem SelectedSource { get; set; }
|
||||||
|
|
||||||
@@ -241,10 +243,9 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
public AddServerViewModel(ProfileItem profileItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
public AddServerViewModel(ProfileItem profileItem)
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
FetchCertCmd = ReactiveCommand.CreateFromTask(async () =>
|
FetchCertCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
@@ -443,7 +444,7 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ public class BackupAndRestoreViewModel : MyReactiveObject
|
|||||||
public WebDavItem SelectedSource { get; set; }
|
public WebDavItem SelectedSource { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string OperationMsg { get; set; }
|
public string OperationMsg { get; set; } = string.Empty;
|
||||||
|
|
||||||
public BackupAndRestoreViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public BackupAndRestoreViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
WebDavCheckCmd = ReactiveCommand.CreateFromTask(async () =>
|
WebDavCheckCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,15 +7,16 @@ public class CheckUpdateViewModel : MyReactiveObject
|
|||||||
private List<CheckUpdateModel> _lstUpdated = [];
|
private List<CheckUpdateModel> _lstUpdated = [];
|
||||||
private static readonly string _tag = "CheckUpdateViewModel";
|
private static readonly string _tag = "CheckUpdateViewModel";
|
||||||
|
|
||||||
|
public EventChannel<Unit> ReloadRequested { get; } = new();
|
||||||
|
|
||||||
public IObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = new ObservableCollectionExtended<CheckUpdateModel>();
|
public IObservableCollection<CheckUpdateModel> CheckUpdateModels { get; } = new ObservableCollectionExtended<CheckUpdateModel>();
|
||||||
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
|
public ReactiveCommand<Unit, Unit> CheckUpdateCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> CheckOnlyCmd { get; }
|
public ReactiveCommand<Unit, Unit> CheckOnlyCmd { get; }
|
||||||
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
|
[Reactive] public bool EnableCheckPreReleaseUpdate { get; set; }
|
||||||
|
|
||||||
public CheckUpdateViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public CheckUpdateViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
CheckUpdateCmd = ReactiveCommand.CreateFromTask(CheckUpdate);
|
CheckUpdateCmd = ReactiveCommand.CreateFromTask(CheckUpdate);
|
||||||
CheckUpdateCmd.ThrownExceptions.Subscribe(ex =>
|
CheckUpdateCmd.ThrownExceptions.Subscribe(ex =>
|
||||||
@@ -299,7 +300,7 @@ public class CheckUpdateViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
if (blReload)
|
if (blReload)
|
||||||
{
|
{
|
||||||
AppEvents.ReloadRequested.Publish();
|
ReloadRequested.Publish();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,10 +16,9 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public bool AutoRefresh { get; set; }
|
public bool AutoRefresh { get; set; }
|
||||||
|
|
||||||
public ClashConnectionsViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public ClashConnectionsViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
|
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
|
||||||
|
|
||||||
var canEditRemove = this.WhenAnyValue(
|
var canEditRemove = this.WhenAnyValue(
|
||||||
|
|||||||
@@ -33,10 +33,9 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public bool AutoRefresh { get; set; }
|
public bool AutoRefresh { get; set; }
|
||||||
|
|
||||||
public ClashProxiesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public ClashProxiesViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
ProxiesReloadCmd = ReactiveCommand.CreateFromTask(async () =>
|
ProxiesReloadCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
@@ -86,15 +85,6 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#endregion WhenAnyValue && ReactiveCommand
|
#endregion WhenAnyValue && ReactiveCommand
|
||||||
|
|
||||||
#region AppEvents
|
|
||||||
|
|
||||||
AppEvents.ProxiesReloadRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await ProxiesReload());
|
|
||||||
|
|
||||||
#endregion AppEvents
|
|
||||||
|
|
||||||
_ = Init();
|
_ = Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class DNSSettingViewModel : MyReactiveObject
|
public class DNSSettingViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
|
||||||
[Reactive] public bool? UseSystemHosts { get; set; }
|
[Reactive] public bool? UseSystemHosts { get; set; }
|
||||||
[Reactive] public bool? AddCommonHosts { get; set; }
|
[Reactive] public bool? AddCommonHosts { get; set; }
|
||||||
[Reactive] public bool? FakeIP { get; set; }
|
[Reactive] public bool? FakeIP { get; set; }
|
||||||
@@ -17,15 +19,15 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||||||
[Reactive] public bool? ServeStale { get; set; }
|
[Reactive] public bool? ServeStale { get; set; }
|
||||||
|
|
||||||
[Reactive] public bool UseSystemHostsCompatible { get; set; }
|
[Reactive] public bool UseSystemHostsCompatible { get; set; }
|
||||||
[Reactive] public string DomainStrategy4FreedomCompatible { get; set; }
|
[Reactive] public string DomainStrategy4FreedomCompatible { get; set; } = string.Empty;
|
||||||
[Reactive] public string DomainDNSAddressCompatible { get; set; }
|
[Reactive] public string DomainDNSAddressCompatible { get; set; } = string.Empty;
|
||||||
[Reactive] public string NormalDNSCompatible { get; set; }
|
[Reactive] public string NormalDNSCompatible { get; set; } = string.Empty;
|
||||||
[Reactive] public string TunDNSCompatible { get; set; }
|
[Reactive] public string TunDNSCompatible { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Reactive] public string DomainStrategy4Freedom2Compatible { get; set; }
|
[Reactive] public string DomainStrategy4Freedom2Compatible { get; set; } = string.Empty;
|
||||||
[Reactive] public string DomainDNSAddress2Compatible { get; set; }
|
[Reactive] public string DomainDNSAddress2Compatible { get; set; } = string.Empty;
|
||||||
[Reactive] public string NormalDNS2Compatible { get; set; }
|
[Reactive] public string NormalDNS2Compatible { get; set; } = string.Empty;
|
||||||
[Reactive] public string TunDNS2Compatible { get; set; }
|
[Reactive] public string TunDNS2Compatible { get; set; } = string.Empty;
|
||||||
[Reactive] public bool RayCustomDNSEnableCompatible { get; set; }
|
[Reactive] public bool RayCustomDNSEnableCompatible { get; set; }
|
||||||
[Reactive] public bool SBCustomDNSEnableCompatible { get; set; }
|
[Reactive] public bool SBCustomDNSEnableCompatible { get; set; }
|
||||||
|
|
||||||
@@ -35,10 +37,9 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCompatibleCmd { get; }
|
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCompatibleCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCompatibleCmd { get; }
|
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCompatibleCmd { get; }
|
||||||
|
|
||||||
public DNSSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public DNSSettingViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
SaveCmd = ReactiveCommand.CreateFromTask(SaveSettingAsync);
|
SaveCmd = ReactiveCommand.CreateFromTask(SaveSettingAsync);
|
||||||
|
|
||||||
ImportDefConfig4V2rayCompatibleCmd = ReactiveCommand.CreateFromTask(async () =>
|
ImportDefConfig4V2rayCompatibleCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
@@ -183,9 +184,6 @@ public class DNSSettingViewModel : MyReactiveObject
|
|||||||
await ConfigHandler.SaveDNSItems(_config, item2);
|
await ConfigHandler.SaveDNSItems(_config, item2);
|
||||||
|
|
||||||
await ConfigHandler.SaveConfig(_config);
|
await ConfigHandler.SaveConfig(_config);
|
||||||
if (_updateView != null)
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
{
|
|
||||||
await _updateView(EViewAction.CloseWindow, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class FullConfigTemplateViewModel : MyReactiveObject
|
public class FullConfigTemplateViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
|
||||||
#region Reactive
|
#region Reactive
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
@@ -11,16 +13,16 @@ public class FullConfigTemplateViewModel : MyReactiveObject
|
|||||||
public bool EnableFullConfigTemplate4Singbox { get; set; }
|
public bool EnableFullConfigTemplate4Singbox { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string FullConfigTemplate4Ray { get; set; }
|
public string FullConfigTemplate4Ray { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string FullTunConfigTemplate4Ray { get; set; }
|
public string FullTunConfigTemplate4Ray { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string FullConfigTemplate4Singbox { get; set; }
|
public string FullConfigTemplate4Singbox { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string FullTunConfigTemplate4Singbox { get; set; }
|
public string FullTunConfigTemplate4Singbox { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public bool AddProxyOnly4Ray { get; set; }
|
public bool AddProxyOnly4Ray { get; set; }
|
||||||
@@ -29,19 +31,18 @@ public class FullConfigTemplateViewModel : MyReactiveObject
|
|||||||
public bool AddProxyOnly4Singbox { get; set; }
|
public bool AddProxyOnly4Singbox { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string ProxyDetour4Ray { get; set; }
|
public string ProxyDetour4Ray { get; set; } = string.Empty;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string ProxyDetour4Singbox { get; set; }
|
public string ProxyDetour4Singbox { get; set; } = string.Empty;
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
#endregion Reactive
|
#endregion Reactive
|
||||||
|
|
||||||
public FullConfigTemplateViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public FullConfigTemplateViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SaveSettingAsync();
|
await SaveSettingAsync();
|
||||||
@@ -84,7 +85,7 @@ public class FullConfigTemplateViewModel : MyReactiveObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
_ = _updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> SaveXrayConfigAsync()
|
private async Task<bool> SaveXrayConfigAsync()
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class GlobalHotkeySettingViewModel : MyReactiveObject
|
public class GlobalHotkeySettingViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
|
||||||
private readonly List<KeyEventItem> _globalHotkeys;
|
private readonly List<KeyEventItem> _globalHotkeys;
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
public GlobalHotkeySettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public GlobalHotkeySettingViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
_globalHotkeys = JsonUtils.DeepCopy(_config.GlobalHotkeys);
|
_globalHotkeys = JsonUtils.DeepCopy(_config.GlobalHotkeys);
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ public class GlobalHotkeySettingViewModel : MyReactiveObject
|
|||||||
|
|
||||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||||
{
|
{
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,21 @@ namespace ServiceLib.ViewModels;
|
|||||||
|
|
||||||
public class MainWindowViewModel : MyReactiveObject
|
public class MainWindowViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
|
public Interaction<Unit, string?> ReadTextFromClipboardInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, byte[]?> ScanScreenInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, string?> BrowseImageFileInteraction { get; } = new();
|
||||||
|
public Interaction<bool?, Unit> ShowHideWindowInteraction { get; } = new();
|
||||||
|
|
||||||
|
public bool DesignMode { get; set; }
|
||||||
|
|
||||||
|
public ProfilesViewModel ProfilesViewModel { get; } = new();
|
||||||
|
public MsgViewModel MsgViewModel { get; } = new();
|
||||||
|
public ClashProxiesViewModel ClashProxiesViewModel { get; } = new();
|
||||||
|
public ClashConnectionsViewModel ClashConnectionsViewModel { get; } = new();
|
||||||
|
public CheckUpdateViewModel CheckUpdateViewModel { get; } = new();
|
||||||
|
public BackupAndRestoreViewModel BackupAndRestoreViewModel { get; } = new();
|
||||||
|
public StatusBarViewModel StatusBarViewModel { get; } = StatusBarViewModel.Instance;
|
||||||
|
|
||||||
#region Menu
|
#region Menu
|
||||||
|
|
||||||
//servers
|
//servers
|
||||||
@@ -67,15 +82,17 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
[Reactive] public bool BlNewUpdate { get; set; }
|
[Reactive] public bool BlNewUpdate { get; set; }
|
||||||
|
|
||||||
|
[Reactive] public EGirdOrientation MainGirdOrientation { get; set; }
|
||||||
|
|
||||||
#endregion Menu
|
#endregion Menu
|
||||||
|
|
||||||
#region Init
|
#region Init
|
||||||
|
|
||||||
public MainWindowViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public MainWindowViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
BlIsWindows = Utils.IsWindows();
|
BlIsWindows = Utils.IsWindows();
|
||||||
|
MainGirdOrientation = _config.UiItem.MainGirdOrientation;
|
||||||
|
|
||||||
#region WhenAnyValue && ReactiveCommand
|
#region WhenAnyValue && ReactiveCommand
|
||||||
|
|
||||||
@@ -191,7 +208,8 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
});
|
});
|
||||||
GlobalHotkeySettingCmd = ReactiveCommand.CreateFromTask(async () =>
|
GlobalHotkeySettingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
if (await _updateView?.Invoke(EViewAction.GlobalHotkeySettingWindow, null) == true)
|
var globalHotkeySettingViewModel = new GlobalHotkeySettingViewModel();
|
||||||
|
if (await AppManager.Instance.WindowDialog.ShowDialogAsync(globalHotkeySettingViewModel) == true)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
}
|
}
|
||||||
@@ -233,26 +251,11 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#region AppEvents
|
#region AppEvents
|
||||||
|
|
||||||
AppEvents.ReloadRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await Reload());
|
|
||||||
|
|
||||||
AppEvents.AddServerViaScanRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await AddServerViaScanAsync());
|
|
||||||
|
|
||||||
AppEvents.AddServerViaClipboardRequested
|
AppEvents.AddServerViaClipboardRequested
|
||||||
.AsObservable()
|
.AsObservable()
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
.Subscribe(async _ => await AddServerViaClipboardAsync(null));
|
.Subscribe(async _ => await AddServerViaClipboardAsync(null));
|
||||||
|
|
||||||
AppEvents.SubscriptionsUpdateRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy));
|
|
||||||
|
|
||||||
AppEvents.HasUpdateNotified
|
AppEvents.HasUpdateNotified
|
||||||
.AsObservable()
|
.AsObservable()
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
@@ -260,6 +263,48 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#endregion AppEvents
|
#endregion AppEvents
|
||||||
|
|
||||||
|
var vmReloadRequestedList = new List<IObservable<Unit>>
|
||||||
|
{
|
||||||
|
ProfilesViewModel.ReloadRequested.AsObservable(),
|
||||||
|
StatusBarViewModel.ReloadRequested.AsObservable(),
|
||||||
|
CheckUpdateViewModel.ReloadRequested.AsObservable(),
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var reloadRequested in vmReloadRequestedList)
|
||||||
|
{
|
||||||
|
reloadRequested
|
||||||
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
|
.Subscribe(async _ => await Reload());
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBarViewModel.AddServerViaScanRequested
|
||||||
|
.AsObservable()
|
||||||
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
|
.Subscribe(async _ => await AddServerViaScanAsync());
|
||||||
|
|
||||||
|
StatusBarViewModel.AddServerViaClipboardRequested
|
||||||
|
.AsObservable()
|
||||||
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
|
.Subscribe(async _ => await AddServerViaClipboardAsync(null));
|
||||||
|
|
||||||
|
StatusBarViewModel.ShowHideWindowRequested
|
||||||
|
.AsObservable()
|
||||||
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
|
.Subscribe(async blShow =>
|
||||||
|
{
|
||||||
|
await ShowHideWindowInteraction.Handle(blShow);
|
||||||
|
});
|
||||||
|
|
||||||
|
StatusBarViewModel.SetDefaultServerRequested
|
||||||
|
.AsObservable()
|
||||||
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
|
.Subscribe(async indexId => await ProfilesViewModel.SetDefaultServer(indexId));
|
||||||
|
|
||||||
|
StatusBarViewModel.SubscriptionsUpdateRequested
|
||||||
|
.AsObservable()
|
||||||
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
|
.Subscribe(async blProxy => await UpdateSubscriptionProcess("", blProxy));
|
||||||
|
|
||||||
_ = Init();
|
_ = Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,6 +312,11 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
AppManager.Instance.ShowInTaskbar = true;
|
AppManager.Instance.ShowInTaskbar = true;
|
||||||
|
|
||||||
|
if (DesignMode)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//await ConfigHandler.InitBuiltinRouting(_config);
|
//await ConfigHandler.InitBuiltinRouting(_config);
|
||||||
await ConfigHandler.InitBuiltinDNS(_config);
|
await ConfigHandler.InitBuiltinDNS(_config);
|
||||||
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
await ConfigHandler.InitBuiltinFullConfigTemplate(_config);
|
||||||
@@ -279,7 +329,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
await StatisticsManager.Instance.Init(_config, UpdateStatisticsHandler);
|
await StatisticsManager.Instance.Init(_config, UpdateStatisticsHandler);
|
||||||
}
|
}
|
||||||
await RefreshServers();
|
await RefreshServersDispatcherAsync();
|
||||||
|
|
||||||
await Reload();
|
await Reload();
|
||||||
}
|
}
|
||||||
@@ -304,7 +354,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
var indexIdOld = _config.IndexId;
|
var indexIdOld = _config.IndexId;
|
||||||
await RefreshServers();
|
await RefreshServersDispatcherAsync();
|
||||||
|
|
||||||
// If indexId changed or subIndexId is empty, directly reload.
|
// If indexId changed or subIndexId is empty, directly reload.
|
||||||
if (indexIdOld != _config.IndexId || _config.SubIndexId.IsNullOrEmpty())
|
if (indexIdOld != _config.IndexId || _config.SubIndexId.IsNullOrEmpty())
|
||||||
@@ -323,7 +373,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
if (_config.UiItem.EnableAutoAdjustMainLvColWidth)
|
if (_config.UiItem.EnableAutoAdjustMainLvColWidth)
|
||||||
{
|
{
|
||||||
AppEvents.AdjustMainLvColWidthRequested.Publish();
|
await ProfilesViewModel.AdjustMainLvColWidth();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,14 +394,20 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task RefreshServers()
|
private async Task RefreshServers()
|
||||||
{
|
{
|
||||||
AppEvents.ProfilesRefreshRequested.Publish();
|
await ProfilesViewModel.RefreshServers();
|
||||||
|
await StatusBarViewModel.RefreshServers();
|
||||||
|
|
||||||
await Task.Delay(200);
|
// await Task.Delay(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshSubscriptions()
|
private async Task RefreshServersDispatcherAsync()
|
||||||
{
|
{
|
||||||
AppEvents.SubscriptionsRefreshRequested.Publish();
|
await Observable.Start(async () => await RefreshServers(), RxSchedulers.MainThreadScheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshSubscriptions()
|
||||||
|
{
|
||||||
|
await Observable.Start(async () => await ProfilesViewModel.RefreshSubscriptions(), RxSchedulers.MainThreadScheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Servers && Groups
|
#endregion Servers && Groups
|
||||||
@@ -370,19 +426,22 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
bool? ret = false;
|
bool? ret = false;
|
||||||
if (eConfigType == EConfigType.Custom)
|
if (eConfigType == EConfigType.Custom)
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddServer2Window, item);
|
var addServer2ViewModel = new AddServer2ViewModel(item);
|
||||||
|
ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(addServer2ViewModel);
|
||||||
}
|
}
|
||||||
else if (eConfigType.IsGroupType())
|
else if (eConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddGroupServerWindow, item);
|
var addGroupServerViewModel = new AddGroupServerViewModel(item);
|
||||||
|
ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(addGroupServerViewModel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddServerWindow, item);
|
var addServerViewModel = new AddServerViewModel(item);
|
||||||
|
ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(addServerViewModel);
|
||||||
}
|
}
|
||||||
if (ret == true)
|
if (ret == true)
|
||||||
{
|
{
|
||||||
await RefreshServers();
|
await RefreshServersDispatcherAsync();
|
||||||
if (item.IndexId == _config.IndexId)
|
if (item.IndexId == _config.IndexId)
|
||||||
{
|
{
|
||||||
await Reload();
|
await Reload();
|
||||||
@@ -392,16 +451,22 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public async Task AddServerViaClipboardAsync(string? clipboardData)
|
public async Task AddServerViaClipboardAsync(string? clipboardData)
|
||||||
{
|
{
|
||||||
|
var stringData = clipboardData;
|
||||||
if (clipboardData == null)
|
if (clipboardData == null)
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.AddServerViaClipboard, null);
|
var result = await ReadTextFromClipboardInteraction.Handle(Unit.Default);
|
||||||
return;
|
if (result.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stringData = result;
|
||||||
}
|
}
|
||||||
var ret = await ConfigHandler.AddBatchServers(_config, clipboardData, _config.SubIndexId, false);
|
var ret = await ConfigHandler.AddBatchServers(_config, stringData, _config.SubIndexId, false);
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
{
|
{
|
||||||
RefreshSubscriptions();
|
await RefreshSubscriptions();
|
||||||
await RefreshServers();
|
await RefreshServersDispatcherAsync();
|
||||||
NoticeManager.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret));
|
NoticeManager.Instance.Enqueue(string.Format(ResUI.SuccessfullyImportedServerViaClipboard, ret));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -412,8 +477,8 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public async Task AddServerViaScanAsync()
|
public async Task AddServerViaScanAsync()
|
||||||
{
|
{
|
||||||
_updateView?.Invoke(EViewAction.ScanScreenTask, null);
|
var result = await ScanScreenInteraction.Handle(Unit.Default);
|
||||||
await Task.CompletedTask;
|
await ScanScreenResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ScanScreenResult(byte[]? bytes)
|
public async Task ScanScreenResult(byte[]? bytes)
|
||||||
@@ -424,8 +489,8 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public async Task AddServerViaImageAsync()
|
public async Task AddServerViaImageAsync()
|
||||||
{
|
{
|
||||||
_updateView?.Invoke(EViewAction.ScanImageTask, null);
|
var imageFileName = await BrowseImageFileInteraction.Handle(Unit.Default);
|
||||||
await Task.CompletedTask;
|
await AddScanResultAsync(imageFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ScanImageResult(string fileName)
|
public async Task ScanImageResult(string fileName)
|
||||||
@@ -450,8 +515,8 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
var ret = await ConfigHandler.AddBatchServers(_config, result, _config.SubIndexId, false);
|
var ret = await ConfigHandler.AddBatchServers(_config, result, _config.SubIndexId, false);
|
||||||
if (ret > 0)
|
if (ret > 0)
|
||||||
{
|
{
|
||||||
RefreshSubscriptions();
|
await RefreshSubscriptions();
|
||||||
await RefreshServers();
|
await RefreshServersDispatcherAsync();
|
||||||
NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
|
NoticeManager.Instance.Enqueue(ResUI.SuccessfullyImportedServerViaScan);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -467,9 +532,10 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task SubSettingAsync()
|
private async Task SubSettingAsync()
|
||||||
{
|
{
|
||||||
if (await _updateView?.Invoke(EViewAction.SubSettingWindow, null) == true)
|
var subSettingViewModel = new SubSettingViewModel();
|
||||||
|
if (await AppManager.Instance.WindowDialog.ShowDialogAsync(subSettingViewModel) == true)
|
||||||
{
|
{
|
||||||
RefreshSubscriptions();
|
await RefreshSubscriptions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,28 +550,38 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task OptionSettingAsync()
|
private async Task OptionSettingAsync()
|
||||||
{
|
{
|
||||||
var ret = await _updateView?.Invoke(EViewAction.OptionSettingWindow, null);
|
var settingViewModel = new OptionSettingViewModel();
|
||||||
|
var ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(settingViewModel);
|
||||||
if (ret == true)
|
if (ret == true)
|
||||||
{
|
{
|
||||||
AppEvents.InboundDisplayRequested.Publish();
|
MainGirdOrientation = _config.UiItem.MainGirdOrientation;
|
||||||
|
RxSchedulers.MainThreadScheduler.Schedule(async () =>
|
||||||
|
{
|
||||||
|
await StatusBarViewModel.InboundDisplayStatus();
|
||||||
|
});
|
||||||
await Reload();
|
await Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RoutingSettingAsync()
|
private async Task RoutingSettingAsync()
|
||||||
{
|
{
|
||||||
var ret = await _updateView?.Invoke(EViewAction.RoutingSettingWindow, null);
|
var routingSettingViewModel = new RoutingSettingViewModel();
|
||||||
|
var ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(routingSettingViewModel);
|
||||||
if (ret == true)
|
if (ret == true)
|
||||||
{
|
{
|
||||||
await ConfigHandler.InitBuiltinRouting(_config);
|
await ConfigHandler.InitBuiltinRouting(_config);
|
||||||
AppEvents.RoutingsMenuRefreshRequested.Publish();
|
RxSchedulers.MainThreadScheduler.Schedule(async () =>
|
||||||
|
{
|
||||||
|
await StatusBarViewModel.RefreshRoutingsMenu();
|
||||||
|
});
|
||||||
await Reload();
|
await Reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DNSSettingAsync()
|
private async Task DNSSettingAsync()
|
||||||
{
|
{
|
||||||
var ret = await _updateView?.Invoke(EViewAction.DNSSettingWindow, null);
|
var dnsSettingViewModel = new DNSSettingViewModel();
|
||||||
|
var ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(dnsSettingViewModel);
|
||||||
if (ret == true)
|
if (ret == true)
|
||||||
{
|
{
|
||||||
await Reload();
|
await Reload();
|
||||||
@@ -514,7 +590,8 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task FullConfigTemplateAsync()
|
private async Task FullConfigTemplateAsync()
|
||||||
{
|
{
|
||||||
var ret = await _updateView?.Invoke(EViewAction.FullConfigTemplateWindow, null);
|
var fullConfigTemplateViewModel = new FullConfigTemplateViewModel();
|
||||||
|
var ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(fullConfigTemplateViewModel);
|
||||||
if (ret == true)
|
if (ret == true)
|
||||||
{
|
{
|
||||||
await Reload();
|
await Reload();
|
||||||
@@ -524,7 +601,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
private async Task ClearServerStatistics()
|
private async Task ClearServerStatistics()
|
||||||
{
|
{
|
||||||
await StatisticsManager.Instance.ClearAllServerStatistics();
|
await StatisticsManager.Instance.ClearAllServerStatistics();
|
||||||
await RefreshServers();
|
await RefreshServersDispatcherAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OpenTheFileLocation()
|
private async Task OpenTheFileLocation()
|
||||||
@@ -561,6 +638,12 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (DesignMode)
|
||||||
|
{
|
||||||
|
_reloadSemaphore.Release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SetReloadEnabled(false);
|
SetReloadEnabled(false);
|
||||||
@@ -583,12 +666,22 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
await SysProxyHandler.UpdateSysProxy(_config, false);
|
await SysProxyHandler.UpdateSysProxy(_config, false);
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
});
|
});
|
||||||
AppEvents.TestServerRequested.Publish();
|
RxSchedulers.MainThreadScheduler.Schedule(async () =>
|
||||||
|
{
|
||||||
|
await StatusBarViewModel.TestServerAvailability();
|
||||||
|
});
|
||||||
|
|
||||||
var showClashUI = AppManager.Instance.IsRunningCore(ECoreType.sing_box);
|
var showClashUI = AppManager.Instance.IsRunningCore(ECoreType.sing_box);
|
||||||
if (showClashUI)
|
if (showClashUI)
|
||||||
{
|
{
|
||||||
AppEvents.ProxiesReloadRequested.Publish();
|
//await Observable.Start(async () =>
|
||||||
|
//{
|
||||||
|
// await ClashProxiesViewModel.ProxiesReload();
|
||||||
|
//}, RxSchedulers.MainThreadScheduler);
|
||||||
|
RxSchedulers.MainThreadScheduler.Schedule(async () =>
|
||||||
|
{
|
||||||
|
await ClashProxiesViewModel.ProxiesReload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ReloadResult(showClashUI);
|
ReloadResult(showClashUI);
|
||||||
@@ -633,7 +726,10 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
await ConfigHandler.ApplyRegionalPreset(_config, type);
|
await ConfigHandler.ApplyRegionalPreset(_config, type);
|
||||||
await ConfigHandler.InitRouting(_config);
|
await ConfigHandler.InitRouting(_config);
|
||||||
AppEvents.RoutingsMenuRefreshRequested.Publish();
|
RxSchedulers.MainThreadScheduler.Schedule(async () =>
|
||||||
|
{
|
||||||
|
await StatusBarViewModel.RefreshRoutingsMenu();
|
||||||
|
});
|
||||||
|
|
||||||
await ConfigHandler.SaveConfig(_config);
|
await ConfigHandler.SaveConfig(_config);
|
||||||
await new UpdateService(_config, UpdateTaskHandler).UpdateGeoFileAll();
|
await new UpdateService(_config, UpdateTaskHandler).UpdateGeoFileAll();
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ namespace ServiceLib.ViewModels;
|
|||||||
|
|
||||||
public class MsgViewModel : MyReactiveObject
|
public class MsgViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
|
public Interaction<string, Unit> DispatcherShowMsgInteraction { get; } = new();
|
||||||
|
|
||||||
private readonly ConcurrentQueue<string> _queueMsg = new();
|
private readonly ConcurrentQueue<string> _queueMsg = new();
|
||||||
private volatile bool _lastMsgFilterNotAvailable;
|
private volatile bool _lastMsgFilterNotAvailable;
|
||||||
private int _showLock = 0; // 0 = unlocked, 1 = locked
|
private int _showLock = 0; // 0 = unlocked, 1 = locked
|
||||||
@@ -13,10 +15,9 @@ public class MsgViewModel : MyReactiveObject
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public bool AutoRefresh { get; set; }
|
public bool AutoRefresh { get; set; }
|
||||||
|
|
||||||
public MsgViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public MsgViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
MsgFilter = _config.MsgUIItem.MainMsgFilter ?? string.Empty;
|
MsgFilter = _config.MsgUIItem.MainMsgFilter ?? string.Empty;
|
||||||
AutoRefresh = _config.MsgUIItem.AutoRefresh ?? true;
|
AutoRefresh = _config.MsgUIItem.AutoRefresh ?? true;
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ public class MsgViewModel : MyReactiveObject
|
|||||||
sb.Append(line);
|
sb.Append(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString());
|
await DispatcherShowMsgInteraction.Handle(sb.ToString());
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class OptionSettingViewModel : MyReactiveObject
|
public class OptionSettingViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
|
||||||
#region Core
|
#region Core
|
||||||
|
|
||||||
[Reactive] public int LocalPort { get; set; }
|
[Reactive] public int LocalPort { get; set; }
|
||||||
@@ -123,10 +125,9 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
public OptionSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public OptionSettingViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
BlIsWindows = Utils.IsWindows();
|
BlIsWindows = Utils.IsWindows();
|
||||||
BlIsLinux = Utils.IsLinux();
|
BlIsLinux = Utils.IsLinux();
|
||||||
BlIsIsMacOS = Utils.IsMacOS();
|
BlIsIsMacOS = Utils.IsMacOS();
|
||||||
@@ -142,8 +143,6 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task Init()
|
private async Task Init()
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.InitSettingFont, null);
|
|
||||||
|
|
||||||
#region Core
|
#region Core
|
||||||
|
|
||||||
var inbound = _config.Inbound.First();
|
var inbound = _config.Inbound.First();
|
||||||
@@ -314,8 +313,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||||||
|| DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed
|
|| DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed
|
||||||
|| EnableDragDropSort != _config.UiItem.EnableDragDropSort
|
|| EnableDragDropSort != _config.UiItem.EnableDragDropSort
|
||||||
|| EnableHWA != _config.GuiItem.EnableHWA
|
|| EnableHWA != _config.GuiItem.EnableHWA
|
||||||
|| CurrentFontFamily != _config.UiItem.CurrentFontFamily
|
|| CurrentFontFamily != _config.UiItem.CurrentFontFamily;
|
||||||
|| MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation;
|
|
||||||
|
|
||||||
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
|
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
|
||||||
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
|
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
|
||||||
@@ -432,7 +430,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||||||
AppManager.Instance.Reset();
|
AppManager.Instance.Reset();
|
||||||
|
|
||||||
NoticeManager.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class ProfilesSelectViewModel : MyReactiveObject
|
public class ProfilesSelectViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
public Interaction<Unit, Unit> ProfilesFocusInteraction { get; } = new();
|
||||||
|
|
||||||
#region private prop
|
#region private prop
|
||||||
|
|
||||||
private string _serverFilter = string.Empty;
|
private string _serverFilter = string.Empty;
|
||||||
@@ -12,6 +15,8 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#endregion private prop
|
#endregion private prop
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
#region ObservableCollection
|
#region ObservableCollection
|
||||||
|
|
||||||
public IObservableCollection<ProfileItemModel> ProfileItems { get; } = new ObservableCollectionExtended<ProfileItemModel>();
|
public IObservableCollection<ProfileItemModel> ProfileItems { get; } = new ObservableCollectionExtended<ProfileItemModel>();
|
||||||
@@ -36,18 +41,25 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public bool FilterExclude { get; set; }
|
public bool FilterExclude { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public bool MultiSelect { get; set; }
|
||||||
|
|
||||||
#endregion ObservableCollection
|
#endregion ObservableCollection
|
||||||
|
|
||||||
#region Init
|
#region Init
|
||||||
|
|
||||||
public ProfilesSelectViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public ProfilesSelectViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
_subIndexId = _config.SubIndexId ?? string.Empty;
|
_subIndexId = _config.SubIndexId ?? string.Empty;
|
||||||
|
|
||||||
#region WhenAnyValue && ReactiveCommand
|
#region WhenAnyValue && ReactiveCommand
|
||||||
|
|
||||||
|
SaveCmd = ReactiveCommand.Create(() =>
|
||||||
|
{
|
||||||
|
SelectFinish();
|
||||||
|
});
|
||||||
|
|
||||||
this.WhenAnyValue(
|
this.WhenAnyValue(
|
||||||
x => x.SelectedSub,
|
x => x.SelectedSub,
|
||||||
y => y != null && !y.Remarks.IsNullOrEmpty() && _subIndexId != y.Id)
|
y => y != null && !y.Remarks.IsNullOrEmpty() && _subIndexId != y.Id)
|
||||||
@@ -107,7 +119,7 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +137,13 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
|||||||
|
|
||||||
await RefreshServers();
|
await RefreshServers();
|
||||||
|
|
||||||
await _updateView?.Invoke(EViewAction.ProfilesFocus, null);
|
try
|
||||||
|
{
|
||||||
|
await ProfilesFocusInteraction.Handle(Unit.Default);
|
||||||
|
}
|
||||||
|
catch (UnhandledInteractionException<Unit, Unit>)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ServerFilterChanged(bool c)
|
private async Task ServerFilterChanged(bool c)
|
||||||
@@ -157,8 +175,6 @@ public class ProfilesSelectViewModel : MyReactiveObject
|
|||||||
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
|
var selected = lstModel.FirstOrDefault(t => t.IndexId == _config.IndexId);
|
||||||
SelectedProfile = selected ?? lstModel.First();
|
SelectedProfile = selected ?? lstModel.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshSubscriptions()
|
private async Task RefreshSubscriptions()
|
||||||
|
|||||||
@@ -2,6 +2,16 @@ namespace ServiceLib.ViewModels;
|
|||||||
|
|
||||||
public class ProfilesViewModel : MyReactiveObject
|
public class ProfilesViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
|
public Interaction<string, bool> ShowYesNoInteraction { get; } = new();
|
||||||
|
public Interaction<ProfileItem, bool> SaveFileDialogInteraction { get; } = new();
|
||||||
|
public Interaction<string, Unit> SetClipboardDataInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, Unit> ProfilesFocusInteraction { get; } = new();
|
||||||
|
public Interaction<string, Unit> ShareServerInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, Unit> DispatcherRefreshServersBizInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, Unit> AdjustMainLvColWidthInteraction { get; } = new();
|
||||||
|
|
||||||
|
public EventChannel<Unit> ReloadRequested { get; } = new();
|
||||||
|
|
||||||
#region private prop
|
#region private prop
|
||||||
|
|
||||||
private List<ProfileItem> _lstProfile;
|
private List<ProfileItem> _lstProfile;
|
||||||
@@ -82,10 +92,9 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#region Init
|
#region Init
|
||||||
|
|
||||||
public ProfilesViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public ProfilesViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
#region WhenAnyValue && ReactiveCommand
|
#region WhenAnyValue && ReactiveCommand
|
||||||
|
|
||||||
@@ -236,26 +245,11 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#region AppEvents
|
#region AppEvents
|
||||||
|
|
||||||
AppEvents.ProfilesRefreshRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await RefreshServersBiz());
|
|
||||||
|
|
||||||
AppEvents.SubscriptionsRefreshRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await RefreshSubscriptions());
|
|
||||||
|
|
||||||
AppEvents.DispatcherStatisticsRequested
|
AppEvents.DispatcherStatisticsRequested
|
||||||
.AsObservable()
|
.AsObservable()
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
.Subscribe(async result => await UpdateStatistics(result));
|
.Subscribe(async result => await UpdateStatistics(result));
|
||||||
|
|
||||||
AppEvents.SetDefaultServerRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async indexId => await SetDefaultServer(indexId));
|
|
||||||
|
|
||||||
#endregion AppEvents
|
#endregion AppEvents
|
||||||
|
|
||||||
_ = Init();
|
_ = Init();
|
||||||
@@ -277,7 +271,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private void Reload()
|
private void Reload()
|
||||||
{
|
{
|
||||||
AppEvents.ReloadRequested.Publish();
|
ReloadRequested.Publish();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetSpeedTestResult(SpeedTestResult result)
|
public async Task SetSpeedTestResult(SpeedTestResult result)
|
||||||
@@ -350,7 +344,13 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
await RefreshServers();
|
await RefreshServers();
|
||||||
|
|
||||||
await _updateView?.Invoke(EViewAction.ProfilesFocus, null);
|
try
|
||||||
|
{
|
||||||
|
await ProfilesFocusInteraction.Handle(Unit.Default);
|
||||||
|
}
|
||||||
|
catch (UnhandledInteractionException<Unit, Unit>)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ServerFilterChanged(bool c)
|
private async Task ServerFilterChanged(bool c)
|
||||||
@@ -368,9 +368,9 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public async Task RefreshServers()
|
public async Task RefreshServers()
|
||||||
{
|
{
|
||||||
AppEvents.ProfilesRefreshRequested.Publish();
|
await RefreshServersBiz();
|
||||||
|
|
||||||
await Task.Delay(200);
|
// await Task.Delay(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshServersBiz()
|
private async Task RefreshServersBiz()
|
||||||
@@ -379,8 +379,8 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
|
_lstProfile = JsonUtils.Deserialize<List<ProfileItem>>(JsonUtils.Serialize(lstModel)) ?? [];
|
||||||
|
|
||||||
ProfileItems.Clear();
|
ProfileItems.Clear();
|
||||||
ProfileItems.AddRange(lstModel);
|
ProfileItems.AddRange(lstModel ?? []);
|
||||||
if (lstModel.Count > 0)
|
if (lstModel?.Count > 0)
|
||||||
{
|
{
|
||||||
ProfileItemModel? selected = null;
|
ProfileItemModel? selected = null;
|
||||||
if (!_pendingSelectIndexId.IsNullOrEmpty())
|
if (!_pendingSelectIndexId.IsNullOrEmpty())
|
||||||
@@ -392,10 +392,16 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
SelectedProfile = selected ?? lstModel.First();
|
SelectedProfile = selected ?? lstModel.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _updateView?.Invoke(EViewAction.DispatcherRefreshServersBiz, null);
|
try
|
||||||
|
{
|
||||||
|
await DispatcherRefreshServersBizInteraction.Handle(Unit.Default);
|
||||||
|
}
|
||||||
|
catch (UnhandledInteractionException<Unit, Unit>)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshSubscriptions()
|
public async Task RefreshSubscriptions()
|
||||||
{
|
{
|
||||||
var subItems = await AppManager.Instance.SubItems();
|
var subItems = await AppManager.Instance.SubItems();
|
||||||
subItems.Insert(0, new SubItem { Remarks = ResUI.AllGroupServers });
|
subItems.Insert(0, new SubItem { Remarks = ResUI.AllGroupServers });
|
||||||
@@ -408,6 +414,11 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
: null) ?? subItems.FirstOrDefault();
|
: null) ?? subItems.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task AdjustMainLvColWidth()
|
||||||
|
{
|
||||||
|
await AdjustMainLvColWidthInteraction.Handle(Unit.Default);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
private async Task<List<ProfileItemModel>?> GetProfileItemsEx(string subid, string filter)
|
||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileModels(_config.SubIndexId, filter);
|
var lstModel = await AppManager.Instance.ProfileModels(_config.SubIndexId, filter);
|
||||||
@@ -491,15 +502,18 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
bool? ret = false;
|
bool? ret = false;
|
||||||
if (eConfigType == EConfigType.Custom)
|
if (eConfigType == EConfigType.Custom)
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddServer2Window, item);
|
var addServer2ViewModel = new AddServer2ViewModel(item);
|
||||||
|
ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(addServer2ViewModel);
|
||||||
}
|
}
|
||||||
else if (eConfigType.IsGroupType())
|
else if (eConfigType.IsGroupType())
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddGroupServerWindow, item);
|
var addGroupServerViewModel = new AddGroupServerViewModel(item);
|
||||||
|
ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(addGroupServerViewModel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = await _updateView?.Invoke(EViewAction.AddServerWindow, item);
|
var addServerViewModel = new AddServerViewModel(item);
|
||||||
|
ret = await AppManager.Instance.WindowDialog.ShowDialogAsync(addServerViewModel);
|
||||||
}
|
}
|
||||||
if (ret == true)
|
if (ret == true)
|
||||||
{
|
{
|
||||||
@@ -518,7 +532,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -539,7 +553,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task RemoveDuplicateServer()
|
private async Task RemoveDuplicateServer()
|
||||||
{
|
{
|
||||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -576,7 +590,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
await SetDefaultServer(SelectedProfile.IndexId);
|
await SetDefaultServer(SelectedProfile.IndexId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetDefaultServer(string? indexId)
|
public async Task SetDefaultServer(string? indexId)
|
||||||
{
|
{
|
||||||
if (indexId.IsNullOrEmpty())
|
if (indexId.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
@@ -614,7 +628,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _updateView?.Invoke(EViewAction.ShareServer, url);
|
await ShareServerInteraction.Handle(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task GenGroupAllServer()
|
private async Task GenGroupAllServer()
|
||||||
@@ -783,13 +797,13 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.SetClipboardData, result.Data);
|
await SetClipboardDataInteraction.Handle((string)result.Data);
|
||||||
NoticeManager.Instance.SendMessage(ResUI.OperationSuccess);
|
NoticeManager.Instance.SendMessage(ResUI.OperationSuccess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.SaveFileDialog, item);
|
await SaveFileDialogInteraction.Handle(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,11 +852,11 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
if (blEncode)
|
if (blEncode)
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.SetClipboardData, Utils.Base64Encode(sb.ToString()));
|
await SetClipboardDataInteraction.Handle(Utils.Base64Encode(sb.ToString()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.SetClipboardData, sb.ToString());
|
await SetClipboardDataInteraction.Handle(sb.ToString());
|
||||||
}
|
}
|
||||||
NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully);
|
NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully);
|
||||||
}
|
}
|
||||||
@@ -865,7 +879,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
if (!result.IsNullOrEmpty())
|
if (!result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.SetClipboardData, result);
|
await SetClipboardDataInteraction.Handle(result);
|
||||||
NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully);
|
NoticeManager.Instance.SendMessage(ResUI.BatchExportURLSuccessfully);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -893,7 +907,8 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (await _updateView?.Invoke(EViewAction.SubEditWindow, item) == true)
|
var subEditViewModel = new SubEditViewModel(item);
|
||||||
|
if (await AppManager.Instance.WindowDialog.ShowDialogAsync(subEditViewModel) == true)
|
||||||
{
|
{
|
||||||
await RefreshSubscriptions();
|
await RefreshSubscriptions();
|
||||||
await SubSelectedChangedAsync(true);
|
await SubSelectedChangedAsync(true);
|
||||||
@@ -908,7 +923,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class RoutingRuleDetailsViewModel : MyReactiveObject
|
public class RoutingRuleDetailsViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
|
||||||
public IList<string> ProtocolItems { get; set; }
|
public IList<string> ProtocolItems { get; set; }
|
||||||
public IList<string> InboundTagItems { get; set; }
|
public IList<string> InboundTagItems { get; set; }
|
||||||
|
|
||||||
@@ -23,13 +25,17 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public bool AutoSort { get; set; }
|
public bool AutoSort { get; set; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> SelectProfileCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
public RoutingRuleDetailsViewModel(RulesItem rulesItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
public RoutingRuleDetailsViewModel(RulesItem rulesItem)
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
|
SelectProfileCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await SelectProfileAsync();
|
||||||
|
});
|
||||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SaveRulesAsync();
|
await SaveRulesAsync();
|
||||||
@@ -88,6 +94,24 @@ public class RoutingRuleDetailsViewModel : MyReactiveObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
//NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
await _updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SelectProfileAsync()
|
||||||
|
{
|
||||||
|
var profileSelectViewModel = new ProfilesSelectViewModel();
|
||||||
|
profileSelectViewModel.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||||
|
var result = await AppManager.Instance.WindowDialog.ShowDialogAsync(profileSelectViewModel);
|
||||||
|
if (result != true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var profileItem = await profileSelectViewModel.GetProfileItem();
|
||||||
|
if (profileItem != null)
|
||||||
|
{
|
||||||
|
SelectedSource.OutboundTag = profileItem.Remarks;
|
||||||
|
SelectedSource = JsonUtils.DeepCopy(SelectedSource);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class RoutingRuleSettingViewModel : MyReactiveObject
|
public class RoutingRuleSettingViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
public Interaction<string, bool> ShowYesNoInteraction { get; } = new();
|
||||||
|
public Interaction<string, Unit> SetClipboardDataInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, string?> ReadTextFromClipboardInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, string?> BrowseRulesFileInteraction { get; } = new();
|
||||||
|
|
||||||
private List<RulesItem> _rules;
|
private List<RulesItem> _rules;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
@@ -27,10 +33,9 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
public RoutingRuleSettingViewModel(RoutingItem routingItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
public RoutingRuleSettingViewModel(RoutingItem routingItem)
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
var canEditRemove = this.WhenAnyValue(
|
var canEditRemove = this.WhenAnyValue(
|
||||||
x => x.SelectedSource,
|
x => x.SelectedSource,
|
||||||
@@ -42,7 +47,8 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||||||
});
|
});
|
||||||
ImportRulesFromFileCmd = ReactiveCommand.CreateFromTask(async () =>
|
ImportRulesFromFileCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.ImportRulesFromFile, null);
|
var fileName = await BrowseRulesFileInteraction.Handle(Unit.Default);
|
||||||
|
await ImportRulesFromFileAsync(fileName);
|
||||||
});
|
});
|
||||||
ImportRulesFromClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
ImportRulesFromClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
@@ -131,7 +137,8 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (await _updateView?.Invoke(EViewAction.RoutingRuleDetailsWindow, item) == true)
|
var routingRuleDetailsViewModel = new RoutingRuleDetailsViewModel(item);
|
||||||
|
if (await AppManager.Instance.WindowDialog.ShowDialogAsync(routingRuleDetailsViewModel) == true)
|
||||||
{
|
{
|
||||||
if (blNew)
|
if (blNew)
|
||||||
{
|
{
|
||||||
@@ -148,7 +155,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -191,7 +198,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
};
|
};
|
||||||
await _updateView?.Invoke(EViewAction.SetClipboardData, JsonUtils.Serialize(lst, options));
|
await SetClipboardDataInteraction.Handle(JsonUtils.Serialize(lst, options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +241,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||||||
if (await ConfigHandler.SaveRoutingItem(_config, item) == 0)
|
if (await ConfigHandler.SaveRoutingItem(_config, item) == 0)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -266,12 +273,18 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public async Task ImportRulesFromClipboardAsync(string? clipboardData)
|
public async Task ImportRulesFromClipboardAsync(string? clipboardData)
|
||||||
{
|
{
|
||||||
|
var stringData = clipboardData;
|
||||||
if (clipboardData == null)
|
if (clipboardData == null)
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.ImportRulesFromClipboard, null);
|
var result = await ReadTextFromClipboardInteraction.Handle(Unit.Default);
|
||||||
return;
|
if (result.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stringData = result;
|
||||||
}
|
}
|
||||||
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, clipboardData);
|
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, stringData);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
RefreshRulesItems();
|
RefreshRulesItems();
|
||||||
@@ -301,7 +314,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
|
|||||||
private async Task<int> AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData)
|
private async Task<int> AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData)
|
||||||
{
|
{
|
||||||
var blReplace = false;
|
var blReplace = false;
|
||||||
if (await _updateView?.Invoke(EViewAction.AddBatchRoutingRulesYesNo, null) == false)
|
if (await ShowYesNoInteraction.Handle(ResUI.AddBatchRoutingRulesYesNo) == false)
|
||||||
{
|
{
|
||||||
blReplace = true;
|
blReplace = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ namespace ServiceLib.ViewModels;
|
|||||||
|
|
||||||
public class RoutingSettingViewModel : MyReactiveObject
|
public class RoutingSettingViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
|
public Interaction<string, bool> ShowYesNoInteraction { get; } = new();
|
||||||
|
|
||||||
#region Reactive
|
#region Reactive
|
||||||
|
|
||||||
public IObservableCollection<RoutingItemModel> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItemModel>();
|
public IObservableCollection<RoutingItemModel> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItemModel>();
|
||||||
@@ -26,10 +28,9 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#endregion Reactive
|
#endregion Reactive
|
||||||
|
|
||||||
public RoutingSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public RoutingSettingViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
var canEditRemove = this.WhenAnyValue(
|
var canEditRemove = this.WhenAnyValue(
|
||||||
x => x.SelectedSource,
|
x => x.SelectedSource,
|
||||||
@@ -131,7 +132,8 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (await _updateView?.Invoke(EViewAction.RoutingRuleSettingWindow, item) == true)
|
var routingRuleSettingViewModel = new RoutingRuleSettingViewModel(item);
|
||||||
|
if (await AppManager.Instance.WindowDialog.ShowDialogAsync(routingRuleSettingViewModel) == true)
|
||||||
{
|
{
|
||||||
await RefreshRoutingItems();
|
await RefreshRoutingItems();
|
||||||
IsModified = true;
|
IsModified = true;
|
||||||
@@ -145,7 +147,7 @@ public class RoutingSettingViewModel : MyReactiveObject
|
|||||||
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
NoticeManager.Instance.Enqueue(ResUI.PleaseSelectRules);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,20 @@ namespace ServiceLib.ViewModels;
|
|||||||
|
|
||||||
public class StatusBarViewModel : MyReactiveObject
|
public class StatusBarViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
private static readonly Lazy<StatusBarViewModel> _instance = new(() => new(null));
|
public Interaction<string, Unit> SetClipboardDataInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, string?> PasswordInputInteraction { get; } = new();
|
||||||
|
public Interaction<Unit, Unit> DispatcherRefreshIconInteraction { get; } = new();
|
||||||
|
public EventChannel<bool> SubscriptionsUpdateRequested { get; } = new();
|
||||||
|
public EventChannel<bool?> ShowHideWindowRequested { get; } = new();
|
||||||
|
|
||||||
|
private static readonly Lazy<StatusBarViewModel> _instance = new(() => new());
|
||||||
public static StatusBarViewModel Instance => _instance.Value;
|
public static StatusBarViewModel Instance => _instance.Value;
|
||||||
|
|
||||||
|
public EventChannel<string> SetDefaultServerRequested { get; } = new();
|
||||||
|
public EventChannel<Unit> ReloadRequested { get; } = new();
|
||||||
|
public EventChannel<Unit> AddServerViaScanRequested { get; } = new();
|
||||||
|
public EventChannel<Unit> AddServerViaClipboardRequested { get; } = new();
|
||||||
|
|
||||||
#region ObservableCollection
|
#region ObservableCollection
|
||||||
|
|
||||||
public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>();
|
public IObservableCollection<RoutingItem> RoutingItems { get; } = new ObservableCollectionExtended<RoutingItem>();
|
||||||
@@ -92,7 +103,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#endregion UI
|
#endregion UI
|
||||||
|
|
||||||
public StatusBarViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public StatusBarViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
SelectedRouting = new();
|
SelectedRouting = new();
|
||||||
@@ -140,17 +151,17 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
|
|
||||||
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
|
NotifyLeftClickCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
AppEvents.ShowHideWindowRequested.Publish(null);
|
ShowHideWindowRequested.Publish(null);
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
});
|
});
|
||||||
ShowWindowCmd = ReactiveCommand.CreateFromTask(async () =>
|
ShowWindowCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
AppEvents.ShowHideWindowRequested.Publish(true);
|
ShowHideWindowRequested.Publish(true);
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
});
|
});
|
||||||
HideWindowCmd = ReactiveCommand.CreateFromTask(async () =>
|
HideWindowCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
AppEvents.ShowHideWindowRequested.Publish(false);
|
ShowHideWindowRequested.Publish(false);
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -193,31 +204,11 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#region AppEvents
|
#region AppEvents
|
||||||
|
|
||||||
if (updateView != null)
|
|
||||||
{
|
|
||||||
InitUpdateView(updateView);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppEvents.DispatcherStatisticsRequested
|
AppEvents.DispatcherStatisticsRequested
|
||||||
.AsObservable()
|
.AsObservable()
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
.Subscribe(async result => await UpdateStatistics(result));
|
.Subscribe(async result => await UpdateStatistics(result));
|
||||||
|
|
||||||
AppEvents.RoutingsMenuRefreshRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await RefreshRoutingsMenu());
|
|
||||||
|
|
||||||
AppEvents.TestServerRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await TestServerAvailability());
|
|
||||||
|
|
||||||
AppEvents.InboundDisplayRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await InboundDisplayStatus());
|
|
||||||
|
|
||||||
AppEvents.SysProxyChangeRequested
|
AppEvents.SysProxyChangeRequested
|
||||||
.AsObservable()
|
.AsObservable()
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
@@ -238,18 +229,6 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
BlRouting = true;
|
BlRouting = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitUpdateView(Func<EViewAction, object?, Task<bool>>? updateView)
|
|
||||||
{
|
|
||||||
_updateView = updateView;
|
|
||||||
if (_updateView != null)
|
|
||||||
{
|
|
||||||
AppEvents.ProfilesRefreshRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(async _ => await RefreshServersBiz()); //.DisposeWith(_disposables);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task CopyProxyCmdToClipboard()
|
private async Task CopyProxyCmdToClipboard()
|
||||||
{
|
{
|
||||||
var cmd = Utils.IsWindows() ? "set" : "export";
|
var cmd = Utils.IsWindows() ? "set" : "export";
|
||||||
@@ -264,27 +243,32 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
sb.AppendLine($"{cmd} HTTPS_PROXY={Global.HttpProtocol}{address}");
|
sb.AppendLine($"{cmd} HTTPS_PROXY={Global.HttpProtocol}{address}");
|
||||||
sb.AppendLine($"{cmd} ALL_PROXY={Global.Socks5Protocol}{address}");
|
sb.AppendLine($"{cmd} ALL_PROXY={Global.Socks5Protocol}{address}");
|
||||||
|
|
||||||
await _updateView?.Invoke(EViewAction.SetClipboardData, sb.ToString());
|
await SetClipboardDataInteraction.Handle(sb.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddServerViaClipboard()
|
private async Task AddServerViaClipboard()
|
||||||
{
|
{
|
||||||
AppEvents.AddServerViaClipboardRequested.Publish();
|
AddServerViaClipboardRequested.Publish();
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddServerViaScan()
|
private async Task AddServerViaScan()
|
||||||
{
|
{
|
||||||
AppEvents.AddServerViaScanRequested.Publish();
|
AddServerViaScanRequested.Publish();
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task UpdateSubscriptionProcess(bool blProxy)
|
private async Task UpdateSubscriptionProcess(bool blProxy)
|
||||||
{
|
{
|
||||||
AppEvents.SubscriptionsUpdateRequested.Publish(blProxy);
|
SubscriptionsUpdateRequested.Publish(blProxy);
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RefreshServers()
|
||||||
|
{
|
||||||
|
await RefreshServersBiz();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RefreshServersBiz()
|
private async Task RefreshServersBiz()
|
||||||
{
|
{
|
||||||
await RefreshServersMenu();
|
await RefreshServersMenu();
|
||||||
@@ -312,8 +296,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
var lstModel = await AppManager.Instance.ProfileModels(_config.SubIndexId, "");
|
var lstModel = await AppManager.Instance.ProfileModels(_config.SubIndexId, "");
|
||||||
|
|
||||||
Servers.Clear();
|
if (lstModel?.Count > _config.GuiItem.TrayMenuServersLimit)
|
||||||
if (lstModel.Count > _config.GuiItem.TrayMenuServersLimit)
|
|
||||||
{
|
{
|
||||||
BlServers = false;
|
BlServers = false;
|
||||||
return;
|
return;
|
||||||
@@ -332,6 +315,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
SelectedServer = item;
|
SelectedServer = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Servers.Clear();
|
||||||
Servers.AddRange(models);
|
Servers.AddRange(models);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,7 +333,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AppEvents.SetDefaultServerRequested.Publish(SelectedServer.ID);
|
SetDefaultServerRequested.Publish(SelectedServer.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task TestServerAvailability()
|
public async Task TestServerAvailability()
|
||||||
@@ -411,11 +395,18 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
|
|
||||||
if (blChange)
|
if (blChange)
|
||||||
{
|
{
|
||||||
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
try
|
||||||
|
{
|
||||||
|
await DispatcherRefreshIconInteraction.Handle(Unit.Default);
|
||||||
|
}
|
||||||
|
catch (UnhandledInteractionException<Unit, Unit>)
|
||||||
|
{
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RefreshRoutingsMenu()
|
public async Task RefreshRoutingsMenu()
|
||||||
{
|
{
|
||||||
var routings = await AppManager.Instance.RoutingItems();
|
var routings = await AppManager.Instance.RoutingItems();
|
||||||
|
|
||||||
@@ -446,8 +437,8 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting);
|
NoticeManager.Instance.SendMessageEx(ResUI.TipChangeRouting);
|
||||||
AppEvents.ReloadRequested.Publish();
|
ReloadRequested.Publish();
|
||||||
_updateView?.Invoke(EViewAction.DispatcherRefreshIcon, null);
|
await DispatcherRefreshIconInteraction.Handle(Unit.Default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,8 +475,8 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool? passwordResult = await _updateView?.Invoke(EViewAction.PasswordInput, null);
|
var password = await PasswordInputInteraction.Handle(Unit.Default);
|
||||||
if (passwordResult == false)
|
if (password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
_config.TunModeItem.EnableTun = false;
|
_config.TunModeItem.EnableTun = false;
|
||||||
return;
|
return;
|
||||||
@@ -494,7 +485,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ConfigHandler.SaveConfig(_config);
|
await ConfigHandler.SaveConfig(_config);
|
||||||
AppEvents.ReloadRequested.Publish();
|
ReloadRequested.Publish();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AllowEnableTun()
|
private bool AllowEnableTun()
|
||||||
@@ -518,7 +509,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
|
|
||||||
#region UI
|
#region UI
|
||||||
|
|
||||||
private async Task InboundDisplayStatus()
|
public async Task InboundDisplayStatus()
|
||||||
{
|
{
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}");
|
sb.Append($"[{EInboundProtocol.mixed}:{AppManager.Instance.GetLocalPort(EInboundProtocol.socks)}");
|
||||||
|
|||||||
@@ -1,17 +1,38 @@
|
|||||||
namespace ServiceLib.ViewModels;
|
namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class SubEditViewModel : MyReactiveObject
|
public class SubEditViewModel : MyReactiveObject, ICloseable
|
||||||
{
|
{
|
||||||
|
public event EventHandler? RequestClose;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public SubItem SelectedSource { get; set; }
|
public SubItem SelectedSource { get; set; }
|
||||||
|
|
||||||
|
public ReactiveCommand<Unit, Unit> SelectPrevProfileCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> SelectNextProfileCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
|
|
||||||
public SubEditViewModel(SubItem subItem, Func<EViewAction, object?, Task<bool>>? updateView)
|
public SubEditViewModel(SubItem subItem)
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
|
SelectPrevProfileCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
var profileItem = await SelectProfileAsync();
|
||||||
|
if (profileItem != null)
|
||||||
|
{
|
||||||
|
SelectedSource?.PrevProfile = profileItem.Remarks;
|
||||||
|
SelectedSource = JsonUtils.DeepCopy(SelectedSource);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
SelectNextProfileCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
var profileItem = await SelectProfileAsync();
|
||||||
|
if (profileItem != null)
|
||||||
|
{
|
||||||
|
SelectedSource?.NextProfile = profileItem.Remarks;
|
||||||
|
SelectedSource = JsonUtils.DeepCopy(SelectedSource);
|
||||||
|
}
|
||||||
|
});
|
||||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await SaveSubAsync();
|
await SaveSubAsync();
|
||||||
@@ -49,11 +70,24 @@ public class SubEditViewModel : MyReactiveObject
|
|||||||
if (await ConfigHandler.AddSubItem(_config, SelectedSource) == 0)
|
if (await ConfigHandler.AddSubItem(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
RequestClose?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<ProfileItem?> SelectProfileAsync()
|
||||||
|
{
|
||||||
|
var profileSelectViewModel = new ProfilesSelectViewModel();
|
||||||
|
profileSelectViewModel.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
||||||
|
var result = await AppManager.Instance.WindowDialog.ShowDialogAsync(profileSelectViewModel);
|
||||||
|
if (result != true)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var profileItem = await profileSelectViewModel.GetProfileItem();
|
||||||
|
return profileItem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ namespace ServiceLib.ViewModels;
|
|||||||
|
|
||||||
public class SubSettingViewModel : MyReactiveObject
|
public class SubSettingViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
|
public Interaction<string, bool> ShowYesNoInteraction { get; } = new();
|
||||||
|
public Interaction<string, Unit> ShareSubInteraction { get; } = new();
|
||||||
|
|
||||||
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
|
public IObservableCollection<SubItem> SubItems { get; } = new ObservableCollectionExtended<SubItem>();
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
@@ -15,10 +18,9 @@ public class SubSettingViewModel : MyReactiveObject
|
|||||||
public ReactiveCommand<Unit, Unit> SubShareCmd { get; }
|
public ReactiveCommand<Unit, Unit> SubShareCmd { get; }
|
||||||
public bool IsModified { get; set; }
|
public bool IsModified { get; set; }
|
||||||
|
|
||||||
public SubSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
public SubSettingViewModel()
|
||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_updateView = updateView;
|
|
||||||
|
|
||||||
var canEditRemove = this.WhenAnyValue(
|
var canEditRemove = this.WhenAnyValue(
|
||||||
x => x.SelectedSource,
|
x => x.SelectedSource,
|
||||||
@@ -38,7 +40,7 @@ public class SubSettingViewModel : MyReactiveObject
|
|||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
SubShareCmd = ReactiveCommand.CreateFromTask(async () =>
|
SubShareCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await _updateView?.Invoke(EViewAction.ShareSub, SelectedSource?.Url);
|
await ShareSubInteraction.Handle(SelectedSource?.Url);
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
|
|
||||||
_ = Init();
|
_ = Init();
|
||||||
@@ -72,7 +74,8 @@ public class SubSettingViewModel : MyReactiveObject
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (await _updateView?.Invoke(EViewAction.SubEditWindow, item) == true)
|
var subEditViewModel = new SubEditViewModel(item);
|
||||||
|
if (await AppManager.Instance.WindowDialog.ShowDialogAsync(subEditViewModel) == true)
|
||||||
{
|
{
|
||||||
await RefreshSubItems();
|
await RefreshSubItems();
|
||||||
IsModified = true;
|
IsModified = true;
|
||||||
@@ -81,7 +84,7 @@ public class SubSettingViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task DeleteSubAsync()
|
private async Task DeleteSubAsync()
|
||||||
{
|
{
|
||||||
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
if (await ShowYesNoInteraction.Handle(ResUI.RemoveServer) == false)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ public partial class App : Application
|
|||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
{
|
{
|
||||||
|
var viewLocator = SimpleViewLocator.Instance;
|
||||||
|
DataTemplates.Add(viewLocator);
|
||||||
|
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
if (!Design.IsDesignMode)
|
if (!Design.IsDesignMode)
|
||||||
@@ -23,7 +26,9 @@ public partial class App : Application
|
|||||||
DataContext = StatusBarViewModel.Instance;
|
DataContext = StatusBarViewModel.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mainWindow = new MainWindow();
|
var mainWindowViewModel = new MainWindowViewModel();
|
||||||
|
var mainWindow = (MainWindow)viewLocator.Build(mainWindowViewModel);
|
||||||
|
mainWindow.ViewModel = mainWindowViewModel;
|
||||||
desktop.MainWindow = mainWindow;
|
desktop.MainWindow = mainWindow;
|
||||||
|
|
||||||
if (OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsMacOS())
|
||||||
|
|||||||
69
v2rayN/v2rayN.Desktop/Common/SimpleViewLocator.cs
Normal file
69
v2rayN/v2rayN.Desktop/Common/SimpleViewLocator.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using Avalonia.Controls.Templates;
|
||||||
|
using v2rayN.Desktop.ViewModels;
|
||||||
|
using v2rayN.Desktop.Views;
|
||||||
|
|
||||||
|
namespace v2rayN.Desktop.Common;
|
||||||
|
|
||||||
|
public class SimpleViewLocator : IDataTemplate
|
||||||
|
{
|
||||||
|
private static readonly Lazy<SimpleViewLocator> _instance = new(() => new SimpleViewLocator());
|
||||||
|
|
||||||
|
private readonly Dictionary<Type, Func<Control?>> _locator = new();
|
||||||
|
|
||||||
|
private SimpleViewLocator()
|
||||||
|
{
|
||||||
|
RegisterViewFactory<AddGroupServerViewModel, AddGroupServerWindow>();
|
||||||
|
RegisterViewFactory<AddServer2ViewModel, AddServer2Window>();
|
||||||
|
RegisterViewFactory<AddServerViewModel, AddServerWindow>();
|
||||||
|
RegisterViewFactory<BackupAndRestoreViewModel, BackupAndRestoreView>();
|
||||||
|
RegisterViewFactory<CheckUpdateViewModel, CheckUpdateView>();
|
||||||
|
RegisterViewFactory<ClashConnectionsViewModel, ClashConnectionsView>();
|
||||||
|
RegisterViewFactory<ClashProxiesViewModel, ClashProxiesView>();
|
||||||
|
RegisterViewFactory<DNSSettingViewModel, DNSSettingWindow>();
|
||||||
|
RegisterViewFactory<FullConfigTemplateViewModel, FullConfigTemplateWindow>();
|
||||||
|
RegisterViewFactory<GlobalHotkeySettingViewModel, GlobalHotkeySettingWindow>();
|
||||||
|
RegisterViewFactory<MainWindowViewModel, MainWindow>();
|
||||||
|
RegisterViewFactory<MsgViewModel, MsgView>();
|
||||||
|
RegisterViewFactory<OptionSettingViewModel, OptionSettingWindow>();
|
||||||
|
RegisterViewFactory<ProfilesSelectViewModel, ProfilesSelectWindow>();
|
||||||
|
RegisterViewFactory<ProfilesViewModel, ProfilesView>();
|
||||||
|
RegisterViewFactory<RoutingRuleDetailsViewModel, RoutingRuleDetailsWindow>();
|
||||||
|
RegisterViewFactory<RoutingRuleSettingViewModel, RoutingRuleSettingWindow>();
|
||||||
|
RegisterViewFactory<RoutingSettingViewModel, RoutingSettingWindow>();
|
||||||
|
RegisterViewFactory<StatusBarViewModel, StatusBarView>();
|
||||||
|
RegisterViewFactory<SubEditViewModel, SubEditWindow>();
|
||||||
|
RegisterViewFactory<SubSettingViewModel, SubSettingWindow>();
|
||||||
|
RegisterViewFactory<ThemeSettingViewModel, ThemeSettingView>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SimpleViewLocator Instance => _instance.Value;
|
||||||
|
|
||||||
|
public Control Build(object? data)
|
||||||
|
{
|
||||||
|
if (data is null)
|
||||||
|
{
|
||||||
|
return new TextBlock { Text = "No VM provided" };
|
||||||
|
}
|
||||||
|
|
||||||
|
_locator.TryGetValue(data.GetType(), out var factory);
|
||||||
|
|
||||||
|
return factory?.Invoke() ?? new TextBlock { Text = $"VM Not Registered: {data.GetType()}" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Match(object? data)
|
||||||
|
{
|
||||||
|
return data is MyReactiveObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterViewFactory<TViewModel>(Func<Control> factory) where TViewModel : class
|
||||||
|
{
|
||||||
|
_locator.Add(typeof(TViewModel), factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterViewFactory<TViewModel, TView>()
|
||||||
|
where TViewModel : class
|
||||||
|
where TView : Control, new()
|
||||||
|
{
|
||||||
|
_locator.Add(typeof(TViewModel), () => new TView());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
|
using v2rayN.Desktop.Manager;
|
||||||
using v2rayN.Desktop.Views;
|
using v2rayN.Desktop.Views;
|
||||||
|
|
||||||
namespace v2rayN.Desktop.Common;
|
namespace v2rayN.Desktop.Common;
|
||||||
@@ -7,16 +8,17 @@ internal class UI
|
|||||||
{
|
{
|
||||||
private static readonly string caption = Global.AppName;
|
private static readonly string caption = Global.AppName;
|
||||||
|
|
||||||
public static async Task<ButtonResult> ShowYesNo(Window owner, string msg)
|
public static async Task<ButtonResult> ShowYesNo(string msg)
|
||||||
{
|
{
|
||||||
|
var owner = WindowDialog.TryGetOwnerWindow();
|
||||||
var box = new MessageBoxDialog(caption, msg);
|
var box = new MessageBoxDialog(caption, msg);
|
||||||
var result = await box.ShowDialog<ButtonResult>(owner);
|
var result = await box.ShowDialog<ButtonResult>(owner);
|
||||||
return result == ButtonResult.Yes ? ButtonResult.Yes : ButtonResult.No;
|
return result == ButtonResult.Yes ? ButtonResult.Yes : ButtonResult.No;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string?> OpenFileDialog(Window owner, FilePickerFileType? filter)
|
public static async Task<string?> OpenFileDialog(FilePickerFileType? filter)
|
||||||
{
|
{
|
||||||
var sp = GetStorageProvider(owner);
|
var sp = GetStorageProvider();
|
||||||
if (sp is null)
|
if (sp is null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -32,9 +34,9 @@ internal class UI
|
|||||||
return files.FirstOrDefault()?.TryGetLocalPath();
|
return files.FirstOrDefault()?.TryGetLocalPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string?> SaveFileDialog(Window owner, string filter)
|
public static async Task<string?> SaveFileDialog(string filter)
|
||||||
{
|
{
|
||||||
var sp = GetStorageProvider(owner);
|
var sp = GetStorageProvider();
|
||||||
if (sp is null)
|
if (sp is null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -48,8 +50,9 @@ internal class UI
|
|||||||
return files?.TryGetLocalPath();
|
return files?.TryGetLocalPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IStorageProvider? GetStorageProvider(Window owner)
|
private static IStorageProvider? GetStorageProvider()
|
||||||
{
|
{
|
||||||
|
var owner = WindowDialog.TryGetOwnerWindow();
|
||||||
var topLevel = TopLevel.GetTopLevel(owner);
|
var topLevel = TopLevel.GetTopLevel(owner);
|
||||||
return topLevel?.StorageProvider;
|
return topLevel?.StorageProvider;
|
||||||
}
|
}
|
||||||
|
|||||||
99
v2rayN/v2rayN.Desktop/DesignData/DesignData.cs
Normal file
99
v2rayN/v2rayN.Desktop/DesignData/DesignData.cs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
using ServiceLib.Models.Entities;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using v2rayN.Desktop.Manager;
|
||||||
|
using v2rayN.Desktop.ViewModels;
|
||||||
|
|
||||||
|
namespace v2rayN.Desktop.DesignData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides design-time data for Avalonia XAML previewer.
|
||||||
|
/// Each inner class lazily initializes <see cref="AppManager"/> with a stub config
|
||||||
|
/// so that ViewModel constructors don't fail during design-time rendering.
|
||||||
|
/// </summary>
|
||||||
|
public static class DesignData
|
||||||
|
{
|
||||||
|
// ── Parameterless-constructor ViewModels ───────────────────────────────
|
||||||
|
|
||||||
|
public static MainWindowViewModel? MainWindow { get; } = SafeCreate(CreateMainWindow);
|
||||||
|
|
||||||
|
public static ProfilesViewModel? Profiles { get; } = SafeCreate(() => new ProfilesViewModel());
|
||||||
|
|
||||||
|
public static StatusBarViewModel? StatusBar { get; } = SafeCreate(CreateStatusBar);
|
||||||
|
|
||||||
|
public static MsgViewModel? Msg { get; } = SafeCreate(() => new MsgViewModel());
|
||||||
|
|
||||||
|
public static SubSettingViewModel? SubSetting { get; } = SafeCreate(() => new SubSettingViewModel());
|
||||||
|
|
||||||
|
public static RoutingSettingViewModel? RoutingSetting { get; } = SafeCreate(() => new RoutingSettingViewModel());
|
||||||
|
|
||||||
|
public static ClashProxiesViewModel? ClashProxies { get; } = SafeCreate(() => new ClashProxiesViewModel());
|
||||||
|
|
||||||
|
public static ClashConnectionsViewModel? ClashConnections { get; } = SafeCreate(() => new ClashConnectionsViewModel());
|
||||||
|
|
||||||
|
public static CheckUpdateViewModel? CheckUpdate { get; } = SafeCreate(() => new CheckUpdateViewModel());
|
||||||
|
|
||||||
|
public static DNSSettingViewModel? DNSSetting { get; } = SafeCreate(() => new DNSSettingViewModel());
|
||||||
|
|
||||||
|
public static FullConfigTemplateViewModel? FullConfigTemplate { get; } = SafeCreate(() => new FullConfigTemplateViewModel());
|
||||||
|
|
||||||
|
public static GlobalHotkeySettingViewModel? GlobalHotkeySetting { get; } = SafeCreate(() => new GlobalHotkeySettingViewModel());
|
||||||
|
|
||||||
|
public static OptionSettingViewModel? OptionSetting { get; } = SafeCreate(() => new OptionSettingViewModel());
|
||||||
|
|
||||||
|
public static ProfilesSelectViewModel? ProfilesSelect { get; } = SafeCreate(() => new ProfilesSelectViewModel());
|
||||||
|
|
||||||
|
public static BackupAndRestoreViewModel? BackupAndRestore { get; } = SafeCreate(() => new BackupAndRestoreViewModel());
|
||||||
|
|
||||||
|
public static ThemeSettingViewModel? ThemeSetting { get; } = SafeCreate(() => new ThemeSettingViewModel());
|
||||||
|
|
||||||
|
// ── ViewModels that require constructor parameters ─────────────────────
|
||||||
|
|
||||||
|
public static AddGroupServerViewModel? AddGroupServer { get; } = SafeCreate(() => new AddGroupServerViewModel(new ProfileItem { Remarks = "Design Group", ConfigType = EConfigType.PolicyGroup }));
|
||||||
|
|
||||||
|
public static AddServer2ViewModel? AddServer2 { get; } = SafeCreate(() => new AddServer2ViewModel(new ProfileItem { Remarks = "Design Custom Server", ConfigType = EConfigType.Custom }));
|
||||||
|
|
||||||
|
public static AddServerViewModel? AddServer { get; } = SafeCreate(() => new AddServerViewModel(new ProfileItem { Remarks = "Design VMess Server", ConfigType = EConfigType.VMess, Address = "example.com", Port = 443 }));
|
||||||
|
|
||||||
|
public static RoutingRuleSettingViewModel? RoutingRuleSetting { get; } = SafeCreate(() => new RoutingRuleSettingViewModel(new RoutingItem { Remarks = "Design Routing Rule" }));
|
||||||
|
|
||||||
|
public static RoutingRuleDetailsViewModel? RoutingRuleDetails { get; } = SafeCreate(() => new RoutingRuleDetailsViewModel(new RulesItem { Domain = ["example.com"], OutboundTag = "direct" }));
|
||||||
|
|
||||||
|
public static SubEditViewModel? SubEdit { get; } = SafeCreate(() => new SubEditViewModel(new SubItem { Remarks = "Design Subscription", Url = "https://example.com/sub" }));
|
||||||
|
|
||||||
|
// ── Helper factories ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private static MainWindowViewModel CreateMainWindow()
|
||||||
|
{
|
||||||
|
var vm = new MainWindowViewModel { DesignMode = true };
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StatusBarViewModel CreateStatusBar()
|
||||||
|
{
|
||||||
|
var vm = StatusBarViewModel.Instance;
|
||||||
|
vm.InboundDisplay = "socks:10808";
|
||||||
|
vm.InboundLanDisplay = "http:10809";
|
||||||
|
vm.RunningServerDisplay = "🚀 Design Server (Active)";
|
||||||
|
vm.RunningInfoDisplay = "v2rayN Design Mode";
|
||||||
|
vm.SpeedProxyDisplay = "↑ 1.2 MB/s";
|
||||||
|
vm.SpeedDirectDisplay = "↓ 5.6 MB/s";
|
||||||
|
vm.RoutingItems.Add(new RoutingItem { Remarks = "Default Routing" });
|
||||||
|
vm.RoutingItems.Add(new RoutingItem { Remarks = "Global" });
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static T? SafeCreate<T>(Func<T> factory) where T : class
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AppManager.Instance.InitApp();
|
||||||
|
AppManager.Instance.WindowDialog = new WindowDialog();
|
||||||
|
return factory();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.WriteLine($"[DesignData] Failed to create {typeof(T).Name}: {ex}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ global using System.Collections.Generic;
|
|||||||
global using System.Globalization;
|
global using System.Globalization;
|
||||||
global using System.IO;
|
global using System.IO;
|
||||||
global using System.Linq;
|
global using System.Linq;
|
||||||
|
global using System.Reactive;
|
||||||
global using System.Reactive.Disposables.Fluent;
|
global using System.Reactive.Disposables.Fluent;
|
||||||
global using System.Reactive.Linq;
|
global using System.Reactive.Linq;
|
||||||
global using System.Runtime.Versioning;
|
global using System.Runtime.Versioning;
|
||||||
|
|||||||
68
v2rayN/v2rayN.Desktop/Manager/WindowDialog.cs
Normal file
68
v2rayN/v2rayN.Desktop/Manager/WindowDialog.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using v2rayN.Desktop.Base;
|
||||||
|
using v2rayN.Desktop.Common;
|
||||||
|
|
||||||
|
namespace v2rayN.Desktop.Manager;
|
||||||
|
|
||||||
|
public class WindowDialog : IWindowDialog
|
||||||
|
{
|
||||||
|
public async Task<bool> ShowDialogAsync<TViewModel>(TViewModel vm)
|
||||||
|
where TViewModel : class
|
||||||
|
{
|
||||||
|
var owner = TryGetOwnerWindow();
|
||||||
|
|
||||||
|
var view = SimpleViewLocator.Instance.Build(vm);
|
||||||
|
|
||||||
|
if (view is not WindowBase<TViewModel> window)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.ViewModel = vm;
|
||||||
|
|
||||||
|
if (vm is ServiceLib.Base.ICloseable closeable)
|
||||||
|
{
|
||||||
|
closeable.RequestClose += (_, _) => Dispatch(() => window.Close(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await window.ShowDialog<bool>(owner);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Window TryGetOwnerWindow()
|
||||||
|
{
|
||||||
|
var desktop = Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime
|
||||||
|
?? throw new InvalidOperationException("Application lifetime is not of type IClassicDesktopStyleApplicationLifetime.");
|
||||||
|
var openWindows = desktop.Windows
|
||||||
|
.Where(w => w.IsVisible)
|
||||||
|
.ToArray();
|
||||||
|
if (openWindows.Length == 0)
|
||||||
|
{
|
||||||
|
return desktop.MainWindow
|
||||||
|
?? throw new InvalidOperationException("No open windows and no main window found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openWindows.Length == 1)
|
||||||
|
{
|
||||||
|
return openWindows[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Window.SortWindowsByZOrder(openWindows);
|
||||||
|
|
||||||
|
var activeTopmost = openWindows.Reverse().FirstOrDefault(w => w.IsActive);
|
||||||
|
return activeTopmost ?? openWindows[^1];
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return desktop.Windows.FirstOrDefault(w => w.IsActive)
|
||||||
|
?? desktop.MainWindow
|
||||||
|
?? openWindows[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Dispatch(Action action)
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.InvokeAsync(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using v2rayN.Desktop.Common;
|
using v2rayN.Desktop.Common;
|
||||||
|
using v2rayN.Desktop.Manager;
|
||||||
|
|
||||||
namespace v2rayN.Desktop;
|
namespace v2rayN.Desktop;
|
||||||
|
|
||||||
@@ -48,6 +49,8 @@ internal class Program
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppManager.Instance.WindowDialog = new WindowDialog();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
Width="900"
|
Width="900"
|
||||||
Height="700"
|
Height="700"
|
||||||
x:DataType="vms:AddGroupServerViewModel"
|
x:DataType="vms:AddGroupServerViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.AddGroupServer}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -5,11 +5,6 @@ namespace v2rayN.Desktop.Views;
|
|||||||
public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
||||||
{
|
{
|
||||||
public AddGroupServerWindow()
|
public AddGroupServerWindow()
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddGroupServerWindow(ProfileItem profileItem)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@@ -18,8 +13,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
||||||
|
|
||||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbCoreType.ItemsSource = Global.CoreTypes;
|
cmbCoreType.ItemsSource = Global.CoreTypes;
|
||||||
cmbPolicyGroupType.ItemsSource = new List<string>
|
cmbPolicyGroupType.ItemsSource = new List<string>
|
||||||
{
|
{
|
||||||
@@ -31,6 +24,42 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||||||
};
|
};
|
||||||
cmbFilter.ItemsSource = Global.PolicyGroupDefaultFilterList;
|
cmbFilter.ItemsSource = Global.PolicyGroupDefaultFilterList;
|
||||||
|
|
||||||
|
this.WhenActivated(disposables =>
|
||||||
|
{
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.SelectedValue).DisposeWith(disposables);
|
||||||
|
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Filter, v => v.cmbFilter.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.AddCmd, v => v.menuAddChildServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveDownCmd, v => v.menuMoveDown).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(v => v.ViewModel.SelectedSource)
|
||||||
|
.WhereNotNull()
|
||||||
|
.Subscribe(InitializeData)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Context menu actions that require custom logic (Add, SelectAll)
|
||||||
|
menuSelectAllChild.Click += (s, e) => lstChild.SelectAll();
|
||||||
|
|
||||||
|
// Keyboard shortcuts when focus is within grid
|
||||||
|
AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
|
||||||
|
lstChild.LoadingRow += LstChild_LoadingRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeData(ProfileItem profileItem)
|
||||||
|
{
|
||||||
switch (profileItem.ConfigType)
|
switch (profileItem.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.PolicyGroup:
|
case EConfigType.PolicyGroup:
|
||||||
@@ -46,34 +75,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
|
||||||
{
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.SelectedValue).DisposeWith(disposables);
|
|
||||||
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Filter, v => v.cmbFilter.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveDownCmd, v => v.menuMoveDown).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Context menu actions that require custom logic (Add, SelectAll)
|
|
||||||
menuAddChildServer.Click += MenuAddChild_Click;
|
|
||||||
menuSelectAllChild.Click += (s, e) => lstChild.SelectAll();
|
|
||||||
|
|
||||||
// Keyboard shortcuts when focus is within grid
|
|
||||||
AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
|
|
||||||
lstChild.LoadingRow += LstChild_LoadingRow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LstChild_LoadingRow(object? sender, DataGridRowEventArgs e)
|
private void LstChild_LoadingRow(object? sender, DataGridRowEventArgs e)
|
||||||
@@ -81,17 +82,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||||||
e.Row.Header = $" {e.Row.Index + 1}";
|
e.Row.Header = $" {e.Row.Index + 1}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
txtRemarks.Focus();
|
txtRemarks.Focus();
|
||||||
@@ -145,19 +135,6 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void MenuAddChild_Click(object? sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
|
||||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
|
||||||
selectWindow.AllowMultiSelect(true);
|
|
||||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
|
||||||
if (result == true)
|
|
||||||
{
|
|
||||||
var profiles = await selectWindow.ProfileItems;
|
|
||||||
ViewModel?.ChildItemsObs.AddRange(profiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstChild_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
private void LstChild_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (ViewModel != null)
|
if (ViewModel != null)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
Width="700"
|
Width="700"
|
||||||
Height="500"
|
Height="500"
|
||||||
x:DataType="vms:AddServer2ViewModel"
|
x:DataType="vms:AddServer2ViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.AddServer2}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -6,17 +6,11 @@ namespace v2rayN.Desktop.Views;
|
|||||||
public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
|
public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
|
||||||
{
|
{
|
||||||
public AddServer2Window()
|
public AddServer2Window()
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddServer2Window(ProfileItem profileItem)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
btnCancel.Click += (s, e) => Close();
|
btnCancel.Click += (s, e) => Close();
|
||||||
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != nameof(ECoreType.v2rayN)).ToList().AppendEmpty();
|
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != nameof(ECoreType.v2rayN)).ToList().AppendEmpty();
|
||||||
|
|
||||||
@@ -31,30 +25,15 @@ public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
|
|||||||
this.BindCommand(ViewModel, vm => vm.BrowseServerCmd, v => v.btnBrowse).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.BrowseServerCmd, v => v.btnBrowse).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.btnEdit).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.btnEdit).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.BrowseConfigFileInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var fileName = await UI.OpenFileDialog(null);
|
||||||
|
interaction.SetOutput(fileName);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.BrowseServer:
|
|
||||||
var fileName = await UI.OpenFileDialog(this, null);
|
|
||||||
if (fileName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ViewModel?.BrowseServer(fileName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
txtRemarks.Focus();
|
txtRemarks.Focus();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:views="clr-namespace:v2rayN.Desktop.Views"
|
xmlns:views="clr-namespace:v2rayN.Desktop.Views"
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
Width="900"
|
Width="900"
|
||||||
Height="600"
|
Height="600"
|
||||||
x:DataType="vms:AddServerViewModel"
|
x:DataType="vms:AddServerViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.AddServer}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Reactive.Disposables;
|
||||||
using v2rayN.Desktop.Base;
|
using v2rayN.Desktop.Base;
|
||||||
using v2rayN.Desktop.Common;
|
using v2rayN.Desktop.Common;
|
||||||
|
|
||||||
@@ -6,11 +7,6 @@ namespace v2rayN.Desktop.Views;
|
|||||||
public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||||
{
|
{
|
||||||
public AddServerWindow()
|
public AddServerWindow()
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddServerWindow(ProfileItem profileItem)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@@ -22,8 +18,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||||||
btnGUID.Click += btnGUID_Click;
|
btnGUID.Click += btnGUID_Click;
|
||||||
btnGUID5.Click += btnGUID_Click;
|
btnGUID5.Click += btnGUID_Click;
|
||||||
|
|
||||||
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
|
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
|
||||||
cmbNetwork.ItemsSource = Global.Networks;
|
cmbNetwork.ItemsSource = Global.Networks;
|
||||||
|
|
||||||
@@ -40,8 +34,173 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||||
cmbAlpn.ItemsSource = Global.Alpns;
|
cmbAlpn.ItemsSource = Global.Alpns;
|
||||||
|
|
||||||
var lstStreamSecurity = new List<string> { string.Empty, Global.StreamSecurity };
|
gridTlsMore.IsVisible = false;
|
||||||
|
|
||||||
|
this.WhenActivated(disposables =>
|
||||||
|
{
|
||||||
|
var configTypeBindings = new SerialDisposable().DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Address, v => v.txtAddress.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(v => v.ViewModel.SelectedSource.ConfigType)
|
||||||
|
.Subscribe(configType =>
|
||||||
|
{
|
||||||
|
var currentTypeDisposables = new CompositeDisposable();
|
||||||
|
configTypeBindings.Disposable = currentTypeDisposables;
|
||||||
|
|
||||||
|
switch (configType)
|
||||||
|
{
|
||||||
|
case EConfigType.VMess:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.SelectedValue).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Shadowsocks:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.SOCKS:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.HTTP:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.HttpHeadersJson, v => v.txtHttpHeadersJson.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.VLESS:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Trojan:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Hysteria2:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SalamanderPass, v => v.txtPath7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Hy2RealmUrl, v => v.txtHy2RealmUrl.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.GeckoMinPacketSize, v => v.txtMinGeckoPacketSize.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.GeckoMaxPacketSize, v => v.txtMaxGeckoPacketSize.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.TUIC:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.SelectedValue).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.WireGuard:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgPresharedKey, v => v.txtPreSharedKey9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Anytls:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Naive:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.cmbCongestionControl12.IsEnabled).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.SelectedValue).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.RawHeaderType, v => v.cmbHeaderTypeRaw.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostRaw.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathRaw.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.KcpHeaderType, v => v.cmbHeaderTypeKcp.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.KcpSeed, v => v.txtKcpSeed.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.KcpMtu, v => v.txtKcpMtu.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostWs.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathWs.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostHttpupgrade.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathHttpupgrade.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.XhttpMode, v => v.cmbHeaderTypeXhttp.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostXhttp.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathXhttp.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.XhttpExtra, v => v.txtExtraXhttp.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.GrpcMode, v => v.cmbHeaderTypeGrpc.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.GrpcAuthority, v => v.txtRequestHostGrpc.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.GrpcServiceName, v => v.txtPathGrpc.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.AllowInsecure, v => v.togAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.VerifyPeerCertByName, v => v.txtVerifyPeerCertByName.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
//reality
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(v => v.ViewModel.SelectedSource)
|
||||||
|
.WhereNotNull()
|
||||||
|
.Subscribe(InitializeData)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeData(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
Title = $"{profileItem.ConfigType}";
|
||||||
|
var lstStreamSecurity = new List<string> { string.Empty, Global.StreamSecurity };
|
||||||
switch (profileItem.ConfigType)
|
switch (profileItem.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
@@ -127,159 +286,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
||||||
|
cmbStreamSecurity.SelectedItem = profileItem.StreamSecurity;
|
||||||
gridTlsMore.IsVisible = false;
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
|
||||||
{
|
|
||||||
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Address, v => v.txtAddress.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
switch (profileItem.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.VMess:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.SOCKS:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.HTTP:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.HttpHeadersJson, v => v.txtHttpHeadersJson.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Hysteria2:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SalamanderPass, v => v.txtPath7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Hy2RealmUrl, v => v.txtHy2RealmUrl.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.GeckoMinPacketSize, v => v.txtMinGeckoPacketSize.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.GeckoMaxPacketSize, v => v.txtMaxGeckoPacketSize.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.SelectedValue).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.WireGuard:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgPresharedKey, v => v.txtPreSharedKey9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Anytls:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Naive:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.cmbCongestionControl12.IsEnabled).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.RawHeaderType, v => v.cmbHeaderTypeRaw.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostRaw.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathRaw.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.KcpHeaderType, v => v.cmbHeaderTypeKcp.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.KcpSeed, v => v.txtKcpSeed.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.KcpMtu, v => v.txtKcpMtu.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostWs.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathWs.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostHttpupgrade.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathHttpupgrade.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.XhttpMode, v => v.cmbHeaderTypeXhttp.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostXhttp.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathXhttp.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.XhttpExtra, v => v.txtExtraXhttp.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.GrpcMode, v => v.cmbHeaderTypeGrpc.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.GrpcAuthority, v => v.txtRequestHostGrpc.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.GrpcServiceName, v => v.txtPathGrpc.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.AllowInsecure, v => v.togAllowInsecure.IsChecked).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.VerifyPeerCertByName, v => v.txtVerifyPeerCertByName.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
//reality
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.SelectedValue).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
|
||||||
});
|
|
||||||
|
|
||||||
Title = $"{profileItem.ConfigType}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -3,10 +3,12 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.BackupAndRestore}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<Style Selector="Button">
|
<Style Selector="Button">
|
||||||
@@ -18,8 +20,8 @@
|
|||||||
<StackPanel Margin="{StaticResource Margin4}" DockPanel.Dock="Bottom">
|
<StackPanel Margin="{StaticResource Margin4}" DockPanel.Dock="Bottom">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Name="txtMsg"
|
Name="txtMsg"
|
||||||
HorizontalAlignment="Left"
|
Margin="{StaticResource Margin4}"
|
||||||
Margin="{StaticResource Margin4}" />
|
HorizontalAlignment="Left" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
@@ -41,15 +43,15 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.menuLocalBackup}" />
|
Text="{x:Static resx:ResUI.menuLocalBackup}" />
|
||||||
<Button
|
<Button
|
||||||
Name="menuLocalBackup"
|
Name="menuLocalBackup"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Content="{x:Static resx:ResUI.menuLocalBackup}" />
|
Content="{x:Static resx:ResUI.menuLocalBackup}" />
|
||||||
|
|
||||||
<Separator
|
<Separator
|
||||||
@@ -61,15 +63,15 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.menuLocalRestore}" />
|
Text="{x:Static resx:ResUI.menuLocalRestore}" />
|
||||||
<Button
|
<Button
|
||||||
Name="menuLocalRestore"
|
Name="menuLocalRestore"
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Content="{x:Static resx:ResUI.menuLocalRestore}" />
|
Content="{x:Static resx:ResUI.menuLocalRestore}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
@@ -87,11 +89,9 @@
|
|||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<TextBlock Margin="{StaticResource Margin4}" Text="{x:Static resx:ResUI.menuRemoteBackupAndRestore}" />
|
<TextBlock Margin="{StaticResource Margin4}" Text="{x:Static resx:ResUI.menuRemoteBackupAndRestore}" />
|
||||||
|
|
||||||
<Button
|
<Button Margin="{StaticResource MarginLr8}" Classes="IconButton">
|
||||||
Classes="IconButton"
|
|
||||||
Margin="{StaticResource MarginLr8}">
|
|
||||||
<Button.Content>
|
<Button.Content>
|
||||||
<PathIcon Data="{StaticResource SemiIconMore}" >
|
<PathIcon Data="{StaticResource SemiIconMore}">
|
||||||
<PathIcon.RenderTransform>
|
<PathIcon.RenderTransform>
|
||||||
<RotateTransform Angle="90" />
|
<RotateTransform Angle="90" />
|
||||||
</PathIcon.RenderTransform>
|
</PathIcon.RenderTransform>
|
||||||
@@ -104,67 +104,67 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.LvWebDavUrl}" />
|
Text="{x:Static resx:ResUI.LvWebDavUrl}" />
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtWebDavUrl"
|
x:Name="txtWebDavUrl"
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.LvWebDavUserName}" />
|
Text="{x:Static resx:ResUI.LvWebDavUserName}" />
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtWebDavUserName"
|
x:Name="txtWebDavUserName"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
Margin="{StaticResource Margin4}"
|
||||||
Margin="{StaticResource Margin4}" />
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.LvWebDavPassword}" />
|
Text="{x:Static resx:ResUI.LvWebDavPassword}" />
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtWebDavPassword"
|
x:Name="txtWebDavPassword"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
Margin="{StaticResource Margin4}"
|
||||||
Margin="{StaticResource Margin4}" />
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.LvWebDavDirName}" />
|
Text="{x:Static resx:ResUI.LvWebDavDirName}" />
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtWebDavDirName"
|
x:Name="txtWebDavDirName"
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
Margin="{StaticResource Margin4}"
|
||||||
Margin="{StaticResource Margin4}" />
|
VerticalAlignment="Center" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
x:Name="menuWebDavCheck"
|
x:Name="menuWebDavCheck"
|
||||||
Grid.Row="4"
|
Grid.Row="4"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
Content="{x:Static resx:ResUI.LvWebDavCheck}" />
|
Content="{x:Static resx:ResUI.LvWebDavCheck}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -177,15 +177,15 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.menuRemoteBackup}" />
|
Text="{x:Static resx:ResUI.menuRemoteBackup}" />
|
||||||
<Button
|
<Button
|
||||||
Name="menuRemoteBackup"
|
Name="menuRemoteBackup"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Content="{x:Static resx:ResUI.menuRemoteBackup}" />
|
Content="{x:Static resx:ResUI.menuRemoteBackup}" />
|
||||||
|
|
||||||
<Separator
|
<Separator
|
||||||
@@ -196,15 +196,15 @@
|
|||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.menuRemoteRestore}" />
|
Text="{x:Static resx:ResUI.menuRemoteRestore}" />
|
||||||
<Button
|
<Button
|
||||||
Name="menuRemoteRestore"
|
Name="menuRemoteRestore"
|
||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Content="{x:Static resx:ResUI.menuRemoteRestore}" />
|
Content="{x:Static resx:ResUI.menuRemoteRestore}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -4,23 +4,12 @@ namespace v2rayN.Desktop.Views;
|
|||||||
|
|
||||||
public partial class BackupAndRestoreView : ReactiveUserControl<BackupAndRestoreViewModel>
|
public partial class BackupAndRestoreView : ReactiveUserControl<BackupAndRestoreViewModel>
|
||||||
{
|
{
|
||||||
private Window? _window;
|
|
||||||
|
|
||||||
public BackupAndRestoreView()
|
public BackupAndRestoreView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
|
||||||
|
|
||||||
public BackupAndRestoreView(Window window)
|
|
||||||
{
|
|
||||||
_window = window;
|
|
||||||
|
|
||||||
InitializeComponent();
|
|
||||||
menuLocalBackup.Click += MenuLocalBackup_Click;
|
menuLocalBackup.Click += MenuLocalBackup_Click;
|
||||||
menuLocalRestore.Click += MenuLocalRestore_Click;
|
menuLocalRestore.Click += MenuLocalRestore_Click;
|
||||||
|
|
||||||
ViewModel = new BackupAndRestoreViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.Bind(ViewModel, vm => vm.OperationMsg, v => v.txtMsg.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.OperationMsg, v => v.txtMsg.Text).DisposeWith(disposables);
|
||||||
@@ -39,7 +28,7 @@ public partial class BackupAndRestoreView : ReactiveUserControl<BackupAndRestore
|
|||||||
|
|
||||||
private async void MenuLocalBackup_Click(object? sender, RoutedEventArgs e)
|
private async void MenuLocalBackup_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var fileName = await UI.SaveFileDialog(_window, "Zip|*.zip");
|
var fileName = await UI.SaveFileDialog("Zip|*.zip");
|
||||||
if (fileName.IsNullOrEmpty())
|
if (fileName.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -50,7 +39,7 @@ public partial class BackupAndRestoreView : ReactiveUserControl<BackupAndRestore
|
|||||||
|
|
||||||
private async void MenuLocalRestore_Click(object? sender, RoutedEventArgs e)
|
private async void MenuLocalRestore_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var fileName = await UI.OpenFileDialog(_window, null);
|
var fileName = await UI.OpenFileDialog(null);
|
||||||
if (fileName.IsNullOrEmpty())
|
if (fileName.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -58,9 +47,4 @@ public partial class BackupAndRestoreView : ReactiveUserControl<BackupAndRestore
|
|||||||
|
|
||||||
ViewModel?.LocalRestore(fileName);
|
ViewModel?.LocalRestore(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
d:DesignHeight="600"
|
d:DesignHeight="600"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:DataType="vms:CheckUpdateViewModel"
|
x:DataType="vms:CheckUpdateViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.CheckUpdate}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
ViewModel = new CheckUpdateViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
|
||||||
@@ -17,9 +15,4 @@ public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
|
|||||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,32 +3,34 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:DataType="vms:ClashConnectionsViewModel"
|
x:DataType="vms:ClashConnectionsViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.ClashConnections}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<DockPanel Margin="2">
|
<DockPanel Margin="2">
|
||||||
<WrapPanel
|
<WrapPanel
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
DockPanel.Dock="Top"
|
DockPanel.Dock="Top"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtHostFilter"
|
x:Name="txtHostFilter"
|
||||||
Width="200"
|
Width="200"
|
||||||
VerticalContentAlignment="Center"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
PlaceholderText="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
|
PlaceholderText="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnConnectionCloseAll"
|
x:Name="btnConnectionCloseAll"
|
||||||
Classes="IconButton Success"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
Classes="IconButton Success"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuConnectionCloseAll}">
|
ToolTip.Tip="{x:Static resx:ResUI.menuConnectionCloseAll}">
|
||||||
<Button.Content>
|
<Button.Content>
|
||||||
<PathIcon Data="{StaticResource SemiIconClose}" />
|
<PathIcon Data="{StaticResource SemiIconClose}" />
|
||||||
@@ -37,8 +39,8 @@
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnAutofitColumnWidth"
|
x:Name="btnAutofitColumnWidth"
|
||||||
Classes="IconButton Success"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
Classes="IconButton Success"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
||||||
<Button.Content>
|
<Button.Content>
|
||||||
<PathIcon Data="{StaticResource SemiIconExpand}" />
|
<PathIcon Data="{StaticResource SemiIconExpand}" />
|
||||||
@@ -46,13 +48,13 @@
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbAutoRefresh}" />
|
Text="{x:Static resx:ResUI.TbAutoRefresh}" />
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
x:Name="togAutoRefresh"
|
x:Name="togAutoRefresh"
|
||||||
HorizontalAlignment="Left"
|
Margin="{StaticResource MarginLr8}"
|
||||||
Margin="{StaticResource MarginLr8}" />
|
HorizontalAlignment="Left" />
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ public partial class ClashConnectionsView : ReactiveUserControl<ClashConnections
|
|||||||
|
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
|
|
||||||
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
|
|
||||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
@@ -36,11 +35,6 @@ public partial class ClashConnectionsView : ReactiveUserControl<ClashConnections
|
|||||||
RestoreUI();
|
RestoreUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
|
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
AutofitColumnWidth();
|
AutofitColumnWidth();
|
||||||
|
|||||||
@@ -4,12 +4,14 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:conv="using:v2rayN.Desktop.Converters"
|
xmlns:conv="using:v2rayN.Desktop.Converters"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:DataType="vms:ClashProxiesViewModel"
|
x:DataType="vms:ClashProxiesViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.ClashProxies}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewMode
|
|||||||
public ClashProxiesView()
|
public ClashProxiesView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
|
|
||||||
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
|
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
|
||||||
KeyDown += ClashProxiesView_KeyDown;
|
KeyDown += ClashProxiesView_KeyDown;
|
||||||
|
|
||||||
@@ -29,11 +28,6 @@ public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewMode
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClashProxiesView_KeyDown(object? sender, KeyEventArgs e)
|
private void ClashProxiesView_KeyDown(object? sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
switch (e.Key)
|
switch (e.Key)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:local="using:v2rayN.Desktop.Views"
|
xmlns:local="using:v2rayN.Desktop.Views"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
Width="900"
|
Width="900"
|
||||||
Height="600"
|
Height="600"
|
||||||
x:DataType="vms:DNSSettingViewModel"
|
x:DataType="vms:DNSSettingViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.DNSSetting}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
|||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
btnCancel.Click += (s, e) => Close();
|
btnCancel.Click += (s, e) => Close();
|
||||||
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy;
|
cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy;
|
||||||
cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy;
|
cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy;
|
||||||
@@ -64,26 +63,15 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
|||||||
|
|
||||||
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
|
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
|
||||||
.Select(b => !b)
|
.Select(b => !b)
|
||||||
.BindTo(this.FindControl<TextBlock>("txtBasicDNSSettingsInvalid"), t => t.IsVisible);
|
.BindTo(txtBasicDNSSettingsInvalid, t => t.IsVisible);
|
||||||
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
|
this.WhenAnyValue(x => x.ViewModel.IsSimpleDNSEnabled)
|
||||||
.Select(b => !b)
|
.Select(b => !b)
|
||||||
.BindTo(this.FindControl<TextBlock>("txtAdvancedDNSSettingsInvalid"), t => t.IsVisible);
|
.BindTo(txtAdvancedDNSSettingsInvalid, t => t.IsVisible);
|
||||||
this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridBasicDNSSettings.IsEnabled).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridBasicDNSSettings.IsEnabled).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridAdvancedDNSSettings.IsEnabled).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.IsSimpleDNSEnabled, v => v.gridAdvancedDNSSettings.IsEnabled).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void linkDnsObjectDoc_Click(object? sender, RoutedEventArgs e)
|
private void linkDnsObjectDoc_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ProcUtils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:views="clr-namespace:v2rayN.Desktop.Views"
|
xmlns:views="clr-namespace:v2rayN.Desktop.Views"
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
Width="900"
|
Width="900"
|
||||||
Height="600"
|
Height="600"
|
||||||
x:DataType="vms:FullConfigTemplateViewModel"
|
x:DataType="vms:FullConfigTemplateViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.FullConfigTemplate}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ public partial class FullConfigTemplateWindow : WindowBase<FullConfigTemplateVie
|
|||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
btnCancel.Click += (_, _) => Close();
|
btnCancel.Click += (_, _) => Close();
|
||||||
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
@@ -32,17 +31,6 @@ public partial class FullConfigTemplateWindow : WindowBase<FullConfigTemplateVie
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void linkFullConfigTemplateDoc_Click(object sender, RoutedEventArgs e)
|
private void linkFullConfigTemplateDoc_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ProcUtils.ProcessStart("https://github.com/2dust/v2rayN/wiki/Description-of-some-ui#%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF%E8%AE%BE%E7%BD%AE");
|
ProcUtils.ProcessStart("https://github.com/2dust/v2rayN/wiki/Description-of-some-ui#%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF%E8%AE%BE%E7%BD%AE");
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
Width="700"
|
Width="700"
|
||||||
Height="500"
|
Height="500"
|
||||||
x:DataType="vms:GlobalHotkeySettingViewModel"
|
x:DataType="vms:GlobalHotkeySettingViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.GlobalHotkeySetting}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
btnReset.Click += btnReset_Click;
|
btnReset.Click += btnReset_Click;
|
||||||
|
|
||||||
HotkeyManager.Instance.IsPause = true;
|
HotkeyManager.Instance.IsPause = true;
|
||||||
@@ -23,21 +21,10 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
|
|||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
BindingData();
|
||||||
});
|
});
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
BindingData();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Init()
|
private void Init()
|
||||||
@@ -67,9 +54,13 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (ViewModel == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var item = ViewModel?.GetKeyEventItem((EGlobalHotkey)txtBox.Tag);
|
var item = ViewModel.GetKeyEventItem((EGlobalHotkey)txtBox.Tag!);
|
||||||
var modifierKeys = new Key[] { Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift, Key.LeftAlt, Key.RightAlt, Key.LWin, Key.RWin };
|
var modifierKeys = new[] { Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift, Key.LeftAlt, Key.RightAlt, Key.LWin, Key.RWin };
|
||||||
|
|
||||||
item.KeyCode = (int)(e.Key == Key.System ? modifierKeys.Contains(Key.System) ? Key.None : Key.System : modifierKeys.Contains(e.Key) ? Key.None : e.Key);
|
item.KeyCode = (int)(e.Key == Key.System ? modifierKeys.Contains(Key.System) ? Key.None : Key.System : modifierKeys.Contains(e.Key) ? Key.None : e.Key);
|
||||||
item.Alt = (e.KeyModifiers & KeyModifiers.Alt) == KeyModifiers.Alt;
|
item.Alt = (e.KeyModifiers & KeyModifiers.Alt) == KeyModifiers.Alt;
|
||||||
@@ -109,17 +100,17 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
|
|||||||
|
|
||||||
if (item.Control)
|
if (item.Control)
|
||||||
{
|
{
|
||||||
res.Append($"{KeyModifiers.Control} +");
|
res.Append($"{KeyModifiers.Control} + ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.Shift)
|
if (item.Shift)
|
||||||
{
|
{
|
||||||
res.Append($"{KeyModifiers.Shift} +");
|
res.Append($"{KeyModifiers.Shift} + ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.Alt)
|
if (item.Alt)
|
||||||
{
|
{
|
||||||
res.Append($"{KeyModifiers.Alt} +");
|
res.Append($"{KeyModifiers.Alt} + ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
|
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:ae="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
|
xmlns:ae="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib">
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800">
|
||||||
|
|
||||||
<ae:TextEditor
|
<ae:TextEditor
|
||||||
Name="Editor"
|
Name="Editor"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public partial class JsonEditor : UserControl
|
|||||||
public JsonEditor()
|
public JsonEditor()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
var isDark = Application.Current?.ActualThemeVariant != ThemeVariant.Light;
|
var isDark = Application.Current?.ActualThemeVariant != ThemeVariant.Light;
|
||||||
Editor.SyntaxHighlighting = isDark ? SHighlightingDark.Value : SHighlightingLight.Value;
|
Editor.SyntaxHighlighting = isDark ? SHighlightingDark.Value : SHighlightingLight.Value;
|
||||||
Editor.TextArea.TextView.Options.EnableHyperlinks = false;
|
Editor.TextArea.TextView.Options.EnableHyperlinks = false;
|
||||||
@@ -46,6 +47,11 @@ public partial class JsonEditor : UserControl
|
|||||||
Editor.Text = text ?? string.Empty;
|
Editor.Text = text ?? string.Empty;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
Text = "{\n \"key\": \"value\",\n \"number\": 123,\n \"boolean\": true,\n \"nullValue\": null\n}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IHighlightingDefinition BuildHighlighting(bool dark)
|
private static IHighlightingDefinition BuildHighlighting(bool dark)
|
||||||
|
|||||||
@@ -3,16 +3,17 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
|
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:view="using:v2rayN.Desktop.Views"
|
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
Title="v2rayN"
|
Title="v2rayN"
|
||||||
Width="1200"
|
Width="1200"
|
||||||
Height="800"
|
Height="800"
|
||||||
MinWidth="600"
|
MinWidth="600"
|
||||||
x:DataType="vms:MainWindowViewModel"
|
x:DataType="vms:MainWindowViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.MainWindow}"
|
||||||
Icon="/Assets/NotifyIcon1.ico"
|
Icon="/Assets/NotifyIcon1.ico"
|
||||||
ShowInTaskbar="True"
|
ShowInTaskbar="True"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
@@ -111,7 +112,7 @@
|
|||||||
IsVisible="False" />
|
IsVisible="False" />
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
<view:StatusBarView DockPanel.Dock="Bottom" />
|
<ContentControl x:Name="contentStatusBarView" DockPanel.Dock="Bottom" />
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid
|
<Grid
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Reactive.Disposables;
|
||||||
using Avalonia.Controls.Notifications;
|
using Avalonia.Controls.Notifications;
|
||||||
using DialogHostAvalonia;
|
using DialogHostAvalonia;
|
||||||
using v2rayN.Desktop.Base;
|
using v2rayN.Desktop.Base;
|
||||||
@@ -9,6 +10,7 @@ namespace v2rayN.Desktop.Views;
|
|||||||
public partial class MainWindow : WindowBase<MainWindowViewModel>
|
public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||||
{
|
{
|
||||||
private static Config _config;
|
private static Config _config;
|
||||||
|
private readonly SerialDisposable _layoutBindingsDisposable = new();
|
||||||
private readonly WindowNotificationManager? _manager;
|
private readonly WindowNotificationManager? _manager;
|
||||||
private CheckUpdateView? _checkUpdateView;
|
private CheckUpdateView? _checkUpdateView;
|
||||||
private BackupAndRestoreView? _backupAndRestoreView;
|
private BackupAndRestoreView? _backupAndRestoreView;
|
||||||
@@ -29,35 +31,6 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
|
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
|
||||||
menuClose.Click += MenuClose_Click;
|
menuClose.Click += MenuClose_Click;
|
||||||
|
|
||||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
switch (_config.UiItem.MainGirdOrientation)
|
|
||||||
{
|
|
||||||
case EGirdOrientation.Horizontal:
|
|
||||||
tabProfiles.Content ??= new ProfilesView(this);
|
|
||||||
tabMsgView.Content ??= new MsgView();
|
|
||||||
tabClashProxies.Content ??= new ClashProxiesView();
|
|
||||||
tabClashConnections.Content ??= new ClashConnectionsView();
|
|
||||||
gridMain.IsVisible = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EGirdOrientation.Vertical:
|
|
||||||
tabProfiles1.Content ??= new ProfilesView(this);
|
|
||||||
tabMsgView1.Content ??= new MsgView();
|
|
||||||
tabClashProxies1.Content ??= new ClashProxiesView();
|
|
||||||
tabClashConnections1.Content ??= new ClashConnectionsView();
|
|
||||||
gridMain1.IsVisible = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EGirdOrientation.Tab:
|
|
||||||
default:
|
|
||||||
tabProfiles2.Content ??= new ProfilesView(this);
|
|
||||||
tabMsgView2.Content ??= new MsgView();
|
|
||||||
tabClashProxies2.Content ??= new ClashProxiesView();
|
|
||||||
tabClashConnections2.Content ??= new ClashConnectionsView();
|
|
||||||
gridMain2.IsVisible = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
conTheme.Content ??= new ThemeSettingView();
|
conTheme.Content ??= new ThemeSettingView();
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
@@ -105,29 +78,41 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.BlNewUpdate, v => v.btnNewUpdate.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.BlNewUpdate, v => v.btnNewUpdate.IsVisible).DisposeWith(disposables);
|
||||||
|
|
||||||
switch (_config.UiItem.MainGirdOrientation)
|
this.OneWayBind(ViewModel, vm => vm.StatusBarViewModel, v => v.contentStatusBarView.Content).DisposeWith(disposables);
|
||||||
|
|
||||||
|
_layoutBindingsDisposable.DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(v => v.ViewModel.MainGirdOrientation)
|
||||||
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
|
.Subscribe(UpdateLayout)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ReadTextFromClipboardInteraction.RegisterHandler(async interaction =>
|
||||||
{
|
{
|
||||||
case EGirdOrientation.Horizontal:
|
var result = await AvaUtils.GetClipboardData(this);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(disposables);
|
interaction.SetOutput(result);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).DisposeWith(disposables);
|
}).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.IsVisible).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain.SelectedIndex).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EGirdOrientation.Vertical:
|
ViewModel.ScanScreenInteraction.RegisterHandler(async interaction =>
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(disposables);
|
{
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(disposables);
|
ShowHideWindow(false);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(disposables);
|
await Task.Delay(200);
|
||||||
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain1.SelectedIndex).DisposeWith(disposables);
|
var result = QRCodeAvaloniaUtils.CaptureScreen();
|
||||||
break;
|
ShowHideWindow(true);
|
||||||
|
interaction.SetOutput(result);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
case EGirdOrientation.Tab:
|
ViewModel.BrowseImageFileInteraction.RegisterHandler(async interaction =>
|
||||||
default:
|
{
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(disposables);
|
var result = await UI.OpenFileDialog(null);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(disposables);
|
interaction.SetOutput(result);
|
||||||
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
|
}).DisposeWith(disposables);
|
||||||
break;
|
|
||||||
}
|
ViewModel.ShowHideWindowInteraction.RegisterHandler(interaction =>
|
||||||
|
{
|
||||||
|
ShowHideWindow(interaction.Input);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
AppEvents.SendSnackMsgRequested
|
AppEvents.SendSnackMsgRequested
|
||||||
.AsObservable()
|
.AsObservable()
|
||||||
@@ -146,20 +131,17 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
.Subscribe(Shutdown)
|
.Subscribe(Shutdown)
|
||||||
.DisposeWith(disposables);
|
.DisposeWith(disposables);
|
||||||
|
|
||||||
AppEvents.ShowHideWindowRequested
|
|
||||||
.AsObservable()
|
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
.Subscribe(ShowHideWindow)
|
|
||||||
.DisposeWith(disposables);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Utils.IsWindows())
|
if (Utils.IsWindows())
|
||||||
{
|
{
|
||||||
Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
||||||
|
|
||||||
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
|
if (!Design.IsDesignMode)
|
||||||
HotkeyManager.Instance.Init(_config, OnHotkeyHandler);
|
{
|
||||||
|
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
|
||||||
|
HotkeyManager.Instance.Init(_config, OnHotkeyHandler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -186,72 +168,10 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
|
|
||||||
private async Task DelegateSnackMsg(string content)
|
private async Task DelegateSnackMsg(string content)
|
||||||
{
|
{
|
||||||
_manager?.Show(new Notification(null, content, NotificationType.Information));
|
_manager?.Show(new Avalonia.Controls.Notifications.Notification(null, content, NotificationType.Information));
|
||||||
await Task.CompletedTask;
|
await Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.AddServerWindow:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new AddServerWindow((ProfileItem)obj).ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.AddServer2Window:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new AddServer2Window((ProfileItem)obj).ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.AddGroupServerWindow:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new AddGroupServerWindow((ProfileItem)obj).ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.DNSSettingWindow:
|
|
||||||
return await new DNSSettingWindow().ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.FullConfigTemplateWindow:
|
|
||||||
return await new FullConfigTemplateWindow().ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.RoutingSettingWindow:
|
|
||||||
return await new RoutingSettingWindow().ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.OptionSettingWindow:
|
|
||||||
return await new OptionSettingWindow().ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.GlobalHotkeySettingWindow:
|
|
||||||
return await new GlobalHotkeySettingWindow().ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.SubSettingWindow:
|
|
||||||
return await new SubSettingWindow().ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.ScanScreenTask:
|
|
||||||
await ScanScreenTaskAsync();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.ScanImageTask:
|
|
||||||
await ScanImageTaskAsync();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.AddServerViaClipboard:
|
|
||||||
await AddServerViaClipboardAsync();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHotkeyHandler(EGlobalHotkey e)
|
private void OnHotkeyHandler(EGlobalHotkey e)
|
||||||
{
|
{
|
||||||
switch (e)
|
switch (e)
|
||||||
@@ -351,23 +271,10 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
ShowHideWindow(true);
|
ShowHideWindow(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ScanImageTaskAsync()
|
|
||||||
{
|
|
||||||
var fileName = await UI.OpenFileDialog(this, null);
|
|
||||||
if (fileName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ViewModel != null)
|
|
||||||
{
|
|
||||||
await ViewModel.ScanImageResult(fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MenuCheckUpdate_Click(object? sender, RoutedEventArgs e)
|
private void MenuCheckUpdate_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_checkUpdateView ??= new CheckUpdateView();
|
_checkUpdateView ??= new CheckUpdateView();
|
||||||
|
_checkUpdateView.ViewModel = ViewModel?.CheckUpdateViewModel;
|
||||||
DialogHost.Show(_checkUpdateView);
|
DialogHost.Show(_checkUpdateView);
|
||||||
|
|
||||||
AppEvents.HasUpdateNotified.Publish(false);
|
AppEvents.HasUpdateNotified.Publish(false);
|
||||||
@@ -375,21 +282,29 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
|
|
||||||
private void MenuBackupAndRestore_Click(object? sender, RoutedEventArgs e)
|
private void MenuBackupAndRestore_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_backupAndRestoreView ??= new BackupAndRestoreView(this);
|
_backupAndRestoreView ??= new BackupAndRestoreView();
|
||||||
|
_backupAndRestoreView.ViewModel = ViewModel?.BackupAndRestoreViewModel;
|
||||||
DialogHost.Show(_backupAndRestoreView);
|
DialogHost.Show(_backupAndRestoreView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void MenuClose_Click(object? sender, RoutedEventArgs e)
|
private async void MenuClose_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (await UI.ShowYesNo(this, ResUI.menuExitTips) != ButtonResult.Yes)
|
try
|
||||||
{
|
{
|
||||||
return;
|
if (await UI.ShowYesNo(ResUI.menuExitTips) != ButtonResult.Yes)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_blCloseByUser = true;
|
||||||
|
StorageUI();
|
||||||
|
|
||||||
|
await AppManager.Instance.AppExitAsync(true);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
_blCloseByUser = true;
|
|
||||||
StorageUI();
|
|
||||||
|
|
||||||
await AppManager.Instance.AppExitAsync(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Shutdown(bool obj)
|
private void Shutdown(bool obj)
|
||||||
@@ -485,6 +400,54 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateLayout(EGirdOrientation orientation)
|
||||||
|
{
|
||||||
|
var currentLayoutDisposables = new CompositeDisposable();
|
||||||
|
_layoutBindingsDisposable.Disposable = currentLayoutDisposables;
|
||||||
|
|
||||||
|
gridMain.IsVisible = orientation == EGirdOrientation.Horizontal;
|
||||||
|
gridMain1.IsVisible = orientation == EGirdOrientation.Vertical;
|
||||||
|
gridMain2.IsVisible = orientation == EGirdOrientation.Tab;
|
||||||
|
|
||||||
|
switch (orientation)
|
||||||
|
{
|
||||||
|
case EGirdOrientation.Horizontal:
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ProfilesViewModel, v => v.tabProfiles.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.MsgViewModel, v => v.tabMsgView.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ClashProxiesViewModel, v => v.tabClashProxies.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ClashConnectionsViewModel, v => v.tabClashConnections.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.IsVisible).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain.SelectedIndex).DisposeWith(currentLayoutDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EGirdOrientation.Vertical:
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ProfilesViewModel, v => v.tabProfiles1.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.MsgViewModel, v => v.tabMsgView1.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ClashProxiesViewModel, v => v.tabClashProxies1.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ClashConnectionsViewModel, v => v.tabClashConnections1.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain1.SelectedIndex).DisposeWith(currentLayoutDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EGirdOrientation.Tab:
|
||||||
|
default:
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ProfilesViewModel, v => v.tabProfiles2.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.MsgViewModel, v => v.tabMsgView2.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ClashProxiesViewModel, v => v.tabClashProxies2.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ClashConnectionsViewModel, v => v.tabClashConnections2.Content).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(currentLayoutDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(currentLayoutDisposables);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
RestoreUI();
|
||||||
|
}
|
||||||
|
|
||||||
private void AddHelpMenuItem()
|
private void AddHelpMenuItem()
|
||||||
{
|
{
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo();
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo();
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ public partial class MessageBoxDialog : Window
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
caption = "Design Caption";
|
||||||
|
message = "Design Message";
|
||||||
|
}
|
||||||
|
|
||||||
Title = caption;
|
Title = caption;
|
||||||
txtMessage.Text = message;
|
txtMessage.Text = message;
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:avaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
|
xmlns:avaloniaEdit="clr-namespace:AvaloniaEdit;assembly=AvaloniaEdit"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.Msg}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<DockPanel Margin="2">
|
<DockPanel Margin="2">
|
||||||
<WrapPanel
|
<WrapPanel
|
||||||
|
|||||||
@@ -10,12 +10,19 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
txtMsg.TextArea.TextView.Options.EnableHyperlinks = false;
|
txtMsg.TextArea.TextView.Options.EnableHyperlinks = false;
|
||||||
ViewModel = new MsgViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.DispatcherShowMsgInteraction.RegisterHandler(interaction =>
|
||||||
|
{
|
||||||
|
var msg = interaction.Input;
|
||||||
|
Dispatcher.UIThread.Post(() => ShowMsg(msg),
|
||||||
|
DispatcherPriority.ApplicationIdle);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
|
|
||||||
TextEditorKeywordHighlighter.Attach(txtMsg, Global.LogLevelColors.ToDictionary(
|
TextEditorKeywordHighlighter.Attach(txtMsg, Global.LogLevelColors.ToDictionary(
|
||||||
@@ -24,23 +31,6 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.DispatcherShowMsg:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() => ShowMsg(obj),
|
|
||||||
DispatcherPriority.ApplicationIdle);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ShowMsg(object msg)
|
private void ShowMsg(object msg)
|
||||||
{
|
{
|
||||||
//var lineCount = txtMsg.LineCount;
|
//var lineCount = txtMsg.LineCount;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
Width="1000"
|
Width="1000"
|
||||||
Height="600"
|
Height="600"
|
||||||
x:DataType="vms:OptionSettingViewModel"
|
x:DataType="vms:OptionSettingViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.OptionSetting}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||||||
btnCancel.Click += (s, e) => Close();
|
btnCancel.Click += (s, e) => Close();
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
|
|
||||||
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
clbdestOverride.SelectionChanged += ClbdestOverride_SelectionChanged;
|
clbdestOverride.SelectionChanged += ClbdestOverride_SelectionChanged;
|
||||||
btnBrowseCustomSystemProxyPacPath.Click += BtnBrowseCustomSystemProxyPacPath_Click;
|
btnBrowseCustomSystemProxyPacPath.Click += BtnBrowseCustomSystemProxyPacPath_Click;
|
||||||
btnBrowseCustomSystemProxyScriptPath.Click += BtnBrowseCustomSystemProxyScriptPath_Click;
|
btnBrowseCustomSystemProxyScriptPath.Click += BtnBrowseCustomSystemProxyScriptPath_Click;
|
||||||
@@ -60,6 +58,8 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||||||
|
|
||||||
cmbMainGirdOrientation.ItemsSource = Utils.GetEnumNames<EGirdOrientation>();
|
cmbMainGirdOrientation.ItemsSource = Utils.GetEnumNames<EGirdOrientation>();
|
||||||
|
|
||||||
|
_ = InitSettingFont();
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.Bind(ViewModel, vm => vm.LocalPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.LocalPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
|
||||||
@@ -142,21 +142,6 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.InitSettingFont:
|
|
||||||
await InitSettingFont();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitSettingFont()
|
private async Task InitSettingFont()
|
||||||
{
|
{
|
||||||
var lstFonts = await GetFonts();
|
var lstFonts = await GetFonts();
|
||||||
@@ -212,7 +197,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||||||
|
|
||||||
private async void BtnBrowseCustomSystemProxyPacPath_Click(object? sender, RoutedEventArgs e)
|
private async void BtnBrowseCustomSystemProxyPacPath_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var fileName = await UI.OpenFileDialog(this, null);
|
var fileName = await UI.OpenFileDialog(null);
|
||||||
if (fileName.IsNullOrEmpty())
|
if (fileName.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -223,7 +208,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||||||
|
|
||||||
private async void BtnBrowseCustomSystemProxyScriptPath_Click(object? sender, RoutedEventArgs e)
|
private async void BtnBrowseCustomSystemProxyScriptPath_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var fileName = await UI.OpenFileDialog(this, null);
|
var fileName = await UI.OpenFileDialog(null);
|
||||||
if (fileName.IsNullOrEmpty())
|
if (fileName.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -10,32 +11,33 @@
|
|||||||
Width="800"
|
Width="800"
|
||||||
Height="450"
|
Height="450"
|
||||||
x:DataType="vms:ProfilesSelectViewModel"
|
x:DataType="vms:ProfilesSelectViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.ProfilesSelect}"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<DockPanel Margin="8">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
<!-- Bottom buttons -->
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Margin="4"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
DockPanel.Dock="Bottom"
|
DockPanel.Dock="Bottom"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnSave"
|
x:Name="btnSave"
|
||||||
Width="100"
|
Width="100"
|
||||||
Click="BtnSave_Click"
|
Content="{x:Static resx:ResUI.TbConfirm}"
|
||||||
Content="{x:Static resx:ResUI.TbConfirm}" />
|
IsDefault="True" />
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnCancel"
|
x:Name="btnCancel"
|
||||||
Width="100"
|
Width="100"
|
||||||
Margin="8,0"
|
Margin="{StaticResource MarginLr8}"
|
||||||
Content="{x:Static resx:ResUI.TbCancel}" />
|
Content="{x:Static resx:ResUI.TbCancel}"
|
||||||
|
IsCancel="True" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
<!-- Top tools -->
|
<!-- Top tools -->
|
||||||
<WrapPanel Margin="4" DockPanel.Dock="Top">
|
<WrapPanel Margin="{StaticResource Margin4}" DockPanel.Dock="Top">
|
||||||
<ListBox
|
<ListBox
|
||||||
x:Name="lstGroup"
|
x:Name="lstGroup"
|
||||||
Margin="{StaticResource MarginLr4}"
|
Margin="{StaticResource MarginLr4}"
|
||||||
@@ -62,7 +64,7 @@
|
|||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtServerFilter"
|
x:Name="txtServerFilter"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="8,0"
|
Margin="{StaticResource MarginLr4}"
|
||||||
VerticalContentAlignment="Center"
|
VerticalContentAlignment="Center"
|
||||||
Text="{Binding ServerFilter, Mode=TwoWay}"
|
Text="{Binding ServerFilter, Mode=TwoWay}"
|
||||||
PlaceholderText="{x:Static resx:ResUI.MsgServerTitle}" />
|
PlaceholderText="{x:Static resx:ResUI.MsgServerTitle}" />
|
||||||
|
|||||||
@@ -5,18 +5,11 @@ namespace v2rayN.Desktop.Views;
|
|||||||
|
|
||||||
public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
||||||
{
|
{
|
||||||
private static Config _config;
|
|
||||||
|
|
||||||
public Task<ProfileItem?> ProfileItem => GetProfileItem();
|
|
||||||
public Task<List<ProfileItem>?> ProfileItems => GetProfileItems();
|
|
||||||
private bool _allowMultiSelect = false;
|
|
||||||
|
|
||||||
public ProfilesSelectWindow()
|
public ProfilesSelectWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_config = AppManager.Instance.Config;
|
btnCancel.Click += (_, _) => Close();
|
||||||
|
|
||||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||||
txtServerFilter.KeyDown += TxtServerFilter_KeyDown;
|
txtServerFilter.KeyDown += TxtServerFilter_KeyDown;
|
||||||
lstProfiles.KeyDown += LstProfiles_KeyDown;
|
lstProfiles.KeyDown += LstProfiles_KeyDown;
|
||||||
@@ -25,9 +18,6 @@ public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
|||||||
lstProfiles.Sorting += LstProfiles_Sorting;
|
lstProfiles.Sorting += LstProfiles_Sorting;
|
||||||
lstProfiles.DoubleTapped += LstProfiles_DoubleTapped;
|
lstProfiles.DoubleTapped += LstProfiles_DoubleTapped;
|
||||||
|
|
||||||
ViewModel = new ProfilesSelectViewModel(UpdateViewHandler);
|
|
||||||
DataContext = ViewModel;
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
|
||||||
@@ -35,14 +25,23 @@ public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
|||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSub, v => v.lstGroup.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSub, v => v.lstGroup.SelectedItem).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
||||||
});
|
|
||||||
|
|
||||||
btnCancel.Click += (s, e) => Close(false);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(x => x.ViewModel.MultiSelect)
|
||||||
|
.Subscribe(AllowMultiSelect)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ProfilesFocusInteraction.RegisterHandler(interaction =>
|
||||||
|
{
|
||||||
|
lstProfiles.Focus();
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AllowMultiSelect(bool allow)
|
private void AllowMultiSelect(bool allow)
|
||||||
{
|
{
|
||||||
_allowMultiSelect = allow;
|
|
||||||
if (allow)
|
if (allow)
|
||||||
{
|
{
|
||||||
lstProfiles.SelectionMode = DataGridSelectionMode.Extended;
|
lstProfiles.SelectionMode = DataGridSelectionMode.Extended;
|
||||||
@@ -51,30 +50,9 @@ public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
lstProfiles.SelectionMode = DataGridSelectionMode.Single;
|
lstProfiles.SelectionMode = DataGridSelectionMode.Single;
|
||||||
if (lstProfiles.SelectedItems.Count > 0)
|
|
||||||
{
|
|
||||||
var first = lstProfiles.SelectedItems[0];
|
|
||||||
lstProfiles.SelectedItems.Clear();
|
|
||||||
lstProfiles.SelectedItem = first;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expose ConfigType filter controls to callers
|
|
||||||
public void SetConfigTypeFilter(IEnumerable<EConfigType> types, bool exclude = false)
|
|
||||||
=> ViewModel?.SetConfigTypeFilter(types, exclude);
|
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstProfiles_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
private void LstProfiles_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (ViewModel != null)
|
if (ViewModel != null)
|
||||||
@@ -124,7 +102,7 @@ public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
|||||||
{
|
{
|
||||||
if (e.Key == Key.A)
|
if (e.Key == Key.A)
|
||||||
{
|
{
|
||||||
if (_allowMultiSelect)
|
if (ViewModel?.MultiSelect == true)
|
||||||
{
|
{
|
||||||
lstProfiles.SelectAll();
|
lstProfiles.SelectAll();
|
||||||
}
|
}
|
||||||
@@ -167,22 +145,4 @@ public partial class ProfilesSelectWindow : WindowBase<ProfilesSelectViewModel>
|
|||||||
ViewModel?.RefreshServers();
|
ViewModel?.RefreshServers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ProfileItem?> GetProfileItem()
|
|
||||||
{
|
|
||||||
var item = await ViewModel?.GetProfileItem();
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<ProfileItem>?> GetProfileItems()
|
|
||||||
{
|
|
||||||
var item = await ViewModel?.GetProfileItems();
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BtnSave_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
// Trigger selection finalize when Confirm is clicked
|
|
||||||
ViewModel?.SelectFinish();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:conv="using:v2rayN.Desktop.Converters"
|
xmlns:conv="using:v2rayN.Desktop.Converters"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:DataType="vms:ProfilesViewModel"
|
x:DataType="vms:ProfilesViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.Profiles}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
||||||
@@ -39,60 +41,62 @@
|
|||||||
</ListBox.ContextMenu>
|
</ListBox.ContextMenu>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
|
|
||||||
<Button
|
<StackPanel Orientation="Horizontal">
|
||||||
x:Name="btnEditSub"
|
<Button
|
||||||
Margin="{StaticResource MarginLr4}"
|
x:Name="btnEditSub"
|
||||||
Classes="IconButton Success"
|
Margin="{StaticResource MarginLr4}"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuSubEdit}">
|
Classes="IconButton Success"
|
||||||
<Button.Content>
|
ToolTip.Tip="{x:Static resx:ResUI.menuSubEdit}">
|
||||||
<PathIcon Data="{StaticResource SemiIconEdit}" />
|
<Button.Content>
|
||||||
</Button.Content>
|
<PathIcon Data="{StaticResource SemiIconEdit}" />
|
||||||
</Button>
|
</Button.Content>
|
||||||
<Button
|
</Button>
|
||||||
x:Name="btnAddSub"
|
<Button
|
||||||
Margin="{StaticResource MarginLr4}"
|
x:Name="btnAddSub"
|
||||||
Classes="IconButton Success"
|
Margin="{StaticResource MarginLr4}"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuSubAdd}">
|
Classes="IconButton Success"
|
||||||
<Button.Content>
|
ToolTip.Tip="{x:Static resx:ResUI.menuSubAdd}">
|
||||||
<PathIcon Data="{StaticResource SemiIconPlus}" />
|
<Button.Content>
|
||||||
</Button.Content>
|
<PathIcon Data="{StaticResource SemiIconPlus}" />
|
||||||
</Button>
|
</Button.Content>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtServerFilter"
|
x:Name="txtServerFilter"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource MarginLr4}"
|
Margin="{StaticResource MarginLr4}"
|
||||||
VerticalContentAlignment="Center"
|
VerticalContentAlignment="Center"
|
||||||
PlaceholderText="{x:Static resx:ResUI.MsgServerTitle}" />
|
PlaceholderText="{x:Static resx:ResUI.MsgServerTitle}" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnAutofitColumnWidth"
|
x:Name="btnAutofitColumnWidth"
|
||||||
Margin="{StaticResource MarginLr4}"
|
Margin="{StaticResource MarginLr4}"
|
||||||
Classes="IconButton Success"
|
Classes="IconButton Success"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
||||||
<Button.Content>
|
<Button.Content>
|
||||||
<PathIcon Data="{StaticResource SemiIconExpand}" />
|
<PathIcon Data="{StaticResource SemiIconExpand}" />
|
||||||
</Button.Content>
|
</Button.Content>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnFastRealPing"
|
x:Name="btnFastRealPing"
|
||||||
Margin="{StaticResource MarginLr4}"
|
Margin="{StaticResource MarginLr4}"
|
||||||
Classes="IconButton Success"
|
Classes="IconButton Success"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuFastRealPing}">
|
ToolTip.Tip="{x:Static resx:ResUI.menuFastRealPing}">
|
||||||
<Button.Content>
|
<Button.Content>
|
||||||
<PathIcon Data="{StaticResource SemiIconBolt}" />
|
<PathIcon Data="{StaticResource SemiIconBolt}" />
|
||||||
</Button.Content>
|
</Button.Content>
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
x:Name="menuMixedTestServer"
|
x:Name="menuMixedTestServer"
|
||||||
Margin="{StaticResource MarginLr4}"
|
Margin="{StaticResource MarginLr4}"
|
||||||
Classes="IconButton Success"
|
Classes="IconButton Success"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuMixedTestServer}">
|
ToolTip.Tip="{x:Static resx:ResUI.menuMixedTestServer}">
|
||||||
<Button.Content>
|
<Button.Content>
|
||||||
<PathIcon Data="{StaticResource building_ping}" />
|
<PathIcon Data="{StaticResource building_ping}" />
|
||||||
</Button.Content>
|
</Button.Content>
|
||||||
</Button>
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
<DataGrid
|
<DataGrid
|
||||||
|
|||||||
@@ -6,20 +6,13 @@ namespace v2rayN.Desktop.Views;
|
|||||||
public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||||
{
|
{
|
||||||
private static Config _config;
|
private static Config _config;
|
||||||
private Window? _window;
|
|
||||||
private static readonly string _tag = "ProfilesView";
|
private static readonly string _tag = "ProfilesView";
|
||||||
|
|
||||||
public ProfilesView()
|
public ProfilesView()
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProfilesView(Window window)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
_window = window;
|
|
||||||
|
|
||||||
menuSelectAll.Click += menuSelectAll_Click;
|
menuSelectAll.Click += menuSelectAll_Click;
|
||||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||||
@@ -38,8 +31,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||||||
// lstProfiles.Drop += LstProfiles_Drop;
|
// lstProfiles.Drop += LstProfiles_Drop;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
ViewModel = new ProfilesViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
|
||||||
@@ -90,17 +81,74 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||||||
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.Export2InnerUriCmd, v => v.menuExport2InnerUri).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.Export2InnerUriCmd, v => v.menuExport2InnerUri).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ShowYesNoInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var message = interaction.Input;
|
||||||
|
var result = await UI.ShowYesNo(message);
|
||||||
|
interaction.SetOutput(result == ButtonResult.Yes);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.SaveFileDialogInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var viewModel = ViewModel;
|
||||||
|
if (viewModel is null)
|
||||||
|
{
|
||||||
|
interaction.SetOutput(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var profileItem = interaction.Input;
|
||||||
|
var fileName = await UI.SaveFileDialog("");
|
||||||
|
if (fileName.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
interaction.SetOutput(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await viewModel.Export2ClientConfigResult(fileName, profileItem);
|
||||||
|
interaction.SetOutput(true);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.SetClipboardDataInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var strData = interaction.Input;
|
||||||
|
await AvaUtils.SetClipboardData(this, strData);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ProfilesFocusInteraction.RegisterHandler(interaction =>
|
||||||
|
{
|
||||||
|
lstProfiles.Focus();
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ShareServerInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var url = interaction.Input;
|
||||||
|
if (url.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await ShareServer(url);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.DispatcherRefreshServersBizInteraction.RegisterHandler(interaction =>
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(RefreshServersBiz, DispatcherPriority.Default);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.AdjustMainLvColWidthInteraction.RegisterHandler(interaction =>
|
||||||
|
{
|
||||||
|
//AutofitColumnWidth();
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
AppEvents.AppExitRequested
|
AppEvents.AppExitRequested
|
||||||
.AsObservable()
|
.AsObservable()
|
||||||
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
.ObserveOn(RxSchedulers.MainThreadScheduler)
|
||||||
.Subscribe(_ => StorageUI())
|
.Subscribe(_ => StorageUI())
|
||||||
.DisposeWith(disposables);
|
.DisposeWith(disposables);
|
||||||
|
|
||||||
//AppEvents.AdjustMainLvColWidthRequested
|
|
||||||
// .AsObservable()
|
|
||||||
// .ObserveOn(RxSchedulers.MainThreadScheduler)
|
|
||||||
// .Subscribe(_ => AutofitColumnWidth())
|
|
||||||
// .DisposeWith(disposables);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
@@ -120,93 +168,6 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||||||
|
|
||||||
#region Event
|
#region Event
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.SetClipboardData:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await AvaUtils.SetClipboardData(this, (string)obj);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.ProfilesFocus:
|
|
||||||
lstProfiles.Focus();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.ShowYesNo:
|
|
||||||
if (await UI.ShowYesNo(_window, ResUI.RemoveServer) != ButtonResult.Yes)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.SaveFileDialog:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileName = await UI.SaveFileDialog(_window, "");
|
|
||||||
if (fileName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ViewModel?.Export2ClientConfigResult(fileName, (ProfileItem)obj);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.AddServerWindow:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new AddServerWindow((ProfileItem)obj).ShowDialog<bool>(_window);
|
|
||||||
|
|
||||||
case EViewAction.AddServer2Window:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new AddServer2Window((ProfileItem)obj).ShowDialog<bool>(_window);
|
|
||||||
|
|
||||||
case EViewAction.AddGroupServerWindow:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new AddGroupServerWindow((ProfileItem)obj).ShowDialog<bool>(_window);
|
|
||||||
|
|
||||||
case EViewAction.ShareServer:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await ShareServer((string)obj);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.SubEditWindow:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new SubEditWindow((SubItem)obj).ShowDialog<bool>(_window);
|
|
||||||
|
|
||||||
case EViewAction.DispatcherRefreshServersBiz:
|
|
||||||
Dispatcher.UIThread.Post(RefreshServersBiz, DispatcherPriority.Default);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ShareServer(string url)
|
public async Task ShareServer(string url)
|
||||||
{
|
{
|
||||||
if (url.IsNullOrEmpty())
|
if (url.IsNullOrEmpty())
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:sys="clr-namespace:System;assembly=netstandard"
|
xmlns:sys="clr-namespace:System;assembly=netstandard"
|
||||||
d:DesignHeight="600"
|
|
||||||
d:DesignWidth="600"
|
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
|
|||||||
@@ -5,6 +5,13 @@ public partial class QrcodeView : UserControl
|
|||||||
public QrcodeView()
|
public QrcodeView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
{
|
||||||
|
//const string url = Global.AppName;
|
||||||
|
const string url = Global.SystemProxyExceptionsWindows;
|
||||||
|
txtContent.Text = url;
|
||||||
|
imgQrcode.Source = GetQRCode(url);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public QrcodeView(string? url)
|
public QrcodeView(string? url)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
Width="900"
|
Width="900"
|
||||||
Height="600"
|
Height="600"
|
||||||
x:DataType="vms:RoutingRuleDetailsViewModel"
|
x:DataType="vms:RoutingRuleDetailsViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.RoutingRuleDetails}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
@@ -92,7 +94,6 @@
|
|||||||
<Button
|
<Button
|
||||||
x:Name="btnSelectProfile"
|
x:Name="btnSelectProfile"
|
||||||
Margin="0,0,8,0"
|
Margin="0,0,8,0"
|
||||||
Click="BtnSelectProfile_Click"
|
|
||||||
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
|||||||
@@ -5,11 +5,6 @@ namespace v2rayN.Desktop.Views;
|
|||||||
public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsViewModel>
|
public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsViewModel>
|
||||||
{
|
{
|
||||||
public RoutingRuleDetailsWindow()
|
public RoutingRuleDetailsWindow()
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingRuleDetailsWindow(RulesItem rulesItem)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@@ -18,26 +13,12 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
|
|||||||
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
|
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
|
||||||
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
|
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
|
||||||
|
|
||||||
ViewModel = new RoutingRuleDetailsViewModel(rulesItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbOutboundTag.ItemsSource = Global.OutboundTags;
|
cmbOutboundTag.ItemsSource = Global.OutboundTags;
|
||||||
clbProtocol.ItemsSource = Global.RuleProtocols;
|
clbProtocol.ItemsSource = Global.RuleProtocols;
|
||||||
clbInboundTag.ItemsSource = Global.InboundTags;
|
clbInboundTag.ItemsSource = Global.InboundTags;
|
||||||
cmbNetwork.ItemsSource = Global.RuleNetworks;
|
cmbNetwork.ItemsSource = Global.RuleNetworks;
|
||||||
cmbRuleType.ItemsSource = Utils.GetEnumNames<ERuleType>().AppendEmpty();
|
cmbRuleType.ItemsSource = Utils.GetEnumNames<ERuleType>().AppendEmpty();
|
||||||
|
|
||||||
if (!rulesItem.Id.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
rulesItem.Protocol?.ForEach(it =>
|
|
||||||
{
|
|
||||||
clbProtocol?.SelectedItems?.Add(it);
|
|
||||||
});
|
|
||||||
rulesItem.InboundTag?.ForEach(it =>
|
|
||||||
{
|
|
||||||
clbInboundTag?.SelectedItems?.Add(it);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
|
||||||
@@ -52,19 +33,30 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
|
|||||||
this.Bind(ViewModel, vm => vm.AutoSort, v => v.chkAutoSort.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AutoSort, v => v.chkAutoSort.IsChecked).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.RuleType, v => v.cmbRuleType.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.RuleType, v => v.cmbRuleType.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SelectProfileCmd, v => v.btnSelectProfile).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(v => v.ViewModel.SelectedSource)
|
||||||
|
.WhereNotNull()
|
||||||
|
.Subscribe(InitializeData)
|
||||||
|
.DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
private void InitializeData(RulesItem rulesItem)
|
||||||
{
|
{
|
||||||
switch (action)
|
if (rulesItem.Id.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
case EViewAction.CloseWindow:
|
return;
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return await Task.FromResult(true);
|
rulesItem.Protocol?.ForEach(it =>
|
||||||
|
{
|
||||||
|
clbProtocol?.SelectedItems?.Add(it);
|
||||||
|
});
|
||||||
|
rulesItem.InboundTag?.ForEach(it =>
|
||||||
|
{
|
||||||
|
clbInboundTag?.SelectedItems?.Add(it);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||||
@@ -92,19 +84,4 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
|
|||||||
{
|
{
|
||||||
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void BtnSelectProfile_Click(object? sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
|
||||||
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
|
||||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
|
||||||
if (result == true)
|
|
||||||
{
|
|
||||||
var profile = await selectWindow.ProfileItem;
|
|
||||||
if (profile != null)
|
|
||||||
{
|
|
||||||
cmbOutboundTag.Text = profile.Remarks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
Width="900"
|
Width="900"
|
||||||
Height="600"
|
Height="600"
|
||||||
x:DataType="vms:RoutingRuleSettingViewModel"
|
x:DataType="vms:RoutingRuleSettingViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.RoutingRuleSetting}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -6,11 +6,6 @@ namespace v2rayN.Desktop.Views;
|
|||||||
public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingViewModel>
|
public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingViewModel>
|
||||||
{
|
{
|
||||||
public RoutingRuleSettingWindow()
|
public RoutingRuleSettingWindow()
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingRuleSettingWindow(RoutingItem routingItem)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
@@ -23,8 +18,6 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
|
|||||||
//btnBrowseCustomIcon.Click += btnBrowseCustomIcon_Click;
|
//btnBrowseCustomIcon.Click += btnBrowseCustomIcon_Click;
|
||||||
btnBrowseCustomRulesetPath4Singbox.Click += btnBrowseCustomRulesetPath4Singbox_ClickAsync;
|
btnBrowseCustomRulesetPath4Singbox.Click += btnBrowseCustomRulesetPath4Singbox_ClickAsync;
|
||||||
|
|
||||||
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
|
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
|
||||||
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
|
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
|
||||||
|
|
||||||
@@ -56,70 +49,35 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
|
|||||||
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ShowYesNoInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var message = interaction.Input;
|
||||||
|
var result = await UI.ShowYesNo(message);
|
||||||
|
interaction.SetOutput(result == ButtonResult.Yes);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.SetClipboardDataInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var strData = interaction.Input;
|
||||||
|
await AvaUtils.SetClipboardData(this, strData);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ReadTextFromClipboardInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var result = await AvaUtils.GetClipboardData(this);
|
||||||
|
interaction.SetOutput(result);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.BrowseRulesFileInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var fileName = await UI.OpenFileDialog(null);
|
||||||
|
interaction.SetOutput(fileName);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.ShowYesNo:
|
|
||||||
if (await UI.ShowYesNo(this, ResUI.RemoveServer) != ButtonResult.Yes)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.AddBatchRoutingRulesYesNo:
|
|
||||||
if (await UI.ShowYesNo(this, ResUI.AddBatchRoutingRulesYesNo) != ButtonResult.Yes)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.RoutingRuleDetailsWindow:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new RoutingRuleDetailsWindow((RulesItem)obj).ShowDialog<bool>(this);
|
|
||||||
|
|
||||||
case EViewAction.ImportRulesFromFile:
|
|
||||||
var fileName = await UI.OpenFileDialog(this, null);
|
|
||||||
if (fileName.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
ViewModel?.ImportRulesFromFileAsync(fileName);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.SetClipboardData:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await AvaUtils.SetClipboardData(this, (string)obj);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.ImportRulesFromClipboard:
|
|
||||||
var clipboardData = await AvaUtils.GetClipboardData(this);
|
|
||||||
if (clipboardData.IsNotEmpty())
|
|
||||||
{
|
|
||||||
ViewModel?.ImportRulesFromClipboardAsync(clipboardData);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
txtRemarks.Focus();
|
txtRemarks.Focus();
|
||||||
@@ -197,7 +155,7 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
|
|||||||
|
|
||||||
private async void btnBrowseCustomRulesetPath4Singbox_ClickAsync(object? sender, RoutedEventArgs e)
|
private async void btnBrowseCustomRulesetPath4Singbox_ClickAsync(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var fileName = await UI.OpenFileDialog(this, null);
|
var fileName = await UI.OpenFileDialog(null);
|
||||||
if (fileName.IsNullOrEmpty())
|
if (fileName.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
@@ -10,6 +11,7 @@
|
|||||||
Width="900"
|
Width="900"
|
||||||
Height="600"
|
Height="600"
|
||||||
x:DataType="vms:RoutingSettingViewModel"
|
x:DataType="vms:RoutingSettingViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.RoutingSetting}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -16,8 +16,6 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
|
|||||||
lstRoutings.DoubleTapped += LstRoutings_DoubleTapped;
|
lstRoutings.DoubleTapped += LstRoutings_DoubleTapped;
|
||||||
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
|
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
|
||||||
|
|
||||||
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
|
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
|
||||||
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
|
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Sbox;
|
||||||
|
|
||||||
@@ -35,31 +33,16 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
|
|||||||
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedSetDefaultCmd, v => v.menuRoutingAdvancedSetDefault).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedSetDefaultCmd, v => v.menuRoutingAdvancedSetDefault).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules2).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedImportRulesCmd, v => v.menuRoutingAdvancedImportRules2).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ShowYesNoInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var message = interaction.Input;
|
||||||
|
var result = await UI.ShowYesNo(message);
|
||||||
|
interaction.SetOutput(result == ButtonResult.Yes);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.ShowYesNo:
|
|
||||||
if (await UI.ShowYesNo(this, ResUI.RemoveRules) != ButtonResult.Yes)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.RoutingRuleSettingWindow:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await new RoutingRuleSettingWindow((RoutingItem)obj).ShowDialog<bool>(this);
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _closed = false;
|
private bool _closed = false;
|
||||||
|
|
||||||
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
|
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
d:DesignHeight="450"
|
|
||||||
d:DesignWidth="800"
|
|
||||||
x:DataType="vms:StatusBarViewModel"
|
x:DataType="vms:StatusBarViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.StatusBar}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<Grid>
|
<Grid>
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
<StackPanel
|
<StackPanel
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
DockPanel.Dock="Right">
|
DockPanel.Dock="Right">
|
||||||
<TextBlock x:Name="txtSpeedProxyDisplay" HorizontalAlignment="Right" />
|
<TextBlock x:Name="txtSpeedProxyDisplay" HorizontalAlignment="Right" />
|
||||||
<Border Margin="1" />
|
<Border Margin="1" />
|
||||||
@@ -22,8 +22,8 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
DockPanel.Dock="Left">
|
DockPanel.Dock="Left">
|
||||||
<TextBlock x:Name="txtInboundDisplay" />
|
<TextBlock x:Name="txtInboundDisplay" />
|
||||||
<Border Margin="1" />
|
<Border Margin="1" />
|
||||||
@@ -32,24 +32,24 @@
|
|||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
x:Name="spEnableTun"
|
x:Name="spEnableTun"
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
DockPanel.Dock="Left"
|
DockPanel.Dock="Left"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbEnableTunAs}" />
|
Text="{x:Static resx:ResUI.TbEnableTunAs}" />
|
||||||
<ToggleSwitch
|
<ToggleSwitch
|
||||||
x:Name="togEnableTun"
|
x:Name="togEnableTun"
|
||||||
HorizontalAlignment="Center"
|
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
Theme="{DynamicResource SimpleToggleSwitch}" />
|
Theme="{DynamicResource SimpleToggleSwitch}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="{StaticResource MarginLr8}"
|
Margin="{StaticResource MarginLr8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
DockPanel.Dock="Left"
|
DockPanel.Dock="Left"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
ToolTip.Tip="{x:Static resx:ResUI.menuRouting}" />
|
ToolTip.Tip="{x:Static resx:ResUI.menuRouting}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel VerticalAlignment="Center" Margin="{StaticResource MarginLr8}">
|
<StackPanel Margin="{StaticResource MarginLr8}" VerticalAlignment="Center">
|
||||||
<TextBlock x:Name="txtRunningServerDisplay" />
|
<TextBlock x:Name="txtRunningServerDisplay" />
|
||||||
<Border Margin="1" />
|
<Border Margin="1" />
|
||||||
<TextBlock x:Name="txtRunningInfoDisplay" />
|
<TextBlock x:Name="txtRunningInfoDisplay" />
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
|||||||
|
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
|
|
||||||
ViewModel = StatusBarViewModel.Instance;
|
|
||||||
ViewModel?.InitUpdateView(UpdateViewHandler);
|
|
||||||
|
|
||||||
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;
|
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;
|
||||||
txtRunningInfoDisplay.Tapped += TxtRunningServerDisplay_Tapped;
|
txtRunningInfoDisplay.Tapped += TxtRunningServerDisplay_Tapped;
|
||||||
|
|
||||||
@@ -32,6 +29,26 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
|||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.SetClipboardDataInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var strData = interaction.Input;
|
||||||
|
await AvaUtils.SetClipboardData(this, strData);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.PasswordInputInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var result = await PasswordInputAsync();
|
||||||
|
interaction.SetOutput(result);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.DispatcherRefreshIconInteraction.RegisterHandler(interaction =>
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(RefreshIcon,
|
||||||
|
DispatcherPriority.Default);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
|
|
||||||
//spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator);
|
//spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator);
|
||||||
@@ -42,30 +59,6 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.DispatcherRefreshIcon:
|
|
||||||
Dispatcher.UIThread.Post(RefreshIcon,
|
|
||||||
DispatcherPriority.Default);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.SetClipboardData:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await AvaUtils.SetClipboardData(this, (string)obj);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.PasswordInput:
|
|
||||||
return await PasswordInputAsync();
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshIcon()
|
private void RefreshIcon()
|
||||||
{
|
{
|
||||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
@@ -77,7 +70,7 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> PasswordInputAsync()
|
private async Task<string?> PasswordInputAsync()
|
||||||
{
|
{
|
||||||
var dialog = new SudoPasswordInputView();
|
var dialog = new SudoPasswordInputView();
|
||||||
var obj = await DialogHost.Show(dialog);
|
var obj = await DialogHost.Show(dialog);
|
||||||
@@ -86,11 +79,11 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
|||||||
if (password.IsNullOrEmpty())
|
if (password.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
togEnableTun.IsChecked = false;
|
togEnableTun.IsChecked = false;
|
||||||
return false;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppManager.Instance.LinuxSudoPwd = password;
|
AppManager.Instance.LinuxSudoPwd = password;
|
||||||
return true;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TxtRunningServerDisplay_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
|
private void TxtRunningServerDisplay_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
|
||||||
|
|||||||
@@ -3,12 +3,14 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
Title="{x:Static resx:ResUI.menuSubSetting}"
|
Title="{x:Static resx:ResUI.menuSubSetting}"
|
||||||
Width="700"
|
Width="700"
|
||||||
Height="600"
|
Height="600"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.SubEdit}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
@@ -72,11 +74,11 @@
|
|||||||
<Button
|
<Button
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Classes="IconButton"
|
Margin="{StaticResource MarginLr8}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Margin="{StaticResource MarginLr8}">
|
Classes="IconButton">
|
||||||
<Button.Content>
|
<Button.Content>
|
||||||
<PathIcon Data="{StaticResource SemiIconMore}" >
|
<PathIcon Data="{StaticResource SemiIconMore}">
|
||||||
<PathIcon.RenderTransform>
|
<PathIcon.RenderTransform>
|
||||||
<RotateTransform Angle="90" />
|
<RotateTransform Angle="90" />
|
||||||
</PathIcon.RenderTransform>
|
</PathIcon.RenderTransform>
|
||||||
@@ -208,10 +210,10 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
PlaceholderText="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
PlaceholderText="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||||
<Button
|
<Button
|
||||||
|
x:Name="btnSelectPrevProfile"
|
||||||
Grid.Row="9"
|
Grid.Row="9"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Click="BtnSelectPrevProfile_Click"
|
|
||||||
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -228,10 +230,10 @@
|
|||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
PlaceholderText="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
PlaceholderText="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||||
<Button
|
<Button
|
||||||
|
x:Name="btnSelectNextProfile"
|
||||||
Grid.Row="10"
|
Grid.Row="10"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Click="BtnSelectNextProfile_Click"
|
|
||||||
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
Content="{x:Static resx:ResUI.TbSelectProfile}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
|||||||
@@ -5,19 +5,12 @@ namespace v2rayN.Desktop.Views;
|
|||||||
public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
||||||
{
|
{
|
||||||
public SubEditWindow()
|
public SubEditWindow()
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubEditWindow(SubItem subItem)
|
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
btnCancel.Click += (s, e) => Close();
|
btnCancel.Click += (s, e) => Close();
|
||||||
|
|
||||||
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbConvertTarget.ItemsSource = Global.SubConvertTargets;
|
cmbConvertTarget.ItemsSource = Global.SubConvertTargets;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
@@ -36,53 +29,14 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
|||||||
this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Memo, v => v.txtMemo.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Memo, v => v.txtMemo.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SelectPrevProfileCmd, v => v.btnSelectPrevProfile).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SelectNextProfileCmd, v => v.btnSelectNextProfile).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
txtRemarks.Focus();
|
txtRemarks.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void BtnSelectPrevProfile_Click(object? sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
|
||||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
|
||||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
|
||||||
if (result == true)
|
|
||||||
{
|
|
||||||
var profile = await selectWindow.ProfileItem;
|
|
||||||
if (profile != null)
|
|
||||||
{
|
|
||||||
txtPrevProfile.Text = profile.Remarks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void BtnSelectNextProfile_Click(object? sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
|
||||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
|
||||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
|
||||||
if (result == true)
|
|
||||||
{
|
|
||||||
var profile = await selectWindow.ProfileItem;
|
|
||||||
if (profile != null)
|
|
||||||
{
|
|
||||||
txtNextProfile.Text = profile.Remarks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
|
xmlns:dialogHost="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
Width="900"
|
Width="900"
|
||||||
Height="600"
|
Height="600"
|
||||||
x:DataType="vms:SubSettingViewModel"
|
x:DataType="vms:SubSettingViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.SubSetting}"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
|
|||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
Closing += SubSettingWindow_Closing;
|
Closing += SubSettingWindow_Closing;
|
||||||
KeyDown += SubSettingWindow_KeyDown;
|
KeyDown += SubSettingWindow_KeyDown;
|
||||||
ViewModel = new SubSettingViewModel(UpdateViewHandler);
|
|
||||||
lstSubscription.DoubleTapped += LstSubscription_DoubleTapped;
|
lstSubscription.DoubleTapped += LstSubscription_DoubleTapped;
|
||||||
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
|
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
|
||||||
|
|
||||||
@@ -34,46 +33,28 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
|
|||||||
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete2).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete2).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit2).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit2).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare2).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare2).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ShowYesNoInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var message = interaction.Input;
|
||||||
|
var result = await UI.ShowYesNo(message);
|
||||||
|
interaction.SetOutput(result == ButtonResult.Yes);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
|
|
||||||
|
ViewModel.ShareSubInteraction.RegisterHandler(async interaction =>
|
||||||
|
{
|
||||||
|
var url = interaction.Input;
|
||||||
|
if (url.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await ShareSub(url);
|
||||||
|
interaction.SetOutput(Unit.Default);
|
||||||
|
}).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
Close();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.ShowYesNo:
|
|
||||||
if (await UI.ShowYesNo(this, ResUI.RemoveServer) != ButtonResult.Yes)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.SubEditWindow:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var window = new SubEditWindow((SubItem)obj);
|
|
||||||
await window.ShowDialog(this);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.ShareSub:
|
|
||||||
if (obj is null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
await ShareSub((string)obj);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ShareSub(string url)
|
private async Task ShareSub(string url)
|
||||||
{
|
{
|
||||||
if (url.IsNullOrEmpty())
|
if (url.IsNullOrEmpty())
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
d:DesignHeight="200"
|
|
||||||
d:DesignWidth="400"
|
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:dd="clr-namespace:v2rayN.Desktop.DesignData"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:v2rayN.Desktop.ViewModels"
|
xmlns:vms="clr-namespace:v2rayN.Desktop.ViewModels"
|
||||||
d:DesignHeight="450"
|
|
||||||
d:DesignWidth="800"
|
|
||||||
x:DataType="vms:ThemeSettingViewModel"
|
x:DataType="vms:ThemeSettingViewModel"
|
||||||
|
Design.DataContext="{x:Static dd:DesignData.ThemeSetting}"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Styles>
|
<UserControl.Styles>
|
||||||
<Style Selector="TextBlock">
|
<Style Selector="TextBlock">
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:conv="clr-namespace:v2rayN.Converters"
|
xmlns:conv="clr-namespace:v2rayN.Converters"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
ShutdownMode="OnExplicitShutdown"
|
ShutdownMode="OnExplicitShutdown">
|
||||||
StartupUri="Views/MainWindow.xaml">
|
|
||||||
<Application.Resources>
|
<Application.Resources>
|
||||||
<ResourceDictionary xmlns:system="clr-namespace:System;assembly=mscorlib">
|
<ResourceDictionary xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
using v2rayN.Manager;
|
||||||
|
using v2rayN.Views;
|
||||||
|
|
||||||
namespace v2rayN;
|
namespace v2rayN;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interaction logic for App.xaml
|
/// Interaction logic for App.xaml
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class App : Application
|
public partial class App
|
||||||
{
|
{
|
||||||
public static EventWaitHandle ProgramStarted;
|
public static EventWaitHandle ProgramStarted;
|
||||||
|
|
||||||
@@ -38,6 +41,8 @@ public partial class App : Application
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppManager.Instance.WindowDialog = new WindowDialog();
|
||||||
|
|
||||||
AppManager.Instance.InitComponents();
|
AppManager.Instance.InitComponents();
|
||||||
|
|
||||||
RxAppBuilder.CreateReactiveUIBuilder()
|
RxAppBuilder.CreateReactiveUIBuilder()
|
||||||
@@ -45,6 +50,14 @@ public partial class App : Application
|
|||||||
.BuildApp();
|
.BuildApp();
|
||||||
|
|
||||||
base.OnStartup(e);
|
base.OnStartup(e);
|
||||||
|
|
||||||
|
var mainWindowViewModel = new MainWindowViewModel();
|
||||||
|
var viewFor = SimpleViewLocator.Instance.ResolveView(mainWindowViewModel);
|
||||||
|
viewFor!.ViewModel = mainWindowViewModel;
|
||||||
|
|
||||||
|
var mainWindow = (MainWindow)viewFor;
|
||||||
|
mainWindow.Show();
|
||||||
|
MainWindow = mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
private void App_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||||
|
|||||||
22
v2rayN/v2rayN/Base/ViewHost.cs
Normal file
22
v2rayN/v2rayN/Base/ViewHost.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace v2rayN.Base;
|
||||||
|
|
||||||
|
public static class ViewHost
|
||||||
|
{
|
||||||
|
public static void Show(
|
||||||
|
ContentControl host,
|
||||||
|
object? viewModel)
|
||||||
|
{
|
||||||
|
if (viewModel == null)
|
||||||
|
{
|
||||||
|
host.Content = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var view = SimpleViewLocator.Instance.ResolveView(viewModel);
|
||||||
|
view?.ViewModel = viewModel;
|
||||||
|
|
||||||
|
host.Content = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
75
v2rayN/v2rayN/Common/SimpleViewLocator.cs
Normal file
75
v2rayN/v2rayN/Common/SimpleViewLocator.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using v2rayN.ViewModels;
|
||||||
|
using v2rayN.Views;
|
||||||
|
|
||||||
|
namespace v2rayN.Common;
|
||||||
|
|
||||||
|
public class SimpleViewLocator : IViewLocator
|
||||||
|
{
|
||||||
|
private static readonly Lazy<SimpleViewLocator> _instance = new(() => new SimpleViewLocator());
|
||||||
|
private readonly Dictionary<Type, Func<IViewFor>> _mappings = new();
|
||||||
|
|
||||||
|
private SimpleViewLocator()
|
||||||
|
{
|
||||||
|
Register<AddGroupServerViewModel, AddGroupServerWindow>();
|
||||||
|
Register<AddServer2ViewModel, AddServer2Window>();
|
||||||
|
Register<AddServerViewModel, AddServerWindow>();
|
||||||
|
Register<BackupAndRestoreViewModel, BackupAndRestoreView>();
|
||||||
|
Register<CheckUpdateViewModel, CheckUpdateView>();
|
||||||
|
Register<ClashConnectionsViewModel, ClashConnectionsView>();
|
||||||
|
Register<ClashProxiesViewModel, ClashProxiesView>();
|
||||||
|
Register<DNSSettingViewModel, DNSSettingWindow>();
|
||||||
|
Register<FullConfigTemplateViewModel, FullConfigTemplateWindow>();
|
||||||
|
Register<GlobalHotkeySettingViewModel, GlobalHotkeySettingWindow>();
|
||||||
|
Register<MainWindowViewModel, MainWindow>();
|
||||||
|
Register<MsgViewModel, MsgView>();
|
||||||
|
Register<OptionSettingViewModel, OptionSettingWindow>();
|
||||||
|
Register<ProfilesSelectViewModel, ProfilesSelectWindow>();
|
||||||
|
Register<ProfilesViewModel, ProfilesView>();
|
||||||
|
Register<RoutingRuleDetailsViewModel, RoutingRuleDetailsWindow>();
|
||||||
|
Register<RoutingRuleSettingViewModel, RoutingRuleSettingWindow>();
|
||||||
|
Register<RoutingSettingViewModel, RoutingSettingWindow>();
|
||||||
|
Register<StatusBarViewModel, StatusBarView>();
|
||||||
|
Register<SubEditViewModel, SubEditWindow>();
|
||||||
|
Register<SubSettingViewModel, SubSettingWindow>();
|
||||||
|
Register<ThemeSettingViewModel, ThemeSettingView>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SimpleViewLocator Instance => _instance.Value;
|
||||||
|
|
||||||
|
public IViewFor<TViewModel>? ResolveView<TViewModel>(string? contract = null) where TViewModel : class
|
||||||
|
{
|
||||||
|
if (_mappings.TryGetValue(typeof(TViewModel), out var factory))
|
||||||
|
{
|
||||||
|
return factory() as IViewFor<TViewModel>;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IViewFor? ResolveView(object? instance, string? contract = null)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var viewModelType = instance.GetType();
|
||||||
|
if (_mappings.TryGetValue(viewModelType, out var factory))
|
||||||
|
{
|
||||||
|
return factory();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Register<TViewModel, TView>(Func<TView> factory)
|
||||||
|
where TViewModel : class
|
||||||
|
where TView : class, IViewFor<TViewModel>
|
||||||
|
{
|
||||||
|
_mappings[typeof(TViewModel)] = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Register<TViewModel, TView>()
|
||||||
|
where TViewModel : class
|
||||||
|
where TView : class, IViewFor<TViewModel>, new()
|
||||||
|
{
|
||||||
|
_mappings[typeof(TViewModel)] = () => new TView();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,6 +6,7 @@ global using System.Diagnostics;
|
|||||||
global using System.Globalization;
|
global using System.Globalization;
|
||||||
global using System.IO;
|
global using System.IO;
|
||||||
global using System.Linq;
|
global using System.Linq;
|
||||||
|
global using System.Reactive;
|
||||||
global using System.Reactive.Disposables.Fluent;
|
global using System.Reactive.Disposables.Fluent;
|
||||||
global using System.Reactive.Linq;
|
global using System.Reactive.Linq;
|
||||||
global using System.Runtime.InteropServices;
|
global using System.Runtime.InteropServices;
|
||||||
|
|||||||
37
v2rayN/v2rayN/Manager/WindowDialog.cs
Normal file
37
v2rayN/v2rayN/Manager/WindowDialog.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using v2rayN.Base;
|
||||||
|
|
||||||
|
namespace v2rayN.Manager;
|
||||||
|
|
||||||
|
public class WindowDialog : IWindowDialog
|
||||||
|
{
|
||||||
|
public Task<bool> ShowDialogAsync<TViewModel>(TViewModel vm)
|
||||||
|
where TViewModel : class
|
||||||
|
{
|
||||||
|
var owner = Application.Current.MainWindow;
|
||||||
|
|
||||||
|
var viewFor = SimpleViewLocator.Instance.ResolveView(vm);
|
||||||
|
//var viewFor = SimpleViewLocator.Instance.ResolveView<TViewModel>();
|
||||||
|
|
||||||
|
if (viewFor is not WindowBase<TViewModel> window)
|
||||||
|
{
|
||||||
|
return Task.FromResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Owner = owner;
|
||||||
|
window.ViewModel = vm;
|
||||||
|
|
||||||
|
if (vm is ServiceLib.Base.ICloseable closeable)
|
||||||
|
{
|
||||||
|
closeable.RequestClose += (_, _) => Dispatch(() => window.DialogResult = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = window.ShowDialog();
|
||||||
|
|
||||||
|
return Task.FromResult(result ?? false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Dispatch(Action action)
|
||||||
|
{
|
||||||
|
Application.Current.Dispatcher.Invoke(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -213,7 +213,6 @@
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuAddChildServer"
|
x:Name="menuAddChildServer"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Click="MenuAddChild_Click"
|
|
||||||
Header="{x:Static resx:ResUI.menuAddChildServer}" />
|
Header="{x:Static resx:ResUI.menuAddChildServer}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuRemoveChildServer"
|
x:Name="menuRemoveChildServer"
|
||||||
|
|||||||
@@ -2,19 +2,16 @@ namespace v2rayN.Views;
|
|||||||
|
|
||||||
public partial class AddGroupServerWindow
|
public partial class AddGroupServerWindow
|
||||||
{
|
{
|
||||||
public AddGroupServerWindow(ProfileItem profileItem)
|
public AddGroupServerWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Owner = Application.Current.MainWindow;
|
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
|
||||||
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
||||||
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
menuSelectAllChild.Click += MenuSelectAllChild_Click;
|
||||||
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
tabControl.SelectionChanged += TabControl_SelectionChanged;
|
||||||
|
|
||||||
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbCoreType.ItemsSource = Global.CoreTypes;
|
cmbCoreType.ItemsSource = Global.CoreTypes;
|
||||||
cmbPolicyGroupType.ItemsSource = new List<string>
|
cmbPolicyGroupType.ItemsSource = new List<string>
|
||||||
{
|
{
|
||||||
@@ -26,22 +23,6 @@ public partial class AddGroupServerWindow
|
|||||||
};
|
};
|
||||||
cmbFilter.ItemsSource = Global.PolicyGroupDefaultFilterList;
|
cmbFilter.ItemsSource = Global.PolicyGroupDefaultFilterList;
|
||||||
|
|
||||||
switch (profileItem.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.PolicyGroup:
|
|
||||||
Title = ResUI.TbConfigTypePolicyGroup;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.ProxyChain:
|
|
||||||
Title = ResUI.TbConfigTypeProxyChain;
|
|
||||||
gridPolicyGroup.Visibility = Visibility.Collapsed;
|
|
||||||
if (tabControl.Items.Count > 0)
|
|
||||||
{
|
|
||||||
tabControl.Items.RemoveAt(0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
||||||
@@ -56,6 +37,7 @@ public partial class AddGroupServerWindow
|
|||||||
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.AllProfilePreviewItemsObs, v => v.lstPreviewChild.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.AllProfilePreviewItemsObs, v => v.lstPreviewChild.ItemsSource).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.AddCmd, v => v.menuAddChildServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||||
@@ -63,19 +45,32 @@ public partial class AddGroupServerWindow
|
|||||||
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(v => v.ViewModel.SelectedSource)
|
||||||
|
.WhereNotNull()
|
||||||
|
.Subscribe(InitializeData)
|
||||||
|
.DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
private void InitializeData(ProfileItem profileItem)
|
||||||
{
|
{
|
||||||
switch (action)
|
switch (profileItem.ConfigType)
|
||||||
{
|
{
|
||||||
case EViewAction.CloseWindow:
|
case EConfigType.PolicyGroup:
|
||||||
DialogResult = true;
|
Title = ResUI.TbConfigTypePolicyGroup;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.ProxyChain:
|
||||||
|
Title = ResUI.TbConfigTypeProxyChain;
|
||||||
|
gridPolicyGroup.Visibility = Visibility.Collapsed;
|
||||||
|
if (tabControl.Items.Count > 0)
|
||||||
|
{
|
||||||
|
tabControl.Items.RemoveAt(0);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
@@ -125,18 +120,6 @@ public partial class AddGroupServerWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void MenuAddChild_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
var selectWindow = new ProfilesSelectWindow();
|
|
||||||
selectWindow.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
|
|
||||||
selectWindow.AllowMultiSelect(true);
|
|
||||||
if (selectWindow.ShowDialog() == true)
|
|
||||||
{
|
|
||||||
var profiles = await selectWindow.ProfileItems;
|
|
||||||
ViewModel?.ChildItemsObs.AddRange(profiles);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LstChild_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
private void LstChild_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (ViewModel != null)
|
if (ViewModel != null)
|
||||||
|
|||||||
@@ -2,13 +2,11 @@ namespace v2rayN.Views;
|
|||||||
|
|
||||||
public partial class AddServer2Window
|
public partial class AddServer2Window
|
||||||
{
|
{
|
||||||
public AddServer2Window(ProfileItem profileItem)
|
public AddServer2Window()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Owner = Application.Current.MainWindow;
|
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != nameof(ECoreType.v2rayN)).ToList().AppendEmpty();
|
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != nameof(ECoreType.v2rayN)).ToList().AppendEmpty();
|
||||||
|
|
||||||
@@ -23,28 +21,18 @@ public partial class AddServer2Window
|
|||||||
this.BindCommand(ViewModel, vm => vm.BrowseServerCmd, v => v.btnBrowse).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.BrowseServerCmd, v => v.btnBrowse).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.btnEdit).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.btnEdit).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
});
|
|
||||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
ViewModel.BrowseConfigFileInteraction.RegisterHandler(interaction =>
|
||||||
{
|
{
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
DialogResult = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EViewAction.BrowseServer:
|
|
||||||
if (UI.OpenFileDialog(out var fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true)
|
if (UI.OpenFileDialog(out var fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true)
|
||||||
{
|
{
|
||||||
return false;
|
interaction.SetOutput(null);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
ViewModel?.BrowseServer(fileName);
|
interaction.SetOutput(fileName);
|
||||||
break;
|
}).DisposeWith(disposables);
|
||||||
}
|
});
|
||||||
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
|
using System.Reactive.Disposables;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
||||||
namespace v2rayN.Views;
|
namespace v2rayN.Views;
|
||||||
|
|
||||||
public partial class AddServerWindow
|
public partial class AddServerWindow
|
||||||
{
|
{
|
||||||
public AddServerWindow(ProfileItem profileItem)
|
public AddServerWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Owner = Application.Current.MainWindow;
|
|
||||||
Loaded += Window_Loaded;
|
Loaded += Window_Loaded;
|
||||||
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
|
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
|
||||||
cmbHeaderTypeRaw.SelectionChanged += CmbHeaderTypeRaw_SelectionChanged;
|
cmbHeaderTypeRaw.SelectionChanged += CmbHeaderTypeRaw_SelectionChanged;
|
||||||
@@ -16,14 +16,8 @@ public partial class AddServerWindow
|
|||||||
btnGUID.Click += btnGUID_Click;
|
btnGUID.Click += btnGUID_Click;
|
||||||
btnGUID5.Click += btnGUID_Click;
|
btnGUID5.Click += btnGUID_Click;
|
||||||
|
|
||||||
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
|
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
|
||||||
cmbNetwork.ItemsSource = Global.Networks;
|
cmbNetwork.ItemsSource = Global.Networks;
|
||||||
if (ViewModel.SelectedSource.Network.IsNullOrEmpty() || !Global.Networks.Contains(ViewModel.SelectedSource.Network))
|
|
||||||
{
|
|
||||||
ViewModel.SelectedSource.Network = Global.DefaultNetwork;
|
|
||||||
}
|
|
||||||
|
|
||||||
cmbHeaderTypeRaw.ItemsSource = new List<string> { Global.None, Global.RawHeaderHttp };
|
cmbHeaderTypeRaw.ItemsSource = new List<string> { Global.None, Global.RawHeaderHttp };
|
||||||
|
|
||||||
@@ -38,8 +32,174 @@ public partial class AddServerWindow
|
|||||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||||
cmbAlpn.ItemsSource = Global.Alpns;
|
cmbAlpn.ItemsSource = Global.Alpns;
|
||||||
|
|
||||||
var lstStreamSecurity = new List<string> { string.Empty, Global.StreamSecurity };
|
gridTlsMore.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
|
this.WhenActivated(disposables =>
|
||||||
|
{
|
||||||
|
var configTypeBindings = new SerialDisposable().DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Address, v => v.txtAddress.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(v => v.ViewModel.SelectedSource.ConfigType)
|
||||||
|
.Subscribe(configType =>
|
||||||
|
{
|
||||||
|
var currentTypeDisposables = new CompositeDisposable();
|
||||||
|
configTypeBindings.Disposable = currentTypeDisposables;
|
||||||
|
|
||||||
|
switch (configType)
|
||||||
|
{
|
||||||
|
case EConfigType.VMess:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Shadowsocks:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.SOCKS:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.HTTP:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.HttpHeadersJson, v => v.txtHttpHeadersJson.Text)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.VLESS:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Trojan:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Hysteria2:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SalamanderPass, v => v.txtPath7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Hy2RealmUrl, v => v.txtHy2RealmUrl.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.GeckoMinPacketSize, v => v.txtMinGeckoPacketSize.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.GeckoMaxPacketSize, v => v.txtMaxGeckoPacketSize.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.TUIC:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.WireGuard:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgPresharedKey, v => v.txtPreSharedKey9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Anytls:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Naive:
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(currentTypeDisposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(currentTypeDisposables);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.RawHeaderType, v => v.cmbHeaderTypeRaw.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostRaw.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathRaw.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.KcpHeaderType, v => v.cmbHeaderTypeKcp.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.KcpSeed, v => v.txtKcpSeed.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.KcpMtu, v => v.txtKcpMtu.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostWs.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathWs.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostHttpupgrade.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathHttpupgrade.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.XhttpMode, v => v.cmbHeaderTypeXhttp.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostXhttp.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathXhttp.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.XhttpExtra, v => v.txtExtraXhttp.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.GrpcMode, v => v.cmbHeaderTypeGrpc.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.GrpcAuthority, v => v.txtRequestHostGrpc.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.GrpcServiceName, v => v.txtPathGrpc.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.AllowInsecure, v => v.togAllowInsecure.IsChecked).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.VerifyPeerCertByName, v => v.txtVerifyPeerCertByName.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
//reality
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.WhenAnyValue(v => v.ViewModel.SelectedSource)
|
||||||
|
.WhereNotNull()
|
||||||
|
.Subscribe(InitializeData)
|
||||||
|
.DisposeWith(disposables);
|
||||||
|
});
|
||||||
|
|
||||||
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeData(ProfileItem profileItem)
|
||||||
|
{
|
||||||
|
Title = $"{profileItem.ConfigType}";
|
||||||
|
var lstStreamSecurity = new List<string> { string.Empty, Global.StreamSecurity };
|
||||||
switch (profileItem.ConfigType)
|
switch (profileItem.ConfigType)
|
||||||
{
|
{
|
||||||
case EConfigType.VMess:
|
case EConfigType.VMess:
|
||||||
@@ -125,160 +285,7 @@ public partial class AddServerWindow
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
||||||
|
cmbStreamSecurity.SelectedItem = profileItem.StreamSecurity;
|
||||||
gridTlsMore.Visibility = Visibility.Collapsed;
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
|
||||||
{
|
|
||||||
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Address, v => v.txtAddress.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
switch (profileItem.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.VMess:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.VmessSecurity, v => v.cmbSecurity.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.SOCKS:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.HTTP:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId4.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtSecurity4.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.HttpHeadersJson, v => v.txtHttpHeadersJson.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId5.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow5.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.VlessEncryption, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Trojan:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId6.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Flow, v => v.cmbFlow6.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Hysteria2:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SalamanderPass, v => v.txtPath7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.HopInterval, v => v.txtHopInt7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.UpMbps, v => v.txtUpMbps7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.DownMbps, v => v.txtDownMbps7.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Hy2RealmUrl, v => v.txtHy2RealmUrl.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.GeckoMinPacketSize, v => v.txtMinGeckoPacketSize.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.GeckoMaxPacketSize, v => v.txtMaxGeckoPacketSize.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.TUIC:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.WireGuard:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgPublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgPresharedKey, v => v.txtPreSharedKey9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgReserved, v => v.txtPath9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgInterfaceAddress, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.WgMtu, v => v.txtShortId9.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Anytls:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Naive:
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(disposables);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.RawHeaderType, v => v.cmbHeaderTypeRaw.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostRaw.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathRaw.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.KcpHeaderType, v => v.cmbHeaderTypeKcp.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.KcpSeed, v => v.txtKcpSeed.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.KcpMtu, v => v.txtKcpMtu.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostWs.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathWs.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostHttpupgrade.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathHttpupgrade.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.XhttpMode, v => v.cmbHeaderTypeXhttp.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Host, v => v.txtRequestHostXhttp.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Path, v => v.txtPathXhttp.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.XhttpExtra, v => v.txtExtraXhttp.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.GrpcMode, v => v.cmbHeaderTypeGrpc.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.GrpcAuthority, v => v.txtRequestHostGrpc.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.GrpcServiceName, v => v.txtPathGrpc.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.AllowInsecure, v => v.togAllowInsecure.IsChecked).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.VerifyPeerCertByName, v => v.txtVerifyPeerCertByName.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
//reality
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Finalmask, v => v.txtFinalmask.Text).DisposeWith(disposables);
|
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertCmd, v => v.btnFetchCert).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.FetchCertChainCmd, v => v.btnFetchCertChain).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
|
||||||
});
|
|
||||||
|
|
||||||
Title = $"{profileItem.ConfigType}";
|
|
||||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
DialogResult = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ public partial class BackupAndRestoreView
|
|||||||
menuLocalBackup.Click += MenuLocalBackup_Click;
|
menuLocalBackup.Click += MenuLocalBackup_Click;
|
||||||
menuLocalRestore.Click += MenuLocalRestore_Click;
|
menuLocalRestore.Click += MenuLocalRestore_Click;
|
||||||
|
|
||||||
ViewModel = new BackupAndRestoreViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.Bind(ViewModel, vm => vm.OperationMsg, v => v.txtMsg.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.OperationMsg, v => v.txtMsg.Text).DisposeWith(disposables);
|
||||||
@@ -43,9 +41,4 @@ public partial class BackupAndRestoreView
|
|||||||
}
|
}
|
||||||
ViewModel?.LocalRestore(fileName);
|
ViewModel?.LocalRestore(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ public partial class CheckUpdateView
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
ViewModel = new CheckUpdateViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
|
||||||
@@ -17,9 +15,4 @@ public partial class CheckUpdateView
|
|||||||
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ public partial class ClashConnectionsView
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
|
|
||||||
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
|
|
||||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
@@ -41,11 +40,6 @@ public partial class ClashConnectionsView
|
|||||||
RestoreUI();
|
RestoreUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BtnAutofitColumnWidth_Click(object sender, RoutedEventArgs e)
|
private void BtnAutofitColumnWidth_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
AutofitColumnWidth();
|
AutofitColumnWidth();
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ public partial class ClashProxiesView
|
|||||||
public ClashProxiesView()
|
public ClashProxiesView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
|
|
||||||
lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick;
|
lstProxyDetails.PreviewMouseDoubleClick += lstProxyDetails_PreviewMouseDoubleClick;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
@@ -31,11 +30,6 @@ public partial class ClashProxiesView
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProxiesView_KeyDown(object sender, KeyEventArgs e)
|
private void ProxiesView_KeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
switch (e.Key)
|
switch (e.Key)
|
||||||
|
|||||||
@@ -8,11 +8,8 @@ public partial class DNSSettingWindow
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Owner = Application.Current.MainWindow;
|
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
|
|
||||||
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy;
|
cmbDirectDNSStrategy.ItemsSource = Global.DomainStrategy;
|
||||||
cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy;
|
cmbRemoteDNSStrategy.ItemsSource = Global.DomainStrategy;
|
||||||
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
||||||
@@ -74,17 +71,6 @@ public partial class DNSSettingWindow
|
|||||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
DialogResult = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void linkDnsObjectDoc_Click(object sender, RoutedEventArgs e)
|
private void linkDnsObjectDoc_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ProcUtils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
|
ProcUtils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
|
||||||
|
|||||||
@@ -8,11 +8,8 @@ public partial class FullConfigTemplateWindow
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Owner = Application.Current.MainWindow;
|
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
|
|
||||||
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.Bind(ViewModel, vm => vm.EnableFullConfigTemplate4Ray, v => v.rayFullConfigTemplateEnable.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.EnableFullConfigTemplate4Ray, v => v.rayFullConfigTemplateEnable.IsChecked).DisposeWith(disposables);
|
||||||
@@ -31,17 +28,6 @@ public partial class FullConfigTemplateWindow
|
|||||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
DialogResult = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void linkFullConfigTemplateDoc_Click(object sender, RoutedEventArgs e)
|
private void linkFullConfigTemplateDoc_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ProcUtils.ProcessStart("https://github.com/2dust/v2rayN/wiki/Description-of-some-ui#%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF%E8%AE%BE%E7%BD%AE");
|
ProcUtils.ProcessStart("https://github.com/2dust/v2rayN/wiki/Description-of-some-ui#%E5%AE%8C%E6%95%B4%E9%85%8D%E7%BD%AE%E6%A8%A1%E6%9D%BF%E8%AE%BE%E7%BD%AE");
|
||||||
|
|||||||
@@ -11,10 +11,6 @@ public partial class GlobalHotkeySettingWindow
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Owner = Application.Current.MainWindow;
|
|
||||||
|
|
||||||
ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
|
|
||||||
|
|
||||||
btnReset.Click += btnReset_Click;
|
btnReset.Click += btnReset_Click;
|
||||||
|
|
||||||
HotkeyManager.Instance.IsPause = true;
|
HotkeyManager.Instance.IsPause = true;
|
||||||
@@ -23,22 +19,11 @@ public partial class GlobalHotkeySettingWindow
|
|||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
BindingData();
|
||||||
});
|
});
|
||||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
BindingData();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
||||||
{
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case EViewAction.CloseWindow:
|
|
||||||
DialogResult = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return await Task.FromResult(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Init()
|
private void Init()
|
||||||
@@ -68,9 +53,13 @@ public partial class GlobalHotkeySettingWindow
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (ViewModel == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var item = ViewModel?.GetKeyEventItem((EGlobalHotkey)txtBox.Tag);
|
var item = ViewModel.GetKeyEventItem((EGlobalHotkey)txtBox.Tag);
|
||||||
var modifierKeys = new Key[] { Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift, Key.LeftAlt, Key.RightAlt, Key.LWin, Key.RWin };
|
var modifierKeys = new[] { Key.LeftCtrl, Key.RightCtrl, Key.LeftShift, Key.RightShift, Key.LeftAlt, Key.RightAlt, Key.LWin, Key.RWin };
|
||||||
|
|
||||||
item.KeyCode = (int)(e.Key == Key.System ? (modifierKeys.Contains(e.SystemKey) ? Key.None : e.SystemKey) : (modifierKeys.Contains(e.Key) ? Key.None : e.Key));
|
item.KeyCode = (int)(e.Key == Key.System ? (modifierKeys.Contains(e.SystemKey) ? Key.None : e.SystemKey) : (modifierKeys.Contains(e.Key) ? Key.None : e.Key));
|
||||||
item.Alt = (Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt;
|
item.Alt = (Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt;
|
||||||
@@ -110,17 +99,17 @@ public partial class GlobalHotkeySettingWindow
|
|||||||
|
|
||||||
if (item.Control)
|
if (item.Control)
|
||||||
{
|
{
|
||||||
res.Append($"{ModifierKeys.Control} +");
|
res.Append($"{ModifierKeys.Control} + ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.Shift)
|
if (item.Shift)
|
||||||
{
|
{
|
||||||
res.Append($"{ModifierKeys.Shift} +");
|
res.Append($"{ModifierKeys.Shift} + ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.Alt)
|
if (item.Alt)
|
||||||
{
|
{
|
||||||
res.Append($"{ModifierKeys.Alt} +");
|
res.Append($"{ModifierKeys.Alt} + ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
|
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user