csharp_style_namespace_declarations = file_scoped

This commit is contained in:
2dust
2025-04-02 11:44:23 +08:00
parent d92540121f
commit 4d3db56065
186 changed files with 23574 additions and 23759 deletions

View File

@@ -1,18 +1,17 @@
using Avalonia;
using Avalonia.Media;
namespace v2rayN.Desktop.Common
namespace v2rayN.Desktop.Common;
public static class AppBuilderExtension
{
public static class AppBuilderExtension
public static AppBuilder WithFontByDefault(this AppBuilder appBuilder)
{
public static AppBuilder WithFontByDefault(this AppBuilder appBuilder)
var uri = Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC");
return appBuilder.With(new FontManagerOptions()
{
var uri = Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC");
return appBuilder.With(new FontManagerOptions()
{
//DefaultFamilyName = uri,
FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily(uri) } }
});
}
//DefaultFamilyName = uri,
FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily(uri) } }
});
}
}

View File

@@ -4,56 +4,55 @@ using Avalonia.Input;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace v2rayN.Desktop.Common
{
internal class AvaUtils
{
public static async Task<string?> GetClipboardData(Window owner)
{
try
{
var clipboard = TopLevel.GetTopLevel(owner)?.Clipboard;
if (clipboard == null)
{
return null;
}
namespace v2rayN.Desktop.Common;
return await clipboard.GetTextAsync();
}
catch
internal class AvaUtils
{
public static async Task<string?> GetClipboardData(Window owner)
{
try
{
var clipboard = TopLevel.GetTopLevel(owner)?.Clipboard;
if (clipboard == null)
{
return null;
}
return await clipboard.GetTextAsync();
}
public static async Task SetClipboardData(Visual? visual, string strData)
catch
{
try
{
var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard;
if (clipboard == null)
return;
var dataObject = new DataObject();
dataObject.Set(DataFormats.Text, strData);
await clipboard.SetDataObjectAsync(dataObject);
}
catch
{
}
}
public static WindowIcon GetAppIcon(ESysProxyType sysProxyType)
{
var index = (int)sysProxyType + 1;
var fileName = Utils.GetPath($"NotifyIcon{index}.ico");
if (File.Exists(fileName))
{
return new(fileName);
}
var uri = new Uri(Path.Combine(Global.AvaAssets, $"NotifyIcon{index}.ico"));
using var bitmap = new Bitmap(AssetLoader.Open(uri));
return new(bitmap);
return null;
}
}
public static async Task SetClipboardData(Visual? visual, string strData)
{
try
{
var clipboard = TopLevel.GetTopLevel(visual)?.Clipboard;
if (clipboard == null)
return;
var dataObject = new DataObject();
dataObject.Set(DataFormats.Text, strData);
await clipboard.SetDataObjectAsync(dataObject);
}
catch
{
}
}
public static WindowIcon GetAppIcon(ESysProxyType sysProxyType)
{
var index = (int)sysProxyType + 1;
var fileName = Utils.GetPath($"NotifyIcon{index}.ico");
if (File.Exists(fileName))
{
return new(fileName);
}
var uri = new Uri(Path.Combine(Global.AvaAssets, $"NotifyIcon{index}.ico"));
using var bitmap = new Bitmap(AssetLoader.Open(uri));
return new(bitmap);
}
}

View File

@@ -3,56 +3,55 @@ using Avalonia.Platform.Storage;
using MsBox.Avalonia;
using MsBox.Avalonia.Enums;
namespace v2rayN.Desktop.Common
namespace v2rayN.Desktop.Common;
internal class UI
{
internal class UI
private static readonly string caption = Global.AppName;
public static async Task<ButtonResult> ShowYesNo(Window owner, string msg)
{
private static readonly string caption = Global.AppName;
var box = MessageBoxManager.GetMessageBoxStandard(caption, msg, ButtonEnum.YesNo);
return await box.ShowWindowDialogAsync(owner);
}
public static async Task<ButtonResult> ShowYesNo(Window owner, string msg)
public static async Task<string?> OpenFileDialog(Window owner, FilePickerFileType? filter)
{
var sp = GetStorageProvider(owner);
if (sp is null)
{
var box = MessageBoxManager.GetMessageBoxStandard(caption, msg, ButtonEnum.YesNo);
return await box.ShowWindowDialogAsync(owner);
return null;
}
public static async Task<string?> OpenFileDialog(Window owner, FilePickerFileType? filter)
// Start async operation to open the dialog.
var files = await sp.OpenFilePickerAsync(new FilePickerOpenOptions
{
var sp = GetStorageProvider(owner);
if (sp is null)
{
return null;
}
AllowMultiple = false,
FileTypeFilter = filter is null ? [FilePickerFileTypes.All, FilePickerFileTypes.ImagePng] : [filter]
});
// Start async operation to open the dialog.
var files = await sp.OpenFilePickerAsync(new FilePickerOpenOptions
{
AllowMultiple = false,
FileTypeFilter = filter is null ? [FilePickerFileTypes.All, FilePickerFileTypes.ImagePng] : [filter]
});
return files.FirstOrDefault()?.TryGetLocalPath();
}
return files.FirstOrDefault()?.TryGetLocalPath();
public static async Task<string?> SaveFileDialog(Window owner, string filter)
{
var sp = GetStorageProvider(owner);
if (sp is null)
{
return null;
}
public static async Task<string?> SaveFileDialog(Window owner, string filter)
// Start async operation to open the dialog.
var files = await sp.SaveFilePickerAsync(new FilePickerSaveOptions
{
var sp = GetStorageProvider(owner);
if (sp is null)
{
return null;
}
});
// Start async operation to open the dialog.
var files = await sp.SaveFilePickerAsync(new FilePickerSaveOptions
{
});
return files?.TryGetLocalPath();
}
return files?.TryGetLocalPath();
}
private static IStorageProvider? GetStorageProvider(Window owner)
{
var topLevel = TopLevel.GetTopLevel(owner);
return topLevel?.StorageProvider;
}
private static IStorageProvider? GetStorageProvider(Window owner)
{
var topLevel = TopLevel.GetTopLevel(owner);
return topLevel?.StorageProvider;
}
}

View File

@@ -2,25 +2,24 @@ using System.Globalization;
using Avalonia.Data.Converters;
using Avalonia.Media;
namespace v2rayN.Desktop.Converters
namespace v2rayN.Desktop.Converters;
public class DelayColorConverter : IValueConverter
{
public class DelayColorConverter : IValueConverter
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
_ = int.TryParse(value?.ToString(), out var delay);
_ = int.TryParse(value?.ToString(), out var delay);
return delay switch
{
<= 0 => new SolidColorBrush(Colors.Red),
<= 500 => new SolidColorBrush(Colors.Green),
_ => new SolidColorBrush(Colors.IndianRed)
};
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
return delay switch
{
return null;
}
<= 0 => new SolidColorBrush(Colors.Red),
<= 500 => new SolidColorBrush(Colors.Green),
_ => new SolidColorBrush(Colors.IndianRed)
};
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
return null;
}
}

View File

@@ -4,88 +4,87 @@ using Avalonia.ReactiveUI;
using Avalonia.Win32.Input;
using GlobalHotKeys;
namespace v2rayN.Desktop.Handler
namespace v2rayN.Desktop.Handler;
public sealed class HotkeyHandler
{
public sealed class HotkeyHandler
private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
public static HotkeyHandler Instance = _instance.Value;
private readonly Dictionary<int, EGlobalHotkey> _hotkeyTriggerDic = new();
private HotKeyManager? _hotKeyManager;
private Config? _config;
private event Action<EGlobalHotkey>? _updateFunc;
public bool IsPause { get; set; } = false;
public void Init(Config config, Action<EGlobalHotkey> updateFunc)
{
private static readonly Lazy<HotkeyHandler> _instance = new(() => new());
public static HotkeyHandler Instance = _instance.Value;
private readonly Dictionary<int, EGlobalHotkey> _hotkeyTriggerDic = new();
private HotKeyManager? _hotKeyManager;
_config = config;
_updateFunc = updateFunc;
private Config? _config;
Register();
}
private event Action<EGlobalHotkey>? _updateFunc;
public void Dispose()
{
_hotKeyManager?.Dispose();
}
public bool IsPause { get; set; } = false;
public void Init(Config config, Action<EGlobalHotkey> updateFunc)
private void Register()
{
if (_config.GlobalHotkeys.Any(t => t.KeyCode > 0) == false)
{
_config = config;
_updateFunc = updateFunc;
return;
}
_hotKeyManager ??= new GlobalHotKeys.HotKeyManager();
_hotkeyTriggerDic.Clear();
Register();
foreach (var item in _config.GlobalHotkeys)
{
if (item.KeyCode is null or 0)
{
continue;
}
var vKey = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode);
var modifiers = Modifiers.None;
if (item.Control)
{
modifiers |= Modifiers.Control;
}
if (item.Shift)
{
modifiers |= Modifiers.Shift;
}
if (item.Alt)
{
modifiers |= Modifiers.Alt;
}
var result = _hotKeyManager?.Register((VirtualKeyCode)vKey, modifiers);
if (result?.IsSuccessful == true)
{
_hotkeyTriggerDic.Add(result.Id, item.EGlobalHotkey);
}
}
public void Dispose()
_hotKeyManager?.HotKeyPressed
.ObserveOn(AvaloniaScheduler.Instance)
.Subscribe(OnNext);
}
private void OnNext(HotKey key)
{
if (_updateFunc == null || IsPause)
{
_hotKeyManager?.Dispose();
return;
}
private void Register()
if (_hotkeyTriggerDic.TryGetValue(key.Id, out var value))
{
if (_config.GlobalHotkeys.Any(t => t.KeyCode > 0) == false)
{
return;
}
_hotKeyManager ??= new GlobalHotKeys.HotKeyManager();
_hotkeyTriggerDic.Clear();
foreach (var item in _config.GlobalHotkeys)
{
if (item.KeyCode is null or 0)
{
continue;
}
var vKey = KeyInterop.VirtualKeyFromKey((Key)item.KeyCode);
var modifiers = Modifiers.None;
if (item.Control)
{
modifiers |= Modifiers.Control;
}
if (item.Shift)
{
modifiers |= Modifiers.Shift;
}
if (item.Alt)
{
modifiers |= Modifiers.Alt;
}
var result = _hotKeyManager?.Register((VirtualKeyCode)vKey, modifiers);
if (result?.IsSuccessful == true)
{
_hotkeyTriggerDic.Add(result.Id, item.EGlobalHotkey);
}
}
_hotKeyManager?.HotKeyPressed
.ObserveOn(AvaloniaScheduler.Instance)
.Subscribe(OnNext);
}
private void OnNext(HotKey key)
{
if (_updateFunc == null || IsPause)
{
return;
}
if (_hotkeyTriggerDic.TryGetValue(key.Id, out var value))
{
_updateFunc?.Invoke(value);
}
_updateFunc?.Invoke(value);
}
}
}

View File

@@ -9,100 +9,139 @@ using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using Semi.Avalonia;
namespace v2rayN.Desktop.ViewModels
namespace v2rayN.Desktop.ViewModels;
public class ThemeSettingViewModel : MyReactiveObject
{
public class ThemeSettingViewModel : MyReactiveObject
[Reactive] public string CurrentTheme { get; set; }
[Reactive] public int CurrentFontSize { get; set; }
[Reactive] public string CurrentLanguage { get; set; }
public ThemeSettingViewModel()
{
[Reactive] public string CurrentTheme { get; set; }
_config = AppHandler.Instance.Config;
[Reactive] public int CurrentFontSize { get; set; }
BindingUI();
RestoreUI();
}
[Reactive] public string CurrentLanguage { get; set; }
private void RestoreUI()
{
ModifyTheme();
ModifyFontFamily();
ModifyFontSize();
}
public ThemeSettingViewModel()
{
_config = AppHandler.Instance.Config;
private void BindingUI()
{
CurrentTheme = _config.UiItem.CurrentTheme;
CurrentFontSize = _config.UiItem.CurrentFontSize;
CurrentLanguage = _config.UiItem.CurrentLanguage;
BindingUI();
RestoreUI();
}
private void RestoreUI()
{
ModifyTheme();
ModifyFontFamily();
ModifyFontSize();
}
private void BindingUI()
{
CurrentTheme = _config.UiItem.CurrentTheme;
CurrentFontSize = _config.UiItem.CurrentFontSize;
CurrentLanguage = _config.UiItem.CurrentLanguage;
this.WhenAnyValue(x => x.CurrentTheme)
.Subscribe(c =>
{
if (_config.UiItem.CurrentTheme != CurrentTheme)
{
_config.UiItem.CurrentTheme = CurrentTheme;
ModifyTheme();
ConfigHandler.SaveConfig(_config);
}
});
this.WhenAnyValue(
x => x.CurrentFontSize,
y => y > 0)
.Subscribe(c =>
{
if (_config.UiItem.CurrentFontSize != CurrentFontSize && CurrentFontSize >= Global.MinFontSize)
{
_config.UiItem.CurrentFontSize = CurrentFontSize;
ModifyFontSize();
ConfigHandler.SaveConfig(_config);
}
});
this.WhenAnyValue(
x => x.CurrentLanguage,
y => y != null && !y.IsNullOrEmpty())
.Subscribe(c =>
{
if (CurrentLanguage.IsNotEmpty() && _config.UiItem.CurrentLanguage != CurrentLanguage)
{
_config.UiItem.CurrentLanguage = CurrentLanguage;
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
ConfigHandler.SaveConfig(_config);
NoticeHandler.Instance.Enqueue(ResUI.NeedRebootTips);
}
});
}
private void ModifyTheme()
{
var app = Application.Current;
if (app is not null)
this.WhenAnyValue(x => x.CurrentTheme)
.Subscribe(c =>
{
app.RequestedThemeVariant = CurrentTheme switch
if (_config.UiItem.CurrentTheme != CurrentTheme)
{
nameof(ETheme.Dark) => ThemeVariant.Dark,
nameof(ETheme.Light) => ThemeVariant.Light,
nameof(ETheme.Aquatic) => SemiTheme.Aquatic,
nameof(ETheme.Desert) => SemiTheme.Desert,
nameof(ETheme.Dusk) => SemiTheme.Dusk,
nameof(ETheme.NightSky) => SemiTheme.NightSky,
_ => ThemeVariant.Default,
};
}
_config.UiItem.CurrentTheme = CurrentTheme;
ModifyTheme();
ConfigHandler.SaveConfig(_config);
}
});
this.WhenAnyValue(
x => x.CurrentFontSize,
y => y > 0)
.Subscribe(c =>
{
if (_config.UiItem.CurrentFontSize != CurrentFontSize && CurrentFontSize >= Global.MinFontSize)
{
_config.UiItem.CurrentFontSize = CurrentFontSize;
ModifyFontSize();
ConfigHandler.SaveConfig(_config);
}
});
this.WhenAnyValue(
x => x.CurrentLanguage,
y => y != null && !y.IsNullOrEmpty())
.Subscribe(c =>
{
if (CurrentLanguage.IsNotEmpty() && _config.UiItem.CurrentLanguage != CurrentLanguage)
{
_config.UiItem.CurrentLanguage = CurrentLanguage;
Thread.CurrentThread.CurrentUICulture = new(CurrentLanguage);
ConfigHandler.SaveConfig(_config);
NoticeHandler.Instance.Enqueue(ResUI.NeedRebootTips);
}
});
}
private void ModifyTheme()
{
var app = Application.Current;
if (app is not null)
{
app.RequestedThemeVariant = CurrentTheme switch
{
nameof(ETheme.Dark) => ThemeVariant.Dark,
nameof(ETheme.Light) => ThemeVariant.Light,
nameof(ETheme.Aquatic) => SemiTheme.Aquatic,
nameof(ETheme.Desert) => SemiTheme.Desert,
nameof(ETheme.Dusk) => SemiTheme.Dusk,
nameof(ETheme.NightSky) => SemiTheme.NightSky,
_ => ThemeVariant.Default,
};
}
}
private void ModifyFontSize()
{
double size = CurrentFontSize;
if (size < Global.MinFontSize)
return;
Style style = new(x => Selectors.Or(
x.OfType<Button>(),
x.OfType<TextBox>(),
x.OfType<TextBlock>(),
x.OfType<Menu>(),
x.OfType<ContextMenu>(),
x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>(),
x.OfType<HeaderedContentControl>()
));
style.Add(new Setter()
{
Property = TemplatedControl.FontSizeProperty,
Value = size,
});
Application.Current?.Styles.Add(style);
ModifyFontSizeEx(size);
}
private void ModifyFontSizeEx(double size)
{
//DataGrid
var rowHeight = 20 + (size / 2);
var style = new Style(x => x.OfType<DataGrid>());
style.Add(new Setter(DataGrid.RowHeightProperty, rowHeight));
Application.Current?.Styles.Add(style);
}
private void ModifyFontFamily()
{
var currentFontFamily = _config.UiItem.CurrentFontFamily;
if (currentFontFamily.IsNullOrEmpty())
{
return;
}
private void ModifyFontSize()
try
{
double size = CurrentFontSize;
if (size < Global.MinFontSize)
return;
Style style = new(x => Selectors.Or(
x.OfType<Button>(),
x.OfType<TextBox>(),
@@ -111,59 +150,19 @@ namespace v2rayN.Desktop.ViewModels
x.OfType<ContextMenu>(),
x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>(),
x.OfType<HeaderedContentControl>()
x.OfType<HeaderedContentControl>(),
x.OfType<WindowNotificationManager>()
));
style.Add(new Setter()
{
Property = TemplatedControl.FontSizeProperty,
Value = size,
Property = TemplatedControl.FontFamilyProperty,
Value = new FontFamily(currentFontFamily),
});
Application.Current?.Styles.Add(style);
ModifyFontSizeEx(size);
}
private void ModifyFontSizeEx(double size)
catch (Exception ex)
{
//DataGrid
var rowHeight = 20 + (size / 2);
var style = new Style(x => x.OfType<DataGrid>());
style.Add(new Setter(DataGrid.RowHeightProperty, rowHeight));
Application.Current?.Styles.Add(style);
}
private void ModifyFontFamily()
{
var currentFontFamily = _config.UiItem.CurrentFontFamily;
if (currentFontFamily.IsNullOrEmpty())
{
return;
}
try
{
Style style = new(x => Selectors.Or(
x.OfType<Button>(),
x.OfType<TextBox>(),
x.OfType<TextBlock>(),
x.OfType<Menu>(),
x.OfType<ContextMenu>(),
x.OfType<DataGridRow>(),
x.OfType<ListBoxItem>(),
x.OfType<HeaderedContentControl>(),
x.OfType<WindowNotificationManager>()
));
style.Add(new Setter()
{
Property = TemplatedControl.FontFamilyProperty,
Value = new FontFamily(currentFontFamily),
});
Application.Current?.Styles.Add(style);
}
catch (Exception ex)
{
Logging.SaveLog("ModifyFontFamily", ex);
}
Logging.SaveLog("ModifyFontFamily", ex);
}
}
}

