-
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 12 commits
28b1471
7cb5b7e
ffa1c03
b7cbe82
ed1e67c
0a95df7
6861056
b3d5b4f
c90b1da
9357561
805ba4d
46dc340
9889e8c
284c423
522cf31
529423b
9b6aa17
b9036c3
11c54ea
6b92ff5
b179f47
713b950
c2aa6d4
572a024
8392fc5
9aa90f6
b477234
c298c47
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" | ||
| } | ||
|
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. What are these changes for? Please drop them if unnecessary.
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. this got auto-generated.
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. Yeah, but it's unneeded i.e. your changes will build with no changes to the solution file too, then by include it? Please unstage and drop this file from the PR. It'd be noise otherwise. If these changes are needed for your PR to work, then we need to understand what changed in this file. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| <Application | ||
| x:Class="Microsoft.ReactNative.XamlApplication" | ||
| xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" | ||
| xmlns:local="using:Microsoft.ReactNative"> | ||
| </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); | ||
sundaramramaswamy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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?
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. We added this to make the UIA "ElementProviderFromPoint" hit test work correctly. When the RootComponentView gets a ElementProviderFromPoint call ( My concern here was that I don't know if hittest is used for other things, and if that could cause trouble.
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 see that, but this function should be doing the same thing as the base does? With the exception that this implementation is not correctly handling the pointerEvents property. The base implementation of this function in ViewComponentView::hitTest should already do the same as this function, no? And then I'd expect the Xaml elements that are children of the ContentIslandComponentView to not be ViewComponentView's, and so they'd use the base of ComponentView::hitTest, which would ensure that they do not hitTest by default.
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. Ah, so maybe the problem is that the Xaml element children are deriving from ViewComponentViews (or some other type) and they shouldn't be. I haven't worked with this code for a while, so I'm just going off what I see in the comments.
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. Yes, if the Xaml element was deriving from ViewComponentViews that would be wrong, and would require this change.
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. Let remove this change, and ensure that the Xaml elements are not deriving from ViewComponentViews. |
||
| 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.
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. Sure, please add the task to the comment too.
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. |
||
| 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 |
|---|---|---|
|
|
@@ -17,4 +17,12 @@ namespace Microsoft.ReactNative | |
| void CreatePackage(IReactPackageBuilder packageBuilder); | ||
| }; | ||
|
|
||
| [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 | ||
Uh oh!
There was an error while loading. Please reload this page.