From 7fd1f4439e2e1f109d4d2b20a97073f14bcbf504 Mon Sep 17 00:00:00 2001 From: Andrew Coates <30809111+acoates-ms@users.noreply.github.com> Date: Wed, 1 May 2024 09:38:01 -0700 Subject: [PATCH] [0.74] Fix reference cycle between CompositionRootView and CompositionEventHandler (#13171) * fix * Rework custom resources API (#13013) * Rework custom resources API * Change files * fix * Fix a reference cycle between CompositionRootView and CompositionEventHandler (#13163) * Fix a reference cycle between CompositionRootView and CompositionEventHandler * Change files * fix * Fix close when instance is not running * fix change files --- ...-55134846-d638-4b46-8190-55b23cf11c3d.json | 7 + ...-73af8adf-3550-45d3-baed-c5e49c135395.json | 7 + .../Playground-Composition.cpp | 24 +- .../CompositionRootView.idl | 6 +- .../Composition/CompositionEventHandler.cpp | 329 ++++++++++-------- .../Composition/CompositionEventHandler.h | 2 +- .../Composition/CompositionRootView.cpp | 75 ++-- .../Fabric/Composition/CompositionRootView.h | 6 +- .../CompositionRootView_emptyimpl.cpp | 7 + .../Fabric/Composition/Theme.cpp | 38 +- .../Fabric/Composition/Theme.h | 7 +- .../Fabric/Composition/Theme_emptyimpl.cpp | 8 +- vnext/Microsoft.ReactNative/Theme.idl | 3 +- 13 files changed, 318 insertions(+), 201 deletions(-) create mode 100644 change/react-native-windows-55134846-d638-4b46-8190-55b23cf11c3d.json create mode 100644 change/react-native-windows-73af8adf-3550-45d3-baed-c5e49c135395.json diff --git a/change/react-native-windows-55134846-d638-4b46-8190-55b23cf11c3d.json b/change/react-native-windows-55134846-d638-4b46-8190-55b23cf11c3d.json new file mode 100644 index 00000000000..aedbea742aa --- /dev/null +++ b/change/react-native-windows-55134846-d638-4b46-8190-55b23cf11c3d.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Rework custom resources API", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-73af8adf-3550-45d3-baed-c5e49c135395.json b/change/react-native-windows-73af8adf-3550-45d3-baed-c5e49c135395.json new file mode 100644 index 00000000000..00a75ee2f72 --- /dev/null +++ b/change/react-native-windows-73af8adf-3550-45d3-baed-c5e49c135395.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Fix a reference cycle between CompositionRootView and CompositionEventHandler", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/playground/windows/playground-composition/Playground-Composition.cpp b/packages/playground/windows/playground-composition/Playground-Composition.cpp index 524a3be40ec..5a28d256d91 100644 --- a/packages/playground/windows/playground-composition/Playground-Composition.cpp +++ b/packages/playground/windows/playground-composition/Playground-Composition.cpp @@ -460,9 +460,23 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) hwnd, LOWORD(wparam), reinterpret_cast(lparam), HIWORD(wparam)); } case WM_DESTROY: { + auto data = WindowData::GetFromWindow(hwnd); + // Before we shutdown the application - gracefully unload the ReactNativeHost instance + bool shouldPostQuitMessage = true; + if (data->m_host) { + shouldPostQuitMessage = false; + auto async = data->m_host.UnloadInstance(); + async.Completed([host = data->m_host](auto asyncInfo, winrt::Windows::Foundation::AsyncStatus asyncStatus) { + assert(asyncStatus == winrt::Windows::Foundation::AsyncStatus::Completed); + host.InstanceSettings().UIDispatcher().Post([]() { PostQuitMessage(0); }); + }); + } + delete WindowData::GetFromWindow(hwnd); SetProp(hwnd, WindowDataProperty, 0); - PostQuitMessage(0); + if (shouldPostQuitMessage) { + PostQuitMessage(0); + } return 0; } case WM_NCCREATE: { @@ -531,6 +545,14 @@ int RunPlayground(int showCmd, bool useWebDebugger) { g_liftedDispatcherQueueController.DispatcherQueue().RunEventLoop(); + // Rundown the DispatcherQueue. This drains the queue and raises events to let components + // know the message loop has finished. + g_liftedDispatcherQueueController.ShutdownQueue(); + + // Destroy all Composition objects + g_compositor.Close(); + g_compositor = nullptr; + return 0; } diff --git a/vnext/Microsoft.ReactNative/CompositionRootView.idl b/vnext/Microsoft.ReactNative/CompositionRootView.idl index dfcc6cfb81e..9090942282a 100644 --- a/vnext/Microsoft.ReactNative/CompositionRootView.idl +++ b/vnext/Microsoft.ReactNative/CompositionRootView.idl @@ -95,8 +95,10 @@ namespace Microsoft.ReactNative Object GetUiaProvider(); - DOC_STRING("Theme used for Platform colors within this RootView") - Microsoft.ReactNative.Composition.Theme Theme; + DOC_STRING("Provides resources used for Platform colors within this RootView") + Microsoft.ReactNative.Composition.ICustomResourceLoader Resources; + + Microsoft.ReactNative.Composition.Theme Theme { get; }; #ifdef USE_WINUI3 Microsoft.UI.Content.ContentIsland Island { get; }; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp index 1e532a7fae6..30d0d1c87c4 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp @@ -152,67 +152,78 @@ struct CompositionInputKeyboardSource : winrt::implements< CompositionEventHandler::CompositionEventHandler( const winrt::Microsoft::ReactNative::ReactContext &context, const winrt::Microsoft::ReactNative::CompositionRootView &CompositionRootView) - : m_context(context) { - m_compRootView = CompositionRootView; - + : m_context(context), m_wkRootView(CompositionRootView) { #ifdef USE_WINUI3 - if (auto island = m_compRootView.Island()) { + if (auto island = CompositionRootView.Island()) { auto pointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(island); m_pointerPressedToken = pointerSource.PointerPressed([this]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (SurfaceId() == -1) - return; + if (auto strongRootView = m_wkRootView.get()) { + if (SurfaceId() == -1) + return; - auto pp = winrt::make( - args.CurrentPoint(), m_compRootView.ScaleFactor()); - onPointerPressed(pp, args.KeyModifiers()); + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + onPointerPressed(pp, args.KeyModifiers()); + } }); m_pointerReleasedToken = pointerSource.PointerReleased([this]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (SurfaceId() == -1) - return; + if (auto strongRootView = m_wkRootView.get()) { + if (SurfaceId() == -1) + return; - auto pp = winrt::make( - args.CurrentPoint(), m_compRootView.ScaleFactor()); - onPointerReleased(pp, args.KeyModifiers()); + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + onPointerReleased(pp, args.KeyModifiers()); + } }); m_pointerMovedToken = pointerSource.PointerMoved([this]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - auto pp = winrt::make( - args.CurrentPoint(), m_compRootView.ScaleFactor()); - onPointerMoved(pp, args.KeyModifiers()); + if (auto strongRootView = m_wkRootView.get()) { + if (SurfaceId() == -1) + return; + + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + onPointerMoved(pp, args.KeyModifiers()); + } }); m_pointerCaptureLostToken = pointerSource.PointerCaptureLost([this]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (SurfaceId() == -1) - return; + if (auto strongRootView = m_wkRootView.get()) { + if (SurfaceId() == -1) + return; - auto pp = winrt::make( - args.CurrentPoint(), m_compRootView.ScaleFactor()); - onPointerCaptureLost(pp, args.KeyModifiers()); + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + onPointerCaptureLost(pp, args.KeyModifiers()); + } }); m_pointerWheelChangedToken = pointerSource.PointerWheelChanged([this]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (SurfaceId() == -1) - return; + if (auto strongRootView = m_wkRootView.get()) { + if (SurfaceId() == -1) + return; - auto pp = winrt::make( - args.CurrentPoint(), m_compRootView.ScaleFactor()); - onPointerWheelChanged(pp, args.KeyModifiers()); + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + onPointerWheelChanged(pp, args.KeyModifiers()); + } }); auto keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(island); @@ -220,61 +231,71 @@ CompositionEventHandler::CompositionEventHandler( m_keyDownToken = keyboardSource.KeyDown([this]( winrt::Microsoft::UI::Input::InputKeyboardSource const &source, winrt::Microsoft::UI::Input::KeyEventArgs const &args) { - if (SurfaceId() == -1) - return; - - auto focusedComponent = RootComponentView().GetFocusedComponent(); - auto keyArgs = winrt::make( - focusedComponent - ? focusedComponent.Tag() - : static_cast( - winrt::get_self(m_compRootView) - ->GetTag()), - args); - auto keyboardSource = winrt::make(source); - onKeyDown(keyboardSource, keyArgs); - winrt::get_self(keyboardSource)->Disconnect(); + if (auto strongRootView = m_wkRootView.get()) { + if (SurfaceId() == -1) + return; + + auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto keyArgs = + winrt::make( + focusedComponent + ? focusedComponent.Tag() + : static_cast( + winrt::get_self( + strongRootView) + ->GetTag()), + args); + auto keyboardSource = winrt::make(source); + onKeyDown(keyboardSource, keyArgs); + winrt::get_self(keyboardSource)->Disconnect(); + } }); m_keyUpToken = keyboardSource.KeyUp([this]( winrt::Microsoft::UI::Input::InputKeyboardSource const &source, winrt::Microsoft::UI::Input::KeyEventArgs const &args) { - if (SurfaceId() == -1) - return; - - auto focusedComponent = RootComponentView().GetFocusedComponent(); - auto keyArgs = winrt::make( - focusedComponent - ? focusedComponent.Tag() - : static_cast( - winrt::get_self(m_compRootView) - ->GetTag()), - args); - auto keyboardSource = winrt::make(source); - onKeyUp(keyboardSource, keyArgs); - winrt::get_self(keyboardSource)->Disconnect(); + if (auto strongRootView = m_wkRootView.get()) { + if (SurfaceId() == -1) + return; + + auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto keyArgs = + winrt::make( + focusedComponent + ? focusedComponent.Tag() + : static_cast( + winrt::get_self( + strongRootView) + ->GetTag()), + args); + auto keyboardSource = winrt::make(source); + onKeyUp(keyboardSource, keyArgs); + winrt::get_self(keyboardSource)->Disconnect(); + } }); m_characterReceivedToken = keyboardSource.CharacterReceived([this]( winrt::Microsoft::UI::Input::InputKeyboardSource const &source, winrt::Microsoft::UI::Input::CharacterReceivedEventArgs const &args) { - if (SurfaceId() == -1) - return; - - auto focusedComponent = RootComponentView().GetFocusedComponent(); - auto charArgs = winrt::make< - winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>( - focusedComponent - ? focusedComponent.Tag() - : static_cast( - winrt::get_self( - m_compRootView) - ->GetTag()), - args); - auto keyboardSource = winrt::make(source); - onCharacterReceived(keyboardSource, charArgs); - winrt::get_self(keyboardSource)->Disconnect(); + if (auto strongRootView = m_wkRootView.get()) { + if (SurfaceId() == -1) + return; + + auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto charArgs = winrt::make< + winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>( + focusedComponent + ? focusedComponent.Tag() + : static_cast( + winrt::get_self( + strongRootView) + ->GetTag()), + args); + auto keyboardSource = winrt::make(source); + onCharacterReceived(keyboardSource, charArgs); + winrt::get_self(keyboardSource)->Disconnect(); + } }); } #endif @@ -282,24 +303,29 @@ CompositionEventHandler::CompositionEventHandler( CompositionEventHandler::~CompositionEventHandler() { #ifdef USE_WINUI3 - if (auto island = m_compRootView.Island()) { - auto pointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(island); - pointerSource.PointerPressed(m_pointerPressedToken); - pointerSource.PointerReleased(m_pointerReleasedToken); - pointerSource.PointerMoved(m_pointerMovedToken); - pointerSource.PointerCaptureLost(m_pointerCaptureLostToken); - pointerSource.PointerWheelChanged(m_pointerWheelChangedToken); - auto keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(island); - keyboardSource.KeyDown(m_keyDownToken); - keyboardSource.KeyUp(m_keyUpToken); - keyboardSource.CharacterReceived(m_characterReceivedToken); + if (auto strongRootView = m_wkRootView.get()) { + if (auto island = strongRootView.Island()) { + auto pointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(island); + pointerSource.PointerPressed(m_pointerPressedToken); + pointerSource.PointerReleased(m_pointerReleasedToken); + pointerSource.PointerMoved(m_pointerMovedToken); + pointerSource.PointerCaptureLost(m_pointerCaptureLostToken); + pointerSource.PointerWheelChanged(m_pointerWheelChangedToken); + auto keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(island); + keyboardSource.KeyDown(m_keyDownToken); + keyboardSource.KeyUp(m_keyUpToken); + keyboardSource.CharacterReceived(m_characterReceivedToken); + } } #endif } facebook::react::SurfaceId CompositionEventHandler::SurfaceId() const noexcept { - return static_cast( - winrt::get_self(m_compRootView)->GetTag()); + if (auto strongRootView = m_wkRootView.get()) { + return static_cast( + winrt::get_self(strongRootView)->GetTag()); + } + return -1; } winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView & @@ -369,86 +395,104 @@ winrt::Windows::System::VirtualKeyModifiers GetKeyModifiers(uint64_t wParam) { int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t wParam, int64_t lParam) noexcept { switch (msg) { case WM_LBUTTONDOWN: { - auto pp = winrt::make( - hwnd, msg, wParam, lParam, m_compRootView.ScaleFactor()); - onPointerPressed(pp, GetKeyModifiers(wParam)); + if (auto strongRootView = m_wkRootView.get()) { + auto pp = winrt::make( + hwnd, msg, wParam, lParam, strongRootView.ScaleFactor()); + onPointerPressed(pp, GetKeyModifiers(wParam)); + } return 0; } case WM_POINTERDOWN: { - auto pp = winrt::make( - hwnd, msg, wParam, lParam, m_compRootView.ScaleFactor()); - onPointerPressed(pp, GetKeyModifiers(wParam)); + if (auto strongRootView = m_wkRootView.get()) { + auto pp = winrt::make( + hwnd, msg, wParam, lParam, strongRootView.ScaleFactor()); + onPointerPressed(pp, GetKeyModifiers(wParam)); + } return 0; } case WM_LBUTTONUP: { - auto pp = winrt::make( - hwnd, msg, wParam, lParam, m_compRootView.ScaleFactor()); - onPointerReleased(pp, GetKeyModifiers(wParam)); + if (auto strongRootView = m_wkRootView.get()) { + auto pp = winrt::make( + hwnd, msg, wParam, lParam, strongRootView.ScaleFactor()); + onPointerReleased(pp, GetKeyModifiers(wParam)); + } return 0; } case WM_POINTERUP: { - auto pp = winrt::make( - hwnd, msg, wParam, lParam, m_compRootView.ScaleFactor()); - onPointerReleased(pp, GetKeyModifiers(wParam)); + if (auto strongRootView = m_wkRootView.get()) { + auto pp = winrt::make( + hwnd, msg, wParam, lParam, strongRootView.ScaleFactor()); + onPointerReleased(pp, GetKeyModifiers(wParam)); + } return 0; } case WM_MOUSEMOVE: { - auto pp = winrt::make( - hwnd, msg, wParam, lParam, m_compRootView.ScaleFactor()); - onPointerMoved(pp, GetKeyModifiers(wParam)); + if (auto strongRootView = m_wkRootView.get()) { + auto pp = winrt::make( + hwnd, msg, wParam, lParam, strongRootView.ScaleFactor()); + onPointerMoved(pp, GetKeyModifiers(wParam)); + } return 0; } case WM_CAPTURECHANGED: { - auto pp = winrt::make( - hwnd, msg, wParam, lParam, m_compRootView.ScaleFactor()); - onPointerCaptureLost(pp, winrt::Windows::System::VirtualKeyModifiers::None); + if (auto strongRootView = m_wkRootView.get()) { + auto pp = winrt::make( + hwnd, msg, wParam, lParam, strongRootView.ScaleFactor()); + onPointerCaptureLost(pp, winrt::Windows::System::VirtualKeyModifiers::None); + } return 0; } case WM_MOUSEWHEEL: { - auto pp = winrt::make( - hwnd, msg, wParam, lParam, m_compRootView.ScaleFactor()); - onPointerWheelChanged(pp, GetKeyModifiers(wParam)); + if (auto strongRootView = m_wkRootView.get()) { + auto pp = winrt::make( + hwnd, msg, wParam, lParam, strongRootView.ScaleFactor()); + onPointerWheelChanged(pp, GetKeyModifiers(wParam)); + } break; } case WM_CHAR: case WM_SYSCHAR: { - auto focusedComponent = RootComponentView().GetFocusedComponent(); - auto args = winrt::make< - winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>( - focusedComponent - ? focusedComponent.Tag() - : static_cast( - winrt::get_self(m_compRootView) - ->GetTag()), - msg, - wParam, - lParam); - auto keyboardSource = winrt::make(this); - onCharacterReceived(keyboardSource, args); - winrt::get_self(keyboardSource)->Disconnect(); + if (auto strongRootView = m_wkRootView.get()) { + auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto args = winrt::make< + winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>( + focusedComponent ? focusedComponent.Tag() + : static_cast( + winrt::get_self( + strongRootView) + ->GetTag()), + msg, + wParam, + lParam); + auto keyboardSource = winrt::make(this); + onCharacterReceived(keyboardSource, args); + winrt::get_self(keyboardSource)->Disconnect(); + } break; } case WM_KEYDOWN: case WM_KEYUP: case WM_SYSKEYDOWN: case WM_SYSKEYUP: { - auto focusedComponent = RootComponentView().GetFocusedComponent(); - auto args = winrt::make( - focusedComponent - ? focusedComponent.Tag() - : static_cast( - winrt::get_self(m_compRootView) - ->GetTag()), - msg, - wParam, - lParam); - auto keyboardSource = winrt::make(this); - if (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) { - onKeyDown(keyboardSource, args); - } else { - onKeyUp(keyboardSource, args); + if (auto strongRootView = m_wkRootView.get()) { + auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto args = winrt::make( + focusedComponent ? focusedComponent.Tag() + : static_cast( + winrt::get_self( + strongRootView) + ->GetTag()), + msg, + wParam, + lParam); + auto keyboardSource = winrt::make(this); + if (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) { + onKeyDown(keyboardSource, args); + } else { + onKeyUp(keyboardSource, args); + } + winrt::get_self(keyboardSource)->Disconnect(); } - winrt::get_self(keyboardSource)->Disconnect(); break; } } @@ -571,9 +615,9 @@ void CompositionEventHandler::HandleIncomingPointerEvent( // We only want to emit events to JS if there is a view that is currently listening to said event // so we only send those event to the JS side if the element which has been entered is itself listening, - // or if one of its parents is listening in case those listeners care about the capturing phase. Adding the ability - // for native to distinguish between capturing listeners and not could be an optimization to further reduce the - // number of events we send to JS + // or if one of its parents is listening in case those listeners care about the capturing phase. Adding the + // ability for native to distinguish between capturing listeners and not could be an optimization to further + // reduce the number of events we send to JS bool hasParentEnterListener = false; bool emittedNativeEnteredEvent = false; @@ -673,7 +717,8 @@ void CompositionEventHandler::HandleIncomingPointerEvent( } for (auto itComponentView = viewsToEmitJSLeaveEventsTo.rbegin(); itComponentView != viewsToEmitJSLeaveEventsTo.rend(); - itComponentView++) { // for (UIView *componentView in [viewsToEmitJSLeaveEventsTo reverseObjectEnumerator]) { + itComponentView++) { // for (UIView *componentView in [viewsToEmitJSLeaveEventsTo + // reverseObjectEnumerator]) { auto componentView = *itComponentView; const auto eventEmitter = @@ -769,8 +814,10 @@ void CompositionEventHandler::getTargetPointerArgs( auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view; auto clientRect = winrt::get_self(targetComponentView) ->getClientRect(); - ptLocal.x = ptScaled.x - (clientRect.left / m_compRootView.ScaleFactor()); - ptLocal.y = ptScaled.y - (clientRect.top / m_compRootView.ScaleFactor()); + if (auto strongRootView = m_wkRootView.get()) { + ptLocal.x = ptScaled.x - (clientRect.left / strongRootView.ScaleFactor()); + ptLocal.y = ptScaled.y - (clientRect.top / strongRootView.ScaleFactor()); + } } else { tag = RootComponentView().hitTest(ptScaled, ptLocal); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h index 0268c69e7ba..87ecfff1f9c 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h @@ -154,7 +154,7 @@ class CompositionEventHandler { PointerId m_touchId = 0; std::map> m_currentlyHoveredViewsPerPointer; - winrt::Microsoft::ReactNative::CompositionRootView m_compRootView{nullptr}; + winrt::weak_ref m_wkRootView; winrt::Microsoft::ReactNative::ReactContext m_context; facebook::react::Tag m_pointerCapturingComponentTag{-1}; // Component that has captured input diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp index 157349cff93..f99876db0cc 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp @@ -131,6 +131,13 @@ CompositionRootView::CompositionRootView(const winrt::Microsoft::UI::Composition : m_compositor(compositor) {} #endif +CompositionRootView::~CompositionRootView() noexcept { + if (m_uiDispatcher) { + assert(m_uiDispatcher.HasThreadAccess()); + UninitRootView(); + } +} + ReactNative::IReactViewHost CompositionRootView::ReactViewHost() noexcept { return m_reactViewHost; } @@ -240,29 +247,32 @@ void CompositionRootView::ScaleFactor(float value) noexcept { } } +winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader CompositionRootView::Resources() noexcept { + return m_resources; +} + +void CompositionRootView::Resources( + const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &resources) noexcept { + m_resources = resources; + + if (m_context && m_theme) { + Theme(winrt::make(m_context, m_resources)); + } +} + winrt::Microsoft::ReactNative::Composition::Theme CompositionRootView::Theme() noexcept { if (!m_theme) { - Theme(winrt::Microsoft::ReactNative::Composition::Theme::GetDefaultTheme(m_context.Handle())); - m_themeChangedSubscription = m_context.Notifications().Subscribe( - winrt::Microsoft::ReactNative::ReactNotificationId( - winrt::Microsoft::ReactNative::Composition::Theme::ThemeChangedEventName()), - m_context.UIDispatcher(), - [wkThis = get_weak()]( - IInspectable const & /*sender*/, - winrt::Microsoft::ReactNative::ReactNotificationArgs const & /*args*/) { - auto pThis = wkThis.get(); - pThis->Theme(winrt::Microsoft::ReactNative::Composition::Theme::GetDefaultTheme(pThis->m_context.Handle())); - }); + assert(m_context); + if (m_resources) { + Theme(winrt::make(m_context, m_resources)); + } else { + Theme(winrt::Microsoft::ReactNative::Composition::Theme::GetDefaultTheme(m_context.Handle())); + } } return m_theme; } void CompositionRootView::Theme(const winrt::Microsoft::ReactNative::Composition::Theme &value) noexcept { - if (m_themeChangedSubscription) { - m_themeChangedSubscription.Unsubscribe(); - m_themeChangedSubscription = nullptr; - } - if (value == m_theme) return; @@ -270,20 +280,22 @@ void CompositionRootView::Theme(const winrt::Microsoft::ReactNative::Composition m_themeChangedRevoker = m_theme.ThemeChanged( winrt::auto_revoke, - [this]( + [wkThis = get_weak()]( const winrt::Windows::Foundation::IInspectable & /*sender*/, const winrt::Windows::Foundation::IInspectable & /*args*/) { - if (auto rootView = GetComponentView()) { - Mso::Functor fn = - [](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept { - winrt::get_self(view)->onThemeChanged(); - return false; - }; - - winrt::Microsoft::ReactNative::ComponentView view{nullptr}; - winrt::check_hresult(rootView->QueryInterface( - winrt::guid_of(), winrt::put_abi(view))); - walkTree(view, true, fn); + if (auto strongThis = wkThis.get()) { + if (auto rootView = strongThis->GetComponentView()) { + Mso::Functor fn = + [](const winrt::Microsoft::ReactNative::ComponentView &view) noexcept { + winrt::get_self(view)->onThemeChanged(); + return false; + }; + + winrt::Microsoft::ReactNative::ComponentView view{nullptr}; + winrt::check_hresult(rootView->QueryInterface( + winrt::guid_of(), winrt::put_abi(view))); + walkTree(view, true, fn); + } } }); @@ -382,13 +394,8 @@ void CompositionRootView::InitRootView( } m_context = winrt::Microsoft::ReactNative::ReactContext(std::move(context)); - - winrt::Microsoft::ReactNative::CompositionRootView compositionRootView; - get_strong().as(compositionRootView); - m_reactViewOptions = std::move(viewOptions); - m_CompositionEventHandler = - std::make_shared<::Microsoft::ReactNative::CompositionEventHandler>(m_context, compositionRootView); + m_CompositionEventHandler = std::make_shared<::Microsoft::ReactNative::CompositionEventHandler>(m_context, *this); UpdateRootViewInternal(); diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h index b18472b8332..f04e2e066ff 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h @@ -45,6 +45,7 @@ struct CompositionRootView : CompositionRootViewT, ::Microsoft::ReactNative::ICompositionRootView { CompositionRootView() noexcept; + ~CompositionRootView() noexcept; #ifdef USE_WINUI3 CompositionRootView(const winrt::Microsoft::UI::Composition::Compositor &compositor) noexcept; @@ -73,6 +74,9 @@ struct CompositionRootView void RemoveRenderedVisual(const winrt::Microsoft::ReactNative::Composition::Experimental::IVisual &visual) noexcept; bool TrySetFocus() noexcept; + winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader Resources() noexcept; + void Resources(const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &resources) noexcept; + winrt::Microsoft::ReactNative::Composition::Theme Theme() noexcept; void Theme(const winrt::Microsoft::ReactNative::Composition::Theme &value) noexcept; @@ -134,8 +138,8 @@ struct CompositionRootView winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_rootVisual{nullptr}; winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual m_loadingVisual{nullptr}; winrt::Microsoft::ReactNative::Composition::Experimental::IActivityVisual m_loadingActivityVisual{nullptr}; + winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader m_resources{nullptr}; winrt::Microsoft::ReactNative::Composition::Theme m_theme{nullptr}; - winrt::Microsoft::ReactNative::ReactNotificationSubscription m_themeChangedSubscription{nullptr}; winrt::Microsoft::ReactNative::Composition::Theme::ThemeChanged_revoker m_themeChangedRevoker; void UpdateRootViewInternal() noexcept; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp index f6845d8b560..69453b8ca5a 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp @@ -28,6 +28,7 @@ struct CompositionReactViewInstance //=========================================================================== CompositionRootView::CompositionRootView() noexcept {} +CompositionRootView::~CompositionRootView() noexcept {} CompositionRootView::CompositionRootView(const winrt::Microsoft::UI::Composition::Compositor &compositor) noexcept {} @@ -69,6 +70,12 @@ winrt::Microsoft::ReactNative::Composition::Theme CompositionRootView::Theme() n } void CompositionRootView::Theme(const winrt::Microsoft::ReactNative::Composition::Theme &) noexcept {} +winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader CompositionRootView::Resources() noexcept { + return nullptr; +} +void CompositionRootView::Resources( + const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &) noexcept {} + winrt::IInspectable CompositionRootView::GetUiaProvider() noexcept { return nullptr; } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.cpp index 3eb8b447408..eae79920a7b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.cpp @@ -526,6 +526,15 @@ static const winrt::Microsoft::ReactNative::ReactPropertyId + &ThemeResourcesPropertyId() noexcept { + static const winrt::Microsoft::ReactNative::ReactPropertyId< + winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader> + prop{L"ReactNative.Composition", L"ThemeResources"}; + return prop; +} + winrt::Microsoft::ReactNative::Composition::Theme Theme::EmptyTheme() noexcept { static winrt::Microsoft::ReactNative::Composition::Theme s_emptyTheme{nullptr}; if (!s_emptyTheme) { @@ -537,14 +546,28 @@ winrt::Microsoft::ReactNative::Composition::Theme Theme::EmptyTheme() noexcept { /*static*/ winrt::Microsoft::ReactNative::Composition::Theme Theme::GetDefaultTheme( const winrt::Microsoft::ReactNative::IReactContext &context) noexcept { return winrt::Microsoft::ReactNative::ReactPropertyBag(context.Properties()) - .GetOrCreate(ThemePropertyId(), [context]() { return winrt::make(context, nullptr); }); + .GetOrCreate(ThemePropertyId(), [context]() { + return winrt::make( + context, + winrt::Microsoft::ReactNative::ReactPropertyBag(context.Properties()).Get(ThemeResourcesPropertyId())); + }); } -/*static*/ void Theme::SetDefaultTheme( +/*static*/ void Theme::SetDefaultResources( const winrt::Microsoft::ReactNative::ReactInstanceSettings &settings, - const winrt::Microsoft::ReactNative::Composition::Theme &theme) noexcept { - winrt::Microsoft::ReactNative::ReactPropertyBag(settings.Properties()).Set(ThemePropertyId(), theme); - settings.Notifications().SendNotification(ThemeChangedEventName(), nullptr, nullptr); + const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &resources) noexcept { + winrt::Microsoft::ReactNative::ReactPropertyBag properties(settings.Properties()); + properties.Set(ThemeResourcesPropertyId(), resources); + // If a default theme has already been created - we need to update it with the new resources + if (auto theme = properties.Get(ThemePropertyId())) { + winrt::get_self(theme)->UpdateCustomResources(resources); + } +} + +void Theme::UpdateCustomResources( + const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &resources) noexcept { + m_customResourceLoader = resources; + ClearCacheAndRaiseChangedEvent(); } IReactPropertyNamespace ThemeNamespace() noexcept { @@ -552,9 +575,4 @@ IReactPropertyNamespace ThemeNamespace() noexcept { return value; } -/*static*/ IReactPropertyName Theme::ThemeChangedEventName() noexcept { - static IReactPropertyName propName = ReactPropertyBagHelper::GetName(ThemeNamespace(), L"Changed"); - return propName; -} - } // namespace winrt::Microsoft::ReactNative::Composition::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.h b/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.h index 21ebb74e121..707fde5d29a 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Theme.h @@ -50,12 +50,13 @@ struct Theme : ThemeT { static winrt::Microsoft::ReactNative::Composition::Theme GetDefaultTheme( const winrt::Microsoft::ReactNative::IReactContext &context) noexcept; - static void SetDefaultTheme( + static void SetDefaultResources( const winrt::Microsoft::ReactNative::ReactInstanceSettings &settings, - const winrt::Microsoft::ReactNative::Composition::Theme &theme) noexcept; - static winrt::Microsoft::ReactNative::IReactPropertyName ThemeChangedEventName() noexcept; + const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &resources) noexcept; private: + void UpdateCustomResources( + const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &resources) noexcept; bool TryGetPlatformColor(const std::string &platformColor, winrt::Windows::UI::Color &color) noexcept; void ClearCacheAndRaiseChangedEvent() noexcept; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Theme_emptyimpl.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Theme_emptyimpl.cpp index 328bdbe67e3..76946c7726e 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Theme_emptyimpl.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Theme_emptyimpl.cpp @@ -74,12 +74,8 @@ winrt::Microsoft::ReactNative::Composition::Theme Theme::EmptyTheme() noexcept { return nullptr; } -/*static*/ void Theme::SetDefaultTheme( +/*static*/ void Theme::SetDefaultResources( const winrt::Microsoft::ReactNative::ReactInstanceSettings &, - const winrt::Microsoft::ReactNative::Composition::Theme &) noexcept {} - -/*static*/ IReactPropertyName Theme::ThemeChangedEventName() noexcept { - return nullptr; -} + const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &) noexcept {} } // namespace winrt::Microsoft::ReactNative::Composition::implementation diff --git a/vnext/Microsoft.ReactNative/Theme.idl b/vnext/Microsoft.ReactNative/Theme.idl index 91d2bedd3a7..7dc4d62f981 100644 --- a/vnext/Microsoft.ReactNative/Theme.idl +++ b/vnext/Microsoft.ReactNative/Theme.idl @@ -61,8 +61,7 @@ namespace Microsoft.ReactNative.Composition event Windows.Foundation.EventHandler ThemeChanged; static Theme GetDefaultTheme(Microsoft.ReactNative.IReactContext context); - static void SetDefaultTheme(Microsoft.ReactNative.ReactInstanceSettings settings, Theme theme); - static Microsoft.ReactNative.IReactPropertyName ThemeChangedEventName { get; }; + static void SetDefaultResources(Microsoft.ReactNative.ReactInstanceSettings settings, ICustomResourceLoader theme); }; } // namespace Microsoft.ReactNative