View File

@@ -4,69 +4,68 @@ using Avalonia.ReactiveUI;
using ReactiveUI;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class AddServer2Window : ReactiveWindow<AddServer2ViewModel>
{
public partial class AddServer2Window : ReactiveWindow<AddServer2ViewModel>
public AddServer2Window()
{
public AddServer2Window()
InitializeComponent();
}
public AddServer2Window(ProfileItem profileItem)
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
foreach (ECoreType it in Enum.GetValues(typeof(ECoreType)))
{
InitializeComponent();
if (it == ECoreType.v2rayN)
continue;
cmbCoreType.Items.Add(it.ToString());
}
cmbCoreType.Items.Add(string.Empty);
this.WhenActivated(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.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.DisplayLog, v => v.togDisplayLog.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).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.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
case EViewAction.BrowseServer:
var fileName = await UI.OpenFileDialog(this, null);
if (fileName.IsNullOrEmpty())
{
return false;
}
ViewModel?.BrowseServer(fileName);
break;
}
public AddServer2Window(ProfileItem profileItem)
{
InitializeComponent();
return await Task.FromResult(true);
}
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
foreach (ECoreType it in Enum.GetValues(typeof(ECoreType)))
{
if (it == ECoreType.v2rayN)
continue;
cmbCoreType.Items.Add(it.ToString());
}
cmbCoreType.Items.Add(string.Empty);
this.WhenActivated(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.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.DisplayLog, v => v.togDisplayLog.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.PreSocksPort, v => v.txtPreSocksPort.Text).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.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.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)
{
txtRemarks.Focus();
}
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
txtRemarks.Focus();
}
}

View File

@@ -4,369 +4,368 @@ using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using ReactiveUI;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
{
public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
public AddServerWindow()
{
public AddServerWindow()
InitializeComponent();
}
public AddServerWindow(ProfileItem profileItem)
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
btnGUID5.Click += btnGUID_Click;
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
Global.CoreTypes.ForEach(it =>
{
InitializeComponent();
cmbCoreType.Items.Add(it);
});
cmbCoreType.Items.Add(string.Empty);
cmbStreamSecurity.Items.Add(string.Empty);
cmbStreamSecurity.Items.Add(Global.StreamSecurity);
Global.Networks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
Global.Fingerprints.ForEach(it =>
{
cmbFingerprint.Items.Add(it);
cmbFingerprint2.Items.Add(it);
});
Global.AllowInsecure.ForEach(it =>
{
cmbAllowInsecure.Items.Add(it);
});
Global.Alpns.ForEach(it =>
{
cmbAlpn.Items.Add(it);
});
switch (profileItem.ConfigType)
{
case EConfigType.VMess:
gridVMess.IsVisible = true;
Global.VmessSecurities.ForEach(it =>
{
cmbSecurity.Items.Add(it);
});
if (profileItem.Security.IsNullOrEmpty())
{
profileItem.Security = Global.DefaultSecurity;
}
break;
case EConfigType.Shadowsocks:
gridSs.IsVisible = true;
AppHandler.Instance.GetShadowsocksSecurities(profileItem).ForEach(it =>
{
cmbSecurity3.Items.Add(it);
});
break;
case EConfigType.SOCKS:
case EConfigType.HTTP:
gridSocks.IsVisible = true;
break;
case EConfigType.VLESS:
gridVLESS.IsVisible = true;
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
Global.Flows.ForEach(it =>
{
cmbFlow5.Items.Add(it);
});
if (profileItem.Security.IsNullOrEmpty())
{
profileItem.Security = Global.None;
}
break;
case EConfigType.Trojan:
gridTrojan.IsVisible = true;
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
Global.Flows.ForEach(it =>
{
cmbFlow6.Items.Add(it);
});
break;
case EConfigType.Hysteria2:
gridHysteria2.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
cmbCoreType.IsEnabled = false;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty;
break;
case EConfigType.TUIC:
gridTuic.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
cmbCoreType.IsEnabled = false;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty;
Global.TuicCongestionControls.ForEach(it =>
{
cmbHeaderType8.Items.Add(it);
});
break;
case EConfigType.WireGuard:
gridWireguard.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
gridTls.IsVisible = false;
cmbCoreType.IsEnabled = false;
break;
}
public AddServerWindow(ProfileItem profileItem)
gridTlsMore.IsVisible = false;
this.WhenActivated(disposables =>
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
btnGUID5.Click += btnGUID_Click;
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
Global.CoreTypes.ForEach(it =>
{
cmbCoreType.Items.Add(it);
});
cmbCoreType.Items.Add(string.Empty);
cmbStreamSecurity.Items.Add(string.Empty);
cmbStreamSecurity.Items.Add(Global.StreamSecurity);
Global.Networks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
Global.Fingerprints.ForEach(it =>
{
cmbFingerprint.Items.Add(it);
cmbFingerprint2.Items.Add(it);
});
Global.AllowInsecure.ForEach(it =>
{
cmbAllowInsecure.Items.Add(it);
});
Global.Alpns.ForEach(it =>
{
cmbAlpn.Items.Add(it);
});
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:
gridVMess.IsVisible = true;
Global.VmessSecurities.ForEach(it =>
{
cmbSecurity.Items.Add(it);
});
if (profileItem.Security.IsNullOrEmpty())
{
profileItem.Security = Global.DefaultSecurity;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.Shadowsocks:
gridSs.IsVisible = true;
AppHandler.Instance.GetShadowsocksSecurities(profileItem).ForEach(it =>
{
cmbSecurity3.Items.Add(it);
});
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.SOCKS:
case EConfigType.HTTP:
gridSocks.IsVisible = true;
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId4.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity4.Text).DisposeWith(disposables);
break;
case EConfigType.VLESS:
gridVLESS.IsVisible = true;
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
Global.Flows.ForEach(it =>
{
cmbFlow5.Items.Add(it);
});
if (profileItem.Security.IsNullOrEmpty())
{
profileItem.Security = Global.None;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
break;
case EConfigType.Trojan:
gridTrojan.IsVisible = true;
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
Global.Flows.ForEach(it =>
{
cmbFlow6.Items.Add(it);
});
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.Hysteria2:
gridHysteria2.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
cmbCoreType.IsEnabled = false;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty;
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
break;
case EConfigType.TUIC:
gridTuic.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
cmbCoreType.IsEnabled = false;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty;
Global.TuicCongestionControls.ForEach(it =>
{
cmbHeaderType8.Items.Add(it);
});
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.WireGuard:
gridWireguard.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
gridTls.IsVisible = false;
cmbCoreType.IsEnabled = false;
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.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.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).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);
//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.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
this.Title = $"{profileItem.ConfigType}";
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
}
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
txtRemarks.Focus();
}
private void CmbNetwork_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
SetHeaderType();
SetTips();
}
private void CmbStreamSecurity_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
var security = cmbStreamSecurity.SelectedItem.ToString();
if (security == Global.StreamSecurityReality)
{
gridRealityMore.IsVisible = true;
gridTlsMore.IsVisible = false;
}
else if (security == Global.StreamSecurity)
{
gridRealityMore.IsVisible = false;
gridTlsMore.IsVisible = true;
}
else
{
gridRealityMore.IsVisible = false;
gridTlsMore.IsVisible = false;
}
}
this.WhenActivated(disposables =>
private void btnGUID_Click(object? sender, RoutedEventArgs e)
{
txtId.Text =
txtId5.Text = Utils.GetGuid();
}
private void SetHeaderType()
{
cmbHeaderType.Items.Clear();
var network = cmbNetwork.SelectedItem.ToString();
if (network.IsNullOrEmpty())
{
cmbHeaderType.Items.Add(Global.None);
return;
}
if (network == nameof(ETransport.tcp))
{
cmbHeaderType.Items.Add(Global.None);
cmbHeaderType.Items.Add(Global.TcpHeaderHttp);
}
else if (network is nameof(ETransport.kcp) or nameof(ETransport.quic))
{
cmbHeaderType.Items.Add(Global.None);
Global.KcpHeaderTypes.ForEach(it =>
{
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.Id, v => v.txtId.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.Shadowsocks:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.SOCKS:
case EConfigType.HTTP:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId4.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity4.Text).DisposeWith(disposables);
break;
case EConfigType.VLESS:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
break;
case EConfigType.Trojan:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.Hysteria2:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId7.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath7.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Ports, v => v.txtPorts7.Text).DisposeWith(disposables);
break;
case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.WireGuard:
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.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.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).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);
//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.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
cmbHeaderType.Items.Add(it);
});
this.Title = $"{profileItem.ConfigType}";
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
else if (network is nameof(ETransport.xhttp))
{
switch (action)
Global.XhttpMode.ForEach(it =>
{
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
cmbHeaderType.Items.Add(it);
});
}
private void Window_Loaded(object? sender, RoutedEventArgs e)
else if (network == nameof(ETransport.grpc))
{
txtRemarks.Focus();
cmbHeaderType.Items.Add(Global.GrpcGunMode);
cmbHeaderType.Items.Add(Global.GrpcMultiMode);
}
private void CmbNetwork_SelectionChanged(object? sender, SelectionChangedEventArgs e)
else
{
SetHeaderType();
SetTips();
cmbHeaderType.Items.Add(Global.None);
}
cmbHeaderType.SelectedIndex = 0;
}
private void CmbStreamSecurity_SelectionChanged(object? sender, SelectionChangedEventArgs e)
private void SetTips()
{
var network = cmbNetwork.SelectedItem.ToString();
if (network.IsNullOrEmpty())
{
var security = cmbStreamSecurity.SelectedItem.ToString();
if (security == Global.StreamSecurityReality)
{
gridRealityMore.IsVisible = true;
gridTlsMore.IsVisible = false;
}
else if (security == Global.StreamSecurity)
{
gridRealityMore.IsVisible = false;
gridTlsMore.IsVisible = true;
}
else
{
gridRealityMore.IsVisible = false;
gridTlsMore.IsVisible = false;
}
network = Global.DefaultNetwork;
}
labHeaderType.IsVisible = true;
btnExtra.IsVisible = false;
tipRequestHost.Text =
tipPath.Text =
tipHeaderType.Text = string.Empty;
private void btnGUID_Click(object? sender, RoutedEventArgs e)
switch (network)
{
txtId.Text =
txtId5.Text = Utils.GetGuid();
}
case nameof(ETransport.tcp):
tipRequestHost.Text = ResUI.TransportRequestHostTip1;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip1;
break;
private void SetHeaderType()
{
cmbHeaderType.Items.Clear();
case nameof(ETransport.kcp):
tipHeaderType.Text = ResUI.TransportHeaderTypeTip2;
tipPath.Text = ResUI.TransportPathTip5;
break;
var network = cmbNetwork.SelectedItem.ToString();
if (network.IsNullOrEmpty())
{
cmbHeaderType.Items.Add(Global.None);
return;
}
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
break;
if (network == nameof(ETransport.tcp))
{
cmbHeaderType.Items.Add(Global.None);
cmbHeaderType.Items.Add(Global.TcpHeaderHttp);
}
else if (network is nameof(ETransport.kcp) or nameof(ETransport.quic))
{
cmbHeaderType.Items.Add(Global.None);
Global.KcpHeaderTypes.ForEach(it =>
{
cmbHeaderType.Items.Add(it);
});
}
else if (network is nameof(ETransport.xhttp))
{
Global.XhttpMode.ForEach(it =>
{
cmbHeaderType.Items.Add(it);
});
}
else if (network == nameof(ETransport.grpc))
{
cmbHeaderType.Items.Add(Global.GrpcGunMode);
cmbHeaderType.Items.Add(Global.GrpcMultiMode);
}
else
{
cmbHeaderType.Items.Add(Global.None);
}
cmbHeaderType.SelectedIndex = 0;
}
case nameof(ETransport.xhttp):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip5;
labHeaderType.IsVisible = false;
btnExtra.IsVisible = true;
break;
private void SetTips()
{
var network = cmbNetwork.SelectedItem.ToString();
if (network.IsNullOrEmpty())
{
network = Global.DefaultNetwork;
}
labHeaderType.IsVisible = true;
btnExtra.IsVisible = false;
tipRequestHost.Text =
tipPath.Text =
tipHeaderType.Text = string.Empty;
case nameof(ETransport.h2):
tipRequestHost.Text = ResUI.TransportRequestHostTip3;
tipPath.Text = ResUI.TransportPathTip2;
break;
switch (network)
{
case nameof(ETransport.tcp):
tipRequestHost.Text = ResUI.TransportRequestHostTip1;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip1;
break;
case nameof(ETransport.quic):
tipRequestHost.Text = ResUI.TransportRequestHostTip4;
tipPath.Text = ResUI.TransportPathTip3;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip3;
break;
case nameof(ETransport.kcp):
tipHeaderType.Text = ResUI.TransportHeaderTypeTip2;
tipPath.Text = ResUI.TransportPathTip5;
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
break;
case nameof(ETransport.xhttp):
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
tipPath.Text = ResUI.TransportPathTip1;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip5;
labHeaderType.IsVisible = false;
btnExtra.IsVisible = true;
break;
case nameof(ETransport.h2):
tipRequestHost.Text = ResUI.TransportRequestHostTip3;
tipPath.Text = ResUI.TransportPathTip2;
break;
case nameof(ETransport.quic):
tipRequestHost.Text = ResUI.TransportRequestHostTip4;
tipPath.Text = ResUI.TransportPathTip3;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip3;
break;
case nameof(ETransport.grpc):
tipRequestHost.Text = ResUI.TransportRequestHostTip5;
tipPath.Text = ResUI.TransportPathTip4;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip4;
labHeaderType.IsVisible = false;
break;
}
case nameof(ETransport.grpc):
tipRequestHost.Text = ResUI.TransportRequestHostTip5;
tipPath.Text = ResUI.TransportPathTip4;
tipHeaderType.Text = ResUI.TransportHeaderTypeTip4;
labHeaderType.IsVisible = false;
break;
}
}
}

View File

