react-native-windows 0.81.3 → 0.81.5

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 (71) hide show
  1. package/Libraries/Core/ReactNativeVersion.js +1 -1
  2. package/Libraries/NativeComponent/ViewConfigIgnore.windows.js +45 -0
  3. package/Libraries/Renderer/implementations/ReactFabric-dev.js +38 -35
  4. package/Libraries/Renderer/implementations/ReactFabric-prod.js +51 -22
  5. package/Libraries/Renderer/implementations/ReactFabric-profiling.js +54 -24
  6. package/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js +36 -33
  7. package/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js +5 -5
  8. package/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js +5 -5
  9. package/Libraries/Renderer/shims/ReactNativeTypes.js +23 -11
  10. package/Libraries/Renderer/shims/ReactNativeTypes.windows.js +23 -12
  11. package/Microsoft.ReactNative/ABIViewManager.cpp +12 -1
  12. package/Microsoft.ReactNative/ComponentView.idl +2 -0
  13. package/Microsoft.ReactNative/Composition.Input.idl +7 -0
  14. package/Microsoft.ReactNative/CompositionComponentView.idl +5 -0
  15. package/Microsoft.ReactNative/CompositionHwndHost.idl +1 -0
  16. package/Microsoft.ReactNative/CompositionSwitcher.idl +16 -9
  17. package/Microsoft.ReactNative/Fabric/ComponentView.cpp +19 -1
  18. package/Microsoft.ReactNative/Fabric/ComponentView.h +10 -1
  19. package/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp +12 -0
  20. package/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h +15 -0
  21. package/Microsoft.ReactNative/Fabric/Composition/CompositionContextHelper.cpp +15 -0
  22. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +75 -0
  23. package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +1 -0
  24. package/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.cpp +10 -45
  25. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +86 -19
  26. package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +4 -0
  27. package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp +95 -22
  28. package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h +15 -0
  29. package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +61 -74
  30. package/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +71 -12
  31. package/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h +11 -0
  32. package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +4 -3
  33. package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h +2 -1
  34. package/Microsoft.ReactNative/Fabric/Composition/ReactNativeWindow.cpp +245 -0
  35. package/Microsoft.ReactNative/Fabric/Composition/ReactNativeWindow.h +80 -0
  36. package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +33 -1
  37. package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h +17 -0
  38. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +47 -23
  39. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +3 -0
  40. package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.cpp +3 -1
  41. package/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp +8 -4
  42. package/Microsoft.ReactNative/Modules/ImageViewManagerModule.cpp +41 -15
  43. package/Microsoft.ReactNative/Modules/LogBoxModule.cpp +20 -95
  44. package/Microsoft.ReactNative/Modules/LogBoxModule.h +1 -1
  45. package/Microsoft.ReactNative/ReactNativeAppBuilder.cpp +0 -41
  46. package/Microsoft.ReactNative/ReactNativeAppBuilder.idl +0 -11
  47. package/Microsoft.ReactNative/ReactNativeIsland.idl +2 -3
  48. package/Microsoft.ReactNative/ReactNativeWin32App.cpp +31 -101
  49. package/Microsoft.ReactNative/ReactNativeWin32App.h +2 -13
  50. package/Microsoft.ReactNative/ReactNativeWindow.idl +44 -0
  51. package/Mso/src/dispatchQueue/queueService.cpp +3 -1
  52. package/Mso/src/dispatchQueue/uiScheduler_winrt.cpp +2 -1
  53. package/PropertySheets/Generated/PackageVersion.g.props +3 -3
  54. package/Shared/Networking/OriginPolicyHttpFilter.cpp +2 -1
  55. package/Shared/Shared.vcxitems +7 -0
  56. package/Shared/Shared.vcxitems.filters +6 -0
  57. package/codegen/react/components/rnwcore/ActivityIndicatorView.g.h +2 -1
  58. package/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h +42 -25
  59. package/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h +2 -1
  60. package/codegen/react/components/rnwcore/AndroidProgressBar.g.h +2 -1
  61. package/codegen/react/components/rnwcore/AndroidSwipeRefreshLayout.g.h +11 -6
  62. package/codegen/react/components/rnwcore/AndroidSwitch.g.h +11 -6
  63. package/codegen/react/components/rnwcore/DebuggingOverlay.g.h +1 -0
  64. package/codegen/react/components/rnwcore/InputAccessory.g.h +2 -1
  65. package/codegen/react/components/rnwcore/ModalHostView.g.h +40 -23
  66. package/codegen/react/components/rnwcore/PullToRefreshView.g.h +11 -6
  67. package/codegen/react/components/rnwcore/SafeAreaView.g.h +1 -0
  68. package/codegen/react/components/rnwcore/Switch.g.h +11 -6
  69. package/codegen/react/components/rnwcore/UnimplementedNativeView.g.h +2 -1
  70. package/codegen/react/components/rnwcore/VirtualView.g.h +41 -8
  71. package/package.json +21 -21
