Browse Source

Decouple automation peers from platform nodes.

Now peers are entirely unaware of the platform implementation.
ui-automation-test
Steven Kirk 4 years ago
parent
commit
11c60b4294
  1. 2
      samples/ControlCatalog.Android/Resources/Resource.Designer.cs
  2. 2
      src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs
  3. 23
      src/Avalonia.Controls/Automation/AutomationPropertyChangedEventArgs.cs
  4. 37
      src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
  5. 5
      src/Avalonia.Controls/Automation/Peers/ButtonAutomationPeer.cs
  6. 7
      src/Avalonia.Controls/Automation/Peers/CheckBoxAutomationPeer.cs
  7. 10
      src/Avalonia.Controls/Automation/Peers/ComboBoxAutomationPeer.cs
  8. 5
      src/Avalonia.Controls/Automation/Peers/ComboBoxItemAutomationPeer.cs
  9. 7
      src/Avalonia.Controls/Automation/Peers/ContentControlAutomationPeer.cs
  10. 7
      src/Avalonia.Controls/Automation/Peers/ContextMenuAutomationPeer.cs
  11. 34
      src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
  12. 7
      src/Avalonia.Controls/Automation/Peers/ImageAutomationPeer.cs
  13. 9
      src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs
  14. 7
      src/Avalonia.Controls/Automation/Peers/ListItemAutomationPeer.cs
  15. 7
      src/Avalonia.Controls/Automation/Peers/MenuAutomationPeer.cs
  16. 7
      src/Avalonia.Controls/Automation/Peers/MenuItemAutomationPeer.cs
  17. 5
      src/Avalonia.Controls/Automation/Peers/NoneAutomationPeer.cs
  18. 9
      src/Avalonia.Controls/Automation/Peers/PopupAutomationPeer.cs
  19. 5
      src/Avalonia.Controls/Automation/Peers/PopupRootAutomationPeer.cs
  20. 5
      src/Avalonia.Controls/Automation/Peers/RangeBaseAutomationPeer.cs
  21. 5
      src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs
  22. 11
      src/Avalonia.Controls/Automation/Peers/SelectingItemsControlAutomationPeer.cs
  23. 5
      src/Avalonia.Controls/Automation/Peers/SliderAutomationPeer.cs
  24. 7
      src/Avalonia.Controls/Automation/Peers/TabControlAutomationPeer.cs
  25. 7
      src/Avalonia.Controls/Automation/Peers/TabItemAutomationPeer.cs
  26. 7
      src/Avalonia.Controls/Automation/Peers/TextBlockAutomationPeer.cs
  27. 7
      src/Avalonia.Controls/Automation/Peers/TextBoxAutomationPeer.cs
  28. 7
      src/Avalonia.Controls/Automation/Peers/ToggleButtonAutomationPeer.cs
  29. 6
      src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs
  30. 5
      src/Avalonia.Controls/Automation/Peers/WindowAutomationPeer.cs
  31. 18
      src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs
  32. 32
      src/Avalonia.Controls/Automation/Platform/IAutomationNode.cs
  33. 18
      src/Avalonia.Controls/Automation/Platform/IAutomationNodeFactory.cs
  34. 20
      src/Avalonia.Controls/Automation/Platform/IRootAutomationNode.cs
  35. 4
      src/Avalonia.Controls/Automation/Provider/IRootProvider.cs
  36. 6
      src/Avalonia.Controls/Button.cs
  37. 5
      src/Avalonia.Controls/CheckBox.cs
  38. 5
      src/Avalonia.Controls/ComboBox.cs
  39. 5
      src/Avalonia.Controls/ComboBoxItem.cs
  40. 5
      src/Avalonia.Controls/ContextMenu.cs
  41. 16
      src/Avalonia.Controls/Control.cs
  42. 5
      src/Avalonia.Controls/Image.cs
  43. 5
      src/Avalonia.Controls/ItemsControl.cs
  44. 5
      src/Avalonia.Controls/ListBoxItem.cs
  45. 5
      src/Avalonia.Controls/Menu.cs
  46. 5
      src/Avalonia.Controls/MenuItem.cs
  47. 6
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  48. 5
      src/Avalonia.Controls/Primitives/AccessText.cs
  49. 5
      src/Avalonia.Controls/Primitives/Popup.cs
  50. 5
      src/Avalonia.Controls/Primitives/PopupRoot.cs
  51. 5
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  52. 5
      src/Avalonia.Controls/ScrollViewer.cs
  53. 5
      src/Avalonia.Controls/Slider.cs
  54. 5
      src/Avalonia.Controls/TabControl.cs
  55. 5
      src/Avalonia.Controls/TabItem.cs
  56. 5
      src/Avalonia.Controls/TextBlock.cs
  57. 5
      src/Avalonia.Controls/TextBox.cs
  58. 2
      src/Avalonia.Controls/TopLevel.cs
  59. 5
      src/Avalonia.Controls/Window.cs
  60. 20
      src/Avalonia.Controls/WindowBase.cs
  61. 4
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  62. 2
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  63. 2
      src/Avalonia.Headless/HeadlessWindowImpl.cs
  64. 7
      src/Avalonia.Native/AutomationNode.cs
  65. 24
      src/Avalonia.Native/AutomationNodeFactory.cs
  66. 3
      src/Avalonia.Native/AvnAutomationPeer.cs
  67. 38
      src/Avalonia.Native/WindowImplBase.cs
  68. 1
      src/Avalonia.Native/avn.idl
  69. 2
      src/Avalonia.X11/X11Window.cs
  70. 4
      src/Windows/Avalonia.Win32/Automation/AutomationNode.Selection.cs
  71. 39
      src/Windows/Avalonia.Win32/Automation/AutomationNode.cs
  72. 19
      src/Windows/Avalonia.Win32/Automation/AutomationNodeFactory.cs
  73. 18
      src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs
  74. 13
      src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs
  75. 2
      src/Windows/Avalonia.Win32/WindowImpl.cs
  76. 506
      tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs

2
samples/ControlCatalog.Android/Resources/Resource.Designer.cs

