diff --git a/change/@react-native-windows-codegen-e7b6a9a3-a6de-499d-95bb-1510e5009dd2.json b/change/@react-native-windows-codegen-e7b6a9a3-a6de-499d-95bb-1510e5009dd2.json new file mode 100644 index 00000000000..102cd43cc4e --- /dev/null +++ b/change/@react-native-windows-codegen-e7b6a9a3-a6de-499d-95bb-1510e5009dd2.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Fix UpdateState on generated base class", + "packageName": "@react-native-windows/codegen", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} \ No newline at end of file diff --git a/change/react-native-windows-56595f43-5a8f-443b-9594-7de92ea7e45c.json b/change/react-native-windows-56595f43-5a8f-443b-9594-7de92ea7e45c.json new file mode 100644 index 00000000000..fdf40e5858c --- /dev/null +++ b/change/react-native-windows-56595f43-5a8f-443b-9594-7de92ea7e45c.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Allow portals to have independent layout constraints and scale factor", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-6667ac90-8a6f-422f-917d-bba7f01145b6.json b/change/react-native-windows-6667ac90-8a6f-422f-917d-bba7f01145b6.json new file mode 100644 index 00000000000..fef206725d2 --- /dev/null +++ b/change/react-native-windows-6667ac90-8a6f-422f-917d-bba7f01145b6.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "add modal implementation with PopupWindowSiteBridge", + "packageName": "react-native-windows", + "email": "tatianakapos@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-96dd8a87-4b77-4f7e-bf40-fb214350e0f3.json b/change/react-native-windows-96dd8a87-4b77-4f7e-bf40-fb214350e0f3.json new file mode 100644 index 00000000000..c88eac345e6 --- /dev/null +++ b/change/react-native-windows-96dd8a87-4b77-4f7e-bf40-fb214350e0f3.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Basic support for stitching the UIA tree for a ContentIslandComponentView's child", + "packageName": "react-native-windows", + "email": "email not defined", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-9fae9acd-d5a6-48d1-9d63-c333ad09ca76.json b/change/react-native-windows-9fae9acd-d5a6-48d1-9d63-c333ad09ca76.json new file mode 100644 index 00000000000..6ee708c33cd --- /dev/null +++ b/change/react-native-windows-9fae9acd-d5a6-48d1-9d63-c333ad09ca76.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Fix crash when currently focused element gets marked as enableFocusRing=false", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-ca3eff01-d91f-4d53-80fe-5ce6cfdf0727.json b/change/react-native-windows-ca3eff01-d91f-4d53-80fe-5ce6cfdf0727.json new file mode 100644 index 00000000000..a564204ef91 --- /dev/null +++ b/change/react-native-windows-ca3eff01-d91f-4d53-80fe-5ce6cfdf0727.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Round Focus visuals by default, fix nudge rendering", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/@react-native-windows/codegen/src/generators/GenerateComponentWindows.ts b/packages/@react-native-windows/codegen/src/generators/GenerateComponentWindows.ts index 8a6c7544856..1bff2df1730 100644 --- a/packages/@react-native-windows/codegen/src/generators/GenerateComponentWindows.ts +++ b/packages/@react-native-windows/codegen/src/generators/GenerateComponentWindows.ts @@ -196,7 +196,7 @@ void Register::_COMPONENT_NAME_::NativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } 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 b851c8af979..feda144e8d0 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 @@ -293,7 +293,7 @@ const styles = StyleSheet.create({ marginTop: 6, }, modalContainer: { - flex: 1, + //flex: 1, // [Windows] - This will cause the modal to stretch to be as tall as the availiable space given to it. justifyContent: 'center', padding: 20, }, diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiPortalShadowNode.cpp b/vnext/Microsoft.ReactNative/Fabric/AbiPortalShadowNode.cpp new file mode 100644 index 00000000000..913d3d6f052 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/AbiPortalShadowNode.cpp @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "AbiPortalShadowNode.h" + +#include +#include +#include +#include +#include + +#include + +namespace Microsoft::ReactNative { + +extern const char AbiPortalComponentName[] = "AbiPortal"; + +facebook::react::Size AbiPortalShadowNode::measureContent( + const facebook::react::LayoutContext &layoutContext, + const facebook::react::LayoutConstraints &layoutConstraints) const { + return {0, 0}; // The portal placeholder node shouldn't take up any space +} + +void AbiPortalShadowNode::layout(facebook::react::LayoutContext layoutContext) { + ensureUnsealed(); + auto layoutMetrics = getLayoutMetrics(); + + auto portalOwningShadowNode = ShadowNode::Unshared{}; + + if (getChildren().empty()) { + return; + } + + // A Portal should only have a single child + react_native_assert(getChildren().size() == 1); + + const auto &childNode = getChildren()[0]; + + auto clonedShadowNode = ShadowNode::Unshared{}; + + portalOwningShadowNode = cloneTree(childNode->getFamily(), [&](const ShadowNode &oldShadowNode) { + clonedShadowNode = oldShadowNode.clone({}); + return clonedShadowNode; + }); + auto portalShadowNode = static_cast(portalOwningShadowNode.get()); + + auto &layoutableShadowNode = dynamic_cast(*clonedShadowNode); + + auto &state = getStateData(); + + facebook::react::LayoutConstraints layoutConstraints; + layoutConstraints.layoutDirection = layoutMetrics.layoutDirection; + + if (state.userdata) { + // If the portal component set a state of type IPortalStateData, + // extract constraint information from it, and use that for layout + if (auto portalState = state.userdata.try_as()) { + auto stateConstraints = portalState.LayoutConstraints(); + + layoutConstraints.minimumSize = {stateConstraints.MinimumSize.Width, stateConstraints.MinimumSize.Height}; + layoutConstraints.maximumSize = {stateConstraints.MaximumSize.Width, stateConstraints.MaximumSize.Height}; + if (stateConstraints.LayoutDirection == winrt::Microsoft::ReactNative::LayoutDirection::LeftToRight) { + layoutConstraints.layoutDirection = facebook::react::LayoutDirection::LeftToRight; + } else if (stateConstraints.LayoutDirection == winrt::Microsoft::ReactNative::LayoutDirection::RightToLeft) { + layoutConstraints.layoutDirection = facebook::react::LayoutDirection::RightToLeft; + } + } + } + + // Laying out the `ShadowNode` and the subtree starting from it. + layoutableShadowNode.layoutTree(layoutContext, layoutConstraints); + + auto childLayoutMetrics = layoutableShadowNode.getLayoutMetrics(); + childLayoutMetrics.frame.origin = {0, 0}; + layoutableShadowNode.setLayoutMetrics(childLayoutMetrics); + + // Update the list of children to reflect the changes that we made. + this->children_ = static_cast(portalOwningShadowNode.get())->children_; +} + +void AbiPortalShadowNode::Builder(winrt::Microsoft::ReactNative::IReactViewComponentBuilder builder) noexcept { + m_builder = builder; +} + +winrt::Microsoft::ReactNative::IReactViewComponentBuilder AbiPortalShadowNode::Builder() const noexcept { + return m_builder; +} + +void AbiPortalShadowNode::Proxy(winrt::Microsoft::ReactNative::ShadowNode proxy) noexcept { + m_proxy = proxy; +} + +winrt::Microsoft::ReactNative::ShadowNode AbiPortalShadowNode::Proxy() const noexcept { + return m_proxy; +} + +} // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiPortalShadowNode.h b/vnext/Microsoft.ReactNative/Fabric/AbiPortalShadowNode.h new file mode 100644 index 00000000000..71f066a87e1 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/AbiPortalShadowNode.h @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include +#include +#include "AbiShadowNode.h" +#include "AbiState.h" +#include "AbiViewProps.h" + +#include +#include + +namespace Microsoft::ReactNative { + +extern const char AbiPortalComponentName[]; + +class AbiPortalShadowNode final : public facebook::react::ConcreteViewShadowNode< + AbiPortalComponentName, + AbiViewProps, + facebook::react::ViewEventEmitter, + Microsoft::ReactNative::AbiStateData> { + public: + using ConcreteViewShadowNode::ConcreteViewShadowNode; + + static facebook::react::ShadowNodeTraits BaseTraits() { + auto traits = facebook::react::ShadowNode::BaseTraits(); + traits.set(facebook::react::ShadowNodeTraits::Trait::FormsStackingContext); + traits.set(facebook::react::ShadowNodeTraits::Trait::FormsView); + traits.set(facebook::react::ShadowNodeTraits::Trait::RootNodeKind); + traits.set(facebook::react::ShadowNodeTraits::Trait::LeafYogaNode); + traits.set(facebook::react::ShadowNodeTraits::Trait::MeasurableYogaNode); + return traits; + } + + facebook::react::Size measureContent( + const facebook::react::LayoutContext &layoutContext, + const facebook::react::LayoutConstraints &layoutConstraints) const override; + void layout(facebook::react::LayoutContext layoutContext) override; + + void OnClone(const facebook::react::ShadowNode &sourceShadowNode) noexcept; + void Builder(winrt::Microsoft::ReactNative::IReactViewComponentBuilder builder) noexcept; + winrt::Microsoft::ReactNative::IReactViewComponentBuilder Builder() const noexcept; + void Proxy(winrt::Microsoft::ReactNative::ShadowNode handle) noexcept; + winrt::Microsoft::ReactNative::ShadowNode Proxy() const noexcept; + + private: + winrt::Microsoft::ReactNative::ShadowNode m_proxy{nullptr}; + winrt::Microsoft::ReactNative::IReactViewComponentBuilder m_builder{nullptr}; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.cpp b/vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.cpp deleted file mode 100644 index f60f841c76a..00000000000 --- a/vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.cpp +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -#include "pch.h" - -#include "AbiViewComponentDescriptor.h" - -#include -#include -#include -#include -#include "DynamicReader.h" - -namespace Microsoft::ReactNative { - -AbiViewComponentDescriptor::AbiViewComponentDescriptor(facebook::react::ComponentDescriptorParameters const ¶meters) - : ComponentDescriptor(parameters) { - auto flavor = std::static_pointer_cast(this->flavor_); - m_builder = WindowsComponentDescriptorRegistry::FromProperties( - parameters.contextContainer->at("MSRN.ReactContext") - .Properties()) - ->GetDescriptor(flavor); - - rawPropsParser_.prepare(); -} - -facebook::react::ComponentHandle AbiViewComponentDescriptor::getComponentHandle() const { - return reinterpret_cast(getComponentName()); -} - -facebook::react::ComponentName AbiViewComponentDescriptor::getComponentName() const { - return std::static_pointer_cast(this->flavor_)->c_str(); -} - -facebook::react::ShadowNodeTraits AbiViewComponentDescriptor::getTraits() const { - auto traits = ShadowNodeT::BaseTraits(); - if (winrt::get_self(m_builder) - ->MeasureContentHandler()) { - traits.set(facebook::react::ShadowNodeTraits::LeafYogaNode); - traits.set(facebook::react::ShadowNodeTraits::MeasurableYogaNode); - } - return traits; -} - -std::shared_ptr AbiViewComponentDescriptor::createShadowNode( - const facebook::react::ShadowNodeFragment &fragment, - facebook::react::ShadowNodeFamily::Shared const &family) const { - auto shadowNode = std::make_shared(fragment, family, getTraits()); - - shadowNode->Proxy(winrt::make(shadowNode)); - winrt::get_self(m_builder) - ->CreateShadowNode(shadowNode->Proxy()); - - adopt(*shadowNode); - return shadowNode; -} - -facebook::react::ShadowNode::Unshared AbiViewComponentDescriptor::cloneShadowNode( - const facebook::react::ShadowNode &sourceShadowNode, - const facebook::react::ShadowNodeFragment &fragment) const { - auto shadowNode = std::make_shared(sourceShadowNode, fragment); - - shadowNode->Proxy(winrt::make(shadowNode)); - winrt::get_self(m_builder) - ->CloneShadowNode(shadowNode->Proxy(), static_cast(sourceShadowNode).Proxy()); - - adopt(*shadowNode); - return shadowNode; -} - -void AbiViewComponentDescriptor::appendChild( - const facebook::react::ShadowNode::Shared &parentShadowNode, - const facebook::react::ShadowNode::Shared &childShadowNode) const { - auto concreteParentShadowNode = std::static_pointer_cast(parentShadowNode); - auto concreteNonConstParentShadowNode = std::const_pointer_cast(concreteParentShadowNode); - concreteNonConstParentShadowNode->appendChild(childShadowNode); -} - -facebook::react::Props::Shared AbiViewComponentDescriptor::cloneProps( - const facebook::react::PropsParserContext &context, - const facebook::react::Props::Shared &props, - facebook::react::RawProps rawProps) const { - // Optimization: - // Quite often nodes are constructed with default/empty props: the base - // `props` object is `null` (there no base because it's not cloning) and the - // `rawProps` is empty. In this case, we can return the default props object - // of a concrete type entirely bypassing parsing. - if (!props && rawProps.isEmpty()) { - return ShadowNodeT::defaultSharedProps(); - } - - if constexpr (facebook::react::RawPropsFilterable) { - ShadowNodeT::filterRawProps(rawProps); - } - - rawProps.parse(rawPropsParser_); - - // Call old-style constructor - // auto shadowNodeProps = std::make_shared(context, rawProps, props); - auto shadowNodeProps = std::make_shared( - context, props ? static_cast(*props) : *ShadowNodeT::defaultSharedProps(), rawProps); - auto viewProps = - winrt::make(shadowNodeProps, false /*holdRef*/); - auto userProps = - winrt::get_self(m_builder) - ->CreateProps(viewProps, props ? static_cast(*props).UserProps() : nullptr); - shadowNodeProps->SetUserProps(userProps, viewProps); - - rawProps.iterateOverValues( - [&](facebook::react::RawPropsPropNameHash hash, const char *propName, facebook::react::RawValue const &fn) { - shadowNodeProps.get()->setProp(context, hash, propName, fn); - userProps.SetProp( - hash, - winrt::to_hstring(propName), - winrt::make(folly::dynamic(fn))); - }); - - return shadowNodeProps; -}; - -AbiViewComponentDescriptor::ConcreteStateData AbiViewComponentDescriptor::initialStateData( - const facebook::react::Props::Shared &props, - const facebook::react::ShadowNodeFamily::Shared & /*family*/, - const facebook::react::ComponentDescriptor &componentDescriptor) noexcept { - return {winrt::get_self( - static_cast(componentDescriptor).m_builder) - ->InitialStateData(std::static_pointer_cast(props)->UserProps())}; -} - -facebook::react::State::Shared AbiViewComponentDescriptor::createInitialState( - facebook::react::Props::Shared const &props, - facebook::react::ShadowNodeFamily::Shared const &family) const { - if (std::is_same::value) { - // Default case: Returning `null` for nodes that don't use `State`. - return nullptr; - } - - return std::make_shared( - std::make_shared(AbiViewComponentDescriptor::initialStateData(props, family, *this)), - family); -} - -facebook::react::State::Shared AbiViewComponentDescriptor::createState( - facebook::react::ShadowNodeFamily const &family, - facebook::react::StateData::Shared const &data) const { - if (std::is_same::value) { - // Default case: Returning `null` for nodes that don't use `State`. - return nullptr; - } - - react_native_assert(data && "Provided `data` is nullptr."); - - return std::make_shared( - std::static_pointer_cast(data), *family.getMostRecentState()); -} - -facebook::react::ShadowNodeFamily::Shared AbiViewComponentDescriptor::createFamily( - facebook::react::ShadowNodeFamilyFragment const &fragment) const { - auto eventEmitter = std::make_shared( - std::make_shared(fragment.instanceHandle), eventDispatcher_); - return std::make_shared( - fragment, std::move(eventEmitter), eventDispatcher_, *this); -} - -/* - * Called immediately after `ShadowNode` is created or cloned. - * - * Override this method to pass information from custom `ComponentDescriptor` - * to new instance of `ShadowNode`. - * - * Example usages: - * - Inject image manager to `ImageShadowNode` in - * `ImageComponentDescriptor`. - * - Set `ShadowNode`'s size from state in - * `ModalHostViewComponentDescriptor`. - */ -void AbiViewComponentDescriptor::adopt(facebook::react::ShadowNode &shadowNode) const { - react_native_assert(shadowNode.getComponentHandle() == getComponentHandle()); - - auto &abiViewShadowNode = static_cast(shadowNode); - - abiViewShadowNode.Builder(m_builder); - - if (winrt::get_self(m_builder) - ->MeasureContentHandler()) { - abiViewShadowNode.dirtyLayout(); - abiViewShadowNode.enableMeasurement(); - } -} - -} // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.h b/vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.h index 32ff0581c12..e726693fa18 100644 --- a/vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.h +++ b/vnext/Microsoft.ReactNative/Fabric/AbiViewComponentDescriptor.h @@ -3,16 +3,22 @@ #pragma once +#include +#include +#include #include #include +#include "AbiPortalShadowNode.h" #include "AbiViewProps.h" #include "AbiViewShadowNode.h" +#include "DynamicReader.h" #include "winrt/Microsoft.ReactNative.h" namespace Microsoft::ReactNative { -class AbiViewComponentDescriptor : public facebook::react::ComponentDescriptor { - using ShadowNodeT = AbiViewShadowNode; +template +class ConcreteAbiViewComponentDescriptor : public facebook::react::ComponentDescriptor { + protected: using SharedShadowNodeT = std::shared_ptr; public: @@ -24,34 +30,143 @@ class AbiViewComponentDescriptor : public facebook::react::ComponentDescriptor { using ConcreteState = typename ShadowNodeT::ConcreteState; using ConcreteStateData = typename ShadowNodeT::ConcreteState::Data; - AbiViewComponentDescriptor(facebook::react::ComponentDescriptorParameters const ¶meters); + ConcreteAbiViewComponentDescriptor(facebook::react::ComponentDescriptorParameters const ¶meters) + : ComponentDescriptor(parameters) { + auto flavor = std::static_pointer_cast(this->flavor_); + m_builder = WindowsComponentDescriptorRegistry::FromProperties( + parameters.contextContainer->at("MSRN.ReactContext") + .Properties()) + ->GetDescriptor(flavor); + + rawPropsParser_.prepare(); + } + facebook::react::ComponentHandle getComponentHandle() const override { + return reinterpret_cast(getComponentName()); + } + + facebook::react::ComponentName getComponentName() const override { + return std::static_pointer_cast(this->flavor_)->c_str(); + } + + facebook::react::ShadowNodeTraits getTraits() const override { + auto traits = ShadowNodeT::BaseTraits(); + if (winrt::get_self(m_builder) + ->MeasureContentHandler()) { + traits.set(facebook::react::ShadowNodeTraits::LeafYogaNode); + traits.set(facebook::react::ShadowNodeTraits::MeasurableYogaNode); + } + return traits; + } - facebook::react::ComponentHandle getComponentHandle() const override; - facebook::react::ComponentName getComponentName() const override; - facebook::react::ShadowNodeTraits getTraits() const override; std::shared_ptr createShadowNode( const facebook::react::ShadowNodeFragment &fragment, - facebook::react::ShadowNodeFamily::Shared const &family) const override; + facebook::react::ShadowNodeFamily::Shared const &family) const override { + auto shadowNode = std::make_shared(fragment, family, getTraits()); + shadowNode->Proxy(winrt::make(shadowNode)); + winrt::get_self(m_builder) + ->CreateShadowNode(shadowNode->Proxy()); + + adopt(*shadowNode); + return shadowNode; + } + facebook::react::ShadowNode::Unshared cloneShadowNode( const facebook::react::ShadowNode &sourceShadowNode, - const facebook::react::ShadowNodeFragment &fragment) const override; + const facebook::react::ShadowNodeFragment &fragment) const override { + auto shadowNode = std::make_shared(sourceShadowNode, fragment); + shadowNode->Proxy(winrt::make(shadowNode)); + winrt::get_self(m_builder) + ->CloneShadowNode(shadowNode->Proxy(), static_cast(sourceShadowNode).Proxy()); + + adopt(*shadowNode); + return shadowNode; + } void appendChild( const facebook::react::ShadowNode::Shared &parentShadowNode, - const facebook::react::ShadowNode::Shared &childShadowNode) const override; + const facebook::react::ShadowNode::Shared &childShadowNode) const override { + auto concreteParentShadowNode = std::static_pointer_cast(parentShadowNode); + auto concreteNonConstParentShadowNode = std::const_pointer_cast(concreteParentShadowNode); + concreteNonConstParentShadowNode->appendChild(childShadowNode); + } + virtual facebook::react::Props::Shared cloneProps( const facebook::react::PropsParserContext &context, const facebook::react::Props::Shared &props, - facebook::react::RawProps rawProps) const override; + facebook::react::RawProps rawProps) const override { + // Optimization: + // Quite often nodes are constructed with default/empty props: the base + // `props` object is `null` (there no base because it's not cloning) and the + // `rawProps` is empty. In this case, we can return the default props object + // of a concrete type entirely bypassing parsing. + if (!props && rawProps.isEmpty()) { + return ShadowNodeT::defaultSharedProps(); + } + + if constexpr (facebook::react::RawPropsFilterable) { + ShadowNodeT::filterRawProps(rawProps); + } + + rawProps.parse(rawPropsParser_); + + // Call old-style constructor + // auto shadowNodeProps = std::make_shared(context, rawProps, props); + auto shadowNodeProps = std::make_shared( + context, props ? static_cast(*props) : *ShadowNodeT::defaultSharedProps(), rawProps); + auto viewProps = + winrt::make(shadowNodeProps, false /*holdRef*/); + auto userProps = + winrt::get_self(m_builder) + ->CreateProps(viewProps, props ? static_cast(*props).UserProps() : nullptr); + shadowNodeProps->SetUserProps(userProps, viewProps); + + rawProps.iterateOverValues( + [&](facebook::react::RawPropsPropNameHash hash, const char *propName, facebook::react::RawValue const &fn) { + shadowNodeProps.get()->setProp(context, hash, propName, fn); + userProps.SetProp( + hash, + winrt::to_hstring(propName), + winrt::make(folly::dynamic(fn))); + }); + + return shadowNodeProps; + } + virtual facebook::react::State::Shared createInitialState( facebook::react::Props::Shared const &props, - facebook::react::ShadowNodeFamily::Shared const &family) const override; + facebook::react::ShadowNodeFamily::Shared const &family) const override { + if (std::is_same::value) { + // Default case: Returning `null` for nodes that don't use `State`. + return nullptr; + } + + return std::make_shared( + std::make_shared( + ConcreteAbiViewComponentDescriptor::initialStateData(props, family, *this)), + family); + } + virtual facebook::react::State::Shared createState( facebook::react::ShadowNodeFamily const &family, - facebook::react::StateData::Shared const &data) const override; + facebook::react::StateData::Shared const &data) const override { + if (std::is_same::value) { + // Default case: Returning `null` for nodes that don't use `State`. + return nullptr; + } + + react_native_assert(data && "Provided `data` is nullptr."); + + return std::make_shared( + std::static_pointer_cast(data), *family.getMostRecentState()); + } facebook::react::ShadowNodeFamily::Shared createFamily( - facebook::react::ShadowNodeFamilyFragment const &fragment) const override; + facebook::react::ShadowNodeFamilyFragment const &fragment) const override { + auto eventEmitter = std::make_shared( + std::make_shared(fragment.instanceHandle), eventDispatcher_); + return std::make_shared( + fragment, std::move(eventEmitter), eventDispatcher_, *this); + } protected: /* @@ -66,15 +181,44 @@ class AbiViewComponentDescriptor : public facebook::react::ComponentDescriptor { * - Set `ShadowNode`'s size from state in * `ModalHostViewComponentDescriptor`. */ - virtual void adopt(facebook::react::ShadowNode &shadowNode) const; + virtual void adopt(facebook::react::ShadowNode &shadowNode) const { + react_native_assert(shadowNode.getComponentHandle() == getComponentHandle()); + + auto &abiViewShadowNode = static_cast(shadowNode); + + abiViewShadowNode.Builder(m_builder); + + if (winrt::get_self(m_builder) + ->MeasureContentHandler()) { + abiViewShadowNode.dirtyLayout(); + abiViewShadowNode.enableMeasurement(); + } + } private: static ConcreteStateData initialStateData( - const facebook::react::Props::Shared & /*props*/, + const facebook::react::Props::Shared &props, const facebook::react::ShadowNodeFamily::Shared & /*family*/, - const facebook::react::ComponentDescriptor & /*componentDescriptor*/) noexcept; + const facebook::react::ComponentDescriptor &componentDescriptor) noexcept { + return {winrt::get_self( + static_cast &>(componentDescriptor).m_builder) + ->InitialStateData(std::static_pointer_cast(props)->UserProps())}; + return {}; + } + + winrt::Microsoft::ReactNative::IReactViewComponentBuilder m_builder{nullptr}; +}; - winrt::Microsoft::ReactNative::IReactViewComponentBuilder m_builder; +class AbiViewComponentDescriptor : public ConcreteAbiViewComponentDescriptor { + public: + AbiViewComponentDescriptor(facebook::react::ComponentDescriptorParameters const ¶meters) + : ConcreteAbiViewComponentDescriptor(parameters) {} +}; + +class AbiPortalComponentDescriptor : public ConcreteAbiViewComponentDescriptor { + public: + AbiPortalComponentDescriptor(facebook::react::ComponentDescriptorParameters const ¶meters) + : ConcreteAbiViewComponentDescriptor(parameters) {} }; } // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp index 3fe7fcd2e57..caa4608a2b4 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "RootComponentView.h" #include "UiaHelpers.h" @@ -27,12 +28,29 @@ CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider( } } +#ifdef USE_EXPERIMENTAL_WINUI3 +CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider( + const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView, + const winrt::Microsoft::UI::Content::ChildSiteLink &childSiteLink) noexcept + : m_view{componentView}, m_childSiteLink{childSiteLink} {} +#endif // USE_EXPERIMENTAL_WINUI3 + HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate( NavigateDirection direction, IRawElementProviderFragment **pRetVal) { if (pRetVal == nullptr) return E_POINTER; +#ifdef USE_EXPERIMENTAL_WINUI3 + if (m_childSiteLink) { + if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild) { + auto fragment = m_childSiteLink.AutomationProvider().try_as(); + *pRetVal = fragment.detach(); + return S_OK; + } + } +#endif // USE_EXPERIMENTAL_WINUI3 + return UiaNavigateHelper(m_view.view(), direction, *pRetVal); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h index 34a15284196..564766cd3c3 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.h @@ -25,6 +25,12 @@ class CompositionDynamicAutomationProvider : public winrt::implements< CompositionDynamicAutomationProvider( const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView) noexcept; +#ifdef USE_EXPERIMENTAL_WINUI3 + CompositionDynamicAutomationProvider( + const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView, + const winrt::Microsoft::UI::Content::ChildSiteLink &childContentLink) noexcept; +#endif // USE_EXPERIMENTAL_WINUI3 + // inherited via IRawElementProviderFragment virtual HRESULT __stdcall Navigate(NavigateDirection direction, IRawElementProviderFragment **pRetVal) override; virtual HRESULT __stdcall GetRuntimeId(SAFEARRAY **pRetVal) override; @@ -86,6 +92,10 @@ class CompositionDynamicAutomationProvider : public winrt::implements< private: ::Microsoft::ReactNative::ReactTaggedView m_view; std::vector> m_selectionItems; +#ifdef USE_EXPERIMENTAL_WINUI3 + // Non-null when this UIA node is the peer of a ContentIslandComponentView. + winrt::Microsoft::UI::Content::ChildSiteLink m_childSiteLink{nullptr}; +#endif }; } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp index a4633f174b2..d18b645cbec 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp @@ -33,6 +33,7 @@ namespace winrt::Microsoft::ReactNative::Composition::implementation { constexpr float FOCUS_VISUAL_WIDTH = 2.0f; +constexpr float FOCUS_VISUAL_RADIUS = 3.0f; // m_outerVisual // | @@ -168,11 +169,14 @@ void ComponentView::updateProps( m_componentHostingFocusVisual->hostFocusVisual(false, get_strong()); } - if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive) { - m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive->updateProps(oldViewProps, newViewProps); - } - if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive) { - m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive->updateProps(oldViewProps, newViewProps); + // We have to check m_componentHostingFocusVisual again, as it can be set to null by above hostFocusVisual call + if (m_componentHostingFocusVisual) { + if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive) { + m_componentHostingFocusVisual->m_focusPrimitive->m_focusInnerPrimitive->updateProps(oldViewProps, newViewProps); + } + if (m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive) { + m_componentHostingFocusVisual->m_focusPrimitive->m_focusOuterPrimitive->updateProps(oldViewProps, newViewProps); + } } } if ((m_flags & ComponentViewFeatures::ShadowProps) == ComponentViewFeatures::ShadowProps) { @@ -225,29 +229,11 @@ void ComponentView::updateFocusLayoutMetrics() noexcept { facebook::react::RectangleEdges nudgeEdges; auto scaleFactor = m_focusPrimitive->m_focusVisualComponent->m_layoutMetrics.pointScaleFactor; if (m_focusPrimitive) { + auto nudgeEdges = m_focusPrimitive->m_focusVisualComponent->focusNudges(); if (m_focusPrimitive->m_focusOuterPrimitive) { auto outerFocusMetrics = m_focusPrimitive->m_focusVisualComponent->focusLayoutMetrics(false /*inner*/); - - if (outerFocusMetrics.frame.origin.x < 0) { - nudgeEdges.left = true; - } - if (outerFocusMetrics.frame.origin.y < 0) { - nudgeEdges.top = true; - } - if (outerFocusMetrics.frame.getMaxX() > m_layoutMetrics.frame.getMaxX()) { - nudgeEdges.right = true; - } - if (outerFocusMetrics.frame.getMaxY() > m_layoutMetrics.frame.getMaxY()) { - nudgeEdges.bottom = true; - } - m_focusPrimitive->m_focusOuterPrimitive->RootVisual().Size( - {outerFocusMetrics.frame.size.width * scaleFactor - - (nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0) - - (nudgeEdges.right ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0), - outerFocusMetrics.frame.size.height * scaleFactor - - (nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0) - - (nudgeEdges.bottom ? (FOCUS_VISUAL_WIDTH * 2 * scaleFactor) : 0)}); + {outerFocusMetrics.frame.size.width * scaleFactor, outerFocusMetrics.frame.size.height * scaleFactor}); m_focusPrimitive->m_focusOuterPrimitive->RootVisual().Offset( {nudgeEdges.left ? 0 : -(FOCUS_VISUAL_WIDTH * 2 * scaleFactor), nudgeEdges.top ? 0 : -(FOCUS_VISUAL_WIDTH * 2 * scaleFactor), @@ -258,15 +244,10 @@ void ComponentView::updateFocusLayoutMetrics() noexcept { if (m_focusPrimitive->m_focusInnerPrimitive) { auto innerFocusMetrics = m_focusPrimitive->m_focusVisualComponent->focusLayoutMetrics(true /*inner*/); m_focusPrimitive->m_focusInnerPrimitive->RootVisual().Size( - {innerFocusMetrics.frame.size.width * scaleFactor - - (nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0) - - (nudgeEdges.right ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0), - innerFocusMetrics.frame.size.height * scaleFactor - - (nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0) - - (nudgeEdges.bottom ? (FOCUS_VISUAL_WIDTH * scaleFactor) : 0)}); + {innerFocusMetrics.frame.size.width * scaleFactor, innerFocusMetrics.frame.size.height * scaleFactor}); m_focusPrimitive->m_focusInnerPrimitive->RootVisual().Offset( - {nudgeEdges.left ? 0 : -FOCUS_VISUAL_WIDTH * scaleFactor, - nudgeEdges.top ? 0 : -FOCUS_VISUAL_WIDTH * scaleFactor, + {nudgeEdges.left ? (FOCUS_VISUAL_WIDTH * scaleFactor) : (-FOCUS_VISUAL_WIDTH * scaleFactor), + nudgeEdges.top ? (FOCUS_VISUAL_WIDTH * scaleFactor) : (-FOCUS_VISUAL_WIDTH * scaleFactor), 0.0f}); m_focusPrimitive->m_focusInnerPrimitive->markNeedsUpdate(); } @@ -535,7 +516,33 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ComponentView: return m_outerVisual ? m_outerVisual : Visual(); } -facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) const noexcept { +// If the focus visual would extend past the bounds of the hosting visual, +// then we will nudge the focus visual back inside the hosting visuals bounds. +facebook::react::RectangleEdges ComponentView::focusNudges() const noexcept { + facebook::react::RectangleEdges nudgeEdges; + + // Always use outer focus metrics to determine if we need to nudge the focus rect over to fit + facebook::react::LayoutMetrics layoutMetrics = focusLayoutMetricsNoNudge(false /*inner*/); + + Assert(m_componentHostingFocusVisual); + + if (layoutMetrics.frame.origin.x < 0) { + nudgeEdges.left = true; + } + if (layoutMetrics.frame.origin.y < 0) { + nudgeEdges.top = true; + } + if (layoutMetrics.frame.getMaxX() > m_componentHostingFocusVisual->m_layoutMetrics.frame.getMaxX()) { + nudgeEdges.right = true; + } + if (layoutMetrics.frame.getMaxY() > m_componentHostingFocusVisual->m_layoutMetrics.frame.getMaxY()) { + nudgeEdges.bottom = true; + } + + return nudgeEdges; +} + +facebook::react::LayoutMetrics ComponentView::focusLayoutMetricsNoNudge(bool inner) const noexcept { facebook::react::LayoutMetrics layoutMetrics = m_layoutMetrics; layoutMetrics.frame.origin.x -= FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); layoutMetrics.frame.origin.y -= FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); @@ -544,6 +551,28 @@ facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) con return layoutMetrics; } +facebook::react::LayoutMetrics ComponentView::focusLayoutMetrics(bool inner) const noexcept { + auto nudgeEdges = focusNudges(); + auto layoutMetrics = focusLayoutMetricsNoNudge(inner); + + if (nudgeEdges.left) { + layoutMetrics.frame.origin.x += FOCUS_VISUAL_WIDTH * 2; + layoutMetrics.frame.size.width -= FOCUS_VISUAL_WIDTH * 2; + } + if (nudgeEdges.top) { + layoutMetrics.frame.origin.y += FOCUS_VISUAL_WIDTH * 2; + layoutMetrics.frame.size.height -= FOCUS_VISUAL_WIDTH * 2; + } + if (nudgeEdges.right) { + layoutMetrics.frame.size.width -= FOCUS_VISUAL_WIDTH * 2; + } + if (nudgeEdges.bottom) { + layoutMetrics.frame.size.height -= FOCUS_VISUAL_WIDTH * 2; + } + + return layoutMetrics; +} + facebook::react::BorderMetrics ComponentView::focusBorderMetrics( bool inner, const facebook::react::LayoutMetrics &layoutMetrics) const noexcept { @@ -553,14 +582,17 @@ facebook::react::BorderMetrics ComponentView::focusBorderMetrics( innerColor.m_platformColor.push_back(inner ? "FocusVisualSecondary" : "FocusVisualPrimary"); metrics.borderColors.bottom = metrics.borderColors.left = metrics.borderColors.right = metrics.borderColors.top = innerColor; - if (metrics.borderRadii.bottomLeft != 0) - metrics.borderRadii.bottomLeft += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); - if (metrics.borderRadii.bottomRight != 0) - metrics.borderRadii.bottomRight += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); - if (metrics.borderRadii.topLeft != 0) - metrics.borderRadii.topLeft += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); - if (metrics.borderRadii.topRight != 0) - metrics.borderRadii.topRight += FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); + + metrics.borderRadii.bottomLeft = + (metrics.borderRadii.bottomLeft ? metrics.borderRadii.bottomLeft : FOCUS_VISUAL_RADIUS) + + FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); + metrics.borderRadii.bottomRight = + (metrics.borderRadii.bottomRight ? metrics.borderRadii.bottomRight : FOCUS_VISUAL_RADIUS) + + FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); + metrics.borderRadii.topLeft = (metrics.borderRadii.topLeft ? metrics.borderRadii.topLeft : FOCUS_VISUAL_RADIUS) + + FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); + metrics.borderRadii.topRight = (metrics.borderRadii.topRight ? metrics.borderRadii.topRight : FOCUS_VISUAL_RADIUS) + + FOCUS_VISUAL_WIDTH * (inner ? 1 : 2); metrics.borderStyles.bottom = metrics.borderStyles.left = metrics.borderStyles.right = metrics.borderStyles.top = facebook::react::BorderStyle::Solid; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h index ee7c13e9a67..9a59c4c4dfe 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h @@ -143,6 +143,8 @@ struct ComponentView : public ComponentViewT< void FinalizeTransform( facebook::react::LayoutMetrics const &layoutMetrics, const facebook::react::ViewProps &viewProps) noexcept; + facebook::react::RectangleEdges focusNudges() const noexcept; + facebook::react::LayoutMetrics focusLayoutMetricsNoNudge(bool inner) const noexcept; facebook::react::LayoutMetrics focusLayoutMetrics(bool inner) const noexcept; facebook::react::BorderMetrics focusBorderMetrics(bool inner, const facebook::react::LayoutMetrics &layoutMetrics) const noexcept; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp index 2d9ac52b823..482ac71be5f 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp @@ -11,12 +11,15 @@ #include #include #include +#include #include #include "CompositionContextHelper.h" #include "RootComponentView.h" #include "Composition.ContentIslandComponentView.g.cpp" +#include "CompositionDynamicAutomationProvider.h" + namespace winrt::Microsoft::ReactNative::Composition::implementation { ContentIslandComponentView::ContentIslandComponentView( @@ -47,6 +50,22 @@ void ContentIslandComponentView::OnMounted() noexcept { winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(Visual()) .as()); m_childSiteLink.ActualSize({m_layoutMetrics.frame.size.width, m_layoutMetrics.frame.size.height}); + + m_navigationHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteLink(m_childSiteLink); + + m_navigationHostDepartFocusRequestedToken = + m_navigationHost.DepartFocusRequested([wkThis = get_weak()](const auto &, const auto &args) { + if (auto strongThis = wkThis.get()) { + const bool next = (args.Request().Reason() != winrt::Microsoft::UI::Input::FocusNavigationReason::Last); + strongThis->rootComponentView()->TryMoveFocus(next); + args.Result(winrt::Microsoft::UI::Input::FocusNavigationResult::Moved); + } + }); + + // We configure automation even if there's no UIA client at this point, because it's possible the first UIA + // request we'll get will be for a child of this island calling upward in the UIA tree. + ConfigureChildSiteLinkAutomation(); + if (m_islandToConnect) { m_childSiteLink.Connect(m_islandToConnect); m_islandToConnect = nullptr; @@ -70,6 +89,12 @@ void ContentIslandComponentView::OnMounted() noexcept { void ContentIslandComponentView::OnUnmounted() noexcept { m_layoutMetricChangedRevokers.clear(); +#ifdef USE_EXPERIMENTAL_WINUI3 + if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) { + m_navigationHost.DepartFocusRequested(m_navigationHostDepartFocusRequestedToken); + m_navigationHostDepartFocusRequestedToken = {}; + } +#endif } void ContentIslandComponentView::ParentLayoutChanged() noexcept { @@ -92,7 +117,79 @@ void ContentIslandComponentView::ParentLayoutChanged() noexcept { #endif } +winrt::IInspectable ContentIslandComponentView::EnsureUiaProvider() noexcept { +#ifdef USE_EXPERIMENTAL_WINUI3 + if (m_uiaProvider == nullptr) { + m_uiaProvider = winrt::make( + *get_strong(), m_childSiteLink); + } + return m_uiaProvider; +#else + return Super::EnsureUiaProvider(); +#endif +} + +bool ContentIslandComponentView::focusable() const noexcept { +#ifdef USE_EXPERIMENTAL_WINUI3 + // We don't have a way to check to see if the ContentIsland has focusable content, + // so we'll always return true. We'll have to handle the case where the content doesn't have + // focusable content in the OnGotFocus handler. + return true; +#else + return Super::focusable(); +#endif +} + +// Helper to convert a FocusNavigationDirection to a FocusNavigationReason. +winrt::Microsoft::UI::Input::FocusNavigationReason GetFocusNavigationReason( + winrt::Microsoft::ReactNative::FocusNavigationDirection direction) noexcept { + switch (direction) { + case winrt::Microsoft::ReactNative::FocusNavigationDirection::First: + case winrt::Microsoft::ReactNative::FocusNavigationDirection::Next: + return winrt::Microsoft::UI::Input::FocusNavigationReason::First; + case winrt::Microsoft::ReactNative::FocusNavigationDirection::Last: + case winrt::Microsoft::ReactNative::FocusNavigationDirection::Previous: + return winrt::Microsoft::UI::Input::FocusNavigationReason::Last; + } + return winrt::Microsoft::UI::Input::FocusNavigationReason::Restore; +} + +void ContentIslandComponentView::onGotFocus( + const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept { +#ifdef USE_EXPERIMENTAL_WINUI3 + auto gotFocusEventArgs = args.as(); + const auto navigationReason = GetFocusNavigationReason(gotFocusEventArgs->Direction()); + m_navigationHost.NavigateFocus(winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(navigationReason)); +#else + return Super::onGotFocus(args); +#endif // USE_EXPERIMENTAL_WINUI3 +} + ContentIslandComponentView::~ContentIslandComponentView() noexcept { +#ifdef USE_EXPERIMENTAL_WINUI3 + if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) { + m_navigationHost.DepartFocusRequested(m_navigationHostDepartFocusRequestedToken); + m_navigationHostDepartFocusRequestedToken = {}; + } + if (m_childSiteLink) { + if (m_fragmentRootAutomationProviderRequestedToken) { + m_childSiteLink.FragmentRootAutomationProviderRequested(m_fragmentRootAutomationProviderRequestedToken); + m_fragmentRootAutomationProviderRequestedToken = {}; + } + if (m_parentAutomationProviderRequestedToken) { + m_childSiteLink.ParentAutomationProviderRequested(m_parentAutomationProviderRequestedToken); + m_parentAutomationProviderRequestedToken = {}; + } + if (m_nextSiblingAutomationProviderRequestedToken) { + m_childSiteLink.NextSiblingAutomationProviderRequested(m_nextSiblingAutomationProviderRequestedToken); + m_nextSiblingAutomationProviderRequestedToken = {}; + } + if (m_previousSiblingAutomationProviderRequestedToken) { + m_childSiteLink.PreviousSiblingAutomationProviderRequested(m_previousSiblingAutomationProviderRequestedToken); + m_previousSiblingAutomationProviderRequestedToken = {}; + } + } +#endif // USE_EXPERIMENTAL_WINUI3 if (m_islandToConnect) { m_islandToConnect.Close(); } @@ -132,11 +229,65 @@ void ContentIslandComponentView::Connect(const winrt::Microsoft::UI::Content::Co } else { m_islandToConnect = contentIsland; } -#endif +#endif // USE_EXPERIMENTAL_WINUI3 } void ContentIslandComponentView::prepareForRecycle() noexcept { Super::prepareForRecycle(); } +#ifdef USE_EXPERIMENTAL_WINUI3 +void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept { + // This automation mode must be set before connecting the child ContentIsland. + // It puts the child content into a mode where it won't own its own framework root. Instead, the child island's + // automation peers will use the same framework root as the automation peer of this ContentIslandComponentView. + m_childSiteLink.AutomationTreeOption(winrt::Microsoft::UI::Content::AutomationTreeOptions::FragmentBased); + + // These events are raised in response to the child ContentIsland asking for providers. + // For example, the ContentIsland.FragmentRootAutomationProvider property will return + // the provider we provide here in FragmentRootAutomationProviderRequested. + + // We capture "this" as a raw pointer because ContentIslandComponentView doesn't currently support weak ptrs. + // It's safe because we disconnect these events in the destructor. + + m_fragmentRootAutomationProviderRequestedToken = m_childSiteLink.FragmentRootAutomationProviderRequested( + [this]( + const winrt::Microsoft::UI::Content::IContentSiteAutomation &, + const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) { + // The child island's fragment tree doesn't have its own fragment root. + // Here's how we can provide the correct fragment root to the child's UIA logic. + winrt::com_ptr fragmentRoot{nullptr}; + auto uiaProvider = this->EnsureUiaProvider(); + uiaProvider.as()->get_FragmentRoot(fragmentRoot.put()); + args.AutomationProvider(fragmentRoot.as()); + args.Handled(true); + }); + + m_parentAutomationProviderRequestedToken = m_childSiteLink.ParentAutomationProviderRequested( + [this]( + const winrt::Microsoft::UI::Content::IContentSiteAutomation &, + const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) { + auto uiaProvider = this->EnsureUiaProvider(); + args.AutomationProvider(uiaProvider); + args.Handled(true); + }); + + m_nextSiblingAutomationProviderRequestedToken = m_childSiteLink.NextSiblingAutomationProviderRequested( + [](const winrt::Microsoft::UI::Content::IContentSiteAutomation &, + const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) { + // The ContentIsland will always be the one and only child of this node, so it won't have siblings. + args.AutomationProvider(nullptr); + args.Handled(true); + }); + + m_previousSiblingAutomationProviderRequestedToken = m_childSiteLink.PreviousSiblingAutomationProviderRequested( + [](const winrt::Microsoft::UI::Content::IContentSiteAutomation &, + const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) { + // The ContentIsland will always be the one and only child of this node, so it won't have siblings. + args.AutomationProvider(nullptr); + args.Handled(true); + }); +} +#endif // USE_EXPERIMENTAL_WINUI3 + } // namespace winrt::Microsoft::ReactNative::Composition::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h index d6e81df9de0..88c2499df86 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h @@ -8,6 +8,7 @@ #include #include +#include #include #include "CompositionHelpers.h" #include "CompositionViewComponentView.h" @@ -37,6 +38,12 @@ struct ContentIslandComponentView : ContentIslandComponentViewT m_layoutMetricChangedRevokers; #ifdef USE_EXPERIMENTAL_WINUI3 winrt::Microsoft::UI::Content::ChildSiteLink m_childSiteLink{nullptr}; + winrt::Microsoft::UI::Input::InputFocusNavigationHost m_navigationHost{nullptr}; + winrt::event_token m_navigationHostDepartFocusRequestedToken{}; + + // Automation + void ConfigureChildSiteLinkAutomation() noexcept; + winrt::event_token m_fragmentRootAutomationProviderRequestedToken{}; + winrt::event_token m_parentAutomationProviderRequestedToken{}; + winrt::event_token m_nextSiblingAutomationProviderRequestedToken{}; + winrt::event_token m_previousSiblingAutomationProviderRequestedToken{}; #endif }; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.cpp index ff5a0dd8404..b8e90650783 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.cpp @@ -14,8 +14,10 @@ int32_t LostFocusEventArgs::OriginalSource() noexcept { return m_originalSource; } -GotFocusEventArgs::GotFocusEventArgs(const winrt::Microsoft::ReactNative::ComponentView &originalSource) - : m_originalSource(originalSource ? originalSource.Tag() : -1) {} +GotFocusEventArgs::GotFocusEventArgs( + const winrt::Microsoft::ReactNative::ComponentView &originalSource, + winrt::Microsoft::ReactNative::FocusNavigationDirection direction) + : m_originalSource(originalSource ? originalSource.Tag() : -1), m_direction(direction) {} int32_t GotFocusEventArgs::OriginalSource() noexcept { return m_originalSource; } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.h b/vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.h index d09db5f5db8..c78b7c0e552 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/FocusManager.h @@ -21,11 +21,19 @@ struct LostFocusEventArgs struct GotFocusEventArgs : winrt::implements { - GotFocusEventArgs(const winrt::Microsoft::ReactNative::ComponentView &originalSource); + GotFocusEventArgs( + const winrt::Microsoft::ReactNative::ComponentView &originalSource, + winrt::Microsoft::ReactNative::FocusNavigationDirection direction); int32_t OriginalSource() noexcept; + winrt::Microsoft::ReactNative::FocusNavigationDirection Direction() const noexcept { + return m_direction; + } + private: const int32_t m_originalSource; + winrt::Microsoft::ReactNative::FocusNavigationDirection m_direction{ + winrt::Microsoft::ReactNative::FocusNavigationDirection::None}; }; struct LosingFocusEventArgs diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp index 6bb1de556d9..0c7728bd853 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp @@ -14,6 +14,24 @@ namespace winrt::Microsoft::ReactNative::Composition::implementation { +struct ModalHostState + : winrt::implements { + ModalHostState(winrt::Microsoft::ReactNative::LayoutConstraints layoutConstraints, float scaleFactor) + : m_layoutConstraints(layoutConstraints), m_pointScaleFactor(scaleFactor) {} + + winrt::Microsoft::ReactNative::LayoutConstraints LayoutConstraints() const noexcept { + return m_layoutConstraints; + } + + float PointScaleFactor() const noexcept { + return m_pointScaleFactor; + } + + private: + float m_pointScaleFactor{1.0f}; + winrt::Microsoft::ReactNative::LayoutConstraints m_layoutConstraints; +}; + struct ModalHostView : public winrt::implements, ::Microsoft::ReactNativeSpecs::BaseModalHostView { ~ModalHostView() { @@ -37,6 +55,13 @@ struct ModalHostView : public winrt::implements::UpdateProps(view, newProps, oldProps); } - 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); - } + void UpdateState( + const winrt::Microsoft::ReactNative::ComponentView & /*view*/, + const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept override { + m_state = newState; + } + + void MountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView & /*view*/, + const winrt::Microsoft::ReactNative::MountChildComponentViewArgs &args) noexcept override { + AdjustWindowSize(args.Child().LayoutMetrics()); + assert(!m_childLayoutMetricsToken); + m_childLayoutMetricsToken = args.Child().LayoutMetricsChanged( + [wkThis = get_weak()]( + auto &sender, const winrt::Microsoft::ReactNative::LayoutMetricsChangedArgs &layoutMetricsChangedArgs) { + if (auto strongThis = wkThis.get()) { + strongThis->AdjustWindowSize(layoutMetricsChangedArgs.NewLayoutMetrics()); + } + }); + } + + void UnmountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView & /*view*/, + const winrt::Microsoft::ReactNative::UnmountChildComponentViewArgs &args) noexcept override { + assert(m_childLayoutMetricsToken); + args.Child().LayoutMetricsChanged(m_childLayoutMetricsToken); + m_childLayoutMetricsToken.value = 0; } void FinalizeUpdate( @@ -85,7 +129,6 @@ struct ModalHostView : public winrt::implements( (parentRC.top + parentRC.bottom - layoutMetrics.Frame.Height * layoutMetrics.PointScaleFactor) / 2); +#ifdef USE_EXPERIMENTAL_WINUI3 + winrt::Windows::Graphics::RectInt32 rect2{ + (int)xCor, + (int)yCor, + static_cast(layoutMetrics.Frame.Width * (layoutMetrics.PointScaleFactor)), + static_cast(layoutMetrics.Frame.Height * (layoutMetrics.PointScaleFactor))}; + m_popUp.MoveAndResize(rect2); +#else // 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}); +#endif }; void ShowOnUIThread(const winrt::Microsoft::ReactNative::ComponentView &view) { @@ -122,6 +182,24 @@ struct ModalHostView : public winrt::implements()); + 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); + } + } +#endif + if (m_window && !m_window.IsVisible()) { m_bridge.Enable(); m_window.Show(true); @@ -146,6 +224,12 @@ struct ModalHostView : public winrt::implements()->GetHwndForParenting(); + auto portal = view.as(); + +#ifdef USE_EXPERIMENTAL_WINUI3 + m_bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create( + view.Parent().as().Compositor(), + winrt::Microsoft::UI::GetWindowIdFromWindow(m_parentHwnd)); + m_reactNativeIsland = winrt::Microsoft::ReactNative::ReactNativeIsland::CreatePortal(portal); + auto contentIsland = m_reactNativeIsland.Island(); + + m_popUp = m_bridge.TryCreatePopupSiteBridge(); + m_popUp.Connect(contentIsland); + + // set the top-level windows as the new hwnd + winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId( + view.ReactContext().Properties(), + reinterpret_cast(winrt::Microsoft::UI::GetWindowFromWindowId(m_popUp.WindowId()))); + + auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge( + m_popUp.as()); + 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()); + } + }); + +#else auto presenter = winrt::Microsoft::UI::Windowing::OverlappedPresenter::CreateForDialog(); presenter.SetBorderAndTitleBar(true, false); presenter.IsModal(true); @@ -190,8 +307,7 @@ struct ModalHostView : public winrt::implements().Compositor(), m_window.Id()); - m_reactNativeIsland = winrt::Microsoft::ReactNative::ReactNativeIsland::CreatePortal( - view.as()); + m_reactNativeIsland = winrt::Microsoft::ReactNative::ReactNativeIsland::CreatePortal(portal); auto contentIsland = m_reactNativeIsland.Island(); auto navHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteBridge(m_bridge); @@ -202,13 +318,58 @@ struct ModalHostView : public winrt::implementsUpdateConstraints(); + } + } + }); + + UpdateConstraints(); + + if (portal.ContentRoot().Children().Size()) { + AdjustWindowSize(portal.ContentRoot().Children().GetAt(0).LayoutMetrics()); + } m_bridge.Show(); } + void UpdateConstraints() noexcept { + auto displayArea = winrt::Microsoft::UI::Windowing::DisplayArea::GetFromDisplayId( + m_bridge.SiteView().EnvironmentView().DisplayId()); + auto workArea = displayArea.WorkArea(); + + float scale = m_reactNativeIsland.Island().RasterizationScale(); + + winrt::Microsoft::ReactNative::LayoutConstraints constraints; + constraints.MinimumSize = {0, 0}; + // Constrain the size of the modal to 90% of the screen size + constraints.MaximumSize = { + static_cast((workArea.Width / scale) * 0.9), static_cast((workArea.Height / scale) * 0.9)}; + constraints.LayoutDirection = winrt::Microsoft::ReactNative::LayoutDirection::Undefined; + + auto layoutDirection = m_reactNativeIsland.Island().LayoutDirection(); + if (layoutDirection == winrt::Microsoft::UI::Content::ContentLayoutDirection::LeftToRight) + constraints.LayoutDirection = winrt::Microsoft::ReactNative::LayoutDirection::LeftToRight; + else if (layoutDirection == winrt::Microsoft::UI::Content::ContentLayoutDirection::RightToLeft) + constraints.LayoutDirection = winrt::Microsoft::ReactNative::LayoutDirection::RightToLeft; + + // By setting a custom contraint here the behavior of the modal slightly changes. + // When no constraint is set (maxSize is std::numeric_limits::infinity()), yoga will layout the content to a + // desired size If we provide a specific max size, then contents with a flex:1 will expand to fill that size. We + // might want to provide a windows specific property to control this behavior. + m_state.UpdateState(winrt::make(constraints, m_reactNativeIsland.Island().RasterizationScale())); + } + static void TrySetFocus(const winrt::Microsoft::ReactNative::ComponentView &view) { auto focusController = winrt::Microsoft::UI::Input::InputFocusController::GetForIsland( view.as().Root().ReactNativeIsland().Island()); @@ -222,10 +383,16 @@ struct ModalHostView : public winrt::implementsMountChildComponentView(childComponentView, index); + if (m_builder && m_builder->MountChildComponentViewHandler()) { + m_builder->MountChildComponentViewHandler()( + *this, + winrt::make( + childComponentView, index)); + } } void PortalComponentView::UnmountChildComponentView( const winrt::Microsoft::ReactNative::ComponentView &childComponentView, uint32_t index) noexcept { + if (m_builder && m_builder->UnmountChildComponentViewHandler()) { + m_builder->UnmountChildComponentViewHandler()( + *this, + winrt::make( + childComponentView, index)); + } m_rootComponentView->UnmountChildComponentView(childComponentView, index); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp index f7c96535343..83b25e3c727 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.cpp @@ -3,6 +3,7 @@ #include "pch.h" #include "ReactCompositionViewComponentBuilder.h" +#include #include #include #include @@ -128,7 +129,7 @@ void ReactCompositionViewComponentBuilder::SetPortalComponentViewInitializer( }; m_descriptorConstructorFactory = []() { return &facebook::react::concreteComponentDescriptorConstructor< - ::Microsoft::ReactNative::AbiViewComponentDescriptor>; + ::Microsoft::ReactNative::AbiPortalComponentDescriptor>; }; } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h index 852d08a8cd8..f9d9ab9a227 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h @@ -3,7 +3,6 @@ // Licensed under the MIT License. #include -#include #include #include #include "winrt/Microsoft.ReactNative.Composition.Experimental.h" diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp index 594652018cb..33812dab916 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp @@ -906,21 +906,23 @@ winrt::Microsoft::UI::Content::ContentIsland ReactNativeIsland::Island() { } }); #ifdef USE_EXPERIMENTAL_WINUI3 - m_islandConnectedToken = m_island.Connected( - [weakThis = get_weak()]( - winrt::IInspectable const &, winrt::Microsoft::UI::Content::ContentIsland const &island) { - if (auto pThis = weakThis.get()) { - pThis->OnMounted(); - } - }); + if (!m_isFragment) { + m_islandConnectedToken = m_island.Connected( + [weakThis = get_weak()]( + winrt::IInspectable const &, winrt::Microsoft::UI::Content::ContentIsland const &island) { + if (auto pThis = weakThis.get()) { + pThis->OnMounted(); + } + }); - m_islandDisconnectedToken = m_island.Disconnected( - [weakThis = get_weak()]( - winrt::IInspectable const &, winrt::Microsoft::UI::Content::ContentIsland const &island) { - if (auto pThis = weakThis.get()) { - pThis->OnUnmounted(); - } - }); + m_islandDisconnectedToken = m_island.Disconnected( + [weakThis = get_weak()]( + winrt::IInspectable const &, winrt::Microsoft::UI::Content::ContentIsland const &island) { + if (auto pThis = weakThis.get()) { + pThis->OnUnmounted(); + } + }); + } #endif } return m_island; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp index 8cb42b9226e..a9ebbeae7fc 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp @@ -76,7 +76,9 @@ void RootComponentView::updateLayoutMetrics( winrt::Microsoft::ReactNative::ComponentView RootComponentView::GetFocusedComponent() noexcept { return m_focusedComponent; } -void RootComponentView::SetFocusedComponent(const winrt::Microsoft::ReactNative::ComponentView &value) noexcept { +void RootComponentView::SetFocusedComponent( + const winrt::Microsoft::ReactNative::ComponentView &value, + winrt::Microsoft::ReactNative::FocusNavigationDirection direction) noexcept { if (m_focusedComponent == value) return; @@ -90,7 +92,7 @@ void RootComponentView::SetFocusedComponent(const winrt::Microsoft::ReactNative: if (auto rootView = m_wkRootView.get()) { winrt::get_self(rootView)->TrySetFocus(); } - auto args = winrt::make(value); + auto args = winrt::make(value, direction); winrt::get_self(value)->onGotFocus(args); } @@ -151,7 +153,7 @@ bool RootComponentView::TrySetFocusedComponent( winrt::get_self(losingFocusArgs.NewFocusedComponent()) ->rootComponentView() - ->SetFocusedComponent(gettingFocusArgs.NewFocusedComponent()); + ->SetFocusedComponent(gettingFocusArgs.NewFocusedComponent(), direction); } return true; @@ -241,7 +243,7 @@ void RootComponentView::start(const winrt::Microsoft::ReactNative::ReactNativeIs } void RootComponentView::stop() noexcept { - SetFocusedComponent(nullptr); + SetFocusedComponent(nullptr, winrt::Microsoft::ReactNative::FocusNavigationDirection::None); if (m_visualAddedToIsland) { if (auto rootView = m_wkRootView.get()) { winrt::get_self(rootView)->RemoveRenderedVisual( diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h index 7790d171693..59102f2f744 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h @@ -27,7 +27,9 @@ struct RootComponentView : RootComponentViewT true - - true - true @@ -174,6 +171,9 @@ true + + true + true diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 870ccbd2f87..ae70481500e 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -239,7 +239,6 @@ - @@ -293,6 +292,7 @@ + diff --git a/vnext/codegen/react/components/rnwcore/ActivityIndicatorView.g.h b/vnext/codegen/react/components/rnwcore/ActivityIndicatorView.g.h index 1a058c412ac..282e5a2d5fd 100644 --- a/vnext/codegen/react/components/rnwcore/ActivityIndicatorView.g.h +++ b/vnext/codegen/react/components/rnwcore/ActivityIndicatorView.g.h @@ -159,7 +159,7 @@ void RegisterActivityIndicatorViewNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h b/vnext/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h index b421af61211..64ff75203fb 100644 --- a/vnext/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h +++ b/vnext/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h @@ -236,7 +236,7 @@ void RegisterAndroidDrawerLayoutNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h b/vnext/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h index 4e645647d83..f0fa905730c 100644 --- a/vnext/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h +++ b/vnext/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h @@ -147,7 +147,7 @@ void RegisterAndroidHorizontalScrollContentViewNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/AndroidProgressBar.g.h b/vnext/codegen/react/components/rnwcore/AndroidProgressBar.g.h index 0a1f55c6b60..15854240ec3 100644 --- a/vnext/codegen/react/components/rnwcore/AndroidProgressBar.g.h +++ b/vnext/codegen/react/components/rnwcore/AndroidProgressBar.g.h @@ -171,7 +171,7 @@ void RegisterAndroidProgressBarNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/AndroidSwipeRefreshLayout.g.h b/vnext/codegen/react/components/rnwcore/AndroidSwipeRefreshLayout.g.h index 7785a25dab8..e48471f23d6 100644 --- a/vnext/codegen/react/components/rnwcore/AndroidSwipeRefreshLayout.g.h +++ b/vnext/codegen/react/components/rnwcore/AndroidSwipeRefreshLayout.g.h @@ -191,7 +191,7 @@ void RegisterAndroidSwipeRefreshLayoutNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/AndroidSwitch.g.h b/vnext/codegen/react/components/rnwcore/AndroidSwitch.g.h index 8493270876c..b367d2bb0f5 100644 --- a/vnext/codegen/react/components/rnwcore/AndroidSwitch.g.h +++ b/vnext/codegen/react/components/rnwcore/AndroidSwitch.g.h @@ -208,7 +208,7 @@ void RegisterAndroidSwitchNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/DebuggingOverlay.g.h b/vnext/codegen/react/components/rnwcore/DebuggingOverlay.g.h index f5590ea2446..7d2c246120d 100644 --- a/vnext/codegen/react/components/rnwcore/DebuggingOverlay.g.h +++ b/vnext/codegen/react/components/rnwcore/DebuggingOverlay.g.h @@ -175,7 +175,7 @@ void RegisterDebuggingOverlayNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/InputAccessory.g.h b/vnext/codegen/react/components/rnwcore/InputAccessory.g.h index d91a3e7e0ef..ba9b07bc9d4 100644 --- a/vnext/codegen/react/components/rnwcore/InputAccessory.g.h +++ b/vnext/codegen/react/components/rnwcore/InputAccessory.g.h @@ -147,7 +147,7 @@ void RegisterInputAccessoryNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/ModalHostView.g.h b/vnext/codegen/react/components/rnwcore/ModalHostView.g.h index 7a29d82ef25..eb142c91fa4 100644 --- a/vnext/codegen/react/components/rnwcore/ModalHostView.g.h +++ b/vnext/codegen/react/components/rnwcore/ModalHostView.g.h @@ -226,7 +226,7 @@ void RegisterModalHostViewNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/PullToRefreshView.g.h b/vnext/codegen/react/components/rnwcore/PullToRefreshView.g.h index 52a05536f56..ce7bd99dc91 100644 --- a/vnext/codegen/react/components/rnwcore/PullToRefreshView.g.h +++ b/vnext/codegen/react/components/rnwcore/PullToRefreshView.g.h @@ -187,7 +187,7 @@ void RegisterPullToRefreshViewNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/SafeAreaView.g.h b/vnext/codegen/react/components/rnwcore/SafeAreaView.g.h index 1b7877916ea..71570085328 100644 --- a/vnext/codegen/react/components/rnwcore/SafeAreaView.g.h +++ b/vnext/codegen/react/components/rnwcore/SafeAreaView.g.h @@ -144,7 +144,7 @@ void RegisterSafeAreaViewNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/Switch.g.h b/vnext/codegen/react/components/rnwcore/Switch.g.h index 194aefb0edb..4fe657d83ca 100644 --- a/vnext/codegen/react/components/rnwcore/Switch.g.h +++ b/vnext/codegen/react/components/rnwcore/Switch.g.h @@ -204,7 +204,7 @@ void RegisterSwitchNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/codegen/react/components/rnwcore/UnimplementedNativeView.g.h b/vnext/codegen/react/components/rnwcore/UnimplementedNativeView.g.h index 57d260ce6e7..9a72f246500 100644 --- a/vnext/codegen/react/components/rnwcore/UnimplementedNativeView.g.h +++ b/vnext/codegen/react/components/rnwcore/UnimplementedNativeView.g.h @@ -147,7 +147,7 @@ void RegisterUnimplementedNativeViewNativeComponent( builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &view, const winrt::Microsoft::ReactNative::IComponentState &newState) noexcept { auto userData = view.UserData().as(); - userData->member(view, newState); + userData->UpdateState(view, newState); }); } diff --git a/vnext/src-win/Libraries/Modal/Modal.windows.js b/vnext/src-win/Libraries/Modal/Modal.windows.js index bee411a14df..7f0068f0b55 100644 --- a/vnext/src-win/Libraries/Modal/Modal.windows.js +++ b/vnext/src-win/Libraries/Modal/Modal.windows.js @@ -274,8 +274,11 @@ class Modal extends React.Component { } } + // [Windows] - apply empty rootViewStyle to AppContainer to prevent modal from always expanding to fill available space const innerChildren = __DEV__ ? ( - {this.props.children} + + {this.props.children} + ) : ( this.props.children );