mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
284 lines
13 KiB
C++
284 lines
13 KiB
C++
// 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.
|
|
|
|
#ifndef FLUTTER_SHELL_PLATFORM_COMMON_CPP_ACCESSIBILITY_BRIDGE_H_
|
|
#define FLUTTER_SHELL_PLATFORM_COMMON_CPP_ACCESSIBILITY_BRIDGE_H_
|
|
|
|
#include <unordered_map>
|
|
|
|
#include "flutter/shell/platform/embedder/embedder.h"
|
|
|
|
#include "flutter/third_party/accessibility/ax/ax_event_generator.h"
|
|
#include "flutter/third_party/accessibility/ax/ax_tree.h"
|
|
#include "flutter/third_party/accessibility/ax/ax_tree_observer.h"
|
|
#include "flutter/third_party/accessibility/ax/platform/ax_platform_node_delegate.h"
|
|
|
|
#include "flutter_platform_node_delegate.h"
|
|
|
|
namespace flutter {
|
|
|
|
//------------------------------------------------------------------------------
|
|
/// Use this class to maintain an accessibility tree. This class consumes
|
|
/// semantics updates from the embedder API and produces an accessibility tree
|
|
/// in the native format.
|
|
///
|
|
/// The bridge creates an AXTree to hold the semantics data that comes from
|
|
/// Flutter semantics updates. The tree holds AXNode[s] which contain the
|
|
/// semantics information for semantics node. The AXTree ressemble the Flutter
|
|
/// semantics tree in the Flutter framework. The bridge also uses
|
|
/// FlutterPlatformNodeDelegate to wrap each AXNode in order to provide
|
|
/// an accessibility tree in the native format.
|
|
///
|
|
/// This class takes in a AccessibilityBridgeDelegate instance and is in charge
|
|
/// of its lifecycle. The delegate are used to handle the accessibility events
|
|
/// and actions.
|
|
///
|
|
/// To use this class, you must provide your own implementation of
|
|
/// FlutterPlatformNodeDelegate and AccessibilityBridgeDelegate.
|
|
class AccessibilityBridge
|
|
: public std::enable_shared_from_this<AccessibilityBridge>,
|
|
public FlutterPlatformNodeDelegate::OwnerBridge,
|
|
private ui::AXTreeObserver {
|
|
public:
|
|
//-----------------------------------------------------------------------------
|
|
/// Delegate to handle requests from the accessibility bridge. The requests
|
|
/// include sending accessibility event to native accessibility system,
|
|
/// routing accessibility action to the Flutter framework, and creating
|
|
/// platform specific FlutterPlatformNodeDelegate.
|
|
///
|
|
/// The accessibility events are generated when accessibility tree changes.
|
|
/// These events must be sent to the native accessibility system through
|
|
/// the native API for the system to pick up the changes
|
|
/// (e.g. NSAccessibilityPostNotification in MacOS).
|
|
///
|
|
/// The accessibility actions are generated by the native accessibility system
|
|
/// when users interacted with the assistive technologies. Those actions
|
|
/// needed to be sent to the Flutter framework.
|
|
///
|
|
/// Each platform needs to implement the FlutterPlatformNodeDelegate and
|
|
/// returns its platform specific instance of FlutterPlatformNodeDelegate
|
|
/// in this delegate.
|
|
class AccessibilityBridgeDelegate {
|
|
public:
|
|
virtual ~AccessibilityBridgeDelegate() = default;
|
|
//---------------------------------------------------------------------------
|
|
/// @brief Handle accessibility events generated due to accessibility
|
|
/// tree changes. These events are generated in accessibility
|
|
/// bridge and needed to be sent to native accessibility system.
|
|
/// See ui::AXEventGenerator::Event for possible events.
|
|
///
|
|
/// @param[in] targeted_event The object that contains both the
|
|
/// generated event and the event target.
|
|
virtual void OnAccessibilityEvent(
|
|
ui::AXEventGenerator::TargetedEvent targeted_event) = 0;
|
|
|
|
//---------------------------------------------------------------------------
|
|
/// @brief Dispatch accessibility action back to the Flutter framework.
|
|
/// These actions are generated in the native accessibility
|
|
/// system when users interact with the assistive technologies.
|
|
/// For example, a
|
|
/// FlutterSemanticsAction::kFlutterSemanticsActionTap is
|
|
/// fired when user click or touch the screen.
|
|
///
|
|
/// @param[in] target The semantics node id of the action
|
|
/// target.
|
|
/// @param[in] action The generated flutter semantics action.
|
|
/// @param[in] data Additional data associated with the
|
|
/// action.
|
|
virtual void DispatchAccessibilityAction(AccessibilityNodeId target,
|
|
FlutterSemanticsAction action,
|
|
std::vector<uint8_t> data) = 0;
|
|
|
|
//---------------------------------------------------------------------------
|
|
/// @brief Creates a platform specific FlutterPlatformNodeDelegate.
|
|
/// Ownership passes to the caller. This method will be called
|
|
/// by accessibility bridge whenever a new AXNode is created in
|
|
/// AXTree. Each platform needs to implement this method in
|
|
/// order to inject its subclass into the accessibility bridge.
|
|
virtual std::unique_ptr<FlutterPlatformNodeDelegate>
|
|
CreateFlutterPlatformNodeDelegate() = 0;
|
|
};
|
|
//-----------------------------------------------------------------------------
|
|
/// @brief Creates a new instance of a accessibility bridge.
|
|
///
|
|
/// @param[in] user_data A custom pointer to the data of your
|
|
/// choice. This pointer can be retrieve later
|
|
/// through GetUserData().
|
|
AccessibilityBridge(std::unique_ptr<AccessibilityBridgeDelegate> delegate);
|
|
~AccessibilityBridge();
|
|
|
|
//------------------------------------------------------------------------------
|
|
/// @brief Adds a semantics node update to the pending semantics update.
|
|
/// Calling this method alone will NOT update the semantics tree.
|
|
/// To flush the pending updates, call the CommitUpdates().
|
|
///
|
|
/// @param[in] node A pointer to the semantics node update.
|
|
void AddFlutterSemanticsNodeUpdate(const FlutterSemanticsNode* node);
|
|
|
|
//------------------------------------------------------------------------------
|
|
/// @brief Adds a custom semantics action update to the pending semantics
|
|
/// update. Calling this method alone will NOT update the
|
|
/// semantics tree. To flush the pending updates, call the
|
|
/// CommitUpdates().
|
|
///
|
|
/// @param[in] action A pointer to the custom semantics action
|
|
/// update.
|
|
void AddFlutterSemanticsCustomActionUpdate(
|
|
const FlutterSemanticsCustomAction* action);
|
|
|
|
//------------------------------------------------------------------------------
|
|
/// @brief Flushes the pending updates and applies them to this
|
|
/// accessibility bridge. Calling this with no pending updates
|
|
/// does nothing, and callers should call this method at the end
|
|
/// of an atomic batch to avoid leaving the tree in a unstable
|
|
/// state. For example if a node reparents from A to B, callers
|
|
/// should only call this method when both removal from A and
|
|
/// addition to B are in the pending updates.
|
|
void CommitUpdates();
|
|
|
|
//------------------------------------------------------------------------------
|
|
/// @brief Get the flutter platform node delegate with the given id from
|
|
/// this accessibility bridge. Returns expired weak_ptr if the
|
|
/// delegate associated with the id does not exist or has been
|
|
/// removed from the accessibility tree.
|
|
///
|
|
/// @param[in] id The id of the flutter accessibility node you want
|
|
/// to retrieve.
|
|
std::weak_ptr<FlutterPlatformNodeDelegate>
|
|
GetFlutterPlatformNodeDelegateFromID(AccessibilityNodeId id) const;
|
|
|
|
//------------------------------------------------------------------------------
|
|
/// @brief Get the ax tree data from this accessibility bridge. The tree
|
|
/// data contains information such as the id of the node that
|
|
/// has the keyboard focus or the text selection range.
|
|
const ui::AXTreeData& GetAXTreeData() const;
|
|
|
|
private:
|
|
// See FlutterSemanticsNode in embedder.h
|
|
typedef struct {
|
|
int32_t id;
|
|
FlutterSemanticsFlag flags;
|
|
FlutterSemanticsAction actions;
|
|
int32_t text_selection_base;
|
|
int32_t text_selection_extent;
|
|
int32_t scroll_child_count;
|
|
int32_t scroll_index;
|
|
double scroll_position;
|
|
double scroll_extent_max;
|
|
double scroll_extent_min;
|
|
double elevation;
|
|
double thickness;
|
|
std::string label;
|
|
std::string hint;
|
|
std::string value;
|
|
std::string increased_value;
|
|
std::string decreased_value;
|
|
FlutterTextDirection text_direction;
|
|
FlutterRect rect;
|
|
FlutterTransformation transform;
|
|
std::vector<int32_t> children_in_traversal_order;
|
|
std::vector<int32_t> custom_accessibility_actions;
|
|
} SemanticsNode;
|
|
|
|
// See FlutterSemanticsCustomAction in embedder.h
|
|
typedef struct {
|
|
int32_t id;
|
|
FlutterSemanticsAction override_action;
|
|
std::string label;
|
|
std::string hint;
|
|
} SemanticsCustomAction;
|
|
|
|
std::unordered_map<AccessibilityNodeId,
|
|
std::shared_ptr<FlutterPlatformNodeDelegate>>
|
|
id_wrapper_map_;
|
|
ui::AXTree tree_;
|
|
ui::AXEventGenerator event_generator_;
|
|
std::unordered_map<int32_t, SemanticsNode> pending_semantics_node_updates_;
|
|
std::unordered_map<int32_t, SemanticsCustomAction>
|
|
pending_semantics_custom_action_updates_;
|
|
AccessibilityNodeId last_focused_id_ = ui::AXNode::kInvalidAXID;
|
|
std::unique_ptr<AccessibilityBridgeDelegate> delegate_;
|
|
|
|
void InitAXTree(const ui::AXTreeUpdate& initial_state);
|
|
void GetSubTreeList(SemanticsNode target, std::vector<SemanticsNode>& result);
|
|
void ConvertFluterUpdate(const SemanticsNode& node,
|
|
ui::AXTreeUpdate& tree_update);
|
|
void SetRoleFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetStateFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetActionsFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetBooleanAttributesFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetIntAttributesFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetIntListAttributesFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetStringListAttributesFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetNameFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetValueFromFlutterUpdate(ui::AXNodeData& node_data,
|
|
const SemanticsNode& node);
|
|
void SetTreeData(const SemanticsNode& node, ui::AXTreeUpdate& tree_update);
|
|
SemanticsNode FromFlutterSemanticsNode(
|
|
const FlutterSemanticsNode* flutter_node);
|
|
SemanticsCustomAction FromFlutterSemanticsCustomAction(
|
|
const FlutterSemanticsCustomAction* flutter_custom_action);
|
|
|
|
// |AXTreeObserver|
|
|
void OnNodeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
|
|
|
|
// |AXTreeObserver|
|
|
void OnSubtreeWillBeDeleted(ui::AXTree* tree, ui::AXNode* node) override;
|
|
|
|
// |AXTreeObserver|
|
|
void OnNodeCreated(ui::AXTree* tree, ui::AXNode* node) override;
|
|
|
|
// |AXTreeObserver|
|
|
void OnNodeDeleted(ui::AXTree* tree, AccessibilityNodeId node_id) override;
|
|
|
|
// |AXTreeObserver|
|
|
void OnNodeReparented(ui::AXTree* tree, ui::AXNode* node) override;
|
|
|
|
// |AXTreeObserver|
|
|
void OnRoleChanged(ui::AXTree* tree,
|
|
ui::AXNode* node,
|
|
ax::mojom::Role old_role,
|
|
ax::mojom::Role new_role) override;
|
|
|
|
// |AXTreeObserver|
|
|
void OnAtomicUpdateFinished(
|
|
ui::AXTree* tree,
|
|
bool root_changed,
|
|
const std::vector<ui::AXTreeObserver::Change>& changes) override;
|
|
|
|
// |FlutterPlatformNodeDelegate::OwnerBridge|
|
|
void SetLastFocusedId(AccessibilityNodeId node_id) override;
|
|
|
|
// |FlutterPlatformNodeDelegate::OwnerBridge|
|
|
AccessibilityNodeId GetLastFocusedId() override;
|
|
|
|
// |FlutterPlatformNodeDelegate::OwnerBridge|
|
|
gfx::NativeViewAccessible GetNativeAccessibleFromId(
|
|
AccessibilityNodeId id) override;
|
|
|
|
// |FlutterPlatformNodeDelegate::OwnerBridge|
|
|
void DispatchAccessibilityAction(AccessibilityNodeId target,
|
|
FlutterSemanticsAction action,
|
|
std::vector<uint8_t> data) override;
|
|
|
|
// |FlutterPlatformNodeDelegate::OwnerBridge|
|
|
gfx::RectF RelativeToGlobalBounds(const ui::AXNode* node,
|
|
bool& offscreen,
|
|
bool clip_bounds) override;
|
|
|
|
BASE_DISALLOW_COPY_AND_ASSIGN(AccessibilityBridge);
|
|
};
|
|
|
|
} // namespace flutter
|
|
|
|
#endif // FLUTTER_SHELL_PLATFORM_COMMON_CPP_ACCESSIBILITY_BRIDGE_H_
|