@@ -121,7 +121,7 @@ struct ComponentView
121
121
  virtual void onLostFocus(const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept;
122
122
  virtual void onGotFocus(const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept;
123
123
  virtual void onMounted() noexcept;
124
- bool isMounted() noexcept;
124
+ bool isMounted() const noexcept;
125
125
  virtual void onUnmounted() noexcept;
126
126
  void onDestroying() noexcept;
127
127
 
@@ -167,6 +167,10 @@ struct ComponentView
167
167
  winrt::Windows::Foundation::EventHandler<
168
168
  winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs> const &handler) noexcept;
169
169
  void CharacterReceived(winrt::event_token const &token) noexcept;
170
+ winrt::event_token ContextMenuKey(
171
+ winrt::Windows::Foundation::EventHandler<
172
+ winrt::Microsoft::ReactNative::Composition::Input::ContextMenuKeyEventArgs> const &handler) noexcept;
173
+ void ContextMenuKey(winrt::event_token const &token) noexcept;
170
174
  winrt::event_token PointerPressed(
171
175
  winrt::Windows::Foundation::EventHandler<
172
176
  winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept;
@@ -253,6 +257,8 @@ struct ComponentView
253
257
  virtual void OnKeyUp(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept;
254
258
  virtual void OnCharacterReceived(
255
259
  const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept;
260
+ virtual void OnContextMenuKey(
261
+ const winrt::Microsoft::ReactNative::Composition::Input::ContextMenuKeyEventArgs &args) noexcept;
256
262
 
257
263
  protected:
258
264
  winrt::com_ptr<winrt::Microsoft::ReactNative::Composition::ReactCompositionViewComponentBuilder> m_builder;
@@ -277,6 +283,9 @@ struct ComponentView
277
283
  winrt::event<winrt::Windows::Foundation::EventHandler<
278
284
  winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs>>
279
285
  m_characterReceivedEvent;
286
+ winrt::event<winrt::Windows::Foundation::EventHandler<
287
+ winrt::Microsoft::ReactNative::Composition::Input::ContextMenuKeyEventArgs>>
288
+ m_contextMenuKeyEvent;
280
289
  winrt::event<winrt::Windows::Foundation::EventHandler<
281
290
  winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs>>
282
291
  m_pointerPressedEvent;
@@ -140,6 +140,18 @@ winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource CharacterRecei
140
140
  return m_source;
141
141
  }
142
142
 
143
+ ContextMenuKeyEventArgs::ContextMenuKeyEventArgs(facebook::react::Tag tag) : m_tag(tag) {}
144
+
145
+ int32_t ContextMenuKeyEventArgs::OriginalSource() noexcept {
146
+ return m_tag;
147
+ }
148
+ bool ContextMenuKeyEventArgs::Handled() noexcept {
149
+ return m_handled;
150
+ }
151
+ void ContextMenuKeyEventArgs::Handled(bool value) noexcept {
152
+ m_handled = value;
153
+ }
154
+
143
155
  Pointer::Pointer(winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType type, uint32_t id)
144
156
  : m_type(type), m_id(id) {}
145
157
 
@@ -84,6 +84,21 @@ struct CharacterReceivedRoutedEventArgs
84
84
  const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource m_source;
85
85
  };
86
86
 
87
+ struct ContextMenuKeyEventArgs : winrt::implements<
88
+ ContextMenuKeyEventArgs,
89
+ winrt::Microsoft::ReactNative::Composition::Input::ContextMenuKeyEventArgs,
90
+ winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs> {
91
+ ContextMenuKeyEventArgs(facebook::react::Tag tag);
92
+
93
+ int32_t OriginalSource() noexcept;
94
+ bool Handled() noexcept;
95
+ void Handled(bool value) noexcept;
96
+
97
+ private:
98
+ facebook::react::Tag m_tag{-1};
99
+ bool m_handled{false};
100
+ };
101
+
87
102
  struct Pointer : PointerT<Pointer> {
88
103
  Pointer(winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType type, uint32_t id);
89
104
 
@@ -54,6 +54,7 @@ struct CompositionTypeTraits<WindowsTypeTag> {
54
54
  using CompositionStretch = winrt::Windows::UI::Composition::CompositionStretch;
55
55
  using CompositionStrokeCap = winrt::Windows::UI::Composition::CompositionStrokeCap;
56
56
  using CompositionSurfaceBrush = winrt::Windows::UI::Composition::CompositionSurfaceBrush;
57
+ using CompositionDropShadowSourcePolicy = winrt::Windows::UI::Composition::CompositionDropShadowSourcePolicy;
57
58
  using Compositor = winrt::Windows::UI::Composition::Compositor;
58
59
  using ContainerVisual = winrt::Windows::UI::Composition::ContainerVisual;
59
60
  using CubicBezierEasingFunction = winrt::Windows::UI::Composition::CubicBezierEasingFunction;
@@ -127,6 +128,7 @@ struct CompositionTypeTraits<MicrosoftTypeTag> {
127
128
  using CompositionStretch = winrt::Microsoft::UI::Composition::CompositionStretch;
128
129
  using CompositionStrokeCap = winrt::Microsoft::UI::Composition::CompositionStrokeCap;
129
130
  using CompositionSurfaceBrush = winrt::Microsoft::UI::Composition::CompositionSurfaceBrush;
131
+ using CompositionDropShadowSourcePolicy = winrt::Microsoft::UI::Composition::CompositionDropShadowSourcePolicy;
130
132
  using Compositor = winrt::Microsoft::UI::Composition::Compositor;
131
133
  using ContainerVisual = winrt::Microsoft::UI::Composition::ContainerVisual;
132
134
  using CubicBezierEasingFunction = winrt::Microsoft::UI::Composition::CubicBezierEasingFunction;
@@ -224,6 +226,19 @@ struct CompDropShadow : public winrt::implements<
224
226
  m_shadow.Color(color);
225
227
  }
226
228
 
229
+ void Mask(winrt::Microsoft::ReactNative::Composition::Experimental::IBrush const &mask) noexcept {
230
+ if (mask) {
231
+ m_shadow.Mask(mask.as<typename TTypeRedirects::IInnerCompositionBrush>()->InnerBrush());
232
+ } else {
233
+ m_shadow.Mask(nullptr);
234
+ }
235
+ }
236
+
237
+ void SourcePolicy(
238
+ winrt::Microsoft::ReactNative::Composition::Experimental::CompositionDropShadowSourcePolicy policy) noexcept {
239
+ m_shadow.SourcePolicy(static_cast<typename TTypeRedirects::CompositionDropShadowSourcePolicy>(policy));
240
+ }
241
+
227
242
  private:
228
243
  typename TTypeRedirects::DropShadow m_shadow;
229
244
  };
@@ -323,6 +323,32 @@ void CompositionEventHandler::Initialize() noexcept {
323
323
  }
324
324
  }
325
325
  });
