mirror of
https://github.com/2dust/v2rayN.git
synced 2026-08-05 14:52:05 +03:00
Changed key handling and menu input gestures to allow Backspace (in addition to Delete) for removing items in server, profile, and routing rule lists. This improves usability and consistency across both Avalonia and WPF views.
171 lines
5.9 KiB
C#
171 lines
5.9 KiB
C#
using v2rayN.Desktop.Base;
|
|
|
|
namespace v2rayN.Desktop.Views;
|
|
|
|
public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
|
|
{
|
|
public AddGroupServerWindow()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
public AddGroupServerWindow(ProfileItem profileItem)
|
|
{
|
|
InitializeComponent();
|
|
|
|
Loaded += Window_Loaded;
|
|
btnCancel.Click += (s, e) => Close();
|
|
lstChild.SelectionChanged += LstChild_SelectionChanged;
|
|
|
|
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
|
|
|
|
cmbCoreType.ItemsSource = Global.CoreTypes;
|
|
cmbPolicyGroupType.ItemsSource = new List<string>
|
|
{
|
|
ResUI.TbLeastPing,
|
|
ResUI.TbFallback,
|
|
ResUI.TbRandom,
|
|
ResUI.TbRoundRobin,
|
|
ResUI.TbLeastLoad,
|
|
};
|
|
|
|
switch (profileItem.ConfigType)
|
|
{
|
|
case EConfigType.PolicyGroup:
|
|
Title = ResUI.TbConfigTypePolicyGroup;
|
|
break;
|
|
|
|
case EConfigType.ProxyChain:
|
|
Title = ResUI.TbConfigTypeProxyChain;
|
|
gridPolicyGroup.IsVisible = false;
|
|
break;
|
|
}
|
|
|
|
this.WhenActivated(disposables =>
|
|
{
|
|
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
|
this.Bind(ViewModel, vm => vm.CoreType, v => v.cmbCoreType.SelectedValue).DisposeWith(disposables);
|
|
this.Bind(ViewModel, vm => vm.PolicyGroupType, v => v.cmbPolicyGroupType.SelectedValue).DisposeWith(disposables);
|
|
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbSubChildItems.ItemsSource).DisposeWith(disposables);
|
|
this.Bind(ViewModel, vm => vm.SelectedSubItem, v => v.cmbSubChildItems.SelectedItem).DisposeWith(disposables);
|
|
this.Bind(ViewModel, vm => vm.Filter, v => v.txtFilter.Text).DisposeWith(disposables);
|
|
|
|
this.OneWayBind(ViewModel, vm => vm.ChildItemsObs, v => v.lstChild.ItemsSource).DisposeWith(disposables);
|
|
this.Bind(ViewModel, vm => vm.SelectedChild, v => v.lstChild.SelectedItem).DisposeWith(disposables);
|
|
|
|
this.BindCommand(ViewModel, vm => vm.RemoveCmd, v => v.menuRemoveChildServer).DisposeWith(disposables);
|
|
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
|
this.BindCommand(ViewModel, vm => vm.MoveDownCmd, v => v.menuMoveDown).DisposeWith(disposables);
|
|
this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);
|
|
|
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
|
});
|
|
|
|
// Context menu actions that require custom logic (Add, SelectAll)
|
|
menuAddChildServer.Click += MenuAddChild_Click;
|
|
menuSelectAllChild.Click += (s, e) => lstChild.SelectAll();
|
|
|
|
// Keyboard shortcuts when focus is within grid
|
|
AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
|
|
lstChild.LoadingRow += LstChild_LoadingRow;
|
|
}
|
|
|
|
private void LstChild_LoadingRow(object? sender, DataGridRowEventArgs e)
|
|
{
|
|
e.Row.Header = $" {e.Row.Index + 1}";
|
|
}
|
|
|
|
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
|
{
|
|
switch (action)
|
|
{
|
|
case EViewAction.CloseWindow:
|
|
Close(true);
|
|
break;
|
|
}
|
|
return await Task.FromResult(true);
|
|
}
|
|
|
|
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
|
{
|
|
txtRemarks.Focus();
|
|
}
|
|
|
|
private void AddGroupServerWindow_KeyDown(object? sender, KeyEventArgs e)
|
|
{
|
|
if (!lstChild.IsKeyboardFocusWithin)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((e.KeyModifiers & (KeyModifiers.Control | KeyModifiers.Meta)) != 0)
|
|
{
|
|
if (e.Key == Key.A)
|
|
{
|
|
lstChild.SelectAll();
|
|
e.Handled = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (e.Key)
|
|
{
|
|
case Key.T:
|
|
ViewModel?.MoveServer(EMove.Top);
|
|
e.Handled = true;
|
|
break;
|
|
|
|
case Key.U:
|
|
ViewModel?.MoveServer(EMove.Up);
|
|
e.Handled = true;
|
|
break;
|
|
|
|
case Key.D:
|
|
ViewModel?.MoveServer(EMove.Down);
|
|
e.Handled = true;
|
|
break;
|
|
|
|
case Key.B:
|
|
ViewModel?.MoveServer(EMove.Bottom);
|
|
e.Handled = true;
|
|
break;
|
|
|
|
case Key.Delete:
|
|
case Key.Back:
|
|
ViewModel?.ChildRemoveAsync();
|
|
e.Handled = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private async void MenuAddChild_Click(object? sender, RoutedEventArgs e)
|
|
{
|
|
var selectWindow = new ProfilesSelectWindow();
|
|
if (ViewModel?.SelectedSource?.ConfigType == EConfigType.PolicyGroup)
|
|
{
|
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom }, exclude: true);
|
|
}
|
|
else
|
|
{
|
|
selectWindow.SetConfigTypeFilter(new[] { EConfigType.Custom, EConfigType.PolicyGroup, EConfigType.ProxyChain }, exclude: true);
|
|
}
|
|
selectWindow.AllowMultiSelect(true);
|
|
var result = await selectWindow.ShowDialog<bool?>(this);
|
|
if (result == true)
|
|
{
|
|
var profiles = await selectWindow.ProfileItems;
|
|
ViewModel?.ChildItemsObs.AddRange(profiles);
|
|
}
|
|
}
|
|
|
|
private void LstChild_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
|
{
|
|
if (ViewModel != null)
|
|
{
|
|
ViewModel.SelectedChildren = lstChild.SelectedItems.Cast<ProfileItem>().ToList();
|
|
}
|
|
}
|
|
}
|