Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions StabilityMatrix.Avalonia/Models/AppArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ public class AppArgs
[Option("vulkan", HelpText = "Prefer Vulkan rendering")]
public bool UseVulkanRendering { get; set; }

/// <summary>
/// Flag to render popups (flyouts, dropdowns, menus) inside the window instead of as
/// separate OS windows, so window captures and UI automation can see them.
/// Popups are clipped to the window bounds in this mode.
/// </summary>
[Option("overlay-popups", HelpText = "Render popups inside the window (for UI automation / capture)")]
public bool UseOverlayPopups { get; set; }

/// <summary>
/// Override global app home directory
/// Defaults to (%APPDATA%|~/.config)/StabilityMatrix
Expand Down
49 changes: 12 additions & 37 deletions StabilityMatrix.Avalonia/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,56 +236,31 @@ public static AppBuilder BuildAvaloniaApp()

var app = AppBuilder.Configure<App>().UsePlatformDetect().WithInterFont().LogToTrace();

if (Compat.IsLinux)
{
app = app.With(new X11PlatformOptions { OverlayPopups = true, WmClass = "stabilitymatrix" });
}
else if (Compat.IsMacOS)
{
app = app.With(new AvaloniaNativePlatformOptions { OverlayPopups = true });
}
// .With<T>() replaces the registered options instance wholesale, so each platform's
// options must be composed into a single object before applying.
var win32Options = new Win32PlatformOptions { OverlayPopups = Args.UseOverlayPopups };
var x11Options = new X11PlatformOptions { OverlayPopups = true, WmClass = "stabilitymatrix" };
var macOptions = new AvaloniaNativePlatformOptions { OverlayPopups = true };

if (Args.UseOpenGlRendering)
{
app = app.With(
new Win32PlatformOptions
{
RenderingMode = [Win32RenderingMode.Wgl, Win32RenderingMode.Software],
}
);
win32Options.RenderingMode = [Win32RenderingMode.Wgl, Win32RenderingMode.Software];
}

if (Args.UseVulkanRendering)
{
app = app.With(
new X11PlatformOptions
{
RenderingMode = [X11RenderingMode.Vulkan],
WmClass = "stabilitymatrix",
}
)
.With(new Win32PlatformOptions { RenderingMode = [Win32RenderingMode.Vulkan] });
win32Options.RenderingMode = [Win32RenderingMode.Vulkan];
x11Options.RenderingMode = [X11RenderingMode.Vulkan];
}

if (Args.DisableGpuRendering)
{
app = app.With(new Win32PlatformOptions { RenderingMode = new[] { Win32RenderingMode.Software } })
.With(
new X11PlatformOptions
{
RenderingMode = new[] { X11RenderingMode.Software },
WmClass = "stabilitymatrix",
}
)
.With(
new AvaloniaNativePlatformOptions
{
RenderingMode = new[] { AvaloniaNativeRenderingMode.Software },
}
);
win32Options.RenderingMode = [Win32RenderingMode.Software];
x11Options.RenderingMode = [X11RenderingMode.Software];
macOptions.RenderingMode = [AvaloniaNativeRenderingMode.Software];
}

return app;
return app.With(win32Options).With(x11Options).With(macOptions);
}

private static void HandleUpdateReplacement()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Foreground" Value="{DynamicResource TextFillColorSecondaryBrush}" />
<Setter Property="ToolTip.Tip" Value="{x:Static lang:Resources.Label_ViewDocumentation}" />
<Setter Property="AutomationProperties.Name" Value="{x:Static lang:Resources.Label_ViewDocumentation}" />

<Setter Property="Template">
<ControlTemplate>
Expand Down
7 changes: 7 additions & 0 deletions StabilityMatrix.Avalonia/Views/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@
<KeyBinding Command="{Binding OpenDocumentationCommand}" Gesture="F1" />
</controls:AppWindowBase.KeyBindings>

<controls:AppWindowBase.Styles>
<!-- FluentAvalonia's template gives this icon-only button no accessible name -->
<Style Selector="ui|NavigationView /template/ Button#TogglePaneButton">
<Setter Property="AutomationProperties.Name" Value="Toggle Navigation" />
</Style>
</controls:AppWindowBase.Styles>

