Skip to content

Commit 5b9f6a7

Browse files
[Cherry pick 14995 15023 15088] Latest Accessibility Fixes for Fabric (#15105)
* [Fabric] Implement announceForAccessibility in AccessibilityInfo Module (#14995) * Implementation of announce for accessibility for fabric * Build fix * Removed Debug Message * Yarn Change * Copilot Suggestions * Review Changes * Storing the weak_ref island in property to avoid circular ref * Adding deleted Change Files * [Fabric] Raising UIA Event if Toggle State Changes in Switch Component (#15023) * Update Toggle State to Narrator * Yarn Change * Review Changes * Review Changes 2.0 * [Fabric] Fix for Text and TextInput focus issue with screen readers (#15088) * Few more implementation of TextRange Provider * Yarn change * Update Change file
1 parent 9bc8710 commit 5b9f6a7

10 files changed

+131
-9
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[Fabric] Fix for Text and TextInput focus issue with screen readers.",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[Fabric] Implement announceForAccessibility in AccessibilityInfo Module",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[Fabric] Raising UIA Event if Toggle State Changes in Switch Component",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Fabric/Composition/CompositionTextRangeProvider.cpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,30 @@ CompositionTextRangeProvider::CompositionTextRangeProvider(
1818
}
1919

2020
HRESULT __stdcall CompositionTextRangeProvider::Clone(ITextRangeProvider **pRetVal) {
21-
// no-op
22-
*pRetVal = nullptr;
21+
if (pRetVal == nullptr)
22+
return E_POINTER;
23+
24+
auto clone = winrt::make<winrt::Microsoft::ReactNative::implementation::CompositionTextRangeProvider>(
25+
m_view.view().as<winrt::Microsoft::ReactNative::Composition::ComponentView>(), m_parentProvider.get());
26+
*pRetVal = clone.detach();
2327
return S_OK;
2428
}
2529

2630
HRESULT __stdcall CompositionTextRangeProvider::Compare(ITextRangeProvider *range, BOOL *pRetVal) {
27-
// no-op
28-
*pRetVal = false;
31+
if (pRetVal == nullptr)
32+
return E_POINTER;
33+
if (range == nullptr) {
34+
*pRetVal = FALSE;
35+
return S_OK;
36+
}
37+
38+
// Try to cast to our type , considering provider only supports a single range per view
39+
auto other = dynamic_cast<CompositionTextRangeProvider *>(range);
40+
if (other && other->m_view.view() == m_view.view()) {
41+
*pRetVal = TRUE;
42+
} else {
43+
*pRetVal = FALSE;
44+
}
2945
return S_OK;
3046
}
3147

@@ -34,7 +50,10 @@ HRESULT __stdcall CompositionTextRangeProvider::CompareEndpoints(
3450
ITextRangeProvider *targetRange,
3551
TextPatternRangeEndpoint targetEndpoint,
3652
int *pRetVal) {
37-
// no-op
53+
if (pRetVal == nullptr)
54+
return E_POINTER;
55+
56+
// For a single-range provider, always equal:
3857
*pRetVal = 0;
3958
return S_OK;
4059
}
@@ -98,13 +117,13 @@ HRESULT __stdcall CompositionTextRangeProvider::GetAttributeValue(TEXTATTRIBUTEI
98117
textTransform = props->textAttributes.textTransform.value();
99118
}
100119
if (fontVariant == facebook::react::FontVariant::SmallCaps) {
101-
return CapStyle_SmallCap;
120+
pRetVal->lVal = CapStyle_SmallCap;
102121
} else if (textTransform == facebook::react::TextTransform::Capitalize) {
103-
return CapStyle_Titling;
122+
pRetVal->lVal = CapStyle_Titling;
104123
} else if (textTransform == facebook::react::TextTransform::Lowercase) {
105-
return CapStyle_None;
124+
pRetVal->lVal = CapStyle_None;
106125
} else if (textTransform == facebook::react::TextTransform::Uppercase) {
107-
return CapStyle_AllCap;
126+
pRetVal->lVal = CapStyle_AllCap;
108127
}
109128
} else if (attributeId == UIA_FontNameAttributeId) {
110129
pRetVal->vt = VT_BSTR;
@@ -282,6 +301,8 @@ HRESULT __stdcall CompositionTextRangeProvider::ScrollIntoView(BOOL alignToTop)
282301
return S_OK;
283302
}
284303

304+
// All the below methods should be implemented once the selection comes for paragraph and TextInput
305+
285306
HRESULT __stdcall CompositionTextRangeProvider::AddToSelection() {
286307
// no-op
287308
return S_OK;

vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@
4040

4141
namespace winrt::Microsoft::ReactNative::implementation {
4242

43+
ReactPropertyId<winrt::Microsoft::ReactNative::ReactNonAbiValue<
44+
winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>>>
45+
ReactNativeIsland::LastFocusedReactNativeIslandProperty() noexcept {
46+
static const ReactPropertyId<winrt::Microsoft::ReactNative::ReactNonAbiValue<
47+
winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>>>
48+
prop{L"ReactNative.Composition", L"ReactNativeIsland"};
49+
return prop;
50+
}
4351
constexpr float loadingActivitySize = 12.0f;
4452
constexpr float loadingActivityHorizontalOffset = 16.0f;
4553
constexpr float loadingBarHeight = 36.0f;
@@ -861,6 +869,20 @@ winrt::Microsoft::UI::Content::ContentIsland ReactNativeIsland::Island() {
861869
}
862870
}
863871
});
872+
focusController.GotFocus(
873+
[weakThis = get_weak()](const auto &sender, const winrt::Microsoft::UI::Input::FocusChangedEventArgs &args) {
874+
if (auto pThis = weakThis.get()) {
875+
// Set the island to React context so it can be accessed by native modules
876+
if (pThis->m_context && pThis->m_island) {
877+
auto properties = pThis->m_context.Properties();
878+
properties.Set(
879+
ReactNativeIsland::LastFocusedReactNativeIslandProperty(),
880+
winrt::Microsoft::ReactNative::ReactNonAbiValue<
881+
winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>>{
882+
std::in_place, weakThis});
883+
}
884+
}
885+
});
864886

