Browse Source

macOS: fix accessibility read-only, selection, and expand/collapse actions (#21599)

* Gate macOS setAccessibilityValue on value read-only state

A read-only value provider must not advertise the setAccessibilityValue selector, so AX clients see the value as not settable.

* Gate macOS read-only range value mutations

Report a read-only IRangeValueProvider (ProgressBar, read-only NumericUpDown) as not settable, and stop advertising setAccessibilityValue/increment/decrement for it; keep min/max exposed.

* Support deselect and toggle expand/collapse in macOS AX

setAccessibilitySelected:NO now removes from selection, and AXPress toggles an expand/collapse provider instead of always expanding.

* Harden macOS AX select, value-set, and disabled-control actions

setAccessibilitySelected:YES now adds to the selection instead of replacing it, the value setter respects read-only, and mutating actions are gated on IsEnabled.
pull/21602/head
Jumar Macato 3 months ago
committed by GitHub
parent
commit
59e7353a45
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 41
      native/Avalonia.Native/src/OSX/automation.mm
  2. 3
      src/Avalonia.Native/AvnAutomationPeer.cs
  3. 5
      src/Avalonia.Native/avn.idl

41
native/Avalonia.Native/src/OSX/automation.mm

@ -260,14 +260,16 @@
- (void)setAccessibilityValue:(id)newValue
{
if (_peer->IsValueProvider())
if (!_peer->IsEnabled())
return;
if (_peer->IsValueProvider() && !_peer->ValueProvider_IsReadOnly())
{
if (newValue == nil)
_peer->ValueProvider_SetValue(nil);
else if ([newValue isKindOfClass:[NSString class]])
_peer->ValueProvider_SetValue([(NSString*)newValue UTF8String]);
}
else if (_peer->IsRangeValueProvider())
else if (_peer->IsRangeValueProvider() && !_peer->RangeValueProvider_IsReadOnly())
{
if ([newValue isKindOfClass:[NSNumber class]])
_peer->RangeValueProvider_SetValue([(NSNumber*)newValue doubleValue]);
@ -281,7 +283,7 @@
if (_peer->IsValueProvider())
return !_peer->ValueProvider_IsReadOnly();
if (_peer->IsRangeValueProvider())
return YES;
return !_peer->RangeValueProvider_IsReadOnly();
return NO;
}
@ -395,7 +397,7 @@
- (void)setAccessibilityExpanded:(BOOL)accessibilityExpanded
{
if (!_peer->IsExpandCollapseProvider())
if (!_peer->IsExpandCollapseProvider() || !_peer->IsEnabled())
return;
if (accessibilityExpanded)
_peer->ExpandCollapseProvider_Expand();
@ -405,13 +407,18 @@
- (BOOL)accessibilityPerformPress
{
if (!_peer->IsEnabled())
return NO;
if (_peer->IsInvokeProvider())
{
_peer->InvokeProvider_Invoke();
}
else if (_peer->IsExpandCollapseProvider())
{
_peer->ExpandCollapseProvider_Expand();
if (_peer->ExpandCollapseProvider_GetIsExpanded())
_peer->ExpandCollapseProvider_Collapse();
else
_peer->ExpandCollapseProvider_Expand();
}
else if (_peer->IsToggleProvider())
{
@ -422,7 +429,7 @@
- (BOOL)accessibilityPerformIncrement
{
if (!_peer->IsRangeValueProvider())
if (!_peer->IsRangeValueProvider() || _peer->RangeValueProvider_IsReadOnly() || !_peer->IsEnabled())
return NO;
auto value = _peer->RangeValueProvider_GetValue();
value += _peer->RangeValueProvider_GetSmallChange();
@ -432,7 +439,7 @@
- (BOOL)accessibilityPerformDecrement
{
if (!_peer->IsRangeValueProvider())
if (!_peer->IsRangeValueProvider() || _peer->RangeValueProvider_IsReadOnly() || !_peer->IsEnabled())
return NO;
auto value = _peer->RangeValueProvider_GetValue();
value -= _peer->RangeValueProvider_GetSmallChange();
@ -442,7 +449,7 @@
- (BOOL)accessibilityPerformShowMenu
{
if (!_peer->IsExpandCollapseProvider())
if (!_peer->IsExpandCollapseProvider() || !_peer->IsEnabled())
return NO;
_peer->ExpandCollapseProvider_Expand();
return YES;
@ -476,15 +483,20 @@
- (void)setAccessibilitySelected:(BOOL)accessibilitySelected
{
if (accessibilitySelected && _peer->IsSelectionItemProvider())
_peer->SelectionItemProvider_Select();
if (!_peer->IsSelectionItemProvider() || !_peer->IsEnabled())
return;
if (accessibilitySelected)
_peer->SelectionItemProvider_AddToSelection();
else
_peer->SelectionItemProvider_RemoveFromSelection();
}
- (BOOL)isAccessibilitySelectorAllowed:(SEL)selector
{
if (selector == @selector(setAccessibilityValue:))
{
return _peer->IsValueProvider() || _peer->IsRangeValueProvider();
return (_peer->IsValueProvider() && !_peer->ValueProvider_IsReadOnly()) ||
(_peer->IsRangeValueProvider() && !_peer->RangeValueProvider_IsReadOnly());
}
else if (selector == @selector(accessibilityPerformShowMenu))
{
@ -503,8 +515,11 @@
return _peer->IsSelectionItemProvider();
}
else if (selector == @selector(accessibilityPerformIncrement) ||
selector == @selector(accessibilityPerformDecrement) ||
selector == @selector(accessibilityMinValue) ||
selector == @selector(accessibilityPerformDecrement))
{
return _peer->IsRangeValueProvider() && !_peer->RangeValueProvider_IsReadOnly();
}
else if (selector == @selector(accessibilityMinValue) ||
selector == @selector(accessibilityMaxValue))
{
return _peer->IsRangeValueProvider();

3
src/Avalonia.Native/AvnAutomationPeer.cs

@ -184,10 +184,13 @@ namespace Avalonia.Native
public double RangeValueProvider_GetSmallChange() => RangeValueProvider.SmallChange;
public double RangeValueProvider_GetLargeChange() => RangeValueProvider.LargeChange;
public void RangeValueProvider_SetValue(double value) => RangeValueProvider.SetValue(value);
public int RangeValueProvider_IsReadOnly() => RangeValueProvider.IsReadOnly.AsComBool();
public int IsSelectionItemProvider() => IsProvider<ISelectionItemProvider>();
public int SelectionItemProvider_IsSelected() => SelectionItemProvider.IsSelected.AsComBool();
public void SelectionItemProvider_Select() => SelectionItemProvider.Select();
public void SelectionItemProvider_AddToSelection() => SelectionItemProvider.AddToSelection();
public void SelectionItemProvider_RemoveFromSelection() => SelectionItemProvider.RemoveFromSelection();
public IAvnAutomationPeer? ScrollProvider_GetHorizontalScrollBar()
=> _inner is ScrollViewerAutomationPeer scrollViewer ? Wrap(scrollViewer.GetHorizontalScrollBarPeer()) : null;

5
src/Avalonia.Native/avn.idl

@ -1315,10 +1315,13 @@ interface IAvnAutomationPeer : IUnknown
double RangeValueProvider_GetSmallChange();
double RangeValueProvider_GetLargeChange();
void RangeValueProvider_SetValue(double value);
bool RangeValueProvider_IsReadOnly();
bool IsSelectionItemProvider();
bool SelectionItemProvider_IsSelected();
void SelectionItemProvider_Select();
void SelectionItemProvider_AddToSelection();
void SelectionItemProvider_RemoveFromSelection();
IAvnAutomationPeer* ScrollProvider_GetHorizontalScrollBar();
IAvnAutomationPeer* ScrollProvider_GetVerticalScrollBar();

Loading…
Cancel
Save