@@ -5,68 +5,67 @@ using Avalonia.ReactiveUI;
using ReactiveUI;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class BackupAndRestoreView : ReactiveUserControl<BackupAndRestoreViewModel>
{
public partial class BackupAndRestoreView : ReactiveUserControl<BackupAndRestoreViewModel>
private Window? _window;
public BackupAndRestoreView()
{
private Window? _window;
InitializeComponent();
}
public BackupAndRestoreView()
public BackupAndRestoreView(Window window)
{
_window = window;
InitializeComponent();
menuLocalBackup.Click += MenuLocalBackup_Click;
menuLocalRestore.Click += MenuLocalRestore_Click;
ViewModel = new BackupAndRestoreViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
{
InitializeComponent();
this.Bind(ViewModel, vm => vm.OperationMsg, v => v.txtMsg.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Url, v => v.txtWebDavUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.UserName, v => v.txtWebDavUserName.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtWebDavPassword.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.DirName, v => v.txtWebDavDirName.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.WebDavCheckCmd, v => v.menuWebDavCheck).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoteBackupCmd, v => v.menuRemoteBackup).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoteRestoreCmd, v => v.menuRemoteRestore).DisposeWith(disposables);
});
}
private async void MenuLocalBackup_Click(object? sender, RoutedEventArgs e)
{
var fileName = await UI.SaveFileDialog(_window, "Zip|*.zip");
if (fileName.IsNullOrEmpty())
{
return;
}
public BackupAndRestoreView(Window window)
ViewModel?.LocalBackup(fileName);
}
private async void MenuLocalRestore_Click(object? sender, RoutedEventArgs e)
{
var fileName = await UI.OpenFileDialog(_window, null);
if (fileName.IsNullOrEmpty())
{
_window = window;
InitializeComponent();
menuLocalBackup.Click += MenuLocalBackup_Click;
menuLocalRestore.Click += MenuLocalRestore_Click;
ViewModel = new BackupAndRestoreViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.OperationMsg, v => v.txtMsg.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Url, v => v.txtWebDavUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.UserName, v => v.txtWebDavUserName.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtWebDavPassword.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.DirName, v => v.txtWebDavDirName.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.WebDavCheckCmd, v => v.menuWebDavCheck).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoteBackupCmd, v => v.menuRemoteBackup).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoteRestoreCmd, v => v.menuRemoteRestore).DisposeWith(disposables);
});
return;
}
private async void MenuLocalBackup_Click(object? sender, RoutedEventArgs e)
{
var fileName = await UI.SaveFileDialog(_window, "Zip|*.zip");
if (fileName.IsNullOrEmpty())
{
return;
}
ViewModel?.LocalRestore(fileName);
}
ViewModel?.LocalBackup(fileName);
}
private async void MenuLocalRestore_Click(object? sender, RoutedEventArgs e)
{
var fileName = await UI.OpenFileDialog(_window, null);
if (fileName.IsNullOrEmpty())
{
return;
}
ViewModel?.LocalRestore(fileName);
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
return await Task.FromResult(true);
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
return await Task.FromResult(true);
}
}

View File

@@ -3,47 +3,46 @@ using Avalonia.ReactiveUI;
using Avalonia.Threading;
using ReactiveUI;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
{
public partial class CheckUpdateView : ReactiveUserControl<CheckUpdateViewModel>
public CheckUpdateView()
{
public CheckUpdateView()
InitializeComponent();
ViewModel = new CheckUpdateViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
{
InitializeComponent();
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
ViewModel = new CheckUpdateViewModel(UpdateViewHandler);
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
});
}
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.CheckUpdateModels, v => v.lstCheckUpdates.ItemsSource).DisposeWith(disposables);
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.DispatcherCheckUpdate:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.UpdateViewResult((CheckUpdateModel)obj),
DispatcherPriority.Default);
break;
this.Bind(ViewModel, vm => vm.EnableCheckPreReleaseUpdate, v => v.togEnableCheckPreReleaseUpdate.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CheckUpdateCmd, v => v.btnCheckUpdate).DisposeWith(disposables);
});
case EViewAction.DispatcherCheckUpdateFinished:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.UpdateFinishedResult((bool)obj),
DispatcherPriority.Default);
break;
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.DispatcherCheckUpdate:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.UpdateViewResult((CheckUpdateModel)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherCheckUpdateFinished:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.UpdateFinishedResult((bool)obj),
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true);
}
return await Task.FromResult(true);
}
}

View File

@@ -5,62 +5,61 @@ using Avalonia.ReactiveUI;
using Avalonia.Threading;
using ReactiveUI;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class ClashConnectionsView : ReactiveUserControl<ClashConnectionsViewModel>
{
public partial class ClashConnectionsView : ReactiveUserControl<ClashConnectionsViewModel>
public ClashConnectionsView()
{
public ClashConnectionsView()
InitializeComponent();
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
this.WhenActivated(disposables =>
{
InitializeComponent();
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
this.OneWayBind(ViewModel, vm => vm.ConnectionItems, v => v.lstConnections.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstConnections.SelectedItem).DisposeWith(disposables);
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.ConnectionItems, v => v.lstConnections.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstConnections.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseCmd, v => v.menuConnectionClose).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseCmd, v => v.menuConnectionClose).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
});
}
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
});
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.DispatcherRefreshConnections:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshConnections((List<ConnectionItem>?)obj),
DispatcherPriority.Default);
break;
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.DispatcherRefreshConnections:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshConnections((List<ConnectionItem>?)obj),
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true);
}
return await Task.FromResult(true);
}
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
{
AutofitColumnWidth();
}
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
private void AutofitColumnWidth()
{
foreach (var it in lstConnections.Columns)
{
AutofitColumnWidth();
}
private void AutofitColumnWidth()
{
foreach (var it in lstConnections.Columns)
{
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
private void btnClose_Click(object? sender, RoutedEventArgs e)
{
ViewModel?.ClashConnectionClose(false);
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
private void btnClose_Click(object? sender, RoutedEventArgs e)
{
ViewModel?.ClashConnectionClose(false);
}
}

View File

@@ -6,77 +6,76 @@ using DynamicData;
using ReactiveUI;
using Splat;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewModel>
{
public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewModel>
public ClashProxiesView()
{
public ClashProxiesView()
InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
this.KeyDown += ClashProxiesView_KeyDown;
this.WhenActivated(disposables =>
{
InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ClashProxiesViewModel));
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
this.KeyDown += ClashProxiesView_KeyDown;
this.OneWayBind(ViewModel, vm => vm.ProxyGroups, v => v.lstProxyGroups.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedGroup, v => v.lstProxyGroups.SelectedItem).DisposeWith(disposables);
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.ProxyGroups, v => v.lstProxyGroups.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedGroup, v => v.lstProxyGroups.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ProxyDetails, v => v.lstProxyDetails.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedDetail, v => v.lstProxyDetails.SelectedItem).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ProxyDetails, v => v.lstProxyDetails.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedDetail, v => v.lstProxyDetails.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesReloadCmd, v => v.menuProxiesReload).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelayTestCmd, v => v.menuProxiesDelaytest).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesReloadCmd, v => v.menuProxiesReload).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelayTestCmd, v => v.menuProxiesDelaytest).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelayTestPartCmd, v => v.menuProxiesDelaytestPart).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesSelectActivityCmd, v => v.menuProxiesSelectActivity).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesDelayTestPartCmd, v => v.menuProxiesDelaytestPart).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ProxiesSelectActivityCmd, v => v.menuProxiesSelectActivity).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RuleModeSelected, v => v.cmbRulemode.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
});
}
this.Bind(ViewModel, vm => vm.RuleModeSelected, v => v.cmbRulemode.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
});
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.DispatcherRefreshProxyGroups:
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshProxyGroups(),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherProxiesDelayTest:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.ProxiesDelayTestResult((SpeedTestResult)obj),
DispatcherPriority.Default);
break;
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
return await Task.FromResult(true);
}
private void ClashProxiesView_KeyDown(object? sender, KeyEventArgs e)
{
switch (e.Key)
{
switch (action)
{
case EViewAction.DispatcherRefreshProxyGroups:
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshProxyGroups(),
DispatcherPriority.Default);
break;
case Key.F5:
ViewModel?.ProxiesReload();
break;
case EViewAction.DispatcherProxiesDelayTest:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.ProxiesDelayTestResult((SpeedTestResult)obj),
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true);
}
private void ClashProxiesView_KeyDown(object? sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.F5:
ViewModel?.ProxiesReload();
break;
case Key.Enter:
ViewModel?.SetActiveProxy();
break;
}
}
private void LstProxyDetails_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.SetActiveProxy();
case Key.Enter:
ViewModel?.SetActiveProxy();
break;
}
}
private void LstProxyDetails_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.SetActiveProxy();
}
}

View File

@@ -3,74 +3,73 @@ using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using ReactiveUI;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class DNSSettingWindow : ReactiveWindow<DNSSettingViewModel>
{
public partial class DNSSettingWindow : ReactiveWindow<DNSSettingViewModel>
private static Config _config;
public DNSSettingWindow()
{
private static Config _config;
InitializeComponent();
public DNSSettingWindow()
_config = AppHandler.Instance.Config;
btnCancel.Click += (s, e) => this.Close();
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
Global.DomainStrategy4Freedoms.ForEach(it =>
{
InitializeComponent();
_config = AppHandler.Instance.Config;
btnCancel.Click += (s, e) => this.Close();
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
Global.DomainStrategy4Freedoms.ForEach(it =>
{
cmbdomainStrategy4Freedom.Items.Add(it);
});
Global.SingboxDomainStrategy4Out.ForEach(it =>
{
cmbdomainStrategy4Out.Items.Add(it);
});
Global.DomainDNSAddress.ForEach(it =>
{
cmbdomainDNSAddress.Items.Add(it);
});
Global.SingboxDomainDNSAddress.ForEach(it =>
{
cmbdomainDNSAddress2.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.UseSystemHosts, v => v.togUseSystemHosts.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom, v => v.cmbdomainStrategy4Freedom.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainDNSAddress, v => v.cmbdomainDNSAddress.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NormalDNS, v => v.txtnormalDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2, v => v.cmbdomainStrategy4Out.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainDNSAddress2, v => v.cmbdomainDNSAddress2.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NormalDNS2, v => v.txtnormalDNS2.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunDNS2, v => v.txttunDNS2.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCmd, v => v.btnImportDefConfig4V2ray).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCmd, v => v.btnImportDefConfig4Singbox).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
cmbdomainStrategy4Freedom.Items.Add(it);
});
Global.SingboxDomainStrategy4Out.ForEach(it =>
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
}
private void linkDnsObjectDoc_Click(object? sender, RoutedEventArgs e)
cmbdomainStrategy4Out.Items.Add(it);
});
Global.DomainDNSAddress.ForEach(it =>
{
ProcUtils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
}
private void linkDnsSingboxObjectDoc_Click(object? sender, RoutedEventArgs e)
cmbdomainDNSAddress.Items.Add(it);
});
Global.SingboxDomainDNSAddress.ForEach(it =>
{
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/dns/");
cmbdomainDNSAddress2.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.UseSystemHosts, v => v.togUseSystemHosts.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom, v => v.cmbdomainStrategy4Freedom.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainDNSAddress, v => v.cmbdomainDNSAddress.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NormalDNS, v => v.txtnormalDNS.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2, v => v.cmbdomainStrategy4Out.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainDNSAddress2, v => v.cmbdomainDNSAddress2.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NormalDNS2, v => v.txtnormalDNS2.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunDNS2, v => v.txttunDNS2.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCmd, v => v.btnImportDefConfig4V2ray).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCmd, v => v.btnImportDefConfig4Singbox).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
}
private void linkDnsObjectDoc_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://xtls.github.io/config/dns.html#dnsobject");
}
private void linkDnsSingboxObjectDoc_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/dns/");
}
}

View File

