react-native-windows 0.81.4 → 0.81.6
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.
- package/Libraries/Core/ReactNativeVersion.js +1 -1
- package/Libraries/NativeComponent/ViewConfigIgnore.windows.js +45 -0
- package/Libraries/Renderer/implementations/ReactFabric-dev.js +38 -35
- package/Libraries/Renderer/implementations/ReactFabric-prod.js +51 -22
- package/Libraries/Renderer/implementations/ReactFabric-profiling.js +54 -24
- package/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js +36 -33
- package/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js +5 -5
- package/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js +5 -5
- package/Libraries/Renderer/shims/ReactNativeTypes.js +23 -11
- package/Libraries/Renderer/shims/ReactNativeTypes.windows.js +23 -12
- package/Microsoft.ReactNative/ComponentView.idl +2 -0
- package/Microsoft.ReactNative/Composition.Input.idl +7 -0
- package/Microsoft.ReactNative/CompositionComponentView.idl +5 -0
- package/Microsoft.ReactNative/CompositionHwndHost.idl +1 -0
- package/Microsoft.ReactNative/Fabric/ComponentView.cpp +19 -1
- package/Microsoft.ReactNative/Fabric/ComponentView.h +10 -1
- package/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp +12 -0
- package/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h +15 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +75 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +1 -0
- package/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.cpp +10 -45
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +86 -98
- package/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +4 -0
- package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp +80 -48
- package/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h +11 -3
- package/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +61 -74
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +4 -3
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h +2 -1
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeWindow.cpp +245 -0
- package/Microsoft.ReactNative/Fabric/Composition/ReactNativeWindow.h +80 -0
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.cpp +33 -1
- package/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h +17 -0
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.cpp +137 -84
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +7 -1
- package/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputShadowNode.cpp +3 -1
- package/Microsoft.ReactNative/Fabric/platform/react/renderer/textlayoutmanager/WindowsTextLayoutManager.cpp +7 -2
- package/Microsoft.ReactNative/Modules/LogBoxModule.cpp +20 -95
- package/Microsoft.ReactNative/Modules/LogBoxModule.h +1 -1
- package/Microsoft.ReactNative/ReactNativeAppBuilder.cpp +0 -41
- package/Microsoft.ReactNative/ReactNativeAppBuilder.idl +0 -11
- package/Microsoft.ReactNative/ReactNativeIsland.idl +2 -3
- package/Microsoft.ReactNative/ReactNativeWin32App.cpp +31 -101
- package/Microsoft.ReactNative/ReactNativeWin32App.h +2 -13
- package/Microsoft.ReactNative/ReactNativeWindow.idl +44 -0
- package/PropertySheets/Generated/PackageVersion.g.props +3 -3
- package/Shared/Shared.vcxitems +7 -0
- package/Shared/Shared.vcxitems.filters +6 -0
- package/codegen/react/components/rnwcore/ActivityIndicatorView.g.h +2 -1
- package/codegen/react/components/rnwcore/AndroidDrawerLayout.g.h +26 -9
- package/codegen/react/components/rnwcore/AndroidHorizontalScrollContentView.g.h +2 -1
- package/codegen/react/components/rnwcore/AndroidProgressBar.g.h +2 -1
- package/codegen/react/components/rnwcore/AndroidSwipeRefreshLayout.g.h +8 -3
- package/codegen/react/components/rnwcore/AndroidSwitch.g.h +8 -3
- package/codegen/react/components/rnwcore/DebuggingOverlay.g.h +1 -0
- package/codegen/react/components/rnwcore/InputAccessory.g.h +2 -1
- package/codegen/react/components/rnwcore/ModalHostView.g.h +26 -9
- package/codegen/react/components/rnwcore/PullToRefreshView.g.h +8 -3
- package/codegen/react/components/rnwcore/SafeAreaView.g.h +1 -0
- package/codegen/react/components/rnwcore/Switch.g.h +8 -3
- package/codegen/react/components/rnwcore/UnimplementedNativeView.g.h +2 -1
- package/codegen/react/components/rnwcore/VirtualView.g.h +8 -3
- package/package.json +21 -21
|
@@ -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
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
39
|
+
auto bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create(
|
|
40
|
+
compositor, winrt::Microsoft::UI::GetWindowIdFromWindow(m_hwnd));
|
|
48
41
|
|
|
49
|
-
|
|
50
|
-
bridge.Show();
|
|
42
|
+
auto island = m_compRootView.Island();
|
|
51
43
|
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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 <
|
|
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 <
|
|
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,86 +711,9 @@ void ComponentView::applyShadowProps(const facebook::react::ViewProps &viewProps
|
|
|
710
711
|
shadow.Color(theme()->Color(*viewProps.shadowColor));
|
|
711
712
|
}
|
|
712
713
|
|
|
713
|
-
//
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
borderMetrics.borderRadii.topRight.horizontal != 0 || borderMetrics.borderRadii.bottomLeft.horizontal != 0 ||
|
|
717
|
-
borderMetrics.borderRadii.bottomRight.horizontal != 0 || borderMetrics.borderRadii.topLeft.vertical != 0 ||
|
|
718
|
-
borderMetrics.borderRadii.topRight.vertical != 0 || borderMetrics.borderRadii.bottomLeft.vertical != 0 ||
|
|
719
|
-
borderMetrics.borderRadii.bottomRight.vertical != 0;
|
|
720
|
-
|
|
721
|
-
if (hasBorderRadius) {
|
|
722
|
-
// When borderRadius is set, we need to create a shadow mask that follows the rounded rectangle shape.
|
|
723
|
-
// Use CompositionVisualSurface to capture the clipped visual's appearance as the shadow mask.
|
|
724
|
-
bool maskSet = false;
|
|
725
|
-
|
|
726
|
-
// Try Microsoft (WinUI3) Composition first
|
|
727
|
-
auto msCompositor =
|
|
728
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerCompositor(
|
|
729
|
-
m_compContext);
|
|
730
|
-
if (msCompositor) {
|
|
731
|
-
auto innerVisual =
|
|
732
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual(
|
|
733
|
-
Visual());
|
|
734
|
-
if (innerVisual) {
|
|
735
|
-
// Create a VisualSurface that captures the visual (with its clip applied)
|
|
736
|
-
auto visualSurface = msCompositor.CreateVisualSurface();
|
|
737
|
-
visualSurface.SourceVisual(innerVisual);
|
|
738
|
-
visualSurface.SourceSize(
|
|
739
|
-
{m_layoutMetrics.frame.size.width * m_layoutMetrics.pointScaleFactor,
|
|
740
|
-
m_layoutMetrics.frame.size.height * m_layoutMetrics.pointScaleFactor});
|
|
741
|
-
|
|
742
|
-
// Create a brush from the visual surface to use as shadow mask
|
|
743
|
-
auto maskBrush = msCompositor.CreateSurfaceBrush(visualSurface);
|
|
744
|
-
maskBrush.Stretch(winrt::Microsoft::UI::Composition::CompositionStretch::Fill);
|
|
745
|
-
|
|
746
|
-
// Get the inner shadow and set the mask
|
|
747
|
-
auto innerShadow = winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::
|
|
748
|
-
InnerDropShadow(shadow);
|
|
749
|
-
if (innerShadow) {
|
|
750
|
-
innerShadow.Mask(maskBrush);
|
|
751
|
-
maskSet = true;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
// Fallback to System (Windows.UI) Composition if Microsoft Composition is not available
|
|
757
|
-
if (!maskSet) {
|
|
758
|
-
auto sysCompositor =
|
|
759
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::InnerCompositor(
|
|
760
|
-
m_compContext);
|
|
761
|
-
if (sysCompositor) {
|
|
762
|
-
auto innerVisual =
|
|
763
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::InnerVisual(
|
|
764
|
-
Visual());
|
|
765
|
-
if (innerVisual) {
|
|
766
|
-
auto visualSurface = sysCompositor.CreateVisualSurface();
|
|
767
|
-
visualSurface.SourceVisual(innerVisual);
|
|
768
|
-
visualSurface.SourceSize(
|
|
769
|
-
{m_layoutMetrics.frame.size.width * m_layoutMetrics.pointScaleFactor,
|
|
770
|
-
m_layoutMetrics.frame.size.height * m_layoutMetrics.pointScaleFactor});
|
|
771
|
-
|
|
772
|
-
auto maskBrush = sysCompositor.CreateSurfaceBrush(visualSurface);
|
|
773
|
-
maskBrush.Stretch(winrt::Windows::UI::Composition::CompositionStretch::Fill);
|
|
774
|
-
|
|
775
|
-
auto innerShadow =
|
|
776
|
-
winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::InnerDropShadow(
|
|
777
|
-
shadow);
|
|
778
|
-
if (innerShadow) {
|
|
779
|
-
innerShadow.Mask(maskBrush);
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// Apply shadow to OuterVisual (which is not clipped) so the shadow can extend beyond the clip
|
|
786
|
-
OuterVisual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(shadow);
|
|
787
|
-
Visual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(nullptr);
|
|
788
|
-
} else {
|
|
789
|
-
// No border radius - apply shadow directly to Visual (original behavior)
|
|
790
|
-
Visual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(shadow);
|
|
791
|
-
OuterVisual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(nullptr);
|
|
792
|
-
}
|
|
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);
|
|
793
717
|
}
|
|
794
718
|
|
|
795
719
|
void ComponentView::updateTransformProps(
|
|
@@ -973,23 +897,26 @@ void ComponentView::updateClippingPath(
|
|
|
973
897
|
const facebook::react::ViewProps &viewProps) noexcept {
|
|
974
898
|
auto borderMetrics = BorderPrimitive::resolveAndAlignBorderMetrics(layoutMetrics, viewProps);
|
|
975
899
|
|
|
976
|
-
|
|
977
|
-
borderMetrics.borderRadii.
|
|
978
|
-
borderMetrics.borderRadii.
|
|
979
|
-
borderMetrics.borderRadii.
|
|
980
|
-
|
|
981
|
-
|
|
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) {
|
|
982
913
|
winrt::com_ptr<ID2D1PathGeometry> pathGeometry = BorderPrimitive::GenerateRoundedRectPathGeometry(
|
|
983
|
-
m_compContext,
|
|
984
|
-
borderMetrics.borderRadii,
|
|
985
|
-
{0, 0, 0, 0},
|
|
986
|
-
{0,
|
|
987
|
-
0,
|
|
988
|
-
layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
|
|
989
|
-
layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor});
|
|
914
|
+
m_compContext, borderMetrics.borderRadii, {0, 0, 0, 0}, {0, 0, viewWidth, viewHeight});
|
|
990
915
|
|
|
991
916
|
Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(
|
|
992
917
|
pathGeometry.get());
|
|
918
|
+
} else {
|
|
919
|
+
Visual().as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath(nullptr);
|
|
993
920
|
}
|
|
994
921
|
}
|
|
995
922
|
|
|
@@ -1164,6 +1091,11 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual
|
|
|
1164
1091
|
ViewComponentView::VisualToMountChildrenInto() noexcept {
|
|
1165
1092
|
if (m_builder && m_builder->VisualToMountChildrenIntoHandler())
|
|
1166
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;
|
|
1167
1099
|
return Visual();
|
|
1168
1100
|
}
|
|
1169
1101
|
|
|
@@ -1172,9 +1104,14 @@ void ViewComponentView::MountChildComponentView(
|
|
|
1172
1104
|
uint32_t index) noexcept {
|
|
1173
1105
|
base_type::MountChildComponentView(childComponentView, index);
|
|
1174
1106
|
|
|
1175
|
-
indexOffsetForBorder(index);
|
|
1176
1107
|
ensureVisual();
|
|
1177
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
|
+
|
|
1178
1115
|
if (auto compositionChild = childComponentView.try_as<ComponentView>()) {
|
|
1179
1116
|
auto visualIndex = index;
|
|
1180
1117
|
// Most of the time child index will align with visual index.
|
|
@@ -1186,6 +1123,7 @@ void ViewComponentView::MountChildComponentView(
|
|
|
1186
1123
|
}
|
|
1187
1124
|
}
|
|
1188
1125
|
}
|
|
1126
|
+
|
|
1189
1127
|
VisualToMountChildrenInto().InsertAt(compositionChild->OuterVisual(), visualIndex);
|
|
1190
1128
|
} else {
|
|
1191
1129
|
m_hasNonVisualChildren = true;
|
|
@@ -1197,7 +1135,6 @@ void ViewComponentView::UnmountChildComponentView(
|
|
|
1197
1135
|
uint32_t index) noexcept {
|
|
1198
1136
|
base_type::UnmountChildComponentView(childComponentView, index);
|
|
1199
1137
|
|
|
1200
|
-
indexOffsetForBorder(index);
|
|
1201
1138
|
if (auto compositionChild = childComponentView.try_as<ComponentView>()) {
|
|
1202
1139
|
VisualToMountChildrenInto().Remove(compositionChild->OuterVisual());
|
|
1203
1140
|
}
|
|
@@ -1397,6 +1334,57 @@ void ViewComponentView::updateLayoutMetrics(
|
|
|
1397
1334
|
Visual().Size(
|
|
1398
1335
|
{layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor,
|
|
1399
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
|
+
}
|
|
1400
1388
|
}
|
|
1401
1389
|
|
|
1402
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
|
|
|
@@ -50,15 +51,31 @@ winrt::Microsoft::UI::Content::ContentIsland ContentIslandComponentView::ParentC
|
|
|
50
51
|
return root->parentContentIsland();
|
|
51
52
|
}
|
|
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
|
+
|
|
53
66
|
void ContentIslandComponentView::ConnectInternal() noexcept {
|
|
54
67
|
if (!m_islandToConnect)
|
|
55
68
|
return;
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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));
|
|
62
79
|
|
|
63
80
|
m_navigationHost = winrt::Microsoft::UI::Input::InputFocusNavigationHost::GetForSiteLink(m_childSiteLink);
|
|
64
81
|
|
|
@@ -76,10 +93,18 @@ void ContentIslandComponentView::ConnectInternal() noexcept {
|
|
|
76
93
|
ConfigureChildSiteLinkAutomation();
|
|
77
94
|
|
|
78
95
|
if (m_islandToConnect) {
|
|
96
|
+
Assert(m_childSiteLink.SiteView().IsConnected());
|
|
97
|
+
Assert(!m_islandToConnect.IsConnected());
|
|
98
|
+
|
|
79
99
|
m_childSiteLink.Connect(m_islandToConnect);
|
|
80
100
|
m_islandToConnect = nullptr;
|
|
81
101
|
}
|
|
82
|
-
|
|
102
|
+
|
|
103
|
+
if (m_pendingNavigateFocus) {
|
|
104
|
+
m_navigationHost.NavigateFocus(
|
|
105
|
+
winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(*m_pendingNavigateFocus));
|
|
106
|
+
m_pendingNavigateFocus.reset();
|
|
107
|
+
}
|
|
83
108
|
|
|
84
109
|
ParentLayoutChanged();
|
|
85
110
|
auto view = Parent();
|
|
@@ -92,64 +117,65 @@ void ContentIslandComponentView::ConnectInternal() noexcept {
|
|
|
92
117
|
strongThis->ParentLayoutChanged();
|
|
93
118
|
}
|
|
94
119
|
}));
|
|
95
|
-
view = view.Parent();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
void ContentIslandComponentView::RegisterForRootIslandEvents() noexcept {
|
|
100
|
-
m_parentContentIsland = ParentContentIsland();
|
|
101
120
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
}
|
|
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
|
+
}
|
|
115
132
|
|
|
116
|
-
|
|
117
|
-
if (m_islandStateChangedToken) {
|
|
118
|
-
m_parentContentIsland.StateChanged(m_islandStateChangedToken);
|
|
119
|
-
m_islandStateChangedToken = {};
|
|
120
|
-
m_parentContentIsland = nullptr;
|
|
133
|
+
view = view.Parent();
|
|
121
134
|
}
|
|
122
135
|
}
|
|
123
136
|
|
|
124
137
|
void ContentIslandComponentView::OnMounted() noexcept {
|
|
125
|
-
|
|
138
|
+
m_parentContentIsland = ParentContentIsland();
|
|
139
|
+
ConnectInternal();
|
|
126
140
|
}
|
|
127
141
|
|
|
128
142
|
void ContentIslandComponentView::OnUnmounted() noexcept {
|
|
129
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
|
+
|
|
130
153
|
if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) {
|
|
131
154
|
m_navigationHost.DepartFocusRequested(m_navigationHostDepartFocusRequestedToken);
|
|
132
155
|
m_navigationHostDepartFocusRequestedToken = {};
|
|
133
156
|
}
|
|
134
|
-
UnregisterForRootIslandEvents();
|
|
135
157
|
}
|
|
136
158
|
|
|
137
159
|
void ContentIslandComponentView::ParentLayoutChanged() noexcept {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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));
|
|
153
179
|
}
|
|
154
180
|
|
|
155
181
|
winrt::Windows::Foundation::IInspectable ContentIslandComponentView::CreateAutomationProvider() noexcept {
|
|
@@ -205,7 +231,10 @@ void ContentIslandComponentView::onGotFocus(
|
|
|
205
231
|
const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept {
|
|
206
232
|
auto gotFocusEventArgs = args.as<winrt::Microsoft::ReactNative::implementation::GotFocusEventArgs>();
|
|
207
233
|
const auto navigationReason = GetFocusNavigationReason(gotFocusEventArgs->Direction());
|
|
208
|
-
m_navigationHost
|
|
234
|
+
if (m_navigationHost)
|
|
235
|
+
m_navigationHost.NavigateFocus(winrt::Microsoft::UI::Input::FocusNavigationRequest::Create(navigationReason));
|
|
236
|
+
else
|
|
237
|
+
m_pendingNavigateFocus = navigationReason;
|
|
209
238
|
}
|
|
210
239
|
|
|
211
240
|
ContentIslandComponentView::~ContentIslandComponentView() noexcept {
|
|
@@ -264,6 +293,9 @@ void ContentIslandComponentView::Connect(const winrt::Microsoft::UI::Content::Co
|
|
|
264
293
|
m_childSiteLink.Connect(contentIsland);
|
|
265
294
|
} else {
|
|
266
295
|
m_islandToConnect = contentIsland;
|
|
296
|
+
if (isMounted()) {
|
|
297
|
+
ConnectInternal();
|
|
298
|
+
}
|
|
267
299
|
}
|
|
268
300
|
}
|
|
269
301
|
|