mirror of https://github.com/2dust/v2rayN
avalonia
parent
9a4d8faaa2
commit
1242011513
|
@ -0,0 +1,118 @@
|
|||
<Window
|
||||
x:Class="v2rayN.Desktop.Views.ProfilesSelectWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
xmlns:conv="using:v2rayN.Desktop.Converters"
|
||||
x:DataType="vms:ProfilesSelectViewModel"
|
||||
Width="800"
|
||||
Height="450"
|
||||
Title="{x:Static resx:ResUI.TbSelectProfile}"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Window.Resources>
|
||||
<conv:DelayColorConverter x:Key="DelayColorConverter" />
|
||||
</Window.Resources>
|
||||
|
||||
<DockPanel Margin="8">
|
||||
<!-- Bottom buttons -->
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Center" Margin="4">
|
||||
<Button x:Name="btnSave" Width="100" Content="{x:Static resx:ResUI.TbConfirm}" />
|
||||
<Button x:Name="btnCancel" Width="100" Margin="8,0" Content="{x:Static resx:ResUI.TbCancel}" />
|
||||
</StackPanel>
|
||||
|
||||
<Grid>
|
||||
<DockPanel>
|
||||
<!-- Top tools -->
|
||||
<WrapPanel DockPanel.Dock="Top" Margin="4">
|
||||
<ListBox
|
||||
x:Name="lstGroup"
|
||||
Margin="4,0"
|
||||
DisplayMemberBinding="{Binding Remarks}"
|
||||
ItemsSource="{Binding SubItems}">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<WrapPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
</ListBox>
|
||||
|
||||
<Button
|
||||
x:Name="btnAutofitColumnWidth"
|
||||
Width="32"
|
||||
Height="32"
|
||||
Margin="8,0"
|
||||
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
||||
<Button.Content>
|
||||
<PathIcon Data="{StaticResource building_fit}" />
|
||||
</Button.Content>
|
||||
</Button>
|
||||
|
||||
<TextBox
|
||||
x:Name="txtServerFilter"
|
||||
Width="200"
|
||||
Margin="8,0"
|
||||
VerticalContentAlignment="Center"
|
||||
Watermark="{x:Static resx:ResUI.MsgServerTitle}"
|
||||
Text="{Binding ServerFilter, Mode=TwoWay}" />
|
||||
</WrapPanel>
|
||||
|
||||
<!-- Profiles grid -->
|
||||
<DataGrid
|
||||
x:Name="lstProfiles"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserReorderColumns="True"
|
||||
CanUserResizeColumns="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="All"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding ProfileItems}">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Width="80" Binding="{Binding ConfigType}" Header="{x:Static resx:ResUI.LvServiceType}" Tag="ConfigType" />
|
||||
|
||||
<DataGridTemplateColumn Tag="Remarks">
|
||||
<DataGridTemplateColumn.Header>
|
||||
<TextBlock Text="{x:Static resx:ResUI.LvRemarks}" />
|
||||
</DataGridTemplateColumn.Header>
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal" Margin="8,0">
|
||||
<TextBlock Text="{Binding Remarks}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
||||
<DataGridTextColumn Width="120" Binding="{Binding Address}" Header="{x:Static resx:ResUI.LvAddress}" Tag="Address" />
|
||||
<DataGridTextColumn Width="60" Binding="{Binding Port}" Header="{x:Static resx:ResUI.LvPort}" Tag="Port" />
|
||||
<DataGridTextColumn Width="100" Binding="{Binding Network}" Header="{x:Static resx:ResUI.LvTransportProtocol}" Tag="Network" />
|
||||
<DataGridTextColumn Width="100" Binding="{Binding StreamSecurity}" Header="{x:Static resx:ResUI.LvTLS}" Tag="StreamSecurity" />
|
||||
<DataGridTextColumn Width="100" Binding="{Binding SubRemarks}" Header="{x:Static resx:ResUI.LvSubscription}" Tag="SubRemarks" />
|
||||
|
||||
<DataGridTemplateColumn SortMemberPath="Delay" Tag="DelayVal">
|
||||
<DataGridTemplateColumn.Header>
|
||||
<TextBlock Text="{x:Static resx:ResUI.LvTestDelay}" />
|
||||
</DataGridTemplateColumn.Header>
|
||||
<DataGridTemplateColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Margin="8,0" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="{Binding Delay, Converter={StaticResource DelayColorConverter}}" Text="{Binding DelayVal}" />
|
||||
</DataTemplate>
|
||||
</DataGridTemplateColumn.CellTemplate>
|
||||
</DataGridTemplateColumn>
|
||||
|
||||
<DataGridTextColumn Width="100" Binding="{Binding SpeedVal}" Header="{x:Static resx:ResUI.LvTestSpeed}" Tag="SpeedVal" />
|
||||
|
||||
<DataGridTextColumn Width="100" Binding="{Binding TodayUp}" Header="{x:Static resx:ResUI.LvTodayUploadDataAmount}" Tag="TodayUp" />
|
||||
<DataGridTextColumn Width="100" Binding="{Binding TodayDown}" Header="{x:Static resx:ResUI.LvTodayDownloadDataAmount}" Tag="TodayDown" />
|
||||
<DataGridTextColumn Width="100" Binding="{Binding TotalUp}" Header="{x:Static resx:ResUI.LvTotalUploadDataAmount}" Tag="TotalUp" />
|
||||
<DataGridTextColumn Width="100" Binding="{Binding TotalDown}" Header="{x:Static resx:ResUI.LvTotalDownloadDataAmount}" Tag="TotalDown" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</Window>
|
|
@ -0,0 +1,141 @@
|
|||
using System.Linq;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using ServiceLib.Manager;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class ProfilesSelectWindow : ReactiveWindow<ProfilesSelectViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
public Task<ProfileItem?> ProfileItem => GetFirstProfileItemAsync();
|
||||
|
||||
public ProfilesSelectWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppManager.Instance.Config;
|
||||
|
||||
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||
txtServerFilter.KeyDown += TxtServerFilter_KeyDown;
|
||||
lstProfiles.KeyDown += LstProfiles_KeyDown;
|
||||
lstProfiles.SelectionChanged += LstProfiles_SelectionChanged;
|
||||
lstProfiles.LoadingRow += LstProfiles_LoadingRow;
|
||||
lstProfiles.Sorting += LstProfiles_Sorting;
|
||||
lstProfiles.DoubleTapped += LstProfiles_DoubleTapped;
|
||||
|
||||
ViewModel = new ProfilesSelectViewModel(UpdateViewHandler);
|
||||
DataContext = ViewModel;
|
||||
|
||||
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.Bind(ViewModel, vm => vm.SelectedSub, v => v.lstGroup.SelectedItem).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
||||
});
|
||||
|
||||
btnSave.Click += (s, e) => ViewModel?.SelectFinish();
|
||||
btnCancel.Click += (s, e) => Close(false);
|
||||
}
|
||||
|
||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case EViewAction.CloseWindow:
|
||||
Close(true);
|
||||
break;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
|
||||
private void LstProfiles_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (ViewModel != null)
|
||||
{
|
||||
ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
|
||||
{
|
||||
e.Row.Header = $" {e.Row.Index + 1}";
|
||||
}
|
||||
|
||||
private void LstProfiles_DoubleTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
ViewModel?.SelectFinish();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void LstProfiles_KeyDown(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
|
||||
{
|
||||
if (e.Key == Key.A)
|
||||
{
|
||||
lstProfiles.SelectAll();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (e.Key is Key.Enter or Key.Return)
|
||||
{
|
||||
ViewModel?.SelectFinish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
AutofitColumnWidth();
|
||||
}
|
||||
|
||||
private void AutofitColumnWidth()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var col in lstProfiles.Columns)
|
||||
{
|
||||
col.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void TxtServerFilter_KeyDown(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key is Key.Enter or Key.Return)
|
||||
{
|
||||
ViewModel?.RefreshServers();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ProfileItem?> GetFirstProfileItemAsync()
|
||||
{
|
||||
var item = await ViewModel?.GetProfileItem();
|
||||
return item;
|
||||
}
|
||||
}
|
|
@ -54,13 +54,22 @@
|
|||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding SelectedSource.OutboundTag, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
|
||||
VerticalAlignment="Center">
|
||||
<Button
|
||||
x:Name="btnSelectProfile"
|
||||
Margin="0,0,8,0"
|
||||
Content="{x:Static resx:ResUI.TbSelectProfile}"
|
||||
Click="BtnSelectProfile_Click" />
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
|
|
|
@ -3,6 +3,7 @@ using Avalonia.Controls;
|
|||
using Avalonia.Interactivity;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
|
@ -93,4 +94,18 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
|
|||
{
|
||||
ProcUtils.ProcessStart("https://xtls.github.io/config/routing.html#ruleobject");
|
||||
}
|
||||
|
||||
private async void BtnSelectProfile_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
var profile = await selectWindow.ProfileItem;
|
||||
if (profile != null)
|
||||
{
|
||||
cmbOutboundTag.Text = profile.Remarks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,6 +204,12 @@
|
|||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
<Button
|
||||
Grid.Row="9"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Content="{x:Static resx:ResUI.TbSelectProfile}"
|
||||
Click="BtnSelectPrevProfile_Click" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="10"
|
||||
|
@ -218,6 +224,12 @@
|
|||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Watermark="{x:Static resx:ResUI.LvPrevProfileTip}" />
|
||||
<Button
|
||||
Grid.Row="10"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Content="{x:Static resx:ResUI.TbSelectProfile}"
|
||||
Click="BtnSelectNextProfile_Click" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="11"
|
||||
|
|
|
@ -3,6 +3,7 @@ using Avalonia;
|
|||
using Avalonia.Interactivity;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
|
@ -59,4 +60,32 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
|||
{
|
||||
txtRemarks.Focus();
|
||||
}
|
||||
|
||||
private async void BtnSelectPrevProfile_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
var profile = await selectWindow.ProfileItem;
|
||||
if (profile != null)
|
||||
{
|
||||
txtPrevProfile.Text = profile.Remarks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void BtnSelectNextProfile_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var selectWindow = new ProfilesSelectWindow();
|
||||
var result = await selectWindow.ShowDialog<bool?>(this);
|
||||
if (result == true)
|
||||
{
|
||||
var profile = await selectWindow.ProfileItem;
|
||||
if (profile != null)
|
||||
{
|
||||
txtNextProfile.Text = profile.Remarks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue