mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Adds fuchsia node roles to accessibility bridge updates. (#20385)
This commit is contained in:
parent
95f2b72728
commit
58a6207655
@ -300,8 +300,12 @@ class SemanticsFlag {
|
||||
static const int _kIsReadOnlyIndex = 1 << 20;
|
||||
static const int _kIsFocusableIndex = 1 << 21;
|
||||
static const int _kIsLinkIndex = 1 << 22;
|
||||
static const int _kIsSliderIndex = 1 << 23;
|
||||
// READ THIS: if you add a flag here, you MUST update the numSemanticsFlags
|
||||
// value in testing/dart/semantics_test.dart, or tests will fail.
|
||||
// value in testing/dart/semantics_test.dart, or tests will fail. Also,
|
||||
// please update the Flag enum in
|
||||
// flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java,
|
||||
// and the SemanticsFlag class in lib/web_ui/lib/src/ui/semantics.dart.
|
||||
|
||||
const SemanticsFlag._(this.index) : assert(index != null); // ignore: unnecessary_null_comparison
|
||||
|
||||
@ -355,6 +359,9 @@ class SemanticsFlag {
|
||||
/// affordances.
|
||||
static const SemanticsFlag isTextField = SemanticsFlag._(_kIsTextFieldIndex);
|
||||
|
||||
/// Whether the semantic node represents a slider.
|
||||
static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex);
|
||||
|
||||
/// Whether the semantic node is read only.
|
||||
///
|
||||
/// Only applicable when [isTextField] is true.
|
||||
@ -551,7 +558,8 @@ class SemanticsFlag {
|
||||
_kIsReadOnlyIndex: isReadOnly,
|
||||
_kIsFocusableIndex: isFocusable,
|
||||
_kIsLinkIndex: isLink,
|
||||
};
|
||||
_kIsSliderIndex: isSlider,
|
||||
};
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
@ -602,6 +610,8 @@ class SemanticsFlag {
|
||||
return 'SemanticsFlag.isFocusable';
|
||||
case _kIsLinkIndex:
|
||||
return 'SemanticsFlag.isLink';
|
||||
case _kIsSliderIndex:
|
||||
return 'SemanticsFlag.isSlider';
|
||||
}
|
||||
assert(false, 'Unhandled index: $index');
|
||||
return '';
|
||||
|
||||
@ -79,6 +79,7 @@ enum class SemanticsFlags : int32_t {
|
||||
kIsReadOnly = 1 << 20,
|
||||
kIsFocusable = 1 << 21,
|
||||
kIsLink = 1 << 22,
|
||||
kIsSlider = 1 << 23,
|
||||
};
|
||||
|
||||
const int kScrollableSemanticsFlags =
|
||||
|
||||
@ -156,6 +156,7 @@ class SemanticsFlag {
|
||||
static const int _kIsReadOnlyIndex = 1 << 20;
|
||||
static const int _kIsFocusableIndex = 1 << 21;
|
||||
static const int _kIsLinkIndex = 1 << 22;
|
||||
static const int _kIsSliderIndex = 1 << 23;
|
||||
|
||||
const SemanticsFlag._(this.index) : assert(index != null); // ignore: unnecessary_null_comparison
|
||||
final int index;
|
||||
@ -182,12 +183,14 @@ class SemanticsFlag {
|
||||
static const SemanticsFlag isToggled = SemanticsFlag._(_kIsToggledIndex);
|
||||
static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._(_kHasImplicitScrollingIndex);
|
||||
static const SemanticsFlag isMultiline = SemanticsFlag._(_kIsMultilineIndex);
|
||||
static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex);
|
||||
static const Map<int, SemanticsFlag> values = <int, SemanticsFlag>{
|
||||
_kHasCheckedStateIndex: hasCheckedState,
|
||||
_kIsCheckedIndex: isChecked,
|
||||
_kIsSelectedIndex: isSelected,
|
||||
_kIsButtonIndex: isButton,
|
||||
_kIsLinkIndex: isLink,
|
||||
_kIsSliderIndex: isSlider,
|
||||
_kIsTextFieldIndex: isTextField,
|
||||
_kIsFocusableIndex: isFocusable,
|
||||
_kIsFocusedIndex: isFocused,
|
||||
|
||||
@ -115,6 +115,31 @@ fuchsia::accessibility::semantics::States AccessibilityBridge::GetNodeStates(
|
||||
return states;
|
||||
}
|
||||
|
||||
fuchsia::accessibility::semantics::Role AccessibilityBridge::GetNodeRole(
|
||||
const flutter::SemanticsNode& node) const {
|
||||
if (node.HasFlag(flutter::SemanticsFlags::kIsButton)) {
|
||||
return fuchsia::accessibility::semantics::Role::BUTTON;
|
||||
}
|
||||
|
||||
if (node.HasFlag(flutter::SemanticsFlags::kIsHeader)) {
|
||||
return fuchsia::accessibility::semantics::Role::HEADER;
|
||||
}
|
||||
|
||||
if (node.HasFlag(flutter::SemanticsFlags::kIsImage)) {
|
||||
return fuchsia::accessibility::semantics::Role::IMAGE;
|
||||
}
|
||||
|
||||
if (node.HasFlag(flutter::SemanticsFlags::kIsTextField)) {
|
||||
return fuchsia::accessibility::semantics::Role::TEXT_FIELD;
|
||||
}
|
||||
|
||||
if (node.HasFlag(flutter::SemanticsFlags::kIsSlider)) {
|
||||
return fuchsia::accessibility::semantics::Role::SLIDER;
|
||||
}
|
||||
|
||||
return fuchsia::accessibility::semantics::Role::UNKNOWN;
|
||||
}
|
||||
|
||||
std::unordered_set<int32_t> AccessibilityBridge::GetDescendants(
|
||||
int32_t node_id) const {
|
||||
std::unordered_set<int32_t> descendents;
|
||||
@ -227,6 +252,7 @@ void AccessibilityBridge::AddSemanticsNodeUpdate(
|
||||
.set_transform(GetNodeTransform(flutter_node))
|
||||
.set_attributes(GetNodeAttributes(flutter_node, &this_node_size))
|
||||
.set_states(GetNodeStates(flutter_node, &this_node_size))
|
||||
.set_role(GetNodeRole(flutter_node))
|
||||
.set_child_ids(child_ids);
|
||||
this_node_size +=
|
||||
kNodeIdSize * flutter_node.childrenInTraversalOrder.size();
|
||||
|
||||
@ -145,6 +145,11 @@ class AccessibilityBridge
|
||||
const flutter::SemanticsNode& node,
|
||||
size_t* additional_size) const;
|
||||
|
||||
// Derives the role for a Fuchsia semantics node from a Flutter semantics
|
||||
// node.
|
||||
fuchsia::accessibility::semantics::Role GetNodeRole(
|
||||
const flutter::SemanticsNode& node) const;
|
||||
|
||||
// Gets the set of reachable descendants from the given node id.
|
||||
std::unordered_set<int32_t> GetDescendants(int32_t node_id) const;
|
||||
|
||||
|
||||
@ -19,6 +19,20 @@
|
||||
|
||||
namespace flutter_runner_test {
|
||||
|
||||
namespace {
|
||||
|
||||
void ExpectNodeHasRole(
|
||||
const fuchsia::accessibility::semantics::Node& node,
|
||||
const std::unordered_map<uint32_t, fuchsia::accessibility::semantics::Role>
|
||||
roles_by_node_id) {
|
||||
ASSERT_TRUE(node.has_node_id());
|
||||
ASSERT_NE(roles_by_node_id.find(node.node_id()), roles_by_node_id.end());
|
||||
EXPECT_TRUE(node.has_role());
|
||||
EXPECT_EQ(node.role(), roles_by_node_id.at(node.node_id()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class AccessibilityBridgeTestDelegate
|
||||
: public flutter_runner::AccessibilityBridge::Delegate {
|
||||
public:
|
||||
@ -89,6 +103,67 @@ TEST_F(AccessibilityBridgeTest, EnableDisable) {
|
||||
EXPECT_TRUE(accessibility_delegate_.enabled());
|
||||
}
|
||||
|
||||
TEST_F(AccessibilityBridgeTest, UpdatesNodeRoles) {
|
||||
flutter::SemanticsNodeUpdates updates;
|
||||
|
||||
flutter::SemanticsNode node0;
|
||||
node0.id = 0;
|
||||
node0.flags |= static_cast<int>(flutter::SemanticsFlags::kIsButton);
|
||||
node0.childrenInTraversalOrder = {1, 2, 3, 4};
|
||||
node0.childrenInHitTestOrder = {1, 2, 3, 4};
|
||||
updates.emplace(0, node0);
|
||||
|
||||
flutter::SemanticsNode node1;
|
||||
node1.id = 1;
|
||||
node1.flags |= static_cast<int>(flutter::SemanticsFlags::kIsHeader);
|
||||
node1.childrenInTraversalOrder = {};
|
||||
node1.childrenInHitTestOrder = {};
|
||||
updates.emplace(1, node1);
|
||||
|
||||
flutter::SemanticsNode node2;
|
||||
node2.id = 2;
|
||||
node2.flags |= static_cast<int>(flutter::SemanticsFlags::kIsImage);
|
||||
node2.childrenInTraversalOrder = {};
|
||||
node2.childrenInHitTestOrder = {};
|
||||
updates.emplace(2, node2);
|
||||
|
||||
flutter::SemanticsNode node3;
|
||||
node3.id = 3;
|
||||
node3.flags |= static_cast<int>(flutter::SemanticsFlags::kIsTextField);
|
||||
node3.childrenInTraversalOrder = {};
|
||||
node3.childrenInHitTestOrder = {};
|
||||
updates.emplace(3, node3);
|
||||
|
||||
flutter::SemanticsNode node4;
|
||||
node4.childrenInTraversalOrder = {};
|
||||
node4.childrenInHitTestOrder = {};
|
||||
node4.id = 4;
|
||||
node4.flags |= static_cast<int>(flutter::SemanticsFlags::kIsSlider);
|
||||
updates.emplace(4, node4);
|
||||
|
||||
accessibility_bridge_->AddSemanticsNodeUpdate(std::move(updates));
|
||||
RunLoopUntilIdle();
|
||||
|
||||
std::unordered_map<uint32_t, fuchsia::accessibility::semantics::Role>
|
||||
roles_by_node_id = {
|
||||
{0u, fuchsia::accessibility::semantics::Role::BUTTON},
|
||||
{1u, fuchsia::accessibility::semantics::Role::HEADER},
|
||||
{2u, fuchsia::accessibility::semantics::Role::IMAGE},
|
||||
{3u, fuchsia::accessibility::semantics::Role::TEXT_FIELD},
|
||||
{4u, fuchsia::accessibility::semantics::Role::SLIDER}};
|
||||
|
||||
EXPECT_EQ(0, semantics_manager_.DeleteCount());
|
||||
EXPECT_EQ(1, semantics_manager_.UpdateCount());
|
||||
EXPECT_EQ(1, semantics_manager_.CommitCount());
|
||||
EXPECT_EQ(5U, semantics_manager_.LastUpdatedNodes().size());
|
||||
for (const auto& node : semantics_manager_.LastUpdatedNodes()) {
|
||||
ExpectNodeHasRole(node, roles_by_node_id);
|
||||
}
|
||||
|
||||
EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
|
||||
EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
|
||||
}
|
||||
|
||||
TEST_F(AccessibilityBridgeTest, DeletesChildrenTransitively) {
|
||||
// Test that when a node is deleted, so are its transitive children.
|
||||
flutter::SemanticsNode node2;
|
||||
|
||||
@ -9,7 +9,7 @@ import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
|
||||
/// Verifies Semantics flags and actions.
|
||||
void main() {
|
||||
// This must match the number of flags in lib/ui/semantics.dart
|
||||
const int numSemanticsFlags = 23;
|
||||
const int numSemanticsFlags = 24;
|
||||
test('SemanticsFlag.values refers to all flags.', () async {
|
||||
expect(SemanticsFlag.values.length, equals(numSemanticsFlags));
|
||||
for (int index = 0; index < numSemanticsFlags; ++index) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user