326
+
327
+ m_contextMenuKeyToken =
328
+ keyboardSource.ContextMenuKey([wkThis = weak_from_this()](
329
+ winrt::Microsoft::UI::Input::InputKeyboardSource const & /*source*/,
330
+ winrt::Microsoft::UI::Input::ContextMenuKeyEventArgs const &args) {
331
+ if (auto strongThis = wkThis.lock()) {
332
+ if (auto strongRootView = strongThis->m_wkRootView.get()) {
333
+ if (strongThis->SurfaceId() == -1)
334
+ return;
335
+
336
+ auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent();
337
+ if (focusedComponent) {
338
+ auto tag =
339
+ winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)
340
+ ->Tag();
341
+ auto contextMenuArgs = winrt::make<
342
+ winrt::Microsoft::ReactNative::Composition::Input::implementation::ContextMenuKeyEventArgs>(tag);
343
+ winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(focusedComponent)
344
+ ->OnContextMenuKey(contextMenuArgs);
345
+ if (contextMenuArgs.Handled()) {
346
+ args.Handled(true);
347
+ }
348
+ }
349
+ }
350
+ }
351
+ });
326
352
  }
327
353
  #endif
328
354
  }
@@ -341,6 +367,7 @@ CompositionEventHandler::~CompositionEventHandler() {
341
367
  keyboardSource.KeyDown(m_keyDownToken);
342
368
  keyboardSource.KeyUp(m_keyUpToken);
343
369
  keyboardSource.CharacterReceived(m_characterReceivedToken);
370
+ keyboardSource.ContextMenuKey(m_contextMenuKeyToken);
344
371
  }
345
372
  }
346
373
  #endif
@@ -449,6 +476,54 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w
449
476
  }
450
477
  return 0;
451
478
  }