@@ -7,132 +7,131 @@ using Avalonia.ReactiveUI;
using ReactiveUI;
using v2rayN.Desktop.Handler;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class GlobalHotkeySettingWindow : ReactiveWindow<GlobalHotkeySettingViewModel>
{
public partial class GlobalHotkeySettingWindow : ReactiveWindow<GlobalHotkeySettingViewModel>
private readonly List<object> _textBoxKeyEventItem = new();
public GlobalHotkeySettingWindow()
{
private readonly List<object> _textBoxKeyEventItem = new();
InitializeComponent();
public GlobalHotkeySettingWindow()
ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
btnReset.Click += btnReset_Click;
HotkeyHandler.Instance.IsPause = true;
this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false;
btnCancel.Click += (s, e) => this.Close();
this.WhenActivated(disposables =>
{
InitializeComponent();
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
Init();
BindingData();
}
btnReset.Click += btnReset_Click;
HotkeyHandler.Instance.IsPause = true;
this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false;
btnCancel.Click += (s, e) => this.Close();
this.WhenActivated(disposables =>
{
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
Init();
BindingData();
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
private void Init()
{
_textBoxKeyEventItem.Add(txtGlobalHotkey0);
_textBoxKeyEventItem.Add(txtGlobalHotkey1);
_textBoxKeyEventItem.Add(txtGlobalHotkey2);
_textBoxKeyEventItem.Add(txtGlobalHotkey3);
_textBoxKeyEventItem.Add(txtGlobalHotkey4);
for (var index = 0; index < _textBoxKeyEventItem.Count; index++)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
}
private void Init()
{
_textBoxKeyEventItem.Add(txtGlobalHotkey0);
_textBoxKeyEventItem.Add(txtGlobalHotkey1);
_textBoxKeyEventItem.Add(txtGlobalHotkey2);
_textBoxKeyEventItem.Add(txtGlobalHotkey3);
_textBoxKeyEventItem.Add(txtGlobalHotkey4);
for (var index = 0; index < _textBoxKeyEventItem.Count; index++)
{
var sender = _textBoxKeyEventItem[index];
if (sender is not TextBox txtBox)
{
continue;
}
txtBox.Tag = (EGlobalHotkey)index;
txtBox.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
}
}
private void TxtGlobalHotkey_PreviewKeyDown(object? sender, KeyEventArgs e)
{
e.Handled = true;
var sender = _textBoxKeyEventItem[index];
if (sender is not TextBox txtBox)
{
return;
continue;
}
txtBox.Tag = (EGlobalHotkey)index;
txtBox.KeyDown += TxtGlobalHotkey_PreviewKeyDown;
}
}
private void TxtGlobalHotkey_PreviewKeyDown(object? sender, KeyEventArgs e)
{
e.Handled = true;
if (sender is not TextBox txtBox)
{
return;
}
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 };
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.Control = (e.KeyModifiers & KeyModifiers.Control) == KeyModifiers.Control;
item.Shift = (e.KeyModifiers & KeyModifiers.Shift) == KeyModifiers.Shift;
txtBox.Text = KeyEventItemToString(item);
}
private void BindingData()
{
foreach (var sender in _textBoxKeyEventItem)
{
if (sender is not TextBox txtBox)
{
continue;
}
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 };
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.Control = (e.KeyModifiers & KeyModifiers.Control) == KeyModifiers.Control;
item.Shift = (e.KeyModifiers & KeyModifiers.Shift) == KeyModifiers.Shift;
txtBox.Text = KeyEventItemToString(item);
}
}
private void BindingData()
private void btnReset_Click(object sender, RoutedEventArgs e)
{
ViewModel?.ResetKeyEventItem();
BindingData();
}
private string KeyEventItemToString(KeyEventItem? item)
{
if (item == null)
{
foreach (var sender in _textBoxKeyEventItem)
{
if (sender is not TextBox txtBox)
{
continue;
}
return string.Empty;
}
var res = new StringBuilder();
var item = ViewModel?.GetKeyEventItem((EGlobalHotkey)txtBox.Tag);
txtBox.Text = KeyEventItemToString(item);
}
if (item.Control)
{
res.Append($"{KeyModifiers.Control} +");
}
private void btnReset_Click(object sender, RoutedEventArgs e)
if (item.Shift)
{
ViewModel?.ResetKeyEventItem();
BindingData();
res.Append($"{KeyModifiers.Shift} +");
}
private string KeyEventItemToString(KeyEventItem? item)
if (item.Alt)
{
if (item == null)
{
return string.Empty;
}
var res = new StringBuilder();
if (item.Control)
{
res.Append($"{KeyModifiers.Control} +");
}
if (item.Shift)
{
res.Append($"{KeyModifiers.Shift} +");
}
if (item.Alt)
{
res.Append($"{KeyModifiers.Alt} +");
}
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
{
res.Append($"{(Key)item.KeyCode}");
}
return res.ToString();
res.Append($"{KeyModifiers.Alt} +");
}
if (item.KeyCode != null && (Key)item.KeyCode != Key.None)
{
res.Append($"{(Key)item.KeyCode}");
}
return res.ToString();
}
}

View File

@@ -14,238 +14,306 @@ using Splat;
using v2rayN.Desktop.Common;
using v2rayN.Desktop.Handler;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
{
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
private static Config _config;
private WindowNotificationManager? _manager;
private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView;
private bool _blCloseByUser = false;
public MainWindow()
{
private static Config _config;
private WindowNotificationManager? _manager;
private CheckUpdateView? _checkUpdateView;
private BackupAndRestoreView? _backupAndRestoreView;
private bool _blCloseByUser = false;
InitializeComponent();
public MainWindow()
_config = AppHandler.Instance.Config;
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.BottomRight };
this.KeyDown += MainWindow_KeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuCheckUpdate.Click += MenuCheckUpdate_Click;
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
menuClose.Click += MenuClose_Click;
MessageBus.Current.Listen<string>(EMsgCommand.SendSnackMsg.ToString()).Subscribe(DelegateSnackMsg);
ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
switch (_config.UiItem.MainGirdOrientation)
{
InitializeComponent();
case EGirdOrientation.Horizontal:
tabProfiles.Content ??= new ProfilesView(this);
tabMsgView.Content ??= new MsgView();
tabClashProxies.Content ??= new ClashProxiesView();
tabClashConnections.Content ??= new ClashConnectionsView();
gridMain.IsVisible = true;
break;
_config = AppHandler.Instance.Config;
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.BottomRight };
case EGirdOrientation.Vertical:
tabProfiles1.Content ??= new ProfilesView(this);
tabMsgView1.Content ??= new MsgView();
tabClashProxies1.Content ??= new ClashProxiesView();
tabClashConnections1.Content ??= new ClashConnectionsView();
gridMain1.IsVisible = true;
break;
this.KeyDown += MainWindow_KeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuCheckUpdate.Click += MenuCheckUpdate_Click;
menuBackupAndRestore.Click += MenuBackupAndRestore_Click;
menuClose.Click += MenuClose_Click;
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();
MessageBus.Current.Listen<string>(EMsgCommand.SendSnackMsg.ToString()).Subscribe(DelegateSnackMsg);
ViewModel = new MainWindowViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
this.WhenActivated(disposables =>
{
//servers
this.BindCommand(ViewModel, vm => vm.AddVmessServerCmd, v => v.menuAddVmessServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddVlessServerCmd, v => v.menuAddVlessServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddShadowsocksServerCmd, v => v.menuAddShadowsocksServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddSocksServerCmd, v => v.menuAddSocksServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddHttpServerCmd, v => v.menuAddHttpServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddTrojanServerCmd, v => v.menuAddTrojanServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaImageCmd, v => v.menuAddServerViaImage).DisposeWith(disposables);
//sub
this.BindCommand(ViewModel, vm => vm.SubSettingCmd, v => v.menuSubSetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubUpdateCmd, v => v.menuSubUpdate).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubUpdateViaProxyCmd, v => v.menuSubUpdateViaProxy).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubGroupUpdateCmd, v => v.menuSubGroupUpdate).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubGroupUpdateViaProxyCmd, v => v.menuSubGroupUpdateViaProxy).DisposeWith(disposables);
//setting
this.BindCommand(ViewModel, vm => vm.OptionSettingCmd, v => v.menuOptionSetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingSettingCmd, v => v.menuRoutingSetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.DNSSettingCmd, v => v.menuDNSSetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GlobalHotkeySettingCmd, v => v.menuGlobalHotkeySetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RebootAsAdminCmd, v => v.menuRebootAsAdmin).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ClearServerStatisticsCmd, v => v.menuClearServerStatistics).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.OpenTheFileLocationCmd, v => v.menuOpenTheFileLocation).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RegionalPresetDefaultCmd, v => v.menuRegionalPresetsDefault).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RegionalPresetRussiaCmd, v => v.menuRegionalPresetsRussia).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RegionalPresetIranCmd, v => v.menuRegionalPresetsIran).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
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;
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).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:
tabProfiles1.Content ??= new ProfilesView(this);
tabMsgView1.Content ??= new MsgView();
tabClashProxies1.Content ??= new ClashProxiesView();
tabClashConnections1.Content ??= new ClashConnectionsView();
gridMain1.IsVisible = true;
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain1.SelectedIndex).DisposeWith(disposables);
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;
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
break;
}
conTheme.Content ??= new ThemeSettingView();
});
this.WhenActivated(disposables =>
{
//servers
this.BindCommand(ViewModel, vm => vm.AddVmessServerCmd, v => v.menuAddVmessServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddVlessServerCmd, v => v.menuAddVlessServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddShadowsocksServerCmd, v => v.menuAddShadowsocksServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddSocksServerCmd, v => v.menuAddSocksServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddHttpServerCmd, v => v.menuAddHttpServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddTrojanServerCmd, v => v.menuAddTrojanServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddServerViaImageCmd, v => v.menuAddServerViaImage).DisposeWith(disposables);
//sub
this.BindCommand(ViewModel, vm => vm.SubSettingCmd, v => v.menuSubSetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubUpdateCmd, v => v.menuSubUpdate).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubUpdateViaProxyCmd, v => v.menuSubUpdateViaProxy).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubGroupUpdateCmd, v => v.menuSubGroupUpdate).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubGroupUpdateViaProxyCmd, v => v.menuSubGroupUpdateViaProxy).DisposeWith(disposables);
//setting
this.BindCommand(ViewModel, vm => vm.OptionSettingCmd, v => v.menuOptionSetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingSettingCmd, v => v.menuRoutingSetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.DNSSettingCmd, v => v.menuDNSSetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.GlobalHotkeySettingCmd, v => v.menuGlobalHotkeySetting).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RebootAsAdminCmd, v => v.menuRebootAsAdmin).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ClearServerStatisticsCmd, v => v.menuClearServerStatistics).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.OpenTheFileLocationCmd, v => v.menuOpenTheFileLocation).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RegionalPresetDefaultCmd, v => v.menuRegionalPresetsDefault).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RegionalPresetRussiaCmd, v => v.menuRegionalPresetsRussia).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RegionalPresetIranCmd, v => v.menuRegionalPresetsIran).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ReloadCmd, v => v.menuReload).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.BlReloadEnabled, v => v.menuReload.IsEnabled).DisposeWith(disposables);
switch (_config.UiItem.MainGirdOrientation)
{
case EGirdOrientation.Horizontal:
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).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:
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain1.SelectedIndex).DisposeWith(disposables);
break;
case EGirdOrientation.Tab:
default:
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
break;
}
});
this.Title = $"{Utils.GetVersion()}";
if (Utils.IsWindows())
{
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
HotkeyHandler.Instance.Init(_config, OnHotkeyHandler);
}
else
{
if (AppHandler.Instance.IsAdministrator)
{
this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}";
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp);
}
menuRebootAsAdmin.IsVisible = false;
menuSettingsSetUWP.IsVisible = false;
menuGlobalHotkeySetting.IsVisible = false;
}
menuAddServerViaScan.IsVisible = false;
RestoreUI();
AddHelpMenuItem();
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
}
#region Event
private void OnProgramStarted(object state, bool timeout)
this.Title = $"{Utils.GetVersion()}";
if (Utils.IsWindows())
{
Dispatcher.UIThread.Post(() =>
ShowHideWindow(true),
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
HotkeyHandler.Instance.Init(_config, OnHotkeyHandler);
}
else
{
if (AppHandler.Instance.IsAdministrator)
{
this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}";
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp);
}
menuRebootAsAdmin.IsVisible = false;
menuSettingsSetUWP.IsVisible = false;
menuGlobalHotkeySetting.IsVisible = false;
}
menuAddServerViaScan.IsVisible = false;
RestoreUI();
AddHelpMenuItem();
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
}
#region Event
private void OnProgramStarted(object state, bool timeout)
{
Dispatcher.UIThread.Post(() =>
ShowHideWindow(true),
DispatcherPriority.Default);
}
private void DelegateSnackMsg(string content)
{
Dispatcher.UIThread.Post(() =>
_manager?.Show(new Notification(null, content, NotificationType.Information)),
DispatcherPriority.Normal);
}
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.DNSSettingWindow:
return await new DNSSettingWindow().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.ShowHideWindow:
Dispatcher.UIThread.Post(() =>
ShowHideWindow((bool?)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherStatistics:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.SetStatisticsResult((ServerSpeedItem)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherReload:
Dispatcher.UIThread.Post(() =>
ViewModel?.ReloadResult(),
DispatcherPriority.Default);
break;
case EViewAction.Shutdown:
if (obj != null && _blCloseByUser == false)
{
_blCloseByUser = (bool)obj;
}
StorageUI();
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
HotkeyHandler.Instance.Dispose();
desktop.Shutdown();
}
break;
case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync();
break;
case EViewAction.ScanImageTask:
await ScanImageTaskAsync();
break;
case EViewAction.AddServerViaClipboard:
var clipboardData = await AvaUtils.GetClipboardData(this);
if (clipboardData.IsNotEmpty() && ViewModel != null)
{
await ViewModel.AddServerViaClipboardAsync(clipboardData);
}
break;
case EViewAction.AdjustMainLvColWidth:
Dispatcher.UIThread.Post(() =>
Locator.Current.GetService<ProfilesViewModel>()?.AutofitColumnWidthAsync(),
DispatcherPriority.Default);
break;
}
private void DelegateSnackMsg(string content)
return await Task.FromResult(true);
}
private void OnHotkeyHandler(EGlobalHotkey e)
{
switch (e)
{
Dispatcher.UIThread.Post(() =>
_manager?.Show(new Notification(null, content, NotificationType.Information)),
DispatcherPriority.Normal);
case EGlobalHotkey.ShowForm:
ShowHideWindow(null);
break;
case EGlobalHotkey.SystemProxyClear:
case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac:
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1));
break;
}
}
protected override async void OnClosing(WindowClosingEventArgs e)
{
if (_blCloseByUser)
{
return;
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
Logging.SaveLog("OnClosing -> " + e.CloseReason.ToString());
switch (e.CloseReason)
{
switch (action)
case WindowCloseReason.OwnerWindowClosing or WindowCloseReason.WindowClosing:
e.Cancel = true;
ShowHideWindow(false);
break;
case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown:
if (ViewModel != null)
{
await ViewModel.MyAppExitAsync(true);
}
break;
}
base.OnClosing(e);
}
private async void MainWindow_KeyDown(object? sender, KeyEventArgs e)
{
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
{
switch (e.Key)
{
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.DNSSettingWindow:
return await new DNSSettingWindow().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.ShowHideWindow:
Dispatcher.UIThread.Post(() =>
ShowHideWindow((bool?)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherStatistics:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.SetStatisticsResult((ServerSpeedItem)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherReload:
Dispatcher.UIThread.Post(() =>
ViewModel?.ReloadResult(),
DispatcherPriority.Default);
break;
case EViewAction.Shutdown:
if (obj != null && _blCloseByUser == false)
{
_blCloseByUser = (bool)obj;
}
StorageUI();
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
HotkeyHandler.Instance.Dispose();
desktop.Shutdown();
}
break;
case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync();
break;
case EViewAction.ScanImageTask:
await ScanImageTaskAsync();
break;
case EViewAction.AddServerViaClipboard:
case Key.V:
var clipboardData = await AvaUtils.GetClipboardData(this);
if (clipboardData.IsNotEmpty() && ViewModel != null)
{
@@ -253,254 +321,185 @@ namespace v2rayN.Desktop.Views
}
break;
case EViewAction.AdjustMainLvColWidth:
Dispatcher.UIThread.Post(() =>
Locator.Current.GetService<ProfilesViewModel>()?.AutofitColumnWidthAsync(),
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true);
}
private void OnHotkeyHandler(EGlobalHotkey e)
{
switch (e)
{
case EGlobalHotkey.ShowForm:
ShowHideWindow(null);
break;
case EGlobalHotkey.SystemProxyClear:
case EGlobalHotkey.SystemProxySet:
case EGlobalHotkey.SystemProxyUnchanged:
case EGlobalHotkey.SystemProxyPac:
Locator.Current.GetService<StatusBarViewModel>()?.SetListenerType((ESysProxyType)((int)e - 1));
case Key.S:
await ScanScreenTaskAsync();
break;
}
}
protected override async void OnClosing(WindowClosingEventArgs e)
else
{
if (_blCloseByUser)
if (e.Key == Key.F5)
{
ViewModel?.Reload();
}
}
}
private void menuPromotion_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
}
private void menuSettingsSetUWP_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
}
public async Task ScanScreenTaskAsync()
{
//ShowHideWindow(false);
NoticeHandler.Instance.SendMessageAndEnqueue("Not yet implemented.(还未实现)");
await Task.CompletedTask;
//if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
//{
// //var bytes = QRCodeHelper.CaptureScreen(desktop);
// //await ViewModel?.ScanScreenResult(bytes);
//}
//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)
{
_checkUpdateView ??= new CheckUpdateView();
DialogHost.Show(_checkUpdateView);
}
private void MenuBackupAndRestore_Click(object? sender, RoutedEventArgs e)
{
_backupAndRestoreView ??= new BackupAndRestoreView(this);
DialogHost.Show(_backupAndRestoreView);
}
private async void MenuClose_Click(object? sender, RoutedEventArgs e)
{
if (await UI.ShowYesNo(this, ResUI.menuExitTips) == ButtonResult.No)
{
return;
}
_blCloseByUser = true;
StorageUI();
if (ViewModel != null)
{
await ViewModel.MyAppExitAsync(false);
}
}
#endregion Event
#region UI
public void ShowHideWindow(bool? blShow)
{
var bl = blShow ?? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized));
if (bl)
{
this.Show();
if (this.WindowState == WindowState.Minimized)
{
this.WindowState = WindowState.Normal;
}
this.Activate();
this.Focus();
}
else
{
if (Utils.IsLinux() && _config.UiItem.Hide2TrayWhenClose == false)
{
this.WindowState = WindowState.Minimized;
return;
}
Logging.SaveLog("OnClosing -> " + e.CloseReason.ToString());
switch (e.CloseReason)
foreach (var ownedWindow in this.OwnedWindows)
{
case WindowCloseReason.OwnerWindowClosing or WindowCloseReason.WindowClosing:
e.Cancel = true;
ShowHideWindow(false);
break;
case WindowCloseReason.ApplicationShutdown or WindowCloseReason.OSShutdown:
if (ViewModel != null)
{
await ViewModel.MyAppExitAsync(true);
}
break;
ownedWindow.Close();
}
base.OnClosing(e);
this.Hide();
}
private async void MainWindow_KeyDown(object? sender, KeyEventArgs e)
{
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
{
switch (e.Key)
{
case Key.V:
var clipboardData = await AvaUtils.GetClipboardData(this);
if (clipboardData.IsNotEmpty() && ViewModel != null)
{
await ViewModel.AddServerViaClipboardAsync(clipboardData);
}
break;
_config.UiItem.ShowInTaskbar = bl;
}
case Key.S:
await ScanScreenTaskAsync();
break;
}
}
else
{
if (e.Key == Key.F5)
{
ViewModel?.Reload();
}
}
private void RestoreUI()
{
if (_config.UiItem.MainWidth > 0 && _config.UiItem.MainHeight > 0)
{
Width = _config.UiItem.MainWidth;
Height = _config.UiItem.MainHeight;
}
private void menuPromotion_Click(object? sender, RoutedEventArgs e)
if (_config.UiItem.MainGirdHeight1 > 0 && _config.UiItem.MainGirdHeight2 > 0)
{
ProcUtils.ProcessStart($"{Utils.Base64Decode(Global.PromotionUrl)}?t={DateTime.Now.Ticks}");
}
private void menuSettingsSetUWP_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart(Utils.GetBinPath("EnableLoopback.exe"));
}
public async Task ScanScreenTaskAsync()
{
//ShowHideWindow(false);
NoticeHandler.Instance.SendMessageAndEnqueue("Not yet implemented.(还未实现)");
await Task.CompletedTask;
//if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
//{
// //var bytes = QRCodeHelper.CaptureScreen(desktop);
// //await ViewModel?.ScanScreenResult(bytes);
//}
//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)
{
_checkUpdateView ??= new CheckUpdateView();
DialogHost.Show(_checkUpdateView);
}
private void MenuBackupAndRestore_Click(object? sender, RoutedEventArgs e)
{
_backupAndRestoreView ??= new BackupAndRestoreView(this);
DialogHost.Show(_backupAndRestoreView);
}
private async void MenuClose_Click(object? sender, RoutedEventArgs e)
{
if (await UI.ShowYesNo(this, ResUI.menuExitTips) == ButtonResult.No)
{
return;
}
_blCloseByUser = true;
StorageUI();
if (ViewModel != null)
{
await ViewModel.MyAppExitAsync(false);
}
}
#endregion Event
#region UI
public void ShowHideWindow(bool? blShow)
{
var bl = blShow ?? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized));
if (bl)
{
this.Show();
if (this.WindowState == WindowState.Minimized)
{
this.WindowState = WindowState.Normal;
}
this.Activate();
this.Focus();
}
else
{
if (Utils.IsLinux() && _config.UiItem.Hide2TrayWhenClose == false)
{
this.WindowState = WindowState.Minimized;
return;
}
foreach (var ownedWindow in this.OwnedWindows)
{
ownedWindow.Close();
}
this.Hide();
}
_config.UiItem.ShowInTaskbar = bl;
}
private void RestoreUI()
{
if (_config.UiItem.MainWidth > 0 && _config.UiItem.MainHeight > 0)
{
Width = _config.UiItem.MainWidth;
Height = _config.UiItem.MainHeight;
}
if (_config.UiItem.MainGirdHeight1 > 0 && _config.UiItem.MainGirdHeight2 > 0)
{
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
{
gridMain.ColumnDefinitions[0].Width = new GridLength(_config.UiItem.MainGirdHeight1, GridUnitType.Star);
gridMain.ColumnDefinitions[2].Width = new GridLength(_config.UiItem.MainGirdHeight2, GridUnitType.Star);
}
else if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Vertical)
{
gridMain1.RowDefinitions[0].Height = new GridLength(_config.UiItem.MainGirdHeight1, GridUnitType.Star);
gridMain1.RowDefinitions[2].Height = new GridLength(_config.UiItem.MainGirdHeight2, GridUnitType.Star);
}
}
}
private void StorageUI(string? n = null)
{
_config.UiItem.MainWidth = this.Width;
_config.UiItem.MainHeight = this.Height;
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
{
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain.ColumnDefinitions[0].ActualWidth + 0.1);
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain.ColumnDefinitions[2].ActualWidth + 0.1);
gridMain.ColumnDefinitions[0].Width = new GridLength(_config.UiItem.MainGirdHeight1, GridUnitType.Star);
gridMain.ColumnDefinitions[2].Width = new GridLength(_config.UiItem.MainGirdHeight2, GridUnitType.Star);
}
else if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Vertical)
{
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain1.RowDefinitions[0].ActualHeight + 0.1);
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain1.RowDefinitions[2].ActualHeight + 0.1);
gridMain1.RowDefinitions[0].Height = new GridLength(_config.UiItem.MainGirdHeight1, GridUnitType.Star);
gridMain1.RowDefinitions[2].Height = new GridLength(_config.UiItem.MainGirdHeight2, GridUnitType.Star);
}
}
private void AddHelpMenuItem()
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo
.Where(t => t.CoreType != ECoreType.v2fly
&& t.CoreType != ECoreType.hysteria))
{
var item = new MenuItem()
{
Tag = it.Url?.Replace(@"/releases", ""),
Header = string.Format(ResUI.menuWebsiteItem, it.CoreType.ToString().Replace("_", " ")).UpperFirstChar()
};
item.Click += MenuItem_Click;
menuHelp.Items.Add(item);
}
}
private void MenuItem_Click(object? sender, RoutedEventArgs e)
{
if (sender is MenuItem item)
{
ProcUtils.ProcessStart(item.Tag?.ToString());
}
}
#endregion UI
}
private void StorageUI(string? n = null)
{
_config.UiItem.MainWidth = this.Width;
_config.UiItem.MainHeight = this.Height;
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
{
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain.ColumnDefinitions[0].ActualWidth + 0.1);
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain.ColumnDefinitions[2].ActualWidth + 0.1);
}
else if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Vertical)
{
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain1.RowDefinitions[0].ActualHeight + 0.1);
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain1.RowDefinitions[2].ActualHeight + 0.1);
}
}
private void AddHelpMenuItem()
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo
.Where(t => t.CoreType != ECoreType.v2fly
&& t.CoreType != ECoreType.hysteria))
{
var item = new MenuItem()
{
Tag = it.Url?.Replace(@"/releases", ""),
Header = string.Format(ResUI.menuWebsiteItem, it.CoreType.ToString().Replace("_", " ")).UpperFirstChar()
};
item.Click += MenuItem_Click;
menuHelp.Items.Add(item);
}
}
private void MenuItem_Click(object? sender, RoutedEventArgs e)
{
if (sender is MenuItem item)
{
ProcUtils.ProcessStart(item.Tag?.ToString());
}
}
#endregion UI
}

