react-native-windows 0.74.5 → 0.74.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/Microsoft.ReactNative/ComponentView.idl +26 -0
  2. package/Microsoft.ReactNative/CompositionComponentView.idl +2 -0
  3. package/Microsoft.ReactNative/CompositionRootView.idl +13 -3
  4. package/Microsoft.ReactNative/Fabric/ComponentView.cpp +83 -12
  5. package/Microsoft.ReactNative/Fabric/ComponentView.h +33 -2
  6. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +6 -6
  7. package/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.cpp +3 -2
  8. package/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp +131 -79
  9. package/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h +20 -13
  10. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +37 -23
  11. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +8 -2
  12. package/Microsoft.ReactNative/Fabric/Composition/FocusManager.cpp +152 -0
  13. package/Microsoft.ReactNative/Fabric/Composition/FocusManager.h +85 -0
  14. package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp +54 -40
  15. package/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h +9 -1
  16. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +7 -5
  17. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +2 -2
  18. package/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp +6 -12
  19. package/Microsoft.ReactNative/Fabric/FabricUIManagerModule.h +1 -0
  20. package/Microsoft.ReactNative/FocusManager.idl +22 -0
  21. package/PropertySheets/Generated/PackageVersion.g.props +3 -3
  22. package/Shared/Shared.vcxitems +6 -5
  23. package/package.json +1 -1
  24. package/templates/cpp-app/windows/MyApp/MyApp.cpp +3 -2
  25. package/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp +0 -149
  26. package/Microsoft.ReactNative/Views/ICompositionRootView.h +0 -18
@@ -67,6 +67,26 @@ namespace Microsoft.ReactNative
67
67
  IReactContext ReactContext { get;};
68
68
  };
69
69
 
70
+ [experimental]
71
+ [webhosthidden]
72
+ runtimeclass LosingFocusEventArgs : Microsoft.ReactNative.Composition.Input.RoutedEventArgs {
73
+ Microsoft.ReactNative.ComponentView NewFocusedComponent { get; };
74
+ Microsoft.ReactNative.ComponentView OldFocusedComponent { get; };
75
+
76
+ void TryCancel();
77
+ void TrySetNewFocusedComponent(Microsoft.ReactNative.ComponentView component);
78
+ };
79
+
80
+ [experimental]
81
+ [webhosthidden]
82
+ runtimeclass GettingFocusEventArgs : Microsoft.ReactNative.Composition.Input.RoutedEventArgs {
83
+ Microsoft.ReactNative.ComponentView NewFocusedComponent { get; };
84
+ Microsoft.ReactNative.ComponentView OldFocusedComponent { get; };
85
+
86
+ void TryCancel();
87
+ void TrySetNewFocusedComponent(Microsoft.ReactNative.ComponentView component);
88
+ };
89
+
70
90
  [experimental]
71
91
  [webhosthidden]
72
92
  unsealed runtimeclass ComponentView {
@@ -101,6 +121,12 @@ namespace Microsoft.ReactNative
101
121
  overridable void OnPointerExited(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args);
102
122
  overridable void OnPointerCaptureLost();
103
123
 
124
+ Boolean TryFocus();
125
+
126
+ event Windows.Foundation.EventHandler<LosingFocusEventArgs> LosingFocus;
127
+ event Windows.Foundation.EventHandler<GettingFocusEventArgs> GettingFocus;
128
+ event Windows.Foundation.EventHandler<Microsoft.ReactNative.Composition.Input.RoutedEventArgs> LostFocus;
129
+ event Windows.Foundation.EventHandler<Microsoft.ReactNative.Composition.Input.RoutedEventArgs> GotFocus;
104
130
  };
105
131
 
106
132
  } // namespace Microsoft.ReactNative
@@ -48,6 +48,7 @@ namespace Microsoft.ReactNative.Composition
48
48
  ComponentView(CreateCompositionComponentViewArgs args);
49
49
 
50
50
  Microsoft.UI.Composition.Compositor Compositor { get; };
51
+ RootComponentView Root { get; };
51
52
  Theme Theme;
52
53
  overridable void OnThemeChanged();
53
54
  Boolean CapturePointer(Microsoft.ReactNative.Composition.Input.Pointer pointer);
@@ -88,6 +89,7 @@ namespace Microsoft.ReactNative.Composition
88
89
  [webhosthidden]
89
90
  [default_interface]
90
91
  unsealed runtimeclass RootComponentView : ViewComponentView {
92
+ Microsoft.ReactNative.ComponentView GetFocusedComponent();
91
93
  };
