diff --git a/packages/e2e-test-app/windows/RNTesterApp/RNTesterApp.csproj b/packages/e2e-test-app/windows/RNTesterApp/RNTesterApp.csproj
index a92232a0f26..e84397943fc 100644
--- a/packages/e2e-test-app/windows/RNTesterApp/RNTesterApp.csproj
+++ b/packages/e2e-test-app/windows/RNTesterApp/RNTesterApp.csproj
@@ -24,6 +24,7 @@
true
false
7.3
+ false
$(AssetTargetFallback);native
diff --git a/packages/e2e-test-app/windows/RNTesterApp/packages.lock.json b/packages/e2e-test-app/windows/RNTesterApp/packages.lock.json
index 3ac0f02233b..0f87de10120 100644
--- a/packages/e2e-test-app/windows/RNTesterApp/packages.lock.json
+++ b/packages/e2e-test-app/windows/RNTesterApp/packages.lock.json
@@ -24,7 +24,7 @@
"type": "Direct",
"requested": "[0.11.0-ms.6, )",
"resolved": "0.11.0-ms.6",
- "contentHash": "7KGeDHh4QR4ua5+aSNAfuhj1sF2PBJbTHJ9m520xo1GZZRW4cxJlyNDNjW5t/sFGeHWw/Uhs7ZrWE2maL9BOEw=="
+ "contentHash": "WAVLsSZBV4p/3hNC3W67su7xu3f/ZMSKxu0ON7g2GaKRbkJmH0Qyif1IlzcJwtvR48kuOdfgPu7Bgtz3AY+gqg=="
},
"XamlTreeDump": {
"type": "Direct",
@@ -32,6 +32,11 @@
"resolved": "1.0.9",
"contentHash": "rvh/RZghhSG28PDL1dw56nTZRN0/ViV2TIja/ykU9FHn0gtM8pwtgD8Ebo1nobu0QnSjn8Cg6Ncu39VV19rkrw=="
},
+ "boost": {
+ "type": "Transitive",
+ "resolved": "1.76.0",
+ "contentHash": "p+w3YvNdXL8Cu9Fzrmexssu0tZbWxuf6ywsQqHjDlKFE5ojXHof1HIyMC3zDLfLnh80dIeFcEUAuR2Asg/XHRA=="
+ },
"Microsoft.Net.Native.Compiler": {
"type": "Transitive",
"resolved": "2.2.7-rel-27913-00",
@@ -58,6 +63,16 @@
"resolved": "2.1.0",
"contentHash": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA=="
},
+ "Microsoft.Windows.CppWinRT": {
+ "type": "Transitive",
+ "resolved": "2.0.211028.7",
+ "contentHash": "JBGI0c3WLoU6aYJRy9Qo0MLDQfObEp+d4nrhR95iyzf7+HOgjRunHDp/6eGFREd7xq3OI1mll9ecJrMfzBvlyg=="
+ },
+ "Microsoft.Windows.SDK.BuildTools": {
+ "type": "Transitive",
+ "resolved": "10.0.22000.194",
+ "contentHash": "4L0P3zqut466SIqT3VBeLTNUQTxCBDOrTRymRuROCRJKazcK7ibLz9yAO1nKWRt50ttCj39oAa2Iuz9ZTDmLlg=="
+ },
"NETStandard.Library": {
"type": "Transitive",
"resolved": "2.0.3",
@@ -134,11 +149,39 @@
"contentHash": "qF6RRZKaflI+LR1YODNyWYjq5YoX8IJ2wx5y8O+AW2xO+1t/Q6Mm+jQ38zJbWnmXbrcOqUYofn7Y3/KC6lTLBQ=="
},
"automationchannel": {
+ "type": "Project",
+ "dependencies": {
+ "Microsoft.ReactNative": "1.0.0",
+ "Microsoft.UI.Xaml": "2.7.0",
+ "Microsoft.Windows.CppWinRT": "2.0.211028.7"
+ }
+ },
+ "common": {
"type": "Project"
},
- "microsoft.reactnative": {
+ "fmt": {
"type": "Project"
},
+ "folly": {
+ "type": "Project",
+ "dependencies": {
+ "boost": "1.76.0",
+ "fmt": "1.0.0"
+ }
+ },
+ "microsoft.reactnative": {
+ "type": "Project",
+ "dependencies": {
+ "Common": "1.0.0",
+ "Folly": "1.0.0",
+ "Microsoft.UI.Xaml": "2.7.0",
+ "Microsoft.Windows.CppWinRT": "2.0.211028.7",
+ "Microsoft.Windows.SDK.BuildTools": "10.0.22000.194",
+ "ReactCommon": "1.0.0",
+ "ReactNative.Hermes.Windows": "0.11.0-ms.6",
+ "boost": "1.76.0"
+ }
+ },
"microsoft.reactnative.managed": {
"type": "Project",
"dependencies": {
@@ -146,16 +189,25 @@
"Microsoft.ReactNative": "1.0.0"
}
},
+ "reactcommon": {
+ "type": "Project",
+ "dependencies": {
+ "Folly": "1.0.0",
+ "boost": "1.76.0"
+ }
+ },
"reactnativepicker": {
"type": "Project",
"dependencies": {
- "Microsoft.ReactNative": "1.0.0"
+ "Microsoft.ReactNative": "1.0.0",
+ "Microsoft.UI.Xaml": "2.7.0"
}
},
"reactnativexaml": {
"type": "Project",
"dependencies": {
- "Microsoft.ReactNative": "1.0.0"
+ "Microsoft.ReactNative": "1.0.0",
+ "Microsoft.UI.Xaml": "2.7.0"
}
}
},
diff --git a/vnext/Microsoft.ReactNative.Managed/packages.lock.json b/vnext/Microsoft.ReactNative.Managed/packages.lock.json
index decd73cbdce..85237bbbcfe 100644
--- a/vnext/Microsoft.ReactNative.Managed/packages.lock.json
+++ b/vnext/Microsoft.ReactNative.Managed/packages.lock.json
@@ -58,7 +58,7 @@
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "2.1.0",
- "contentHash": "GmkKfoyerqmsHMn7OZj0AKpcBabD+GaafqphvX2Mw406IwiJRy1pKcKqdCfKJfYmkRyJ6+e+RaUylgdJoDa1jQ=="
+ "contentHash": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
@@ -83,7 +83,7 @@
"NETStandard.Library": {
"type": "Transitive",
"resolved": "2.0.3",
- "contentHash": "548M6mnBSJWxsIlkQHfbzoYxpiYFXZZSL00p4GHYv8PkiqFBnnT68mW5mGEsA/ch9fDO9GkPgkFQpWiXZN7mAQ==",
+ "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0"
}
diff --git a/vnext/Microsoft.ReactNative/ABIViewManager.cpp b/vnext/Microsoft.ReactNative/ABIViewManager.cpp
index ec29aafa053..6b57d6e24f1 100644
--- a/vnext/Microsoft.ReactNative/ABIViewManager.cpp
+++ b/vnext/Microsoft.ReactNative/ABIViewManager.cpp
@@ -13,8 +13,124 @@
#include
#include "ReactHost/MsoUtils.h"
+#include "NativeMeasuringPanel.g.h"
+#include
+#include
+#include
+
+namespace winrt::Microsoft::ReactNative::implementation {
+struct NativeMeasuringPanel : NativeMeasuringPanelT {
+ using super = xaml::Controls::Grid;
+ NativeMeasuringPanel() = default;
+
+ static YGSize
+ SelfMeasureFunc(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode) {
+ auto context = reinterpret_cast<::Microsoft::ReactNative::YogaContext *>(YGNodeGetContext(node));
+
+ // TODO: VEC context != nullptr, DefaultYogaSelfMeasureFunc expects a context.
+
+ auto view = context->view;
+ auto element = view.as();
+
+ float constrainToWidth =
+ widthMode == YGMeasureMode::YGMeasureModeUndefined ? std::numeric_limits::max() : width;
+ float constrainToHeight =
+ heightMode == YGMeasureMode::YGMeasureModeUndefined ? std::numeric_limits::max() : height;
+ winrt::Windows::Foundation::Size availableSpace(constrainToWidth, constrainToHeight);
+ element.Measure(availableSpace);
+
+ YGSize desiredSize{element.DesiredSize().Width, element.DesiredSize().Height};
+
+ return desiredSize;
+ }
+
+ static implementation::NativeMeasuringPanel* GetFromWrapped(::Microsoft::ReactNative::XamlView view)
+ {
+ if (auto grid = view.try_as()) {
+ if (grid.Children().Size() == 1) {
+ if (auto nmp = grid.Children().GetAt(0).try_as()) {
+ return winrt::get_self(nmp);
+ }
+ }
+ }
+ return nullptr;
+ }
+
+ Size MeasureOverride(Size const &available) {
+ if (Children().Size() == 0) {
+ return {};
+ }
+ auto child = Children().GetAt(0).as();
+
+ const auto ret = NativeMeasuringPanelT::MeasureOverride(available);
+ const bool wasMeasuring = m_isMeasuring;
+ if (ret != m_last && (ret.Width * ret.Height) != 0 && !m_isMeasuring) {
+ m_isMeasuring = true;
+ m_last = ret;
+ if (auto nativeParent = this->Parent()) {
+ nativeParent.ClearValue(xaml::FrameworkElement::WidthProperty());
+ nativeParent.ClearValue(xaml::FrameworkElement::HeightProperty());
+ }
+
+ auto x = 0;
+ auto &ctx = m_shadowNode->GetViewManager()->GetReactContext();
+ if (auto nativeUIManager = ::Microsoft::ReactNative::GetNativeUIManager(ctx).lock()) {
+ nativeUIManager->DirtyYogaNode(m_shadowNode->m_tag);
+ nativeUIManager->ensureInBatch();
+ nativeUIManager->onBatchComplete();
+ }
+ m_isMeasuring = false;
+ }
+ cdebug << L"MeasureOverride " << winrt::get_class_name(child).c_str() << L" available (" << available.Width << L", "
+ << available.Height << L")" << L" (" << m_last.Width << L", " << m_last.Height << L")" << L" measuring = "
+ << wasMeasuring << L"\n\n";
+ return ret;
+ }
+
+ Size m_last{};
+ ::Microsoft::ReactNative::ShadowNodeBase *m_shadowNode{nullptr};
+ bool m_isMeasuring{false};
+};
+} // namespace winrt::Microsoft::ReactNative::implementation
+
+namespace winrt::Microsoft::ReactNative::factory_implementation {
+struct NativeMeasuringPanel : NativeMeasuringPanelT {};
+} // namespace winrt::Microsoft::ReactNative::factory_implementation
+
+#if __has_include("NativeMeasuringPanel.g.cpp")
+#include "NativeMeasuringPanel.g.cpp"
+#endif
+
+
namespace winrt::Microsoft::ReactNative {
+
+xaml::DependencyObject WrapNativeView(xaml::DependencyObject created) {
+ auto panel = NativeMeasuringPanel();
+ panel.Children().Append(created.as());
+
+ auto grid = xaml::Controls::Grid();
+ grid.Children().Append(panel);
+#ifdef DEBUG
+ panel.Name(L"Measuring panel for native view");
+ grid.Name(L"Wrapped native view");
+#endif
+ return grid;
+}
+
+xaml::DependencyObject UnwrapNativeView(xaml::DependencyObject o) {
+ if (auto grid = o.try_as()) {
+ if (grid.Children().Size() == 1) {
+ if (auto panel = grid.Children().GetAt(0).try_as()) {
+ if (panel.Children().Size() == 1) {
+ return panel.Children().GetAt(0);
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
class ABIShadowNode : public ::Microsoft::ReactNative::ShadowNodeBase {
using Super = ShadowNodeBase;
@@ -24,6 +140,14 @@ class ABIShadowNode : public ::Microsoft::ReactNative::ShadowNodeBase {
return m_needsForceLayout;
}
+ void createView(const winrt::Microsoft::ReactNative::JSValueObject &props) override {
+ Super::createView(props);
+
+ auto nmp = implementation::NativeMeasuringPanel::GetFromWrapped(m_view);
+
+ nmp->m_shadowNode = this;
+ }
+
private:
bool m_needsForceLayout;
};
@@ -55,15 +179,21 @@ const wchar_t *ABIViewManager::GetName() const {
return m_name.c_str();
}
+
+
xaml::DependencyObject ABIViewManager::CreateViewCore(
int64_t,
const winrt::Microsoft::ReactNative::JSValueObject &props) {
+ xaml::DependencyObject created{nullptr};
if (auto viewCreateProps = m_viewManager.try_as()) {
auto view = viewCreateProps.CreateViewWithProperties(
MakeJSValueTreeReader(winrt::Microsoft::ReactNative::JSValue(props.Copy())));
- return view.as();
+ created = view.as();
+ } else {
+ created = m_viewManager.CreateView();
}
- return m_viewManager.CreateView();
+
+ return WrapNativeView(created);
}
void ABIViewManager::GetExportedViewConstants(const winrt::Microsoft::ReactNative::IJSValueWriter &writer) const {
@@ -118,7 +248,7 @@ void ABIViewManager::UpdateProperties(
}
if (m_viewManagerWithNativeProperties) {
- auto view = nodeToUpdate->GetView().as();
+ auto view = UnwrapNativeView(nodeToUpdate->GetView()).as();
if (props.size() > 0) {
m_viewManagerWithNativeProperties.UpdateProperties(
@@ -178,10 +308,19 @@ void ABIViewManager::GetExportedCustomDirectEventTypeConstants(
}
void ABIViewManager::AddView(const xaml::DependencyObject &parent, const xaml::DependencyObject &child, int64_t index) {
+ auto parentView = UnwrapNativeView(parent);
+ auto childView = UnwrapNativeView(child);
+ if (!parentView || !childView) {
+ assert(false && "failed to unwrap a native view");
+ return;
+ }
+ auto childNMP = child.as().Children().GetAt(0).as();
+ childNMP.Children().Clear(); // need to unparent the real element before we parent it
+
if (m_viewManagerWithChildren) {
- m_viewManagerWithChildren.AddView(parent.as(), child.as(), index);
+ m_viewManagerWithChildren.AddView(parentView.as(), childView.as(), index);
} else {
- Super::AddView(parent, child, index);
+ Super::AddView(parentView, childView, index);
}
}
@@ -215,7 +354,7 @@ void ABIViewManager::ReplaceChild(
YGMeasureFunc ABIViewManager::GetYogaCustomMeasureFunc() const {
if (m_viewManagerRequiresNativeLayout && m_viewManagerRequiresNativeLayout.RequiresNativeLayout()) {
- return ::Microsoft::ReactNative::DefaultYogaSelfMeasureFunc;
+ return implementation::NativeMeasuringPanel::SelfMeasureFunc;
} else {
return nullptr;
}
diff --git a/vnext/Microsoft.ReactNative/Views/ViewManagerBase.cpp b/vnext/Microsoft.ReactNative/Views/ViewManagerBase.cpp
index fbb4d17cd09..362e95e47d6 100644
--- a/vnext/Microsoft.ReactNative/Views/ViewManagerBase.cpp
+++ b/vnext/Microsoft.ReactNative/Views/ViewManagerBase.cpp
@@ -325,6 +325,19 @@ void ViewManagerBase::NotifyUnimplementedProperty(
#endif // DEBUG
}
+static xaml::DependencyObject UnwrapNativeView(xaml::DependencyObject o) {
+ if (auto grid = o.try_as()) {
+ if (grid.Children().Size() == 1) {
+ if (auto panel = grid.Children().GetAt(0).try_as()) {
+ if (panel.Children().Size() == 1) {
+ return panel.Children().GetAt(0);
+ }
+ }
+ }
+ }
+ return nullptr;
+}
+
void ViewManagerBase::SetLayoutProps(
ShadowNodeBase &nodeToUpdate,
const XamlView &viewToUpdate,
@@ -343,6 +356,10 @@ void ViewManagerBase::SetLayoutProps(
}
}
+ if (auto unwrapped = UnwrapNativeView(viewToUpdate)) {
+ auto cn = winrt::get_class_name(unwrapped);
+ }
+
auto element = viewToUpdate.as();
if (element == nullptr) {
// TODO: Assert
diff --git a/vnext/Microsoft.ReactNative/Views/cppwinrt/ViewPanel.idl b/vnext/Microsoft.ReactNative/Views/cppwinrt/ViewPanel.idl
index caee217953f..29a03dc8e37 100644
--- a/vnext/Microsoft.ReactNative/Views/cppwinrt/ViewPanel.idl
+++ b/vnext/Microsoft.ReactNative/Views/cppwinrt/ViewPanel.idl
@@ -53,4 +53,12 @@ namespace Microsoft.ReactNative
ViewPanel GetPanel();
}
+
+
+ [default_interface]
+ [webhosthidden]
+ runtimeclass NativeMeasuringPanel : XAML_NAMESPACE.Controls.Grid {
+ NativeMeasuringPanel();
+ }
+
}