diff --git a/native/Avalonia.Native/src/OSX/automation.mm b/native/Avalonia.Native/src/OSX/automation.mm
index 32230ff5b7..b400785a13 100644
--- a/native/Avalonia.Native/src/OSX/automation.mm
+++ b/native/Avalonia.Native/src/OSX/automation.mm
@@ -26,6 +26,17 @@ public:
NSAccessibilityPostNotification(_node, NSAccessibilityLayoutChangedNotification);
}
+ virtual void PropertyChanged(AvnAutomationProperty property) override
+ {
+ switch (property) {
+ case RangeValueProvider_Value:
+ NSAccessibilityPostNotification(_node, NSAccessibilityValueChangedNotification);
+ break;
+ default:
+ break;
+ }
+ }
+
virtual NSObject* GetNSAccessibility() override
{
return _node;
@@ -75,7 +86,7 @@ public:
case AutomationStatusBar: return NSAccessibilityTableRole;
case AutomationTab: return NSAccessibilityTabGroupRole;
case AutomationTabItem: return NSAccessibilityRadioButtonRole;
- case AutomationText: return NSAccessibilityTextFieldRole;
+ case AutomationText: return NSAccessibilityStaticTextRole;
case AutomationToolBar: return NSAccessibilityToolbarRole;
case AutomationToolTip: return NSAccessibilityPopoverRole;
case AutomationTree: return NSAccessibilityOutlineRole;
@@ -105,7 +116,39 @@ public:
- (NSString *)accessibilityTitle
{
- return GetNSStringAndRelease(_peer->GetName());
+ // StaticText exposes its text via the value property.
+ if (_peer->GetAutomationControlType() != AutomationText)
+ {
+ return GetNSStringAndRelease(_peer->GetName());
+ }
+
+ return [super accessibilityTitle];
+}
+
+- (id)accessibilityValue
+{
+ if (_peer->IsRangeValueProvider())
+ {
+ return [NSNumber numberWithDouble:_peer->RangeValueProvider_GetValue()];
+ }
+ else if (_peer->IsToggleProvider())
+ {
+ switch (_peer->ToggleProvider_GetToggleState()) {
+ case 0: return [NSNumber numberWithBool:NO];
+ case 1: return [NSNumber numberWithBool:YES];
+ default: return [NSNumber numberWithInt:-1];
+ }
+ }
+ else if (_peer->IsValueProvider())
+ {
+ return GetNSStringAndRelease(_peer->ValueProvider_GetValue());
+ }
+ else if (_peer->GetAutomationControlType() == AutomationText)
+ {
+ return GetNSStringAndRelease(_peer->GetName());
+ }
+
+ return [super accessibilityValue];
}
- (NSArray *)accessibilityChildren
@@ -175,16 +218,43 @@ public:
- (BOOL)accessibilityPerformPress
{
+ if (!_peer->IsInvokeProvider())
+ return NO;
_peer->InvokeProvider_Invoke();
return YES;
}
+- (BOOL)accessibilityPerformIncrement
+{
+ if (!_peer->IsRangeValueProvider())
+ return NO;
+ auto value = _peer->RangeValueProvider_GetValue();
+ value += _peer->RangeValueProvider_GetSmallChange();
+ _peer->RangeValueProvider_SetValue(value);
+ return YES;
+}
+
+- (BOOL)accessibilityPerformDecrement
+{
+ if (!_peer->IsRangeValueProvider())
+ return NO;
+ auto value = _peer->RangeValueProvider_GetValue();
+ value -= _peer->RangeValueProvider_GetSmallChange();
+ _peer->RangeValueProvider_SetValue(value);
+ return YES;
+}
+
- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector
{
if (selector == @selector(accessibilityPerformPress))
{
return _peer->IsInvokeProvider();
}
+ else if (selector == @selector(accessibilityPerformIncrement) ||
+ selector == @selector(accessibilityPerformDecrement))
+ {
+ return _peer->IsRangeValueProvider();
+ }
return [super isAccessibilitySelectorAllowed:selector];
}
diff --git a/native/Avalonia.Native/src/OSX/window.mm b/native/Avalonia.Native/src/OSX/window.mm
index 456a2db3ee..5d69fc8e75 100644
--- a/native/Avalonia.Native/src/OSX/window.mm
+++ b/native/Avalonia.Native/src/OSX/window.mm
@@ -514,6 +514,10 @@ public:
NSAccessibilityPostNotification(Window, NSAccessibilityLayoutChangedNotification);
}
+ virtual void PropertyChanged(AvnAutomationProperty property) override
+ {
+ }
+
protected:
virtual NSWindowStyleMask GetStyle()
{
diff --git a/src/Avalonia.Controls/Automation/Peers/RangeBaseAutomationPeer.cs b/src/Avalonia.Controls/Automation/Peers/RangeBaseAutomationPeer.cs
index bafc4c14fc..31a2b7e7af 100644
--- a/src/Avalonia.Controls/Automation/Peers/RangeBaseAutomationPeer.cs
+++ b/src/Avalonia.Controls/Automation/Peers/RangeBaseAutomationPeer.cs
@@ -19,6 +19,9 @@ namespace Avalonia.Automation.Peers
public double Maximum => Owner.Maximum;
public double Minimum => Owner.Minimum;
public double Value => Owner.Value;
+ public double SmallChange => Owner.SmallChange;
+ public double LargeChange => Owner.LargeChange;
+
public void SetValue(double value) => Owner.Value = value;
protected virtual void OwnerPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e)
diff --git a/src/Avalonia.Controls/Automation/Provider/IRangeValueProvider.cs b/src/Avalonia.Controls/Automation/Provider/IRangeValueProvider.cs
index d4cd35fcf9..d494e068f7 100644
--- a/src/Avalonia.Controls/Automation/Provider/IRangeValueProvider.cs
+++ b/src/Avalonia.Controls/Automation/Provider/IRangeValueProvider.cs
@@ -28,6 +28,18 @@ namespace Avalonia.Automation.Provider
///
double Value { get; }
+ ///
+ /// Gets the value that is added to or subtracted from the Value property when a large
+ /// change is made, such as with the PAGE DOWN key.
+ ///
+ double LargeChange { get; }
+
+ ///
+ /// Gets the value that is added to or subtracted from the Value property when a small
+ /// change is made, such as with an arrow key.
+ ///
+ double SmallChange { get; }
+
///
/// Sets the value of the control.
///
diff --git a/src/Avalonia.Native/AutomationNode.cs b/src/Avalonia.Native/AutomationNode.cs
index c0e46b52b9..2601b2f239 100644
--- a/src/Avalonia.Native/AutomationNode.cs
+++ b/src/Avalonia.Native/AutomationNode.cs
@@ -22,7 +22,20 @@ namespace Avalonia.Native
public void PropertyChanged(AutomationProperty property, object? oldValue, object? newValue)
{
- // TODO
+ AvnAutomationProperty p;
+
+ if (property == AutomationElementIdentifiers.BoundingRectangleProperty)
+ p = AvnAutomationProperty.AutomationPeer_BoundingRectangle;
+ else if (property == AutomationElementIdentifiers.ClassNameProperty)
+ p = AvnAutomationProperty.AutomationPeer_ClassName;
+ else if (property == AutomationElementIdentifiers.NameProperty)
+ p = AvnAutomationProperty.AutomationPeer_Name;
+ else if (property == RangeValuePatternIdentifiers.ValueProperty)
+ p = AvnAutomationProperty.RangeValueProvider_Value;
+ else
+ return;
+
+ Native.PropertyChanged(p);
}
public void FocusChanged(AutomationPeer? focus)
diff --git a/src/Avalonia.Native/AvnAutomationPeer.cs b/src/Avalonia.Native/AvnAutomationPeer.cs
index a1519d8d26..0c4ad58b9d 100644
--- a/src/Avalonia.Native/AvnAutomationPeer.cs
+++ b/src/Avalonia.Native/AvnAutomationPeer.cs
@@ -75,8 +75,21 @@ namespace Avalonia.Native
}
public int IsInvokeProvider() => (_inner is IInvokeProvider).AsComBool();
-
public void InvokeProvider_Invoke() => ((IInvokeProvider)_inner).Invoke();
+
+ public int IsRangeValueProvider() => (_inner is IRangeValueProvider).AsComBool();
+ public double RangeValueProvider_GetValue() => ((IRangeValueProvider)_inner).Value;
+ public double RangeValueProvider_GetSmallChange() => ((IRangeValueProvider)_inner).SmallChange;
+ public double RangeValueProvider_GetLargeChange() => ((IRangeValueProvider)_inner).LargeChange;
+ public void RangeValueProvider_SetValue(double value) => ((IRangeValueProvider)_inner).SetValue(value);
+
+ public int IsToggleProvider() => (_inner is IToggleProvider).AsComBool();
+ public int ToggleProvider_GetToggleState() => (int)((IToggleProvider)_inner).ToggleState;
+ public void ToggleProvider_Toggle() => ((IToggleProvider)_inner).Toggle();
+
+ public int IsValueProvider() => (_inner is IValueProvider).AsComBool();
+ public IAvnString ValueProvider_GetValue() => ((IValueProvider)_inner).Value.ToAvnString();
+ public void ValueProvider_SetValue(string value) => ((IValueProvider)_inner).SetValue(value);
public static AvnAutomationPeer? Wrap(AutomationPeer? peer) =>
peer != null ? new AvnAutomationPeer(peer) : null;
diff --git a/src/Avalonia.Native/avn.idl b/src/Avalonia.Native/avn.idl
index 5697665545..88c9225419 100644
--- a/src/Avalonia.Native/avn.idl
+++ b/src/Avalonia.Native/avn.idl
@@ -218,6 +218,14 @@ enum SystemDecorations {
SystemDecorationsFull = 2,
}
+enum AvnAutomationProperty
+{
+ AutomationPeer_BoundingRectangle,
+ AutomationPeer_ClassName,
+ AutomationPeer_Name,
+ RangeValueProvider_Value,
+}
+
struct AvnSize
{
double Width, Height;
@@ -808,6 +816,20 @@ interface IAvnAutomationPeer : IUnknown
bool IsInvokeProvider();
void InvokeProvider_Invoke();
+
+ bool IsRangeValueProvider();
+ double RangeValueProvider_GetValue();
+ double RangeValueProvider_GetSmallChange();
+ double RangeValueProvider_GetLargeChange();
+ void RangeValueProvider_SetValue(double value);
+
+ bool IsToggleProvider();
+ int ToggleProvider_GetToggleState();
+ void ToggleProvider_Toggle();
+
+ bool IsValueProvider();
+ IAvnString* ValueProvider_GetValue();
+ void ValueProvider_SetValue(char* value);
}
[uuid(b00af5da-78af-4b33-bfff-4ce13a6239a9)]
@@ -821,4 +843,5 @@ interface IAvnAutomationPeerArray : IUnknown
interface IAvnAutomationNode : IUnknown
{
void ChildrenChanged();
+ void PropertyChanged(AvnAutomationProperty property);
}