View File

@@ -5,75 +5,74 @@ using Avalonia.Threading;
using ReactiveUI;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class MsgView : ReactiveUserControl<MsgViewModel>
{
public partial class MsgView : ReactiveUserControl<MsgViewModel>
public MsgView()
{
public MsgView()
InitializeComponent();
ViewModel = new MsgViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
{
InitializeComponent();
this.Bind(ViewModel, vm => vm.MsgFilter, v => v.cmbMsgFilter.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
});
}
ViewModel = new MsgViewModel(UpdateViewHandler);
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.DispatcherShowMsg:
if (obj is null)
return false;
this.WhenActivated(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);
});
Dispatcher.UIThread.Post(() =>
ShowMsg(obj),
DispatcherPriority.ApplicationIdle);
break;
}
return await Task.FromResult(true);
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
private void ShowMsg(object msg)
{
txtMsg.Text = msg.ToString();
if (togScrollToEnd.IsChecked ?? true)
{
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)
{
txtMsg.Text = msg.ToString();
if (togScrollToEnd.IsChecked ?? true)
{
txtMsg.CaretIndex = int.MaxValue;
}
}
public void ClearMsg()
{
ViewModel?.ClearMsg();
txtMsg.Clear();
}
private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e)
{
txtMsg.Focus();
txtMsg.SelectAll();
}
private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e)
{
var data = txtMsg.SelectedText.TrimEx();
await AvaUtils.SetClipboardData(this, data);
}
private async void menuMsgViewCopyAll_Click(object? sender, RoutedEventArgs e)
{
var data = txtMsg.Text.TrimEx();
await AvaUtils.SetClipboardData(this, data);
}
private void menuMsgViewClear_Click(object? sender, RoutedEventArgs e)
{
ClearMsg();
txtMsg.CaretIndex = int.MaxValue;
}
}
public void ClearMsg()
{
ViewModel?.ClearMsg();
txtMsg.Clear();
}
private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e)
{
txtMsg.Focus();
txtMsg.SelectAll();
}
private async void menuMsgViewCopy_Click(object? sender, RoutedEventArgs e)
{
var data = txtMsg.SelectedText.TrimEx();
await AvaUtils.SetClipboardData(this, data);
}
private async void menuMsgViewCopyAll_Click(object? sender, RoutedEventArgs e)
{
var data = txtMsg.Text.TrimEx();
await AvaUtils.SetClipboardData(this, data);
}
private void menuMsgViewClear_Click(object? sender, RoutedEventArgs e)
{
ClearMsg();
}
}

View File

@@ -3,255 +3,254 @@ using Avalonia.Controls;
using Avalonia.ReactiveUI;
using ReactiveUI;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel>
{
public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel>
private static Config _config;
public OptionSettingWindow()
{
private static Config _config;
InitializeComponent();
public OptionSettingWindow()
btnCancel.Click += (s, e) => this.Close();
_config = AppHandler.Instance.Config;
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
clbdestOverride.SelectionChanged += ClbdestOverride_SelectionChanged;
Global.destOverrideProtocols.ForEach(it =>
{
InitializeComponent();
clbdestOverride.Items.Add(it);
});
_config.Inbound.First().DestOverride?.ForEach(it =>
{
clbdestOverride.SelectedItems.Add(it);
});
Global.IEProxyProtocols.ForEach(it =>
{
cmbsystemProxyAdvancedProtocol.Items.Add(it);
});
Global.LogLevels.ForEach(it =>
{
cmbloglevel.Items.Add(it);
});
Global.Fingerprints.ForEach(it =>
{
cmbdefFingerprint.Items.Add(it);
});
Global.UserAgent.ForEach(it =>
{
cmbdefUserAgent.Items.Add(it);
});
Global.SingboxMuxs.ForEach(it =>
{
cmbmux4SboxProtocol.Items.Add(it);
});
btnCancel.Click += (s, e) => this.Close();
_config = AppHandler.Instance.Config;
Global.TunMtus.ForEach(it =>
{
cmbMtu.Items.Add(it);
});
Global.TunStacks.ForEach(it =>
{
cmbStack.Items.Add(it);
});
Global.CoreTypes.ForEach(it =>
{
cmbCoreType1.Items.Add(it);
cmbCoreType2.Items.Add(it);
cmbCoreType3.Items.Add(it);
cmbCoreType4.Items.Add(it);
cmbCoreType5.Items.Add(it);
cmbCoreType6.Items.Add(it);
});
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
for (var i = 2; i <= 8; i++)
{
cmbMixedConcurrencyCount.Items.Add(i);
}
for (var i = 2; i <= 6; i++)
{
cmbSpeedTestTimeout.Items.Add(i * 5);
}
clbdestOverride.SelectionChanged += ClbdestOverride_SelectionChanged;
Global.destOverrideProtocols.ForEach(it =>
{
clbdestOverride.Items.Add(it);
});
_config.Inbound.First().DestOverride?.ForEach(it =>
{
clbdestOverride.SelectedItems.Add(it);
});
Global.IEProxyProtocols.ForEach(it =>
{
cmbsystemProxyAdvancedProtocol.Items.Add(it);
});
Global.LogLevels.ForEach(it =>
{
cmbloglevel.Items.Add(it);
});
Global.Fingerprints.ForEach(it =>
{
cmbdefFingerprint.Items.Add(it);
});
Global.UserAgent.ForEach(it =>
{
cmbdefUserAgent.Items.Add(it);
});
Global.SingboxMuxs.ForEach(it =>
{
cmbmux4SboxProtocol.Items.Add(it);
});
cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls;
cmbSpeedPingTestUrl.ItemsSource = Global.SpeedPingTestUrls;
cmbSubConvertUrl.ItemsSource = Global.SubConvertUrls;
Global.TunMtus.ForEach(it =>
{
cmbMtu.Items.Add(it);
});
Global.TunStacks.ForEach(it =>
{
cmbStack.Items.Add(it);
});
Global.CoreTypes.ForEach(it =>
{
cmbCoreType1.Items.Add(it);
cmbCoreType2.Items.Add(it);
cmbCoreType3.Items.Add(it);
cmbCoreType4.Items.Add(it);
cmbCoreType5.Items.Add(it);
cmbCoreType6.Items.Add(it);
});
Global.GeoFilesSources.ForEach(it =>
{
cmbGetFilesSourceUrl.Items.Add(it);
});
Global.SingboxRulesetSources.ForEach(it =>
{
cmbSrsFilesSourceUrl.Items.Add(it);
});
Global.RoutingRulesSources.ForEach(it =>
{
cmbRoutingRulesSourceUrl.Items.Add(it);
});
foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation)))
{
cmbMainGirdOrientation.Items.Add(it.ToString());
}
for (var i = 2; i <= 8; i++)
{
cmbMixedConcurrencyCount.Items.Add(i);
}
for (var i = 2; i <= 6; i++)
{
cmbSpeedTestTimeout.Items.Add(i * 5);
}
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.localPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SecondLocalPortEnabled, v => v.togSecondLocalPortEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.udpEnabled, v => v.togudpEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.sniffingEnabled, v => v.togsniffingEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.routeOnly, v => v.togrouteOnly.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.allowLANConn, v => v.togAllowLANConn.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.togNewPort4LAN.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtuser.IsEnabled).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.user, v => v.txtuser.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.pass, v => v.txtpass.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.muxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.logEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.loglevel, v => v.cmbloglevel.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defFingerprint, v => v.cmbdefFingerprint.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defUserAgent, v => v.cmbdefUserAgent.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.mux4SboxProtocol, v => v.cmbmux4SboxProtocol.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.enableCacheFile4Sbox, v => v.togenableCacheFile4Sbox.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.hyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.hyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.enableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls;
cmbSpeedPingTestUrl.ItemsSource = Global.SpeedPingTestUrls;
cmbSubConvertUrl.ItemsSource = Global.SubConvertUrls;
this.Bind(ViewModel, vm => vm.AutoRun, v => v.togAutoRun.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableStatistics, v => v.togEnableStatistics.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DisplayRealTimeSpeed, v => v.togDisplayRealTimeSpeed.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.KeepOlderDedupl, v => v.togKeepOlderDedupl.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableAutoAdjustMainLvColWidth, v => v.togEnableAutoAdjustMainLvColWidth.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableSecurityProtocolTls13, v => v.togEnableSecurityProtocolTls13.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Hide2TrayWhenClose, v => v.togHide2TrayWhenClose.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MixedConcurrencyCount, v => v.cmbMixedConcurrencyCount.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables);
Global.GeoFilesSources.ForEach(it =>
{
cmbGetFilesSourceUrl.Items.Add(it);
});
Global.SingboxRulesetSources.ForEach(it =>
{
cmbSrsFilesSourceUrl.Items.Add(it);
});
Global.RoutingRulesSources.ForEach(it =>
{
cmbRoutingRulesSourceUrl.Items.Add(it);
});
foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation)))
{
cmbMainGirdOrientation.Items.Add(it.ToString());
}
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.systemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.localPort, v => v.txtlocalPort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SecondLocalPortEnabled, v => v.togSecondLocalPortEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.udpEnabled, v => v.togudpEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.sniffingEnabled, v => v.togsniffingEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.routeOnly, v => v.togrouteOnly.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.allowLANConn, v => v.togAllowLANConn.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.togNewPort4LAN.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtuser.IsEnabled).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.newPort4LAN, v => v.txtpass.IsEnabled).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.user, v => v.txtuser.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.pass, v => v.txtpass.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.muxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.logEnabled, v => v.toglogEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.loglevel, v => v.cmbloglevel.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defAllowInsecure, v => v.togdefAllowInsecure.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defFingerprint, v => v.cmbdefFingerprint.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.defUserAgent, v => v.cmbdefUserAgent.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.mux4SboxProtocol, v => v.cmbmux4SboxProtocol.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.enableCacheFile4Sbox, v => v.togenableCacheFile4Sbox.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.hyUpMbps, v => v.txtUpMbps.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.hyDownMbps, v => v.txtDownMbps.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.enableFragment, v => v.togenableFragment.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunLinuxSudoPassword, v => v.txtLinuxSudoPassword.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoRun, v => v.togAutoRun.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableStatistics, v => v.togEnableStatistics.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DisplayRealTimeSpeed, v => v.togDisplayRealTimeSpeed.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.KeepOlderDedupl, v => v.togKeepOlderDedupl.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableAutoAdjustMainLvColWidth, v => v.togEnableAutoAdjustMainLvColWidth.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableUpdateSubOnlyRemarksExist, v => v.togEnableUpdateSubOnlyRemarksExist.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableSecurityProtocolTls13, v => v.togEnableSecurityProtocolTls13.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoHideStartup, v => v.togAutoHideStartup.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Hide2TrayWhenClose, v => v.togHide2TrayWhenClose.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DoubleClick2Activate, v => v.togDoubleClick2Activate.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoUpdateInterval, v => v.txtautoUpdateInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentFontFamily, v => v.cmbcurrentFontFamily.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MixedConcurrencyCount, v => v.cmbMixedConcurrencyCount.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType3, v => v.cmbCoreType3.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.systemProxyExceptions, v => v.txtsystemProxyExceptions.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
this.Bind(ViewModel, vm => vm.TunStrictRoute, v => v.togStrictRoute.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableExInbound, v => v.togEnableExInbound.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunLinuxSudoPassword, v => v.txtLinuxSudoPassword.Text).DisposeWith(disposables);
if (Utils.IsWindows())
{
txbSettingsExceptionTip2.IsVisible = false;
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType3, v => v.cmbCoreType3.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables);
txtLinuxSudoPassword.IsVisible = false;
labLinuxSudoPassword.IsVisible = false;
labLinuxSudoPasswordTip.IsVisible = false;
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false;
}
else if (Utils.IsLinux())
{
txbSettingsExceptionTip.IsVisible = false;
panSystemProxyAdvanced.IsVisible = false;
}
else if (Utils.IsOSX())
{
txbSettingsExceptionTip.IsVisible = false;
panSystemProxyAdvanced.IsVisible = false;
labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false;
}
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
case EViewAction.InitSettingFont:
await InitSettingFont();
break;
}
return await Task.FromResult(true);
}
private async Task InitSettingFont()
{
var lstFonts = await GetFonts();
lstFonts.Add(string.Empty);
cmbcurrentFontFamily.ItemsSource = lstFonts;
}
private async Task<List<string>> GetFonts()
{
var lstFonts = new List<string>();
try
{
if (Utils.IsWindows())
{
txbSettingsExceptionTip2.IsVisible = false;
txtLinuxSudoPassword.IsVisible = false;
labLinuxSudoPassword.IsVisible = false;
labLinuxSudoPasswordTip.IsVisible = false;
labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false;
return lstFonts;
}
else if (Utils.IsLinux())
else if (Utils.IsNonWindows())
{
txbSettingsExceptionTip.IsVisible = false;
panSystemProxyAdvanced.IsVisible = false;
}
else if (Utils.IsOSX())
{
txbSettingsExceptionTip.IsVisible = false;
panSystemProxyAdvanced.IsVisible = false;
labHide2TrayWhenClose.IsVisible = false;
togHide2TrayWhenClose.IsVisible = false;
}
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
case EViewAction.InitSettingFont:
await InitSettingFont();
break;
}
return await Task.FromResult(true);
}
private async Task InitSettingFont()
{
var lstFonts = await GetFonts();
lstFonts.Add(string.Empty);
cmbcurrentFontFamily.ItemsSource = lstFonts;
}
private async Task<List<string>> GetFonts()
{
var lstFonts = new List<string>();
try
{
if (Utils.IsWindows())
var result = await Utils.GetLinuxFontFamily("zh");
if (result.IsNullOrEmpty())
{
return lstFonts;
}
else if (Utils.IsNonWindows())
{
var result = await Utils.GetLinuxFontFamily("zh");
if (result.IsNullOrEmpty())
{
return lstFonts;
}
var lst = result.Split(Environment.NewLine)
.Where(t => t.IsNotEmpty())
.ToList()
.Select(t => t.Split(",").FirstOrDefault() ?? "")
.OrderBy(t => t)
.ToList();
return lst;
}
var lst = result.Split(Environment.NewLine)
.Where(t => t.IsNotEmpty())
.ToList()
.Select(t => t.Split(",").FirstOrDefault() ?? "")
.OrderBy(t => t)
.ToList();
return lst;
}
catch (Exception ex)
{
Logging.SaveLog("GetFonts", ex);
}
return lstFonts;
}
private void ClbdestOverride_SelectionChanged(object? sender, SelectionChangedEventArgs e)
catch (Exception ex)
{
if (ViewModel != null)
{
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
}
Logging.SaveLog("GetFonts", ex);
}
return lstFonts;
}
private void ClbdestOverride_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.destOverride = clbdestOverride.SelectedItems.Cast<string>().ToList();
}
}
}