92
94
 
93
95
  [experimental]
@@ -5,6 +5,7 @@ import "IJSValueWriter.idl";
5
5
  import "ReactCoreInjection.idl";
6
6
  import "ReactNativeHost.idl";
7
7
  import "Theme.idl";
8
+ import "IReactViewComponentBuilder.idl";
8
9
  #include "NamespaceRedirect.h"
9
10
  #include "DocString.h"
10
11
 
@@ -61,6 +62,13 @@ namespace Microsoft.ReactNative
61
62
  }
62
63
  }
63
64
 
65
+ [default_interface]
66
+ [webhosthidden]
67
+ [experimental]
68
+ runtimeclass RootViewSizeChangedEventArgs {
69
+ Windows.Foundation.Size Size { get; };
70
+ }
71
+
64
72
  [default_interface]
65
73
  [webhosthidden]
66
74
  [experimental]
@@ -82,7 +90,7 @@ namespace Microsoft.ReactNative
82
90
  DOC_STRING("The RootVisual associated with the @CompositionRootView. It must be set to show any React UI elements.")
83
91
  Microsoft.UI.Composition.Visual RootVisual { get; };
84
92
 
85
- Windows.Foundation.Size Size {get; set; };
93
+ Windows.Foundation.Size Size { get; };
86
94
 
87
95
  DOC_STRING("ScaleFactor for this windows (DPI/96)")
88
96
  Single ScaleFactor {get; set;};
@@ -90,8 +98,8 @@ namespace Microsoft.ReactNative
90
98
  DOC_STRING("Move focus to this @CompositionRootView")
91
99
  FocusNavigationResult NavigateFocus(FocusNavigationRequest request);
92
100
 
93
- Windows.Foundation.Size Measure(Windows.Foundation.Size availableSize);
94
- Windows.Foundation.Size Arrange(Windows.Foundation.Size availableSize);
101
+ Windows.Foundation.Size Measure(LayoutConstraints layoutConstraints, Windows.Foundation.Point viewportOffset);
102
+ void Arrange(LayoutConstraints layoutConstraints, Windows.Foundation.Point viewportOffset);
95
103
 
96
104
  Object GetUiaProvider();
97
105
 
@@ -104,6 +112,8 @@ namespace Microsoft.ReactNative
104
112
  #ifdef USE_WINUI3
105
113
  Microsoft.UI.Content.ContentIsland Island { get; };
106
114
  #endif
115
+
116
+ event Windows.Foundation.EventHandler<RootViewSizeChangedEventArgs> SizeChanged;
107
117
  }
108
118
 
109
119
  } // namespace Microsoft.ReactNative
@@ -143,19 +143,16 @@ ComponentView::rootComponentView() noexcept {
143
143
  }
144
144
 
145
145
  void ComponentView::parent(const winrt::Microsoft::ReactNative::ComponentView &parent) noexcept {
146
- if (!parent) {
147
- auto root = rootComponentView();
148
- winrt::Microsoft::ReactNative::ComponentView view{nullptr};
149
- winrt::check_hresult(
150
- QueryInterface(winrt::guid_of<winrt::Microsoft::ReactNative::ComponentView>(), winrt::put_abi(view)));
151
- if (root && root->GetFocusedComponent() == view) {
152
- root->SetFocusedComponent(nullptr); // TODO need move focus logic - where should focus go?
153
- }
154
- }
155
-
156
146
  if (m_parent != parent) {
147
+ auto oldRootView = rootComponentView();
157
148
  m_rootView = nullptr;
149
+ auto oldParent = m_parent;
158
150
  m_parent = parent;
151
+ if (!parent) {
152
+ if (oldRootView && oldRootView->GetFocusedComponent() == *this) {
153
+ oldRootView->TrySetFocusedComponent(oldParent);
154
+ }
155
+ }
159
156
  if (parent) {
160
157
  theme(winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(parent)->theme());
161
158
  }
@@ -220,9 +217,83 @@ facebook::react::Point ComponentView::getClientOffset() const noexcept {
220
217
  return {};
221
218
  }
222
219
 
223
- void ComponentView::onFocusLost() noexcept {}
220
+ void ComponentView::onLostFocus(
221
+ const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
222
+ m_lostFocusEvent(*this, args);
223
+ if (m_parent) {
224
+ winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_parent)->onLostFocus(args);
225
+ }
226
+ }
227
+
228
+ void ComponentView::onGotFocus(
229
+ const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
230
+ m_gotFocusEvent(*this, args);
231
+ if (m_parent) {
232
+ winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_parent)->onGotFocus(args);
233
+ }
234
+ }
235
+
236
+ void ComponentView::onLosingFocus(const winrt::Microsoft::ReactNative::LosingFocusEventArgs &args) noexcept {
237
+ m_losingFocusEvent(*this, args);
238
+ if (m_parent) {
239
+ winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_parent)->onLosingFocus(args);
240
+ }
241
+ }
242
+
243
+ void ComponentView::onGettingFocus(const winrt::Microsoft::ReactNative::GettingFocusEventArgs &args) noexcept {
244
+ m_gettingFocusEvent(*this, args);
245
+ if (m_parent) {
246
+ winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(m_parent)->onGettingFocus(args);
247
+ }
248
+ }
224
249
 