479
+ case WM_RBUTTONDOWN: {
480
+ if (auto strongRootView = m_wkRootView.get()) {
481
+ auto pp = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerPoint>(
482
+ hwnd, msg, wParam, lParam, strongRootView.ScaleFactor());
483
+ onPointerPressed(pp, GetKeyModifiers(wParam));
484
+ }
485
+ return 0;
486
+ }
487
+ case WM_RBUTTONUP: {
488
+ if (auto strongRootView = m_wkRootView.get()) {
489
+ auto pp = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerPoint>(
490
+ hwnd, msg, wParam, lParam, strongRootView.ScaleFactor());
491
+ onPointerReleased(pp, GetKeyModifiers(wParam));
492
+ }
493
+ return 0;
494
+ }
495
+ case WM_MBUTTONDOWN: {
496
+ if (auto strongRootView = m_wkRootView.get()) {
497
+ auto pp = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerPoint>(
498
+ hwnd, msg, wParam, lParam, strongRootView.ScaleFactor());
499
+ onPointerPressed(pp, GetKeyModifiers(wParam));
500
+ }
501
+ return 0;
502
+ }
503
+ case WM_MBUTTONUP: {
504
+ if (auto strongRootView = m_wkRootView.get()) {
505
+ auto pp = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerPoint>(
506
+ hwnd, msg, wParam, lParam, strongRootView.ScaleFactor());
507
+ onPointerReleased(pp, GetKeyModifiers(wParam));
508
+ }
509
+ return 0;
510
+ }
511
+ case WM_XBUTTONDOWN: {
512
+ if (auto strongRootView = m_wkRootView.get()) {
513
+ auto pp = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerPoint>(
514
+ hwnd, msg, wParam, lParam, strongRootView.ScaleFactor());
515
+ onPointerPressed(pp, GetKeyModifiers(wParam));
516
+ }
517
+ return 0;
518
+ }
519
+ case WM_XBUTTONUP: {
520
+ if (auto strongRootView = m_wkRootView.get()) {
521
+ auto pp = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerPoint>(
522
+ hwnd, msg, wParam, lParam, strongRootView.ScaleFactor());
523
+ onPointerReleased(pp, GetKeyModifiers(wParam));
524
+ }
525
+ return 0;
526
+ }
452
527
  case WM_POINTERUP: {
453
528
  if (auto strongRootView = m_wkRootView.get()) {
454
529
  auto pp = winrt::make<winrt::Microsoft::ReactNative::Composition::Input::implementation::PointerPoint>(
@@ -176,6 +176,7 @@ class CompositionEventHandler : public std::enable_shared_from_this<CompositionE
176
176
  winrt::event_token m_keyDownToken;
177
177
  winrt::event_token m_keyUpToken;
178
178
  winrt::event_token m_characterReceivedToken;
179
+ winrt::event_token m_contextMenuKeyToken;
179
180
  #endif
180
181
  };
181
182
 
@@ -32,55 +32,20 @@ CompositionHwndHost::CompositionHwndHost() noexcept {}
32
32
  void CompositionHwndHost::Initialize(uint64_t hwnd) noexcept {
33
33
  m_hwnd = (HWND)hwnd;
34
34
 
35
- auto compositionContext =
36
- winrt::Microsoft::ReactNative::Composition::implementation::CompositionUIService::GetCompositionContext(
37
- ReactViewHost().ReactNativeHost().InstanceSettings().Properties());
38
- #if USE_WINUI3
39
- if (auto liftedCompositor =
40
- winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerCompositor(
41
- compositionContext)) {
42
- m_compRootView = winrt::Microsoft::ReactNative::ReactNativeIsland(liftedCompositor);
43
-
44
- auto bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create(
45
- liftedCompositor, winrt::Microsoft::UI::GetWindowIdFromWindow(m_hwnd));
35
+ auto compositor = winrt::Microsoft::ReactNative::Composition::implementation::CompositionUIService::GetCompositor(
36
+ ReactViewHost().ReactNativeHost().InstanceSettings().Properties());
37
+ m_compRootView = winrt::Microsoft::ReactNative::ReactNativeIsland(compositor);
46
38
 
47
- auto island = m_compRootView.Island();
39
+ auto bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create(
40
+ compositor, winrt::Microsoft::UI::GetWindowIdFromWindow(m_hwnd));
48
41
 
49
- bridge.Connect(island);
50
- bridge.Show();
42
+ auto island = m_compRootView.Island();
51
43
 
52
- m_compRootView.ScaleFactor(ScaleFactor());
53
- bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow);
54
- } else {
55
- m_compRootView = winrt::Microsoft::ReactNative::ReactNativeIsland();
56
- m_compRootView.as<winrt::Microsoft::ReactNative::Composition::Experimental::IInternalCompositionRootView>()
57
- .SetWindow(reinterpret_cast<uint64_t>(m_hwnd));
58
-
59
- #endif
60
- auto compositor =
61
- winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::InnerCompositor(
62
- compositionContext);
63
- auto interop = compositor.as<ABI::Windows::UI::Composition::Desktop::ICompositorDesktopInterop>();
64
- winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget target{nullptr};
65
- check_hresult(interop->CreateDesktopWindowTarget(
66
- m_hwnd,
67
- false,
68
- reinterpret_cast<ABI::Windows::UI::Composition::Desktop::IDesktopWindowTarget **>(put_abi(target))));
69
-
70
- auto root = compositor.CreateContainerVisual();
71
- root.RelativeSizeAdjustment({1.0f, 1.0f});
72
- root.Offset({0, 0, 0});
73
- root.Comment(L"Root Visual");
74
- target.Root(root);
75
-
76
- m_compRootView.as<winrt::Microsoft::ReactNative::Composition::Experimental::IInternalCompositionRootView>()
77
- .InternalRootVisual(
78
- winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::CreateVisual(
79
- target.Root()));
44
+ bridge.Connect(island);
45
+ bridge.Show();
80
46
 
81
- #if USE_WINUI3
82
- }
83
- #endif
47
+ m_compRootView.ScaleFactor(ScaleFactor());
48
+ bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow);
84
49
 
85
50
  m_compRootView.ReactViewHost(std::move(m_reactViewHost));
86
51
  m_compRootView.ScaleFactor(ScaleFactor());
@@ -38,10 +38,11 @@ constexpr float FOCUS_VISUAL_RADIUS = 3.0f;
38
38
 
39
39
  // m_outerVisual
40
40
  // |
41
- // |
42
41
  // ----- m_visual <-- Background / clip - Can be a custom visual depending on Component type
43
42
  // |
44
43
  // ----- Border Visuals x N (BorderPrimitive attached to m_visual)
44
+ // ----- <children> (default: directly in m_visual after border visuals)
45
+ // ----- m_childrenContainer (created on demand when overflow:hidden, children moved here)
45
46
  // ------Focus Visual Container (created when hosting focus visuals)
46
47
  // |
47
48
  // |------Inner Focus Visual
@@ -527,10 +528,10 @@ facebook::react::RectangleEdges<bool> ComponentView::focusNudges() const noexcep
527
528
 
528
529
  Assert(m_componentHostingFocusVisual);
529
530
 
530
- if (layoutMetrics.frame.origin.x < 0) {
531
+ if (layoutMetrics.frame.origin.x < m_componentHostingFocusVisual->m_layoutMetrics.frame.origin.x) {
531
532
  nudgeEdges.left = true;
532
533
  }
533
- if (layoutMetrics.frame.origin.y < 0) {
534
+ if (layoutMetrics.frame.origin.y < m_componentHostingFocusVisual->m_layoutMetrics.frame.origin.y) {
534
535
  nudgeEdges.top = true;
535
536
  }
536
537
  if (layoutMetrics.frame.getMaxX() > m_componentHostingFocusVisual->m_layoutMetrics.frame.getMaxX()) {
@@ -710,7 +711,9 @@ void ComponentView::applyShadowProps(const facebook::react::ViewProps &viewProps
710
711
  shadow.Color(theme()->Color(*viewProps.shadowColor));
711
712
  }
712
713
 
713
- Visual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(shadow);
714
+ // Apply shadow to OuterVisual (not Visual) because Visual may have a rounded-corner clip
715
+ // from updateClippingPath, which would clip the shadow. OuterVisual is not clipped.
716
+ OuterVisual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(shadow);
714
717
  }
715
718
 
716
719
  void ComponentView::updateTransformProps(
@@ -894,23 +897,26 @@ void ComponentView::updateClippingPath(
894
897
  const facebook::react::ViewProps &viewProps) noexcept {
895
898
  auto borderMetrics = BorderPrimitive::resolveAndAlignBorderMetrics(layoutMetrics, viewProps);
896
899
 
897
- if (borderMetrics.borderRadii.topLeft.horizontal == 0 && borderMetrics.borderRadii.topRight.horizontal == 0 &&
898
- borderMetrics.borderRadii.bottomLeft.horizontal == 0 && borderMetrics.borderRadii.bottomRight.horizontal == 0 &&
899
- borderMetrics.borderRadii.topLeft.vertical == 0 && borderMetrics.borderRadii.topRight.vertical == 0 &&
900
- borderMetrics.borderRadii.bottomLeft.vertical == 0 && borderMetrics.borderRadii.bottomRight.vertical == 0) {
901
- Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(nullptr);
902
- } else {
900
+ bool hasRoundedCorners = borderMetrics.borderRadii.topLeft.horizontal != 0 ||
901
+ borderMetrics.borderRadii.topRight.horizontal != 0 || borderMetrics.borderRadii.bottomLeft.horizontal != 0 ||
902
+ borderMetrics.borderRadii.bottomRight.horizontal != 0 || borderMetrics.borderRadii.topLeft.vertical != 0 ||
903
+ borderMetrics.borderRadii.topRight.vertical != 0 || borderMetrics.borderRadii.bottomLeft.vertical != 0 ||
904
+ borderMetrics.borderRadii.bottomRight.vertical != 0;
905
+
906
+ const float scale = layoutMetrics.pointScaleFactor;
907
+ const float viewWidth = layoutMetrics.frame.size.width * scale;
908
+ const float viewHeight = layoutMetrics.frame.size.height * scale;
909
+
910
+ // Apply clipping to m_visual only for rounded corners
911
+ // overflow:hidden clipping is handled separately via m_childrenContainer in ViewComponentView
912
+ if (hasRoundedCorners) {
903
913
  winrt::com_ptr<ID2D1PathGeometry> pathGeometry = BorderPrimitive::GenerateRoundedRectPathGeometry(
904
- m_compContext,
905
- borderMetrics.borderRadii,
906
- {0, 0, 0, 0},
907
- {0,
908
- 0,
909
- layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
910
- layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
914
+ m_compContext, borderMetrics.borderRadii, {0, 0, 0, 0}, {0, 0, viewWidth, viewHeight});
911
915
 
912
916
  Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(
913
917
  pathGeometry.get());
918
+ } else {
919
+ Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(nullptr);
914
920
  }
915
921
  }
916
922
 
@@ -1085,6 +1091,11 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual
1085
1091
  ViewComponentView::VisualToMountChildrenInto() noexcept {
1086
1092
  if (m_builder && m_builder->VisualToMountChildrenIntoHandler())
1087
1093
  return m_builder->VisualToMountChildrenIntoHandler()(*this);
1094
+ // When overflow:hidden, children are hosted in m_childrenContainer (child of m_visual)
1095
+ // so we can apply clipping without affecting borders/background.
1096
+ // Otherwise children go directly into Visual() (the original behavior).
1097
+ if (m_childrenContainer)
1098
+ return m_childrenContainer;
1088
1099
  return Visual();
1089
1100
  }
1090
1101
 
@@ -1093,9 +1104,14 @@ void ViewComponentView::MountChildComponentView(
1093
1104
  uint32_t index) noexcept {
1094
1105
  base_type::MountChildComponentView(childComponentView, index);
1095
1106
 
1096
- indexOffsetForBorder(index);
1097
1107
  ensureVisual();
1098
1108
 
1109
+ // When children are in Visual() directly, offset past border visuals.
1110
+ // When children are in m_childrenContainer, no offset needed.
1111
+ if (!m_childrenContainer) {
1112
+ indexOffsetForBorder(index);
1113
+ }
1114
+
1099
1115
  if (auto compositionChild = childComponentView.try_as<ComponentView>()) {
1100
1116
  auto visualIndex = index;
1101
1117
  // Most of the time child index will align with visual index.
@@ -1107,6 +1123,7 @@ void ViewComponentView::MountChildComponentView(
1107
1123
  }
1108
1124
  }
1109
1125
  }
1126
+
1110
1127
  VisualToMountChildrenInto().InsertAt(compositionChild->OuterVisual(), visualIndex);
1111
1128
  } else {
1112
1129
  m_hasNonVisualChildren = true;
@@ -1118,7 +1135,6 @@ void ViewComponentView::UnmountChildComponentView(
1118
1135
  uint32_t index) noexcept {
1119
1136
  base_type::UnmountChildComponentView(childComponentView, index);
1120
1137
 
1121
- indexOffsetForBorder(index);
1122
1138
  if (auto compositionChild = childComponentView.try_as<ComponentView>()) {
1123
1139
  VisualToMountChildrenInto().Remove(compositionChild->OuterVisual());
1124
1140
  }
@@ -1318,6 +1334,57 @@ void ViewComponentView::updateLayoutMetrics(
1318
1334
  Visual().Size(
1319
1335
  {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
1320
1336
  layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
1337
+
1338
+ // Update children container clipping for overflow:hidden
1339
+ updateChildrenClippingPath(layoutMetrics, *viewProps());
1340
+ }
1341
+
1342
+ void ViewComponentView::updateChildrenClippingPath(
1343
+ facebook::react::LayoutMetrics const &layoutMetrics,
1344
+ const facebook::react::ViewProps &viewProps) noexcept {
1345
+ const float scale = layoutMetrics.pointScaleFactor;
1346
+ const float viewWidth = layoutMetrics.frame.size.width * scale;
1347
+ const float viewHeight = layoutMetrics.frame.size.height * scale;
1348
+
1349
+ if (viewProps.getClipsContentToBounds()) {
1350
+ // Create m_childrenContainer on demand (like iOS _containerView pattern)
1351
+ // m_childrenContainer is a child of m_visual, placed after border visuals.
1352
+ if (!m_childrenContainer) {
1353
+ m_childrenContainer = m_compContext.CreateSpriteVisual();
1354
+
1355
+ // Insert at the end of m_visual's children (after border visuals + existing children)
1356
+ // Then move existing children from m_visual into m_childrenContainer
1357
+ uint32_t borderCount = 0;
1358
+ indexOffsetForBorder(borderCount);
1359
+
1360
+ // Move existing child visuals from m_visual to m_childrenContainer
1361
+ uint32_t childVisualIndex = 0;
1362
+ for (auto it = m_children.begin(); it != m_children.end(); ++it) {
1363
+ if (auto compositionChild = (*it).try_as<ComponentView>()) {
1364
+ Visual().Remove(compositionChild->OuterVisual());
1365
+ m_childrenContainer.InsertAt(compositionChild->OuterVisual(), childVisualIndex++);
1366
+ }
1367
+ }
1368
+
1369
+ // Insert m_childrenContainer after border visuals in m_visual
1370
+ Visual().InsertAt(m_childrenContainer, borderCount);
1371
+
1372
+ // Use relative sizing so container automatically tracks parent's size
1373
+ m_childrenContainer.RelativeSizeWithOffset({0, 0}, {1, 1});
1374
+ }
1375
+
1376
+ // Clip children to view bounds using outer border radii (matches iOS default behavior)
1377
+ auto borderMetrics = BorderPrimitive::resolveAndAlignBorderMetrics(layoutMetrics, viewProps);
1378
+ winrt::com_ptr<ID2D1PathGeometry> pathGeometry = BorderPrimitive::GenerateRoundedRectPathGeometry(
1379
+ m_compContext, borderMetrics.borderRadii, {0, 0, 0, 0}, {0, 0, viewWidth, viewHeight});
1380
+
1381
+ m_childrenContainer.as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(
1382
+ pathGeometry.get());
1383
+ } else if (m_childrenContainer) {
1384
+ // overflow changed from hidden to visible. Keep container, just remove clip.
1385
+ m_childrenContainer.as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(
1386
+ nullptr);
1387
+ }
1321
1388
  }
1322
1389
 
1323
1390
  void ViewComponentView::prepareForRecycle() noexcept {}
@@ -233,11 +233,15 @@ struct ViewComponentView : public ViewComponentViewT<
233
233
 
234
234
  protected:
235
235
  virtual winrt::Microsoft::ReactNative::ViewProps ViewPropsInner() noexcept;
236
+ virtual void updateChildrenClippingPath(
237
+ facebook::react::LayoutMetrics const &layoutMetrics,
238
+ const facebook::react::ViewProps &viewProps) noexcept;
236
239
 
237
240
  private:
238
241
  bool m_hasNonVisualChildren{false};
239
242
  facebook::react::SharedViewProps m_props;
240
243
  winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_visual{nullptr};
244
+ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_childrenContainer{nullptr};
241
245
  winrt::Microsoft::ReactNative::Composition::Experimental::CreateInternalVisualDelegate m_createInternalVisualHandler{
242
246
  nullptr};
243
247
  };
@@ -15,6 +15,7 @@
15
15
  #include <winrt/Windows.UI.Composition.h>
16
16
  #include "CompositionContextHelper.h"
17
17
  #include "RootComponentView.h"
18
+ #include "ScrollViewComponentView.h"
18
19
 
19
20
  #include "Composition.ContentIslandComponentView.g.cpp"
20
21
 
@@ -43,12 +44,38 @@ ContentIslandComponentView::ContentIslandComponentView(
43
44
  });
44
45
  }
45
46
 
46
- void ContentIslandComponentView::OnMounted() noexcept {
47
- m_childSiteLink = winrt::Microsoft::UI::Content::ChildSiteLink::Create(
48
- rootComponentView()->parentContentIsland(),
49
- winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(Visual())
50
- .as<winrt::Microsoft::UI::Composition::ContainerVisual>());
51
- m_childSiteLink.ActualSize({m_layoutMetrics.frame.size.width, m_layoutMetrics.frame.size.height});
47
+ winrt::Microsoft::UI::Content::ContentIsland ContentIslandComponentView::ParentContentIsland() noexcept {
48
+ auto root = rootComponentView();
49
+ if (!root)
50
+ return nullptr;
51
+ return root->parentContentIsland();
52
+ }
53
+
54
+ winrt::Microsoft::UI::Content::ChildSiteLink ContentIslandComponentView::ChildSiteLink() noexcept {
55
+ if (!isMounted())
56
+ return nullptr;
57
+ if (!m_childSiteLink) {
58
+ m_childSiteLink = winrt::Microsoft::UI::Content::ChildSiteLink::Create(
59
+ m_parentContentIsland,
60
+ winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(Visual())
61
+ .as<winrt::Microsoft::UI::Composition::ContainerVisual>());
62
+ }
63
+ return m_childSiteLink;
64
+ }
65
+
66
+ void ContentIslandComponentView::ConnectInternal() noexcept {
67
+ if (!m_islandToConnect)
68
+ return;
69
+
70
+ ChildSiteLink().ActualSize({m_layoutMetrics.frame.size.width, m_layoutMetrics.frame.size.height});
71
+
72
+ // Issue #15557: Set initial LocalToParentTransformMatrix synchronously before Connect.
73
+ // This fixes popup position being wrong even without scrolling.
74
+ // Note: getClientRect() returns physical pixels, but LocalToParentTransformMatrix expects DIPs.
75
+ auto clientRect = getClientRect();
76
+ float scaleFactor = m_layoutMetrics.pointScaleFactor;
77
+ m_childSiteLink.LocalToParentTransformMatrix(winrt::Windows::Foundation::Numerics::make_float4x4_translation(
78
+ static_cast<float>(clientRect.left) / scaleFactor, static_cast<float>(clientRect.top) / scaleFactor, 0.0f));
52
79
 
53
80
  m_navigationHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteLink(m_childSiteLink);
54
81
 
@@ -66,10 +93,19 @@ void ContentIslandComponentView::OnMounted() noexcept {
66
93
  ConfigureChildSiteLinkAutomation();
67
94
 
68
95
  if (m_islandToConnect) {
96
+ Assert(m_childSiteLink.SiteView().IsConnected());
97
+ Assert(!m_islandToConnect.IsConnected());
98
+
69
99
  m_childSiteLink.Connect(m_islandToConnect);
70
100
  m_islandToConnect = nullptr;
71
101
  }
72
102
 
103
+ if (m_pendingNavigateFocus) {
104
+ m_navigationHost.NavigateFocus(
105
+ winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(*m_pendingNavigateFocus));
106
+ m_pendingNavigateFocus.reset();
107
+ }
108
+
73
109
  ParentLayoutChanged();
74
110
  auto view = Parent();
75
111
  while (view) {
@@ -81,12 +117,39 @@ void ContentIslandComponentView::OnMounted() noexcept {
81
117
  strongThis->ParentLayoutChanged();
82
118
  }
83
119
  }));
120
+
121
+ // Issue #15557: Register for ViewChanged on parent ScrollViews to update transform
122
+ // when scroll position changes, ensuring correct XAML popup positioning.
123
+ if (auto scrollView = view.try_as<winrt::Microsoft::ReactNative::Composition::ScrollViewComponentView>()) {
124
+ auto token =
125
+ scrollView.ViewChanged([wkThis = get_weak()](const winrt::IInspectable &, const winrt::IInspectable &) {
126
+ if (auto strongThis = wkThis.get()) {
127
+ strongThis->ParentLayoutChanged();
128
+ }
129
+ });
130
+ m_viewChangedSubscriptions.push_back({scrollView, token});
131
+ }
132
+
84
133
  view = view.Parent();
85
134
  }
86
135
  }
87
136
 
137
+ void ContentIslandComponentView::OnMounted() noexcept {
138
+ m_parentContentIsland = ParentContentIsland();
139
+ ConnectInternal();
140
+ }
141
+
88
142
  void ContentIslandComponentView::OnUnmounted() noexcept {
89
143
  m_layoutMetricChangedRevokers.clear();
144
+
145
+ // Issue #15557: Unsubscribe from parent ScrollView events
146
+ for (auto &subscription : m_viewChangedSubscriptions) {
147
+ if (auto scrollView = subscription.scrollView.get()) {
148
+ scrollView.ViewChanged(subscription.token);
149
+ }
150
+ }
151
+ m_viewChangedSubscriptions.clear();
152
+
90
153
  if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) {
91
154
  m_navigationHost.DepartFocusRequested(m_navigationHostDepartFocusRequestedToken);
92
155
  m_navigationHostDepartFocusRequestedToken = {};
@@ -94,21 +157,25 @@ void ContentIslandComponentView::OnUnmounted() noexcept {
94
157
  }
95
158
 
96
159
  void ContentIslandComponentView::ParentLayoutChanged() noexcept {
97
- if (m_layoutChangePosted)
98
- return;
99
-
100
- m_layoutChangePosted = true;
101
- ReactContext().UIDispatcher().Post([wkThis = get_weak()]() {
102
- if (auto strongThis = wkThis.get()) {
103
- auto clientRect = strongThis->getClientRect();
104
-
105
- strongThis->m_childSiteLink.LocalToParentTransformMatrix(
106
- winrt::Windows::Foundation::Numerics::make_float4x4_translation(
107
- static_cast<float>(clientRect.left), static_cast<float>(clientRect.top), 0.0f));
108
-
109
- strongThis->m_layoutChangePosted = false;
110
- }
111
- });
160
+ // Issue #15557: Update transform synchronously to ensure correct popup position
161
+ // when user clicks. Async updates via UIDispatcher().Post() were causing the
162
+ // popup to open with stale transform values.
163
+ //
164
+ // Note: The original async approach was for batching notifications during layout passes.
165
+ // However, LocalToParentTransformMatrix is a cheap call (just sets a matrix), and
166
+ // synchronous updates are required to ensure correct popup position when clicked.
167
+ //
168
+ // getClientRect() returns values in physical pixels (scaled by pointScaleFactor),
169
+ // but LocalToParentTransformMatrix expects logical pixels (DIPs). We need to divide
170
+ // by the scale factor to convert.
171
+ auto clientRect = getClientRect();
172
+ float scaleFactor = m_layoutMetrics.pointScaleFactor;
173
+
174
+ float x = static_cast<float>(clientRect.left) / scaleFactor;
175
+ float y = static_cast<float>(clientRect.top) / scaleFactor;
176
+
177
+ m_childSiteLink.LocalToParentTransformMatrix(
178
+ winrt::Windows::Foundation::Numerics::make_float4x4_translation(x, y, 0.0f));
112
179
  }
113
180
 
114
181
  winrt::Windows::Foundation::IInspectable ContentIslandComponentView::CreateAutomationProvider() noexcept {
@@ -164,7 +231,10 @@ void ContentIslandComponentView::onGotFocus(
164
231
  const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
165
232
  auto gotFocusEventArgs = args.as<winrt::Microsoft::ReactNative::implementation::GotFocusEventArgs>();
166
233
  const auto navigationReason = GetFocusNavigationReason(gotFocusEventArgs->Direction());
167
- m_navigationHost.NavigateFocus(winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(navigationReason));
234
+ if (m_navigationHost)
235
+ m_navigationHost.NavigateFocus(winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(navigationReason));
236
+ else
237
+ m_pendingNavigateFocus = navigationReason;
168
238
  }
169
239
 
170
240
  ContentIslandComponentView::~ContentIslandComponentView() noexcept {
@@ -223,6 +293,9 @@ void ContentIslandComponentView::Connect(const winrt::Microsoft::UI::Content::Co
223
293
  m_childSiteLink.Connect(contentIsland);
224
294
  } else {
225
295
  m_islandToConnect = contentIsland;
296
+ if (isMounted()) {
297
+ ConnectInternal();
298
+ }
226
299
  }
227
300
  }
228
301