865887
// ContentIsland does not support weak_ref, so we cannot use auto_revoke for these events
866888
m_islandAutomationProviderRequestedToken = m_island.AutomationProviderRequested(

vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ struct ReactNativeIsland
4949
~ReactNativeIsland() noexcept;
5050

5151
ReactNativeIsland(const winrt::Microsoft::UI::Composition::Compositor &compositor) noexcept;
52+
static ReactPropertyId<winrt::Microsoft::ReactNative::ReactNonAbiValue<
53+
winrt::weak_ref<winrt::Microsoft::ReactNative::implementation::ReactNativeIsland>>>
54+
LastFocusedReactNativeIslandProperty() noexcept;
5255
ReactNativeIsland(const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portal) noexcept;
5356

5457
static winrt::Microsoft::ReactNative::ReactNativeIsland CreatePortal(

vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <Fabric/AbiViewProps.h>
1010
#include "CompositionDynamicAutomationProvider.h"
1111
#include "RootComponentView.h"
12+
#include "UiaHelpers.h"
1213

1314
namespace winrt::Microsoft::ReactNative::Composition::implementation {
1415

@@ -80,6 +81,16 @@ void SwitchComponentView::updateProps(
8081
m_visualUpdateRequired = true;
8182
}
8283

84+
if (oldViewProps.value != newViewProps.value) {
85+
if (UiaClientsAreListening()) {
86+
winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty(
87+
EnsureUiaProvider(),
88+
UIA_ToggleToggleStatePropertyId,
89+
oldViewProps.value ? ToggleState_On : ToggleState_Off,
90+
newViewProps.value ? ToggleState_On : ToggleState_Off);
91+
}
92+
}
93+
8394
Super::updateProps(props, oldProps);
8495
}
8596

vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ void UpdateUiaProperty(winrt::IInspectable provider, PROPERTYID propId, bool old
166166
UiaRaiseAutomationPropertyChangedEvent(spProviderSimple.get(), propId, CComVariant(oldValue), CComVariant(newValue));
167167
}
168168

169+
void UpdateUiaProperty(winrt::IInspectable provider, PROPERTYID propId, int oldValue, int newValue) noexcept {
170+
auto spProviderSimple = provider.try_as<IRawElementProviderSimple>();
171+
172+
if (spProviderSimple == nullptr || oldValue == newValue || !WasUiaPropertyAdvised(spProviderSimple, propId))
173+
return;
174+
175+
UiaRaiseAutomationPropertyChangedEvent(spProviderSimple.get(), propId, CComVariant(oldValue), CComVariant(newValue));
176+
}
177+
169178
void UpdateUiaProperty(
170179
winrt::IInspectable provider,
171180
PROPERTYID propId,

vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ void UpdateUiaProperty(
2929
bool oldValue,
3030
bool newValue) noexcept;
3131

32+
void UpdateUiaProperty(
33+
winrt::Windows::Foundation::IInspectable provider,
34+
PROPERTYID propId,
35+
int oldValue,
36+
int newValue) noexcept;
37+
3238
void UpdateUiaProperty(
3339
winrt::Windows::Foundation::IInspectable provider,
3440
PROPERTYID propId,

vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <UI.Xaml.Automation.Peers.h>
88
#include <UI.Xaml.Controls.h>
99
#include <XamlUtils.h>
10+
#else
11+
#include <Fabric/Composition/ReactNativeIsland.h>
1012
#endif
1113
#include <uiautomationcore.h>
1214
#include <uiautomationcoreapi.h>
@@ -79,6 +81,33 @@ void AccessibilityInfo::announceForAccessibility(std::wstring announcement) noex
7981
xaml::Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent,
8082
hstr,
8183
hstr);
84+
#else
85+
if (auto weakIslandWrapper = context.Properties().Get(
86+
winrt::Microsoft::ReactNative::implementation::ReactNativeIsland::LastFocusedReactNativeIslandProperty())) {
87+
if (auto weakIsland = weakIslandWrapper.Value()) {
88+
if (auto reactNativeIsland = weakIsland.get()) {
89+
if (auto uiaprovider = reactNativeIsland->GetUiaProvider()) {
90+
if (auto rawProvider = uiaprovider.try_as<IRawElementProviderSimple>()) {
91+
// Convert announcement to BSTR for UIA
92+
winrt::hstring hstrAnnouncement{announcement};
93+
auto bstrAnnouncement = SysAllocString(hstrAnnouncement.c_str());
94+
if (bstrAnnouncement) {
95+
// Raise the UIA notification event
96+
HRESULT hr = UiaRaiseNotificationEvent(
97+
rawProvider.get(),
98+
NotificationKind_Other,
99+
NotificationProcessing_ImportantMostRecent,
100+
bstrAnnouncement,
101+
bstrAnnouncement);
102+
// Clean up BSTRs
103+
SysFreeString(bstrAnnouncement);
104+
}
105+
}
106+
}
107+
}
108+
}
109+
}
110+
82111
#endif
83112
});
84113
}

0 commit comments

Comments
 (0)