225
- void ComponentView::onFocusGained() noexcept {}
250
+ winrt::event_token ComponentView::LosingFocus(
251
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::LosingFocusEventArgs> const
252
+ &handler) noexcept {
253
+ return m_losingFocusEvent.add(handler);
254
+ }
255
+
256
+ void ComponentView::LosingFocus(winrt::event_token const &token) noexcept {
257
+ m_losingFocusEvent.remove(token);
258
+ }
259
+
260
+ winrt::event_token ComponentView::GettingFocus(
261
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::GettingFocusEventArgs> const
262
+ &handler) noexcept {
263
+ return m_gettingFocusEvent.add(handler);
264
+ }
265
+
266
+ void ComponentView::GettingFocus(winrt::event_token const &token) noexcept {
267
+ m_gettingFocusEvent.remove(token);
268
+ }
269
+
270
+ winrt::event_token ComponentView::LostFocus(
271
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs> const
272
+ &handler) noexcept {
273
+ return m_lostFocusEvent.add(handler);
274
+ }
275
+
276
+ void ComponentView::LostFocus(winrt::event_token const &token) noexcept {
277
+ m_lostFocusEvent.remove(token);
278
+ }
279
+
280
+ winrt::event_token ComponentView::GotFocus(
281
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs> const
282
+ &handler) noexcept {
283
+ return m_gotFocusEvent.add(handler);
284
+ }
285
+
286
+ void ComponentView::GotFocus(winrt::event_token const &token) noexcept {
287
+ m_gotFocusEvent.remove(token);
288
+ }
289
+
290
+ bool ComponentView::TryFocus() noexcept {
291
+ if (auto root = rootComponentView()) {
292
+ return root->TrySetFocusedComponent(*get_strong());
293
+ }
294
+
295
+ return false;
296
+ }
226
297
 
