| // Copyright 2013 The Flutter 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 "flutter/shell/platform/embedder/embedder_semantics_update.h" | |
| namespace flutter { | |
| EmbedderSemanticsUpdate::EmbedderSemanticsUpdate( | |
| const SemanticsNodeUpdates& nodes, | |
| const CustomAccessibilityActionUpdates& actions) { | |
| for (const auto& value : nodes) { | |
| AddNode(value.second); | |
| } | |
| for (const auto& value : actions) { | |
| AddAction(value.second); | |
| } | |
| update_ = { | |
| .struct_size = sizeof(FlutterSemanticsUpdate), | |
| .nodes_count = nodes_.size(), | |
| .nodes = nodes_.data(), | |
| .custom_actions_count = actions_.size(), | |
| .custom_actions = actions_.data(), | |
| }; | |
| } | |
| void EmbedderSemanticsUpdate::AddNode(const SemanticsNode& node) { | |
| SkMatrix transform = node.transform.asM33(); | |
| FlutterTransformation flutter_transform{ | |
| transform.get(SkMatrix::kMScaleX), transform.get(SkMatrix::kMSkewX), | |
| transform.get(SkMatrix::kMTransX), transform.get(SkMatrix::kMSkewY), | |
| transform.get(SkMatrix::kMScaleY), transform.get(SkMatrix::kMTransY), | |
| transform.get(SkMatrix::kMPersp0), transform.get(SkMatrix::kMPersp1), | |
| transform.get(SkMatrix::kMPersp2)}; | |
| // Do not add new members to FlutterSemanticsNode. | |
| // This would break the forward compatibility of FlutterSemanticsUpdate. | |
| // All new members must be added to FlutterSemanticsNode2 instead. | |
| nodes_.push_back({ | |
| sizeof(FlutterSemanticsNode), | |
| node.id, | |
| static_cast<FlutterSemanticsFlag>(node.flags), | |
| static_cast<FlutterSemanticsAction>(node.actions), | |
| node.textSelectionBase, | |
| node.textSelectionExtent, | |
| node.scrollChildren, | |
| node.scrollIndex, | |
| node.scrollPosition, | |
| node.scrollExtentMax, | |
| node.scrollExtentMin, | |
| node.elevation, | |
| node.thickness, | |
| node.label.c_str(), | |
| node.hint.c_str(), | |
| node.value.c_str(), | |
| node.increasedValue.c_str(), | |
| node.decreasedValue.c_str(), | |
| static_cast<FlutterTextDirection>(node.textDirection), | |
| FlutterRect{node.rect.fLeft, node.rect.fTop, node.rect.fRight, | |
| node.rect.fBottom}, | |
| flutter_transform, | |
| node.childrenInTraversalOrder.size(), | |
| node.childrenInTraversalOrder.data(), | |
| node.childrenInHitTestOrder.data(), | |
| node.customAccessibilityActions.size(), | |
| node.customAccessibilityActions.data(), | |
| node.platformViewId, | |
| node.tooltip.c_str(), | |
| }); | |
| } | |
| void EmbedderSemanticsUpdate::AddAction( | |
| const CustomAccessibilityAction& action) { | |
| // Do not add new members to FlutterSemanticsCustomAction. | |
| // This would break the forward compatibility of FlutterSemanticsUpdate. | |
| // All new members must be added to FlutterSemanticsCustomAction2 instead. | |
| actions_.push_back({ | |
| sizeof(FlutterSemanticsCustomAction), | |
| action.id, | |
| static_cast<FlutterSemanticsAction>(action.overrideId), | |
| action.label.c_str(), | |
| action.hint.c_str(), | |
| }); | |
| } | |
| EmbedderSemanticsUpdate::~EmbedderSemanticsUpdate() {} | |
| EmbedderSemanticsUpdate2::EmbedderSemanticsUpdate2( | |
| const SemanticsNodeUpdates& nodes, | |
| const CustomAccessibilityActionUpdates& actions) { | |
| nodes_.reserve(nodes.size()); | |
| node_pointers_.reserve(nodes.size()); | |
| actions_.reserve(actions.size()); | |
| action_pointers_.reserve(actions.size()); | |
| for (const auto& value : nodes) { | |
| AddNode(value.second); | |
| } | |
| for (const auto& value : actions) { | |
| AddAction(value.second); | |
| } | |
| for (size_t i = 0; i < nodes_.size(); i++) { | |
| node_pointers_.push_back(&nodes_[i]); | |
| } | |
| for (size_t i = 0; i < actions_.size(); i++) { | |
| action_pointers_.push_back(&actions_[i]); | |
| } | |
| update_ = { | |
| .struct_size = sizeof(FlutterSemanticsUpdate2), | |
| .node_count = node_pointers_.size(), | |
| .nodes = node_pointers_.data(), | |
| .custom_action_count = action_pointers_.size(), | |
| .custom_actions = action_pointers_.data(), | |
| }; | |
| } | |
| EmbedderSemanticsUpdate2::~EmbedderSemanticsUpdate2() {} | |
| void EmbedderSemanticsUpdate2::AddNode(const SemanticsNode& node) { | |
| SkMatrix transform = node.transform.asM33(); | |
| FlutterTransformation flutter_transform{ | |
| transform.get(SkMatrix::kMScaleX), transform.get(SkMatrix::kMSkewX), | |
| transform.get(SkMatrix::kMTransX), transform.get(SkMatrix::kMSkewY), | |
| transform.get(SkMatrix::kMScaleY), transform.get(SkMatrix::kMTransY), | |
| transform.get(SkMatrix::kMPersp0), transform.get(SkMatrix::kMPersp1), | |
| transform.get(SkMatrix::kMPersp2)}; | |
| auto label_attributes = CreateStringAttributes(node.labelAttributes); | |
| auto hint_attributes = CreateStringAttributes(node.hintAttributes); | |
| auto value_attributes = CreateStringAttributes(node.valueAttributes); | |
| auto increased_value_attributes = | |
| CreateStringAttributes(node.increasedValueAttributes); | |
| auto decreased_value_attributes = | |
| CreateStringAttributes(node.decreasedValueAttributes); | |
| nodes_.push_back({ | |
| sizeof(FlutterSemanticsNode2), | |
| node.id, | |
| static_cast<FlutterSemanticsFlag>(node.flags), | |
| static_cast<FlutterSemanticsAction>(node.actions), | |
| node.textSelectionBase, | |
| node.textSelectionExtent, | |
| node.scrollChildren, | |
| node.scrollIndex, | |
| node.scrollPosition, | |
| node.scrollExtentMax, | |
| node.scrollExtentMin, | |
| node.elevation, | |
| node.thickness, | |
| node.label.c_str(), | |
| node.hint.c_str(), | |
| node.value.c_str(), | |
| node.increasedValue.c_str(), | |
| node.decreasedValue.c_str(), | |
| static_cast<FlutterTextDirection>(node.textDirection), | |
| FlutterRect{node.rect.fLeft, node.rect.fTop, node.rect.fRight, | |
| node.rect.fBottom}, | |
| flutter_transform, | |
| node.childrenInTraversalOrder.size(), | |
| node.childrenInTraversalOrder.data(), | |
| node.childrenInHitTestOrder.data(), | |
| node.customAccessibilityActions.size(), | |
| node.customAccessibilityActions.data(), | |
| node.platformViewId, | |
| node.tooltip.c_str(), | |
| label_attributes.count, | |
| label_attributes.attributes, | |
| hint_attributes.count, | |
| hint_attributes.attributes, | |
| value_attributes.count, | |
| value_attributes.attributes, | |
| increased_value_attributes.count, | |
| increased_value_attributes.attributes, | |
| decreased_value_attributes.count, | |
| decreased_value_attributes.attributes, | |
| }); | |
| } | |
| void EmbedderSemanticsUpdate2::AddAction( | |
| const CustomAccessibilityAction& action) { | |
| actions_.push_back({ | |
| sizeof(FlutterSemanticsCustomAction2), | |
| action.id, | |
| static_cast<FlutterSemanticsAction>(action.overrideId), | |
| action.label.c_str(), | |
| action.hint.c_str(), | |
| }); | |
| } | |
| EmbedderSemanticsUpdate2::EmbedderStringAttributes | |
| EmbedderSemanticsUpdate2::CreateStringAttributes( | |
| const StringAttributes& attributes) { | |
| // Minimize allocations if attributes are empty. | |
| if (attributes.empty()) { | |
| return {.count = 0, .attributes = nullptr}; | |
| } | |
| // Translate the engine attributes to embedder attributes. | |
| // The result vector's data is returned by this method. | |
| // The result vector will be owned by |node_string_attributes_| | |
| // so that the embedder attributes are cleaned up at the end of the | |
| // semantics update callback when when the |EmbedderSemanticsUpdate2| | |
| // is destroyed. | |
| auto result = std::make_unique<std::vector<const FlutterStringAttribute*>>(); | |
| result->reserve(attributes.size()); | |
| for (const auto& attribute : attributes) { | |
| auto embedder_attribute = std::make_unique<FlutterStringAttribute>(); | |
| embedder_attribute->struct_size = sizeof(FlutterStringAttribute); | |
| embedder_attribute->start = attribute->start; | |
| embedder_attribute->end = attribute->end; | |
| switch (attribute->type) { | |
| case StringAttributeType::kLocale: { | |
| std::shared_ptr<flutter::LocaleStringAttribute> locale_attribute = | |
| std::static_pointer_cast<flutter::LocaleStringAttribute>(attribute); | |
| auto embedder_locale = std::make_unique<FlutterLocaleStringAttribute>(); | |
| embedder_locale->struct_size = sizeof(FlutterLocaleStringAttribute); | |
| embedder_locale->locale = locale_attribute->locale.c_str(); | |
| locale_attributes_.push_back(std::move(embedder_locale)); | |
| embedder_attribute->type = FlutterStringAttributeType::kLocale; | |
| embedder_attribute->locale = locale_attributes_.back().get(); | |
| break; | |
| } | |
| case flutter::StringAttributeType::kSpellOut: { | |
| // All spell out attributes are identical and share a lazily created | |
| // instance. | |
| if (!spell_out_attribute_) { | |
| auto spell_out_attribute_ = | |
| std::make_unique<FlutterSpellOutStringAttribute>(); | |
| spell_out_attribute_->struct_size = | |
| sizeof(FlutterSpellOutStringAttribute); | |
| } | |
| embedder_attribute->type = FlutterStringAttributeType::kSpellOut; | |
| embedder_attribute->spell_out = spell_out_attribute_.get(); | |
| break; | |
| } | |
| } | |
| string_attributes_.push_back(std::move(embedder_attribute)); | |
| result->push_back(string_attributes_.back().get()); | |
| } | |
| node_string_attributes_.push_back(std::move(result)); | |
| return { | |
| .count = node_string_attributes_.back()->size(), | |
| .attributes = node_string_attributes_.back()->data(), | |
| }; | |
| } | |
| } // namespace flutter |