Compare commits

...

3 commits

Author SHA1 Message Date
2dust
514dce960a up 7.12.0
Some checks failed
release Linux / build (Release) (push) Has been cancelled
release macOS / build (Release) (push) Has been cancelled
release Windows desktop (Avalonia UI) / build (Release) (push) Has been cancelled
release Windows / build (Release) (push) Has been cancelled
2025-04-28 15:57:09 +08:00
Reza Bakhshi Laktasaraei
6ee6fb1706
Improve Accessibility in StatusBarView and ProfilesView, Update Package Versions (#7199)
* Fix Tab navigation in ToolBar by setting KeyboardNavigation to Continue

* Improve accessibility for ComboBoxes in StatusBarView.xaml

Added ItemContainerStyle to cmbRoutings2 and cmbRoutings to bind AutomationProperties.Name to Remarks, ensuring screen readers announce the correct values instead of the default object type.

* Improve accessibility for cmbServers in StatusBarView.xaml

Added ItemContainerStyle to cmbServers to bind AutomationProperties.Name to Text, ensuring screen readers announce the correct values instead of the default object type.

* Update package versions and fix accessibility in ProfilesView.xaml

- Updated package versions in Directory.Packages.props:
  - Semi.Avalonia and Semi.Avalonia.DataGrid from 11.2.1.6 to 11.2.1.7.
  - ZXing.Net.Bindings.SkiaSharp from 0.16.14 to 0.16.21.
- Fixed MC3024 error in ProfilesView.xaml by creating AccessibleMyChipListBoxItem style:
  - Added AccessibleMyChipListBoxItem style based on MyChipListBoxItem to set AutomationProperties.Name.
  - Replaced ItemContainerStyle with AccessibleMyChipListBoxItem to preserve original appearance.
  - Updated AutomationProperties.Name to use resx:ResUI.menuSubscription for better localization.
  - Removed duplicate AutomationProperties.Name from TextBlock as it's now handled by the style.

* Update Directory.Packages.props

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-04-28 15:25:57 +08:00
2dust
6985328653 Optimize and improve code 2025-04-28 15:16:58 +08:00
7 changed files with 143 additions and 135 deletions

View file

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.11.3</Version>
<Version>7.12.0</Version>
</PropertyGroup>
<PropertyGroup>

View file

@ -27,4 +27,4 @@
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup>
</Project>
</Project>

View file

@ -9,7 +9,6 @@ public sealed class AppHandler
private int? _statePort;
private int? _statePort2;
private Job? _processJob;
private bool? _isAdministrator;
public static AppHandler Instance => _instance.Value;
public Config Config => _config;

View file

@ -10,7 +10,6 @@ public class CoreAdminHandler
public static CoreAdminHandler Instance => _instance.Value;
private Config _config;
private Action<bool, string>? _updateFunc;
private const string _tag = "CoreAdminHandler";
private int _linuxSudoPid = -1;
public async Task Init(Config config, Action<bool, string> updateFunc)
@ -30,69 +29,60 @@ public class CoreAdminHandler
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
{
try
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
Process proc = new()
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
Process proc = new()
StartInfo = new()
{
StartInfo = new()
{
FileName = shFilePath,
Arguments = "",
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
}
};
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
await Task.Delay(100);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
FileName = shFilePath,
Arguments = "",
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
}
};
_linuxSudoPid = proc.Id;
return proc;
}
catch (Exception ex)
proc.OutputDataReceived += (sender, e) =>
{
Logging.SaveLog(_tag, ex);
UpdateFunc(false, ex.Message);
return null;
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
await Task.Delay(100);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
_linuxSudoPid = proc.Id;
return proc;
}
public async Task KillProcessAsLinuxSudo()
@ -102,22 +92,14 @@ public class CoreAdminHandler
return;
}
try
{
var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
var result = await Cli.Wrap(shFilePath)
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
.ExecuteAsync();
await Cli.Wrap(shFilePath)
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
.ExecuteAsync();
_linuxSudoPid = -1;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
UpdateFunc(false, ex.Message);
}
_linuxSudoPid = -1;
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)