View File

@@ -10,502 +10,501 @@ using ReactiveUI;
using Splat;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
{
public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
private static Config _config;
private Window? _window;
public ProfilesView()
{
private static Config _config;
private Window? _window;
InitializeComponent();
}
public ProfilesView()
{
InitializeComponent();
}
public ProfilesView(Window window)
{
InitializeComponent();
public ProfilesView(Window window)
{
InitializeComponent();
_config = AppHandler.Instance.Config;
_window = window;
_config = AppHandler.Instance.Config;
_window = window;
menuSelectAll.Click += menuSelectAll_Click;
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
txtServerFilter.KeyDown += TxtServerFilter_KeyDown;
lstProfiles.KeyDown += LstProfiles_KeyDown;
lstProfiles.SelectionChanged += lstProfiles_SelectionChanged;
lstProfiles.DoubleTapped += LstProfiles_DoubleTapped;
lstProfiles.LoadingRow += LstProfiles_LoadingRow;
lstProfiles.Sorting += LstProfiles_Sorting;
//if (_config.uiItem.enableDragDropSort)
//{
// lstProfiles.AllowDrop = true;
// lstProfiles.PreviewMouseLeftButtonDown += LstProfiles_PreviewMouseLeftButtonDown;
// lstProfiles.MouseMove += LstProfiles_MouseMove;
// lstProfiles.DragEnter += LstProfiles_DragEnter;
// lstProfiles.Drop += LstProfiles_Drop;
//}
ViewModel = new ProfilesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedProfile, v => v.lstProfiles.SelectedItem).DisposeWith(disposables);
// this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.lstGroup.ItemsSource).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.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.btnAddSub).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.btnEditSub).DisposeWith(disposables);
//servers delete
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.menuEditServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveServerCmd, v => v.menuRemoveServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveDuplicateServerCmd, v => v.menuRemoveDuplicateServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayRandomCmd, v => v.menuSetDefaultMultipleServerXrayRandom).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayRoundRobinCmd, v => v.menuSetDefaultMultipleServerXrayRoundRobin).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayLeastPingCmd, v => v.menuSetDefaultMultipleServerXrayLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayLeastLoadCmd, v => v.menuSetDefaultMultipleServerXrayLeastLoad).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerSingBoxLeastPingCmd, v => v.menuSetDefaultMultipleServerSingBoxLeastPing).DisposeWith(disposables);
//servers move
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).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);
//servers ping
this.BindCommand(ViewModel, vm => vm.MixedTestServerCmd, v => v.menuMixedTestServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.TcpingServerCmd, v => v.menuTcpingServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables);
//servers export
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigClipboardCmd, v => v.menuExport2ClientConfigClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables);
});
RestoreUI();
ViewModel?.RefreshServers();
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
}
private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e)
{
e.Handled = true;
if (ViewModel != null && e.Column?.Tag?.ToString() != null)
{
await ViewModel.SortServer(e.Column.Tag.ToString());
}
e.Handled = false;
}
#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.AdjustMainLvColWidth:
Dispatcher.UIThread.Post(() =>
AutofitColumnWidth(),
DispatcherPriority.Default);
break;
case EViewAction.ProfilesFocus:
lstProfiles.Focus();
break;
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(_window, ResUI.RemoveServer) == ButtonResult.No)
{
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.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.DispatcherSpeedTest:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.SetSpeedTestResult((SpeedTestResult)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() =>
{
_ = RefreshServersBiz();
},
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true);
}
public async Task ShareServer(string url)
{
if (url.IsNullOrEmpty())
{
return;
}
var dialog = new QrcodeView(url);
await DialogHost.Show(dialog);
}
public async Task RefreshServersBiz()
{
if (ViewModel != null)
{
await ViewModel.RefreshServersBiz();
}
if (lstProfiles.SelectedIndex > 0)
{
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
}
}
private void lstProfiles_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList();
}
}
private void LstProfiles_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
var source = e.Source as Border;
if (source?.Name == "HeaderBackground")
return;
if (_config.UiItem.DoubleClick2Activate)
{
ViewModel?.SetDefaultServer();
}
else
{
ViewModel?.EditServerAsync(EConfigType.Custom);
}
}
private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
{
e.Row.Header = $" {e.Row.Index + 1}";
}
//private void LstProfiles_ColumnHeader_Click(object? sender, RoutedEventArgs e)
menuSelectAll.Click += menuSelectAll_Click;
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
txtServerFilter.KeyDown += TxtServerFilter_KeyDown;
lstProfiles.KeyDown += LstProfiles_KeyDown;
lstProfiles.SelectionChanged += lstProfiles_SelectionChanged;
lstProfiles.DoubleTapped += LstProfiles_DoubleTapped;
lstProfiles.LoadingRow += LstProfiles_LoadingRow;
lstProfiles.Sorting += LstProfiles_Sorting;
//if (_config.uiItem.enableDragDropSort)
//{
// var colHeader = sender as DataGridColumnHeader;
// if (colHeader == null || colHeader.TabIndex < 0 || colHeader.Column == null)
// {
// return;
// }
// var colName = ((MyDGTextColumn)colHeader.Column).ExName;
// ViewModel?.SortServer(colName);
// lstProfiles.AllowDrop = true;
// lstProfiles.PreviewMouseLeftButtonDown += LstProfiles_PreviewMouseLeftButtonDown;
// lstProfiles.MouseMove += LstProfiles_MouseMove;
// lstProfiles.DragEnter += LstProfiles_DragEnter;
// lstProfiles.Drop += LstProfiles_Drop;
//}
private void menuSelectAll_Click(object? sender, RoutedEventArgs e)
ViewModel = new ProfilesViewModel(UpdateViewHandler);
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(ProfilesViewModel));
this.WhenActivated(disposables =>
{
lstProfiles.SelectAll();
this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedProfile, v => v.lstProfiles.SelectedItem).DisposeWith(disposables);
// this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.lstGroup.ItemsSource).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.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.btnAddSub).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.btnEditSub).DisposeWith(disposables);
//servers delete
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.menuEditServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveServerCmd, v => v.menuRemoveServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveDuplicateServerCmd, v => v.menuRemoveDuplicateServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayRandomCmd, v => v.menuSetDefaultMultipleServerXrayRandom).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayRoundRobinCmd, v => v.menuSetDefaultMultipleServerXrayRoundRobin).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayLeastPingCmd, v => v.menuSetDefaultMultipleServerXrayLeastPing).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerXrayLeastLoadCmd, v => v.menuSetDefaultMultipleServerXrayLeastLoad).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerSingBoxLeastPingCmd, v => v.menuSetDefaultMultipleServerSingBoxLeastPing).DisposeWith(disposables);
//servers move
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).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);
//servers ping
this.BindCommand(ViewModel, vm => vm.MixedTestServerCmd, v => v.menuMixedTestServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.TcpingServerCmd, v => v.menuTcpingServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables);
//servers export
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigClipboardCmd, v => v.menuExport2ClientConfigClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables);
});
RestoreUI();
ViewModel?.RefreshServers();
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
}
private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e)
{
e.Handled = true;
if (ViewModel != null && e.Column?.Tag?.ToString() != null)
{
await ViewModel.SortServer(e.Column.Tag.ToString());
}
private void LstProfiles_KeyDown(object? sender, KeyEventArgs e)
e.Handled = false;
}
#region Event
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
case EViewAction.SetClipboardData:
if (obj is null)
return false;
await AvaUtils.SetClipboardData(this, (string)obj);
break;
case EViewAction.AdjustMainLvColWidth:
Dispatcher.UIThread.Post(() =>
AutofitColumnWidth(),
DispatcherPriority.Default);
break;
case EViewAction.ProfilesFocus:
lstProfiles.Focus();
break;
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(_window, ResUI.RemoveServer) == ButtonResult.No)
{
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.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.DispatcherSpeedTest:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.SetSpeedTestResult((SpeedTestResult)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() =>
{
_ = RefreshServersBiz();
},
DispatcherPriority.Default);
break;
}
return await Task.FromResult(true);
}
public async Task ShareServer(string url)
{
if (url.IsNullOrEmpty())
{
return;
}
var dialog = new QrcodeView(url);
await DialogHost.Show(dialog);
}
public async Task RefreshServersBiz()
{
if (ViewModel != null)
{
await ViewModel.RefreshServersBiz();
}
if (lstProfiles.SelectedIndex > 0)
{
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
}
}
private void lstProfiles_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList();
}
}
private void LstProfiles_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
var source = e.Source as Border;
if (source?.Name == "HeaderBackground")
return;
if (_config.UiItem.DoubleClick2Activate)
{
ViewModel?.SetDefaultServer();
}
else
{
ViewModel?.EditServerAsync(EConfigType.Custom);
}
}
private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
{
e.Row.Header = $" {e.Row.Index + 1}";
}
//private void LstProfiles_ColumnHeader_Click(object? sender, RoutedEventArgs e)
//{
// var colHeader = sender as DataGridColumnHeader;
// if (colHeader == null || colHeader.TabIndex < 0 || colHeader.Column == null)
// {
// return;
// }
// var colName = ((MyDGTextColumn)colHeader.Column).ExName;
// ViewModel?.SortServer(colName);
//}
private void menuSelectAll_Click(object? sender, RoutedEventArgs e)
{
lstProfiles.SelectAll();
}
private void LstProfiles_KeyDown(object? sender, KeyEventArgs e)
{
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
{
switch (e.Key)
{
switch (e.Key)
{
case Key.A:
menuSelectAll_Click(null, null);
break;
case Key.A:
menuSelectAll_Click(null, null);
break;
case Key.C:
ViewModel?.Export2ShareUrlAsync(false);
break;
case Key.C:
ViewModel?.Export2ShareUrlAsync(false);
break;
case Key.D:
ViewModel?.EditServerAsync(EConfigType.Custom);
break;
case Key.D:
ViewModel?.EditServerAsync(EConfigType.Custom);
break;
case Key.F:
ViewModel?.ShareServerAsync();
break;
case Key.F:
ViewModel?.ShareServerAsync();
break;
case Key.O:
ViewModel?.ServerSpeedtest(ESpeedActionType.Tcping);
break;
case Key.O:
ViewModel?.ServerSpeedtest(ESpeedActionType.Tcping);
break;
case Key.R:
ViewModel?.ServerSpeedtest(ESpeedActionType.Realping);
break;
case Key.R:
ViewModel?.ServerSpeedtest(ESpeedActionType.Realping);
break;
case Key.T:
ViewModel?.ServerSpeedtest(ESpeedActionType.Speedtest);
break;
case Key.T:
ViewModel?.ServerSpeedtest(ESpeedActionType.Speedtest);
break;
case Key.E:
ViewModel?.ServerSpeedtest(ESpeedActionType.Mixedtest);
break;
}
}
else
{
if (e.Key is Key.Enter or Key.Return)
{
ViewModel?.SetDefaultServer();
}
else if (e.Key == Key.Delete)
{
ViewModel?.RemoveServerAsync();
}
else if (e.Key == Key.T)
{
ViewModel?.MoveServer(EMove.Top);
}
else if (e.Key == Key.U)
{
ViewModel?.MoveServer(EMove.Up);
}
else if (e.Key == Key.D)
{
ViewModel?.MoveServer(EMove.Down);
}
else if (e.Key == Key.B)
{
ViewModel?.MoveServer(EMove.Bottom);
}
else if (e.Key == Key.Escape)
{
ViewModel?.ServerSpeedtestStop();
}
case Key.E:
ViewModel?.ServerSpeedtest(ESpeedActionType.Mixedtest);
break;
}
}
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
{
AutofitColumnWidth();
}
private void AutofitColumnWidth()
{
foreach (var it in lstProfiles.Columns)
{
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
private void TxtServerFilter_KeyDown(object? sender, KeyEventArgs e)
else
{
if (e.Key is Key.Enter or Key.Return)
{
ViewModel?.RefreshServers();
ViewModel?.SetDefaultServer();
}
}
#endregion Event
#region UI
private void RestoreUI()
{
var lvColumnItem = _config.UiItem.MainColumnItem.OrderBy(t => t.Index).ToList();
var displayIndex = 0;
foreach (var item in lvColumnItem)
else if (e.Key == Key.Delete)
{
foreach (var item2 in lstProfiles.Columns)
{
if (item2.Tag == null)
{
continue;
}
if (item2.Tag.Equals(item.Name))
{
if (item.Width < 0)
{
item2.IsVisible = false;
}
else
{
item2.Width = new DataGridLength(item.Width, DataGridLengthUnitType.Pixel);
item2.DisplayIndex = displayIndex++;
}
if (item.Name.ToLower().StartsWith("to"))
{
item2.IsVisible = _config.GuiItem.EnableStatistics;
}
}
}
ViewModel?.RemoveServerAsync();
}
else if (e.Key == Key.T)
{
ViewModel?.MoveServer(EMove.Top);
}
else if (e.Key == Key.U)
{
ViewModel?.MoveServer(EMove.Up);
}
else if (e.Key == Key.D)
{
ViewModel?.MoveServer(EMove.Down);
}
else if (e.Key == Key.B)
{
ViewModel?.MoveServer(EMove.Bottom);
}
else if (e.Key == Key.Escape)
{
ViewModel?.ServerSpeedtestStop();
}
}
}
private void StorageUI(string? n = null)
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
{
AutofitColumnWidth();
}
private void AutofitColumnWidth()
{
foreach (var it in lstProfiles.Columns)
{
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
}
}
private void TxtServerFilter_KeyDown(object? sender, KeyEventArgs e)
{
if (e.Key is Key.Enter or Key.Return)
{
ViewModel?.RefreshServers();
}
}
#endregion Event
#region UI
private void RestoreUI()
{
var lvColumnItem = _config.UiItem.MainColumnItem.OrderBy(t => t.Index).ToList();
var displayIndex = 0;
foreach (var item in lvColumnItem)
{
List<ColumnItem> lvColumnItem = new();
foreach (var item2 in lstProfiles.Columns)
{
if (item2.Tag == null)
{
continue;
}
lvColumnItem.Add(new()
if (item2.Tag.Equals(item.Name))
{
Name = (string)item2.Tag,
Width = (int)(item2.IsVisible == true ? item2.ActualWidth : -1),
Index = item2.DisplayIndex
});
if (item.Width < 0)
{
item2.IsVisible = false;
}
else
{
item2.Width = new DataGridLength(item.Width, DataGridLengthUnitType.Pixel);
item2.DisplayIndex = displayIndex++;
}
if (item.Name.ToLower().StartsWith("to"))
{
item2.IsVisible = _config.GuiItem.EnableStatistics;
}
}
}
_config.UiItem.MainColumnItem = lvColumnItem;
}
#endregion UI
#region Drag and Drop
//private Point startPoint = new();
//private int startIndex = -1;
//private string formatData = "ProfileItemModel";
///// <summary>
///// Helper to search up the VisualTree
///// </summary>
///// <typeparam name="T"></typeparam>
///// <param name="current"></param>
///// <returns></returns>
//private static T? FindAncestor<T>(DependencyObject current) where T : DependencyObject
//{
// do
// {
// if (current is T)
// {
// return (T)current;
// }
// current = VisualTreeHelper.GetParent(current);
// }
// while (current != null);
// return null;
//}
//private void LstProfiles_PreviewMouseLeftButtonDown(object? sender, MouseButtonEventArgs e)
//{
// // Get current mouse position
// startPoint = e.GetPosition(null);
//}
//private void LstProfiles_MouseMove(object? sender, MouseEventArgs e)
//{
// // Get the current mouse position
// Point mousePos = e.GetPosition(null);
// Vector diff = startPoint - mousePos;
// if (e.LeftButton == MouseButtonState.Pressed &&
// (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
// Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
// {
// // Get the dragged Item
// if (sender is not DataGrid listView) return;
// var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
// if (listViewItem == null) return; // Abort
// // Find the data behind the ListViewItem
// ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
// if (item == null) return; // Abort
// // Initialize the drag & drop operation
// startIndex = lstProfiles.SelectedIndex;
// DataObject dragData = new(formatData, item);
// DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Copy | DragDropEffects.Move);
// }
//}
//private void LstProfiles_DragEnter(object? sender, DragEventArgs e)
//{
// if (!e.Data.GetDataPresent(formatData) || sender != e.Source)
// {
// e.Effects = DragDropEffects.None;
// }
//}
//private void LstProfiles_Drop(object? sender, DragEventArgs e)
//{
// if (e.Data.GetDataPresent(formatData) && sender == e.Source)
// {
// // Get the drop Item destination
// if (sender is not DataGrid listView) return;
// var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
// if (listViewItem == null)
// {
// // Abort
// e.Effects = DragDropEffects.None;
// return;
// }
// // Find the data behind the Item
// ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
// if (item == null) return;
// // Move item into observable collection
// // (this will be automatically reflected to lstView.ItemsSource)
// e.Effects = DragDropEffects.Move;
// ViewModel?.MoveServerTo(startIndex, item);
// startIndex = -1;
// }
//}
#endregion Drag and Drop
}
private void StorageUI(string? n = null)
{
List<ColumnItem> lvColumnItem = new();
foreach (var item2 in lstProfiles.Columns)
{
if (item2.Tag == null)
{
continue;
}
lvColumnItem.Add(new()
{
Name = (string)item2.Tag,
Width = (int)(item2.IsVisible == true ? item2.ActualWidth : -1),
Index = item2.DisplayIndex
});
}
_config.UiItem.MainColumnItem = lvColumnItem;
}
#endregion UI
#region Drag and Drop
//private Point startPoint = new();
//private int startIndex = -1;
//private string formatData = "ProfileItemModel";
///// <summary>
///// Helper to search up the VisualTree
///// </summary>
///// <typeparam name="T"></typeparam>
///// <param name="current"></param>
///// <returns></returns>
//private static T? FindAncestor<T>(DependencyObject current) where T : DependencyObject
//{
// do
// {
// if (current is T)
// {
// return (T)current;
// }
// current = VisualTreeHelper.GetParent(current);
// }
// while (current != null);
// return null;
//}
//private void LstProfiles_PreviewMouseLeftButtonDown(object? sender, MouseButtonEventArgs e)
//{
// // Get current mouse position
// startPoint = e.GetPosition(null);
//}
//private void LstProfiles_MouseMove(object? sender, MouseEventArgs e)
//{
// // Get the current mouse position
// Point mousePos = e.GetPosition(null);
// Vector diff = startPoint - mousePos;
// if (e.LeftButton == MouseButtonState.Pressed &&
// (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
// Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
// {
// // Get the dragged Item
// if (sender is not DataGrid listView) return;
// var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
// if (listViewItem == null) return; // Abort
// // Find the data behind the ListViewItem
// ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
// if (item == null) return; // Abort
// // Initialize the drag & drop operation
// startIndex = lstProfiles.SelectedIndex;
// DataObject dragData = new(formatData, item);
// DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Copy | DragDropEffects.Move);
// }
//}
//private void LstProfiles_DragEnter(object? sender, DragEventArgs e)
//{
// if (!e.Data.GetDataPresent(formatData) || sender != e.Source)
// {
// e.Effects = DragDropEffects.None;
// }
//}
//private void LstProfiles_Drop(object? sender, DragEventArgs e)
//{
// if (e.Data.GetDataPresent(formatData) && sender == e.Source)
// {
// // Get the drop Item destination
// if (sender is not DataGrid listView) return;
// var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
// if (listViewItem == null)
// {
// // Abort
// e.Effects = DragDropEffects.None;
// return;
// }
// // Find the data behind the Item
// ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
// if (item == null) return;
// // Move item into observable collection
// // (this will be automatically reflected to lstView.ItemsSource)
// e.Effects = DragDropEffects.Move;
// ViewModel?.MoveServerTo(startIndex, item);
// startIndex = -1;
// }
//}
#endregion Drag and Drop
}

