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..aa9bc7ccfd9 --- /dev/null +++ b/change/react-native-windows-55134846-d638-4b46-8190-55b23cf11c3d.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Rework custom resources API", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} 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/CompositionRootView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp index 7a5193a4f56..967b527328c 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp @@ -240,29 +240,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 +273,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,10 +387,8 @@ void CompositionRootView::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); + 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..ef3f71531ff 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h @@ -73,6 +73,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 +137,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 0f3f22521da..4eb504e1510 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp @@ -72,8 +72,8 @@ void CompositionRootView::Theme(const winrt::Microsoft::ReactNative::Composition winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader CompositionRootView::Resources() noexcept { return nullptr; } -void CompositionRootView::Resources(const winrt::Microsoft::ReactNative::Composition::ICustomResourceLoader &) noexcept {} - +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