View file

@ -238,66 +238,19 @@ public class CoreHandler
return null;
}
if (mayNeedSudo
&& _config.TunModeItem.EnableTun
&& coreInfo.CoreType == ECoreType.sing_box
&& Utils.IsNonWindows())
{
_linuxSudo = true;
await CoreAdminHandler.Instance.Init(_config, _updateFunc);
return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
}
try
{
Process proc = new()
if (mayNeedSudo
&& _config.TunModeItem.EnableTun
&& coreInfo.CoreType == ECoreType.sing_box
&& Utils.IsNonWindows())
{
StartInfo = new()
{
FileName = fileName,
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
CreateNoWindow = true,
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
}
};
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
}
proc.Start();
if (displayLog)
{
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
_linuxSudo = true;
await CoreAdminHandler.Instance.Init(_config, _updateFunc);
return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
}
await Task.Delay(500);
AppHandler.Instance.AddProcess(proc.Handle);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
return proc;
return await RunProcessNormal(fileName, coreInfo, configPath, displayLog);
}
catch (Exception ex)
{
@ -307,5 +260,57 @@ public class CoreHandler
}
}
private async Task<Process?> RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
{
Process proc = new()
{
StartInfo = new()
{
FileName = fileName,
Arguments = string.Format(coreInfo.Arguments, coreInfo.AbsolutePath ? Utils.GetBinConfigPath(configPath).AppendQuotes() : configPath),
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
CreateNoWindow = true,
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
}
};
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
}
proc.Start();
if (displayLog)
{
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
}
await Task.Delay(100);
AppHandler.Instance.AddProcess(proc.Handle);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
return proc;
}
#endregion Process
}

View file

@ -18,6 +18,9 @@
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
<conv:DelayColorConverter x:Key="DelayColorConverter" />
<Style x:Key="AccessibleMyChipListBoxItem" TargetType="ListBoxItem" BasedOn="{StaticResource MyChipListBoxItem}">
<Setter Property="AutomationProperties.Name" Value="{Binding Remarks}" />
</Style>
</UserControl.Resources>
<Grid>
<DockPanel>
@ -26,8 +29,9 @@
x:Name="lstGroup"
MaxHeight="200"
FontSize="{DynamicResource StdFontSize}"
ItemContainerStyle="{StaticResource MyChipListBoxItem}"
Style="{StaticResource MaterialDesignChoiceChipPrimaryOutlineListBox}">
ItemContainerStyle="{StaticResource AccessibleMyChipListBoxItem}"
Style="{StaticResource MaterialDesignChoiceChipPrimaryOutlineListBox}"
AutomationProperties.Name="{x:Static resx:ResUI.menuSubscription}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Remarks}" />

View file

@ -92,7 +92,13 @@
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"
DisplayMemberPath="Remarks"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFloatingHintComboBox}" />
Style="{StaticResource MaterialDesignFloatingHintComboBox}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="AutomationProperties.Name" Value="{Binding Remarks}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</StackPanel>
<StackPanel Margin="{StaticResource MarginLeftRight8}" VerticalAlignment="Center">
@ -184,7 +190,13 @@
AutomationProperties.Name="{x:Static resx:ResUI.menuRouting}"
DisplayMemberPath="Remarks"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilledComboBox}" />
Style="{StaticResource MaterialDesignFilledComboBox}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="AutomationProperties.Name" Value="{Binding Remarks}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</DockPanel>
</MenuItem.Header>
</MenuItem>
@ -198,7 +210,13 @@
AutomationProperties.Name="{x:Static resx:ResUI.menuServers}"
DisplayMemberPath="Text"
FontSize="{DynamicResource StdFontSize}"
Style="{StaticResource MaterialDesignFilledComboBox}" />
Style="{StaticResource MaterialDesignFilledComboBox}">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="AutomationProperties.Name" Value="{Binding Text}" />
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</DockPanel>
</MenuItem.Header>
</MenuItem>