Description

If SetAlignmentInViewport is called in the UMG Construct, it is ignored internally. This is due to a change that came in 5.1, so it worked fine in 5.0.

First, when the Set Alignment in Viewport node is called with Construct, it goes through the first half of the code below

void UUserWidget::SetAlignmentInViewport(FVector2D Alignment)
{
   if (UGameViewportSubsystem* Subsystem = UGameViewportSubsystem::Get(GetWorld()))
   {
      if (bIsManagedByGameViewportSubsystem)
      {
         FGameViewportWidgetSlot ViewportSlot = Subsystem->GetWidgetSlot(this);
         if (ViewportSlot.Alignment != Alignment)
         {
            ViewportSlot.Alignment = Alignment;
            Subsystem->SetWidgetSlot(this, ViewportSlot);
         }
      }
      else
      {
         FGameViewportWidgetSlot ViewportSlot;
         ViewportSlot.Alignment = Alignment;
         Subsystem->SetWidgetSlot(this, ViewportSlot);
      }
   }
} 

Then, in the code below, it tries to set Alighment for the Slot managed by Subsystem, but at the time of Construct, SlotInfo.FullScreenWidget is null, so nothing is actually set. FullScreenWidget is null at the time of Construct, so the process ends without actually setting anything.

void UGameViewportSubsystem::SetWidgetSlot(UWidget* Widget, FGameViewportWidgetSlot Slot)
{
   if (Widget && !Widget->HasAnyFlags(RF_BeginDestroyed))
   {
      TWeakObjectPtr<UWidget> TempObject = const_cast<UWidget*>(Widget);
      FSlotInfo& SlotInfo = ViewportWidgets.FindOrAdd(TempObject);
      Widget->bIsManagedByGameViewportSubsystem = true;
      SlotInfo.Slot = Slot;
      if (TSharedPtr<SConstraintCanvas> WidgetHost = SlotInfo.FullScreenWidget.Pin())
      {
         check(SlotInfo.FullScreenWidgetSlot);
         TPair<FMargin, bool> OffsetArgument = UE::UMG::Private::CalculateOffsetArgument(Slot);
         SlotInfo.FullScreenWidgetSlot->SetOffset(OffsetArgument.Get<0>());
         SlotInfo.FullScreenWidgetSlot->SetAutoSize(OffsetArgument.Get<1>());
         SlotInfo.FullScreenWidgetSlot->SetAnchors(Slot.Anchors);
         SlotInfo.FullScreenWidgetSlot->SetAlignment(Slot.Alignment);
         WidgetHost->Invalidate(EInvalidateWidgetReason::Layout);
         //todo set the zorder
      }
   }
} 

FullScreenWidget is set at the timing of AddtoViewport of Construct. Specifically, the settings are made in the following code. The problem occurs because the Alighment is not set, but the default value is entered.

void UGameViewportSubsystem::AddToScreen(UWidget* Widget, ULocalPlayer* Player, FGameViewportWidgetSlot& Slot)
{
   ...
      SlotInfo.Slot = Slot;
   SlotInfo.LocalPlayer = Player;
   SlotInfo.FullScreenWidget = FullScreenCanvas;
   SlotInfo.FullScreenWidgetSlot = RawSlot; 

In addition, because ViewportWidgets in the SetWidgetSlot function contains elements at the time of Construct, ViewportWidgets.FindOrAdd works with Find instead of Add. Therefore, the Alighment set in SetAlignmentInViewport is passed to SetWidgetSlot, but this is not working either.

To work around this issue without modifying the engine code, you need to call Set Alignment in Viewport after AddToViewport or OnInitialised. In the latter case, the ViewportWidgets does not yet contain any elements, so the one with the Alighment set is added. As a result, Set Alignment in Viewport works as intended.

Steps to Reproduce
  1. Open repro project 
  2. PIE

expect : red 2d box  appears in the center of the white sphere
result :  red 2d box appears slightly to the lower right of the sphere

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-175311 in the post.

2
Login to Vote

Fixed
ComponentUE - Editor - UI Systems - UMG
Affects Versions5.15.2
Target Fix5.3
Fix Commit24871475
Main Commit24871475
CreatedJan 27, 2023
ResolvedMar 31, 2023
UpdatedJan 8, 2024