-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Initial AccessibilityInfo implementation #5849
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
74661c0
8ceafe5
3e8aa91
195a2b1
1c67c76
50a0d66
5a54995
f53a2eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "type": "prerelease", | ||
| "comment": "Initial AccessibilityInfo implementation", | ||
| "packageName": "react-native-windows", | ||
| "email": "acoates-ms@noreply.github.com", | ||
| "dependentChangeType": "patch", | ||
| "date": "2020-08-26T18:33:49.434Z" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #include "pch.h" | ||
| #include "AccessibilityInfoModule.h" | ||
| #include <UI.Xaml.Automation.Peers.h> | ||
| #include <UI.Xaml.Controls.h> | ||
| #include <XamlUtils.h> | ||
| #include <uiautomationcore.h> | ||
| #include <uiautomationcoreapi.h> | ||
| #include <winrt/Windows.ApplicationModel.DataTransfer.h> | ||
| #include <winrt/Windows.UI.ViewManagement.h> | ||
| #include "Unicode.h" | ||
| #include "Utils/Helpers.h" | ||
|
|
||
| namespace Microsoft::ReactNative { | ||
|
|
||
| void AccessibilityInfo::Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept { | ||
| m_context = reactContext; | ||
| } | ||
|
|
||
| void AccessibilityInfo::isReduceMotionEnabled(std::function<void(React::JSValue const &)> const &onSuccess) noexcept { | ||
| auto jsDispatcher = m_context.JSDispatcher(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
std::move? |
||
| m_context.UIDispatcher().Post([weakThis = weak_from_this(), jsDispatcher, onSuccess] { | ||
| if (auto strongThis = weakThis.lock()) { | ||
| winrt::Windows::UI::ViewManagement::UISettings uiSettings; | ||
| auto animationsEnabled = uiSettings.AnimationsEnabled(); | ||
| jsDispatcher.Post([animationsEnabled, onSuccess] { onSuccess(!animationsEnabled); }); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| void AccessibilityInfo::isTouchExplorationEnabled( | ||
| std::function<void(React::JSValue const &)> const &onSuccess) noexcept { | ||
| onSuccess(UiaClientsAreListening()); | ||
| } | ||
|
|
||
| void AccessibilityInfo::setAccessibilityFocus(double /*reactTag*/) noexcept { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We can use AutomationPeer::SetFocusCore() to implement this. See:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That moves actual focus to that element. This API is just trying to move the narrator focus. -- So, when running narrator, it would move the blue rectangle without moving keyboard focus. (Otherwise, this API is pointless, since there is a similar API that sets normal focus)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops I didn't realize it's just supposed to move narrator focus. I'm asking around, meanwhile I see some hits in Windows that suggest raising the AutomationFocusChanged event might work (eg %SDXROOT%\xbox\shellapps\Shared\Xbox.Foundation.UI\Narrator\NarratorReader.cpp line 125) In reply to: 481482991 [](ancestors = 481482991) |
||
| // no-op - This appears to be unused in RN | ||
| } | ||
|
|
||
| void AccessibilityInfo::announceForAccessibility(std::string announcement) noexcept { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Similar comment, can this be either && or const& to avoid accidental copying? |
||
| m_context.UIDispatcher().Post([context = m_context, announcement = std::move(announcement)] { | ||
| xaml::UIElement element{nullptr}; | ||
|
|
||
| // Windows requires a specific element to announce from. Unfortunately the react-native API does not provide a tag | ||
| // So we need to find something to try to raise the notification event from | ||
|
|
||
| if (auto window = xaml::Window::Current()) { | ||
| element = window.Content(); | ||
| } | ||
|
|
||
| if (!element && react::uwp::Is19H1OrHigher()) { | ||
| // XamlRoot added in 19H1 | ||
| if (auto xamlRoot = React::XamlUIService::GetXamlRoot(context.Properties().Handle())) { | ||
| element = xamlRoot.Content(); | ||
| } | ||
| } | ||
|
|
||
| if (!element) { | ||
| return; | ||
| } | ||
|
|
||
| auto peer = xaml::Automation::Peers::FrameworkElementAutomationPeer::FromElement(element); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is this still returning null in all cases? If so feel free to file an issue and assign to me, I can look into it. |
||
|
|
||
| if (!peer) { | ||
| return; | ||
| } | ||
|
|
||
| winrt::hstring hstr{Microsoft::Common::Unicode::Utf8ToUtf16(announcement)}; | ||
| peer.RaiseNotificationEvent( | ||
| xaml::Automation::Peers::AutomationNotificationKind::Other, | ||
| xaml::Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent, | ||
| hstr, | ||
| hstr); | ||
| }); | ||
| } | ||
|
|
||
| } // namespace Microsoft::ReactNative | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
| #pragma once | ||
|
|
||
| #include <NativeModules.h> | ||
|
|
||
| namespace Microsoft::ReactNative { | ||
|
|
||
| REACT_MODULE(AccessibilityInfo) | ||
| struct AccessibilityInfo : public std::enable_shared_from_this<AccessibilityInfo> { | ||
| REACT_INIT(Initialize) | ||
| void Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept; | ||
|
|
||
| REACT_METHOD(isReduceMotionEnabled) | ||
| void isReduceMotionEnabled(std::function<void(React::JSValue const &)> const &onSuccess) noexcept; | ||
|
|
||
| REACT_METHOD(isTouchExplorationEnabled) | ||
| void isTouchExplorationEnabled(std::function<void(React::JSValue const &)> const &onSuccess) noexcept; | ||
|
|
||
| REACT_METHOD(setAccessibilityFocus) | ||
| void setAccessibilityFocus(double reactTag) noexcept; | ||
|
|
||
| REACT_METHOD(announceForAccessibility) | ||
| void announceForAccessibility(std::string announcement) noexcept; | ||
|
|
||
| private: | ||
| React::ReactContext m_context; | ||
| }; | ||
|
|
||
| } // namespace Microsoft::ReactNative |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| /** | ||
| * Copyright (c) Microsoft Corporation. | ||
acoates-ms marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * Licensed under the MIT License. | ||
| * @format | ||
| */ | ||
|
|
||
| 'use strict'; | ||
|
|
||
| const React = require('react'); | ||
| const {AccessibilityInfo, Button, Text, View} = require('react-native'); | ||
|
|
||
| function announceSomething() { | ||
| AccessibilityInfo.announceForAccessibility('Something!'); | ||
| } | ||
|
|
||
| function AccessibilityInfoExample(props): React.Node { | ||
| const [isReduceMotionEnabled, setIsReduceMotionEnabled] = React.useState( | ||
| 'unknown', | ||
| ); | ||
| const [isScreenReaderEnabled, setIsScreenReaderEnabled] = React.useState( | ||
| 'unknown', | ||
| ); | ||
|
|
||
| React.useEffect(() => { | ||
| AccessibilityInfo.isReduceMotionEnabled().done(isEnabled => { | ||
| setIsReduceMotionEnabled(isEnabled); | ||
| }); | ||
| AccessibilityInfo.isScreenReaderEnabled().done(isEnabled => { | ||
| setIsScreenReaderEnabled(isEnabled); | ||
| }); | ||
| }, [setIsReduceMotionEnabled, setIsScreenReaderEnabled]); | ||
|
|
||
| return ( | ||
| <View> | ||
| <Text>{`AccessibilityInfo.isReduceMotionEnabled: ${isReduceMotionEnabled}`}</Text> | ||
| <Text>{`AccessibilityInfo.isScreenReaderEnabled: ${isScreenReaderEnabled}`}</Text> | ||
| <Button onPress={announceSomething} title="Announce something" /> | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| exports.title = 'AccessibilityInfo'; | ||
| exports.description = 'Examples of using AccessibilityInfo APIs.'; | ||
| exports.examples = [ | ||
| { | ||
| title: 'Basic AccessibilityInfo', | ||
| render(): React.Element<typeof AccessibilityInfoExample> { | ||
| return <AccessibilityInfoExample />; | ||
| }, | ||
| }, | ||
| ]; | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nit) can this be && to avoid std::function copy here and while passing into further lambdas?