Slate blurred border shadow

Hey!

Since SBorder wasnt quite enough to get the desired result I made use of SOverlay widget and ended up with something like this: (mouse cursor imitates ‘light source’ so the shadow moves around)

44077-ezgif.com-gif-maker+(1).gif

The shadow itself is not blurred but it’s just the matter of used brush ; )

Heres the code if anyone needs it (note that I’m using Editor style in this example so you will have to add needed dependencies to your project for it to compile. Also, remove Tick function from SShadowBorder class to remove sample shadow updates)

#pragma once

#include "SlateBasics.h"
#include "EditorStyle.h"

class SShadowBorder : public SCompoundWidget
{
public:
	SLATE_BEGIN_ARGS(SShadowBorder)
		: _Content()
		, _DefaultPadding(10)
	{}
		SLATE_DEFAULT_SLOT(FArguments, Content)
		SLATE_ATTRIBUTE(float, DefaultPadding)
		SLATE_ATTRIBUTE(FMargin, DynamicPadding)
	SLATE_END_ARGS()
	
	void Construct(const FArguments& InArgs)
	{
		DefaultPadding = InArgs._DefaultPadding;
		DynamicPadding = InArgs._DynamicPadding;

		ChildSlot
		[
			SNew(SOverlay)
			// First, add slot with actual shadow border that will move around based on DynamicPadding
			+SOverlay::Slot()
			.Padding(TAttribute<FMargin>::Create(TAttribute<FMargin>::FGetter::CreateRaw(this, &SShadowBorder::GetDynamicPadding)))
			[
				SNew(SBorder)
				.BorderImage(FEditorStyle::Get().GetBrush("ProgressBar.Background"))
				.BorderBackgroundColor(FColor::Black)
			]
			// Now, on the top of the shadow, add actual content
			+SOverlay::Slot()
			.Padding(DefaultPadding.Get())
			[
				InArgs._Content.Widget
			]
		];
	}

	virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override
	{
		// Use position of the cursor like a 'light source' to imitate shadow casting by content
		FVector2D MousePos = FSlateApplication::Get().GetCursorPos();

		FVector2D AbsoluteWidgetPos = AllottedGeometry.LocalToAbsolute(FVector2D::ZeroVector);
		FVector2D WidgetSize = AllottedGeometry.GetLocalSize();
		
		FVector2D WidgetCenter = AbsoluteWidgetPos + WidgetSize / 2.0f;

		FVector2D Delta = MousePos - WidgetCenter;
		Delta = Delta / 10.0f;

		float MaxPadding = 2 * DefaultPadding.Get();

		FMargin newPadding;
		newPadding.Left = FMath::Clamp<float>(DefaultPadding.Get() - Delta.X, 0, MaxPadding);
		newPadding.Top = FMath::Clamp<float>(DefaultPadding.Get() - Delta.Y, 0, MaxPadding);
		newPadding.Right = FMath::Clamp<float>(DefaultPadding.Get() + Delta.X, 0, MaxPadding);
		newPadding.Bottom = FMath::Clamp<float>(DefaultPadding.Get() + Delta.Y, 0, MaxPadding);

		DynamicPadding.Set(newPadding);

		SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
	}

	FMargin GetDynamicPadding()  const
	{
		return DynamicPadding.Get();
	}

	TAttribute<FMargin> DynamicPadding;
	TAttribute<float> DefaultPadding;
};

Cheers!

3 Likes