diff --git a/change/react-native-windows-86668219-62b8-4f50-98cd-ca89397c90ff.json b/change/react-native-windows-86668219-62b8-4f50-98cd-ca89397c90ff.json new file mode 100644 index 00000000000..31db8d104ea --- /dev/null +++ b/change/react-native-windows-86668219-62b8-4f50-98cd-ca89397c90ff.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Introduce xamlhost component", + "packageName": "react-native-windows", + "email": "10109130+sharath2727@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/sample-app-fabric/windows/SampleAppFabric.sln b/packages/sample-app-fabric/windows/SampleAppFabric.sln index 1eabd0cabef..66b1574ab2d 100644 --- a/packages/sample-app-fabric/windows/SampleAppFabric.sln +++ b/packages/sample-app-fabric/windows/SampleAppFabric.sln @@ -36,62 +36,50 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Include", "..\..\..\vnext\include\Include.vcxitems", "{EF074BA1-2D54-4D49-A28E-5E040B47CD2E}" EndProject Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ..\..\..\vnext\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9 - ..\..\..\vnext\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9 - ..\..\..\vnext\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9 - ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9 - ..\..\..\vnext\include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9 - ..\..\..\vnext\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 - ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 - ..\..\..\vnext\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 - ..\..\..\vnext\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - Debug|ARM64 = Debug|ARM64 + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 - Release|ARM64 = Release|ARM64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|ARM64.Build.0 = Debug|ARM64 + {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|ARM64.Deploy.0 = Debug|ARM64 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|x64.ActiveCfg = Debug|x64 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|x64.Build.0 = Debug|x64 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|x64.Deploy.0 = Debug|x64 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|x86.ActiveCfg = Debug|x86 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|x86.Build.0 = Debug|x86 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|x86.Deploy.0 = Debug|x86 - {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|ARM64.Build.0 = Debug|ARM64 - {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|ARM64.ActiveCfg = Release|ARM64 + {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|ARM64.Build.0 = Release|ARM64 + {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|ARM64.Deploy.0 = Release|ARM64 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|x64.ActiveCfg = Release|x64 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|x64.Build.0 = Release|x64 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|x64.Deploy.0 = Release|x64 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|x86.ActiveCfg = Release|x86 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|x86.Build.0 = Release|x86 {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|x86.Deploy.0 = Release|x86 - {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|ARM64.ActiveCfg = Release|ARM64 - {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|ARM64.Build.0 = Release|ARM64 - {7E2B86E3-E6B0-409D-812E-0D81DFFACA9D}.Release|ARM64.Deploy.0 = Release|ARM64 + {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|ARM64.Build.0 = Debug|ARM64 + {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|ARM64.Deploy.0 = Debug|ARM64 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|x64.ActiveCfg = Debug|x64 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|x64.Build.0 = Debug|x64 - {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|x64.Deploy.0 = Debug|x64 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|x86.ActiveCfg = Debug|Win32 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|x86.Build.0 = Debug|Win32 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|x86.Deploy.0 = Debug|Win32 - {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|ARM64.Build.0 = Debug|ARM64 - {E278ABE5-5A88-48C5-A949-CA00B489643F}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|ARM64.ActiveCfg = Release|ARM64 + {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|ARM64.Build.0 = Release|ARM64 + {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|ARM64.Deploy.0 = Release|ARM64 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|x64.ActiveCfg = Release|x64 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|x64.Build.0 = Release|x64 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|x64.Deploy.0 = Release|x64 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|x86.ActiveCfg = Release|Win32 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|x86.Build.0 = Release|Win32 {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|x86.Deploy.0 = Release|Win32 - {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|ARM64.ActiveCfg = Release|ARM64 - {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|ARM64.Build.0 = Release|ARM64 - {E278ABE5-5A88-48C5-A949-CA00B489643F}.Release|ARM64.Deploy.0 = Release|ARM64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.ActiveCfg = Debug|ARM64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|ARM64.Build.0 = Debug|ARM64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Debug|x64.ActiveCfg = Debug|x64 @@ -104,6 +92,20 @@ Global {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x64.Build.0 = Release|x64 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.ActiveCfg = Release|Win32 {A990658C-CE31-4BCC-976F-0FC6B1AF693D}.Release|x86.Build.0 = Release|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|ARM64.Build.0 = Debug|ARM64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x64.ActiveCfg = Debug|x64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x64.Build.0 = Debug|x64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.ActiveCfg = Debug|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.Build.0 = Debug|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.Deploy.0 = Debug|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|ARM64.ActiveCfg = Release|ARM64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|ARM64.Build.0 = Release|ARM64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x64.ActiveCfg = Release|x64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x64.Build.0 = Release|x64 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.ActiveCfg = Release|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.Build.0 = Release|Win32 + {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.Deploy.0 = Release|Win32 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.ActiveCfg = Debug|ARM64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|ARM64.Build.0 = Debug|ARM64 {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD}.Debug|x64.ActiveCfg = Debug|x64 @@ -140,26 +142,13 @@ Global {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x64.Build.0 = Release|x64 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.ActiveCfg = Release|Win32 {FCA38F3C-7C73-4C47-BE4E-32F77FA8538D}.Release|x86.Build.0 = Release|Win32 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|ARM64.Build.0 = Debug|ARM64 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x64.ActiveCfg = Debug|x64 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x64.Build.0 = Debug|x64 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.ActiveCfg = Debug|Win32 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.Build.0 = Debug|Win32 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Debug|x86.Deploy.0 = Debug|Win32 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|ARM64.ActiveCfg = Release|ARM64 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|ARM64.Build.0 = Release|ARM64 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x64.ActiveCfg = Release|x64 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x64.Build.0 = Release|x64 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.ActiveCfg = Release|Win32 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.Build.0 = Release|Win32 - {14B93DC8-FD93-4A6D-81CB-8BC96644501C}.Release|x86.Deploy.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {A990658C-CE31-4BCC-976F-0FC6B1AF693D} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} + {14B93DC8-FD93-4A6D-81CB-8BC96644501C} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {A9D95A91-4DB7-4F72-BEB6-FE8A5C89BFBD} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {C38970C0-5FBF-4D69-90D8-CBAC225AE895} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {F7D32BD0-2749-483E-9A0D-1635EF7E3136} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} @@ -168,9 +157,19 @@ Global {2049DBE9-8D13-42C9-AE4B-413AE38FFFD0} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} {EF074BA1-2D54-4D49-A28E-5E040B47CD2E} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} - {14B93DC8-FD93-4A6D-81CB-8BC96644501C} = {5EA20F54-880A-49F3-99FA-4B3FE54E8AB1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D43FAD39-F619-437D-BB40-04A3982ACB6A} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\..\vnext\Shared\Shared.vcxitems*{2049dbe9-8d13-42c9-ae4b-413ae38fffd0}*SharedItemsImports = 9 + ..\..\..\vnext\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9 + ..\..\..\vnext\Chakra\Chakra.vcxitems*{c38970c0-5fbf-4d69-90d8-cbac225ae895}*SharedItemsImports = 9 + ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9 + ..\..\..\vnext\include\Include.vcxitems*{ef074ba1-2d54-4d49-a28e-5e040b47cd2e}*SharedItemsImports = 9 + ..\..\..\vnext\Chakra\Chakra.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + ..\..\..\vnext\Mso\Mso.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + ..\..\..\vnext\Shared\Shared.vcxitems*{f7d32bd0-2749-483e-9a0d-1635ef7e3136}*SharedItemsImports = 4 + EndGlobalSection EndGlobal diff --git a/vnext/Microsoft.ReactNative/App.xaml b/vnext/Microsoft.ReactNative/App.xaml new file mode 100644 index 00000000000..038e5a57397 --- /dev/null +++ b/vnext/Microsoft.ReactNative/App.xaml @@ -0,0 +1,5 @@ + + diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp index 6ce1074a63c..a54aae50a14 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp @@ -161,6 +161,13 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::SetFocus(void) { return UiaSetFocusHelper(m_view); } +winrt::IUnknown CompositionDynamicAutomationProvider::TryGetChildSiteLinkAutomationProvider() { + if (m_childSiteLink) { + return m_childSiteLink.AutomationProvider().as(); + } + return nullptr; +} + HRESULT __stdcall CompositionDynamicAutomationProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal) { if (pRetVal == nullptr) return E_POINTER; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h index 7009a6e21b8..5049a52c4b3 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h @@ -98,6 +98,14 @@ class CompositionDynamicAutomationProvider : public winrt::implements< void AddToSelectionItems(winrt::com_ptr &item); void RemoveFromSelectionItems(winrt::com_ptr &item); + void SetChildSiteLink(winrt::Microsoft::UI::Content::ChildSiteLink childSiteLink) { + m_childSiteLink = childSiteLink; + } + + // If this object is for a ChildSiteLink, returns the ChildSiteLink's automation provider. + // This will be a provider object from the hosted framework (for example, WinUI). + winrt::IUnknown TryGetChildSiteLinkAutomationProvider(); + private: ::Microsoft::ReactNative::ReactTaggedView m_view; winrt::com_ptr m_textProvider; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp index 6ead642c857..e254eb4c367 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp @@ -219,7 +219,8 @@ HRESULT __stdcall CompositionRootAutomationProvider::ElementProviderFromPoint( auto local = rootView->ConvertScreenToLocal({static_cast(x), static_cast(y)}); auto provider = rootView->UiaProviderFromPoint( {static_cast(local.X * rootView->LayoutMetrics().PointScaleFactor), - static_cast(local.Y * rootView->LayoutMetrics().PointScaleFactor)}); + static_cast(local.Y * rootView->LayoutMetrics().PointScaleFactor)}, + {static_cast(x), static_cast(y)}); auto spFragment = provider.try_as(); if (spFragment) { *pRetVal = spFragment.detach(); diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp index 57aa7d14656..1310d8e7b4c 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp @@ -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); 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? + 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(); + providerImpl->SetChildSiteLink(m_childSiteLink); + } } } // namespace winrt::Microsoft::ReactNative::Composition::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h index f530baa3400..d9b374ffbfb 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h @@ -51,6 +51,9 @@ struct ContentIslandComponentView : ContentIslandComponentViewT #include +#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(ptPixels.x) / m_layoutMetrics.pointScaleFactor, static_cast(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(view)->EnsureUiaProvider(); + auto uiaProvider = + winrt::get_self(view)->EnsureUiaProvider(); + + // TODO: Avoid exposing CompositionDynamicAutomationProvider in RootComponentView + auto dynamicProvider = + uiaProvider.try_as(); + 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()) { + com_ptr 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(); + } + } + } + } + + return uiaProvider; } float RootComponentView::FontSizeMultiplier() const noexcept { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h index 3a037fc6d61..83f633d212e 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h @@ -64,7 +64,7 @@ struct RootComponentView : RootComponentViewT true false + true diff --git a/vnext/Microsoft.ReactNative/XamlApplication.cpp b/vnext/Microsoft.ReactNative/XamlApplication.cpp new file mode 100644 index 00000000000..74b0d0b3992 --- /dev/null +++ b/vnext/Microsoft.ReactNative/XamlApplication.cpp @@ -0,0 +1,73 @@ +#include "pch.h" +#include "XamlApplication.h" +#include "Xaml.XamlApplication.g.cpp" + +#include "winrt/Microsoft.UI.Xaml.XamlTypeInfo.h" + +namespace winrt::Microsoft::ReactNative::Xaml::implementation { +using namespace ::winrt::Microsoft::UI::Xaml; +using namespace ::winrt::Microsoft::UI::Xaml::Markup; +using namespace ::winrt::Windows::UI::Xaml::Interop; +XamlApplication::XamlApplication() { + s_current = *this; + + // TODO: It's probably not a good idea to only load the controls pri file, there are other ones too. + auto resourceManager = + winrt::Microsoft::Windows::ApplicationModel::Resources::ResourceManager(L"Microsoft.UI.Xaml.Controls.pri"); + + this->ResourceManagerRequested([resourceManager](auto &&, ResourceManagerRequestedEventArgs args) { + args.CustomResourceManager(resourceManager); + }); + winrt::Microsoft::UI::Xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread(); + + m_providers.push_back( + winrt::make_self() + .as()); // Default generated provider + m_providers.push_back(winrt::Microsoft::UI::Xaml::XamlTypeInfo::XamlControlsXamlMetaDataProvider()); + + auto winUIResources = winrt::Microsoft::UI::Xaml::Controls::XamlControlsResources(); + Resources().MergedDictionaries().Append(winUIResources); +} + +XamlApplication::~XamlApplication() { + s_current = nullptr; +} + +void XamlApplication::AddMetadataProvider( + winrt::Microsoft::UI::Xaml::Markup::IXamlMetadataProvider const &otherProvider) { + m_providers.push_back(otherProvider); +} + +winrt::Microsoft::UI::Xaml::Markup::IXamlType XamlApplication::GetXamlType( + winrt::Windows::UI::Xaml::Interop::TypeName const &type) { + for (const auto &provider : m_providers) { + if (auto result = provider.GetXamlType(type)) { + return result; + } + } + return nullptr; +} + +winrt::Microsoft::UI::Xaml::Markup::IXamlType XamlApplication::GetXamlType(hstring const &fullName) { + for (const auto &provider : m_providers) { + if (auto result = provider.GetXamlType(fullName)) { + return result; + } + } + + return nullptr; +} +com_array XamlApplication::GetXmlnsDefinitions() { + std::vector<::winrt::Microsoft::UI::Xaml::Markup::XmlnsDefinition> allDefinitions; + for (const auto &provider : m_providers) { + auto definitionsCurrentProvider = provider.GetXmlnsDefinitions(); + for (const auto &definition : definitionsCurrentProvider) { + allDefinitions.insert(allDefinitions.begin(), definition); + } + } + return winrt::com_array<::winrt::Microsoft::UI::Xaml::Markup::XmlnsDefinition>( + allDefinitions.begin(), allDefinitions.end()); +} + +winrt::Microsoft::ReactNative::Xaml::XamlApplication XamlApplication::s_current{nullptr}; +} // namespace winrt::Microsoft::ReactNative::Xaml::implementation diff --git a/vnext/Microsoft.ReactNative/XamlApplication.h b/vnext/Microsoft.ReactNative/XamlApplication.h new file mode 100644 index 00000000000..cb3211ee0e0 --- /dev/null +++ b/vnext/Microsoft.ReactNative/XamlApplication.h @@ -0,0 +1,47 @@ +#pragma once +#include "Xaml.XamlApplication.g.h" + +#include +#include +#include +#include +#include +#include + +#include "winrt/Microsoft.UI.Xaml.Hosting.h" +#include "winrt/Microsoft.UI.Xaml.Interop.h" +#include "winrt/Microsoft.UI.Xaml.Markup.h" +#include "winrt/Microsoft.UI.Xaml.h" + +#include "winrt/Windows.UI.Xaml.Interop.h" + +#include "XamlMetaDataProvider.h" + +namespace winrt::Microsoft::ReactNative::Xaml::implementation { +struct XamlApplication : XamlApplicationT { + XamlApplication(); + ~XamlApplication(); + + static void EnsureCreated() { + if (Current() == nullptr) { + s_current = winrt::make(); + } + } + + static winrt::Microsoft::ReactNative::Xaml::XamlApplication Current() { + return s_current; + } + + void AddMetadataProvider(winrt::Microsoft::UI::Xaml::Markup::IXamlMetadataProvider const &otherProvider); + winrt::Microsoft::UI::Xaml::Markup::IXamlType GetXamlType(winrt::Windows::UI::Xaml::Interop::TypeName const &type); + winrt::Microsoft::UI::Xaml::Markup::IXamlType GetXamlType(hstring const &fullName); + com_array GetXmlnsDefinitions(); + + private: + static winrt::Microsoft::ReactNative::Xaml::XamlApplication s_current; + std::vector m_providers; +}; +} // namespace winrt::Microsoft::ReactNative::Xaml::implementation +namespace winrt::Microsoft::ReactNative::Xaml::factory_implementation { +struct XamlApplication : XamlApplicationT {}; +} // namespace winrt::Microsoft::ReactNative::Xaml::factory_implementation diff --git a/vnext/Microsoft.ReactNative/XamlApplication.idl b/vnext/Microsoft.ReactNative/XamlApplication.idl new file mode 100644 index 00000000000..5a23009d917 --- /dev/null +++ b/vnext/Microsoft.ReactNative/XamlApplication.idl @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.ReactNative.Xaml { +[webhosthidden][default_interface] runtimeclass XamlApplication : Microsoft.UI.Xaml.Application, + Microsoft.UI.Xaml.Markup.IXamlMetadataProvider { + XamlApplication(); + + static void EnsureCreated(); + + static XamlApplication Current { + get; + }; + + void AddMetadataProvider(Microsoft.UI.Xaml.Markup.IXamlMetadataProvider otherProvider); +} +} // namespace Microsoft.ReactNative.Xaml diff --git a/vnext/Microsoft.ReactNative/XamlHost.cpp b/vnext/Microsoft.ReactNative/XamlHost.cpp new file mode 100644 index 00000000000..5b6873b9bbf --- /dev/null +++ b/vnext/Microsoft.ReactNative/XamlHost.cpp @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" + +#include "XamlApplication.h" +#include "XamlHost.h" + +#if defined(RNW_NEW_ARCH) + +#include "..\codegen\react\components\rnwcore\XamlHost.g.h" + +namespace winrt::Microsoft::ReactNative { + +struct XamlHostComponentView : public winrt::implements, + ::Microsoft::ReactNativeSpecs::BaseXamlHost { + void InitializeContentIsland( + const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept { + winrt::Microsoft::ReactNative::Xaml::implementation::XamlApplication::EnsureCreated(); + + m_xamlIsland = winrt::Microsoft::UI::Xaml::XamlIsland{}; + + islandView.Connect(m_xamlIsland.ContentIsland()); + } + + void MountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView & /*view*/, + const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &args) noexcept override { + // Add the xaml child to the m_xamlIsland here. + auto childXamlControl = args.Child().UserData().as(); + if (childXamlControl) { + auto xamlElement = childXamlControl.GetXamlElement(); + m_xamlIsland.Content(xamlElement); + } + } + + void UnmountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView & /*view*/, + const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &) noexcept override { + m_xamlIsland.Content(nullptr); + } + + private: + winrt::Microsoft::UI::Xaml::XamlIsland m_xamlIsland{nullptr}; +}; + +} // namespace winrt::Microsoft::ReactNative + +void RegisterXamlHostComponentView(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) { + ::Microsoft::ReactNativeSpecs::RegisterXamlHostNativeComponent( + packageBuilder, + [](const winrt::Microsoft::ReactNative::Composition::IReactCompositionViewComponentBuilder &builder) { + builder.SetContentIslandComponentViewInitializer( + [](const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept { + auto userData = winrt::make_self(); + userData->InitializeContentIsland(islandView); + islandView.UserData(*userData); + }); + }); +} + +#endif // defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3) diff --git a/vnext/Microsoft.ReactNative/XamlHost.h b/vnext/Microsoft.ReactNative/XamlHost.h new file mode 100644 index 00000000000..7abc42b8241 --- /dev/null +++ b/vnext/Microsoft.ReactNative/XamlHost.h @@ -0,0 +1,7 @@ +#pragma once + +#if defined(RNW_NEW_ARCH) + +void RegisterXamlHostComponentView(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder); + +#endif // defined(RNW_NEW_ARCH) && defined(USE_EXPERIMENTAL_WINUI3) diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index 8fda1683eec..339abc0bd8a 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -245,6 +245,16 @@ true $(ReactNativeWindowsDir)Microsoft.ReactNative\ReactNativeAppBuilder.idl Code + + + true + $(ReactNativeWindowsDir)Microsoft.ReactNative\XamlApplication.idl + Code + + + true + $(ReactNativeWindowsDir)Microsoft.ReactNative\XamlApplication.idl + Code @@ -350,6 +360,16 @@ true $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\ReactNativeAppBuilder.idl Code + + + true + $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\XamlApplication.idl + Code + + + true + $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\XamlApplication.idl + Code $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\IJSValueReader.idl @@ -711,6 +731,8 @@ + + @@ -726,4 +748,7 @@ NotUsing + + + diff --git a/vnext/fmt/packages.lock.json b/vnext/fmt/packages.lock.json new file mode 100644 index 00000000000..a31237b580e --- /dev/null +++ b/vnext/fmt/packages.lock.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "dependencies": { + "native,Version=v0.0": {}, + "native,Version=v0.0/win10-arm": {}, + "native,Version=v0.0/win10-arm-aot": {}, + "native,Version=v0.0/win10-arm64-aot": {}, + "native,Version=v0.0/win10-x64": {}, + "native,Version=v0.0/win10-x64-aot": {}, + "native,Version=v0.0/win10-x86": {}, + "native,Version=v0.0/win10-x86-aot": {} + } +} \ No newline at end of file diff --git a/vnext/src-win/Libraries/Components/Xaml/XamlHost.d.ts b/vnext/src-win/Libraries/Components/Xaml/XamlHost.d.ts new file mode 100644 index 00000000000..a69cfafe0be --- /dev/null +++ b/vnext/src-win/Libraries/Components/Xaml/XamlHost.d.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + * @flow + */ +/// +import type {ViewProps} from 'react-native'; +export interface XamlHostProps extends ViewProps { + label: string; +} +declare const _default: import('react-native/Libraries/Utilities/codegenNativeComponent').NativeComponentType; +export default _default; diff --git a/vnext/src-win/Libraries/Components/Xaml/XamlHost.windows.js b/vnext/src-win/Libraries/Components/Xaml/XamlHost.windows.js new file mode 100644 index 00000000000..723810b17a4 --- /dev/null +++ b/vnext/src-win/Libraries/Components/Xaml/XamlHost.windows.js @@ -0,0 +1,7 @@ +/** + * @format + * @flow + */ + +import XamlHost from '../../../src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent'; +export default XamlHost; diff --git a/vnext/src-win/index.windows.js b/vnext/src-win/index.windows.js index 14ac2344540..8f368fe05fe 100644 --- a/vnext/src-win/index.windows.js +++ b/vnext/src-win/index.windows.js @@ -372,6 +372,9 @@ module.exports = { get AppTheme() { return require('./Libraries/AppTheme/AppTheme').AppTheme; }, + get XamlHost() { + return require('./Libraries/Components/Xaml/XamlHost').default; + }, } as ReactNativePublicAPI; if (__DEV__) { diff --git a/vnext/src-win/index.windows.js.flow b/vnext/src-win/index.windows.js.flow index 6942181c0c3..d601455ba73 100644 --- a/vnext/src-win/index.windows.js.flow +++ b/vnext/src-win/index.windows.js.flow @@ -469,6 +469,7 @@ export {HandledEventPhase } from './Libraries/Components/View/ViewPropTypes' export {default as ViewWindows} from './Libraries/Components/View/View'; export {AppTheme} from './Libraries/AppTheme/AppTheme'; +export {default as XamlHost} from './Libraries/Components/Xaml/XamlHost'; // End Windows Specific exports // #endregion diff --git a/vnext/src-win/src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent.js b/vnext/src-win/src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent.js new file mode 100644 index 00000000000..4b0fc158fd8 --- /dev/null +++ b/vnext/src-win/src/private/specs_DEPRECATED/components/Xaml/XamlHostNativeComponent.js @@ -0,0 +1,20 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + * @flow + */ + +'use strict'; + +import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes'; +import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent'; +import type {HostComponent} from '../../../../../src/private/types/HostComponent'; + +type XamlHostProps = $ReadOnly<{| + ...ViewProps, +|}>; + +type NativeType = HostComponent; + +export default (codegenNativeComponent('XamlHost'): NativeType);