227
298
  void ComponentView::OnPointerEntered(
228
299
  const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept {}
@@ -87,8 +87,29 @@ struct ComponentView : public ComponentViewT<ComponentView> {
87
87
  virtual RECT getClientRect() const noexcept;
88
88
  // The offset from this elements parent to its children (accounts for things like scroll position)
89
89
  virtual facebook::react::Point getClientOffset() const noexcept;
90
- virtual void onFocusLost() noexcept;
91
- virtual void onFocusGained() noexcept;
90
+ virtual void onLosingFocus(const winrt::Microsoft::ReactNative::LosingFocusEventArgs &args) noexcept;
91
+ virtual void onGettingFocus(const winrt::Microsoft::ReactNative::GettingFocusEventArgs &args) noexcept;
92
+ virtual void onLostFocus(const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept;
93
+ virtual void onGotFocus(const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept;
94
+
95
+ winrt::event_token LosingFocus(
96
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::LosingFocusEventArgs> const
97
+ &handler) noexcept;
98
+ void LosingFocus(winrt::event_token const &token) noexcept;
99
+ winrt::event_token GettingFocus(
100
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::GettingFocusEventArgs> const
101
+ &handler) noexcept;
102
+ void GettingFocus(winrt::event_token const &token) noexcept;
103
+ winrt::event_token LostFocus(
104
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs> const
105
+ &handler) noexcept;
106
+ void LostFocus(winrt::event_token const &token) noexcept;
107
+ winrt::event_token GotFocus(
108
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs> const
109
+ &handler) noexcept;
110
+ void GotFocus(winrt::event_token const &token) noexcept;
111
+
112
+ bool TryFocus() noexcept;
92
113
 
93
114
  virtual bool focusable() const noexcept;
94
115
  virtual facebook::react::SharedViewEventEmitter eventEmitterAtPoint(facebook::react::Point pt) noexcept;
@@ -158,6 +179,16 @@ struct ComponentView : public ComponentViewT<ComponentView> {
158
179
  winrt::Microsoft::ReactNative::ComponentView m_parent{nullptr};
159
180
  winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::ReactNative::ComponentView> m_children{
160
181
  winrt::single_threaded_vector<winrt::Microsoft::ReactNative::ComponentView>()};
182
+ winrt::event<winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::LosingFocusEventArgs>>
183
+ m_losingFocusEvent;
184
+ winrt::event<winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::GettingFocusEventArgs>>
185
+ m_gettingFocusEvent;
186
+ winrt::event<
187
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs>>
188
+ m_lostFocusEvent;
189
+ winrt::event<
190
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs>>
191
+ m_gotFocusEvent;
161
192
  };
162
193
 
163
194
  // Run fn on all nodes of the component view tree starting from this one until fn returns true
@@ -243,7 +243,7 @@ CompositionEventHandler::CompositionEventHandler(
243
243
  : static_cast<facebook::react::Tag>(
244
244
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::CompositionRootView>(
245
245
  strongRootView)
246
- ->GetTag()),
246
+ ->RootTag()),
247
247
  args);
248
248
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
249
249
  onKeyDown(keyboardSource, keyArgs);
@@ -266,7 +266,7 @@ CompositionEventHandler::CompositionEventHandler(
266
266
  : static_cast<facebook::react::Tag>(
267
267
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::CompositionRootView>(
268
268
  strongRootView)
269
- ->GetTag()),
269
+ ->RootTag()),
270
270
  args);
271
271
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
272
272
  onKeyUp(keyboardSource, keyArgs);
@@ -290,7 +290,7 @@ CompositionEventHandler::CompositionEventHandler(
290
290
  : static_cast<facebook::react::Tag>(
291
291
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::CompositionRootView>(
292
292
  strongRootView)
293
- ->GetTag()),
293
+ ->RootTag()),
294
294
  args);
295
295
  auto keyboardSource = winrt::make<CompositionInputKeyboardSource>(source);
296
296
  onCharacterReceived(keyboardSource, charArgs);
@@ -323,7 +323,7 @@ CompositionEventHandler::~CompositionEventHandler() {
323
323
  facebook::react::SurfaceId CompositionEventHandler::SurfaceId() const noexcept {
324
324
  if (auto strongRootView = m_wkRootView.get()) {
325
325
  return static_cast<facebook::react::SurfaceId>(
326
- winrt::get_self<winrt::Microsoft::ReactNative::implementation::CompositionRootView>(strongRootView)->GetTag());
326
+ winrt::get_self<winrt::Microsoft::ReactNative::implementation::CompositionRootView>(strongRootView)->RootTag());
327
327
  }
328
328
  return -1;
329
329
  }
@@ -460,7 +460,7 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
460
460
  : static_cast<facebook::react::Tag>(
461
461
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::CompositionRootView>(
462
462
  strongRootView)
463
- ->GetTag()),
463
+ ->RootTag()),
464
464
  msg,
465
465
  wParam,
466
466
  lParam);
@@ -481,7 +481,7 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
481
481
  : static_cast<facebook::react::Tag>(
482
482
  winrt::get_self<winrt::Microsoft::ReactNative::implementation::CompositionRootView>(
483
483
  strongRootView)
484
- ->GetTag()),
484
+ ->RootTag()),
485
485
  msg,
486
486
  wParam,
487
487
  lParam);
@@ -101,8 +101,9 @@ void CompositionHwndHost::UpdateSize() noexcept {
101
101
  static_cast<float>(m_width / ScaleFactor()), static_cast<float>(m_height / ScaleFactor())};
102
102
  // Do not relayout when minimized
103
103
  if (!IsIconic(m_hwnd)) {
104
- m_compRootView.Size(size);
105
- m_compRootView.Arrange(size);
104
+ winrt::Microsoft::ReactNative::LayoutConstraints constraints;
105
+ constraints.MinimumSize = constraints.MaximumSize = size;
106
+ m_compRootView.Arrange(constraints, {0, 0});
106
107
  }
107
108
  }
108
109
  }
@@ -4,6 +4,7 @@
4
4
  #include "CompositionRootView.h"
5
5
  #include "CompositionRootView.g.cpp"
6
6
  #include "FocusNavigationRequest.g.cpp"
7
+ #include <RootViewSizeChangedEventArgs.g.h>
7
8
 
8
9
  #include <AutoDraw.h>
9
10
  #include <DynamicWriter.h>
