Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "adding fix for shadow in rounded box",
"packageName": "react-native-windows",
"email": "protikbiswas@microsoft.com",
"dependentChangeType": "patch"
}
25 changes: 16 additions & 9 deletions vnext/Microsoft.ReactNative/CompositionSwitcher.idl
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,16 @@ enum SnapPointsAlignment {
void Opacity(Single value);
void BlurRadius(Single value);
void Color(Windows.UI.Color value);
void Mask(IBrush mask);
void SourcePolicy(CompositionDropShadowSourcePolicy policy);
}

[webhosthidden][experimental] interface IVisual {
[webhosthidden][experimental] enum CompositionDropShadowSourcePolicy {
Default = 0,
InheritedOnly = 1
};

[webhosthidden][experimental] interface IVisual {
void InsertAt(IVisual visual, Int32 index);
void Remove(IVisual visual);
IVisual GetAt(UInt32 index);
Expand All @@ -72,14 +79,14 @@ enum SnapPointsAlignment {
void AnimationClass(AnimationClass value);
}

[webhosthidden][experimental] interface ISpriteVisual
[webhosthidden][experimental] interface ISpriteVisual
requires IVisual
{
void Brush(IBrush brush);
void Shadow(IDropShadow shadow);
}

[webhosthidden][experimental] interface IRoundedRectangleVisual
[webhosthidden][experimental] interface IRoundedRectangleVisual
requires IVisual
{
void Brush(IBrush brush);
Expand All @@ -88,13 +95,13 @@ enum SnapPointsAlignment {
void StrokeThickness(Single value);
}

[webhosthidden][experimental] interface IScrollPositionChangedArgs {
[webhosthidden][experimental] interface IScrollPositionChangedArgs {
Windows.Foundation.Numerics.Vector2 Position {
get;
};
}

[webhosthidden][experimental] interface IScrollVisual
[webhosthidden][experimental] interface IScrollVisual
requires IVisual
{
void Brush(IBrush brush);
Expand Down Expand Up @@ -122,7 +129,7 @@ enum SnapPointsAlignment {
void SnapToAlignment(SnapPointsAlignment alignment);
}

[webhosthidden][experimental] interface IActivityVisual
[webhosthidden][experimental] interface IActivityVisual
requires IVisual
{
void Size(Single value);
Expand All @@ -131,7 +138,7 @@ enum SnapPointsAlignment {
void StopAnimation();
}

[webhosthidden][experimental] interface ICaretVisual {
[webhosthidden][experimental] interface ICaretVisual {
IVisual InnerVisual {
get;
};
Expand All @@ -144,7 +151,7 @@ enum SnapPointsAlignment {
void Brush(IBrush brush);
}

[webhosthidden][experimental] interface IFocusVisual {
[webhosthidden][experimental] interface IFocusVisual {
IVisual InnerVisual {
get;
};
Expand All @@ -158,7 +165,7 @@ enum SnapPointsAlignment {
};
}

[webhosthidden][experimental] interface ICompositionContext {
[webhosthidden][experimental] interface ICompositionContext {
ISpriteVisual CreateSpriteVisual();
IScrollVisual CreateScrollerVisual();
IRoundedRectangleVisual CreateRoundedRectangleVisual();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ struct CompositionTypeTraits<WindowsTypeTag> {
using CompositionStretch = winrt::Windows::UI::Composition::CompositionStretch;
using CompositionStrokeCap = winrt::Windows::UI::Composition::CompositionStrokeCap;
using CompositionSurfaceBrush = winrt::Windows::UI::Composition::CompositionSurfaceBrush;
using CompositionDropShadowSourcePolicy = winrt::Windows::UI::Composition::CompositionDropShadowSourcePolicy;
using Compositor = winrt::Windows::UI::Composition::Compositor;
using ContainerVisual = winrt::Windows::UI::Composition::ContainerVisual;
using CubicBezierEasingFunction = winrt::Windows::UI::Composition::CubicBezierEasingFunction;
Expand Down Expand Up @@ -127,6 +128,7 @@ struct CompositionTypeTraits<MicrosoftTypeTag> {
using CompositionStretch = winrt::Microsoft::UI::Composition::CompositionStretch;
using CompositionStrokeCap = winrt::Microsoft::UI::Composition::CompositionStrokeCap;
using CompositionSurfaceBrush = winrt::Microsoft::UI::Composition::CompositionSurfaceBrush;
using CompositionDropShadowSourcePolicy = winrt::Microsoft::UI::Composition::CompositionDropShadowSourcePolicy;
using Compositor = winrt::Microsoft::UI::Composition::Compositor;
using ContainerVisual = winrt::Microsoft::UI::Composition::ContainerVisual;
using CubicBezierEasingFunction = winrt::Microsoft::UI::Composition::CubicBezierEasingFunction;
Expand Down Expand Up @@ -224,6 +226,19 @@ struct CompDropShadow : public winrt::implements<
m_shadow.Color(color);
}

void Mask(winrt::Microsoft::ReactNative::Composition::Experimental::IBrush const &mask) noexcept {
if (mask) {
m_shadow.Mask(mask.as<typename TTypeRedirects::IInnerCompositionBrush>()->InnerBrush());
} else {
m_shadow.Mask(nullptr);
}
}

void SourcePolicy(
winrt::Microsoft::ReactNative::Composition::Experimental::CompositionDropShadowSourcePolicy policy) noexcept {
m_shadow.SourcePolicy(static_cast<typename TTypeRedirects::CompositionDropShadowSourcePolicy>(policy));
}

private:
typename TTypeRedirects::DropShadow m_shadow;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,86 @@ void ComponentView::applyShadowProps(const facebook::react::ViewProps &viewProps
shadow.Color(theme()->Color(*viewProps.shadowColor));
}

Visual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(shadow);
// Check if any border radius is set
auto borderMetrics = BorderPrimitive::resolveAndAlignBorderMetrics(m_layoutMetrics, viewProps);
bool hasBorderRadius = borderMetrics.borderRadii.topLeft.horizontal != 0 ||
borderMetrics.borderRadii.topRight.horizontal != 0 || borderMetrics.borderRadii.bottomLeft.horizontal != 0 ||
borderMetrics.borderRadii.bottomRight.horizontal != 0 || borderMetrics.borderRadii.topLeft.vertical != 0 ||
borderMetrics.borderRadii.topRight.vertical != 0 || borderMetrics.borderRadii.bottomLeft.vertical != 0 ||
borderMetrics.borderRadii.bottomRight.vertical != 0;

if (hasBorderRadius) {
// When borderRadius is set, we need to create a shadow mask that follows the rounded rectangle shape.
// Use CompositionVisualSurface to capture the clipped visual's appearance as the shadow mask.
bool maskSet = false;

// Try Microsoft (WinUI3) Composition first
auto msCompositor =
winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerCompositor(
m_compContext);
if (msCompositor) {
auto innerVisual =
winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual(
Visual());
if (innerVisual) {
// Create a VisualSurface that captures the visual (with its clip applied)
auto visualSurface = msCompositor.CreateVisualSurface();
visualSurface.SourceVisual(innerVisual);
visualSurface.SourceSize(
{m_layoutMetrics.frame.size.width * m_layoutMetrics.pointScaleFactor,
m_layoutMetrics.frame.size.height * m_layoutMetrics.pointScaleFactor});

// Create a brush from the visual surface to use as shadow mask
auto maskBrush = msCompositor.CreateSurfaceBrush(visualSurface);
maskBrush.Stretch(winrt::Microsoft::UI::Composition::CompositionStretch::Fill);

// Get the inner shadow and set the mask
auto innerShadow = winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::
InnerDropShadow(shadow);
if (innerShadow) {
innerShadow.Mask(maskBrush);
maskSet = true;
}
}
}

// Fallback to System (Windows.UI) Composition if Microsoft Composition is not available
if (!maskSet) {
auto sysCompositor =
winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::InnerCompositor(
m_compContext);
if (sysCompositor) {
auto innerVisual =
winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::InnerVisual(
Visual());
if (innerVisual) {
auto visualSurface = sysCompositor.CreateVisualSurface();
visualSurface.SourceVisual(innerVisual);
visualSurface.SourceSize(
{m_layoutMetrics.frame.size.width * m_layoutMetrics.pointScaleFactor,
m_layoutMetrics.frame.size.height * m_layoutMetrics.pointScaleFactor});

auto maskBrush = sysCompositor.CreateSurfaceBrush(visualSurface);
maskBrush.Stretch(winrt::Windows::UI::Composition::CompositionStretch::Fill);

auto innerShadow =
winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::InnerDropShadow(
shadow);
if (innerShadow) {
innerShadow.Mask(maskBrush);
}
}
}
}

// Apply shadow to OuterVisual (which is not clipped) so the shadow can extend beyond the clip
OuterVisual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(shadow);
Visual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(nullptr);
} else {
// No border radius - apply shadow directly to Visual (original behavior)
Visual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(shadow);
OuterVisual().as<winrt::Microsoft::ReactNative::Composition::Experimental::ISpriteVisual>().Shadow(nullptr);
}
}

void ComponentView::updateTransformProps(
Expand Down
Loading