<Grid RowDefinitions="Auto,Auto,Auto,*">
<Grid
Name="TitleBarHost"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@
VerticalAlignment="Top"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Top"
AutomationProperties.Name="More Options"
BorderThickness="0"
Classes="transparent">
Classes="transparent"
ToolTip.Tip="More Options">
<ui:SymbolIcon FontSize="18" Symbol="MoreVertical" />
<Button.Flyout>
<MenuFlyout Placement="BottomEdgeAlignedLeft">
Expand Down Expand Up @@ -347,6 +349,7 @@
<Button
Margin="4,4,4,0"
Padding="6"
AutomationProperties.Name="{x:Static lang:Resources.Action_CheckForUpdates}"
Classes="transparent"
Command="{Binding OnLoadedAsync}"
ToolTip.Tip="{x:Static lang:Resources.Action_CheckForUpdates}">
Expand All @@ -363,6 +366,7 @@
<Button
Margin="4,4,4,0"
Padding="4"
AutomationProperties.Name="{x:Static lang:Resources.Action_Update}"
Classes="borderless-success"
Command="{Binding Update}"
IsEnabled="{Binding !IsRunning}"
Expand Down Expand Up @@ -414,6 +418,7 @@
<Button
Margin="4,4,4,0"
Padding="6"
AutomationProperties.Name="Launch Options"
Classes="transparent"
Command="{Binding ShowLaunchOptionsCommand}"
IsVisible="{Binding !IsUnknownPackage}"
Expand All @@ -423,6 +428,7 @@
<Button
Margin="4,4,4,0"
Padding="6"
AutomationProperties.Name="Extensions"
Classes="transparent"
Command="{Binding OpenExtensionsDialogCommand}"
IsVisible="{Binding CanUseExtensions}"
Expand All @@ -444,6 +450,7 @@
Name="LaunchButton"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static lang:Resources.Action_Launch}"
Classes="accent"
Command="{Binding Launch}">
<Button.IsVisible>
Expand All @@ -465,6 +472,7 @@
Name="SplitLaunchButton"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static lang:Resources.Action_Launch}"
Classes="accent"
Command="{Binding Launch}">
<Button.IsVisible>
Expand Down Expand Up @@ -503,6 +511,7 @@
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static lang:Resources.Action_Stop}"
Classes="borderless-danger"
Command="{Binding StopCommand}"
IsVisible="{Binding IsRunning}">
Expand All @@ -517,6 +526,7 @@
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static lang:Resources.Action_Restart}"
BorderBrush="Transparent"
Classes="borderless-info"
Command="{Binding RestartCommand}"
Expand All @@ -532,6 +542,7 @@
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static lang:Resources.Label_Console}"
Classes="accent"
Command="{Binding NavToConsole}"
IsVisible="{Binding IsRunning}">
Expand All @@ -546,6 +557,7 @@
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static lang:Resources.Label_WebUi}"
Classes="accent"
Command="{Binding LaunchWebUi}"
IsVisible="{Binding ShowWebUiButton}">
Expand All @@ -560,6 +572,7 @@
<Button
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static lang:Resources.Action_Import}"
Classes="transparent-info"
Command="{Binding Import}"
IsVisible="{Binding IsUnknownPackage}">
Expand Down Expand Up @@ -641,6 +654,7 @@
Margin="0,8,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static lang:Resources.Action_AddPackage}"
Classes="transparent"
Command="{Binding ShowInstallDialog}">
<StackPanel Margin="8" Orientation="Horizontal">
Expand Down
47 changes: 47 additions & 0 deletions StabilityMatrix.UITests/AutomationNameTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Avalonia.Automation;
using Avalonia.Controls;
using Avalonia.VisualTree;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Languages;
using StabilityMatrix.Core.Models.Documentation;

namespace StabilityMatrix.UITests;

[Collection("TempDir")]
public class AutomationNameTests : TestBase
{
/// <summary>
/// The pane toggle is named via a /template/ style selector in MainWindow.axaml, which
/// silently matches nothing if FluentAvalonia renames the template part.
/// </summary>
[AvaloniaFact]
public async Task NavigationTogglePaneButton_ShouldHaveAutomationName()
{
var (window, _) = GetMainWindow();
await DoInitialSetup();

var toggleButton = window
.FindDescendantOfType<NavigationView>()!
.GetVisualDescendants()
.OfType<Button>()
.FirstOrDefault(b => b.Name == "TogglePaneButton");

Assert.NotNull(toggleButton);
Assert.Equal("Toggle Navigation", AutomationProperties.GetName(toggleButton));
}

/// <summary>
/// Icon-only control: the accessible name comes from the control theme, not content.
/// </summary>
[AvaloniaFact]
public void DocsHelpButton_ShouldHaveAutomationName()
{
var button = new DocsHelpButton { DocsPath = DocumentationPages.EnvironmentVariables };
var window = new Window { Content = button };

window.Show();

Assert.Equal(Resources.Label_ViewDocumentation, AutomationProperties.GetName(button));
}
}
Loading