Skip to content
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

Narrator announces changes to accessibilityState #9871

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Added support for Narrator announcing accessibilityState changes",
"packageName": "react-native-windows",
"email": "agnel@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <WindowsNumerics.h>
#include <winrt/Windows.Foundation.h>

#include <UI.Xaml.Automation.Peers.h>
#include <UI.Xaml.Automation.h>
#include <UI.Xaml.Controls.h>
#include "Utils/PropertyHandlerUtils.h"
Expand Down Expand Up @@ -344,20 +345,61 @@ bool FrameworkElementViewManager::UpdateProperty(
const std::string &innerName = pair.first;
const auto &innerValue = pair.second;

if (innerName == "selected")
auto peer = xaml::Automation::Peers::FrameworkElementAutomationPeer::FromElement(element);

if (innerName == "selected") {
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Selected)] =
innerValue.AsBoolean();
else if (innerName == "disabled")
const auto prevSelectedState = DynamicAutomationProperties::GetAccessibilityStateSelected(element);
if (peer != nullptr && prevSelectedState != innerValue.AsBoolean()) {
peer.RaisePropertyChangedEvent(
winrt::SelectionItemPatternIdentifiers::IsSelectedProperty(),
winrt::box_value(prevSelectedState),
winrt::box_value(innerValue.AsBoolean()));
}
} else if (innerName == "disabled") {
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Disabled)] =
innerValue.AsBoolean();
else if (innerName == "checked") {
const auto prevDisabledState = DynamicAutomationProperties::GetAccessibilityStateDisabled(element);

if (peer != nullptr && prevDisabledState != innerValue.AsBoolean()) {
peer.RaisePropertyChangedEvent(
winrt::AutomationElementIdentifiers::IsEnabledProperty(),
winrt::box_value(!prevDisabledState),
winrt::box_value(!innerValue.AsBoolean()));
}
} else if (innerName == "checked") {
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Checked)] =
innerValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean && innerValue.AsBoolean();
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Unchecked)] =
innerValue.Type() == winrt::Microsoft::ReactNative::JSValueType::Boolean && !innerValue.AsBoolean();
// If the state is "mixed" we'll just set both Checked and Unchecked to false,
// then later in the IToggleProvider implementation it will return the Intermediate state
// due to both being set to false (see DynamicAutomationPeer::ToggleState()).
const auto prevCheckedState = DynamicAutomationProperties::GetAccessibilityStateChecked(element);
const auto prevUncheckedState = DynamicAutomationProperties::GetAccessibilityStateUnchecked(element);

if (peer != nullptr) {
if (prevCheckedState !=
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Checked)] ||
prevUncheckedState !=
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Unchecked)]) {
// Checking if either state has changed here to catch changes involving "mixed" state.
const auto oldValue = prevCheckedState ? winrt::ToggleState::On : winrt::ToggleState::Off;
if (innerValue.Type() != winrt::Microsoft::ReactNative::JSValueType::Boolean) {
peer.RaisePropertyChangedEvent(
winrt::TogglePatternIdentifiers::ToggleStateProperty(),
winrt::box_value(oldValue),
winrt::box_value(winrt::ToggleState::Indeterminate));
} else {
const auto newValue = innerValue.AsBoolean() ? winrt::ToggleState::On : winrt::ToggleState::Off;
peer.RaisePropertyChangedEvent(
winrt::TogglePatternIdentifiers::ToggleStateProperty(),
winrt::box_value(oldValue),
winrt::box_value(newValue));
}
}
}
} else if (innerName == "busy")
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Busy)] =
!innerValue.IsNull() && innerValue.AsBoolean();
Expand All @@ -366,6 +408,19 @@ bool FrameworkElementViewManager::UpdateProperty(
!innerValue.IsNull() && innerValue.AsBoolean();
states[static_cast<int32_t>(winrt::Microsoft::ReactNative::AccessibilityStates::Collapsed)] =
innerValue.IsNull() || !innerValue.AsBoolean();

const auto prevExpandedState = DynamicAutomationProperties::GetAccessibilityStateExpanded(element);

if (peer != nullptr && prevExpandedState != innerValue.AsBoolean()) {
const auto newValue =
innerValue.AsBoolean() ? winrt::ExpandCollapseState::Expanded : winrt::ExpandCollapseState::Collapsed;
const auto oldValue =
prevExpandedState ? winrt::ExpandCollapseState::Expanded : winrt::ExpandCollapseState::Collapsed;
peer.RaisePropertyChangedEvent(
winrt::ExpandCollapsePatternIdentifiers::ExpandCollapseStateProperty(),
winrt::box_value(oldValue),
winrt::box_value(newValue));
}
}
}
}
Expand Down