blob: 677a11acf8f767ebeefc1e3347318bc7c8ffcb97 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "garnet/bin/a11y/a11y_manager/semantic_tree.h"
namespace a11y_manager {
// Internal helper function to check if a point is within a bounding box.
bool BoxContainsPoint(fuchsia::ui::gfx::BoundingBox& box, escher::vec2& point) {
return box.min.x <= point.x && box.max.x >= point.x && box.min.y <= point.y &&
box.max.y >= point.y;
}
void SemanticTree::AddBinding(
fidl::InterfaceRequest<fuchsia::accessibility::SemanticsRoot> request) {
bindings_.AddBinding(this, std::move(request));
}
fuchsia::accessibility::NodePtr SemanticTree::GetHitAccessibilityNode(
zx_koid_t view_id, fuchsia::math::PointF point) {
auto it = nodes_.find(view_id);
if (it == nodes_.end()) {
return nullptr;
}
escher::vec4 coordinate(point.x, point.y, 0, 1);
auto found_node = HitTest(it->second, 0, coordinate);
if (found_node == nullptr) {
return nullptr;
}
auto node_ptr = fuchsia::accessibility::Node::New();
found_node->Clone(node_ptr.get());
return node_ptr;
}
fuchsia::accessibility::NodePtr SemanticTree::GetAccessibilityNode(
zx_koid_t view_id, int32_t node_id) {
auto it = nodes_.find(view_id);
if (it == nodes_.end()) {
return nullptr;
}
auto node_it = it->second.find(node_id);
if (node_it == it->second.end()) {
return nullptr;
}
auto node_ptr = fuchsia::accessibility::Node::New();
node_it->second.Clone(node_ptr.get());
return node_ptr;
}
void SemanticTree::PerformAccessibilityAction(
zx_koid_t view_id, int32_t node_id, fuchsia::accessibility::Action action) {
auto it = providers_.find(view_id);
if (it == providers_.end()) {
return;
}
it->second->PerformAccessibilityAction(node_id, action);
}
void SemanticTree::RegisterSemanticsProvider(
zx_koid_t view_id,
fidl::InterfaceHandle<fuchsia::accessibility::SemanticsProvider> handle) {
auto it = nodes_.find(view_id);
if (it != nodes_.end()) {
return;
}
nodes_.emplace(
view_id,
std::unordered_map<int32_t /*node_id*/, fuchsia::accessibility::Node>());
uncommitted_nodes_.emplace(view_id,
std::vector<fuchsia::accessibility::Node>());
uncommitted_deletes_.emplace(view_id, std::vector<int32_t>());
fuchsia::accessibility::SemanticsProviderPtr provider = handle.Bind();
provider.set_error_handler([this, view_id](zx_status_t status) {
FXL_LOG(INFO) << "Semantic provider disconnected with id: " << view_id;
this->nodes_.erase(view_id);
this->uncommitted_nodes_.erase(view_id);
this->uncommitted_deletes_.erase(view_id);
this->providers_.erase(view_id);
});
providers_.emplace(view_id, std::move(provider));
}
void SemanticTree::UpdateSemanticNodes(
zx_koid_t view_id, std::vector<fuchsia::accessibility::Node> nodes) {
auto it = uncommitted_nodes_.find(view_id);
if (it == uncommitted_nodes_.end()) {
return;
}
it->second.insert(it->second.end(), std::make_move_iterator(nodes.begin()),
std::make_move_iterator(nodes.end()));
}
void SemanticTree::DeleteSemanticNodes(zx_koid_t view_id,
std::vector<int32_t> node_ids) {
auto it = uncommitted_deletes_.find(view_id);
if (it == uncommitted_deletes_.end()) {
return;
}
it->second.insert(it->second.end(), std::make_move_iterator(node_ids.begin()),
std::make_move_iterator(node_ids.end()));
}
void SemanticTree::Commit(zx_koid_t view_id) {
auto nodes_it = nodes_.find(view_id);
auto u_nodes_it = uncommitted_nodes_.find(view_id);
auto u_delete_it = uncommitted_deletes_.find(view_id);
if (nodes_it == nodes_.end() || u_nodes_it == uncommitted_nodes_.end() ||
u_delete_it == uncommitted_deletes_.end()) {
return;
}
for (auto& u_node : u_nodes_it->second) {
nodes_it->second[u_node.node_id] = std::move(u_node);
}
u_nodes_it->second.clear();
for (auto& u_delete : u_delete_it->second) {
nodes_it->second.erase(u_delete);
}
u_delete_it->second.clear();
}
fuchsia::accessibility::Node* SemanticTree::HitTest(
std::unordered_map<int32_t, fuchsia::accessibility::Node>& nodes,
int32_t node_id, escher::vec4 coordinates) {
auto it = nodes.find(node_id);
if (it == nodes.end()) {
return nullptr;
}
escher::mat4 transform = scenic_impl::gfx::Unwrap(it->second.data.transform);
escher::vec4 local_coordinates = transform * coordinates;
escher::vec2 point(local_coordinates[0], local_coordinates[1]);
if (!BoxContainsPoint(it->second.data.location, point)) {
return nullptr;
}
for (auto child : it->second.children_hit_test_order) {
fuchsia::accessibility::Node* node =
HitTest(nodes, child, local_coordinates);
if (node != nullptr) {
return node;
}
}
return &(it->second);
}
} // namespace a11y_manager