diff --git a/change/react-native-windows-dd7d3409-cce5-4725-b7d9-722b0e961a78.json b/change/react-native-windows-dd7d3409-cce5-4725-b7d9-722b0e961a78.json new file mode 100644 index 00000000000..c22c630e43e --- /dev/null +++ b/change/react-native-windows-dd7d3409-cce5-4725-b7d9-722b0e961a78.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "working impl of getBoundingRect and ElementProviderFromPoint", + "packageName": "react-native-windows", + "email": "adrum@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/vnext/Microsoft.ReactNative/Fabric/ComponentView.h b/vnext/Microsoft.ReactNative/Fabric/ComponentView.h index 456ba502155..bdeef7cd2b1 100644 --- a/vnext/Microsoft.ReactNative/Fabric/ComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/ComponentView.h @@ -60,7 +60,12 @@ struct IComponentView { virtual facebook::react::SharedTouchEventEmitter touchEventEmitterAtPoint(facebook::react::Point pt) noexcept = 0; virtual facebook::react::SharedTouchEventEmitter touchEventEmitter() noexcept = 0; virtual facebook::react::Tag tag() const noexcept = 0; - virtual facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept = 0; + // By default, hitTests according the pointerEvents prop on the Component. + // If ignorePointerEvents = true, all Components are treated as valid targets + virtual facebook::react::Tag hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents = false) const noexcept = 0; virtual int64_t sendMessage(uint32_t msg, uint64_t wParam, int64_t lParam) noexcept = 0; virtual winrt::IInspectable EnsureUiaProvider() noexcept = 0; }; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/AbiCompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/AbiCompositionViewComponentView.cpp index 717cffc534e..9d96550668f 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/AbiCompositionViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/AbiCompositionViewComponentView.cpp @@ -119,10 +119,11 @@ winrt::Microsoft::ReactNative::Composition::IVisual AbiCompositionViewComponentV facebook::react::Tag AbiCompositionViewComponentView::hitTest( facebook::react::Point pt, - facebook::react::Point &localPt) const noexcept { + facebook::react::Point &localPt, + bool ignorePointerEvents) const noexcept { facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && ptLocal.y <= m_layoutMetrics.frame.size.height) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/AbiCompositionViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/AbiCompositionViewComponentView.h index 0b59d40a095..98e0e9550c1 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/AbiCompositionViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/AbiCompositionViewComponentView.h @@ -43,7 +43,8 @@ struct AbiCompositionViewComponentView : CompositionBaseComponentView { std::vector supplementalComponentDescriptorProviders() noexcept override; facebook::react::Props::Shared props() noexcept override; - facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept override; + facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt, bool ignorePointerEvents) + const noexcept override; winrt::Microsoft::ReactNative::Composition::IVisual Visual() const noexcept override; private: diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp index a10a337a714..b1fa25d5f51 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp @@ -58,7 +58,7 @@ ComponentViewDescriptor const &ComponentViewRegistry::dequeueComponentViewWithCo } else if (componentHandle == facebook::react::SwitchShadowNode::Handle()) { view = SwitchComponentView::Create(compContext, tag, m_context); } else if (componentHandle == facebook::react::RootShadowNode::Handle()) { - view = RootComponentView::Create(compContext, tag); + view = RootComponentView::Create(compContext, tag, m_context); } else if ( componentHandle == facebook::react::RawTextShadowNode::Handle() || componentHandle == facebook::react::TextShadowNode::Handle()) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp index 6edf62b2b55..8b873f050ef 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp @@ -59,6 +59,30 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::get_BoundingRectangle(Ui if (pRetVal == nullptr) return E_POINTER; + auto hr = UiaGetBoundingRectangleHelper(m_view, *pRetVal); + if (FAILED(hr)) + return hr; + + // Since get_BoundingRectangle needs to provide real screen coordinates back to the UIA client + // we'll use the FragmentRoot's origin to offset our rect because that should have been taken + // into account already. + winrt::com_ptr spFragmentRoot = nullptr; + hr = get_FragmentRoot(spFragmentRoot.put()); + if (FAILED(hr)) + return hr; + + auto spFragment = spFragmentRoot.try_as(); + if (spFragment == nullptr) + return E_FAIL; + + UiaRect rect; + hr = spFragment->get_BoundingRectangle(&rect); + if (FAILED(hr)) + return hr; + + pRetVal->left += rect.left; + pRetVal->top += rect.top; + return S_OK; } @@ -89,10 +113,9 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::get_FragmentRoot(IRawEle return UIA_E_ELEMENTNOTAVAILABLE; auto uiaProvider = rootCV->EnsureUiaProvider(); - if (uiaProvider != nullptr) { - winrt::com_ptr spReps; - uiaProvider.as(spReps); - *pRetVal = spReps.detach(); + auto spFragmentRoot = uiaProvider.try_as(); + if (spFragmentRoot) { + *pRetVal = spFragmentRoot.detach(); } return S_OK; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp index 3b6b3282abb..8ed4f784d5e 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp @@ -55,6 +55,7 @@ HRESULT __stdcall CompositionRootAutomationProvider::get_HostRawElementProvider( if (pRetVal == nullptr) return E_POINTER; + // TODO: assumes windowed if (!IsWindow(m_hwnd)) return UIA_E_ELEMENTNOTAVAILABLE; @@ -67,6 +68,24 @@ HRESULT __stdcall CompositionRootAutomationProvider::get_BoundingRectangle(UiaRe if (pRetVal == nullptr) return E_POINTER; + // TODO: Need host site offsets + // Assume we're hosted in some other visual-based hosting site + if (m_hwnd == nullptr || !IsWindow(m_hwnd)) { + return UiaGetBoundingRectangleHelper(m_view, *pRetVal); + } + + POINT point{0, 0}; + ClientToScreen(m_hwnd, &point); + pRetVal->left = point.x; + pRetVal->top = point.y; + RECT rect; + GetClientRect(m_hwnd, &rect); + point.x = rect.right; + point.y = rect.bottom; + ClientToScreen(m_hwnd, &point); + pRetVal->width = point.x - pRetVal->left; + pRetVal->height = point.y - pRetVal->top; + return S_OK; } @@ -97,6 +116,31 @@ HRESULT __stdcall CompositionRootAutomationProvider::ElementProviderFromPoint( *pRetVal = nullptr; + auto strongView = m_view.view(); + + if (strongView == nullptr) { + return UIA_E_ELEMENTNOTAVAILABLE; + } + + auto spRootView = strongView->rootComponentView(); + if (spRootView == nullptr) { + return UIA_E_ELEMENTNOTAVAILABLE; + } + + if (m_hwnd == nullptr || !IsWindow(m_hwnd)) { + // TODO: Add support for non-HWND based hosting + return E_FAIL; + } + + POINT clientPoint{static_cast(x), static_cast(y)}; + ScreenToClient(m_hwnd, &clientPoint); + + auto provider = spRootView->UiaProviderFromPoint(clientPoint); + auto spFragment = provider.try_as(); + if (spFragment) { + *pRetVal = spFragment.detach(); + } + return S_OK; } @@ -117,9 +161,10 @@ HRESULT __stdcall CompositionRootAutomationProvider::GetFocus(IRawElementProvide if (focusedComponent) { auto focusedUiaProvider = focusedComponent->EnsureUiaProvider(); - winrt::com_ptr spFragment; - focusedUiaProvider.as(spFragment); - *pRetVal = spFragment.detach(); + auto spFragment = focusedUiaProvider.try_as(); + if (spFragment) { + *pRetVal = spFragment.detach(); + } } return S_OK; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp index 658ea0fd3cd..a9f0c1e56bb 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp @@ -1267,13 +1267,15 @@ void CompositionViewComponentView::updateProps( m_props = std::static_pointer_cast(props); } -facebook::react::Tag CompositionViewComponentView::hitTest(facebook::react::Point pt, facebook::react::Point &localPt) - const noexcept { +facebook::react::Tag CompositionViewComponentView::hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents) const noexcept { facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; facebook::react::Tag targetTag; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxNone) && std::any_of(m_children.rbegin(), m_children.rend(), [&targetTag, &ptLocal, &localPt](auto child) { targetTag = static_cast(child)->hitTest(ptLocal, localPt); @@ -1281,7 +1283,7 @@ facebook::react::Tag CompositionViewComponentView::hitTest(facebook::react::Poin })) return targetTag; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && ptLocal.y <= m_layoutMetrics.frame.size.height) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h index f2c41f62feb..cb89637f25f 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h @@ -114,7 +114,10 @@ struct CompositionViewComponentView : public CompositionBaseComponentView { facebook::react::Props::Shared props() noexcept override; - facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept override; + facebook::react::Tag hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents = false) const noexcept override; bool ScrollWheel(facebook::react::Point pt, int32_t delta) noexcept override; winrt::Microsoft::ReactNative::Composition::IVisual Visual() const noexcept override; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp index f445eb1dc2b..66f4cda1c1b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp @@ -362,13 +362,15 @@ facebook::react::Props::Shared ImageComponentView::props() noexcept { return m_props; } -facebook::react::Tag ImageComponentView::hitTest(facebook::react::Point pt, facebook::react::Point &localPt) - const noexcept { +facebook::react::Tag ImageComponentView::hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents) const noexcept { facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; facebook::react::Tag targetTag; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxNone) && std::any_of(m_children.rbegin(), m_children.rend(), [&targetTag, &ptLocal, &localPt](auto child) { targetTag = static_cast(child)->hitTest(ptLocal, localPt); @@ -376,7 +378,7 @@ facebook::react::Tag ImageComponentView::hitTest(facebook::react::Point pt, face })) return targetTag; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && ptLocal.y <= m_layoutMetrics.frame.size.height) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h index f324f52a536..ab80d091e9f 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.h @@ -49,7 +49,8 @@ struct ImageComponentView : CompositionBaseComponentView { facebook::react::Props::Shared props() noexcept override; void OnRenderingDeviceLost() noexcept override; - facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept override; + facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt, bool ignorePointerEvents) + const noexcept override; winrt::Microsoft::ReactNative::Composition::IVisual Visual() const noexcept override; bool focusable() const noexcept override; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp index 4804d964bb9..829054d9314 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp @@ -99,8 +99,10 @@ facebook::react::Props::Shared ParagraphComponentView::props() noexcept { return m_props; } -facebook::react::Tag ParagraphComponentView::hitTest(facebook::react::Point pt, facebook::react::Point &localPt) - const noexcept { +facebook::react::Tag ParagraphComponentView::hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents) const noexcept { facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; facebook::react::Tag targetTag; @@ -115,7 +117,7 @@ facebook::react::Tag ParagraphComponentView::hitTest(facebook::react::Point pt, return targetTag; */ - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && ptLocal.y <= m_layoutMetrics.frame.size.height) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h index 22d8bc81d8f..530785676f1 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h @@ -36,7 +36,8 @@ struct ParagraphComponentView : CompositionBaseComponentView { void finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept override; void prepareForRecycle() noexcept override; facebook::react::Props::Shared props() noexcept override; - facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept override; + facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt, bool ignorePointerEvents) + const noexcept override; void OnRenderingDeviceLost() noexcept override; facebook::react::SharedTouchEventEmitter touchEventEmitterAtPoint(facebook::react::Point pt) noexcept override; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp index 700f22cd6c7..c0c6929e7c7 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp @@ -6,6 +6,7 @@ #include "RootComponentView.h" +#include #include "CompositionRootAutomationProvider.h" #include "CompositionRootView.h" @@ -13,13 +14,15 @@ namespace Microsoft::ReactNative { RootComponentView::RootComponentView( const winrt::Microsoft::ReactNative::Composition::ICompositionContext &compContext, - facebook::react::Tag tag) - : Super(compContext, tag) {} + facebook::react::Tag tag, + winrt::Microsoft::ReactNative::ReactContext const &reactContext) + : Super(compContext, tag), m_context(reactContext) {} std::shared_ptr RootComponentView::Create( const winrt::Microsoft::ReactNative::Composition::ICompositionContext &compContext, - facebook::react::Tag tag) noexcept { - return std::shared_ptr(new RootComponentView(compContext, tag)); + facebook::react::Tag tag, + winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept { + return std::shared_ptr(new RootComponentView(compContext, tag, reactContext)); } RootComponentView *RootComponentView::rootComponentView() noexcept { @@ -113,4 +116,23 @@ std::shared_ptr RootComponentView::getPtr() { return std::static_pointer_cast(shared_from_this()); } +winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixels) noexcept { + facebook::react::Point ptDips{ + static_cast(ptPixels.x) / m_layoutMetrics.pointScaleFactor, + static_cast(ptPixels.y) / m_layoutMetrics.pointScaleFactor}; + + facebook::react::Point localPt; + auto tag = hitTest(ptDips, localPt, true); + + auto uiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties()); + if (uiManager == nullptr) + return nullptr; + + auto view = uiManager->GetViewRegistry().findComponentViewWithTag(tag); + if (view == nullptr) + return nullptr; + + return view->EnsureUiaProvider(); +} + } // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h index 35dd7169746..032907025eb 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h @@ -18,7 +18,8 @@ struct RootComponentView : CompositionViewComponentView { [[nodiscard]] static std::shared_ptr Create( const winrt::Microsoft::ReactNative::Composition::ICompositionContext &compContext, - facebook::react::Tag tag) noexcept; + facebook::react::Tag tag, + winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept; std::shared_ptr getPtr(); @@ -33,11 +34,15 @@ struct RootComponentView : CompositionViewComponentView { winrt::IInspectable EnsureUiaProvider() noexcept override; + winrt::IInspectable UiaProviderFromPoint(const POINT &ptPixels) noexcept; + private: RootComponentView( const winrt::Microsoft::ReactNative::Composition::ICompositionContext &compContext, - facebook::react::Tag tag); + facebook::react::Tag tag, + winrt::Microsoft::ReactNative::ReactContext const &reactContext); ::Microsoft::ReactNative::IComponentView *m_focusedComponent = nullptr; + winrt::Microsoft::ReactNative::ReactContext m_context; }; } // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp index c1fd976ecc9..a073d204f37 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp @@ -335,8 +335,10 @@ void ScrollViewComponentView::ensureVisual() noexcept { } } -facebook::react::Tag ScrollViewComponentView::hitTest(facebook::react::Point pt, facebook::react::Point &localPt) - const noexcept { +facebook::react::Tag ScrollViewComponentView::hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents) const noexcept { facebook::react::Point ptViewport{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; facebook::react::Point ptContent{ @@ -344,7 +346,7 @@ facebook::react::Tag ScrollViewComponentView::hitTest(facebook::react::Point pt, ptViewport.y + m_scrollVisual.ScrollPosition().y / m_layoutMetrics.pointScaleFactor}; facebook::react::Tag targetTag; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxNone) && std::any_of(m_children.rbegin(), m_children.rend(), [&targetTag, &ptContent, &localPt](auto child) { targetTag = static_cast(child)->hitTest(ptContent, localPt); @@ -352,7 +354,7 @@ facebook::react::Tag ScrollViewComponentView::hitTest(facebook::react::Point pt, })) return targetTag; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && ptViewport.x >= 0 && ptViewport.x <= m_layoutMetrics.frame.size.width && ptViewport.y >= 0 && ptViewport.y <= m_layoutMetrics.frame.size.height) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h index c9336666050..f7c21368558 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h @@ -71,7 +71,8 @@ struct ScrollInteractionTrackerOwner : public winrt::implements< facebook::react::Props::Shared props() noexcept override; void handleCommand(std::string const &commandName, folly::dynamic const &arg) noexcept override; - facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept override; + facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt, bool ignorePointerEvents) + const noexcept override; winrt::Microsoft::ReactNative::Composition::IVisual Visual() const noexcept override; // void OnPointerDown(const winrt::Windows::UI::Input::PointerPoint &pp) noexcept override; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp index 8f1a5bc8bff..f58e8fd3f99 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp @@ -214,11 +214,13 @@ void SwitchComponentView::ensureDrawingSurface() noexcept { } } -facebook::react::Tag SwitchComponentView::hitTest(facebook::react::Point pt, facebook::react::Point &localPt) - const noexcept { +facebook::react::Tag SwitchComponentView::hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents) const noexcept { facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && ptLocal.y <= m_layoutMetrics.frame.size.height) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h index 3a27b0449b0..46f53e28301 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h @@ -38,7 +38,8 @@ struct SwitchComponentView : CompositionBaseComponentView { facebook::react::Props::Shared props() noexcept override; bool focusable() const noexcept override; - facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept override; + facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt, bool ignorePointerEvents) + const noexcept override; winrt::Microsoft::ReactNative::Composition::IVisual Visual() const noexcept override; int64_t sendMessage(uint32_t msg, uint64_t wParam, int64_t lParam) noexcept override; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp index 6071e916720..c7a79973a3b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp @@ -1031,8 +1031,10 @@ void WindowsTextInputComponentView::DrawText() noexcept { m_needsRedraw = false; } -facebook::react::Tag WindowsTextInputComponentView::hitTest(facebook::react::Point pt, facebook::react::Point &localPt) - const noexcept { +facebook::react::Tag WindowsTextInputComponentView::hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents) const noexcept { facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; facebook::react::Tag targetTag; @@ -1047,7 +1049,7 @@ facebook::react::Tag WindowsTextInputComponentView::hitTest(facebook::react::Poi return targetTag; */ - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && ptLocal.y <= m_layoutMetrics.frame.size.height) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h index a64875fcea0..f8f608e2ce6 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h @@ -43,7 +43,10 @@ struct WindowsTextInputComponentView : CompositionBaseComponentView { facebook::react::Props::Shared props() noexcept override; void handleCommand(std::string const &commandName, folly::dynamic const &arg) noexcept override; int64_t sendMessage(uint32_t msg, uint64_t wParam, int64_t lParam) noexcept override; - facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept override; + facebook::react::Tag hitTest( + facebook::react::Point pt, + facebook::react::Point &localPt, + bool ignorePointerEvents = false) const noexcept override; void OnRenderingDeviceLost() noexcept override; winrt::Microsoft::ReactNative::Composition::IVisual Visual() const noexcept override; void onFocusLost() noexcept override; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp index f423ea86edb..2cc2f1d542b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp @@ -61,11 +61,28 @@ HRESULT UiaNavigateHelper( } break; } - if (uiaProvider != nullptr) { - winrt::com_ptr spFragment; - uiaProvider.as(spFragment); + auto spFragment = uiaProvider.try_as(); + if (spFragment != nullptr) { retVal = spFragment.detach(); } + + return S_OK; +} + +HRESULT UiaGetBoundingRectangleHelper(::Microsoft::ReactNative::ReactTaggedView &view, UiaRect &rect) noexcept { + auto strongView = view.view(); + + if (strongView == nullptr) { + return UIA_E_ELEMENTNOTAVAILABLE; + } + + auto clientRect = strongView->getClientRect(); + + rect.left = clientRect.left; + rect.top = clientRect.top; + rect.width = clientRect.right - clientRect.left; + rect.height = clientRect.bottom - clientRect.top; + return S_OK; } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h index 4b533cfa6ae..e296786c0f8 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h @@ -13,4 +13,6 @@ UiaNavigateHelper( NavigateDirection direction, IRawElementProviderFragment *&retVal) noexcept; +HRESULT UiaGetBoundingRectangleHelper(::Microsoft::ReactNative::ReactTaggedView &view, UiaRect &rect) noexcept; + } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.cpp index 8e9872d17ab..40e3dc7b3d5 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.cpp @@ -151,10 +151,11 @@ winrt::Microsoft::ReactNative::Composition::IVisual UnimplementedNativeViewCompo facebook::react::Tag UnimplementedNativeViewComponentView::hitTest( facebook::react::Point pt, - facebook::react::Point &localPt) const noexcept { + facebook::react::Point &localPt, + bool ignorePointerEvents) const noexcept { facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y}; - if ((m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || + if ((ignorePointerEvents || m_props->pointerEvents == facebook::react::PointerEventsMode::Auto || m_props->pointerEvents == facebook::react::PointerEventsMode::BoxOnly) && ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && ptLocal.y <= m_layoutMetrics.frame.size.height) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.h index 06297f084e1..ddf8c284bfb 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.h @@ -34,7 +34,8 @@ struct UnimplementedNativeViewComponentView : CompositionBaseComponentView { void finalizeUpdates(RNComponentViewUpdateMask updateMask) noexcept override; void prepareForRecycle() noexcept override; facebook::react::Props::Shared props() noexcept override; - facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt) const noexcept override; + facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt, bool ignorePointerEvents) + const noexcept override; winrt::Microsoft::ReactNative::Composition::IVisual Visual() const noexcept override; winrt::Microsoft::ReactNative::Composition::IVisual OuterVisual() const noexcept override;