-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic implementation of UIA navigation functions #11412
Merged
Merged
Changes from 12 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
1f33633
commit in progress work for merge
FalseLobster 8e5631b
Merge branch 'main' of https://github.com/microsoft/react-native-wind…
FalseLobster b13b132
Mostly working UIA tree nav functions
FalseLobster 96483ee
Merge branch 'main' of https://github.com/microsoft/react-native-wind…
FalseLobster 72df68a
missed merge file
FalseLobster 8731b46
fix some merges
FalseLobster f633659
fix merge issue, add uia helpers
FalseLobster 45fd010
Merge branch 'main' of https://github.com/microsoft/react-native-wind…
FalseLobster b412ca9
missed merge files
FalseLobster 24b3161
More merge fixesh
FalseLobster f6d9771
Change files
FalseLobster 1063206
yarn format
FalseLobster 0199cee
address PR comments
FalseLobster 61d4b90
Merge branch 'main' of https://github.com/microsoft/react-native-wind…
FalseLobster 11f1dd3
missed a role mapping
FalseLobster 9f29b0c
Merge branch 'main' into allynav
jonthysell 6dee8cd
Merge branch 'main' into allynav
jonthysell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/react-native-windows-805ff496-5174-485f-98b2-e4ce60bcb438.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "prerelease", | ||
"comment": "Add UIA Navigate to Fabric Providers", | ||
"packageName": "react-native-windows", | ||
"email": "adrum@microsoft.com", | ||
"dependentChangeType": "patch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
227 changes: 227 additions & 0 deletions
227
vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
#include "pch.h" | ||
#include "CompositionDynamicAutomationProvider.h" | ||
#include <Fabric/ComponentView.h> | ||
#pragma warning(push) | ||
#pragma warning(disable : 4229) | ||
#define IN | ||
#define OUT | ||
#include <atlsafe.h> | ||
#pragma warning(pop) | ||
#include "RootComponentView.h" | ||
#include "UiaHelpers.h" | ||
|
||
namespace winrt::Microsoft::ReactNative::implementation { | ||
|
||
CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider( | ||
const std::shared_ptr<::Microsoft::ReactNative::CompositionBaseComponentView> &componentView) noexcept | ||
: m_view{std::static_pointer_cast<::Microsoft::ReactNative::IComponentView>(componentView)} {} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate( | ||
NavigateDirection direction, | ||
IRawElementProviderFragment **pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
return UiaNavigateHelper(m_view, direction, *pRetVal); | ||
} | ||
|
||
// Implementations should return NULL for a top-level element that is hosted in a window. Other elements should return | ||
// an array that contains UiaAppendRuntimeId (defined in Uiautomationcoreapi.h), followed by a value that is unique | ||
// within an instance of the fragment. | ||
// | ||
// We'll use the react tag as our identifier for those situations | ||
HRESULT __stdcall CompositionDynamicAutomationProvider::GetRuntimeId(SAFEARRAY **pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
*pRetVal = nullptr; | ||
|
||
auto strongView = m_view.view(); | ||
|
||
if (!strongView) | ||
return UIA_E_ELEMENTNOTAVAILABLE; | ||
|
||
CComSafeArray<int32_t> runtimeId; | ||
auto hr = runtimeId.Create(2); | ||
|
||
if (FAILED(hr)) | ||
return hr; | ||
|
||
runtimeId[0] = UiaAppendRuntimeId; | ||
runtimeId[1] = strongView->tag(); | ||
|
||
*pRetVal = runtimeId.Detach(); | ||
|
||
return S_OK; | ||
} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::get_BoundingRectangle(UiaRect *pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
return S_OK; | ||
} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::GetEmbeddedFragmentRoots(SAFEARRAY **pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
*pRetVal = nullptr; | ||
|
||
return S_OK; | ||
} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::SetFocus(void) { | ||
return S_OK; | ||
} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
auto strongView = m_view.view(); | ||
|
||
if (!strongView) | ||
return UIA_E_ELEMENTNOTAVAILABLE; | ||
|
||
auto rootCV = strongView->rootComponentView(); | ||
if (rootCV == nullptr) | ||
return UIA_E_ELEMENTNOTAVAILABLE; | ||
|
||
auto uiaProvider = rootCV->EnsureUiaProvider(); | ||
if (uiaProvider != nullptr) { | ||
winrt::com_ptr<IRawElementProviderFragmentRoot> spReps; | ||
uiaProvider.as(spReps); | ||
*pRetVal = spReps.detach(); | ||
} | ||
|
||
return S_OK; | ||
} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::get_ProviderOptions(ProviderOptions *pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
*pRetVal = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading; | ||
return S_OK; | ||
} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTERNID patternId, IUnknown **pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
*pRetVal = nullptr; | ||
|
||
return S_OK; | ||
} | ||
|
||
long GetControlType(const std::string &role) noexcept { | ||
if (role == "adjustable") { | ||
return UIA_SliderControlTypeId; | ||
} else if (role == "alert" || role == "group" || role == "search" || role == "timer" || role.empty()) { | ||
return UIA_GroupControlTypeId; | ||
} else if ( | ||
role == "button" || role == "imagebutton" || role == "keyboardkey" || role == "switch" || | ||
FalseLobster marked this conversation as resolved.
Show resolved
Hide resolved
|
||
role == "togglebutton") { | ||
return UIA_ButtonControlTypeId; | ||
} else if (role == "checkbox") { | ||
return UIA_CheckBoxControlTypeId; | ||
} else if (role == "combobox") { | ||
return UIA_ComboBoxControlTypeId; | ||
} else if (role == "header" || role == "summary" || role == "text") { | ||
return UIA_TextControlTypeId; | ||
} else if (role == "image") { | ||
return UIA_ImageControlTypeId; | ||
} else if (role == "link") { | ||
return UIA_HyperlinkControlTypeId; | ||
} else if (role == "list") { | ||
FalseLobster marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return UIA_ListControlTypeId; | ||
} else if (role == "listitem") { | ||
return UIA_ListItemControlTypeId; | ||
} else if (role == "menu") { | ||
return UIA_MenuControlTypeId; | ||
} else if (role == "menubar") { | ||
return UIA_MenuBarControlTypeId; | ||
} else if (role == "menuitem") { | ||
return UIA_MenuItemControlTypeId; | ||
} | ||
// If role is "none", remove the element from the control tree | ||
// and expose it as a plain element would in the raw tree. | ||
else if (role == "none") { | ||
return UIA_GroupControlTypeId; | ||
} else if (role == "progressbar") { | ||
return UIA_ProgressBarControlTypeId; | ||
} else if (role == "radio") { | ||
return UIA_RadioButtonControlTypeId; | ||
} else if (role == "radiogroup") { | ||
return UIA_ListControlTypeId; | ||
} else if (role == "scrollbar") { | ||
return UIA_ScrollBarControlTypeId; | ||
} else if (role == "spinbutton") { | ||
return UIA_SpinnerControlTypeId; | ||
} else if (role == "splitbutton") { | ||
return UIA_SplitButtonControlTypeId; | ||
} else if (role == "tab") { | ||
return UIA_TabItemControlTypeId; | ||
} else if (role == "tablist") { | ||
return UIA_TabControlTypeId; | ||
} else if (role == "toolbar") { | ||
return UIA_ToolBarControlTypeId; | ||
} else if (role == "tree") { | ||
return UIA_TreeControlTypeId; | ||
} else if (role == "treeitem") { | ||
return UIA_TreeItemControlTypeId; | ||
} | ||
assert(false); | ||
return UIA_GroupControlTypeId; | ||
} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::GetPropertyValue(PROPERTYID propertyId, VARIANT *pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
auto strongView = m_view.view(); | ||
if (strongView == nullptr) | ||
return UIA_E_ELEMENTNOTAVAILABLE; | ||
|
||
auto props = std::static_pointer_cast<const facebook::react::ViewProps>(strongView->props()); | ||
if (props == nullptr) | ||
return UIA_E_ELEMENTNOTAVAILABLE; | ||
|
||
switch (propertyId) { | ||
case UIA_ControlTypePropertyId: { | ||
pRetVal->vt = VT_I4; | ||
auto role = props->accessibilityRole; | ||
pRetVal->lVal = GetControlType(role); | ||
break; | ||
} | ||
case UIA_AutomationIdPropertyId: { | ||
pRetVal->vt = VT_BSTR; | ||
auto testId = props->testId; | ||
CComBSTR temp(testId.c_str()); | ||
pRetVal->bstrVal = temp.Detach(); | ||
break; | ||
} | ||
case UIA_NamePropertyId: { | ||
pRetVal->vt = VT_BSTR; | ||
auto name = props->accessibilityLabel; | ||
CComBSTR temp(name.c_str()); | ||
pRetVal->bstrVal = temp.Detach(); | ||
break; | ||
} | ||
} | ||
|
||
return S_OK; | ||
} | ||
|
||
HRESULT __stdcall CompositionDynamicAutomationProvider::get_HostRawElementProvider( | ||
IRawElementProviderSimple **pRetVal) { | ||
if (pRetVal == nullptr) | ||
return E_POINTER; | ||
|
||
*pRetVal = nullptr; | ||
|
||
return S_OK; | ||
} | ||
|
||
} // namespace winrt::Microsoft::ReactNative::implementation |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"alert" returned Text before
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I mentioned in the description that some of the ways Paper is mapping roles to UIA control types doesn't agree with the ARIA Core AAM. Given Meta seems to be moving more toward ARIA standards, I thought I'd fix this now, but I suppose we can discuss this more and I'll leave the original mapping in place for now.