View File

@@ -2,40 +2,39 @@ using Avalonia.Controls;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class QrcodeView : UserControl
{
public partial class QrcodeView : UserControl
public QrcodeView()
{
public QrcodeView()
InitializeComponent();
}
public QrcodeView(string? url)
{
InitializeComponent();
txtContent.Text = url;
imgQrcode.Source = GetQRCode(url);
txtContent.GotFocus += (_, _) => Dispatcher.UIThread.Post(() => { txtContent.SelectAll(); });
}
private Bitmap? GetQRCode(string? url)
{
var bytes = QRCodeHelper.GenQRCode(url);
return ByteToBitmap(bytes);
}
private Bitmap? ByteToBitmap(byte[]? bytes)
{
if (bytes is null)
{
InitializeComponent();
return null;
}
public QrcodeView(string? url)
{
InitializeComponent();
txtContent.Text = url;
imgQrcode.Source = GetQRCode(url);
txtContent.GotFocus += (_, _) => Dispatcher.UIThread.Post(() => { txtContent.SelectAll(); });
}
private Bitmap? GetQRCode(string? url)
{
var bytes = QRCodeHelper.GenQRCode(url);
return ByteToBitmap(bytes);
}
private Bitmap? ByteToBitmap(byte[]? bytes)
{
if (bytes is null)
{
return null;
}
using var ms = new MemoryStream(bytes);
return new Bitmap(ms);
}
using var ms = new MemoryStream(bytes);
return new Bitmap(ms);
}
}

View File

@@ -4,104 +4,103 @@ using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using ReactiveUI;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class RoutingRuleDetailsWindow : ReactiveWindow<RoutingRuleDetailsViewModel>
{
public partial class RoutingRuleDetailsWindow : ReactiveWindow<RoutingRuleDetailsViewModel>
public RoutingRuleDetailsWindow()
{
public RoutingRuleDetailsWindow()
InitializeComponent();
}
public RoutingRuleDetailsWindow(RulesItem rulesItem)
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
ViewModel = new RoutingRuleDetailsViewModel(rulesItem, UpdateViewHandler);
cmbOutboundTag.Items.Add(Global.ProxyTag);
cmbOutboundTag.Items.Add(Global.DirectTag);
cmbOutboundTag.Items.Add(Global.BlockTag);
Global.RuleProtocols.ForEach(it =>
{
InitializeComponent();
}
public RoutingRuleDetailsWindow(RulesItem rulesItem)
clbProtocol.Items.Add(it);
});
Global.InboundTags.ForEach(it =>
{
InitializeComponent();
clbInboundTag.Items.Add(it);
});
Global.RuleNetworks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
ViewModel = new RoutingRuleDetailsViewModel(rulesItem, UpdateViewHandler);
cmbOutboundTag.Items.Add(Global.ProxyTag);
cmbOutboundTag.Items.Add(Global.DirectTag);
cmbOutboundTag.Items.Add(Global.BlockTag);
Global.RuleProtocols.ForEach(it =>
if (!rulesItem.Id.IsNullOrEmpty())
{
rulesItem.Protocol?.ForEach(it =>
{
clbProtocol.Items.Add(it);
clbProtocol?.SelectedItems?.Add(it);
});
Global.InboundTags.ForEach(it =>
rulesItem.InboundTag?.ForEach(it =>
{
clbInboundTag.Items.Add(it);
});
Global.RuleNetworks.ForEach(it =>
{
cmbNetwork.Items.Add(it);
});
if (!rulesItem.Id.IsNullOrEmpty())
{
rulesItem.Protocol?.ForEach(it =>
{
clbProtocol?.SelectedItems?.Add(it);
});
rulesItem.InboundTag?.ForEach(it =>
{
clbInboundTag?.SelectedItems?.Add(it);
});
}
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Domain, v => v.txtDomain.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Process, v => v.txtProcess.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoSort, v => v.chkAutoSort.IsChecked).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
clbInboundTag?.SelectedItems?.Add(it);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
this.WhenActivated(disposables =>
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
}
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Domain, v => v.txtDomain.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.IP, v => v.txtIP.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Process, v => v.txtProcess.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.AutoSort, v => v.chkAutoSort.IsChecked).DisposeWith(disposables);
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
cmbOutboundTag.Focus();
}
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
}
private void ClbProtocol_SelectionChanged(object? sender, SelectionChangedEventArgs e)
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
if (ViewModel != null)
{
ViewModel.ProtocolItems = clbProtocol.SelectedItems.Cast<string>().ToList();
}
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
}
private void ClbInboundTag_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.InboundTagItems = clbInboundTag.SelectedItems.Cast<string>().ToList();
}
}
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
cmbOutboundTag.Focus();
}
private void linkRuleobjectDoc_Click(object? sender, RoutedEventArgs e)
private void ClbProtocol_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
ViewModel.ProtocolItems = clbProtocol.SelectedItems.Cast<string>().ToList();
}
}
private void ClbInboundTag_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.InboundTagItems = clbInboundTag.SelectedItems.Cast<string>().ToList();
}
}
private void linkRuleobjectDoc_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
}
}

View File

@@ -7,214 +7,213 @@ using MsBox.Avalonia.Enums;
using ReactiveUI;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class RoutingRuleSettingWindow : ReactiveWindow<RoutingRuleSettingViewModel>
{
public partial class RoutingRuleSettingWindow : ReactiveWindow<RoutingRuleSettingViewModel>
public RoutingRuleSettingWindow()
{
public RoutingRuleSettingWindow()
InitializeComponent();
}
public RoutingRuleSettingWindow(RoutingItem routingItem)
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
this.KeyDown += RoutingRuleSettingWindow_KeyDown;
lstRules.SelectionChanged += lstRules_SelectionChanged;
lstRules.DoubleTapped += LstRules_DoubleTapped;
menuRuleSelectAll.Click += menuRuleSelectAll_Click;
//btnBrowseCustomIcon.Click += btnBrowseCustomIcon_Click;
btnBrowseCustomRulesetPath4Singbox.Click += btnBrowseCustomRulesetPath4Singbox_ClickAsync;
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
Global.DomainStrategies.ForEach(it =>
{
InitializeComponent();
cmbdomainStrategy.Items.Add(it);
});
cmbdomainStrategy.Items.Add(string.Empty);
Global.DomainStrategies4Singbox.ForEach(it =>
{
cmbdomainStrategy4Singbox.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.RulesItems, v => v.lstRules.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstRules.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.DomainStrategy, v => v.cmbdomainStrategy.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.DomainStrategy4Singbox, v => v.cmbdomainStrategy4Singbox.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.Url, v => v.txtUrl.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SelectedRouting.CustomIcon, v => v.txtCustomIcon.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.CustomRulesetPath4Singbox, v => v.txtCustomRulesetPath4Singbox.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.Sort, v => v.txtSort.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RuleAddCmd, v => v.menuRuleAdd).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportRulesFromFileCmd, v => v.menuImportRulesFromFile).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportRulesFromClipboardCmd, v => v.menuImportRulesFromClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportRulesFromUrlCmd, v => v.menuImportRulesFromUrl).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RuleAddCmd, v => v.menuRuleAdd2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RuleRemoveCmd, v => v.menuRuleRemove).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RuleExportSelectedCmd, v => v.menuRuleExportSelected).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);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No)
{
return false;
}
break;
case EViewAction.AddBatchRoutingRulesYesNo:
if (await UI.ShowYesNo(this, ResUI.AddBatchRoutingRulesYesNo) == ButtonResult.No)
{
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;
}
public RoutingRuleSettingWindow(RoutingItem routingItem)
return await Task.FromResult(true);
}
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
txtRemarks.Focus();
}
private void RoutingRuleSettingWindow_KeyDown(object? sender, KeyEventArgs e)
{
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
this.KeyDown += RoutingRuleSettingWindow_KeyDown;
lstRules.SelectionChanged += lstRules_SelectionChanged;
lstRules.DoubleTapped += LstRules_DoubleTapped;
menuRuleSelectAll.Click += menuRuleSelectAll_Click;
//btnBrowseCustomIcon.Click += btnBrowseCustomIcon_Click;
btnBrowseCustomRulesetPath4Singbox.Click += btnBrowseCustomRulesetPath4Singbox_ClickAsync;
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
Global.DomainStrategies.ForEach(it =>
if (e.Key == Key.A)
{
cmbdomainStrategy.Items.Add(it);
});
cmbdomainStrategy.Items.Add(string.Empty);
Global.DomainStrategies4Singbox.ForEach(it =>
{
cmbdomainStrategy4Singbox.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.RulesItems, v => v.lstRules.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstRules.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.DomainStrategy, v => v.cmbdomainStrategy.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.DomainStrategy4Singbox, v => v.cmbdomainStrategy4Singbox.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.Url, v => v.txtUrl.Text).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SelectedRouting.CustomIcon, v => v.txtCustomIcon.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.CustomRulesetPath4Singbox, v => v.txtCustomRulesetPath4Singbox.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting.Sort, v => v.txtSort.Text).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RuleAddCmd, v => v.menuRuleAdd).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportRulesFromFileCmd, v => v.menuImportRulesFromFile).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportRulesFromClipboardCmd, v => v.menuImportRulesFromClipboard).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ImportRulesFromUrlCmd, v => v.menuImportRulesFromUrl).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RuleAddCmd, v => v.menuRuleAdd2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RuleRemoveCmd, v => v.menuRuleRemove).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RuleExportSelectedCmd, v => v.menuRuleExportSelected).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);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No)
{
return false;
}
break;
case EViewAction.AddBatchRoutingRulesYesNo:
if (await UI.ShowYesNo(this, ResUI.AddBatchRoutingRulesYesNo) == ButtonResult.No)
{
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;
lstRules.SelectAll();
}
return await Task.FromResult(true);
}
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
txtRemarks.Focus();
}
private void RoutingRuleSettingWindow_KeyDown(object? sender, KeyEventArgs e)
{
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
else if (e.Key == Key.C)
{
if (e.Key == Key.A)
{
lstRules.SelectAll();
}
else if (e.Key == Key.C)
{
ViewModel?.RuleExportSelectedAsync();
}
}
else
{
if (e.Key == Key.T)
{
ViewModel?.MoveRule(EMove.Top);
}
else if (e.Key == Key.U)
{
ViewModel?.MoveRule(EMove.Up);
}
else if (e.Key == Key.D)
{
ViewModel?.MoveRule(EMove.Down);
}
else if (e.Key == Key.B)
{
ViewModel?.MoveRule(EMove.Bottom);
}
else if (e.Key == Key.Delete)
{
ViewModel?.RuleRemoveAsync();
}
ViewModel?.RuleExportSelectedAsync();
}
}
private void lstRules_SelectionChanged(object? sender, SelectionChangedEventArgs e)
else
{
if (ViewModel != null)
if (e.Key == Key.T)
{
ViewModel.SelectedSources = lstRules.SelectedItems.Cast<RulesItemModel>().ToList();
ViewModel?.MoveRule(EMove.Top);
}
}
private void LstRules_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.RuleEditAsync(false);
}
private void menuRuleSelectAll_Click(object? sender, RoutedEventArgs e)
{
lstRules.SelectAll();
}
//private async void btnBrowseCustomIcon_Click(object? sender, RoutedEventArgs e)
//{
// var fileName = await UI.OpenFileDialog(this, FilePickerFileTypes.ImagePng);
// if (fileName.IsNullOrEmpty())
// {
// return;
// }
// txtCustomIcon.Text = fileName;
//}
private async void btnBrowseCustomRulesetPath4Singbox_ClickAsync(object? sender, RoutedEventArgs e)
{
var fileName = await UI.OpenFileDialog(this, null);
if (fileName.IsNullOrEmpty())
else if (e.Key == Key.U)
{
return;
ViewModel?.MoveRule(EMove.Up);
}
else if (e.Key == Key.D)
{
ViewModel?.MoveRule(EMove.Down);
}
else if (e.Key == Key.B)
{
ViewModel?.MoveRule(EMove.Bottom);
}
else if (e.Key == Key.Delete)
{
ViewModel?.RuleRemoveAsync();
}
txtCustomRulesetPath4Singbox.Text = fileName;
}
private void linkCustomRulesetPath4Singbox(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://github.com/2dust/v2rayCustomRoutingList/blob/master/singbox_custom_ruleset_example.json");
}
}
private void lstRules_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.SelectedSources = lstRules.SelectedItems.Cast<RulesItemModel>().ToList();
}
}
private void LstRules_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.RuleEditAsync(false);
}
private void menuRuleSelectAll_Click(object? sender, RoutedEventArgs e)
{
lstRules.SelectAll();
}
//private async void btnBrowseCustomIcon_Click(object? sender, RoutedEventArgs e)
//{
// var fileName = await UI.OpenFileDialog(this, FilePickerFileTypes.ImagePng);
// if (fileName.IsNullOrEmpty())
// {
// return;
// }
// txtCustomIcon.Text = fileName;
//}
private async void btnBrowseCustomRulesetPath4Singbox_ClickAsync(object? sender, RoutedEventArgs e)
{
var fileName = await UI.OpenFileDialog(this, null);
if (fileName.IsNullOrEmpty())
{
return;
}
txtCustomRulesetPath4Singbox.Text = fileName;
}
private void linkCustomRulesetPath4Singbox(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://github.com/2dust/v2rayCustomRoutingList/blob/master/singbox_custom_ruleset_example.json");
}
}

