-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Xaml UI Hosting #15223
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
base: main
Are you sure you want to change the base?
Xaml UI Hosting #15223
Changes from all commits
28b1471
7cb5b7e
ffa1c03
b7cbe82
ed1e67c
0a95df7
6861056
b3d5b4f
c90b1da
9357561
805ba4d
46dc340
9889e8c
284c423
522cf31
529423b
9b6aa17
b9036c3
11c54ea
6b92ff5
b179f47
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "type": "prerelease", | ||
| "comment": "Introduce xamlhost component", | ||
| "packageName": "react-native-windows", | ||
| "email": "[email protected]", | ||
| "dependentChangeType": "patch" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <Application | ||
| x:Class="Microsoft.ReactNative.Xaml.XamlApplication" | ||
| xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> | ||
| </Application> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -178,14 +178,12 @@ ContentIslandComponentView::~ContentIslandComponentView() noexcept { | |
| void ContentIslandComponentView::MountChildComponentView( | ||
| const winrt::Microsoft::ReactNative::ComponentView &childComponentView, | ||
| uint32_t index) noexcept { | ||
| assert(false); | ||
| base_type::MountChildComponentView(childComponentView, index); | ||
| } | ||
|
|
||
| void ContentIslandComponentView::UnmountChildComponentView( | ||
| const winrt::Microsoft::ReactNative::ComponentView &childComponentView, | ||
| uint32_t index) noexcept { | ||
| assert(false); | ||
|
Comment on lines
-181
to
-188
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. Were these asserts originally put to ensure control doesn't reach here? More like unreachable code but now we've a genuine islands scenario that have them removed?
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. yes sundar |
||
| base_type::UnmountChildComponentView(childComponentView, index); | ||
| } | ||
|
|
||
|
|
@@ -212,6 +210,25 @@ void ContentIslandComponentView::prepareForRecycle() noexcept { | |
| Super::prepareForRecycle(); | ||
| } | ||
|
|
||
| facebook::react::Tag ContentIslandComponentView::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}; | ||
|
|
||
| // This is similar to ViewComponentView::hitTest, but we don't want to hit test the children of this node, | ||
| // because the child ComponentView is kind of a dummy representation of the XamlElement and doesn't do anything. | ||
| // So, we just hit test the ContentIsland itself to make the UIA ElementProviderFromPoint call work. | ||
| // TODO: Will this cause a problem -- does this function need to do something different for non-UIA scenarios? | ||
|
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. I'm curious of Andrew's thoughts on the best way to handle ContentIslandComponentView::hitTest
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.
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. Why do you need this override? What kinds of components are you putting in the ContentIslandComponentView? I'd expect that the components you'd be putting in the XamlHost view would be Xaml components. And those should not inherit from ViewComponent, so they would be ignored by the default hit test, no? |
||
| if (ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 && | ||
| ptLocal.y <= m_layoutMetrics.frame.size.height) { | ||
| localPt = ptLocal; | ||
| return Tag(); | ||
| } | ||
|
|
||
| return -1; | ||
| } | ||
|
|
||
| void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept { | ||
| // This automation mode must be set before connecting the child ContentIsland. | ||
| // It puts the child content into a mode where it won't own its own framework root. Instead, the child island's | ||
|
|
@@ -262,6 +279,12 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept { | |
| args.AutomationProvider(nullptr); | ||
| args.Handled(true); | ||
| }); | ||
|
|
||
| if (m_uiaProvider) { | ||
| auto providerImpl = | ||
| m_uiaProvider.as<winrt::Microsoft::ReactNative::implementation::CompositionDynamicAutomationProvider>(); | ||
| providerImpl->SetChildSiteLink(m_childSiteLink); | ||
| } | ||
| } | ||
|
|
||
| } // namespace winrt::Microsoft::ReactNative::Composition::implementation | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
|
|
||
| #include <Fabric/FabricUIManagerModule.h> | ||
| #include <winrt/Microsoft.UI.Input.h> | ||
| #include "CompositionDynamicAutomationProvider.h" | ||
| #include "CompositionRootAutomationProvider.h" | ||
| #include "ReactNativeIsland.h" | ||
| #include "Theme.h" | ||
|
|
@@ -275,7 +276,7 @@ facebook::react::Point RootComponentView::getClientOffset() const noexcept { | |
| return {}; | ||
| } | ||
|
|
||
| winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixels) noexcept { | ||
| winrt::IUnknown RootComponentView::UiaProviderFromPoint(const POINT &ptPixels, const POINT &ptScreen) noexcept { | ||
| facebook::react::Point ptDips{ | ||
| static_cast<facebook::react::Float>(ptPixels.x) / m_layoutMetrics.pointScaleFactor, | ||
| static_cast<facebook::react::Float>(ptPixels.y) / m_layoutMetrics.pointScaleFactor}; | ||
|
|
@@ -295,7 +296,41 @@ winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixel | |
| if (view == nullptr) | ||
| return nullptr; | ||
|
|
||
| return winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(view)->EnsureUiaProvider(); | ||
| auto uiaProvider = | ||
| winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(view)->EnsureUiaProvider(); | ||
|
|
||
| // TODO: Avoid exposing CompositionDynamicAutomationProvider in RootComponentView | ||
|
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. Please file a follow-up task for this and paste its link here.
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. #15317 - I already created a task and adding pending sub tasks to it. Will add this here. |
||
| auto dynamicProvider = | ||
| uiaProvider.try_as<winrt::Microsoft::ReactNative::implementation::CompositionDynamicAutomationProvider>(); | ||
| if (dynamicProvider) { | ||
| if (auto childProvider = dynamicProvider->TryGetChildSiteLinkAutomationProvider()) { | ||
| // ChildProvider is the the automation provider from the ChildSiteLink. In the case of WinUI, this | ||
| // is a pointer to WinUI's internal CUIAHostWindow object. | ||
| // It seems odd, but even though this node doesn't behave as a fragment root in our case (the real fragment root | ||
| // is the RootComponentView's UIA provider), we still use its IRawElementProviderFragmentRoot -- just so | ||
| // we can do the ElementProviderFromPoint call. (this was recommended by the team who did the initial | ||
| // architecture work). | ||
| if (auto fragmentRoot = childProvider.try_as<IRawElementProviderFragmentRoot>()) { | ||
| com_ptr<IRawElementProviderFragment> frag; | ||
| // WinUI then does its own hitTest inside the XAML tree. | ||
| fragmentRoot->ElementProviderFromPoint( | ||
| ptScreen | ||
| .x, // Note since we're going through IRawElementProviderFragment the coordinates are in screen space. | ||
| ptScreen.y, | ||
| frag.put()); | ||
| // We return the specific child provider(frag) when hosted XAML has an element | ||
| // under the cursor. This satisfies the UIA "element at point" contract and exposes | ||
| // the control’s patterns/properties. If the hosted tree finds nothing, we fall back | ||
| // to the RNW container’s provider (uiaProvider) to keep the island accessible. | ||
| // (A Microsoft_UI_Xaml!CUIAWrapper object) | ||
| if (frag) { | ||
| return frag.as<winrt::IUnknown>(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return uiaProvider; | ||
| } | ||
|
|
||
| float RootComponentView::FontSizeMultiplier() const noexcept { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
|
|
||
| #include "DocString.h" | ||
|
|
||
| namespace Microsoft.ReactNative.Xaml { | ||
| [webhosthidden] | ||
| interface IXamlControl { | ||
| DOC_STRING( | ||
| "Native components that want to be Xaml-based can implement IXamlControl to allow their object to be parented to a XamlHost.") | ||
| Microsoft.UI.Xaml.UIElement GetXamlElement(); | ||
| }; | ||
| } // namespace Microsoft.ReactNative. Xaml |
Uh oh!
There was an error while loading. Please reload this page.