-
Notifications
You must be signed in to change notification settings - Fork 6k
Announce alerts through SemanticsService on Windows #36966
Changes from 1 commit
9def664
cc0913c
0ead656
03acbea
5aa0828
1d3b910
1873b2d
f0a3810
8ab628a
62e4ecc
dc219b2
0c43076
c0d5a17
885f4dc
1ccb847
da7a773
957f9f5
3876e99
de9da98
9317a88
2dfd8ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,157 @@ | |
|
|
||
| #include "flutter/shell/platform/windows/accessibility_alert.h" | ||
|
|
||
| #include "flutter/shell/platform/windows/accessibility_root_node.h" | ||
|
|
||
| namespace flutter { | ||
|
|
||
| AccessibilityAlert::AccessibilityAlert() : text_(L""), parent_(nullptr) {} | ||
|
|
||
| IFACEMETHODIMP AccessibilityAlert::accHitTest(LONG screen_physical_pixel_x, | ||
| LONG screen_physical_pixel_y, | ||
| VARIANT* child) { | ||
| child->vt = VT_EMPTY; | ||
| return S_FALSE; | ||
| } | ||
|
|
||
| // Performs the object's default action. | ||
| IFACEMETHODIMP AccessibilityAlert::accDoDefaultAction(VARIANT var_id) { | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| // Retrieves the specified object's current screen location. | ||
| IFACEMETHODIMP AccessibilityAlert::accLocation(LONG* physical_pixel_left, | ||
| LONG* physical_pixel_top, | ||
| LONG* width, | ||
| LONG* height, | ||
| VARIANT var_id) { | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| // Traverses to another UI element and retrieves the object. | ||
| IFACEMETHODIMP AccessibilityAlert::accNavigate(LONG nav_dir, | ||
| VARIANT start, | ||
| VARIANT* end) { | ||
| end->vt = VT_EMPTY; | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| // Retrieves an IDispatch interface pointer for the specified child. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accChild(VARIANT var_child, | ||
| IDispatch** disp_child) { | ||
| if (V_VT(&var_child) == VT_I4 && V_I4(&var_child) == CHILDID_SELF) { | ||
| *disp_child = this; | ||
| AddRef(); | ||
| return S_OK; | ||
| } | ||
| *disp_child = nullptr; | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| // Retrieves the number of accessible children. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accChildCount(LONG* child_count) { | ||
| *child_count = 0; | ||
| return S_OK; | ||
| } | ||
|
|
||
| // Retrieves a string that describes the object's default action. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accDefaultAction(VARIANT var_id, | ||
| BSTR* default_action) { | ||
| *default_action = nullptr; | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| // Retrieves the tooltip description. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accDescription(VARIANT var_id, BSTR* desc) { | ||
| *desc = nullptr; | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| // Retrieves the object that has the keyboard focus. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accFocus(VARIANT* focus_child) { | ||
| focus_child->vt = VT_EMPTY; | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| // Retrieves the specified object's shortcut. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accKeyboardShortcut(VARIANT var_id, | ||
| BSTR* access_key) { | ||
| *access_key = nullptr; | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| // Retrieves the name of the specified object. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accName(VARIANT var_id, BSTR* name) { | ||
| *name = SysAllocString(text_.c_str()); | ||
| return S_OK; | ||
| } | ||
|
|
||
| // Retrieves the IDispatch interface of the object's parent. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accParent(IDispatch** disp_parent) { | ||
| //*disp_parent = parent_; | ||
| //return S_OK; | ||
| *disp_parent = nullptr; | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| // Retrieves information describing the role of the specified object. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accRole(VARIANT var_id, VARIANT* role) { | ||
| *role = {.vt = VT_I4, .lVal = ROLE_SYSTEM_ALERT}; | ||
| return S_OK; | ||
| } | ||
|
|
||
| // Retrieves the current state of the specified object. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accState(VARIANT var_id, VARIANT* state) { | ||
| *state = {.vt = VT_I4, .lVal = STATE_SYSTEM_DEFAULT}; | ||
| return S_OK; | ||
| } | ||
|
|
||
| // Gets the help string for the specified object. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accHelp(VARIANT var_id, BSTR* help) { | ||
| *help = SysAllocString(L""); | ||
| return S_OK; | ||
| } | ||
|
|
||
| // Retrieve or set the string value associated with the specified object. | ||
| // Setting the value is not typically used by screen readers, but it's | ||
| // used frequently by automation software. | ||
| IFACEMETHODIMP AccessibilityAlert::get_accValue(VARIANT var_id, BSTR* value) { | ||
| *value = SysAllocString(text_.c_str()); | ||
| return S_OK; | ||
| } | ||
| IFACEMETHODIMP AccessibilityAlert::put_accValue(VARIANT var_id, BSTR new_value) { | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| // IAccessible methods not implemented. | ||
loic-sharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| IFACEMETHODIMP AccessibilityAlert::get_accSelection(VARIANT* selected) { | ||
| selected->vt = VT_EMPTY; | ||
| return E_NOTIMPL; | ||
| } | ||
| IFACEMETHODIMP AccessibilityAlert::accSelect(LONG flags_sel, VARIANT var_id) { | ||
| return E_NOTIMPL; | ||
| } | ||
| IFACEMETHODIMP AccessibilityAlert::get_accHelpTopic(BSTR* help_file, | ||
| VARIANT var_id, | ||
| LONG* topic_id) { | ||
| if (help_file) { | ||
| *help_file = nullptr; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does it matter for the return value even if this method returns NotImpl?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This implementation is modeled after the not-implemented method body in |
||
| } | ||
| if (topic_id) { | ||
| *topic_id = 0; | ||
| } | ||
| return E_NOTIMPL; | ||
| } | ||
| IFACEMETHODIMP AccessibilityAlert::put_accName(VARIANT var_id, BSTR put_name) { | ||
| return E_NOTIMPL; | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a comment indicates the below methods are not part of IAccessible. Currently this is in the |
||
| void AccessibilityAlert::SetText(const std::wstring& text) { | ||
| text_ = text; | ||
| } | ||
|
|
||
| void AccessibilityAlert::SetParent(AccessibilityRootNode* parent) { | ||
| parent_ = parent; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,9 +13,11 @@ | |
|
|
||
| namespace flutter { | ||
|
|
||
| class AccessibilityRootNode; | ||
|
|
||
| // An IAccessible node representing an alert read to the screen reader. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to mention a bit about how it works? and also mention this node is not interact-able by user |
||
| class AccessibilityAlert : public CComObjectRootEx<CComMultiThreadModel>, | ||
| public IAccessible { | ||
| public IDispatchImpl<IAccessible> { | ||
| public: | ||
| BEGIN_COM_MAP(AccessibilityAlert) | ||
| COM_INTERFACE_ENTRY(IAccessible) | ||
|
|
@@ -94,11 +96,18 @@ class AccessibilityAlert : public CComObjectRootEx<CComMultiThreadModel>, | |
| LONG* topic_id) override; | ||
| IFACEMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override; | ||
|
|
||
| AccessibilityAlert(); | ||
| ~AccessibilityAlert() = default; | ||
|
|
||
| // Sets the text of this alert to the provided message. | ||
| void SetText(const std::u16string& text); | ||
| void SetText(const std::wstring& text); | ||
|
|
||
| void SetParent(AccessibilityRootNode* parent); | ||
|
|
||
| private: | ||
| std::u16string text_; | ||
| std::wstring text_; | ||
|
|
||
| AccessibilityRootNode* parent_; | ||
|
|
||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,11 +4,15 @@ | |
|
|
||
| #include "flutter/shell/platform/windows/accessibility_root_node.h" | ||
|
|
||
| #include "flutter/fml/logging.h" | ||
|
|
||
| namespace flutter { | ||
|
|
||
| static constexpr LONG kWindowChildId = 1; | ||
| static constexpr LONG kAlertChildId = 2; | ||
| static constexpr LONG kInvalidChildId = 3; | ||
|
|
||
| namespace flutter { | ||
| AccessibilityRootNode::AccessibilityRootNode() : | ||
| alert_accessible_(nullptr) {} | ||
|
|
||
| AccessibilityRootNode::~AccessibilityRootNode() { | ||
| if (alert_accessible_) { | ||
|
|
@@ -66,7 +70,7 @@ IFACEMETHODIMP AccessibilityRootNode::accLocation(LONG* physical_pixel_left, | |
| VARIANT var_id) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->accLocation(physical_pixel_left, physical_pixel_top, width, height, var_id); | ||
| return target->accLocation(physical_pixel_left, physical_pixel_top, width, height, var_id); | ||
| } | ||
| return S_FALSE; | ||
| } | ||
|
|
@@ -76,7 +80,7 @@ IFACEMETHODIMP AccessibilityRootNode::accNavigate(LONG nav_dir, | |
| VARIANT* end) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&start))) { | ||
| return window_accessible_->accNavigate(nav_dir, start, end); | ||
| return target->accNavigate(nav_dir, start, end); | ||
| } | ||
| return S_FALSE; | ||
| } | ||
|
|
@@ -91,10 +95,12 @@ IFACEMETHODIMP AccessibilityRootNode::get_accChild(VARIANT var_child, | |
| *disp_child = this; | ||
| } else if (!window_accessible_) { | ||
| return E_FAIL; | ||
| } else if (child_id == 1) { | ||
| } else if (child_id == kWindowChildId) { | ||
| *disp_child = window_accessible_; | ||
| } else if (child_id == 2 && alert_accessible_) { | ||
| } else if (child_id == kAlertChildId && alert_accessible_) { | ||
| *disp_child = alert_accessible_; | ||
| } else if (child_id < 0) { | ||
| return window_accessible_->get_accChild(var_child, disp_child); | ||
loic-sharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else { | ||
| return E_FAIL; | ||
| } | ||
|
|
@@ -118,7 +124,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accDefaultAction(VARIANT var_id, | |
| BSTR* def_action) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->get_accDefaultAction(var_id, def_action); | ||
| return target->get_accDefaultAction(var_id, def_action); | ||
| } | ||
| *def_action = nullptr; | ||
| return S_FALSE; | ||
|
|
@@ -128,7 +134,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accDescription(VARIANT var_id, | |
| BSTR* desc) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->get_accDescription(var_id, desc); | ||
| return target->get_accDescription(var_id, desc); | ||
| } | ||
| return E_FAIL; | ||
| } | ||
|
|
@@ -145,12 +151,17 @@ IFACEMETHODIMP AccessibilityRootNode::get_accKeyboardShortcut(VARIANT var_id, | |
| BSTR* acc_key) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->get_accKeyboardShortcut(var_id, acc_key); | ||
| return target->get_accKeyboardShortcut(var_id, acc_key); | ||
| } | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| IFACEMETHODIMP AccessibilityRootNode::get_accName(VARIANT var_id, BSTR* name_bstr) { | ||
| if (V_I4(&var_id) == CHILDID_SELF) { | ||
| std::wstring name = L"ROOT_NODE_VIEW"; | ||
| *name_bstr = SysAllocString(name.c_str()); | ||
yaakovschectman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return S_OK; | ||
| } | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->get_accName(var_id, name_bstr); | ||
|
||
|
|
@@ -165,15 +176,15 @@ IFACEMETHODIMP AccessibilityRootNode::get_accParent(IDispatch** disp_parent) { | |
| IFACEMETHODIMP AccessibilityRootNode::get_accRole(VARIANT var_id, VARIANT* role) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->get_accRole(var_id, role); | ||
| return target->get_accRole(var_id, role); | ||
| } | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| IFACEMETHODIMP AccessibilityRootNode::get_accState(VARIANT var_id, VARIANT* state) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->get_accState(var_id, state); | ||
| return target->get_accState(var_id, state); | ||
| } | ||
| return E_FAIL; | ||
| } | ||
|
|
@@ -189,15 +200,15 @@ IFACEMETHODIMP AccessibilityRootNode::get_accHelp(VARIANT var_id, BSTR* help) { | |
| IFACEMETHODIMP AccessibilityRootNode::get_accValue(VARIANT var_id, BSTR* value) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->get_accValue(var_id, value); | ||
| return target->get_accValue(var_id, value); | ||
| } | ||
| return E_FAIL; | ||
| } | ||
|
|
||
| IFACEMETHODIMP AccessibilityRootNode::put_accValue(VARIANT var_id, BSTR new_value) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->put_accValue(var_id, new_value); | ||
| return target->put_accValue(var_id, new_value); | ||
| } | ||
| return E_FAIL; | ||
| } | ||
|
|
@@ -210,7 +221,7 @@ IFACEMETHODIMP AccessibilityRootNode::get_accSelection(VARIANT* selected) { | |
| IFACEMETHODIMP AccessibilityRootNode::accSelect(LONG flagsSelect, VARIANT var_id) { | ||
| IAccessible* target; | ||
| if ((target = GetTargetAndChildID(&var_id))) { | ||
| return window_accessible_->accSelect(flagsSelect, var_id); | ||
| return target->accSelect(flagsSelect, var_id); | ||
| } | ||
| return E_FAIL; | ||
| } | ||
|
|
@@ -231,4 +242,21 @@ IFACEMETHODIMP AccessibilityRootNode::put_accName(VARIANT var_id, BSTR put_name) | |
| return E_NOTIMPL; | ||
| } | ||
|
|
||
| void AccessibilityRootNode::SetWindow(IAccessible* window) { | ||
| window_accessible_ = window; | ||
| } | ||
|
|
||
| AccessibilityAlert* AccessibilityRootNode::GetOrCreateAlert() { | ||
| if (!alert_accessible_) { | ||
| CComObject<AccessibilityAlert>* instance = nullptr; | ||
| HRESULT hr = CComObject<AccessibilityAlert>::CreateInstance(&instance); | ||
yaakovschectman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (!SUCCEEDED(hr)) { | ||
| FML_LOG(FATAL) << "Failed to create alert accessible"; | ||
| } | ||
| instance->SetParent(this); | ||
| alert_accessible_ = instance; | ||
| } | ||
| return alert_accessible_; | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.