View File

@@ -7,142 +7,141 @@ using MsBox.Avalonia.Enums;
using ReactiveUI;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class RoutingSettingWindow : ReactiveWindow<RoutingSettingViewModel>
{
public partial class RoutingSettingWindow : ReactiveWindow<RoutingSettingViewModel>
private bool _manualClose = false;
public RoutingSettingWindow()
{
private bool _manualClose = false;
InitializeComponent();
public RoutingSettingWindow()
this.Closing += RoutingSettingWindow_Closing;
btnCancel.Click += (s, e) => this.Close();
this.KeyDown += RoutingSettingWindow_KeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.DoubleTapped += LstRoutings_DoubleTapped;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
Global.DomainStrategies.ForEach(it =>
{
InitializeComponent();
this.Closing += RoutingSettingWindow_Closing;
btnCancel.Click += (s, e) => this.Close();
this.KeyDown += RoutingSettingWindow_KeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.DoubleTapped += LstRoutings_DoubleTapped;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
Global.DomainStrategies.ForEach(it =>
{
cmbdomainStrategy.Items.Add(it);
});
Global.DomainMatchers.ForEach(it =>
{
cmbdomainMatcher.Items.Add(it);
});
Global.DomainStrategies4Singbox.ForEach(it =>
{
cmbdomainStrategy4Singbox.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.lstRoutings.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstRoutings.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy, v => v.cmbdomainStrategy.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainMatcher, v => v.cmbdomainMatcher.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4Singbox, v => v.cmbdomainStrategy4Singbox.SelectedValue).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedAddCmd, v => v.menuRoutingAdvancedAdd).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedAddCmd, v => v.menuRoutingAdvancedAdd2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedRemoveCmd, v => v.menuRoutingAdvancedRemove).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.menuRoutingAdvancedImportRules2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
cmbdomainStrategy.Items.Add(it);
});
Global.DomainMatchers.ForEach(it =>
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveRules) == ButtonResult.No)
{
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 void RoutingSettingWindow_KeyDown(object? sender, KeyEventArgs e)
cmbdomainMatcher.Items.Add(it);
});
Global.DomainStrategies4Singbox.ForEach(it =>
{
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
{
if (e.Key == Key.A)
cmbdomainStrategy4Singbox.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.RoutingItems, v => v.lstRoutings.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstRoutings.SelectedItem).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy, v => v.cmbdomainStrategy.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainMatcher, v => v.cmbdomainMatcher.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.DomainStrategy4Singbox, v => v.cmbdomainStrategy4Singbox.SelectedValue).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedAddCmd, v => v.menuRoutingAdvancedAdd).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedAddCmd, v => v.menuRoutingAdvancedAdd2).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RoutingAdvancedRemoveCmd, v => v.menuRoutingAdvancedRemove).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.menuRoutingAdvancedImportRules2).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:
this.Close(true);
break;
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveRules) == ButtonResult.No)
{
lstRoutings.SelectAll();
return false;
}
}
else if (e.Key is Key.Enter or Key.Return)
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 void RoutingSettingWindow_KeyDown(object? sender, KeyEventArgs e)
{
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
{
if (e.Key == Key.A)
{
ViewModel?.RoutingAdvancedSetDefault();
}
else if (e.Key == Key.Delete)
{
ViewModel?.RoutingAdvancedRemoveAsync();
lstRoutings.SelectAll();
}
}
private void menuRoutingAdvancedSelectAll_Click(object? sender, RoutedEventArgs e)
else if (e.Key is Key.Enter or Key.Return)
{
lstRoutings.SelectAll();
ViewModel?.RoutingAdvancedSetDefault();
}
private void lstRoutings_SelectionChanged(object? sender, SelectionChangedEventArgs e)
else if (e.Key == Key.Delete)
{
if (ViewModel != null)
ViewModel?.RoutingAdvancedRemoveAsync();
}
}
private void menuRoutingAdvancedSelectAll_Click(object? sender, RoutedEventArgs e)
{
lstRoutings.SelectAll();
}
private void lstRoutings_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.SelectedSources = lstRoutings.SelectedItems.Cast<RoutingItemModel>().ToList();
}
}
private void LstRoutings_DoubleTapped(object? sender, TappedEventArgs e)
{
ViewModel?.RoutingAdvancedEditAsync(false);
}
private void linkdomainStrategy_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html");
}
private void linkdomainStrategy4Singbox_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
}
private void btnCancel_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
this.Close(ViewModel?.IsModified);
}
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
{
if (ViewModel?.IsModified == true)
{
if (!_manualClose)
{
ViewModel.SelectedSources = lstRoutings.SelectedItems.Cast<RoutingItemModel>().ToList();
}
}
private void LstRoutings_DoubleTapped(object? sender, TappedEventArgs e)
{
ViewModel?.RoutingAdvancedEditAsync(false);
}
private void linkdomainStrategy_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html");
}
private void linkdomainStrategy4Singbox_Click(object? sender, RoutedEventArgs e)
{
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
}
private void btnCancel_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
this.Close(ViewModel?.IsModified);
}
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
{
if (ViewModel?.IsModified == true)
{
if (!_manualClose)
{
btnCancel_Click(null, null);
}
btnCancel_Click(null, null);
}
}
}

View File

@@ -8,97 +8,96 @@ using ReactiveUI;
using Splat;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
{
public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
private static Config _config;
public StatusBarView()
{
private static Config _config;
InitializeComponent();
public StatusBarView()
_config = AppHandler.Instance.Config;
//ViewModel = new StatusBarViewModel(UpdateViewHandler);
//Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
ViewModel = Locator.Current.GetService<StatusBarViewModel>();
ViewModel?.InitUpdateView(UpdateViewHandler);
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;
txtRunningInfoDisplay.Tapped += TxtRunningServerDisplay_Tapped;
this.WhenActivated(disposables =>
{
InitializeComponent();
//status bar
this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.InboundLanDisplay, v => v.txtInboundLanDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningServerDisplay, v => v.txtRunningServerDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningInfoDisplay, v => v.txtRunningInfoDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedProxyDisplay, v => v.txtSpeedProxyDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedDirectDisplay, v => v.txtSpeedDirectDisplay.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableTun, v => v.togEnableTun.IsChecked).DisposeWith(disposables);
_config = AppHandler.Instance.Config;
//ViewModel = new StatusBarViewModel(UpdateViewHandler);
//Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(StatusBarViewModel));
ViewModel = Locator.Current.GetService<StatusBarViewModel>();
ViewModel?.InitUpdateView(UpdateViewHandler);
this.Bind(ViewModel, vm => vm.SystemProxySelected, v => v.cmbSystemProxy.SelectedIndex).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedRouting, v => v.cmbRoutings2.SelectedItem).DisposeWith(disposables);
});
txtRunningServerDisplay.Tapped += TxtRunningServerDisplay_Tapped;
txtRunningInfoDisplay.Tapped += TxtRunningServerDisplay_Tapped;
//spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator);
this.WhenActivated(disposables =>
{
//status bar
this.OneWayBind(ViewModel, vm => vm.InboundDisplay, v => v.txtInboundDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.InboundLanDisplay, v => v.txtInboundLanDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningServerDisplay, v => v.txtRunningServerDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.RunningInfoDisplay, v => v.txtRunningInfoDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedProxyDisplay, v => v.txtSpeedProxyDisplay.Text).DisposeWith(disposables);
this.OneWayBind(ViewModel, vm => vm.SpeedDirectDisplay, v => v.txtSpeedDirectDisplay.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.EnableTun, v => v.togEnableTun.IsChecked).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);
});
//spEnableTun.IsVisible = (Utils.IsWindows() || AppHandler.Instance.IsAdministrator);
if (Utils.IsNonWindows() && cmbSystemProxy.Items.IsReadOnly == false)
{
cmbSystemProxy.Items.RemoveAt(cmbSystemProxy.Items.Count - 1);
}
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
if (Utils.IsNonWindows() && cmbSystemProxy.Items.IsReadOnly == false)
{
switch (action)
{
case EViewAction.DispatcherServerAvailability:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.TestServerAvailabilityResult((string)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshServersBiz(),
DispatcherPriority.Default);
break;
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;
}
return await Task.FromResult(true);
}
private void RefreshIcon()
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow.Icon = AvaUtils.GetAppIcon(_config.SystemProxyItem.SysProxyType);
var iconslist = TrayIcon.GetIcons(Application.Current);
iconslist[0].Icon = desktop.MainWindow.Icon;
TrayIcon.SetIcons(Application.Current, iconslist);
}
}
private void TxtRunningServerDisplay_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.TestServerAvailability();
cmbSystemProxy.Items.RemoveAt(cmbSystemProxy.Items.Count - 1);
}
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
case EViewAction.DispatcherServerAvailability:
if (obj is null)
return false;
Dispatcher.UIThread.Post(() =>
ViewModel?.TestServerAvailabilityResult((string)obj),
DispatcherPriority.Default);
break;
case EViewAction.DispatcherRefreshServersBiz:
Dispatcher.UIThread.Post(() =>
ViewModel?.RefreshServersBiz(),
DispatcherPriority.Default);
break;
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;
}
return await Task.FromResult(true);
}
private void RefreshIcon()
{
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow.Icon = AvaUtils.GetAppIcon(_config.SystemProxyItem.SysProxyType);
var iconslist = TrayIcon.GetIcons(Application.Current);
iconslist[0].Icon = desktop.MainWindow.Icon;
TrayIcon.SetIcons(Application.Current, iconslist);
}
}
private void TxtRunningServerDisplay_Tapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.TestServerAvailability();
}
}

View File

@@ -4,63 +4,62 @@ using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
using ReactiveUI;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class SubEditWindow : ReactiveWindow<SubEditViewModel>
{
public partial class SubEditWindow : ReactiveWindow<SubEditViewModel>
public SubEditWindow()
{
public SubEditWindow()
InitializeComponent();
}
public SubEditWindow(SubItem subItem)
{
InitializeComponent();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
Global.SubConvertTargets.ForEach(it =>
{
InitializeComponent();
}
cmbConvertTarget.Items.Add(it);
});
public SubEditWindow(SubItem subItem)
this.WhenActivated(disposables =>
{
InitializeComponent();
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Url, v => v.txtUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.MoreUrl, v => v.txtMoreUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Enabled, v => v.togEnable.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.AutoUpdateInterval, v => v.txtAutoUpdateInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.UserAgent, v => v.txtUserAgent.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Sort, v => v.txtSort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.ConvertTarget, v => v.cmbConvertTarget.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.NextProfile, v => v.txtNextProfile.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);
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
}
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
Global.SubConvertTargets.ForEach(it =>
{
cmbConvertTarget.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Url, v => v.txtUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.MoreUrl, v => v.txtMoreUrl.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Enabled, v => v.togEnable.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.AutoUpdateInterval, v => v.txtAutoUpdateInterval.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.UserAgent, v => v.txtUserAgent.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Sort, v => v.txtSort.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.ConvertTarget, v => v.cmbConvertTarget.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.PrevProfile, v => v.txtPrevProfile.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.NextProfile, v => v.txtNextProfile.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.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
case EViewAction.CloseWindow:
this.Close(true);
break;
}
return await Task.FromResult(true);
}
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
txtRemarks.Focus();
}
private void Window_Loaded(object? sender, RoutedEventArgs e)
{
txtRemarks.Focus();
}
}

View File

@@ -8,102 +8,101 @@ using MsBox.Avalonia.Enums;
using ReactiveUI;
using v2rayN.Desktop.Common;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
public partial class SubSettingWindow : ReactiveWindow<SubSettingViewModel>
{
public partial class SubSettingWindow : ReactiveWindow<SubSettingViewModel>
private bool _manualClose = false;
public SubSettingWindow()
{
private bool _manualClose = false;
InitializeComponent();
public SubSettingWindow()
menuClose.Click += menuClose_Click;
this.Closing += SubSettingWindow_Closing;
ViewModel = new SubSettingViewModel(UpdateViewHandler);
lstSubscription.DoubleTapped += LstSubscription_DoubleTapped;
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
this.WhenActivated(disposables =>
{
InitializeComponent();
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.lstSubscription.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstSubscription.SelectedItem).DisposeWith(disposables);
menuClose.Click += menuClose_Click;
this.Closing += SubSettingWindow_Closing;
ViewModel = new SubSettingViewModel(UpdateViewHandler);
lstSubscription.DoubleTapped += LstSubscription_DoubleTapped;
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
this.BindCommand(ViewModel, vm => vm.SubAddCmd, v => v.menuSubAdd).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare).DisposeWith(disposables);
});
}
this.WhenActivated(disposables =>
{
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.lstSubscription.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource, v => v.lstSubscription.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubAddCmd, v => v.menuSubAdd).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare).DisposeWith(disposables);
});
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
{
switch (action)
{
switch (action)
{
case EViewAction.CloseWindow:
this.Close();
break;
case EViewAction.CloseWindow:
this.Close();
break;
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No)
{
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)
{
if (url.IsNullOrEmpty())
{
return;
}
var dialog = new QrcodeView(url);
await DialogHost.Show(dialog, "dialogHostSub");
}
private void LstSubscription_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.EditSubAsync(false);
}
private void LstSubscription_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.SelectedSources = lstSubscription.SelectedItems.Cast<SubItem>().ToList();
}
}
private void menuClose_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
this.Close(ViewModel?.IsModified);
}
private void SubSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
{
if (ViewModel?.IsModified == true)
{
if (!_manualClose)
case EViewAction.ShowYesNo:
if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No)
{
menuClose_Click(null, null);
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)
{
if (url.IsNullOrEmpty())
{
return;
}
var dialog = new QrcodeView(url);
await DialogHost.Show(dialog, "dialogHostSub");
}
private void LstSubscription_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
{
ViewModel?.EditSubAsync(false);
}
private void LstSubscription_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (ViewModel != null)
{
ViewModel.SelectedSources = lstSubscription.SelectedItems.Cast<SubItem>().ToList();
}
}
private void menuClose_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
this.Close(ViewModel?.IsModified);
}
private void SubSettingWindow_Closing(object? sender, WindowClosingEventArgs e)
{
if (ViewModel?.IsModified == true)
{
if (!_manualClose)
{
menuClose_Click(null, null);
}
}
}

View File

@@ -4,39 +4,38 @@ using Avalonia.ReactiveUI;
using ReactiveUI;
using v2rayN.Desktop.ViewModels;
namespace v2rayN.Desktop.Views
namespace v2rayN.Desktop.Views;
/// <summary>
/// ThemeSettingView.xaml
/// </summary>
public partial class ThemeSettingView : ReactiveUserControl<ThemeSettingViewModel>
{
/// <summary>
/// ThemeSettingView.xaml
/// </summary>
public partial class ThemeSettingView : ReactiveUserControl<ThemeSettingViewModel>
public ThemeSettingView()
{
public ThemeSettingView()
InitializeComponent();
ViewModel = new ThemeSettingViewModel();
foreach (ETheme it in Enum.GetValues(typeof(ETheme)))
{
InitializeComponent();
ViewModel = new ThemeSettingViewModel();
foreach (ETheme it in Enum.GetValues(typeof(ETheme)))
{
cmbCurrentTheme.Items.Add(it.ToString());
}
for (int i = Global.MinFontSize; i <= Global.MinFontSize + 10; i++)
{
cmbCurrentFontSize.Items.Add(i);
}
Global.Languages.ForEach(it =>
{
cmbCurrentLanguage.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.CurrentTheme, v => v.cmbCurrentTheme.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.SelectedValue).DisposeWith(disposables);
});
cmbCurrentTheme.Items.Add(it.ToString());
}
for (int i = Global.MinFontSize; i <= Global.MinFontSize + 10; i++)
{
cmbCurrentFontSize.Items.Add(i);
}
Global.Languages.ForEach(it =>
{
cmbCurrentLanguage.Items.Add(it);
});
this.WhenActivated(disposables =>
{
this.Bind(ViewModel, vm => vm.CurrentTheme, v => v.cmbCurrentTheme.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.SelectedValue).DisposeWith(disposables);
});
}
}