Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 470a18a

Browse files
committed
Adds fuchsia node roles to accessibility bridge updates.
1 parent 49f647f commit 470a18a

File tree

6 files changed

+113
-2
lines changed

6 files changed

+113
-2
lines changed

lib/ui/semantics.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ class SemanticsFlag {
300300
static const int _kIsReadOnlyIndex = 1 << 20;
301301
static const int _kIsFocusableIndex = 1 << 21;
302302
static const int _kIsLinkIndex = 1 << 22;
303+
static const int _kIsSliderIndex = 1 << 23;
303304
// READ THIS: if you add a flag here, you MUST update the numSemanticsFlags
304305
// value in testing/dart/semantics_test.dart, or tests will fail.
305306

@@ -355,6 +356,9 @@ class SemanticsFlag {
355356
/// affordances.
356357
static const SemanticsFlag isTextField = SemanticsFlag._(_kIsTextFieldIndex);
357358

359+
/// Whether the semantic node represents a slider.
360+
static const SemanticsFlag isSlider = SemanticsFlag._(_kIsSliderIndex);
361+
358362
/// Whether the semantic node is read only.
359363
///
360364
/// Only applicable when [isTextField] is true.
@@ -551,7 +555,8 @@ class SemanticsFlag {
551555
_kIsReadOnlyIndex: isReadOnly,
552556
_kIsFocusableIndex: isFocusable,
553557
_kIsLinkIndex: isLink,
554-
};
558+
_kIsSliderIndex: isSlider,
559+
};
555560

556561
@override
557562
String toString() {
@@ -602,6 +607,8 @@ class SemanticsFlag {
602607
return 'SemanticsFlag.isFocusable';
603608
case _kIsLinkIndex:
604609
return 'SemanticsFlag.isLink';
610+
case _kIsSliderIndex:
611+
return 'SemanticsFlag.isSlider';
605612
}
606613
assert(false, 'Unhandled index: $index');
607614
return '';

lib/ui/semantics/semantics_node.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ enum class SemanticsFlags : int32_t {
7979
kIsReadOnly = 1 << 20,
8080
kIsFocusable = 1 << 21,
8181
kIsLink = 1 << 22,
82+
kIsSlider = 1 << 23,
8283
};
8384

8485
const int kScrollableSemanticsFlags =

shell/platform/fuchsia/flutter/accessibility_bridge.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,30 @@ fuchsia::accessibility::semantics::States AccessibilityBridge::GetNodeStates(
115115
return states;
116116
}
117117

118+
fuchsia::accessibility::semantics::Role AccessibilityBridge::GetNodeRole(const flutter::SemanticsNode& node) const {
119+
if (node.HasFlag(flutter::SemanticsFlags::kIsButton)) {
120+
return fuchsia::accessibility::semantics::Role::BUTTON;
121+
}
122+
123+
if (node.HasFlag(flutter::SemanticsFlags::kIsHeader)) {
124+
return fuchsia::accessibility::semantics::Role::HEADER;
125+
}
126+
127+
if (node.HasFlag(flutter::SemanticsFlags::kIsImage)) {
128+
return fuchsia::accessibility::semantics::Role::IMAGE;
129+
}
130+
131+
if (node.HasFlag(flutter::SemanticsFlags::kIsTextField)) {
132+
return fuchsia::accessibility::semantics::Role::TEXT_FIELD;
133+
}
134+
135+
if (node.HasFlag(flutter::SemanticsFlags::kIsSlider)) {
136+
return fuchsia::accessibility::semantics::Role::SLIDER;
137+
}
138+
139+
return fuchsia::accessibility::semantics::Role::UNKNOWN;
140+
}
141+
118142
std::unordered_set<int32_t> AccessibilityBridge::GetDescendants(
119143
int32_t node_id) const {
120144
std::unordered_set<int32_t> descendents;
@@ -227,6 +251,7 @@ void AccessibilityBridge::AddSemanticsNodeUpdate(
227251
.set_transform(GetNodeTransform(flutter_node))
228252
.set_attributes(GetNodeAttributes(flutter_node, &this_node_size))
229253
.set_states(GetNodeStates(flutter_node, &this_node_size))
254+
.set_role(GetNodeRole(flutter_node))
230255
.set_child_ids(child_ids);
231256
this_node_size +=
232257
kNodeIdSize * flutter_node.childrenInTraversalOrder.size();

shell/platform/fuchsia/flutter/accessibility_bridge.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ class AccessibilityBridge
145145
const flutter::SemanticsNode& node,
146146
size_t* additional_size) const;
147147

148+
// Derives the role for a Fuchsia semantics node from a Flutter semantics
149+
// node.
150+
fuchsia::accessibility::semantics::Role GetNodeRole(
151+
const flutter::SemanticsNode& node) const;
152+
148153
// Gets the set of reachable descendants from the given node id.
149154
std::unordered_set<int32_t> GetDescendants(int32_t node_id) const;
150155

shell/platform/fuchsia/flutter/accessibility_bridge_unittest.cc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@
1919

2020
namespace flutter_runner_test {
2121

22+
namespace {
23+
24+
void ExpectNodeHasRole(const fuchsia::accessibility::semantics::Node& node,
25+
const std::unordered_map<uint32_t, fuchsia::accessibility::semantics::Role> roles_by_node_id) {
26+
ASSERT_TRUE(node.has_node_id());
27+
ASSERT_NE(roles_by_node_id.find(node.node_id()), roles_by_node_id.end());
28+
EXPECT_TRUE(node.has_role());
29+
EXPECT_EQ(node.role(), roles_by_node_id.at(node.node_id()));
30+
}
31+
32+
} // namespace
33+
2234
class AccessibilityBridgeTestDelegate
2335
: public flutter_runner::AccessibilityBridge::Delegate {
2436
public:
@@ -89,6 +101,67 @@ TEST_F(AccessibilityBridgeTest, EnableDisable) {
89101
EXPECT_TRUE(accessibility_delegate_.enabled());
90102
}
91103

104+
TEST_F(AccessibilityBridgeTest, UpdatesNodeRoles) {
105+
flutter::SemanticsNodeUpdates updates;
106+
107+
flutter::SemanticsNode node0;
108+
node0.id = 0;
109+
node0.flags |= static_cast<int>(flutter::SemanticsFlags::kIsButton);
110+
node0.childrenInTraversalOrder = {1, 2, 3, 4};
111+
node0.childrenInHitTestOrder = {1, 2, 3, 4};
112+
updates.emplace(0, node0);
113+
114+
flutter::SemanticsNode node1;
115+
node1.id = 1;
116+
node1.flags |= static_cast<int>(flutter::SemanticsFlags::kIsHeader);
117+
node1.childrenInTraversalOrder = {};
118+
node1.childrenInHitTestOrder = {};
119+
updates.emplace(1, node1);
120+
121+
flutter::SemanticsNode node2;
122+
node2.id = 2;
123+
node2.flags |= static_cast<int>(flutter::SemanticsFlags::kIsImage);
124+
node2.childrenInTraversalOrder = {};
125+
node2.childrenInHitTestOrder = {};
126+
updates.emplace(2, node2);
127+
128+
flutter::SemanticsNode node3;
129+
node3.id = 3;
130+
node3.flags |= static_cast<int>(flutter::SemanticsFlags::kIsTextField);
131+
node3.childrenInTraversalOrder = {};
132+
node3.childrenInHitTestOrder = {};
133+
updates.emplace(3, node3);
134+
135+
flutter::SemanticsNode node4;
136+
node4.childrenInTraversalOrder = {};
137+
node4.childrenInHitTestOrder = {};
138+
node4.id = 4;
139+
node4.flags |= static_cast<int>(flutter::SemanticsFlags::kIsSlider);
140+
updates.emplace(4, node4);
141+
142+
accessibility_bridge_->AddSemanticsNodeUpdate(std::move(updates));
143+
RunLoopUntilIdle();
144+
145+
std::unordered_map<uint32_t, fuchsia::accessibility::semantics::Role>
146+
roles_by_node_id = {
147+
{0u, fuchsia::accessibility::semantics::Role::BUTTON},
148+
{1u, fuchsia::accessibility::semantics::Role::HEADER},
149+
{2u, fuchsia::accessibility::semantics::Role::IMAGE},
150+
{3u, fuchsia::accessibility::semantics::Role::TEXT_FIELD},
151+
{4u, fuchsia::accessibility::semantics::Role::SLIDER}};
152+
153+
EXPECT_EQ(0, semantics_manager_.DeleteCount());
154+
EXPECT_EQ(1, semantics_manager_.UpdateCount());
155+
EXPECT_EQ(1, semantics_manager_.CommitCount());
156+
EXPECT_EQ(5U, semantics_manager_.LastUpdatedNodes().size());
157+
for (const auto& node : semantics_manager_.LastUpdatedNodes()) {
158+
ExpectNodeHasRole(node, roles_by_node_id);
159+
}
160+
161+
EXPECT_FALSE(semantics_manager_.DeleteOverflowed());
162+
EXPECT_FALSE(semantics_manager_.UpdateOverflowed());
163+
}
164+
92165
TEST_F(AccessibilityBridgeTest, DeletesChildrenTransitively) {
93166
// Test that when a node is deleted, so are its transitive children.
94167
flutter::SemanticsNode node2;

testing/dart/semantics_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
99
/// Verifies Semantics flags and actions.
1010
void main() {
1111
// This must match the number of flags in lib/ui/semantics.dart
12-
const int numSemanticsFlags = 23;
12+
const int numSemanticsFlags = 24;
1313
test('SemanticsFlag.values refers to all flags.', () async {
1414
expect(SemanticsFlag.values.length, equals(numSemanticsFlags));
1515
for (int index = 0; index < numSemanticsFlags; ++index) {

0 commit comments

Comments
 (0)