Browse Source
Make TextBox.PlaceholderText accessible (#20714 )
* Implement -accessibilityPlaceholderValue on macOS
macOS uses a unique -accessibilityPlaceholderValue property for the
placeholder of a text field; -accessibilityHelp has different meaning from
HelpText on Windows. Map TextBox.PlaceholderText to be returned by
-accessibilityPlaceholderValue.
Ref: https://www.w3.org/TR/core-aam-1.2/#ariaPlaceholder
* Use TextBox placeholder as a fallback for HelpText on Windows
This matches the behavior introduced in 8593ef3 on macOS, as this is where
UIA designates that placeholder text should go.
Ref: https://www.w3.org/TR/core-aam-1.2/#ariaPlaceholder
pull/20716/head
Adam Demasi
1 month ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with
36 additions and
2 deletions
native/Avalonia.Native/src/OSX/automation.mm
samples/IntegrationTestApp/Pages/AutomationPage.axaml
src/Avalonia.Controls/Automation/Peers/AutomationPeer.cs
src/Avalonia.Controls/Automation/Peers/ControlAutomationPeer.cs
src/Avalonia.Controls/Automation/Peers/InteropAutomationPeer.cs
src/Avalonia.Controls/Automation/Peers/TextBoxAutomationPeer.cs
src/Avalonia.Native/AvnAutomationPeer.cs
src/Avalonia.Native/avn.idl
@ -220,6 +220,11 @@
return GetNSStringAndRelease(_peer->GetHelpText());
}
- (NSString *)accessibilityPlaceholderValue
{
return GetNSStringAndRelease(_peer->GetPlaceholderText());
}
- (id)accessibilityValue
{
if (_peer->IsRangeValueProvider())
@ -10,7 +10,7 @@
TextBlockWithNameAndAutomationId
</TextBlock>
<TextBlock Name="TextBlockAsLabel">Label for TextBox</TextBlock>
<TextBox Name="LabeledByTextBox" AutomationProperties.LabeledBy="{Binding #TextBlockAsLabel}">
<TextBox Name="LabeledByTextBox" AutomationProperties.LabeledBy="{Binding #TextBlockAsLabel}" PlaceholderText="Placeholder for TextBox" >
Foo
</TextBox>
<TextBlock Name="TextBlockWithHeader1" AutomationProperties.ControlTypeOverride="Header" AutomationProperties.HeadingLevel="1">
@ -283,6 +283,23 @@ namespace Avalonia.Automation.Peers
/// </remarks>
public string GetHelpText ( ) = > GetHelpTextCore ( ) ? ? string . Empty ;
/// <summary>
/// Gets text that provides a placeholder for the element that is associated with this automation peer.
/// </summary>
/// <remarks>
/// <list type="table">
/// <item>
/// <term>Windows</term>
/// <description>No mapping.</description>
/// </item>
/// <item>
/// <term>macOS</term>
/// <description><c>NSAccessibilityProtocol.accessibilityPlaceholderValue</c></description>
/// </item>
/// </list>
/// </remarks>
public string GetPlaceholderText ( ) = > GetPlaceholderTextCore ( ) ? ? string . Empty ;
/// <summary>
/// Gets the control type for the element that is associated with the UI Automation peer.
/// </summary>
@ -595,6 +612,7 @@ namespace Avalonia.Automation.Peers
protected abstract AutomationPeer ? GetLabeledByCore ( ) ;
protected abstract string? GetNameCore ( ) ;
protected virtual string? GetHelpTextCore ( ) = > null ;
protected virtual string? GetPlaceholderTextCore ( ) = > null ;
protected virtual AutomationLandmarkType ? GetLandmarkTypeCore ( ) = > null ;
protected virtual int GetHeadingLevelCore ( ) = > 0 ;
protected virtual string? GetItemTypeCore ( ) = > null ;
@ -132,6 +132,12 @@ namespace Avalonia.Automation.Peers
result = ToolTip . GetTip ( Owner ) as string ;
}
// Windows uses HelpText for placeholder text; macOS uses a separate property.
if ( string . IsNullOrWhiteSpace ( result ) )
{
result = GetPlaceholderTextCore ( ) ;
}
return result ;
}
protected override AutomationLandmarkType ? GetLandmarkTypeCore ( ) = > AutomationProperties . GetLandmarkType ( Owner ) ;
@ -29,6 +29,7 @@ internal class InteropAutomationPeer : AutomationPeer
protected override AutomationPeer ? GetLabeledByCore ( ) = > throw new NotImplementedException ( ) ;
protected override string? GetNameCore ( ) = > throw new NotImplementedException ( ) ;
protected override string? GetHelpTextCore ( ) = > throw new NotImplementedException ( ) ;
protected override string? GetPlaceholderTextCore ( ) = > throw new NotImplementedException ( ) ;
protected override IReadOnlyList < AutomationPeer > GetOrCreateChildrenCore ( ) = > throw new NotImplementedException ( ) ;
protected override AutomationPeer ? GetParentCore ( ) = > _ parent ;
protected override bool HasKeyboardFocusCore ( ) = > throw new NotImplementedException ( ) ;
@ -21,6 +21,8 @@ namespace Avalonia.Automation.Peers
return AutomationControlType . Edit ;
}
protected override string? GetPlaceholderTextCore ( ) = > Owner . PlaceholderText ;
protected virtual void OwnerPropertyChanged ( object? sender , AvaloniaPropertyChangedEventArgs e )
{
if ( e . Property = = TextBox . TextProperty )
@ -46,6 +46,7 @@ namespace Avalonia.Native
public IAvnAutomationPeer ? LabeledBy = > Wrap ( _ inner . GetLabeledBy ( ) ) ;
public IAvnString ? Name = > _ inner . GetName ( ) . ToAvnString ( ) ;
public IAvnString ? HelpText = > _ inner . GetHelpText ( ) . ToAvnString ( ) ;
public IAvnString ? PlaceholderText = > _ inner . GetPlaceholderText ( ) . ToAvnString ( ) ;
public AvnLandmarkType LandmarkType = > ( AvnLandmarkType ? ) _ inner . GetLandmarkType ( ) ? ? AvnLandmarkType . LandmarkNone ;
public int HeadingLevel = > _ inner . GetHeadingLevel ( ) ;
public IAvnAutomationPeer ? Parent = > Wrap ( _ inner . GetParent ( ) ) ;
@ -1328,6 +1328,7 @@ interface IAvnAutomationPeer : IUnknown
void ValueProvider_SetValue([const] char* value);
IAvnString* GetHelpText();
IAvnString* GetPlaceholderText();
AvnLandmarkType GetLandmarkType();
int GetHeadingLevel();