Skip to content

Commit

Permalink
Narrator announces changes to accessibilityState (#9871)
Browse files Browse the repository at this point in the history
* announcing

* only announce when value is different

* refactor

* account for mixed state in checked

* Revert "account for mixed state in checked", fix formatting

This reverts commit d39db4d.

* mixed state redo

* Change files

* disabled negation fix

* disabled

* address feedback
  • Loading branch information
AgneLukoseviciute authored Apr 21, 2022
1 parent 8c37ad2 commit aa28258
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 3 deletions.
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"
}
61 changes: 58 additions & 3 deletions vnext/Microsoft.ReactNative/Views/FrameworkElementViewManager.cpp
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 @@ -416,20 +417,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 @@ -438,6 +480,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

0 comments on commit aa28258

Please sign in to comment.