Browse Source

Implemented LabeledBy.

ui-automation-test
Steven Kirk 6 years ago
parent
commit
8b642a9141
  1. 4
      samples/IntegrationTestApp/MainWindow.axaml
  2. 7
      src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
  3. 1
      src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs
  4. 77
      src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
  5. 8
      src/Avalonia.Controls/Automation/Peers/TextBlockAutomationPeer.cs
  6. 7
      src/Avalonia.Controls/Automation/Peers/TextBoxAutomationPeer.cs
  7. 2
      src/Avalonia.Controls/TextBlock.cs
  8. 10
      tests/Avalonia.IntegrationTests.Win32/AutomationTests.cs

4
samples/IntegrationTestApp/MainWindow.axaml

@ -12,6 +12,10 @@
<TextBlock Name="NotTheAutomationId" AutomationProperties.AutomationId="TextBlockWithNameAndAutomationId">
TextBlockWithNameAndAutomationId
</TextBlock>
<TextBlock Name="TextBlockAsLabel">Label for TextBox</TextBlock>
<TextBox Name="LabeledByTextBox" AutomationProperties.LabeledBy="{Binding #TextBlockAsLabel}">
Foo
</TextBox>
</StackPanel>
</TabItem>
<TabItem Header="Button">

7
src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs

@ -101,6 +101,12 @@ namespace Avalonia.Automation.Peers
/// </summary>
public string GetClassName() => GetClassNameCore() ?? string.Empty;
/// <summary>
/// Gets the automation peer for the label that is targeted to the element.
/// </summary>
/// <returns></returns>
public AutomationPeer? GetLabeledBy() => GetLabeledByCore();
/// <summary>
/// Gets a human-readable localized string that represents the type of the control that is
/// associated with this automation peer.
@ -207,6 +213,7 @@ namespace Avalonia.Automation.Peers
protected abstract Rect GetBoundingRectangleCore();
protected abstract IReadOnlyList<AutomationPeer> GetOrCreateChildrenCore();
protected abstract string GetClassNameCore();
protected abstract AutomationPeer? GetLabeledByCore();
protected abstract string? GetNameCore();
protected abstract AutomationPeer? GetParentCore();
protected abstract bool HasKeyboardFocusCore();

1
src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs

@ -94,6 +94,7 @@ namespace Avalonia.Automation.Peers
protected override string? GetAutomationIdCore() => null;
protected override string GetClassNameCore() => typeof(ComboBoxItem).Name;
protected override AutomationPeer? GetLabeledByCore() => null;
protected override AutomationPeer? GetParentCore() => _owner;
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.ListItem;

77
src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs

