diff --git a/change/react-native-windows-f295c471-5165-440a-a5d5-4d9b2d9c84d9.json b/change/react-native-windows-f295c471-5165-440a-a5d5-4d9b2d9c84d9.json
new file mode 100644
index 00000000000..724dde632cb
--- /dev/null
+++ b/change/react-native-windows-f295c471-5165-440a-a5d5-4d9b2d9c84d9.json
@@ -0,0 +1,7 @@
+{
+ "type": "prerelease",
+ "comment": "Rework modal implementation to use public APIs",
+ "packageName": "react-native-windows",
+ "email": "30809111+acoates-ms@users.noreply.github.com",
+ "dependentChangeType": "patch"
+}
diff --git a/packages/@react-native-windows/tester/overrides.json b/packages/@react-native-windows/tester/overrides.json
index a17f62b8ec8..2ea7f8fd427 100644
--- a/packages/@react-native-windows/tester/overrides.json
+++ b/packages/@react-native-windows/tester/overrides.json
@@ -41,12 +41,6 @@
"baseHash": "8583dee020400ce4d2754c3e04fd08dfde341c49",
"issue": 12869
},
- {
- "type": "patch",
- "file": "src/js/examples/Modal/ModalOnShow.windows.js",
- "baseFile": "packages/rn-tester/js/examples/Modal/ModalOnShow.js",
- "baseHash": "911507abcf9172b5fdd1bb68faaf02562df704d4"
- },
{
"type": "patch",
"file": "src/js/examples/Modal/ModalPresentation.windows.js",
diff --git a/packages/@react-native-windows/tester/src/js/examples/Modal/ModalOnShow.windows.js b/packages/@react-native-windows/tester/src/js/examples/Modal/ModalOnShow.windows.js
deleted file mode 100644
index 97afc692b3a..00000000000
--- a/packages/@react-native-windows/tester/src/js/examples/Modal/ModalOnShow.windows.js
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @flow strict-local
- * @format
- */
-
-import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
-
-import RNTesterText from '../../components/RNTesterText';
-import * as React from 'react';
-import {Modal, Pressable, StyleSheet, Text, View} from 'react-native';
-
-function ModalOnShowOnDismiss(): React.Node {
- const [modalShowComponent, setModalShowComponent] = React.useState(true);
- const [modalVisible, setModalVisible] = React.useState(false);
- const [onShowCount, setOnShowCount] = React.useState(0);
- const [onDismissCount, setOnDismissCount] = React.useState(0);
-
- return (
-
- {modalShowComponent && (
- {
- setOnShowCount(onShowCount + 1);
- }}
- onDismiss={() => {
- setOnDismissCount(onDismissCount + 1);
- }}
- onRequestClose={() => {
- setModalVisible(false);
- }}>
-
-
-
- onShow is called {onShowCount} times
-
-
- onDismiss is called {onDismissCount} times
-
- setModalVisible(false)}>
-
- Hide modal by setting visible to false
-
-
- setModalShowComponent(false)}>
-
- Hide modal by removing component
-
-
-
-
-
- )}
-
- onShow is called {onShowCount} times
-
-
- onDismiss is called {onDismissCount} times
-
- {
- setModalShowComponent(true);
- setModalVisible(true);
- }}>
-
- Show Modal
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- display: 'flex',
- alignItems: 'center',
- paddingVertical: 30,
- },
- centeredView: {
- // flex: 1, [Windows]
- justifyContent: 'center',
- alignItems: 'center',
- },
- modalBackdrop: {
- backgroundColor: 'rgba(0, 0, 0, 0.5)',
- },
- modalView: {
- margin: 20,
- borderRadius: 20,
- padding: 35,
- alignItems: 'center',
- shadowColor: '#000',
- shadowOffset: {
- width: 0,
- height: 2,
- },
- shadowOpacity: 0.25,
- shadowRadius: 4,
- elevation: 5,
- },
- button: {
- borderRadius: 20,
- padding: 10,
- marginVertical: 20,
- elevation: 2,
- },
- buttonOpen: {
- backgroundColor: '#F194FF',
- },
- buttonClose: {
- backgroundColor: '#2196F3',
- },
- textStyle: {
- color: 'white',
- fontWeight: 'bold',
- textAlign: 'center',
- },
- // [Windows
- widthHeight: {
- width: 300,
- height: 400,
- },
- // Windows]
-});
-
-export default ({
- title: "Modal's onShow/onDismiss",
- name: 'onShow',
- description:
- 'onShow and onDismiss (iOS only) callbacks are called when a modal is shown/dismissed',
- render: (): React.Node => ,
-}: RNTesterModuleExample);
diff --git a/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js b/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js
index f766fa58bc0..9a5f35172a5 100644
--- a/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js
+++ b/packages/@react-native-windows/tester/src/js/examples/Modal/ModalPresentation.windows.js
@@ -348,13 +348,9 @@ const styles = StyleSheet.create({
marginTop: 6,
},
modalContainer: {
- // [Windows
- width: 500,
- height: 500,
- // flex: 1,
- // justifyContent: 'center',
- // padding: 20,
- // Windows ]
+ flex: 1,
+ justifyContent: 'center',
+ padding: 20,
},
modalInnerContainer: {
borderRadius: 10,
diff --git a/vnext/Microsoft.ReactNative/CompositionComponentView.idl b/vnext/Microsoft.ReactNative/CompositionComponentView.idl
index 7cfa2740d2a..5c72cbd5067 100644
--- a/vnext/Microsoft.ReactNative/CompositionComponentView.idl
+++ b/vnext/Microsoft.ReactNative/CompositionComponentView.idl
@@ -7,6 +7,7 @@ import "ViewProps.idl";
import "Composition.Input.idl";
import "CompositionSwitcher.idl";
import "IReactContext.idl";
+import "ReactNativeIsland.idl";
#include "DocString.h"
@@ -110,8 +111,19 @@ namespace Microsoft.ReactNative.Composition
[default_interface]
runtimeclass RootComponentView : ViewComponentView {
Microsoft.ReactNative.ComponentView GetFocusedComponent();
+ Microsoft.ReactNative.ReactNativeIsland ReactNativeIsland { get; };
+ DOC_STRING("Is non-null if this RootComponentView is the content of a portal")
+ PortalComponentView Portal { get; };
};
-
+
+ [experimental]
+ [webhosthidden]
+ [default_interface]
+ DOC_STRING("Used to implement UI that is hosted outside the main UI tree, such as modal.")
+ runtimeclass PortalComponentView : Microsoft.ReactNative.ComponentView {
+ RootComponentView ContentRoot { get; };
+ };
+
[experimental]
[webhosthidden]
[default_interface]
diff --git a/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp
index c7cad679f72..3161fa2cb7a 100644
--- a/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp
@@ -639,7 +639,6 @@ facebook::react::Tag ComponentView::hitTest(
}
winrt::IInspectable ComponentView::EnsureUiaProvider() noexcept {
- assert(false);
return nullptr;
}
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp
index 360a3161c14..4989b3fc0e9 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp
@@ -15,8 +15,6 @@
#include
#include
#include
-#include
-#include
#include
#include
#include
@@ -59,9 +57,6 @@ ComponentViewDescriptor const &ComponentViewRegistry::dequeueComponentViewWithCo
} else if (componentHandle == facebook::react::ImageShadowNode::Handle()) {
view = winrt::Microsoft::ReactNative::Composition::implementation::ImageComponentView::Create(
compContext, tag, m_context);
- } else if (componentHandle == facebook::react::WindowsModalHostViewShadowNode::Handle()) {
- view = winrt::Microsoft::ReactNative::Composition::implementation::WindowsModalHostComponentView::Create(
- compContext, tag, m_context);
} else if (componentHandle == facebook::react::WindowsTextInputShadowNode::Handle()) {
view = winrt::Microsoft::ReactNative::Composition::implementation::WindowsTextInputComponentView::Create(
compContext, tag, m_context);
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
index dcd29eaddf5..ca254789899 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp
@@ -192,8 +192,6 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTE
winrt::get_self(strongView)->props());
if (props == nullptr)
return UIA_E_ELEMENTNOTAVAILABLE;
- auto accessibilityRole =
- props->accessibilityRole.empty() ? compositionView->DefaultControlType() : props->accessibilityRole;
// 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 && (props->onAccessibilityTap)) {
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp
index 004c260b203..b254d602ef3 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp
@@ -142,9 +142,8 @@ struct CompositionInputKeyboardSource : winrt::implements<
CompositionEventHandler::CompositionEventHandler(
const winrt::Microsoft::ReactNative::ReactContext &context,
- const winrt::Microsoft::ReactNative::ReactNativeIsland &reactNativeIsland,
- const int fragmentTag)
- : m_fragmentTag(fragmentTag), m_context(context), m_wkRootView(reactNativeIsland) {}
+ const winrt::Microsoft::ReactNative::ReactNativeIsland &reactNativeIsland)
+ : m_context(context), m_wkRootView(reactNativeIsland) {}
void CompositionEventHandler::Initialize() noexcept {
#ifdef USE_WINUI3
@@ -348,11 +347,8 @@ facebook::react::SurfaceId CompositionEventHandler::SurfaceId() const noexcept {
winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView &
CompositionEventHandler::RootComponentView() const noexcept {
- auto rootComponentViewDescriptor = (::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties()))
- ->GetViewRegistry()
- .componentViewDescriptorWithTag(SurfaceId());
- return *rootComponentViewDescriptor.view
- .as();
+ auto island = m_wkRootView.get();
+ return *winrt::get_self(island)->GetComponentView();
}
void CompositionEventHandler::onPointerWheelChanged(
@@ -365,6 +361,9 @@ void CompositionEventHandler::onPointerWheelChanged(
facebook::react::Point ptLocal;
facebook::react::Point ptScaled = {static_cast(position.X), static_cast(position.Y)};
+ // In the case of a sub rootview, we may have a non-zero origin. hitTest takes a pt in the parent coords, so we
+ // need to apply the current origin
+ ptScaled += RootComponentView().layoutMetrics().frame.origin;
auto tag = RootComponentView().hitTest(ptScaled, ptLocal);
if (tag == -1)
@@ -977,6 +976,11 @@ void CompositionEventHandler::getTargetPointerArgs(
tag = -1;
ptScaled = {position.X, position.Y};
+
+ // In the case of a sub rootview, we may have a non-zero origin. hitTest takes a pt in the parent coords, so we need
+ // to apply the current origin
+ ptScaled += RootComponentView().layoutMetrics().frame.origin;
+
if (std::find(m_capturedPointers.begin(), m_capturedPointers.end(), pointerId) != m_capturedPointers.end()) {
assert(m_pointerCapturingComponentTag != -1);
tag = m_pointerCapturingComponentTag;
@@ -989,30 +993,7 @@ void CompositionEventHandler::getTargetPointerArgs(
ptLocal.y = ptScaled.y - (clientRect.top / strongRootView.ScaleFactor());
}
} else {
- if (m_fragmentTag == -1) {
- tag = RootComponentView().hitTest(ptScaled, ptLocal);
- return;
- }
-
- // check if the fragment tag exists
- if (!fabricuiManager->GetViewRegistry().findComponentViewWithTag(m_fragmentTag)) {
- return;
- }
-
- auto fagmentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_fragmentTag).view;
- auto fagmentchildren = fagmentView.Children();
-
- // call the hitTest with the fargment as the RootComponent
- for (auto index = fagmentchildren.Size(); index > 0; index--) {
- auto childView = fagmentchildren.GetAt(index - 1);
- auto targetTag =
- winrt::get_self(childView)->hitTest(
- ptScaled, ptLocal);
- if (targetTag != -1) {
- tag = targetTag;
- break;
- }
- }
+ tag = RootComponentView().hitTest(ptScaled, ptLocal);
}
}
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h
index f35bd44115d..1826338fadb 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h
@@ -31,8 +31,7 @@ class CompositionEventHandler : public std::enable_shared_from_this m_activeTouches; // iOS is map of touch event args to ActiveTouch..?
PointerId m_touchId = 0;
- int m_fragmentTag = -1;
std::map> m_currentlyHoveredViewsPerPointer;
winrt::weak_ref m_wkRootView;
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
index 5bf4135f57c..a1ab4e62227 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp
@@ -167,7 +167,7 @@ HRESULT __stdcall CompositionRootAutomationProvider::get_ProviderOptions(Provide
return S_OK;
}
-winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *
+winrt::com_ptr
CompositionRootAutomationProvider::rootComponentView() noexcept {
if (auto rootView = m_wkRootView.get()) {
auto innerRootView = winrt::get_self(rootView);
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h
index cdaf5eabd0e..374b4c95f43 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h
@@ -63,7 +63,8 @@ class CompositionRootAutomationProvider : public winrt::implements<
};
private:
- winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *rootComponentView() noexcept;
+ winrt::com_ptr
+ rootComponentView() noexcept;
HRESULT AdvisePropertiesAdded(SAFEARRAY *psaProperties) noexcept;
HRESULT AdvisePropertiesRemoved(SAFEARRAY *psaProperties) noexcept;
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
index 8e4a7c2f5ef..7952c5e990c 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp
@@ -468,7 +468,7 @@ bool ComponentView::CapturePointer(const winrt::Microsoft::ReactNative::Composit
if (!root)
return false;
- auto rootView{uiManager->GetReactNativeIsland(root->Tag())};
+ auto rootView{root->ReactNativeIsland()};
if (!rootView) {
return false;
}
@@ -487,7 +487,7 @@ void ComponentView::ReleasePointerCapture(
if (!root)
return;
- auto rootView{uiManager->GetReactNativeIsland(root->Tag())};
+ auto rootView{root->ReactNativeIsland()};
if (!rootView) {
return;
}
@@ -1330,7 +1330,7 @@ winrt::Microsoft::ReactNative::ComponentView lastDeepChild(
}
// Walks the tree calling the function fn on each node.
-// If fn returns true, then walkTree stops itterating over the tree, and returns true.
+// If fn returns true, then walkTree stops iterating over the tree, and returns true.
// If the tree walk completes without fn returning true, then walkTree returns false.
bool walkTree(
const winrt::Microsoft::ReactNative::ComponentView &view,
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentDescriptor.h b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentDescriptor.h
deleted file mode 100644
index 041f578f767..00000000000
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentDescriptor.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-#include
-// [Windows
-#include "WindowsModalHostViewShadowNode.h"
-// ]
-
-namespace facebook::react {
-
-/*
- * Descriptor for component.
- */
-
-class WindowsModalHostViewComponentDescriptor final
- : public ConcreteComponentDescriptor {
- public:
- using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
-
- void adopt(ShadowNode &shadowNode) const override {
- auto &layoutableShadowNode = static_cast(shadowNode);
- auto &stateData =
- static_cast(*shadowNode.getState()).getData();
-
- layoutableShadowNode.setSize(Size{stateData.screenSize.width, stateData.screenSize.height});
- layoutableShadowNode.setPositionType(YGPositionTypeAbsolute);
-
- ConcreteComponentDescriptor::adopt(shadowNode);
- }
-};
-
-} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
index 08b584f2162..6bb1de556d9 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp
@@ -5,379 +5,241 @@
#include "WindowsModalHostViewComponentView.h"
-#include
-#include
-#include "../CompositionDynamicAutomationProvider.h"
-#include "Unicode.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include "../../../codegen/react/components/rnwcore/ModalHostView.g.h"
+#include
#include
+#include
+#include
#include
-#include
-#include
-#include "IReactContext.h"
-#include "ReactHost/ReactInstanceWin.h"
-#include "ReactNativeHost.h"
-#include "WindowsModalHostViewShadowNode.h"
namespace winrt::Microsoft::ReactNative::Composition::implementation {
-WindowsModalHostComponentView::WindowsModalHostComponentView(
- const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
- facebook::react::Tag tag,
- winrt::Microsoft::ReactNative::ReactContext const &reactContext)
- : Super(compContext, tag, reactContext) {}
-
-WindowsModalHostComponentView::~WindowsModalHostComponentView() {
- // dispatch onDismiss event
- auto emitter = std::static_pointer_cast(m_eventEmitter);
- facebook::react::ModalHostViewEventEmitter::OnDismiss onDismissArgs;
- emitter->onDismiss(onDismissArgs);
-
- // reset the topWindowID
- if (m_prevWindowID) {
- auto host =
- winrt::Microsoft::ReactNative::implementation::ReactNativeHost::GetReactNativeHost(m_reactContext.Properties());
- winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
- host.InstanceSettings().Properties(), m_prevWindowID);
- m_prevWindowID = 0;
- }
-
- // enable input to parent
- EnableWindow(m_parentHwnd, true);
-
- // Check if the window handle (m_hwnd) exists and destroy it if necessary
- if (m_hwnd) {
- // Close/Destroy the modal window
- SendMessage(m_hwnd, WM_DESTROY, 0, 0);
- m_hwnd = nullptr;
- }
-}
-
-winrt::Microsoft::ReactNative::ComponentView WindowsModalHostComponentView::Create(
- const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
- facebook::react::Tag tag,
- winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept {
- return winrt::make(compContext, tag, reactContext);
-}
-// constants for creating a new windows
-constexpr PCWSTR c_modalWindowClassName = L"MS_REACTNATIVE_MODAL";
-constexpr auto CompHostProperty = L"CompHost";
-const int MODAL_MIN_WIDTH = 50;
-const int MODAL_MIN_HEIGHT = 50;
+struct ModalHostView : public winrt::implements,
+ ::Microsoft::ReactNativeSpecs::BaseModalHostView {
+ ~ModalHostView() {
+ if (m_window && m_window.IsVisible()) {
+ CloseWindow();
+ }
-float ScaleFactor(HWND hwnd) noexcept {
- return GetDpiForWindow(hwnd) / static_cast(USER_DEFAULT_SCREEN_DPI);
-}
+ if (m_reactNativeIsland) {
+ m_reactNativeIsland.Island().Close();
+ }
-// creates a new modal window
-void WindowsModalHostComponentView::EnsureModalCreated() {
- auto host =
- winrt::Microsoft::ReactNative::implementation::ReactNativeHost::GetReactNativeHost(m_reactContext.Properties());
+ if (m_bridge) {
+ if (m_departFocusToken && !m_bridge.IsClosed()) {
+ auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(m_bridge);
+ navHost.DepartFocusRequested(m_departFocusToken);
+ }
+ m_bridge.Close();
+ }
- // return if hwnd already exists
- if (!host || m_hwnd) {
- return;
+ if (m_window) {
+ m_window.Destroy();
+ m_window = nullptr;
+ }
}
- RegisterWndClass();
-
- HINSTANCE hInstance = GetModuleHandle(NULL);
- winrt::com_ptr<::IUnknown> spunk;
-
- // get the root hwnd
- m_prevWindowID =
- winrt::Microsoft::ReactNative::ReactCoreInjection::GetTopLevelWindowId(m_reactContext.Properties().Handle());
+ void InitializePortalViewComponent(
+ const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portalComponentView) noexcept {
+ m_reactContext = portalComponentView.ReactContext();
- m_parentHwnd = GetHwndForParenting();
-
- auto windowsStyle = m_showTitleBar ? WS_OVERLAPPEDWINDOW : WS_POPUP;
-
- m_hwnd = CreateWindow(
- c_modalWindowClassName,
- L"React-Native Modal",
- windowsStyle,
- CW_USEDEFAULT,
- CW_USEDEFAULT,
- MODAL_MIN_WIDTH,
- MODAL_MIN_HEIGHT,
- m_parentHwnd, // parent
- nullptr,
- hInstance,
- spunk.get());
-
- // Check if window creation succeeded
- if (!m_hwnd) {
- throw std::exception("Failed to create new hwnd for Modal: " + GetLastError());
+ portalComponentView.Mounted(
+ [](const auto & /*sender*/, const auto &view) { view.UserData().as()->OnMounted(view); });
+ portalComponentView.Unmounted(
+ [](const auto & /*sender*/, const auto &view) { view.UserData().as()->OnUnmounted(view); });
}
- // Disable user sizing of the hwnd
- ::SetWindowLong(m_hwnd, GWL_STYLE, GetWindowLong(m_hwnd, GWL_STYLE) & ~WS_SIZEBOX);
-
- // set the top-level windows as the new hwnd
- winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
- host.InstanceSettings().Properties(), reinterpret_cast(m_hwnd));
-
- // get current compositor - handles the creation/manipulation of visual objects
- auto compositionContext = CompositionContext();
- auto compositor =
- winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerCompositor(
- compositionContext);
-
- // create a react native island - code taken from CompositionHwndHost
- auto bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create(
- compositor, winrt::Microsoft::UI::GetWindowIdFromWindow(m_hwnd));
- m_reactNativeIsland = winrt::Microsoft::ReactNative::ReactNativeIsland(compositor, m_reactContext.Handle(), *this);
- auto contentIsland = m_reactNativeIsland.Island();
- bridge.Connect(contentIsland);
- bridge.Show();
-
- // set ScaleFactor
- ScaleFactor(m_hwnd);
-
- // set layout contraints
- winrt::Microsoft::ReactNative::LayoutConstraints constraints;
- constraints.LayoutDirection = winrt::Microsoft::ReactNative::LayoutDirection::Undefined;
-
- RECT rc;
- GetClientRect(m_parentHwnd, &rc);
- // Maximum size is set to size of parent hwnd
- constraints.MaximumSize = {(rc.right - rc.left) * ScaleFactor(m_hwnd), (rc.bottom - rc.top) / ScaleFactor(m_hwnd)};
- constraints.MinimumSize = {MODAL_MIN_WIDTH * ScaleFactor(m_hwnd), MODAL_MIN_HEIGHT * ScaleFactor(m_hwnd)};
- m_reactNativeIsland.Arrange(constraints, {0, 0});
- bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow);
-
- spunk.detach();
-}
-
-void WindowsModalHostComponentView::ShowOnUIThread() {
- if (m_hwnd && !IsWindowVisible(m_hwnd)) {
- ShowWindow(m_hwnd, SW_NORMAL);
- BringWindowToTop(m_hwnd);
- SetFocus(m_hwnd);
-
- // disable input to parent
- EnableWindow(m_parentHwnd, false);
-
- // dispatch onShow event
- auto emitter = std::static_pointer_cast(m_eventEmitter);
- facebook::react::ModalHostViewEventEmitter::OnShow onShowArgs;
- emitter->onShow(onShowArgs);
+ void UpdateProps(
+ const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::com_ptr<::Microsoft::ReactNativeSpecs::ModalHostViewProps> &newProps,
+ const winrt::com_ptr<::Microsoft::ReactNativeSpecs::ModalHostViewProps> &oldProps) noexcept override {
+ if (!oldProps || newProps->visible != oldProps->visible) {
+ if (newProps->visible.value_or(true)) {
+ // We do not immediately show the window, since we want to resize/position
+ // the window based on the layout metrics before we show it
+ m_showQueued = true;
+ } else {
+ CloseWindow();
+ }
+ }
+ ::Microsoft::ReactNativeSpecs::BaseModalHostView::UpdateProps(view, newProps, oldProps);
}
-}
-void WindowsModalHostComponentView::HideOnUIThread() noexcept {
- if (m_hwnd) {
- SendMessage(m_hwnd, WM_CLOSE, 0, 0);
+ void UpdateLayoutMetrics(
+ const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &newLayoutMetrics,
+ const winrt::Microsoft::ReactNative::LayoutMetrics & /*oldLayoutMetrics*/) noexcept override {
+ if (m_window) {
+ AdjustWindowSize(newLayoutMetrics);
+ }
}
- // dispatch onDismiss event
- auto emitter = std::static_pointer_cast(m_eventEmitter);
- facebook::react::ModalHostViewEventEmitter::OnDismiss onDismissArgs;
- emitter->onDismiss(onDismissArgs);
+ void FinalizeUpdate(
+ const winrt::Microsoft::ReactNative::ComponentView &view,
+ winrt::Microsoft::ReactNative::ComponentViewUpdateMask /*mask*/) noexcept override {
+ if (m_showQueued) {
+ ShowOnUIThread(view);
+ }
+ }
- // enable input to parent
- EnableWindow(m_parentHwnd, true);
+ private:
+ void OnMounted(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ m_mounted = true;
- // reset the topWindowID
- if (m_prevWindowID) {
- auto host =
- winrt::Microsoft::ReactNative::implementation::ReactNativeHost::GetReactNativeHost(m_reactContext.Properties());
- winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
- host.InstanceSettings().Properties(), m_prevWindowID);
- m_prevWindowID = 0;
+ if (m_showQueued) {
+ ShowOnUIThread(view);
+ }
}
-}
-// Windows Procedure - callback function used for handling all messages (generated by NTUser or manual calls to
-// SendMessage)
-LRESULT CALLBACK ModalBoxWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) noexcept {
- auto data = reinterpret_cast<::IUnknown *>(GetProp(
- hwnd,
- CompHostProperty)); // gets data handle from the property list of specified window (ie the window we want to make)
- winrt::Microsoft::ReactNative::CompositionHwndHost host{nullptr};
-
- if (data) {
- winrt::check_hresult(data->QueryInterface(
- winrt::guid_of(),
- winrt::put_abi(host))); // look into the data for a CompositionHwndHost and store it in host
- auto result = static_cast(host.TranslateMessage(message, wparam, lparam));
- if (result) {
- return result;
- }
+ void OnUnmounted(const winrt::Microsoft::ReactNative::ComponentView & /*view*/) noexcept {
+ m_mounted = false;
}
- switch (message) {
- case WM_NCCREATE: { // called before WM_CREATE, lparam should be identical to members of CreateWindowEx
- auto createStruct = reinterpret_cast(lparam); // CreateStruct
- data = static_cast<::IUnknown *>(createStruct->lpCreateParams);
- SetProp(hwnd, CompHostProperty, data); // adds new properties to window
- break;
+ void AdjustWindowSize(const winrt::Microsoft::ReactNative::LayoutMetrics &layoutMetrics) noexcept {
+ if (layoutMetrics.Frame.Width == 0 && layoutMetrics.Frame.Height == 0) {
+ return;
}
- case WM_CLOSE: {
- // Just hide the window instead of destroying it
- ::ShowWindow(hwnd, SW_HIDE);
- return 0;
- }
- case WM_DESTROY: { // called when we want to destroy the window
- ::ShowWindow(hwnd, SW_HIDE);
- if (data) {
- data->Release();
+
+ // get Modal's position based on parent
+ RECT parentRC;
+ GetWindowRect(m_parentHwnd, &parentRC);
+ int32_t xCor = static_cast(
+ (parentRC.left + parentRC.right - layoutMetrics.Frame.Width * layoutMetrics.PointScaleFactor) / 2);
+ int32_t yCor = static_cast(
+ (parentRC.top + parentRC.bottom - layoutMetrics.Frame.Height * layoutMetrics.PointScaleFactor) / 2);
+
+ // Adjust window position and size
+ m_window.ResizeClient(
+ {static_cast(layoutMetrics.Frame.Width * (layoutMetrics.PointScaleFactor)),
+ static_cast(layoutMetrics.Frame.Height * (layoutMetrics.PointScaleFactor))});
+ m_window.Move({xCor, yCor});
+ };
+
+ void ShowOnUIThread(const winrt::Microsoft::ReactNative::ComponentView &view) {
+ if (!m_mounted)
+ return;
+
+ m_showQueued = false;
+ EnsureModalCreated(view);
+
+ if (m_window && !m_window.IsVisible()) {
+ m_bridge.Enable();
+ m_window.Show(true);
+
+ auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(m_bridge);
+ auto result = navHost.NavigateFocus(winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(
+ winrt::Microsoft::UI::Input::FocusNavigationReason::First));
+
+ // dispatch onShow event
+ if (auto eventEmitter = EventEmitter()) {
+ ::Microsoft::ReactNativeSpecs::ModalHostViewEventEmitter::OnShow eventArgs;
+ eventEmitter->onShow(eventArgs);
}
- SetProp(hwnd, CompHostProperty, nullptr);
- break;
}
}
- return DefWindowProc(hwnd, message, wparam, lparam);
-}
-
-// Creates and Register a new window class
-void WindowsModalHostComponentView::RegisterWndClass() noexcept {
- static bool registered = false;
- if (registered) {
- return;
- }
-
- HINSTANCE hInstance =
- GetModuleHandle(NULL); // returns a handle to the file used to create the calling process (.exe file)
-
- WNDCLASSEX wcex = {}; // contains window class information
- wcex.cbSize = sizeof(wcex); // size of windows class (bytes)
- wcex.style = CS_HREDRAW | CS_VREDRAW; // class style (redraw window on size adjustment)
- wcex.lpfnWndProc = &ModalBoxWndProc; // pointer to windows procedure
- wcex.cbClsExtra = DLGWINDOWEXTRA; // extra bytes to allocate
- wcex.cbWndExtra =
- sizeof(winrt::impl::abi::type *); // extra bytes to allocate
- wcex.hInstance = hInstance;
- wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); // handle to class cursor
- wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); // background color
- wcex.lpszClassName = c_modalWindowClassName; // specify resource name
- ATOM classId = RegisterClassEx(&wcex); // register new windows class
- WINRT_VERIFY(classId); // 0 = fail
- winrt::check_win32(!classId);
-
- registered = true;
-}
-
-winrt::Microsoft::ReactNative::Composition::Experimental::IVisual
-WindowsModalHostComponentView::VisualToMountChildrenInto() noexcept {
- return m_reactNativeIsland
- .as()
- .InternalRootVisual();
-}
+ void CloseWindow() noexcept {
+ // enable input to parent before closing the modal window, so focus can return back to the parent window
+ EnableWindow(m_parentHwnd, true);
-// childComponentView - reference to the child component view
-// index - the position in which the childComponentView should be mounted
-void WindowsModalHostComponentView::MountChildComponentView(
- const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
- uint32_t index) noexcept {
- EnsureModalCreated();
- base_type::MountChildComponentView(childComponentView, index);
-}
+ if (m_window) {
+ m_window.Hide();
+ }
-void WindowsModalHostComponentView::UnmountChildComponentView(
- const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
- uint32_t index) noexcept {
- base_type::UnmountChildComponentView(childComponentView, index);
-}
+ // dispatch onDismiss event
+ if (auto eventEmitter = EventEmitter()) {
+ ::Microsoft::ReactNativeSpecs::ModalHostViewEventEmitter::OnDismiss eventArgs;
+ eventEmitter->onDismiss(eventArgs);
+ }
-void WindowsModalHostComponentView::updateLayoutMetrics(
- facebook::react::LayoutMetrics const &layoutMetrics,
- facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept {
- base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
- if (m_hwnd) {
- EnsureModalCreated();
- AdjustWindowSize();
- ShowOnUIThread();
- }
-}
+ // reset the topWindowID
+ if (m_prevWindowID) {
+ winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
+ m_reactContext.Properties().Handle(), m_prevWindowID);
+ m_prevWindowID = 0;
+ }
-void WindowsModalHostComponentView::AdjustWindowSize() noexcept {
- if (m_layoutMetrics.overflowInset.right == 0 && m_layoutMetrics.overflowInset.bottom == 0) {
- return;
+ m_bridge.Disable();
}
- // Modal's size is based on it's children, use the overflow to calculate the width/height
- float xPos = (-m_layoutMetrics.overflowInset.right * (m_layoutMetrics.pointScaleFactor));
- float yPos = (-m_layoutMetrics.overflowInset.bottom * (m_layoutMetrics.pointScaleFactor));
- RECT rc;
- GetClientRect(m_hwnd, &rc);
- RECT rect = {0, 0, (int)xPos, (int)yPos};
-
- if (m_showTitleBar) {
- AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); // Adjust for title bar and borders
- }
+ // creates a new modal window
+ void EnsureModalCreated(const winrt::Microsoft::ReactNative::ComponentView &view) {
+ if (m_window) {
+ return;
+ }
- // set the layoutMetrics
- m_layoutMetrics.frame.size = {(float)rect.right - rect.left, (float)rect.bottom - rect.top};
- m_layoutMetrics.overflowInset.right = 0;
- m_layoutMetrics.overflowInset.bottom = 0;
+ // get the root hwnd
+ m_prevWindowID =
+ winrt::Microsoft::ReactNative::ReactCoreInjection::GetTopLevelWindowId(view.ReactContext().Properties());
- // get Modal's position based on parent
- RECT parentRC;
- GetWindowRect(m_parentHwnd, &parentRC);
- float xCor = (parentRC.left + parentRC.right - m_layoutMetrics.frame.size.width) / 2; // midpointx - width / 2
- float yCor = (parentRC.top + parentRC.bottom - m_layoutMetrics.frame.size.height) / 2; // midpointy - height / 2
+ m_parentHwnd =
+ view.as<::Microsoft::ReactNative::Composition::Experimental::IComponentViewInterop>()->GetHwndForParenting();
- // Adjust window position and size
- MoveWindow(m_hwnd, (int)xCor, (int)yCor, (int)(rect.right - rect.left), (int)(rect.bottom - rect.top), true);
+ auto presenter = winrt::Microsoft::UI::Windowing::OverlappedPresenter::CreateForDialog();
+ presenter.SetBorderAndTitleBar(true, false);
+ presenter.IsModal(true);
- // Let RNWIsland know that Modal's size has changed
- winrt::get_self(m_reactNativeIsland)
- ->NotifySizeChanged();
-};
+ m_window = winrt::Microsoft::UI::Windowing::AppWindow::Create(
+ presenter, winrt::Microsoft::UI::GetWindowIdFromWindow(m_parentHwnd));
-void WindowsModalHostComponentView::updateProps(
- facebook::react::Props::Shared const &props,
- facebook::react::Props::Shared const &oldProps) noexcept {
- const auto &oldModalProps =
- *std::static_pointer_cast(oldProps ? oldProps : viewProps());
- const auto &newModalProps = *std::static_pointer_cast(props);
- newModalProps.visible ? m_isVisible = true : m_isVisible = false;
- if (!m_isVisible) {
- HideOnUIThread();
+ // set the top-level windows as the new hwnd
+ winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId(
+ view.ReactContext().Properties(),
+ reinterpret_cast(winrt::Microsoft::UI::GetWindowFromWindowId(m_window.Id())));
+
+ // create a react native island - code taken from CompositionHwndHost
+ m_bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create(
+ view.Parent().as().Compositor(), m_window.Id());
+ m_reactNativeIsland = winrt::Microsoft::ReactNative::ReactNativeIsland::CreatePortal(
+ view.as());
+ auto contentIsland = m_reactNativeIsland.Island();
+
+ auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(m_bridge);
+ m_departFocusToken = navHost.DepartFocusRequested(
+ [wkView = winrt::make_weak(view)](
+ const auto &sender, const winrt::Microsoft::UI::Input::FocusNavigationRequestEventArgs &args) {
+ if (auto strongView = wkView.get()) {
+ TrySetFocus(strongView.Parent());
+ }
+ });
+
+ m_bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow);
+ m_bridge.Connect(contentIsland);
+ AdjustWindowSize(view.LayoutMetrics());
+ m_bridge.Show();
}
- base_type::updateProps(props, oldProps);
-}
-facebook::react::SharedViewProps WindowsModalHostComponentView::defaultProps() noexcept {
- static auto const defaultProps = std::make_shared();
- return defaultProps;
-}
-const facebook::react::ModalHostViewProps &WindowsModalHostComponentView::modalHostViewProps() const noexcept {
- return *std::static_pointer_cast(viewProps());
-}
-
-facebook::react::Tag WindowsModalHostComponentView::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 ((ignorePointerEvents || viewProps()->pointerEvents == facebook::react::PointerEventsMode::Auto ||
- viewProps()->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) {
- localPt = ptLocal;
- return Tag();
+ static void TrySetFocus(const winrt::Microsoft::ReactNative::ComponentView &view) {
+ auto focusController = winrt::Microsoft::UI::Input::InputFocusController::GetForIsland(
+ view.as().Root().ReactNativeIsland().Island());
+ focusController.TrySetFocus();
}
- return -1;
-}
-
-bool WindowsModalHostComponentView::focusable() const noexcept {
- return false;
-}
+ ReactContext m_reactContext{nullptr};
+ HWND m_parentHwnd{nullptr};
+ winrt::Microsoft::UI::Windowing::AppWindow m_window{nullptr};
+ uint64_t m_prevWindowID;
+ bool m_showTitleBar{false};
+ bool m_showQueued{false};
+ bool m_mounted{false};
+ winrt::Microsoft::UI::Input::InputFocusNavigationHost::DepartFocusRequested_revoker m_departFocusRevoker;
+ winrt::event_token m_departFocusToken;
+ winrt::Microsoft::UI::Content::DesktopChildSiteBridge m_bridge{nullptr};
+ winrt::Microsoft::ReactNative::ReactNativeIsland m_reactNativeIsland{nullptr};
+};
-std::string WindowsModalHostComponentView::DefaultControlType() const noexcept {
- return "modal";
+void RegisterWindowsModalHostNativeComponent(
+ winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept {
+ ::Microsoft::ReactNativeSpecs::RegisterModalHostViewNativeComponent(
+ packageBuilder,
+ [](const winrt::Microsoft::ReactNative::Composition::IReactCompositionViewComponentBuilder &builder) {
+ builder.SetPortalComponentViewInitializer(
+ [](const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portalComponentView) noexcept {
+ auto userData = winrt::make_self();
+ userData->InitializePortalViewComponent(portalComponentView);
+ portalComponentView.UserData(*userData);
+ });
+ });
}
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.h
index fbb3f10ec21..0f75d74d7a0 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.h
@@ -3,69 +3,11 @@
#pragma once
-#include
-#include
-
-#include "Composition.WindowsModalHostComponentView.g.h"
-#include "../CompositionViewComponentView.h"
-
-#include
-#include
+#include
namespace winrt::Microsoft::ReactNative::Composition::implementation {
-struct WindowsModalHostComponentView
- : WindowsModalHostComponentViewT {
- using Super = WindowsModalHostComponentViewT;
-
- ~WindowsModalHostComponentView();
-
- [[nodiscard]] static winrt::Microsoft::ReactNative::ComponentView Create(
- const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
- facebook::react::Tag tag,
- winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept;
-
- winrt::Microsoft::ReactNative::Composition::Experimental::IVisual VisualToMountChildrenInto() noexcept override;
- void MountChildComponentView(
- const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
- uint32_t index) noexcept override;
- void UnmountChildComponentView(
- const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
- uint32_t index) noexcept override;
-
- void AdjustWindowSize() noexcept;
-
- void updateLayoutMetrics(
- facebook::react::LayoutMetrics const &layoutMetrics,
- facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept override;
-
- void updateProps(facebook::react::Props::Shared const &props, facebook::react::Props::Shared const &oldProps) noexcept
- override;
- static facebook::react::SharedViewProps defaultProps() noexcept;
- const facebook::react::ModalHostViewProps &modalHostViewProps() const noexcept;
- bool focusable() const noexcept override;
- facebook::react::Tag hitTest(facebook::react::Point pt, facebook::react::Point &localPt, bool ignorePointerEvents)
- const noexcept override;
- virtual std::string DefaultControlType() const noexcept;
-
- WindowsModalHostComponentView(
- const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
- facebook::react::Tag tag,
- winrt::Microsoft::ReactNative::ReactContext const &reactContext);
-
- // Used for creating new window
- void ShowOnUIThread();
- void HideOnUIThread() noexcept;
- void EnsureModalCreated();
- static void RegisterWndClass() noexcept;
-
- private:
- HWND m_parentHwnd{nullptr};
- HWND m_hwnd{nullptr};
- uint64_t m_prevWindowID;
- bool m_isVisible{false};
- bool m_showTitleBar{false};
- winrt::Microsoft::ReactNative::ReactNativeIsland m_reactNativeIsland;
-};
+void RegisterWindowsModalHostNativeComponent(
+ winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept;
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewShadowNode.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewShadowNode.cpp
deleted file mode 100644
index 35d109c5629..00000000000
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewShadowNode.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-// [Windows
-#include "WindowsModalHostViewShadowNode.h"
-// ]
-
-#include
-
-namespace facebook::react {
-
-extern const char WindowsModalHostViewComponentName[] = "ModalHostView";
-
-} // namespace facebook::react
\ No newline at end of file
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewShadowNode.h b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewShadowNode.h
deleted file mode 100644
index caa591fc0d9..00000000000
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewShadowNode.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) Meta Platforms, Inc. and affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-#pragma once
-
-#include
-// [Windows
-#include
-#include
-// ]
-#include
-
-namespace facebook::react {
-
-extern const char WindowsModalHostViewComponentName[];
-
-/*
- * `ShadowNode` for component.
- */
-class WindowsModalHostViewShadowNode final : public ConcreteViewShadowNode<
- WindowsModalHostViewComponentName,
- ModalHostViewProps,
- ModalHostViewEventEmitter,
- ModalHostViewState> {
- public:
- using ConcreteViewShadowNode::ConcreteViewShadowNode;
-
- static ShadowNodeTraits BaseTraits() {
- auto traits = ConcreteViewShadowNode::BaseTraits();
- traits.set(ShadowNodeTraits::Trait::RootNodeKind);
- return traits;
- }
-};
-
-} // namespace facebook::react
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/PortalComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/PortalComponentView.cpp
new file mode 100644
index 00000000000..1726973ebe8
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/PortalComponentView.cpp
@@ -0,0 +1,66 @@
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#pragma once
+
+#include "PortalComponentView.h"
+
+#include "Composition.PortalComponentView.g.cpp"
+
+namespace winrt::Microsoft::ReactNative::Composition::implementation {
+
+PortalComponentView::PortalComponentView(
+ const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
+ facebook::react::Tag tag,
+ winrt::Microsoft::ReactNative::ReactContext const &reactContext,
+ winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder *builder)
+ : base_type(tag, reactContext, builder) {
+ m_rootComponentView = winrt::make_self(
+ compContext, *this, reactContext);
+}
+
+PortalComponentView::~PortalComponentView() {}
+
+winrt::Microsoft::ReactNative::Composition::RootComponentView PortalComponentView::ContentRoot() const noexcept {
+ return *m_rootComponentView;
+}
+
+void PortalComponentView::MountChildComponentView(
+ const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
+ uint32_t index) noexcept {
+ m_rootComponentView->MountChildComponentView(childComponentView, index);
+}
+
+void PortalComponentView::UnmountChildComponentView(
+ const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
+ uint32_t index) noexcept {
+ m_rootComponentView->UnmountChildComponentView(childComponentView, index);
+}
+
+const winrt::Microsoft::ReactNative::IComponentProps PortalComponentView::userProps(
+ facebook::react::Props::Shared const &props) noexcept {
+ const auto &abiViewProps = *std::static_pointer_cast(props);
+ return abiViewProps.UserProps();
+}
+
+void PortalComponentView::updateProps(
+ facebook::react::Props::Shared const &props,
+ facebook::react::Props::Shared const &oldProps) noexcept {
+ m_rootComponentView->updateProps(props, oldProps);
+ base_type::updateProps(props, oldProps);
+}
+
+void PortalComponentView::updateLayoutMetrics(
+ facebook::react::LayoutMetrics const &layoutMetrics,
+ facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept {
+ m_rootComponentView->updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
+ base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
+}
+
+void PortalComponentView::FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept {
+ m_rootComponentView->FinalizeUpdates(updateMask);
+ base_type::FinalizeUpdates(updateMask);
+}
+
+} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/PortalComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/PortalComponentView.h
new file mode 100644
index 00000000000..29089ba6744
--- /dev/null
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/PortalComponentView.h
@@ -0,0 +1,52 @@
+
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#pragma once
+
+#include
+#include
+#include "RootComponentView.h"
+
+#include "Composition.PortalComponentView.g.h"
+
+namespace Microsoft::ReactNative {
+struct CompContext;
+} // namespace Microsoft::ReactNative
+
+namespace winrt::Microsoft::ReactNative::Composition::implementation {
+
+struct PortalComponentView
+ : public PortalComponentViewT {
+ PortalComponentView(
+ const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
+ facebook::react::Tag tag,
+ winrt::Microsoft::ReactNative::ReactContext const &reactContext,
+ winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder *builder);
+ virtual ~PortalComponentView();
+
+ winrt::Microsoft::ReactNative::Composition::RootComponentView ContentRoot() const noexcept;
+
+ void MountChildComponentView(
+ const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
+ uint32_t index) noexcept override;
+ void UnmountChildComponentView(
+ const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
+ uint32_t index) noexcept override;
+
+ const winrt::Microsoft::ReactNative::IComponentProps userProps(
+ facebook::react::Props::Shared const &props) noexcept override;
+ void updateProps(facebook::react::Props::Shared const &props, facebook::react::Props::Shared const &oldProps) noexcept
+ override;
+
+ void updateLayoutMetrics(
+ facebook::react::LayoutMetrics const &layoutMetrics,
+ facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept override;
+
+ void FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept override;
+
+ private:
+ winrt::com_ptr m_rootComponentView;
+};
+
+} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp
index b19c3bd9b56..f7c96535343 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp
@@ -5,6 +5,7 @@
#include "ReactCompositionViewComponentBuilder.h"
#include
#include
+#include
#include
#include "CompositionContextHelper.h"
#include "DynamicWriter.h"
@@ -111,6 +112,26 @@ void ReactCompositionViewComponentBuilder::SetContentIslandComponentViewInitiali
};
}
+void ReactCompositionViewComponentBuilder::SetPortalComponentViewInitializer(
+ const PortalComponentViewInitializer &initializer) noexcept {
+ m_fnCreateView = [initializer](
+ const IReactContext &reactContext,
+ int32_t tag,
+ const Experimental::ICompositionContext &context,
+ ComponentViewFeatures /*features*/,
+ ReactCompositionViewComponentBuilder &builder)
+ -> winrt::Microsoft::ReactNative::Composition::PortalComponentView {
+ auto view = winrt::make(
+ context, tag, reactContext, &builder);
+ initializer(view);
+ return view;
+ };
+ m_descriptorConstructorFactory = []() {
+ return &facebook::react::concreteComponentDescriptorConstructor<
+ ::Microsoft::ReactNative::AbiViewComponentDescriptor>;
+ };
+}
+
// (Object handle, Microsoft.ReactNative.IComponentState state) => void
// void ReactCompositionViewComponentBuilder::SetStateUpdater(StateUpdater impl) noexcept {
// m_stateUpdater = impl;
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h
index 618aae06e5d..852d08a8cd8 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h
@@ -12,10 +12,12 @@
namespace winrt::Microsoft::ReactNative::Composition {
-struct ReactCompositionViewComponentBuilder : winrt::implements<
- ReactCompositionViewComponentBuilder,
- IReactViewComponentBuilder,
- Composition::IReactCompositionViewComponentBuilder> {
+struct ReactCompositionViewComponentBuilder
+ : winrt::implements<
+ ReactCompositionViewComponentBuilder,
+ IReactViewComponentBuilder,
+ Composition::IReactCompositionViewComponentBuilder,
+ Composition::Experimental::IReactCompositionViewComponentInternalBuilder> {
ReactCompositionViewComponentBuilder() noexcept;
public: // IReactViewComponentBuilder
@@ -42,6 +44,7 @@ struct ReactCompositionViewComponentBuilder : winrt::implements<
public: // Composition::IReactCompositionViewComponentBuilder
void SetViewComponentViewInitializer(const ViewComponentViewInitializer &initializer) noexcept;
void SetContentIslandComponentViewInitializer(const ComponentIslandComponentViewInitializer &initializer) noexcept;
+ void SetPortalComponentViewInitializer(const PortalComponentViewInitializer &initializer) noexcept;
void SetCreateVisualHandler(CreateVisualDelegate impl) noexcept;
void SetViewFeatures(ComponentViewFeatures viewFeatures) noexcept;
void SetVisualToMountChildrenIntoHandler(VisualToMountChildrenIntoDelegate impl) noexcept;
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp
index a27be342689..587f7b37c36 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp
@@ -127,21 +127,39 @@ ReactNativeIsland::ReactNativeIsland(const winrt::Microsoft::UI::Composition::Co
InitTextScaleMultiplier();
}
-// Constructor to initialize ReactNativeIsland with context and componentView
ReactNativeIsland::ReactNativeIsland(
- const winrt::Microsoft::UI::Composition::Compositor &compositor,
- winrt::Microsoft::ReactNative::IReactContext context,
- winrt::Microsoft::ReactNative::ComponentView componentView) noexcept
- : m_compositor(compositor),
- m_context(context),
+ const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portal) noexcept
+ : m_compositor(portal.ContentRoot().Compositor()),
+ m_context(portal.ReactContext()),
m_layoutConstraints({{0, 0}, {0, 0}, winrt::Microsoft::ReactNative::LayoutDirection::Undefined}),
m_isFragment(true) {
- m_rootTag = componentView.Tag();
+ m_portal = winrt::make_weak(portal);
+
+ auto trueRoot =
+ winrt::get_self(portal)
+ ->rootComponentView();
+ while (auto p = trueRoot->Portal()) {
+ trueRoot = winrt::get_self(p)
+ ->rootComponentView();
+ };
+ m_rootTag = trueRoot->Tag();
+
InitTextScaleMultiplier();
- AddFragmentCompositionEventHandler(context, componentView);
+ AddFragmentCompositionEventHandler(m_context.Handle(), portal.ContentRoot());
+ auto selfPortal = winrt::get_self(
+ portal.ContentRoot());
+ selfPortal->ReactNativeIsland(*this);
+ NotifySizeChanged();
+ selfPortal->start(*this);
+}
+
+winrt::Microsoft::ReactNative::ReactNativeIsland ReactNativeIsland::CreatePortal(
+ const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portal) noexcept {
+ return winrt::make(portal);
}
-ReactNativeIsland::ReactNativeIsland() noexcept : ReactNativeIsland(nullptr) {}
+ReactNativeIsland::ReactNativeIsland() noexcept
+ : ReactNativeIsland(winrt::Microsoft::UI::Composition::Compositor{nullptr}) {}
ReactNativeIsland::~ReactNativeIsland() noexcept {
#ifdef USE_WINUI3
@@ -159,14 +177,20 @@ ReactNativeIsland::~ReactNativeIsland() noexcept {
assert(m_uiDispatcher.HasThreadAccess());
UninitRootView();
}
+
+ if (m_island) {
+ m_island.Close();
+ }
}
ReactNative::IReactViewHost ReactNativeIsland::ReactViewHost() noexcept {
return m_reactViewHost;
}
-void ReactNativeIsland::ReactViewHost(winrt::Microsoft::ReactNative::IReactViewHost const &value) noexcept {
- assert(!m_isFragment); // make sure this isn't a FragmentIsalnd
+void ReactNativeIsland::ReactViewHost(winrt::Microsoft::ReactNative::IReactViewHost const &value) {
+ if (m_isFragment)
+ winrt::throw_hresult(E_ACCESSDENIED);
+
if (m_reactViewHost == value) {
return;
}
@@ -217,6 +241,8 @@ void ReactNativeIsland::AddRenderedVisual(
void ReactNativeIsland::RemoveRenderedVisual(
const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual) noexcept {
+ if (m_isFragment)
+ return;
assert(m_hasRenderedVisual);
InternalRootVisual().Remove(visual);
m_hasRenderedVisual = false;
@@ -442,7 +468,7 @@ void ReactNativeIsland::InitRootView(
m_context = winrt::Microsoft::ReactNative::ReactContext(std::move(context));
m_reactViewOptions = std::move(viewOptions);
- m_CompositionEventHandler = std::make_shared<::Microsoft::ReactNative::CompositionEventHandler>(m_context, *this, -1);
+ m_CompositionEventHandler = std::make_shared<::Microsoft::ReactNative::CompositionEventHandler>(m_context, *this);
m_CompositionEventHandler->Initialize();
UpdateRootViewInternal();
@@ -457,15 +483,13 @@ void ReactNativeIsland::AddFragmentCompositionEventHandler(
.Get(winrt::Microsoft::ReactNative::ReactDispatcherHelper::UIDispatcherProperty())
.try_as();
VerifyElseCrash(m_uiDispatcher.HasThreadAccess());
- VerifyElseCrash(m_rootTag != -1);
auto uiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(
winrt::Microsoft::ReactNative::ReactPropertyBag(context.Properties()));
if (!m_CompositionEventHandler) {
// Create CompositionEventHandler if not already created
m_context = winrt::Microsoft::ReactNative::ReactContext(context);
- m_CompositionEventHandler =
- std::make_shared<::Microsoft::ReactNative::CompositionEventHandler>(m_context, *this, componentView.Tag());
+ m_CompositionEventHandler = std::make_shared<::Microsoft::ReactNative::CompositionEventHandler>(m_context, *this);
m_CompositionEventHandler->Initialize();
m_isInitialized = true;
}
@@ -545,6 +569,7 @@ void ReactNativeIsland::ClearLoadingUI() noexcept {
void ReactNativeIsland::EnsureLoadingUI() noexcept {}
void ReactNativeIsland::ShowInstanceLoaded() noexcept {
+ VerifyElseCrash(!m_isFragment);
if (m_rootVisual) {
ClearLoadingUI();
@@ -740,6 +765,9 @@ void ReactNativeIsland::InitTextScaleMultiplier() noexcept {
winrt::Windows::Foundation::Size ReactNativeIsland::Measure(
const winrt::Microsoft::ReactNative::LayoutConstraints &layoutConstraints,
const winrt::Windows::Foundation::Point &viewportOffset) const {
+ if (m_isFragment)
+ winrt::throw_hresult(E_ILLEGAL_METHOD_CALL);
+
facebook::react::Size size{0, 0};
if (layoutConstraints.LayoutDirection != winrt::Microsoft::ReactNative::LayoutDirection::LeftToRight &&
@@ -781,7 +809,7 @@ void ReactNativeIsland::Arrange(
facebook::react::LayoutConstraints fbLayoutConstraints;
ApplyConstraints(layoutConstraints, fbLayoutConstraints);
- if (m_isInitialized && m_rootTag != -1) {
+ if (m_isInitialized && m_rootTag != -1 && !m_isFragment) {
if (auto fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(
winrt::Microsoft::ReactNative::ReactPropertyBag(m_context.Properties()))) {
facebook::react::LayoutContext context;
@@ -820,6 +848,23 @@ winrt::Microsoft::UI::Content::ContentIsland ReactNativeIsland::Island() {
rootVisual));
m_island = winrt::Microsoft::UI::Content::ContentIsland::Create(rootVisual);
+ auto focusController = winrt::Microsoft::UI::Input::InputFocusController::GetForIsland(m_island);
+ focusController.NavigateFocusRequested(
+ [weakThis = get_weak()](
+ const auto &sender, const winrt::Microsoft::UI::Input::FocusNavigationRequestEventArgs &args) {
+ if (auto pThis = weakThis.get()) {
+ if (auto rootView = pThis->GetComponentView()) {
+ args.Result(
+ rootView->NavigateFocus(winrt::Microsoft::ReactNative::FocusNavigationRequest(
+ winrt::Microsoft::ReactNative::FocusNavigationReason::First))
+ ? winrt::Microsoft::UI::Input::FocusNavigationResult::Moved
+ : winrt::Microsoft::UI::Input::FocusNavigationResult::NotMoved);
+ } else {
+ args.Result(winrt::Microsoft::UI::Input::FocusNavigationResult::NoFocusableElements);
+ }
+ }
+ });
+
// ContentIsland does not support weak_ref, so we cannot use auto_revoke for these events
m_islandAutomationProviderRequestedToken = m_island.AutomationProviderRequested(
[weakThis = get_weak()](
@@ -903,13 +948,29 @@ void ReactNativeIsland::OnUnmounted() noexcept {
if (!m_mounted)
return;
m_mounted = false;
+
+ if (m_island && m_island.IsConnected()) {
+ auto focusController = winrt::Microsoft::UI::Input::InputFocusController::GetForIsland(m_island);
+ auto request = winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(
+ winrt::Microsoft::UI::Input::FocusNavigationReason::Programmatic);
+ if (focusController.HasFocus()) {
+ focusController.DepartFocus(request);
+ }
+ }
+
if (auto componentView = GetComponentView()) {
componentView->onUnmounted();
}
}
-winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *
+winrt::com_ptr
ReactNativeIsland::GetComponentView() noexcept {
+ if (auto portal = m_portal.get()) {
+ return winrt::get_self(portal)
+ ->ContentRoot()
+ .as();
+ }
+
if (!m_context || m_context.Handle().LoadingState() != winrt::Microsoft::ReactNative::LoadingState::Loaded ||
m_rootTag == -1)
return nullptr;
@@ -919,8 +980,7 @@ ReactNativeIsland::GetComponentView() noexcept {
auto rootComponentViewDescriptor = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(
static_cast(m_rootTag));
return rootComponentViewDescriptor.view
- .as()
- .get();
+ .as();
}
return nullptr;
}
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h
index c51aa7bd610..8d8ed359be8 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h
@@ -12,6 +12,7 @@
#include
#include
#include "CompositionEventHandler.h"
+#include "PortalComponentView.h"
#include "ReactHost/React.h"
namespace winrt::Microsoft::ReactNative::implementation {
@@ -48,15 +49,15 @@ struct ReactNativeIsland
~ReactNativeIsland() noexcept;
ReactNativeIsland(const winrt::Microsoft::UI::Composition::Compositor &compositor) noexcept;
- ReactNativeIsland(
- const winrt::Microsoft::UI::Composition::Compositor &compositor,
- winrt::Microsoft::ReactNative::IReactContext context,
- winrt::Microsoft::ReactNative::ComponentView componentView) noexcept;
+ ReactNativeIsland(const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portal) noexcept;
+
+ static winrt::Microsoft::ReactNative::ReactNativeIsland CreatePortal(
+ const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portal) noexcept;
winrt::Microsoft::UI::Content::ContentIsland Island();
// property ReactViewHost
ReactNative::IReactViewHost ReactViewHost() noexcept;
- void ReactViewHost(ReactNative::IReactViewHost const &value) noexcept;
+ void ReactViewHost(ReactNative::IReactViewHost const &value);
winrt::Microsoft::UI::Composition::Visual RootVisual() noexcept;
@@ -104,7 +105,8 @@ struct ReactNativeIsland
winrt::Microsoft::ReactNative::FocusNavigationResult NavigateFocus(
const winrt::Microsoft::ReactNative::FocusNavigationRequest &request) noexcept;
- winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *GetComponentView() noexcept;
+ winrt::com_ptr
+ GetComponentView() noexcept;
int64_t RootTag() const noexcept;
@@ -153,8 +155,12 @@ struct ReactNativeIsland
bool m_hasRenderedVisual{false};
bool m_showingLoadingUI{false};
bool m_mounted{false};
+ winrt::weak_ref m_portal{nullptr};
IReactDispatcher m_uiDispatcher{nullptr};
winrt::IInspectable m_uiaProvider{nullptr};
+
+ // This is the surfaceId that this island belongs to.
+ // In the case of portal content root, this will be the surfaceId that contains the portal.
int64_t m_rootTag{-1};
float m_scaleFactor{1.0};
float m_textScaleMultiplier{1.0};
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
index 45d4a49dc1f..8cb42b9226e 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp
@@ -17,7 +17,8 @@ namespace winrt::Microsoft::ReactNative::Composition::implementation {
RootComponentView::RootComponentView(
const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
facebook::react::Tag tag,
- winrt::Microsoft::ReactNative::ReactContext const &reactContext)
+ winrt::Microsoft::ReactNative::ReactContext const &reactContext,
+ ReactCompositionViewComponentBuilder *builder)
: base_type(
{}, // default viewProps
compContext,
@@ -25,20 +26,34 @@ RootComponentView::RootComponentView(
reactContext,
ComponentViewFeatures::Default &
~(ComponentViewFeatures::Background | ComponentViewFeatures::ShadowProps |
- ComponentViewFeatures::NativeBorder | ComponentViewFeatures::FocusVisual)) {}
+ ComponentViewFeatures::NativeBorder | ComponentViewFeatures::FocusVisual),
+ builder) {}
+
+RootComponentView::RootComponentView(
+ const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
+ const winrt::Microsoft::ReactNative::Composition::PortalComponentView &portal,
+ winrt::Microsoft::ReactNative::ReactContext const &reactContext)
+ : base_type(
+ {}, // default viewProps
+ compContext,
+ -1,
+ reactContext,
+ ComponentViewFeatures::Default &
+ ~(ComponentViewFeatures::Background | ComponentViewFeatures::ShadowProps |
+ ComponentViewFeatures::NativeBorder | ComponentViewFeatures::FocusVisual),
+ nullptr // builder,
+ ),
+ m_wkPortal(portal) {}
RootComponentView::~RootComponentView() {
- if (auto rootView = m_wkRootView.get()) {
- winrt::get_self(rootView)->RemoveRenderedVisual(
- OuterVisual());
- }
+ stop();
}
winrt::Microsoft::ReactNative::ComponentView RootComponentView::Create(
const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
facebook::react::Tag tag,
winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept {
- return winrt::make(compContext, tag, reactContext);
+ return winrt::make(compContext, tag, reactContext, nullptr);
}
RootComponentView *RootComponentView::rootComponentView() const noexcept {
@@ -84,13 +99,13 @@ void RootComponentView::SetFocusedComponent(const winrt::Microsoft::ReactNative:
bool RootComponentView::NavigateFocus(const winrt::Microsoft::ReactNative::FocusNavigationRequest &request) noexcept {
if (request.Reason() == winrt::Microsoft::ReactNative::FocusNavigationReason::Restore) {
- // No-op for now
- return m_focusedComponent != nullptr;
+ if (m_focusedComponent)
+ return true;
}
- auto view = (request.Reason() == winrt::Microsoft::ReactNative::FocusNavigationReason::First)
- ? FocusManager::FindFirstFocusableElement(*this)
- : FocusManager::FindLastFocusableElement(*this);
+ auto view = (request.Reason() == winrt::Microsoft::ReactNative::FocusNavigationReason::Last)
+ ? FocusManager::FindLastFocusableElement(*this)
+ : FocusManager::FindFirstFocusableElement(*this);
if (view) {
TrySetFocusedComponent(
view,
@@ -116,8 +131,10 @@ bool RootComponentView::TrySetFocusedComponent(
selfView = winrt::get_self(target);
}
- if (selfView && selfView->rootComponentView() != this)
+ if (selfView && selfView->rootComponentView() != this) {
+ assert(false);
return false;
+ }
auto losingFocusArgs = winrt::make(
target, direction, m_focusedComponent, target);
@@ -197,7 +214,7 @@ HRESULT RootComponentView::GetFragmentRoot(IRawElementProviderFragmentRoot **pRe
if (uiManager == nullptr)
return UIA_E_ELEMENTNOTAVAILABLE;
- auto rootView{uiManager->GetReactNativeIsland(Tag())};
+ auto rootView = m_wkRootView.get();
if (!rootView) {
return UIA_E_ELEMENTNOTAVAILABLE;
}
@@ -214,10 +231,43 @@ uint32_t RootComponentView::overlayIndex() noexcept {
return 1;
}
-void RootComponentView::start(const winrt::Microsoft::ReactNative::ReactNativeIsland &rootView) noexcept {
- winrt::get_self(rootView)->AddRenderedVisual(
+void RootComponentView::start(const winrt::Microsoft::ReactNative::ReactNativeIsland &island) noexcept {
+ theme(winrt::get_self(island.Theme()));
+
+ winrt::get_self(island)->AddRenderedVisual(
OuterVisual());
- m_wkRootView = rootView;
+ m_visualAddedToIsland = true;
+ ReactNativeIsland(island);
+}
+
+void RootComponentView::stop() noexcept {
+ SetFocusedComponent(nullptr);
+ if (m_visualAddedToIsland) {
+ if (auto rootView = m_wkRootView.get()) {
+ winrt::get_self(rootView)->RemoveRenderedVisual(
+ OuterVisual());
+ }
+ m_visualAddedToIsland = false;
+ }
+ // Disconnect from the Island. In case of an instance reload, the island may now
+ // be attached to a new RootComponentView, so we should stop interacting with it.
+ ReactNativeIsland(nullptr);
+}
+
+void RootComponentView::ReactNativeIsland(const winrt::Microsoft::ReactNative::ReactNativeIsland &island) noexcept {
+ m_wkRootView = island;
+}
+
+winrt::Microsoft::ReactNative::ReactNativeIsland RootComponentView::ReactNativeIsland() noexcept {
+ return m_wkRootView.get();
+}
+
+winrt::Microsoft::ReactNative::Composition::PortalComponentView RootComponentView::Portal() const noexcept {
+ return m_wkPortal.get();
+}
+
+facebook::react::Point RootComponentView::getClientOffset() const noexcept {
+ return {};
}
winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixels) noexcept {
@@ -226,6 +276,10 @@ winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixel
static_cast(ptPixels.y) / m_layoutMetrics.pointScaleFactor};
facebook::react::Point localPt;
+
+ // In the case of a sub rootview, we may have a non-zero origin. hitTest takes a pt in the parent coords, so we need
+ // to apply the current origin
+ ptDips += m_layoutMetrics.frame.origin;
auto tag = hitTest(ptDips, localPt, true);
auto uiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_reactContext.Properties());
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h
index 3043faec6ec..7790d171693 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h
@@ -46,6 +46,12 @@ struct RootComponentView : RootComponentViewT m_wkRootView{nullptr};
+ winrt::weak_ref m_wkPortal{nullptr};
+ bool m_visualAddedToIsland{false};
};
} // namespace winrt::Microsoft::ReactNative::Composition::implementation
diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp
index 3e03460ad23..4739ca7ff5a 100644
--- a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp
@@ -42,44 +42,59 @@ HRESULT UiaNavigateHelper(
}
} break;
- case NavigateDirection_LastChild:
- __fallthrough;
-
+ case NavigateDirection_LastChild: {
+ auto children = view.Children();
+ if (children.Size() != 0) {
+ uint32_t index = children.Size() - 1;
+ do {
+ auto child = children.GetAt(index).as();
+ if (uiaProvider = child->EnsureUiaProvider()) {
+ break;
+ }
+ } while (index-- != 0);
+ }
+ } break;
case NavigateDirection_FirstChild: {
auto children = view.Children();
- auto index = direction == NavigateDirection_FirstChild ? 0 : children.Size() - 1;
- if (!children.Size() == 0) {
- uiaProvider = children.GetAt(index)
- .as()
- ->EnsureUiaProvider();
+ if (children.Size() != 0) {
+ uint32_t index = 0;
+ do {
+ auto child = children.GetAt(index).as();
+ if (uiaProvider = child->EnsureUiaProvider()) {
+ break;
+ }
+ } while (++index != children.Size());
}
} break;
-
case NavigateDirection_NextSibling: {
- auto parentCV = view.Parent().as();
+ auto parentCV = view.Parent().as();
if (parentCV != nullptr) {
auto children = parentCV->Children();
auto it = std::find(children.begin(), children.end(), view);
- if (++it != children.end()) {
- uiaProvider = (*it)
- .as()
- ->EnsureUiaProvider();
+
+ while (++it != children.end()) {
+ auto nextchild = (*it).as();
+ if (uiaProvider = nextchild->EnsureUiaProvider()) {
+ break;
+ }
}
}
} break;
case NavigateDirection_PreviousSibling: {
- auto parentCV = view.Parent().as();
+ auto parentCV = view.Parent().as();
if (parentCV != nullptr) {
auto children = parentCV->Children();
- for (auto i = children.Size() - 1; i > 0; i--) {
- auto child = children.GetAt(i);
- if (child == view) {
- uiaProvider = children.GetAt(i - 1)
- .as()
- ->EnsureUiaProvider();
- break;
- }
+ auto it = std::find(children.begin(), children.end(), view);
+
+ if (it != children.begin()) {
+ do {
+ it--;
+ auto prevchild = (*it).as();
+ if (uiaProvider = prevchild->EnsureUiaProvider()) {
+ break;
+ }
+ } while (it != children.begin());
}
}
} break;
diff --git a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp
index 42339633147..7ecc02d985c 100644
--- a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp
@@ -134,7 +134,6 @@ void FabricUIManager::startSurface(
auto root = rootComponentViewDescriptor.view
.as();
- root->theme(winrt::get_self(rootView.Theme()));
root->start(rootView);
});
@@ -154,15 +153,11 @@ void FabricUIManager::startSurface(
void FabricUIManager::stopSurface(facebook::react::SurfaceId surfaceId) noexcept {
m_surfaceManager->stopSurface(surfaceId);
auto &rootDescriptor = m_registry.componentViewDescriptorWithTag(surfaceId);
+ rootDescriptor.view.as()->stop();
m_registry.enqueueComponentViewWithComponentHandle(
facebook::react::RootShadowNode::Handle(), surfaceId, rootDescriptor);
}
-winrt::Microsoft::ReactNative::ReactNativeIsland FabricUIManager::GetReactNativeIsland(
- facebook::react::SurfaceId surfaceId) const noexcept {
- return m_surfaceRegistry.at(surfaceId).wkRootView.get();
-}
-
facebook::react::Size FabricUIManager::measureSurface(
facebook::react::SurfaceId surfaceId,
const facebook::react::LayoutConstraints &layoutConstraints,
diff --git a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h
index 03ffd7635d1..32669487df9 100644
--- a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h
+++ b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h
@@ -49,9 +49,6 @@ struct FabricUIManager final : public std::enable_shared_from_this NotifyMountedId() noexcept;
private:
diff --git a/vnext/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp b/vnext/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp
index c7947ac85d6..b3c0fc9815e 100644
--- a/vnext/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp
+++ b/vnext/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp
@@ -6,7 +6,6 @@
#include
#include
-#include
#include
#include
#include
@@ -37,7 +36,6 @@ WindowsComponentDescriptorRegistry::WindowsComponentDescriptorRegistry()
facebook::react::ActivityIndicatorViewComponentDescriptor>());
add(facebook::react::concreteComponentDescriptorProvider());
add(facebook::react::concreteComponentDescriptorProvider());
- add(facebook::react::concreteComponentDescriptorProvider());
add(facebook::react::concreteComponentDescriptorProvider());
add(facebook::react::concreteComponentDescriptorProvider());
add(facebook::react::concreteComponentDescriptorProvider());
diff --git a/vnext/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl b/vnext/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl
index 2edf2b822aa..788b982e463 100644
--- a/vnext/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl
+++ b/vnext/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl
@@ -21,6 +21,10 @@ namespace Microsoft.ReactNative.Composition
[experimental]
delegate void ComponentIslandComponentViewInitializer(ContentIslandComponentView view);
+ [webhosthidden]
+ [experimental]
+ delegate void PortalComponentViewInitializer(PortalComponentView view);
+
[experimental]
[webhosthidden]
delegate Microsoft.UI.Composition.Visual CreateVisualDelegate(Microsoft.ReactNative.ComponentView view);
@@ -55,6 +59,7 @@ namespace Microsoft.ReactNative.Composition
{
void SetViewComponentViewInitializer(ViewComponentViewInitializer initializer);
void SetContentIslandComponentViewInitializer(ComponentIslandComponentViewInitializer initializer);
+ void SetPortalComponentViewInitializer(PortalComponentViewInitializer initializer);
void SetCreateVisualHandler(CreateVisualDelegate impl);
void SetViewFeatures(ComponentViewFeatures viewFeatures);
void SetUpdateLayoutMetricsHandler(UpdateLayoutMetricsDelegate impl);
diff --git a/vnext/Microsoft.ReactNative/ReactNativeHost.cpp b/vnext/Microsoft.ReactNative/ReactNativeHost.cpp
index 781bbd9d657..4b41880ffca 100644
--- a/vnext/Microsoft.ReactNative/ReactNativeHost.cpp
+++ b/vnext/Microsoft.ReactNative/ReactNativeHost.cpp
@@ -15,6 +15,7 @@
#include "ReactInstanceSettings.h"
#ifdef USE_FABRIC
+#include
#include
#include
#include
@@ -115,6 +116,10 @@ IAsyncAction ReactNativeHost::ReloadInstance() noexcept {
#endif
m_instanceSettings.UseWebDebugger());
+#ifdef USE_FABRIC
+ winrt::Microsoft::ReactNative::Composition::implementation::RegisterWindowsModalHostNativeComponent(m_packageBuilder);
+#endif
+
if (auto packageProviders = InstanceSettings().PackageProviders()) {
for (auto const &packageProvider : packageProviders) {
packageProvider.CreatePackage(m_packageBuilder);
diff --git a/vnext/Microsoft.ReactNative/ReactNativeIsland.idl b/vnext/Microsoft.ReactNative/ReactNativeIsland.idl
index 453bbb25107..67d1937c34a 100644
--- a/vnext/Microsoft.ReactNative/ReactNativeIsland.idl
+++ b/vnext/Microsoft.ReactNative/ReactNativeIsland.idl
@@ -6,6 +6,8 @@ import "ReactCoreInjection.idl";
import "ReactNativeHost.idl";
import "Theme.idl";
import "IReactViewComponentBuilder.idl";
+import "CompositionComponentView.idl";
+
#include "NamespaceRedirect.h"
#include "DocString.h"
@@ -83,7 +85,9 @@ namespace Microsoft.ReactNative
#ifdef USE_WINUI3
ReactNativeIsland(Microsoft.UI.Composition.Compositor compositor);
- ReactNativeIsland(Microsoft.UI.Composition.Compositor compositor, Microsoft.ReactNative.IReactContext context, Microsoft.ReactNative.ComponentView componentView);
+
+ DOC_STRING("Used to create react portals, such as a native modal component.")
+ static ReactNativeIsland CreatePortal(Microsoft.ReactNative.Composition.PortalComponentView portal);
#endif
DOC_STRING(
diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems
index 9371302169c..0ee344ffeac 100644
--- a/vnext/Shared/Shared.vcxitems
+++ b/vnext/Shared/Shared.vcxitems
@@ -111,10 +111,10 @@
true
-
+
true
-
+
true
@@ -317,6 +317,7 @@
+
diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters
index a563dd848cd..de115b08a9e 100644
--- a/vnext/Shared/Shared.vcxitems.filters
+++ b/vnext/Shared/Shared.vcxitems.filters
@@ -179,9 +179,6 @@
Source Files\Fabric\Composition
-
- Source Files\Fabric\Composition
-
Source Files\Fabric\Composition
@@ -239,6 +236,7 @@
Source Files\Fabric\platform\react\renderer\textlayoutmanager
+
@@ -786,6 +784,7 @@
Header Files\Fabric\platform\react\renderer\textlayoutmanager
+
diff --git a/vnext/codegen/react/components/rnwcore/ActivityIndicatorView.g.h b/vnext/codegen/react/components/rnwcore/ActivityIndicatorView.g.h
new file mode 100644
index 00000000000..20bded81dc3
--- /dev/null
+++ b/vnext/codegen/react/components/rnwcore/ActivityIndicatorView.g.h
@@ -0,0 +1,212 @@
+
+/*
+ * This file is auto-generated from ActivityIndicatorViewNativeComponent spec file in flow / TypeScript.
+ */
+// clang-format off
+#pragma once
+
+#include
+
+#ifdef RNW_NEW_ARCH
+#include
+
+#include
+#include
+#endif // #ifdef RNW_NEW_ARCH
+
+#ifdef RNW_NEW_ARCH
+
+namespace Microsoft::ReactNativeSpecs {
+
+REACT_STRUCT(ActivityIndicatorViewProps)
+struct ActivityIndicatorViewProps : winrt::implements {
+ ActivityIndicatorViewProps(winrt::Microsoft::ReactNative::ViewProps props, const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom)
+ : ViewProps(props)
+ {
+ if (cloneFrom) {
+ auto cloneFromProps = cloneFrom.as();
+ hidesWhenStopped = cloneFromProps->hidesWhenStopped;
+ animating = cloneFromProps->animating;
+ color = cloneFromProps->color;
+ size = cloneFromProps->size;
+ }
+ }
+
+ void SetProp(uint32_t hash, winrt::hstring propName, winrt::Microsoft::ReactNative::IJSValueReader value) noexcept {
+ winrt::Microsoft::ReactNative::ReadProp(hash, propName, value, *this);
+ }
+
+ REACT_FIELD(hidesWhenStopped)
+ bool hidesWhenStopped{true};
+
+ REACT_FIELD(animating)
+ bool animating{true};
+
+ REACT_FIELD(color)
+ winrt::Microsoft::ReactNative::Color color{nullptr};
+
+ REACT_FIELD(size)
+ std::optional size;
+
+ const winrt::Microsoft::ReactNative::ViewProps ViewProps;
+};
+
+struct ActivityIndicatorViewEventEmitter {
+ ActivityIndicatorViewEventEmitter(const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter)
+ : m_eventEmitter(eventEmitter) {}
+
+ private:
+ winrt::Microsoft::ReactNative::EventEmitter m_eventEmitter{nullptr};
+};
+
+template
+struct BaseActivityIndicatorView {
+
+ virtual void UpdateProps(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::com_ptr &newProps,
+ const winrt::com_ptr &/*oldProps*/) noexcept {
+ m_props = newProps;
+ }
+
+ // UpdateLayoutMetrics will only be called if this method is overridden
+ virtual void UpdateLayoutMetrics(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &/*newLayoutMetrics*/,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &/*oldLayoutMetrics*/) noexcept {
+ }
+
+ // UpdateState will only be called if this method is overridden
+ virtual void UpdateState(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::IComponentState &/*newState*/) noexcept {
+ }
+
+ virtual void UpdateEventEmitter(const std::shared_ptr &eventEmitter) noexcept {
+ m_eventEmitter = eventEmitter;
+ }
+
+ // MountChildComponentView will only be called if this method is overridden
+ virtual void MountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &/*args*/) noexcept {
+ }
+
+ // UnmountChildComponentView will only be called if this method is overridden
+ virtual void UnmountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &/*args*/) noexcept {
+ }
+
+ // Initialize will only be called if this method is overridden
+ virtual void Initialize(const winrt::Microsoft::ReactNative::ComponentView &/*view*/) noexcept {
+ }
+
+ // CreateVisual will only be called if this method is overridden
+ virtual winrt::Microsoft::UI::Composition::Visual CreateVisual(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ return view.as().Compositor().CreateSpriteVisual();
+ }
+
+ // FinalizeUpdate will only be called if this method is overridden
+ virtual void FinalizeUpdate(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ winrt::Microsoft::ReactNative::ComponentViewUpdateMask /*mask*/) noexcept {
+ }
+
+
+
+ const std::shared_ptr& EventEmitter() const { return m_eventEmitter; }
+ const winrt::com_ptr& Props() const { return m_props; }
+
+private:
+ winrt::com_ptr m_props;
+ std::shared_ptr m_eventEmitter;
+};
+
+template
+void RegisterActivityIndicatorViewNativeComponent(
+ winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder,
+ std::function builderCallback) noexcept {
+ packageBuilder.as().AddViewComponent(
+ L"ActivityIndicatorView", [builderCallback](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept {
+ auto compBuilder = builder.as();
+
+ builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props,
+ const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom) noexcept {
+ return winrt::make(props, cloneFrom);
+ });
+
+ builder.SetUpdatePropsHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::IComponentProps &newProps,
+ const winrt::Microsoft::ReactNative::IComponentProps &oldProps) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateProps(view, newProps ? newProps.as() : nullptr, oldProps ? oldProps.as() : nullptr);
+ });
+
+ compBuilder.SetUpdateLayoutMetricsHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &newLayoutMetrics,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &oldLayoutMetrics) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateLayoutMetrics(view, newLayoutMetrics, oldLayoutMetrics);
+ });
+
+ builder.SetUpdateEventEmitterHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateEventEmitter(std::make_shared(eventEmitter));
+ });
+
+ if constexpr (&TUserData::FinalizeUpdate != &BaseActivityIndicatorView::FinalizeUpdate) {
+ builder.SetFinalizeUpdateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ winrt::Microsoft::ReactNative::ComponentViewUpdateMask mask) noexcept {
+ auto userData = view.UserData().as();
+ userData->FinalizeUpdate(view, mask);
+ });
+ }
+
+ if constexpr (&TUserData::UpdateState != &BaseActivityIndicatorView::UpdateState) {
+ builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept {
+ auto userData = view.UserData().as();
+ userData->member(view, newState);
+ });
+ }
+
+ if constexpr (&TUserData::MountChildComponentView != &BaseActivityIndicatorView::MountChildComponentView) {
+ builder.SetMountChildComponentViewHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &args) noexcept {
+ auto userData = view.UserData().as();
+ return userData->MountChildComponentView(view, args);
+ });
+ }
+
+ if constexpr (&TUserData::UnmountChildComponentView != &BaseActivityIndicatorView::UnmountChildComponentView) {
+ builder.SetUnmountChildComponentViewHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &args) noexcept {
+ auto userData = view.UserData().as();
+ return userData->UnmountChildComponentView(view, args);
+ });
+ }
+
+ compBuilder.SetViewComponentViewInitializer([](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ auto userData = winrt::make_self();
+ if constexpr (&TUserData::Initialize != &BaseActivityIndicatorView::Initialize) {
+ userData->Initialize(view);
+ }
+ view.UserData(*userData);
+ });
+
+ if constexpr (&TUserData::CreateVisual != &BaseActivityIndicatorView::CreateVisual) {
+ compBuilder.SetCreateVisualHandler([](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ auto userData = view.UserData().as();
+ return userData->CreateVisual(view);
+ });
+ }
+
+ // Allow app to further customize the builder
+ if (builderCallback) {
+ builderCallback(compBuilder);
+ }
+ });
+}
+
+} // namespace Microsoft::ReactNativeSpecs
+
+#endif // #ifdef RNW_NEW_ARCH
diff --git a/vnext/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h b/vnext/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h
new file mode 100644
index 00000000000..5b57b09eb35
--- /dev/null
+++ b/vnext/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h
@@ -0,0 +1,295 @@
+
+/*
+ * This file is auto-generated from AndroidDrawerLayoutNativeComponent spec file in flow / TypeScript.
+ */
+// clang-format off
+#pragma once
+
+#include
+
+#ifdef RNW_NEW_ARCH
+#include
+
+#include
+#include
+#endif // #ifdef RNW_NEW_ARCH
+
+#ifdef RNW_NEW_ARCH
+
+namespace Microsoft::ReactNativeSpecs {
+
+REACT_STRUCT(AndroidDrawerLayoutProps)
+struct AndroidDrawerLayoutProps : winrt::implements {
+ AndroidDrawerLayoutProps(winrt::Microsoft::ReactNative::ViewProps props, const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom)
+ : ViewProps(props)
+ {
+ if (cloneFrom) {
+ auto cloneFromProps = cloneFrom.as();
+ keyboardDismissMode = cloneFromProps->keyboardDismissMode;
+ drawerBackgroundColor = cloneFromProps->drawerBackgroundColor;
+ drawerPosition = cloneFromProps->drawerPosition;
+ drawerWidth = cloneFromProps->drawerWidth;
+ drawerLockMode = cloneFromProps->drawerLockMode;
+ statusBarBackgroundColor = cloneFromProps->statusBarBackgroundColor;
+ }
+ }
+
+ void SetProp(uint32_t hash, winrt::hstring propName, winrt::Microsoft::ReactNative::IJSValueReader value) noexcept {
+ winrt::Microsoft::ReactNative::ReadProp(hash, propName, value, *this);
+ }
+
+ REACT_FIELD(keyboardDismissMode)
+ std::optional keyboardDismissMode;
+
+ REACT_FIELD(drawerBackgroundColor)
+ winrt::Microsoft::ReactNative::Color drawerBackgroundColor{nullptr};
+
+ REACT_FIELD(drawerPosition)
+ std::optional drawerPosition;
+
+ REACT_FIELD(drawerWidth)
+ std::optional drawerWidth{};
+
+ REACT_FIELD(drawerLockMode)
+ std::optional drawerLockMode;
+
+ REACT_FIELD(statusBarBackgroundColor)
+ winrt::Microsoft::ReactNative::Color statusBarBackgroundColor{nullptr};
+
+ const winrt::Microsoft::ReactNative::ViewProps ViewProps;
+};
+
+REACT_STRUCT(AndroidDrawerLayout_OnDrawerSlide)
+struct AndroidDrawerLayout_OnDrawerSlide {
+ REACT_FIELD(offset)
+ float offset{};
+};
+
+REACT_STRUCT(AndroidDrawerLayout_OnDrawerStateChanged)
+struct AndroidDrawerLayout_OnDrawerStateChanged {
+ REACT_FIELD(drawerState)
+ int32_t drawerState{};
+};
+
+REACT_STRUCT(AndroidDrawerLayout_OnDrawerOpen)
+struct AndroidDrawerLayout_OnDrawerOpen {
+};
+
+REACT_STRUCT(AndroidDrawerLayout_OnDrawerClose)
+struct AndroidDrawerLayout_OnDrawerClose {
+};
+
+struct AndroidDrawerLayoutEventEmitter {
+ AndroidDrawerLayoutEventEmitter(const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter)
+ : m_eventEmitter(eventEmitter) {}
+
+ using OnDrawerSlide = AndroidDrawerLayout_OnDrawerSlide;
+ using OnDrawerStateChanged = AndroidDrawerLayout_OnDrawerStateChanged;
+ using OnDrawerOpen = AndroidDrawerLayout_OnDrawerOpen;
+ using OnDrawerClose = AndroidDrawerLayout_OnDrawerClose;
+
+ void onDrawerSlide(OnDrawerSlide &value) const {
+ m_eventEmitter.DispatchEvent(L"drawerSlide", [value](const winrt::Microsoft::ReactNative::IJSValueWriter writer) {
+ winrt::Microsoft::ReactNative::WriteValue(writer, value);
+ });
+ }
+
+ void onDrawerStateChanged(OnDrawerStateChanged &value) const {
+ m_eventEmitter.DispatchEvent(L"drawerStateChanged", [value](const winrt::Microsoft::ReactNative::IJSValueWriter writer) {
+ winrt::Microsoft::ReactNative::WriteValue(writer, value);
+ });
+ }
+
+ void onDrawerOpen(OnDrawerOpen &value) const {
+ m_eventEmitter.DispatchEvent(L"drawerOpen", [value](const winrt::Microsoft::ReactNative::IJSValueWriter writer) {
+ winrt::Microsoft::ReactNative::WriteValue(writer, value);
+ });
+ }
+
+ void onDrawerClose(OnDrawerClose &value) const {
+ m_eventEmitter.DispatchEvent(L"drawerClose", [value](const winrt::Microsoft::ReactNative::IJSValueWriter writer) {
+ winrt::Microsoft::ReactNative::WriteValue(writer, value);
+ });
+ }
+
+ private:
+ winrt::Microsoft::ReactNative::EventEmitter m_eventEmitter{nullptr};
+};
+
+template
+struct BaseAndroidDrawerLayout {
+
+ virtual void UpdateProps(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::com_ptr &newProps,
+ const winrt::com_ptr &/*oldProps*/) noexcept {
+ m_props = newProps;
+ }
+
+ // UpdateLayoutMetrics will only be called if this method is overridden
+ virtual void UpdateLayoutMetrics(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &/*newLayoutMetrics*/,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &/*oldLayoutMetrics*/) noexcept {
+ }
+
+ // UpdateState will only be called if this method is overridden
+ virtual void UpdateState(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::IComponentState &/*newState*/) noexcept {
+ }
+
+ virtual void UpdateEventEmitter(const std::shared_ptr &eventEmitter) noexcept {
+ m_eventEmitter = eventEmitter;
+ }
+
+ // MountChildComponentView will only be called if this method is overridden
+ virtual void MountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &/*args*/) noexcept {
+ }
+
+ // UnmountChildComponentView will only be called if this method is overridden
+ virtual void UnmountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &/*args*/) noexcept {
+ }
+
+ // Initialize will only be called if this method is overridden
+ virtual void Initialize(const winrt::Microsoft::ReactNative::ComponentView &/*view*/) noexcept {
+ }
+
+ // CreateVisual will only be called if this method is overridden
+ virtual winrt::Microsoft::UI::Composition::Visual CreateVisual(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ return view.as().Compositor().CreateSpriteVisual();
+ }
+
+ // FinalizeUpdate will only be called if this method is overridden
+ virtual void FinalizeUpdate(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ winrt::Microsoft::ReactNative::ComponentViewUpdateMask /*mask*/) noexcept {
+ }
+
+ // You must provide an implementation of this method to handle the "openDrawer" command
+ virtual void HandleOpenDrawerCommand() noexcept = 0;
+
+ // You must provide an implementation of this method to handle the "closeDrawer" command
+ virtual void HandleCloseDrawerCommand() noexcept = 0;
+
+ void HandleCommand(const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::HandleCommandArgs& args) noexcept {
+ auto userData = view.UserData().as();
+ auto commandName = args.CommandName();
+ if (commandName == L"openDrawer") {
+
+ userData->HandleOpenDrawerCommand();
+ return;
+ }
+
+ if (commandName == L"closeDrawer") {
+
+ userData->HandleCloseDrawerCommand();
+ return;
+ }
+ }
+
+ const std::shared_ptr& EventEmitter() const { return m_eventEmitter; }
+ const winrt::com_ptr& Props() const { return m_props; }
+
+private:
+ winrt::com_ptr m_props;
+ std::shared_ptr m_eventEmitter;
+};
+
+template
+void RegisterAndroidDrawerLayoutNativeComponent(
+ winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder,
+ std::function builderCallback) noexcept {
+ packageBuilder.as().AddViewComponent(
+ L"AndroidDrawerLayout", [builderCallback](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept {
+ auto compBuilder = builder.as();
+
+ builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props,
+ const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom) noexcept {
+ return winrt::make(props, cloneFrom);
+ });
+
+ builder.SetUpdatePropsHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::IComponentProps &newProps,
+ const winrt::Microsoft::ReactNative::IComponentProps &oldProps) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateProps(view, newProps ? newProps.as() : nullptr, oldProps ? oldProps.as() : nullptr);
+ });
+
+ compBuilder.SetUpdateLayoutMetricsHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &newLayoutMetrics,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &oldLayoutMetrics) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateLayoutMetrics(view, newLayoutMetrics, oldLayoutMetrics);
+ });
+
+ builder.SetUpdateEventEmitterHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter) noexcept {
+ auto userData = view.UserData().as();
+ userData->UpdateEventEmitter(std::make_shared(eventEmitter));
+ });
+
+ if constexpr (&TUserData::FinalizeUpdate != &BaseAndroidDrawerLayout::FinalizeUpdate) {
+ builder.SetFinalizeUpdateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ winrt::Microsoft::ReactNative::ComponentViewUpdateMask mask) noexcept {
+ auto userData = view.UserData().as();
+ userData->FinalizeUpdate(view, mask);
+ });
+ }
+
+ if constexpr (&TUserData::UpdateState != &BaseAndroidDrawerLayout::UpdateState) {
+ builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept {
+ auto userData = view.UserData().as();
+ userData->member(view, newState);
+ });
+ }
+
+ builder.SetCustomCommandHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::HandleCommandArgs& args) noexcept {
+ auto userData = view.UserData().as();
+ userData->HandleCommand(view, args);
+ });
+
+ if constexpr (&TUserData::MountChildComponentView != &BaseAndroidDrawerLayout::MountChildComponentView) {
+ builder.SetMountChildComponentViewHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &args) noexcept {
+ auto userData = view.UserData().as();
+ return userData->MountChildComponentView(view, args);
+ });
+ }
+
+ if constexpr (&TUserData::UnmountChildComponentView != &BaseAndroidDrawerLayout::UnmountChildComponentView) {
+ builder.SetUnmountChildComponentViewHandler([](const winrt::Microsoft::ReactNative::ComponentView &view,
+ const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &args) noexcept {
+ auto userData = view.UserData().as();
+ return userData->UnmountChildComponentView(view, args);
+ });
+ }
+
+ compBuilder.SetViewComponentViewInitializer([](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ auto userData = winrt::make_self();
+ if constexpr (&TUserData::Initialize != &BaseAndroidDrawerLayout::Initialize) {
+ userData->Initialize(view);
+ }
+ view.UserData(*userData);
+ });
+
+ if constexpr (&TUserData::CreateVisual != &BaseAndroidDrawerLayout::CreateVisual) {
+ compBuilder.SetCreateVisualHandler([](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ auto userData = view.UserData().as();
+ return userData->CreateVisual(view);
+ });
+ }
+
+ // Allow app to further customize the builder
+ if (builderCallback) {
+ builderCallback(compBuilder);
+ }
+ });
+}
+
+} // namespace Microsoft::ReactNativeSpecs
+
+#endif // #ifdef RNW_NEW_ARCH
diff --git a/vnext/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h b/vnext/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h
new file mode 100644
index 00000000000..a42ace8fd41
--- /dev/null
+++ b/vnext/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h
@@ -0,0 +1,200 @@
+
+/*
+ * This file is auto-generated from AndroidHorizontalScrollContentViewNativeComponent spec file in flow / TypeScript.
+ */
+// clang-format off
+#pragma once
+
+#include
+
+#ifdef RNW_NEW_ARCH
+#include
+
+#include
+#include
+#endif // #ifdef RNW_NEW_ARCH
+
+#ifdef RNW_NEW_ARCH
+
+namespace Microsoft::ReactNativeSpecs {
+
+REACT_STRUCT(AndroidHorizontalScrollContentViewProps)
+struct AndroidHorizontalScrollContentViewProps : winrt::implements {
+ AndroidHorizontalScrollContentViewProps(winrt::Microsoft::ReactNative::ViewProps props, const winrt::Microsoft::ReactNative::IComponentProps& cloneFrom)
+ : ViewProps(props)
+ {
+ if (cloneFrom) {
+ auto cloneFromProps = cloneFrom.as();
+ removeClippedSubviews = cloneFromProps->removeClippedSubviews;
+ }
+ }
+
+ void SetProp(uint32_t hash, winrt::hstring propName, winrt::Microsoft::ReactNative::IJSValueReader value) noexcept {
+ winrt::Microsoft::ReactNative::ReadProp(hash, propName, value, *this);
+ }
+
+ REACT_FIELD(removeClippedSubviews)
+ std::optional removeClippedSubviews{};
+
+ const winrt::Microsoft::ReactNative::ViewProps ViewProps;
+};
+
+struct AndroidHorizontalScrollContentViewEventEmitter {
+ AndroidHorizontalScrollContentViewEventEmitter(const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter)
+ : m_eventEmitter(eventEmitter) {}
+
+ private:
+ winrt::Microsoft::ReactNative::EventEmitter m_eventEmitter{nullptr};
+};
+
+template
+struct BaseAndroidHorizontalScrollContentView {
+
+ virtual void UpdateProps(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::com_ptr &newProps,
+ const winrt::com_ptr &/*oldProps*/) noexcept {
+ m_props = newProps;
+ }
+
+ // UpdateLayoutMetrics will only be called if this method is overridden
+ virtual void UpdateLayoutMetrics(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &/*newLayoutMetrics*/,
+ const winrt::Microsoft::ReactNative::LayoutMetrics &/*oldLayoutMetrics*/) noexcept {
+ }
+
+ // UpdateState will only be called if this method is overridden
+ virtual void UpdateState(
+ const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::IComponentState &/*newState*/) noexcept {
+ }
+
+ virtual void UpdateEventEmitter(const std::shared_ptr &eventEmitter) noexcept {
+ m_eventEmitter = eventEmitter;
+ }
+
+ // MountChildComponentView will only be called if this method is overridden
+ virtual void MountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &/*args*/) noexcept {
+ }
+
+ // UnmountChildComponentView will only be called if this method is overridden
+ virtual void UnmountChildComponentView(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &/*args*/) noexcept {
+ }
+
+ // Initialize will only be called if this method is overridden
+ virtual void Initialize(const winrt::Microsoft::ReactNative::ComponentView &/*view*/) noexcept {
+ }
+
+ // CreateVisual will only be called if this method is overridden
+ virtual winrt::Microsoft::UI::Composition::Visual CreateVisual(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept {
+ return view.as().Compositor().CreateSpriteVisual();
+ }
+
+ // FinalizeUpdate will only be called if this method is overridden
+ virtual void FinalizeUpdate(const winrt::Microsoft::ReactNative::ComponentView &/*view*/,
+ winrt::Microsoft::ReactNative::ComponentViewUpdateMask /*mask*/) noexcept {
+ }
+
+
+
+ const std::shared_ptr& EventEmitter() const { return m_eventEmitter; }
+ const winrt::com_ptr