@@ -132,6 +133,12 @@ CompositionRootView::CompositionRootView(const winrt::Microsoft::UI::Composition
132
133
  #endif
133
134
 
134
135
  CompositionRootView::~CompositionRootView() noexcept {
136
+ #ifdef USE_WINUI3
137
+ if (m_island && m_island.IsConnected()) {
138
+ m_island.AutomationProviderRequested(m_islandAutomationProviderRequestedToken);
139
+ }
140
+ #endif
141
+
135
142
  if (m_uiDispatcher) {
136
143
  assert(m_uiDispatcher.HasThreadAccess());
137
144
  UninitRootView();
@@ -193,7 +200,7 @@ void CompositionRootView::RemoveRenderedVisual(
193
200
 
194
201
  bool CompositionRootView::TrySetFocus() noexcept {
195
202
  #ifdef USE_WINUI3
196
- if (m_island) {
203
+ if (m_island && m_island.IsConnected()) {
197
204
  auto focusController = winrt::Microsoft::UI::Input::InputFocusController::GetForIsland(m_island);
198
205
  return focusController.TrySetFocus();
199
206
  }
@@ -324,26 +331,6 @@ winrt::IInspectable CompositionRootView::GetUiaProvider() noexcept {
324
331
  return m_uiaProvider;
325
332
  }
326
333
 
327
- std::string CompositionRootView::JSComponentName() const noexcept {
328
- return to_string(m_reactViewOptions.ComponentName());
329
- }
330
-
331
- int64_t CompositionRootView::GetActualHeight() const noexcept {
332
- return static_cast<int64_t>(m_size.Height);
333
- }
334
-
335
- int64_t CompositionRootView::GetActualWidth() const noexcept {
336
- return static_cast<int64_t>(m_size.Width);
337
- }
338
-
339
- int64_t CompositionRootView::GetTag() const noexcept {
340
- return m_rootTag;
341
- }
342
-
343
- void CompositionRootView::SetTag(int64_t tag) noexcept {
344
- m_rootTag = tag;
345
- }
346
-
347
334
  void CompositionRootView::SetWindow(uint64_t hwnd) noexcept {
348
335
  m_hwnd = reinterpret_cast<HWND>(hwnd);
349
336
  }
@@ -439,7 +426,7 @@ void CompositionRootView::UninitRootView() noexcept {
439
426
 
440
427
  auto uiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(
441
428
  winrt::Microsoft::ReactNative::ReactPropertyBag(m_context.Properties()));
442
- uiManager->stopSurface(static_cast<facebook::react::SurfaceId>(GetTag()));
429
+ uiManager->stopSurface(static_cast<facebook::react::SurfaceId>(RootTag()));
443
430
 
444
431
  // This is needed to ensure that the unmount JS logic is completed before the the instance is shutdown during
445
432
  // instance destruction. Aligns with similar code in ReactInstanceWin::DetachRootView for paper Future: Instead this
@@ -478,19 +465,82 @@ void CompositionRootView::ShowInstanceLoaded() noexcept {
478
465
  auto uiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(
479
466
  winrt::Microsoft::ReactNative::ReactPropertyBag(m_context.Properties()));
480
467
 
481
- auto rootTag = ::Microsoft::ReactNative::getNextRootViewTag();
482
- SetTag(rootTag);
468
+ m_rootTag = ::Microsoft::ReactNative::getNextRootViewTag();
483
469
  auto initProps = DynamicWriter::ToDynamic(Mso::Copy(m_reactViewOptions.InitialProps()));
484
470
  if (initProps.isNull()) {
485
471
  initProps = folly::dynamic::object();
486
472
  }
487
473
  initProps["concurrentRoot"] = true;
488
- uiManager->startSurface(*this, rootTag, JSComponentName(), initProps);
474
+ uiManager->startSurface(
475
+ *this,
476
+ static_cast<facebook::react::SurfaceId>(m_rootTag),
477
+ m_layoutConstraints,
478
+ to_string(m_reactViewOptions.ComponentName()),
479
+ initProps);
489
480
 
490
481
  m_isJSViewAttached = true;
491
482
  }
492
483
  }
493
484
 
485
+ facebook::react::AttributedStringBox CreateLoadingAttributedString() noexcept {
486
+ auto attributedString = facebook::react::AttributedString{};
487
+ auto fragment = facebook::react::AttributedString::Fragment{};
488
+ fragment.string = "Loading";
489
+ fragment.textAttributes.fontSize = loadingBarFontSize;
490
+ attributedString.appendFragment(fragment);
491
+ return facebook::react::AttributedStringBox{attributedString};
492
+ }
493
+
494
+ facebook::react::Size MeasureLoading(const facebook::react::LayoutConstraints &layoutConstraints, float scaleFactor) {
495
+ auto attributedStringBox = CreateLoadingAttributedString();
496
+ winrt::com_ptr<::IDWriteTextLayout> textLayout;
497
+ facebook::react::TextLayoutManager::GetTextLayout(
498
+ attributedStringBox, {} /*paragraphAttributes*/, layoutConstraints, textLayout);
499
+
500
+ DWRITE_TEXT_METRICS tm;
501
+ textLayout->GetMetrics(&tm);
502
+
503
+ return layoutConstraints.clamp(
504
+ {loadingActivityHorizontalOffset * scaleFactor + tm.width, loadingBarHeight * scaleFactor});
505
+ }
506
+
507
+ winrt::event_token CompositionRootView::SizeChanged(
508
+ winrt::Windows::Foundation::EventHandler<winrt::Microsoft::ReactNative::RootViewSizeChangedEventArgs> const
509
+ &handler) noexcept {
510
+ return m_sizeChangedEvent.add(handler);
511
+ }
512
+
513
+ void CompositionRootView::SizeChanged(winrt::event_token const &token) noexcept {
514
+ m_sizeChangedEvent.remove(token);
515
+ }
516
+
517
+ struct RootViewSizeChangedEventArgs : RootViewSizeChangedEventArgsT<RootViewSizeChangedEventArgs> {
518
+ RootViewSizeChangedEventArgs(winrt::Windows::Foundation::Size size) : m_size(size) {}
519
+ winrt::Windows::Foundation::Size Size() const noexcept {
520
+ return m_size;
521
+ }
522
+
523
+ private:
524
+ const winrt::Windows::Foundation::Size m_size;
525
+ };
526
+
527
+ void CompositionRootView::NotifySizeChanged() noexcept {
528
+ auto oldSize = m_size;
529
+ facebook::react::Size size;
530
+ auto rootComponentView = GetComponentView();
531
+ if (rootComponentView) {
532
+ size = rootComponentView->layoutMetrics().frame.size;
533
+ } else if (m_loadingVisual) {
534
+ size = MeasureLoading(m_layoutConstraints, m_scaleFactor);
535
+ }
536
+
537
+ m_size = {size.width, size.height};
538
+ UpdateRootVisualSize();
539
+ if (oldSize != m_size) {
540
+ m_sizeChangedEvent(*this, winrt::make<RootViewSizeChangedEventArgs>(m_size));
541
+ }
542
+ }
543
+
494
544
  void CompositionRootView::ShowInstanceError() noexcept {
495
545
  ClearLoadingUI();
496
546
  }
@@ -500,6 +550,9 @@ Composition::Experimental::IDrawingSurfaceBrush CompositionRootView::CreateLoadi
500
550
  winrt::Microsoft::ReactNative::Composition::implementation::CompositionUIService::GetCompositionContext(
501
551
  m_context.Properties().Handle());
502
552
 
553
+ if (m_size.Height == 0 || m_size.Width == 0)
554
+ return nullptr;
555
+
503
556
  winrt::Windows::Foundation::Size surfaceSize = {
504
557
  m_size.Width * m_scaleFactor, std::min(m_size.Height, loadingBarHeight) * m_scaleFactor};
505
558
  auto drawingSurface = compContext.CreateDrawingSurfaceBrush(
@@ -517,12 +570,7 @@ Composition::Experimental::IDrawingSurfaceBrush CompositionRootView::CreateLoadi
517
570
  constraints.maximumSize.width = std::max(0.0f, m_size.Width - loadingTextHorizontalOffset);
518
571
  constraints.maximumSize.height = std::max(0.0f, m_size.Height - loadingBarHeight);
519
572
 
520
- auto attributedString = facebook::react::AttributedString{};
521
- auto fragment = facebook::react::AttributedString::Fragment{};
522
- fragment.string = "Loading";
523
- fragment.textAttributes.fontSize = loadingBarFontSize;
524
- attributedString.appendFragment(fragment);
525
- auto attributedStringBox = facebook::react::AttributedStringBox{attributedString};
573
+ auto attributedStringBox = CreateLoadingAttributedString();
526
574
 
527
575
  auto textAttributes = facebook::react::TextAttributes{};
528
576
  textAttributes.foregroundColor = facebook::react::whiteColor();
@@ -563,10 +611,7 @@ void CompositionRootView::ShowInstanceLoading() noexcept {
563
611
  winrt::Microsoft::ReactNative::Composition::implementation::CompositionUIService::GetCompositionContext(
564
612
  m_context.Properties().Handle());
565
613
 
566
- auto drawingSurface = CreateLoadingVisualBrush();
567
-
568
614
  m_loadingVisual = compContext.CreateSpriteVisual();
569
- m_loadingVisual.Brush(drawingSurface);
570
615
 
571
616
  auto foregroundBrush = compContext.CreateColorBrush({255, 255, 255, 255});
572
617
 
@@ -574,14 +619,28 @@ void CompositionRootView::ShowInstanceLoading() noexcept {
574
619
  m_loadingActivityVisual.Brush(foregroundBrush);
575
620
  m_loadingVisual.InsertAt(m_loadingActivityVisual, 0);
576
621
 
622
+ NotifySizeChanged();
577
623
  UpdateLoadingVisualSize();
578
624
 
579
625
  InternalRootVisual().InsertAt(m_loadingVisual, m_hasRenderedVisual ? 1 : 0);
580
626
  }
581
627
 
628
+ void ApplyConstraints(
629
+ const winrt::Microsoft::ReactNative::LayoutConstraints &layoutConstraintsIn,
630
+ facebook::react::LayoutConstraints &layoutConstraintsOut) noexcept {
631
+ layoutConstraintsOut.minimumSize = {layoutConstraintsIn.MinimumSize.Width, layoutConstraintsIn.MinimumSize.Height};
632
+ layoutConstraintsOut.maximumSize = {layoutConstraintsIn.MaximumSize.Width, layoutConstraintsIn.MaximumSize.Height};
633
+ layoutConstraintsOut.layoutDirection =
634
+ static_cast<facebook::react::LayoutDirection>(layoutConstraintsIn.LayoutDirection);
635
+ }
636
+
582
637
  winrt::Windows::Foundation::Size CompositionRootView::Measure(
583
- winrt::Windows::Foundation::Size const &availableSize) const {
584
- winrt::Windows::Foundation::Size size{0.0f, 0.0f};
638
+ const winrt::Microsoft::ReactNative::LayoutConstraints &layoutConstraints,
639
+ const winrt::Windows::Foundation::Point &viewportOffset) const noexcept {
640
+ facebook::react::Size size{0, 0};
641
+
642
+ facebook::react::LayoutConstraints constraints;
643
+ ApplyConstraints(layoutConstraints, constraints);
585
644
 
586
645
  if (m_isInitialized && m_rootTag != -1) {
587
646
  if (auto fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(
@@ -590,55 +649,39 @@ winrt::Windows::Foundation::Size CompositionRootView::Measure(
590
649
  // TODO scaling factor
591
650
  context.pointScaleFactor = static_cast<facebook::react::Float>(m_scaleFactor);
592
651
  context.fontSizeMultiplier = static_cast<facebook::react::Float>(m_scaleFactor);
652
+ context.viewportOffset = {viewportOffset.X, viewportOffset.Y};
593
653
 
594
- facebook::react::LayoutConstraints constraints;
595
- // TODO should support MinHeight/MinWidth
596
- constraints.minimumSize.height = static_cast<facebook::react::Float>(0.0f);
597
- constraints.minimumSize.width = static_cast<facebook::react::Float>(0.0f);
598
-
599
- // TODO should support MaxHeight/MaxWidth props?
600
- constraints.minimumSize.height = constraints.maximumSize.height =
601
- static_cast<facebook::react::Float>(availableSize.Height);
602
- constraints.minimumSize.width = constraints.maximumSize.width =
603
- static_cast<facebook::react::Float>(availableSize.Width);
604
- // TODO get RTL
605
- constraints.layoutDirection = facebook::react::LayoutDirection::LeftToRight;
606
-
607
- auto yogaSize =
608
- fabricuiManager->measureSurface(static_cast<facebook::react::SurfaceId>(m_rootTag), constraints, context);
609
- return {std::min(yogaSize.width, availableSize.Width), std::min(yogaSize.height, availableSize.Height)};
654
+ size = fabricuiManager->measureSurface(static_cast<facebook::react::SurfaceId>(m_rootTag), constraints, context);
610
655
  }
656
+ } else if (m_loadingVisual) {
657
+ size = MeasureLoading(constraints, m_scaleFactor);
611
658
  }
612
659
 
613
- return size;
660
+ auto clampedSize = constraints.clamp(size);
661
+ return {clampedSize.width, clampedSize.height};
614
662
  }
615
663
 
616
- winrt::Windows::Foundation::Size CompositionRootView::Arrange(winrt::Windows::Foundation::Size finalSize) const {
664
+ void CompositionRootView::Arrange(
665
+ const winrt::Microsoft::ReactNative::LayoutConstraints &layoutConstraints,
666
+ const winrt::Windows::Foundation::Point &viewportOffset) noexcept {
667
+ ApplyConstraints(layoutConstraints, m_layoutConstraints);
668
+
617
669
  if (m_isInitialized && m_rootTag != -1) {
618
670
  if (auto fabricuiManager = ::Microsoft::ReactNative::FabricUIManager::FromProperties(
619
671
  winrt::Microsoft::ReactNative::ReactPropertyBag(m_context.Properties()))) {
620
672
  facebook::react::LayoutContext context;
621
673
  context.pointScaleFactor = static_cast<facebook::react::Float>(m_scaleFactor);
622
674
  context.fontSizeMultiplier = static_cast<facebook::react::Float>(m_scaleFactor);
623
-
624
- facebook::react::LayoutConstraints constraints;
625
- // TODO should support MinHeight/MinWidth
626
- constraints.minimumSize.height = static_cast<facebook::react::Float>(0.0f);
627
- constraints.minimumSize.width = static_cast<facebook::react::Float>(0.0f);
628
-
629
- // TODO should support MaxHeight/MaxWidth props?
630
- constraints.minimumSize.height = constraints.maximumSize.height =
631
- static_cast<facebook::react::Float>(finalSize.Height);
632
- constraints.minimumSize.width = constraints.maximumSize.width =
633
- static_cast<facebook::react::Float>(finalSize.Width);
634
- // TODO get RTL
635
- constraints.layoutDirection = facebook::react::LayoutDirection::LeftToRight;
675
+ context.viewportOffset = {viewportOffset.X, viewportOffset.Y};
636
676
 
637
677
  fabricuiManager->constraintSurfaceLayout(
638
- static_cast<facebook::react::SurfaceId>(m_rootTag), constraints, context);
678
+ static_cast<facebook::react::SurfaceId>(m_rootTag), m_layoutConstraints, context);
639
679
  }
680
+ } else if (m_loadingVisual) {
681
+ // TODO: Resize to align loading
682
+ auto s = m_layoutConstraints.clamp(MeasureLoading(m_layoutConstraints, m_scaleFactor));
683
+ NotifySizeChanged();
640
684
  }
641
- return finalSize;
642
685
  }
643
686
 
644
687
  #ifdef USE_WINUI3
@@ -655,20 +698,29 @@ winrt::Microsoft::UI::Content::ContentIsland CompositionRootView::Island() noexc
655
698
  rootVisual));
656
699
  m_island = winrt::Microsoft::UI::Content::ContentIsland::Create(rootVisual);
657
700
 
658
- m_island.AutomationProviderRequested(
659
- [this](
701
+ // ContentIsland does not support weak_ref, so we cannot use auto_revoke for these events
702
+ m_islandAutomationProviderRequestedToken = m_island.AutomationProviderRequested(
703
+ [weakThis = get_weak()](
660
704
  winrt::Microsoft::UI::Content::ContentIsland const &,
661
705
  winrt::Microsoft::UI::Content::ContentIslandAutomationProviderRequestedEventArgs const &args) {
662
- auto provider = GetUiaProvider();
663
- auto pRootProvider =
664
- static_cast<winrt::Microsoft::ReactNative::implementation::CompositionRootAutomationProvider *>(
665
- provider.as<IRawElementProviderSimple>().get());
666
- if (pRootProvider != nullptr) {
667
- pRootProvider->SetIsland(m_island);
706
+ if (auto pThis = weakThis.get()) {
707
+ auto provider = pThis->GetUiaProvider();
708
+ auto pRootProvider =
709
+ static_cast<winrt::Microsoft::ReactNative::implementation::CompositionRootAutomationProvider *>(
710
+ provider.as<IRawElementProviderSimple>().get());
711
+ if (pRootProvider != nullptr) {
712
+ pRootProvider->SetIsland(pThis->m_island);
713
+ }
714
+ args.AutomationProvider(std::move(provider));
715
+ args.Handled(true);
668
716
  }
669
- args.AutomationProvider(std::move(provider));
670
- args.Handled(true);
671
717
  });
718
+
719
+ m_islandFrameworkClosedToken = m_island.FrameworkClosed([weakThis = get_weak()]() {
720
+ if (auto pThis = weakThis.get()) {
721
+ pThis->m_island = nullptr;
722
+ }
723
+ });
672
724
  }
673
725
  return m_island;
674
726
  }