@ -43,8 +43,24 @@ namespace Avalonia.Automation.Peers
}
protected override void BringIntoViewCore() => Owner.BringIntoView();
protected override string? GetAutomationIdCore() => AutomationProperties.GetAutomationId(Owner) ?? Owner.Name;
protected override Rect GetBoundingRectangleCore() => GetBounds(Owner.TransformedBounds);
protected override IReadOnlyList<AutomationPeer> GetOrCreateChildrenCore()
{
var children = _children ?? Array.Empty<AutomationPeer>();
if (_childrenValid)
return children;
var newChildren = GetChildrenCore() ?? Array.Empty<AutomationPeer>();
foreach (var peer in children.Except(newChildren))
peer.TrySetParent(null);
foreach (var peer in newChildren)
peer.TrySetParent(this);
_childrenValid = true;
return _children = newChildren;
}
protected virtual IReadOnlyList<AutomationPeer>? GetChildrenCore()
{
@ -66,6 +82,24 @@ namespace Avalonia.Automation.Peers
return result;
}
protected override AutomationPeer? GetLabeledByCore()
{
var label = AutomationProperties.GetLabeledBy(Owner);
return label is Control c ? GetOrCreatePeer(c) : null;
}
protected override string? GetNameCore()
{
var result = AutomationProperties.GetName(Owner);
if (string.IsNullOrWhiteSpace(result) && GetLabeledBy() is AutomationPeer labeledBy)
{
return labeledBy.GetName();
}
return null;
}
protected override AutomationPeer? GetParentCore()
{
EnsureConnected();
@ -90,34 +124,6 @@ namespace Avalonia.Automation.Peers
_parentValid = false;
}
protected override IReadOnlyList<AutomationPeer> GetOrCreateChildrenCore()
{
var children = _children ?? Array.Empty<AutomationPeer>();
if (_childrenValid)
return children;
var newChildren = GetChildrenCore() ?? Array.Empty<AutomationPeer>();
foreach (var peer in children.Except(newChildren))
peer.TrySetParent(null);
foreach (var peer in newChildren)
peer.TrySetParent(this);
_childrenValid = true;
return _children = newChildren;
}
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Custom;
protected override string GetClassNameCore() => Owner.GetType().Name;
protected override string? GetNameCore() => AutomationProperties.GetName(Owner);
protected override bool HasKeyboardFocusCore() => Owner.IsFocused;
protected override bool IsContentElementCore() => true;
protected override bool IsControlElementCore() => true;
protected override bool IsEnabledCore() => Owner.IsEnabled;
protected override bool IsKeyboardFocusableCore() => Owner.Focusable;
protected override void SetFocusCore() => Owner.Focus();
protected override bool ShowContextMenuCore()
{
var c = Owner;
@ -142,6 +148,17 @@ namespace Avalonia.Automation.Peers
return true;
}
protected override string? GetAutomationIdCore() => AutomationProperties.GetAutomationId(Owner) ?? Owner.Name;
protected override Rect GetBoundingRectangleCore() => GetBounds(Owner.TransformedBounds);
protected override AutomationControlType GetAutomationControlTypeCore() => AutomationControlType.Custom;
protected override string GetClassNameCore() => Owner.GetType().Name;
protected override bool HasKeyboardFocusCore() => Owner.IsFocused;
protected override bool IsContentElementCore() => true;
protected override bool IsControlElementCore() => true;
protected override bool IsEnabledCore() => Owner.IsEnabled;
protected override bool IsKeyboardFocusableCore() => Owner.Focusable;
protected override void SetFocusCore() => Owner.Focus();
private Rect GetBounds(TransformedBounds? bounds)
{
return bounds?.Bounds.TransformToAABB(bounds!.Value.Transform) ?? default;

8
src/Avalonia.Controls/Automation/Peers/TextAutomationPeer.cs → src/Avalonia.Controls/Automation/Peers/TextBlockAutomationPeer.cs

@ -5,19 +5,21 @@ using Avalonia.Controls;
namespace Avalonia.Automation.Peers
{
public class TextAutomationPeer : ControlAutomationPeer
public class TextBlockAutomationPeer : ControlAutomationPeer
{
public TextAutomationPeer(IAutomationNodeFactory factory, Control owner)
public TextBlockAutomationPeer(IAutomationNodeFactory factory, TextBlock owner)
: base(factory, owner)
{
}
public new TextBlock Owner => (TextBlock)base.Owner;
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Text;
}
protected override string? GetNameCore() => Owner.GetValue(TextBlock.TextProperty);
protected override string? GetNameCore() => Owner.Text;
protected override bool IsControlElementCore()
{

7
src/Avalonia.Controls/Automation/Peers/TextBoxAutomationPeer.cs

@ -6,7 +6,7 @@ using Avalonia.Controls;
namespace Avalonia.Automation.Peers
{
public class TextBoxAutomationPeer : TextAutomationPeer, IValueProvider
public class TextBoxAutomationPeer : ControlAutomationPeer, IValueProvider
{
public TextBoxAutomationPeer(IAutomationNodeFactory factory, TextBox owner)
: base(factory, owner)
@ -17,5 +17,10 @@ namespace Avalonia.Automation.Peers
public bool IsReadOnly => Owner.IsReadOnly;
public string? Value => Owner.Text;
public void SetValue(string? value) => Owner.Text = value;
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Edit;
}
}
}

2
src/Avalonia.Controls/TextBlock.cs

@ -536,7 +536,7 @@ namespace Avalonia.Controls
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
{
return new TextAutomationPeer(factory, this);
return new TextBlockAutomationPeer(factory, this);
}
private static bool IsValidMaxLines(int maxLines) => maxLines >= 0;

10
tests/Avalonia.IntegrationTests.Win32/AutomationTests.cs

@ -25,5 +25,15 @@ namespace Avalonia.IntegrationTests.Win32
var byName = _session.FindElementByAccessibilityId("TextBlockWithName");
var byAutomationId = _session.FindElementByAccessibilityId("TextBlockWithNameAndAutomationId");
}
[Fact]
public void LabeledBy()
{
var label = _session.FindElementByAccessibilityId("TextBlockAsLabel");
var labeledTextBox = _session.FindElementByAccessibilityId("LabeledByTextBox");
Assert.Equal("Label for TextBox", label.Text);
Assert.Equal("Label for TextBox", labeledTextBox.GetAttribute("Name"));
}
}
}

Loading…
Cancel
Save