@ -14,7 +14,7 @@ namespace ControlCatalog.Android
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.0.99.19")]
public partial class Resource
{

2
src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs

@ -14,7 +14,7 @@ namespace Avalonia.AndroidTestApplication
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.0.99.19")]
public partial class Resource
{

23
src/Avalonia.Controls/Automation/AutomationPropertyChangedEventArgs.cs

@ -0,0 +1,23 @@
using System;
#nullable enable
namespace Avalonia.Automation
{
public class AutomationPropertyChangedEventArgs : EventArgs
{
public AutomationPropertyChangedEventArgs(
AutomationProperty property,
object? oldValue,
object? newValue)
{
Property = property;
OldValue = oldValue;
NewValue = newValue;
}
public AutomationProperty Property { get; }
public object? OldValue { get; }
public object? NewValue { get; }
}
}

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

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Avalonia.Automation.Platform;
#nullable enable
@ -56,33 +54,6 @@ namespace Avalonia.Automation.Peers
/// </summary>
public abstract class AutomationPeer
{
/// <summary>
/// Initializes a new instance of the <see cref="AutomationPeer"/> class.
/// </summary>
/// <param name="factory">
/// The factory to use to create the platform automation node.
/// </param>
protected AutomationPeer(IAutomationNodeFactory factory)
{
Node = factory.CreateNode(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="AutomationPeer"/> class.
/// </summary>
/// <param name="node">
/// The platform automation node.
/// </param>
protected AutomationPeer(IAutomationNode node)
{
Node = node;
}
/// <summary>
/// Gets the related node in the platform UI Automation tree.
/// </summary>
public IAutomationNode Node { get; }
/// <summary>
/// Attempts to bring the element associated with the automation peer into view.
/// </summary>
@ -188,18 +159,20 @@ namespace Avalonia.Automation.Peers
/// <returns>true if a context menu is present for the element; otherwise false.</returns>
public bool ShowContextMenu() => ShowContextMenuCore();
public event EventHandler<AutomationPropertyChangedEventArgs>? PropertyChanged;
/// <summary>
/// Raises an event to notify the automation client of a changed property value.
/// </summary>
/// <param name="automationProperty">The property that changed.</param>
/// <param name="property">The property that changed.</param>
/// <param name="oldValue">The previous value of the property.</param>
/// <param name="newValue">The new value of the property.</param>
public void RaisePropertyChangedEvent(
AutomationProperty automationProperty,
AutomationProperty property,
object? oldValue,
object? newValue)
{
Node.PropertyChanged(automationProperty, oldValue, newValue);
PropertyChanged?.Invoke(this, new AutomationPropertyChangedEventArgs(property, oldValue, newValue));
}
protected virtual string GetLocalizedControlTypeCore()

5
src/Avalonia.Controls/Automation/Peers/ButtonAutomationPeer.cs

@ -1,4 +1,3 @@
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
@ -9,8 +8,8 @@ namespace Avalonia.Automation.Peers
public class ButtonAutomationPeer : ContentControlAutomationPeer,
IInvokeProvider
{
public ButtonAutomationPeer(IAutomationNodeFactory factory, Button owner)
: base(factory, owner)
public ButtonAutomationPeer(Button owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
#nullable enable
@ -7,8 +6,8 @@ namespace Avalonia.Automation.Peers
{
public class CheckBoxAutomationPeer : ToggleButtonAutomationPeer
{
public CheckBoxAutomationPeer(IAutomationNodeFactory factory, CheckBox owner)
: base(factory, owner)
public CheckBoxAutomationPeer(CheckBox owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using System.Collections.Generic;
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
@ -12,8 +11,8 @@ namespace Avalonia.Automation.Peers
{
private UnrealizedSelectionPeer[]? _selection;
public ComboBoxAutomationPeer(IAutomationNodeFactory factory, ComboBox owner)
: base(factory, owner)
public ComboBoxAutomationPeer(ComboBox owner)
: base(owner)
{
}
@ -39,7 +38,7 @@ namespace Avalonia.Automation.Peers
// peer to represent the unrealized item.
if (Owner.SelectedItem is object selection)
{
_selection ??= new[] { new UnrealizedSelectionPeer(Node.Factory, this) };
_selection ??= new[] { new UnrealizedSelectionPeer(this) };
_selection[0].Item = selection;
return _selection;
}
@ -70,8 +69,7 @@ namespace Avalonia.Automation.Peers
private readonly ComboBoxAutomationPeer _owner;
private object? _item;
public UnrealizedSelectionPeer(IAutomationNodeFactory factory, ComboBoxAutomationPeer owner)
: base(factory)
public UnrealizedSelectionPeer(ComboBoxAutomationPeer owner)
{
_owner = owner;
}

5
src/Avalonia.Controls/Automation/Peers/ComboBoxItemAutomationPeer.cs

@ -1,5 +1,4 @@
using System;
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
@ -11,8 +10,8 @@ namespace Avalonia.Automation.Peers
{
public class ComboBoxItemAutomationPeer : ListItemAutomationPeer
{
public ComboBoxItemAutomationPeer(IAutomationNodeFactory factory, ComboBoxItem owner)
: base(factory, owner)
public ComboBoxItemAutomationPeer(ComboBoxItem owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
#nullable enable
@ -7,8 +6,8 @@ namespace Avalonia.Automation.Peers
{
public class ContentControlAutomationPeer : ControlAutomationPeer
{
protected ContentControlAutomationPeer(IAutomationNodeFactory factory, ContentControl owner)
: base(factory, owner)
protected ContentControlAutomationPeer(ContentControl owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
#nullable enable
@ -7,8 +6,8 @@ namespace Avalonia.Automation.Peers
{
public class ContextMenuAutomationPeer : ControlAutomationPeer
{
public ContextMenuAutomationPeer(IAutomationNodeFactory factory, ContextMenu owner)
: base(factory, owner)
public ContextMenuAutomationPeer(ContextMenu owner)
: base(owner)
{
}

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

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.VisualTree;
@ -19,15 +18,7 @@ namespace Avalonia.Automation.Peers
private AutomationPeer? _parent;
private bool _parentValid;
public ControlAutomationPeer(IAutomationNodeFactory factory, Control owner)
: base(factory)
{
Owner = owner ?? throw new ArgumentNullException("owner");
Initialize();
}
protected ControlAutomationPeer(IAutomationNode node, Control owner)
: base(node)
public ControlAutomationPeer(Control owner)
{
Owner = owner ?? throw new ArgumentNullException("owner");
Initialize();
@ -35,15 +26,18 @@ namespace Avalonia.Automation.Peers
public Control Owner { get; }
public static AutomationPeer GetOrCreatePeer(IAutomationNodeFactory factory, Control element)
public event EventHandler? ChildrenChanged;
public AutomationPeer GetOrCreate(Control element)
{
element = element ?? throw new ArgumentNullException("element");
return element.GetOrCreateAutomationPeer(factory);
if (element == Owner)
return this;
return CreatePeerForElement(element);
}
public AutomationPeer GetOrCreatePeer(Control element)
public static AutomationPeer CreatePeerForElement(Control element)
{
return element == Owner ? this : GetOrCreatePeer(Node.Factory, element);
return element.GetOrCreateAutomationPeer();
}
protected override void BringIntoViewCore() => Owner.BringIntoView();
@ -79,7 +73,7 @@ namespace Avalonia.Automation.Peers
{
if (child is Control c && c.IsVisible)
{
result.Add(GetOrCreatePeer(c));
result.Add(GetOrCreate(c));
}
}
@ -89,7 +83,7 @@ namespace Avalonia.Automation.Peers
protected override AutomationPeer? GetLabeledByCore()
{
var label = AutomationProperties.GetLabeledBy(Owner);
return label is Control c ? GetOrCreatePeer(c) : null;
return label is Control c ? GetOrCreate(c) : null;
}
protected override string? GetNameCore()
@ -116,7 +110,7 @@ namespace Avalonia.Automation.Peers
protected void InvalidateChildren()
{
_childrenValid = false;
Node!.ChildrenChanged();
ChildrenChanged?.Invoke(this, EventArgs.Empty);
}
/// <summary>
@ -185,7 +179,7 @@ namespace Avalonia.Automation.Peers
{
var parent = Owner.GetVisualParent();
if (parent is Control c)
(GetOrCreatePeer(c) as ControlAutomationPeer)?.InvalidateChildren();
(GetOrCreate(c) as ControlAutomationPeer)?.InvalidateChildren();
}
else if (e.Property == Visual.TransformedBoundsProperty)
{
@ -211,7 +205,7 @@ namespace Avalonia.Automation.Peers
{
if (parent is Control c)
{
var parentPeer = GetOrCreatePeer(c);
var parentPeer = GetOrCreate(c);
parentPeer.GetChildren();
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
#nullable enable
@ -7,8 +6,8 @@ namespace Avalonia.Automation.Peers
{
public class ImageAutomationPeer : ControlAutomationPeer
{
public ImageAutomationPeer(IAutomationNodeFactory factory, Control owner)
: base(factory, owner)
public ImageAutomationPeer(Control owner)
: base(owner)
{
}

9
src/Avalonia.Controls/Automation/Peers/ItemsControlAutomationPeer.cs

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
#nullable enable
@ -11,8 +10,8 @@ namespace Avalonia.Automation.Peers
private bool _searchedForScrollable;
private IScrollProvider? _scroller;
public ItemsControlAutomationPeer(IAutomationNodeFactory factory, ItemsControl owner)
: base(factory, owner)
public ItemsControlAutomationPeer(ItemsControl owner)
: base(owner)
{
}
@ -31,7 +30,7 @@ namespace Avalonia.Automation.Peers
if (!_searchedForScrollable)
{
if (Owner.GetValue(ListBox.ScrollProperty) is Control scrollable)
_scroller = GetOrCreatePeer(scrollable) as IScrollProvider;
_scroller = GetOrCreate(scrollable) as IScrollProvider;
_searchedForScrollable = true;
}

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

@ -1,5 +1,4 @@
using System;
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
@ -12,8 +11,8 @@ namespace Avalonia.Automation.Peers
public class ListItemAutomationPeer : ContentControlAutomationPeer,
ISelectionItemProvider
{
public ListItemAutomationPeer(IAutomationNodeFactory factory, ContentControl owner)
: base(factory, owner)
public ListItemAutomationPeer(ContentControl owner)
: base(owner)
{
}
@ -25,7 +24,7 @@ namespace Avalonia.Automation.Peers
{
if (Owner.Parent is Control parent)
{
var parentPeer = GetOrCreatePeer(parent);
var parentPeer = GetOrCreate(parent);
return parentPeer as ISelectionProvider;
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
#nullable enable
@ -7,8 +6,8 @@ namespace Avalonia.Automation.Peers
{
public class MenuAutomationPeer : ControlAutomationPeer
{
public MenuAutomationPeer(IAutomationNodeFactory factory, Menu owner)
: base(factory, owner)
public MenuAutomationPeer(Menu owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
#nullable enable
@ -8,8 +7,8 @@ namespace Avalonia.Automation.Peers
{
public class MenuItemAutomationPeer : ControlAutomationPeer
{
public MenuItemAutomationPeer(IAutomationNodeFactory factory, MenuItem owner)
: base(factory, owner)
public MenuItemAutomationPeer(MenuItem owner)
: base(owner)
{
}

5
src/Avalonia.Controls/Automation/Peers/NoneAutomationPeer.cs

@ -1,4 +1,3 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
#nullable enable
@ -11,8 +10,8 @@ namespace Avalonia.Automation.Peers
/// </summary>
public class NoneAutomationPeer : ControlAutomationPeer
{
public NoneAutomationPeer(IAutomationNodeFactory factory, Control owner)
: base(factory, owner)
public NoneAutomationPeer(Control owner)
: base(owner)
{
}

9
src/Avalonia.Controls/Automation/Peers/PopupAutomationPeer.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.VisualTree;
@ -11,8 +10,8 @@ namespace Avalonia.Automation.Peers
{
public class PopupAutomationPeer : ControlAutomationPeer
{
public PopupAutomationPeer(IAutomationNodeFactory factory, Popup owner)
: base(factory, owner)
public PopupAutomationPeer(Popup owner)
: base(owner)
{
owner.Opened += PopupOpenedClosed;
owner.Closed += PopupOpenedClosed;
@ -22,7 +21,7 @@ namespace Avalonia.Automation.Peers
{
var host = (IVisualTreeHost)Owner;
System.Diagnostics.Debug.WriteLine($"Popup children='{host}'");
return host.Root is Control c ? new[] { GetOrCreatePeer(c) } : null;
return host.Root is Control c ? new[] { GetOrCreate(c) } : null;
}
protected override bool IsContentElementCore() => false;
@ -46,7 +45,7 @@ namespace Avalonia.Automation.Peers
private AutomationPeer? GetPopupRoot()
{
var popupRoot = ((IVisualTreeHost)Owner).Root as Control;
return popupRoot is object ? GetOrCreatePeer(popupRoot) : null;
return popupRoot is object ? GetOrCreate(popupRoot) : null;
}
}
}

5
src/Avalonia.Controls/Automation/Peers/PopupRootAutomationPeer.cs

@ -1,5 +1,4 @@
using System;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Primitives;
#nullable enable
@ -8,8 +7,8 @@ namespace Avalonia.Automation.Peers
{
public class PopupRootAutomationPeer : WindowBaseAutomationPeer
{
public PopupRootAutomationPeer(IAutomationNode node, PopupRoot owner)
: base(node, owner)
public PopupRootAutomationPeer(PopupRoot owner)
: base(owner)
{
if (owner.IsVisible)
StartTrackingFocus();

5
src/Avalonia.Controls/Automation/Peers/RangeBaseAutomationPeer.cs

@ -1,4 +1,3 @@
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls.Primitives;
@ -8,8 +7,8 @@ namespace Avalonia.Automation.Peers
{
public abstract class RangeBaseAutomationPeer : ControlAutomationPeer, IRangeValueProvider
{
public RangeBaseAutomationPeer(IAutomationNodeFactory factory, RangeBase owner)
: base(factory, owner)
public RangeBaseAutomationPeer(RangeBase owner)
: base(owner)
{
owner.PropertyChanged += OwnerPropertyChanged;
}

5
src/Avalonia.Controls/Automation/Peers/ScrollViewerAutomationPeer.cs

@ -1,5 +1,4 @@
using System;
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
using Avalonia.Utilities;
@ -10,8 +9,8 @@ namespace Avalonia.Automation.Peers
{
public class ScrollViewerAutomationPeer : ControlAutomationPeer, IScrollProvider
{
public ScrollViewerAutomationPeer(IAutomationNodeFactory factory, ScrollViewer owner)
: base(factory, owner)
public ScrollViewerAutomationPeer(ScrollViewer owner)
: base(owner)
{
}

11
src/Avalonia.Controls/Automation/Peers/SelectingItemsControlAutomationPeer.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
@ -16,16 +15,16 @@ namespace Avalonia.Automation.Peers
{
private ISelectionModel _selection;
protected SelectingItemsControlAutomationPeer(IAutomationNodeFactory factory, SelectingItemsControl owner)
: base(factory, owner)
protected SelectingItemsControlAutomationPeer(SelectingItemsControl owner)
: base(owner)
{
_selection = owner.GetValue(ListBox.SelectionProperty);
_selection.SelectionChanged += OwnerSelectionChanged;
owner.PropertyChanged += OwnerPropertyChanged;
}
public bool CanSelectMultiple => GetSelectionModeCore().HasFlagCustom(SelectionMode.Multiple);
public bool IsSelectionRequired => GetSelectionModeCore().HasFlagCustom(SelectionMode.AlwaysSelected);
public bool CanSelectMultiple => GetSelectionModeCore().HasAllFlags(SelectionMode.Multiple);
public bool IsSelectionRequired => GetSelectionModeCore().HasAllFlags(SelectionMode.AlwaysSelected);
public IReadOnlyList<AutomationPeer> GetSelection() => GetSelectionCore() ?? Array.Empty<AutomationPeer>();
protected virtual IReadOnlyList<AutomationPeer>? GetSelectionCore()
@ -42,7 +41,7 @@ namespace Avalonia.Automation.Peers
if (container is Control c && ((IVisual)c).IsAttachedToVisualTree)
{
var peer = GetOrCreatePeer(c);
var peer = GetOrCreate(c);
if (peer is object)
{

5
src/Avalonia.Controls/Automation/Peers/SliderAutomationPeer.cs

@ -1,4 +1,3 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
#nullable enable
@ -7,8 +6,8 @@ namespace Avalonia.Automation.Peers
{
public class SliderAutomationPeer : RangeBaseAutomationPeer
{
public SliderAutomationPeer(IAutomationNodeFactory factory, Slider owner)
: base(factory, owner)
public SliderAutomationPeer(Slider owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
#nullable enable
@ -7,8 +6,8 @@ namespace Avalonia.Automation.Peers
{
public class TabControlAutomationPeer : SelectingItemsControlAutomationPeer
{
public TabControlAutomationPeer(IAutomationNodeFactory factory, TabControl owner)
: base(factory, owner)
public TabControlAutomationPeer(TabControl owner)
: base(owner)
{
}

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

@ -1,12 +1,11 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
namespace Avalonia.Automation.Peers
{
public class TabItemAutomationPeer : ListItemAutomationPeer
{
public TabItemAutomationPeer(IAutomationNodeFactory factory, TabItem owner)
: base(factory, owner)
public TabItemAutomationPeer(TabItem owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls;
#nullable enable
@ -7,8 +6,8 @@ namespace Avalonia.Automation.Peers
{
public class TextBlockAutomationPeer : ControlAutomationPeer
{
public TextBlockAutomationPeer(IAutomationNodeFactory factory, TextBlock owner)
: base(factory, owner)
public TextBlockAutomationPeer(TextBlock owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
#nullable enable
@ -8,8 +7,8 @@ namespace Avalonia.Automation.Peers
{
public class TextBoxAutomationPeer : ControlAutomationPeer, IValueProvider
{
public TextBoxAutomationPeer(IAutomationNodeFactory factory, TextBox owner)
: base(factory, owner)
public TextBoxAutomationPeer(TextBox owner)
: base(owner)
{
}

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

@ -1,5 +1,4 @@
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Automation.Provider;
using Avalonia.Controls.Primitives;
#nullable enable
@ -8,8 +7,8 @@ namespace Avalonia.Automation.Peers
{
public class ToggleButtonAutomationPeer : ContentControlAutomationPeer, IToggleProvider
{
public ToggleButtonAutomationPeer(IAutomationNodeFactory factory, ToggleButton owner)
: base(factory, owner)
public ToggleButtonAutomationPeer(ToggleButton owner)
: base(owner)
{
}

6
src/Avalonia.Controls/Automation/Peers/UnrealizedElementAutomationPeer.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using Avalonia.Automation.Platform;
#nullable enable
@ -11,11 +10,6 @@ namespace Avalonia.Automation.Peers
/// </summary>
public abstract class UnrealizedElementAutomationPeer : AutomationPeer
{
protected UnrealizedElementAutomationPeer(IAutomationNodeFactory factory)
: base(factory)
{
}
public void SetParent(AutomationPeer? parent) => TrySetParent(parent);
protected override void BringIntoViewCore() => GetParent()?.BringIntoView();
protected override Rect GetBoundingRectangleCore() => GetParent()?.GetBoundingRectangle() ?? default;

5
src/Avalonia.Controls/Automation/Peers/WindowAutomationPeer.cs

@ -1,5 +1,4 @@
using System;
using Avalonia.Automation.Platform;
using Avalonia.Controls;
#nullable enable
@ -8,8 +7,8 @@ namespace Avalonia.Automation.Peers
{
public class WindowAutomationPeer : WindowBaseAutomationPeer
{
public WindowAutomationPeer(IAutomationNode node, Window owner)
: base(node, owner)
public WindowAutomationPeer(Window owner)
: base(owner)
{
if (owner.IsVisible)
StartTrackingFocus();

18
src/Avalonia.Controls/Automation/Peers/WindowBaseAutomationPeer.cs

@ -1,5 +1,5 @@
using System;
using System.ComponentModel;
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls;
using Avalonia.Input;
@ -14,25 +14,27 @@ namespace Avalonia.Automation.Peers
{
private Control? _focus;
public WindowBaseAutomationPeer(IAutomationNode node, WindowBase owner)
: base(node, owner)
public WindowBaseAutomationPeer(WindowBase owner)
: base(owner)
{
}
public new WindowBase Owner => (WindowBase)base.Owner;
public ITopLevelImpl PlatformImpl => Owner.PlatformImpl;
public event EventHandler? FocusChanged;
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Window;
}
public AutomationPeer? GetFocus() => _focus is object ? GetOrCreatePeer(_focus) : null;
public AutomationPeer? GetFocus() => _focus is object ? GetOrCreate(_focus) : null;
public AutomationPeer? GetPeerFromPoint(Point p)
{
var hit = Owner.GetVisualAt(p)?.FindAncestorOfType<Control>(includeSelf: true);
return hit is object ? GetOrCreatePeer(hit) : null;
return hit is object ? GetOrCreate(hit) : null;
}
protected void StartTrackingFocus()
@ -54,8 +56,10 @@ namespace Avalonia.Automation.Peers
if (_focus != oldFocus)
{
var peer = _focus is object ? GetOrCreatePeer(_focus) : null;
((IRootAutomationNode)Node).FocusChanged(peer);
var peer = _focus is object ?
_focus == Owner ? this :
GetOrCreate(_focus) : null;
FocusChanged?.Invoke(this, EventArgs.Empty);
}
}

32
src/Avalonia.Controls/Automation/Platform/IAutomationNode.cs

@ -1,32 +0,0 @@
using System;
using Avalonia.Automation.Peers;
#nullable enable
namespace Avalonia.Automation.Platform
{
/// <summary>
/// Represents a platform implementation of a node in the UI Automation tree.
/// </summary>
public interface IAutomationNode
{
/// <summary>
/// Gets a factory which can be used to create child nodes.
/// </summary>
IAutomationNodeFactory Factory { get; }
/// <summary>
/// Called by the <see cref="AutomationPeer"/> when the children of the peer change.
/// </summary>
void ChildrenChanged();
/// <summary>
/// Called by the <see cref="AutomationPeer"/> when a property other than the parent,
/// children or root changes.
/// </summary>
/// <param name="property">The property that changed.</param>
/// <param name="oldValue">The previous value of the property.</param>
/// <param name="newValue">The new value of the property.</param>
void PropertyChanged(AutomationProperty property, object? oldValue, object? newValue);
}
}

18
src/Avalonia.Controls/Automation/Platform/IAutomationNodeFactory.cs

@ -1,18 +0,0 @@
using Avalonia.Automation.Peers;
#nullable enable
namespace Avalonia.Automation.Platform
{
/// <summary>
/// Creates nodes in the UI Automation tree of the underlying platform.
/// </summary>
public interface IAutomationNodeFactory
{
/// <summary>
/// Creates an automation node for a peer.
/// </summary>
/// <param name="peer">The peer.</param>
IAutomationNode CreateNode(AutomationPeer peer);
}
}

20
src/Avalonia.Controls/Automation/Platform/IRootAutomationNode.cs

@ -1,20 +0,0 @@
using Avalonia.Automation.Peers;
#nullable enable
namespace Avalonia.Automation.Platform
{
/// <summary>
/// Represents a platform implementation of a root node in the UI Automation tree.
/// </summary>
public interface IRootAutomationNode : IAutomationNode
{
/// <summary>
/// Called by the <see cref="IRootProvider"/> when its focus changes.
/// </summary>
/// <param name="focus">
/// The automation peer for the newly focused control or null if no control is focused.
/// </param>
void FocusChanged(AutomationPeer? focus);
}
}

4
src/Avalonia.Controls/Automation/Provider/IRootProvider.cs

@ -1,4 +1,5 @@
using Avalonia.Automation.Peers;
using System;
using Avalonia.Automation.Peers;
using Avalonia.Platform;
#nullable enable
@ -10,5 +11,6 @@ namespace Avalonia.Automation.Provider
ITopLevelImpl? PlatformImpl { get; }
AutomationPeer? GetFocus();
AutomationPeer? GetPeerFromPoint(Point p);
event EventHandler? FocusChanged;
}
}

6
src/Avalonia.Controls/Button.cs

@ -2,7 +2,6 @@ using System;
using System.Linq;
using System.Windows.Input;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
@ -380,10 +379,7 @@ namespace Avalonia.Controls
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
{
return new ButtonAutomationPeer(factory, this);
}
protected override AutomationPeer OnCreateAutomationPeer() => new ButtonAutomationPeer(this);
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
{

5
src/Avalonia.Controls/CheckBox.cs

@ -1,5 +1,4 @@
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Primitives;
namespace Avalonia.Controls
@ -9,9 +8,9 @@ namespace Avalonia.Controls
/// </summary>
public class CheckBox : ToggleButton
{
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new CheckBoxAutomationPeer(factory, this);
return new CheckBoxAutomationPeer(this);
}
}
}

5
src/Avalonia.Controls/ComboBox.cs

@ -1,7 +1,6 @@
using System;
using System.Linq;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using System.Reactive.Disposables;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Mixins;
@ -298,9 +297,9 @@ namespace Avalonia.Controls
_popup.Closed += PopupClosed;
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ComboBoxAutomationPeer(factory, this);
return new ComboBoxAutomationPeer(this);
}
internal void ItemFocused(ComboBoxItem dropDownItem)

5
src/Avalonia.Controls/ComboBoxItem.cs

@ -1,7 +1,6 @@
using System;
using System.Reactive.Linq;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
namespace Avalonia.Controls
{
@ -16,9 +15,9 @@ namespace Avalonia.Controls
.Subscribe(_ => (Parent as ComboBox)?.ItemFocused(this));
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ComboBoxItemAutomationPeer(factory, this);
return new ComboBoxItemAutomationPeer(this);
}
}
}

5
src/Avalonia.Controls/ContextMenu.cs

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using System.Linq;
using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Generators;
@ -320,9 +319,9 @@ namespace Avalonia.Controls
return new MenuItemContainerGenerator(this);
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ContextMenuAutomationPeer(factory, this);
return new ContextMenuAutomationPeer(this);
}
private void Open(Control control, Control placementTarget, bool requestedByPointer)

16
src/Avalonia.Controls/Control.cs

@ -1,7 +1,6 @@
using System;
using System.ComponentModel;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
@ -230,12 +229,12 @@ namespace Avalonia.Controls
}
}
protected virtual AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected virtual AutomationPeer OnCreateAutomationPeer()
{
return new NoneAutomationPeer(factory, this);
return new NoneAutomationPeer(this);
}
internal AutomationPeer GetOrCreateAutomationPeer(IAutomationNodeFactory factory)
internal AutomationPeer GetOrCreateAutomationPeer()
{
VerifyAccess();
@ -244,17 +243,10 @@ namespace Avalonia.Controls
return _automationPeer;
}
_automationPeer = OnCreateAutomationPeer(factory);
_automationPeer = OnCreateAutomationPeer();
return _automationPeer;
}
internal void SetAutomationPeer(AutomationPeer peer)
{
if (_automationPeer is object)
throw new InvalidOperationException("Automation peer is already set.");
_automationPeer = peer ?? throw new ArgumentNullException(nameof(peer));
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
base.OnPointerReleased(e);

5
src/Avalonia.Controls/Image.cs

@ -1,5 +1,4 @@
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Metadata;
@ -127,9 +126,9 @@ namespace Avalonia.Controls
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ImageAutomationPeer(factory, this);
return new ImageAutomationPeer(this);
}
}
}

5
src/Avalonia.Controls/ItemsControl.cs

@ -5,7 +5,6 @@ using System.Collections.Specialized;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Presenters;
@ -326,9 +325,9 @@ namespace Avalonia.Controls
base.OnKeyDown(e);
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ItemsControlAutomationPeer(factory, this);
return new ItemsControlAutomationPeer(this);
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)

5
src/Avalonia.Controls/ListBoxItem.cs

@ -1,5 +1,4 @@
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
@ -36,9 +35,9 @@ namespace Avalonia.Controls
set { SetValue(IsSelectedProperty, value); }
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ListItemAutomationPeer(factory, this);
return new ListItemAutomationPeer(this);
}
}
}

5
src/Avalonia.Controls/Menu.cs

@ -1,5 +1,4 @@
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
@ -94,9 +93,9 @@ namespace Avalonia.Controls
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MenuAutomationPeer(factory, this);
return new MenuAutomationPeer(this);
}
}
}

5
src/Avalonia.Controls/MenuItem.cs

@ -4,7 +4,6 @@ using System.Linq;
using System.Reactive.Linq;
using System.Windows.Input;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
@ -495,9 +494,9 @@ namespace Avalonia.Controls
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MenuItemAutomationPeer(factory, this);
return new MenuItemAutomationPeer(this);
}
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)

6
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -1,6 +1,5 @@
using System;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
namespace Avalonia.Platform
{
@ -48,11 +47,6 @@ namespace Avalonia.Platform
/// </summary>
Action Activated { get; set; }
/// <summary>
/// Gets or sets a method called when automation is started on the window.
/// </summary>
Func<IAutomationNode, AutomationPeer> AutomationStarted { get; set; }
/// <summary>
/// Gets the platform window handle.
/// </summary>

5
src/Avalonia.Controls/Primitives/AccessText.cs

@ -1,6 +1,5 @@
using System;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Media.TextFormatting;
@ -108,9 +107,9 @@ namespace Avalonia.Controls.Primitives
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new NoneAutomationPeer(factory, this);
return new NoneAutomationPeer(this);
}
internal static string RemoveAccessKeyMarker(string text)

5
src/Avalonia.Controls/Primitives/Popup.cs

@ -3,7 +3,6 @@ using System.ComponentModel;
using System.Linq;
using System.Reactive.Disposables;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Diagnostics;
using Avalonia.Controls.Presenters;
@ -543,9 +542,9 @@ namespace Avalonia.Controls.Primitives
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new PopupAutomationPeer(factory, this);
return new PopupAutomationPeer(this);
}
private static IDisposable SubscribeToEventHandler<T, TEventHandler>(T target, TEventHandler handler, Action<T, TEventHandler> subscribe, Action<T, TEventHandler> unsubscribe)

5
src/Avalonia.Controls/Primitives/PopupRoot.cs

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Interactivity;
using Avalonia.Media;
@ -168,9 +167,9 @@ namespace Avalonia.Controls.Primitives
return ClientSize;
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNode node)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new PopupRootAutomationPeer(node, this);
return new PopupRootAutomationPeer(this);
}
}
}

5
src/Avalonia.Controls/Primitives/ToggleButton.cs

@ -1,6 +1,5 @@
using System;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Metadata;
using Avalonia.Data;
using Avalonia.Interactivity;
@ -171,9 +170,9 @@ namespace Avalonia.Controls.Primitives
RaiseEvent(e);
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ToggleButtonAutomationPeer(factory, this);
return new ToggleButtonAutomationPeer(this);
}
private void OnIsCheckedChanged(AvaloniaPropertyChangedEventArgs e)

5
src/Avalonia.Controls/ScrollViewer.cs

@ -1,7 +1,6 @@
using System;
using System.Reactive.Linq;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
@ -712,9 +711,9 @@ namespace Avalonia.Controls
_scrollBarExpandSubscription = SubscribeToScrollBars(e);
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new ScrollViewerAutomationPeer(factory, this);
return new ScrollViewerAutomationPeer(this);
}
private IDisposable SubscribeToScrollBars(TemplateAppliedEventArgs e)

5
src/Avalonia.Controls/Slider.cs

@ -1,7 +1,6 @@
using System;
using Avalonia.Collections;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
@ -211,9 +210,9 @@ namespace Avalonia.Controls
_pointerMovedDispose = this.AddDisposableHandler(PointerMovedEvent, TrackMoved, RoutingStrategies.Tunnel);
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new SliderAutomationPeer(factory, this);
return new SliderAutomationPeer(this);
}
protected override void OnKeyDown(KeyEventArgs e)

5
src/Avalonia.Controls/TabControl.cs

@ -2,7 +2,6 @@ using System.ComponentModel;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
@ -233,9 +232,9 @@ namespace Avalonia.Controls
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TabControlAutomationPeer(factory, this);
return new TabControlAutomationPeer(this);
}
}
}

5
src/Avalonia.Controls/TabItem.cs

@ -1,5 +1,4 @@
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Metadata;
using Avalonia.Controls.Mixins;
using Avalonia.Controls.Primitives;
@ -83,9 +82,9 @@ namespace Avalonia.Controls
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TabItemAutomationPeer(factory, this);
return new TabItemAutomationPeer(this);
}
}
}

5
src/Avalonia.Controls/TextBlock.cs

@ -1,6 +1,5 @@
using System.Reactive.Linq;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Layout;
using Avalonia.LogicalTree;
using Avalonia.Media;
@ -513,9 +512,9 @@ namespace Avalonia.Controls
InvalidateMeasure();
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TextBlockAutomationPeer(factory, this);
return new TextBlockAutomationPeer(this);
}
private static bool IsValidMaxLines(int maxLines) => maxLines >= 0;

5
src/Avalonia.Controls/TextBox.cs

@ -15,7 +15,6 @@ using Avalonia.Layout;
using Avalonia.Utilities;
using Avalonia.Controls.Metadata;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
namespace Avalonia.Controls
{
@ -1055,9 +1054,9 @@ namespace Avalonia.Controls
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new TextBoxAutomationPeer(factory, this);
return new TextBoxAutomationPeer(this);
}
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)

2
src/Avalonia.Controls/TopLevel.cs

@ -1,7 +1,5 @@
using System;
using System.Reactive.Linq;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Input;

5
src/Avalonia.Controls/Window.cs

@ -5,7 +5,6 @@ using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Interactivity;
@ -1014,9 +1013,9 @@ namespace Avalonia.Controls
}
}
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNode node)
protected override AutomationPeer OnCreateAutomationPeer()
{
return new WindowAutomationPeer(node, this);
return new WindowAutomationPeer(this);
}
}
}

20
src/Avalonia.Controls/WindowBase.cs

@ -4,7 +4,6 @@ using System.Linq;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Layout;
@ -65,7 +64,6 @@ namespace Avalonia.Controls
impl.Activated = HandleActivated;
impl.Deactivated = HandleDeactivated;
impl.PositionChanged = HandlePositionChanged;
impl.AutomationStarted = HandleAutomationStarted;
}
/// <summary>
@ -263,17 +261,6 @@ namespace Avalonia.Controls
/// <returns>The actual size of the window.</returns>
protected virtual Size ArrangeSetBounds(Size size) => size;
protected sealed override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
{
throw new NotSupportedException(
"Automation peer for window controls must be created by the operating system.");
}
protected virtual AutomationPeer OnCreateAutomationPeer(IAutomationNode node)
{
throw new NotImplementedException("OnCreateAutomationPeer must be implemented in a derived class.");
}
/// <summary>
/// Handles a window position change notification from
/// <see cref="IWindowBaseImpl.PositionChanged"/>.
@ -311,13 +298,6 @@ namespace Avalonia.Controls
Deactivated?.Invoke(this, EventArgs.Empty);
}
private AutomationPeer HandleAutomationStarted(IAutomationNode node)
{
var peer = OnCreateAutomationPeer(node);
SetAutomationPeer(peer);
return peer;
}
private void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
{
if (!_ignoreVisibilityChange)

4
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -1,7 +1,4 @@
using System;
using System.Reactive.Disposables;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls.Remote.Server;
using Avalonia.Input;
@ -47,7 +44,6 @@ namespace Avalonia.DesignerSupport.Remote
public IPlatformHandle Handle { get; }
public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; }
public Func<IAutomationNode, AutomationPeer> AutomationStarted { get; set; }
public Size MaxAutoSizeHint { get; } = new Size(4096, 4096);
protected override void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)

2
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -4,7 +4,6 @@ using System.IO;
using System.Reactive.Disposables;
using System.Threading.Tasks;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives.PopupPositioning;
@ -41,7 +40,6 @@ namespace Avalonia.DesignerSupport.Remote
public Action<PixelPoint> PositionChanged { get; set; }
public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; }
public Func<IAutomationNode, AutomationPeer> AutomationStarted { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }

2
src/Avalonia.Headless/HeadlessWindowImpl.cs

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Controls.Primitives.PopupPositioning;
@ -145,7 +144,6 @@ namespace Avalonia.Headless
public IScreenImpl Screen { get; } = new HeadlessScreensStub();
public WindowState WindowState { get; set; }
public Action<WindowState> WindowStateChanged { get; set; }
public Func<IAutomationNode, AutomationPeer> AutomationStarted { get; set; }
public void SetTitle(string title)
{

7
src/Avalonia.Native/AutomationNode.cs

@ -1,22 +1,19 @@
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Native.Interop;
#nullable enable
namespace Avalonia.Native
{
internal class AutomationNode : IRootAutomationNode
internal class AutomationNode
{
public AutomationNode(AutomationNodeFactory factory, IAvnAutomationNode native)
public AutomationNode(IAvnAutomationNode native)
{
Native = native;
Factory = factory;
}
public IAvnAutomationNode Native { get; }
public IAutomationNodeFactory Factory { get; }
public void ChildrenChanged() => Native.ChildrenChanged();

24
src/Avalonia.Native/AutomationNodeFactory.cs

@ -1,24 +0,0 @@
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Native.Interop;
namespace Avalonia.Native
{
internal class AutomationNodeFactory : IAutomationNodeFactory
{
private static AutomationNodeFactory _instance;
private readonly IAvaloniaNativeFactory _native;
public static AutomationNodeFactory GetInstance(IAvaloniaNativeFactory native)
{
return _instance ??= new AutomationNodeFactory(native);
}
private AutomationNodeFactory(IAvaloniaNativeFactory native) => _native = native;
public IAutomationNode CreateNode(AutomationPeer peer)
{
return new AutomationNode(this, _native.CreateAutomationNode(new AvnAutomationPeer(peer)));
}
}
}

3
src/Avalonia.Native/AvnAutomationPeer.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Automation;
@ -15,7 +16,7 @@ namespace Avalonia.Native
public AvnAutomationPeer(AutomationPeer inner) => _inner = inner;
public IAvnAutomationNode Node => ((AutomationNode)_inner.Node).Native;
public IAvnAutomationNode Node => throw new NotImplementedException();
public IAvnString? AcceleratorKey => _inner.GetAcceleratorKey().ToAvnString();
public IAvnString? AccessKey => _inner.GetAccessKey().ToAvnString();
public AvnAutomationControlType AutomationControlType => (AvnAutomationControlType)_inner.GetAutomationControlType();

38
src/Avalonia.Native/WindowImplBase.cs

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Platform.Surfaces;
@ -63,9 +62,7 @@ namespace Avalonia.Native
private GlPlatformSurface _glSurface;
private NativeControlHostImpl _nativeControlHost;
private IGlContext _glContext;
private IAutomationNode _automationNode;
private AvnAutomationPeer _automationPeer;
private Func<IAutomationNode, AutomationPeer> _automationStarted;
internal WindowBaseImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
AvaloniaNativePlatformOpenGlInterface glFeature)
@ -265,8 +262,6 @@ namespace Avalonia.Native
return (AvnDragDropEffects)args.Effects;
}
}
public IAvnAutomationPeer AutomationStarted(IAvnAutomationNode node) => _parent.HandleAutomationStarted(node);
}
public void Activate()
@ -489,38 +484,5 @@ namespace Avalonia.Native
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0, 0);
public IPlatformHandle Handle { get; private set; }
public Func<IAutomationNode, AutomationPeer> AutomationStarted
{
get => _automationStarted;
set
{
_automationStarted = value;
// We've already received an AutomationStarted event, but the Window/PopupRoot wasn't initialized.
// Now it is, so notify it and store the automation peer for the next time the OS invokes an a11y
// query.
if (value is object && _automationNode is object)
_automationPeer = new AvnAutomationPeer(_automationStarted.Invoke(_automationNode));
}
}
private AvnAutomationPeer HandleAutomationStarted(IAvnAutomationNode node)
{
if (_automationPeer is object)
return _automationPeer;
var factory = AutomationNodeFactory.GetInstance(_factory);
_automationNode = new AutomationNode(factory, node);
// If automation is started during platform window creation we don't yet have a Window/PopupRoot
// control to notify. In this case we'll notify them when AutomationStarted gets set. We can safely
// return null here because the peer isn't actually needed at this point and will be re-queried the next
// time it's needed.
if (AutomationStarted is null)
return null;
return _automationPeer = new AvnAutomationPeer(AutomationStarted(_automationNode));
}
}
}

1
src/Avalonia.Native/avn.idl

@ -569,7 +569,6 @@ interface IAvnWindowBaseEvents : IUnknown
AvnDragDropEffects DragEvent(AvnDragEventType type, AvnPoint position,
AvnInputModifiers modifiers, AvnDragDropEffects effects,
IAvnClipboard* clipboard, [intptr]void* dataObjectHandle);
IAvnAutomationPeer* AutomationStarted(IAvnAutomationNode* node);
}
[uuid(1ae178ee-1fcc-447f-b6dd-b7bb727f934c)]

2
src/Avalonia.X11/X11Window.cs

@ -7,7 +7,6 @@ using System.Reactive.Disposables;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives.PopupPositioning;
@ -1163,6 +1162,5 @@ namespace Avalonia.X11
public AcrylicPlatformCompensationLevels AcrylicCompensationLevels { get; } = new AcrylicPlatformCompensationLevels(1, 0.8, 0.8);
public bool NeedsManagedDecorations => false;
public Func<IAutomationNode, AutomationPeer> AutomationStarted { get; set; }
}
}

4
src/Windows/Avalonia.Win32/Automation/AutomationNode.Selection.cs

@ -20,14 +20,14 @@ namespace Avalonia.Win32.Automation
get
{
var peer = InvokeSync<ISelectionItemProvider, ISelectionProvider?>(x => x.SelectionContainer);
return (peer as AutomationPeer)?.Node as AutomationNode;
return GetOrCreate(peer as AutomationPeer);
}
}
public UIA.IRawElementProviderSimple[] GetSelection()
{
var peers = InvokeSync<ISelectionProvider, IReadOnlyList<AutomationPeer>>(x => x.GetSelection());
return peers?.Select(x => (UIA.IRawElementProviderSimple)x.Node).ToArray() ??
return peers?.Select(x => (UIA.IRawElementProviderSimple)GetOrCreate(x)!).ToArray() ??
Array.Empty<UIA.IRawElementProviderSimple>();
}

39
src/Windows/Avalonia.Win32/Automation/AutomationNode.cs

@ -4,10 +4,10 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Avalonia.Automation;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Interop.Automation;
using AAP = Avalonia.Automation.Provider;
@ -18,7 +18,6 @@ namespace Avalonia.Win32.Automation
{
[ComVisible(true)]
internal partial class AutomationNode : MarshalByRefObject,
IAutomationNode,
IRawElementProviderSimple,
IRawElementProviderSimple2,
IRawElementProviderFragment,
@ -46,6 +45,9 @@ namespace Avalonia.Win32.Automation
{ SelectionPatternIdentifiers.SelectionProperty, UiaPropertyId.SelectionSelection },
};
private static ConditionalWeakTable<AutomationPeer, AutomationNode> s_nodes =
new ConditionalWeakTable<AutomationPeer, AutomationNode>();
private readonly int[] _runtimeId;
private int _raiseFocusChanged;
private int _raisePropertyChanged;
@ -54,22 +56,16 @@ namespace Avalonia.Win32.Automation
{
_runtimeId = new int[] { 3, GetHashCode() };
Peer = peer;
}
protected AutomationNode(Func<IAutomationNode, AutomationPeer> peerGetter)
{
_runtimeId = new int[] { 3, GetHashCode() };
Peer = peerGetter(this);
s_nodes.Add(peer, this);
}
public AutomationPeer Peer { get; protected set; }
public IAutomationNodeFactory Factory => AutomationNodeFactory.Instance;
public Rect BoundingRectangle
{
get => InvokeSync(() =>
{
if (GetRoot()?.Node is RootAutomationNode root)
if (GetRoot() is RootAutomationNode root)
return root.ToScreen(Peer.GetBoundingRectangle());
return default;
});
@ -77,7 +73,7 @@ namespace Avalonia.Win32.Automation
public virtual IRawElementProviderFragmentRoot? FragmentRoot
{
get => InvokeSync(() => GetRoot())?.Node as IRawElementProviderFragmentRoot;
get => InvokeSync(() => GetRoot()) as IRawElementProviderFragmentRoot;
}
public virtual IRawElementProviderSimple? HostRawElementProvider => null;
@ -147,7 +143,7 @@ namespace Avalonia.Win32.Automation
public virtual IRawElementProviderFragment? Navigate(NavigateDirection direction)
{
IAutomationNode? GetSibling(int direction)
AutomationNode? GetSibling(int direction)
{
var children = Peer.GetParent()?.GetChildren();
@ -157,7 +153,7 @@ namespace Avalonia.Win32.Automation
{
var j = i + direction;
if (j >= 0 && j < children.Count)
return children[j].Node;
return GetOrCreate(children[j]);
}
}
@ -173,11 +169,11 @@ namespace Avalonia.Win32.Automation
{
return direction switch
{
NavigateDirection.Parent => Peer.GetParent()?.Node,
NavigateDirection.Parent => GetOrCreate(Peer.GetParent()),
NavigateDirection.NextSibling => GetSibling(1),
NavigateDirection.PreviousSibling => GetSibling(-1),
NavigateDirection.FirstChild => Peer.GetChildren().FirstOrDefault()?.Node,
NavigateDirection.LastChild => Peer.GetChildren().LastOrDefault()?.Node,
NavigateDirection.FirstChild => GetOrCreate(Peer.GetChildren().FirstOrDefault()),
NavigateDirection.LastChild => GetOrCreate(Peer.GetChildren().LastOrDefault()),
_ => null,
};
}) as IRawElementProviderFragment;
@ -185,6 +181,13 @@ namespace Avalonia.Win32.Automation
public void SetFocus() => InvokeSync(() => Peer.SetFocus());
public static AutomationNode? GetOrCreate(AutomationPeer? peer)
{
if (peer is null)
return null;
return s_nodes.GetValue(peer, x => new AutomationNode(x));
}
IRawElementProviderSimple[]? IRawElementProviderFragment.GetEmbeddedFragmentRoots() => null;
void IRawElementProviderSimple2.ShowContextMenu() => InvokeSync(() => Peer.ShowContextMenu());
void IInvokeProvider.Invoke() => InvokeSync((AAP.IInvokeProvider x) => x.Invoke());
@ -275,7 +278,7 @@ namespace Avalonia.Win32.Automation
}
}
private AutomationPeer GetRoot()
private AutomationNode? GetRoot()
{
Dispatcher.UIThread.VerifyAccess();
@ -288,7 +291,7 @@ namespace Avalonia.Win32.Automation
parent = peer.GetParent();
}
return peer;
return peer is object ? GetOrCreate(peer) : null;
}
private static UiaControlTypeId ToUiaControlType(AutomationControlType role)

19
src/Windows/Avalonia.Win32/Automation/AutomationNodeFactory.cs

@ -1,19 +0,0 @@
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Threading;
#nullable enable
namespace Avalonia.Win32.Automation
{
internal class AutomationNodeFactory : IAutomationNodeFactory
{
public static readonly AutomationNodeFactory Instance = new AutomationNodeFactory();
public IAutomationNode CreateNode(AutomationPeer peer)
{
Dispatcher.UIThread.VerifyAccess();
return new AutomationNode(peer);
}
}
}

18
src/Windows/Avalonia.Win32/Automation/RootAutomationNode.cs

@ -1,7 +1,6 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Win32.Interop.Automation;
@ -10,12 +9,12 @@ using Avalonia.Win32.Interop.Automation;
namespace Avalonia.Win32.Automation
{
internal class RootAutomationNode : AutomationNode,
IRawElementProviderFragmentRoot,
IRootAutomationNode
IRawElementProviderFragmentRoot
{
public RootAutomationNode(Func<IAutomationNode, AutomationPeer> peerGetter)
: base(peerGetter)
public RootAutomationNode(AutomationPeer peer)
: base(peer)
{
((IRootProvider)peer).FocusChanged += FocusChanged;
}
public override IRawElementProviderFragmentRoot? FragmentRoot => this;
@ -30,20 +29,19 @@ namespace Avalonia.Win32.Automation
var p = WindowImpl.PointToClient(new PixelPoint((int)x, (int)y));
var peer = (WindowBaseAutomationPeer)Peer;
var found = InvokeSync(() => peer.GetPeerFromPoint(p));
var result = found?.Node as IRawElementProviderFragment;
var result = GetOrCreate(found) as IRawElementProviderFragment;
return result;
}
public IRawElementProviderFragment? GetFocus()
{
var focus = InvokeSync(() => Peer.GetFocus());
return (AutomationNode?)focus?.Node;
return GetOrCreate(focus);
}
public void FocusChanged(AutomationPeer? focus)
public void FocusChanged(object sender, EventArgs e)
{
var node = focus?.Node as AutomationNode;
RaiseFocusChanged(node);
RaiseFocusChanged(GetOrCreate(Peer.GetFocus()));
}
public Rect ToScreen(Rect rect)

13
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@ -1,6 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;
using Avalonia.Automation.Peers;
using Avalonia.Controls;
using Avalonia.Controls.Remote;
using Avalonia.Input;
@ -483,16 +484,14 @@ namespace Avalonia.Win32
case WindowsMessage.WM_GETOBJECT:
if ((long)lParam == UiaRootObjectId)
{
if (_automationNode is null && AutomationStarted is object)
if (_automationNode is null)
{
_automationNode = new RootAutomationNode(AutomationStarted);
var peer = ControlAutomationPeer.CreatePeerForElement((Control)_owner);
_automationNode = new RootAutomationNode(peer);
}
if (_automationNode is object)
{
var r = UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, wParam, lParam, _automationNode);
return r;
}
var r = UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, wParam, lParam, _automationNode);
return r;
}
break;
}

2
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -4,7 +4,6 @@ using System.ComponentModel;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Raw;
@ -177,7 +176,6 @@ namespace Avalonia.Win32
public Action LostFocus { get; set; }
public Action<WindowTransparencyLevel> TransparencyLevelChanged { get; set; }
public Func<IAutomationNode, AutomationPeer> AutomationStarted { get; set; }
public Thickness BorderThickness
{

506
tests/Avalonia.Controls.UnitTests/Automation/ControlAutomationPeerTests.cs

@ -1,253 +1,253 @@
using System.Linq;
using Avalonia.Automation.Peers;
using Avalonia.Automation.Platform;
using Avalonia.Automation.Provider;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Platform;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Moq;
using Xunit;
namespace Avalonia.Controls.UnitTests.Automation
{
public class ControlAutomationPeerTests
{
private static Mock<IAutomationNodeFactory> _factory;
public ControlAutomationPeerTests()
{
_factory = new Mock<IAutomationNodeFactory>();
_factory.Setup(x => x.CreateNode(It.IsAny<AutomationPeer>()))
.Returns(() => Mock.Of<IAutomationNode>(x => x.Factory == _factory));
}
public class Children
{
[Fact]
public void Creates_Children_For_Controls_In_Visual_Tree()
{
var panel = new Panel
{
Children =
{
new Border(),
new Border(),
},
};
var factory = CreateFactory();
var target = CreatePeer(factory, panel);
Assert.Equal(
panel.GetVisualChildren(),
target.GetChildren().Cast<ControlAutomationPeer>().Select(x => x.Owner));
}
[Fact]
public void Creates_Children_when_Controls_Attached_To_Visual_Tree()
{
var contentControl = new ContentControl
{
Template = new FuncControlTemplate<ContentControl>((o, ns) =>
new ContentPresenter
{
Name = "PART_ContentPresenter",
[!ContentPresenter.ContentProperty] = o[!ContentControl.ContentProperty],
}),
Content = new Border(),
};
var factory = CreateFactory();
var target = CreatePeer(factory, contentControl);
Assert.Empty(target.GetChildren());
contentControl.Measure(Size.Infinity);
Assert.Equal(1, target.GetChildren().Count);
}
[Fact]
public void Updates_Children_When_VisualChildren_Added()
{
var panel = new Panel
{
Children =
{
new Border(),
new Border(),
},
};
var factory = CreateFactory();
var target = CreatePeer(factory, panel);
var children = target.GetChildren();
Assert.Equal(2, children.Count);
panel.Children.Add(new Decorator());
children = target.GetChildren();
Assert.Equal(3, children.Count);
}
[Fact]
public void Updates_Children_When_VisualChildren_Removed()
{
var panel = new Panel
{
Children =
{
new Border(),
new Border(),
},
};
var factory = CreateFactory();
var target = CreatePeer(factory, panel);
var children = target.GetChildren();
Assert.Equal(2, children.Count);
panel.Children.RemoveAt(1);
children = target.GetChildren();
Assert.Equal(1, children.Count);
}
[Fact]
public void Updates_Children_When_Visibility_Changes()
{
var panel = new Panel
{
Children =
{
new Border(),
new Border(),
},
};
var factory = CreateFactory();
var target = CreatePeer(factory, panel);
var children = target.GetChildren();
Assert.Equal(2, children.Count);
panel.Children[1].IsVisible = false;
children = target.GetChildren();
Assert.Equal(1, children.Count);
panel.Children[1].IsVisible = true;
children = target.GetChildren();
Assert.Equal(2, children.Count);
}
}
public class Parent
{
[Fact]
public void Connects_Peer_To_Tree_When_GetParent_Called()
{
var border = new Border();
var tree = new Decorator
{
Child = new Decorator
{
Child = border,
}
};
var factory = CreateFactory();
// We're accessing Border directly without going via its ancestors. Because the tree
// is built lazily, ensure that calling GetParent causes the ancestor tree to be built.
var target = CreatePeer(factory, border);
var parentPeer = Assert.IsAssignableFrom<ControlAutomationPeer>(target.GetParent());
Assert.Same(border.GetVisualParent(), parentPeer.Owner);
}
[Fact]
public void Parent_Updated_When_Moved_To_Separate_Visual_Tree()
{
var border = new Border();
var root1 = new Decorator { Child = border };
var root2 = new Decorator();
var factory = CreateFactory();
var target = CreatePeer(factory, border);
var parentPeer = Assert.IsAssignableFrom<ControlAutomationPeer>(target.GetParent());
Assert.Same(root1, parentPeer.Owner);
root1.Child = null;
Assert.Null(target.GetParent());
root2.Child = border;
parentPeer = Assert.IsAssignableFrom<ControlAutomationPeer>(target.GetParent());
Assert.Same(root2, parentPeer.Owner);
}
}
private static IAutomationNodeFactory CreateFactory()
{
var factory = new Mock<IAutomationNodeFactory>();
factory.Setup(x => x.CreateNode(It.IsAny<AutomationPeer>()))
.Returns(() => Mock.Of<IAutomationNode>(x => x.Factory == factory.Object));
return factory.Object;
}
private static AutomationPeer CreatePeer(IAutomationNodeFactory factory, Control control)
{
return ControlAutomationPeer.GetOrCreatePeer(factory, control);
}
private class TestControl : Control
{
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
{
return new TestAutomationPeer(factory, this);
}
}
private class AutomationTestRoot : TestRoot
{
protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
{
return new TestRootAutomationPeer(factory, this);
}
}
private class TestAutomationPeer : ControlAutomationPeer
{
public TestAutomationPeer(IAutomationNodeFactory factory, Control owner)
: base(factory, owner)
{
}
}
private class TestRootAutomationPeer : ControlAutomationPeer, IRootProvider
{
public TestRootAutomationPeer(IAutomationNodeFactory factory, Control owner)
: base(factory, owner)
{
}
public ITopLevelImpl PlatformImpl => throw new System.NotImplementedException();
public AutomationPeer GetFocus()
{
throw new System.NotImplementedException();
}
public AutomationPeer GetPeerFromPoint(Point p)
{
throw new System.NotImplementedException();
}
}
}
}
////using System.Linq;
////using Avalonia.Automation.Peers;
////using Avalonia.Automation.Platform;
////using Avalonia.Automation.Provider;
////using Avalonia.Controls.Presenters;
////using Avalonia.Controls.Primitives;
////using Avalonia.Controls.Templates;
////using Avalonia.Platform;
////using Avalonia.UnitTests;
////using Avalonia.VisualTree;
////using Moq;
////using Xunit;
////namespace Avalonia.Controls.UnitTests.Automation
////{
//// public class ControlAutomationPeerTests
//// {
//// private static Mock<IAutomationNodeFactory> _factory;
//// public ControlAutomationPeerTests()
//// {
//// _factory = new Mock<IAutomationNodeFactory>();
//// _factory.Setup(x => x.CreateNode(It.IsAny<AutomationPeer>()))
//// .Returns(() => Mock.Of<IAutomationNode>(x => x.Factory == _factory));
//// }
//// public class Children
//// {
//// [Fact]
//// public void Creates_Children_For_Controls_In_Visual_Tree()
//// {
//// var panel = new Panel
//// {
//// Children =
//// {
//// new Border(),
//// new Border(),
//// },
//// };
//// var factory = CreateFactory();
//// var target = CreatePeer(factory, panel);
//// Assert.Equal(
//// panel.GetVisualChildren(),
//// target.GetChildren().Cast<ControlAutomationPeer>().Select(x => x.Owner));
//// }
//// [Fact]
//// public void Creates_Children_when_Controls_Attached_To_Visual_Tree()
//// {
//// var contentControl = new ContentControl
//// {
//// Template = new FuncControlTemplate<ContentControl>((o, ns) =>
//// new ContentPresenter
//// {
//// Name = "PART_ContentPresenter",
//// [!ContentPresenter.ContentProperty] = o[!ContentControl.ContentProperty],
//// }),
//// Content = new Border(),
//// };
//// var factory = CreateFactory();
//// var target = CreatePeer(factory, contentControl);
//// Assert.Empty(target.GetChildren());
//// contentControl.Measure(Size.Infinity);
//// Assert.Equal(1, target.GetChildren().Count);
//// }
//// [Fact]
//// public void Updates_Children_When_VisualChildren_Added()
//// {
//// var panel = new Panel
//// {
//// Children =
//// {
//// new Border(),
//// new Border(),
//// },
//// };
//// var factory = CreateFactory();
//// var target = CreatePeer(factory, panel);
//// var children = target.GetChildren();
//// Assert.Equal(2, children.Count);
//// panel.Children.Add(new Decorator());
//// children = target.GetChildren();
//// Assert.Equal(3, children.Count);
//// }
//// [Fact]
//// public void Updates_Children_When_VisualChildren_Removed()
//// {
//// var panel = new Panel
//// {
//// Children =
//// {
//// new Border(),
//// new Border(),
//// },
//// };
//// var factory = CreateFactory();
//// var target = CreatePeer(factory, panel);
//// var children = target.GetChildren();
//// Assert.Equal(2, children.Count);
//// panel.Children.RemoveAt(1);
//// children = target.GetChildren();
//// Assert.Equal(1, children.Count);
//// }
//// [Fact]
//// public void Updates_Children_When_Visibility_Changes()
//// {
//// var panel = new Panel
//// {
//// Children =
//// {
//// new Border(),
//// new Border(),
//// },
//// };
//// var factory = CreateFactory();
//// var target = CreatePeer(factory, panel);
//// var children = target.GetChildren();
//// Assert.Equal(2, children.Count);
//// panel.Children[1].IsVisible = false;
//// children = target.GetChildren();
//// Assert.Equal(1, children.Count);
//// panel.Children[1].IsVisible = true;
//// children = target.GetChildren();
//// Assert.Equal(2, children.Count);
//// }
//// }
//// public class Parent
//// {
//// [Fact]
//// public void Connects_Peer_To_Tree_When_GetParent_Called()
//// {
//// var border = new Border();
//// var tree = new Decorator
//// {
//// Child = new Decorator
//// {
//// Child = border,
//// }
//// };
//// var factory = CreateFactory();
//// // We're accessing Border directly without going via its ancestors. Because the tree
//// // is built lazily, ensure that calling GetParent causes the ancestor tree to be built.
//// var target = CreatePeer(factory, border);
//// var parentPeer = Assert.IsAssignableFrom<ControlAutomationPeer>(target.GetParent());
//// Assert.Same(border.GetVisualParent(), parentPeer.Owner);
//// }
//// [Fact]
//// public void Parent_Updated_When_Moved_To_Separate_Visual_Tree()
//// {
//// var border = new Border();
//// var root1 = new Decorator { Child = border };
//// var root2 = new Decorator();
//// var factory = CreateFactory();
//// var target = CreatePeer(factory, border);
//// var parentPeer = Assert.IsAssignableFrom<ControlAutomationPeer>(target.GetParent());
//// Assert.Same(root1, parentPeer.Owner);
//// root1.Child = null;
//// Assert.Null(target.GetParent());
//// root2.Child = border;
//// parentPeer = Assert.IsAssignableFrom<ControlAutomationPeer>(target.GetParent());
//// Assert.Same(root2, parentPeer.Owner);
//// }
//// }
//// private static IAutomationNodeFactory CreateFactory()
//// {
//// var factory = new Mock<IAutomationNodeFactory>();
//// factory.Setup(x => x.CreateNode(It.IsAny<AutomationPeer>()))
//// .Returns(() => Mock.Of<IAutomationNode>(x => x.Factory == factory.Object));
//// return factory.Object;
//// }
//// private static AutomationPeer CreatePeer(IAutomationNodeFactory factory, Control control)
//// {
//// return ControlAutomationPeer.GetOrCreatePeer(factory, control);
//// }
//// private class TestControl : Control
//// {
//// protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
//// {
//// return new TestAutomationPeer(factory, this);
//// }
//// }
//// private class AutomationTestRoot : TestRoot
//// {
//// protected override AutomationPeer OnCreateAutomationPeer(IAutomationNodeFactory factory)
//// {
//// return new TestRootAutomationPeer(factory, this);
//// }
//// }
//// private class TestAutomationPeer : ControlAutomationPeer
//// {
//// public TestAutomationPeer(IAutomationNodeFactory factory, Control owner)
//// : base(factory, owner)
//// {
//// }
//// }
//// private class TestRootAutomationPeer : ControlAutomationPeer, IRootProvider
//// {
//// public TestRootAutomationPeer(IAutomationNodeFactory factory, Control owner)
//// : base(factory, owner)
//// {
//// }
//// public ITopLevelImpl PlatformImpl => throw new System.NotImplementedException();
//// public AutomationPeer GetFocus()
//// {
//// throw new System.NotImplementedException();
//// }
//// public AutomationPeer GetPeerFromPoint(Point p)
//// {
//// throw new System.NotImplementedException();
//// }
//// }
//// }
////}

Loading…
Cancel
Save