Desired Size returns correct size only after the underlying slate widgets have been created. I have very similar thing in my project where I am showing interaction options in a widget when certain objects are interacted with, so the player can choose an option.
The interaction point is first projected to screen space and the widget is positioned to the projected screen location but is also clamped inside screen.
I am using C++ and UMG-View Models to do this but maybe you can get some help from it.
// Header
UFUNCTION(BlueprintPure, Category = "Widget Utils")
static FVector2D ClampWidgetPositionInsideViewport(APlayerController* PlayerController, const FVector2D& ScreenLocation, const FMargin& Margin);
// Cpp
FVector2D UWidgetUtils::ClampWidgetPositionInsideViewport(APlayerController* PlayerController, const FVector2D& ScreenLocation, const FMargin& Margin)
{
if (!IsValid(PlayerController))
{
return FVector2D::ZeroVector;
}
int32 VPSizeX, VPSizeY;
PlayerController->GetViewportSize(VPSizeX, VPSizeY);
if (VPSizeX == 0 || VPSizeY == 0)
{
return FVector2D::ZeroVector;
}
const float MinX = Margin.Left;
const float MaxX = ((float)VPSizeX) - Margin.Right;
const float MinY = Margin.Top;
const float MaxY = ((float)VPSizeY) - Margin.Bottom;
if (MinX >= MaxX || MinY >= MaxY)
{
return FVector2D(VPSizeX / 2.0f, VPSizeY / 2.0f);
}
FVector2D Result = ScreenLocation;
Result.X = FMath::Clamp(ScreenLocation.X, MinX, MaxX);
Result.Y = FMath::Clamp(ScreenLocation.Y, MinY, MaxY);
return Result;
}
This very simple function clamps the given screen location inside viewport with additional margin from each screen edge.
Then in view model I am calculating the screen transform for the widget using the following code:
// Widget screen location is the same as interactable screen location
FVector2D NewWidgetLocation = InteractableScreenLocation;
// Center alignment
NewWidgetLocation = NewWidgetLocation - FVector2D(WidgetSize.X / 2.0f, WidgetSize.Y / 2.0f);
// Clamp inside screen
NewWidgetLocation = UWidgetUtils::ClampWidgetPositionInsideViewport(PlayerController, NewWidgetLocation, FMargin(WidgetSize.X, WidgetSize.Y, WidgetSize.X, WidgetSize.Y));
SetOptionWidgetScreenLocation(NewWidgetLocation);
The code expects that the widget pivot is at the center. WidgetSize is set to the view model by using Desired Size of the widget. SetOptionWidgetScreenLocation just sets a property which is Vector2D with field notify, so the widget can bind into it.
Hope that you get it solved.