Skip to content
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

Implement IValueProvider #12263

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "value provider changes",
"packageName": "react-native-windows",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTE
if (props == nullptr)
return UIA_E_ELEMENTNOTAVAILABLE;
auto accessibilityRole = props->accessibilityRole;
auto accessibilityValue = props->accessibilityValue;
// Invoke control pattern is used to support controls that do not maintain state
// when activated but rather initiate or perform a single, unambiguous action.
if (patternId == UIA_InvokePatternId &&
Expand All @@ -147,6 +148,11 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTE
AddRef();
}

if (patternId == UIA_ValuePatternId && accessibilityValue.text.has_value()) {
*pRetVal = static_cast<IValueProvider *>(this);
AddRef();
}

return S_OK;
}

Expand Down Expand Up @@ -327,4 +333,62 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::Invoke() {
return S_OK;
}

} // namespace winrt::Microsoft::ReactNative::implementation
std::string convertLPCWSTRToString(LPCWSTR val) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably use wint::to_string in place of this.

int strLength = WideCharToMultiByte(CP_UTF8, 0, val, -1, nullptr, 0, nullptr, nullptr);
std::string str(strLength, 0);
WideCharToMultiByte(CP_UTF8, 0, val, -1, &str[0], strLength, nullptr, nullptr);
return str;
}

BSTR StringToBSTR(const std::string &str) {
// Calculate the required BSTR size in bytes
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
if (len == 0) {
return nullptr; // Conversion error
}

// Allocate memory for the BSTR
BSTR bstr = SysAllocStringLen(nullptr, len - 1); // len includes the null terminator

// Convert the std::string to BSTR
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, bstr, len);

return bstr;
}

HRESULT __stdcall CompositionDynamicAutomationProvider::SetValue(LPCWSTR val) {
auto strongView = m_view.view();

if (!strongView)
return UIA_E_ELEMENTNOTAVAILABLE;

auto props = std::static_pointer_cast<const facebook::react::ViewProps>(strongView->props());
std::string value = convertLPCWSTRToString(val);
auto baseView = std::static_pointer_cast<::Microsoft::ReactNative::CompositionBaseComponentView>(strongView);
if (baseView == nullptr)
return UIA_E_ELEMENTNOTAVAILABLE;
winrt::IInspectable uiaProvider = baseView->EnsureUiaProvider();
UpdateUiaProperty(uiaProvider, UIA_ValueValuePropertyId, *props->accessibilityValue.text, value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setValue should go to the ComponentView, which would decide if it needs to update its state based on the new value.
Then it should be up to the ComponentView to call UpdateUiaProperty(uiaProvider, UIA_ValueValuePropertyId, *props->accessibilityValue.text, value); when the value changes. Which it would also need to do when the value changes outside of a call to the AutomationPovider::SetValue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify what you mean by going to ComponentView? I tried adding an UpdateUiaProperty() call in updateAccessibilityProps() which was getting called every time the accessibilityValue prop was updated, but there was no change in the UIA properties on insights.

Also. not sure if this is expected behaviour, but SetValue() never seemed to be getting called when updating accessibilityValue (breakpoint in the function was never hit). My guess was that the accessibilityValue example in accesible.tsx doesn't call SetValue() to update the property.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to SetValue on the AutomationProvider would only occur if the accessibility tool wants to manipulate the value. This would happen say through voice control or if you select the value pattern actions button in insights and set the value from there.

That value set should then go to the component view to actually modify its value.

Separately, whenever the component view updates its value it needs to notify the automation peer that its value has been changed. That should happen through a call to UpdateUiaProperty, which would notify the automation provider of the new value, and then the automation provider should call UiaRaiseAutomationEvent which will notify say insights that the value has changed, at which point it can decide if it needs the updates value, and it would call back into the automation peer's getvalue.


return S_OK;
}

HRESULT __stdcall CompositionDynamicAutomationProvider::get_Value(BSTR* pRetVal) {
if (pRetVal == nullptr)
return E_POINTER;
auto strongView = m_view.view();

if (!strongView)
return UIA_E_ELEMENTNOTAVAILABLE;

auto props = std::static_pointer_cast<const facebook::react::ViewProps>(strongView->props());
*pRetVal = StringToBSTR(*props->accessibilityValue.text);

return S_OK;
}

HRESULT __stdcall CompositionDynamicAutomationProvider::get_IsReadOnly(BOOL* pRetVal) {
return S_OK;
}

} // namespace winrt::Microsoft::ReactNative::implementation
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
IInspectable,
IRawElementProviderFragment,
IRawElementProviderSimple,
IInvokeProvider> {
IInvokeProvider,
IValueProvider> {
public:
CompositionDynamicAutomationProvider(
const std::shared_ptr<::Microsoft::ReactNative::CompositionBaseComponentView> &componentView) noexcept;
Expand All @@ -36,8 +37,12 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
// inherited via IInvokeProvider
virtual HRESULT __stdcall Invoke() override;

// inherited via IValueProvider
virtual HRESULT __stdcall SetValue(LPCWSTR val) override;
virtual HRESULT __stdcall get_Value(BSTR *pRetVal) override;
virtual HRESULT __stdcall get_IsReadOnly(BOOL *pRetVal) override;
private:
::Microsoft::ReactNative::ReactTaggedView m_view;
};

} // namespace winrt::Microsoft::ReactNative::implementation
} // namespace winrt::Microsoft::ReactNative::implementation
Loading