diff --git a/change/react-native-windows-008fe6b2-a10c-4b12-b46e-00415bf35fce.json b/change/react-native-windows-008fe6b2-a10c-4b12-b46e-00415bf35fce.json new file mode 100644 index 00000000000..125caa01ebc --- /dev/null +++ b/change/react-native-windows-008fe6b2-a10c-4b12-b46e-00415bf35fce.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "value provider changes", + "packageName": "react-native-windows", + "email": "yajurgrover24@gmail.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp index bb23cc81e62..e59c796b237 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp @@ -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 && @@ -147,6 +148,11 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTE AddRef(); } + if (patternId == UIA_ValuePatternId && accessibilityValue.text.has_value()) { + *pRetVal = static_cast(this); + AddRef(); + } + return S_OK; } @@ -327,4 +333,53 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::Invoke() { return S_OK; } +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(strongView->props()); + std::string value = winrt::to_string(val); + auto baseView = std::static_pointer_cast<::Microsoft::ReactNative::CompositionBaseComponentView>(strongView); + if (baseView == nullptr) + return UIA_E_ELEMENTNOTAVAILABLE; + baseView->updateAccessibilityValue(value); + 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(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 diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h index 6e800104c3e..da0d7053c29 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h @@ -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; @@ -36,8 +37,13 @@ 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 \ No newline at end of file diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp index e79e92a61b7..048f39b8043 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp @@ -1305,6 +1305,17 @@ void CompositionBaseComponentView::EnsureTransformMatrixFacade() noexcept { } } +void CompositionBaseComponentView::updateAccessibilityValue(std::string newValue) noexcept { + auto props = std::static_pointer_cast(this->props()); + if (newValue != props->accessibilityValue.text) { + auto provider = EnsureUiaProvider(); + if (provider != nullptr) { + winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty( + provider, UIA_ValueValuePropertyId, *props->accessibilityValue.text, newValue); + } + } +} + facebook::react::SharedViewEventEmitter CompositionBaseComponentView::eventEmitter() noexcept { return m_eventEmitter; } @@ -1387,6 +1398,11 @@ void CompositionViewComponentView::updateProps( m_visual.Opacity(newViewProps.opacity); } + if (oldViewProps.accessibilityValue.text.has_value() && newViewProps.accessibilityValue.text.has_value() && + oldViewProps.accessibilityValue.text != newViewProps.accessibilityValue.text) { + updateAccessibilityValue(*newViewProps.accessibilityValue.text); + } + // update BaseComponentView props updateAccessibilityProps(oldViewProps, newViewProps); updateShadowProps(oldViewProps, newViewProps, m_visual); diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h index 08f0b04de36..675dca42ca1 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h @@ -35,6 +35,7 @@ struct CompositionBaseComponentView : public IComponentView, bool runOnChildren(bool forward, Mso::Functor &fn) noexcept override; void onFocusLost() noexcept override; void onFocusGained() noexcept override; + void updateAccessibilityValue(std::string newValue) noexcept; void onPointerEntered( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept override; void onPointerExited(