Files
ZMMO/Source/ZMMO/Game/UI/Common/UIDraggableWindow_Base.cpp
Mateus Rodrigues 7fa10b4cb9 fix(ui/loading): memoiza steps quando widget em async load + draggable status
- UIFrontEndFlowSubsystem: HandlePostLoadMap/HandlePlayerSpawned agora
  registram o StepId em PendingLoadingSteps_ quando ActiveLoadingScreen
  ainda eh null (race com RequestAsyncLoad do WBP_Loading). Lambda do
  callback async drena o set apos criar a tela, fechando o loading
  retroativamente. Servidor rapido (TryFinalizeEnterWorld) deixava o
  cliente preso em "entrando no mundo" quando o async load atrasava.

- UIPlayerStatus_Window: herda de UIDraggableWindow_Base (drag + persist
  via UZMMOUISaveGame). Rename CloseBtn -> Button_Close, adiciona
  Background_Panel/Container_Header/Container_Stats, NativeOnKeyDown
  (Alt+A fecha), NativeGetDesiredFocusTarget=self. Remove bIsBackHandler
  pra parar o "Cannot create action binding" no log (projeto nao tem
  DefaultBackAction no CommonUISettings).

- UIInGameFlowSubsystem.SetState: skip re-push do HUD quando voltando de
  StatusWindow/Inventory/Menu pro Playing (Old != None && != Playing) pra
  evitar Construct novo do HUD e o flicker dos textos HP/SP.

- Novos: UIDraggableWindow_Base (drag generico + persistencia) e
  UIWindowSaveGame (TMap<FName, FVector2D> por WindowId).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 14:50:49 -03:00

132 lines
4.1 KiB
C++

#include "UIDraggableWindow_Base.h"
#include "Components/Border.h"
#include "Engine/Engine.h"
#include "Engine/GameViewportClient.h"
#include "Input/Reply.h"
#include "UIWindowSaveGame.h"
void UUIDraggableWindow_Base::NativeOnActivated()
{
Super::NativeOnActivated();
ApplySavedPosition();
// Validacao usa translation + viewport size, ambos disponiveis ja' aqui
// (sem precisar do CachedGeometry/layout pass).
ValidatePositionInViewport();
}
void UUIDraggableWindow_Base::ApplySavedPosition()
{
const FName Id = GetWindowId();
if (Id.IsNone()) return;
UZMMOUISaveGame* Save = UZMMOUISaveGame::LoadOrCreate();
FVector2D Pos;
if (Save && Save->TryGetWindowPosition(Id, Pos))
{
SetRenderTranslation(Pos);
}
}
void UUIDraggableWindow_Base::PersistCurrentPosition()
{
const FName Id = GetWindowId();
if (Id.IsNone()) return;
UZMMOUISaveGame* Save = UZMMOUISaveGame::LoadOrCreate();
if (!Save) return;
Save->SetWindowPosition(Id, GetRenderTransform().Translation);
UZMMOUISaveGame::SaveNow(Save);
}
bool UUIDraggableWindow_Base::IsDragHandleHit(const FPointerEvent& MouseEvent) const
{
if (!Border_DragHandle) return false;
const FGeometry HandleGeom = Border_DragHandle->GetCachedGeometry();
return HandleGeom.IsUnderLocation(MouseEvent.GetScreenSpacePosition());
}
FReply UUIDraggableWindow_Base::NativeOnMouseButtonDown(const FGeometry& InGeometry,
const FPointerEvent& InMouseEvent)
{
if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && IsDragHandleHit(InMouseEvent))
{
bMaybeDragging = true;
bDragging = false;
DragStartScreenPos = InMouseEvent.GetScreenSpacePosition();
WidgetStartTranslation = GetRenderTransform().Translation;
return FReply::Handled().CaptureMouse(TakeWidget());
}
return Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
}
FReply UUIDraggableWindow_Base::NativeOnMouseMove(const FGeometry& InGeometry,
const FPointerEvent& InMouseEvent)
{
if (!bMaybeDragging && !bDragging)
{
return Super::NativeOnMouseMove(InGeometry, InMouseEvent);
}
const FVector2D Delta = InMouseEvent.GetScreenSpacePosition() - DragStartScreenPos;
if (!bDragging && Delta.SizeSquared() < DragStartThresholdPx * DragStartThresholdPx)
{
return FReply::Handled();
}
bDragging = true;
SetRenderTranslation(WidgetStartTranslation + Delta);
return FReply::Handled();
}
FReply UUIDraggableWindow_Base::NativeOnMouseButtonUp(const FGeometry& InGeometry,
const FPointerEvent& InMouseEvent)
{
if (InMouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && (bMaybeDragging || bDragging))
{
const bool bWasRealDrag = bDragging;
bMaybeDragging = false;
bDragging = false;
if (bWasRealDrag)
{
PersistCurrentPosition();
}
return FReply::Handled().ReleaseMouseCapture();
}
return Super::NativeOnMouseButtonUp(InGeometry, InMouseEvent);
}
void UUIDraggableWindow_Base::NativeOnMouseLeave(const FPointerEvent& InMouseEvent)
{
Super::NativeOnMouseLeave(InMouseEvent);
}
void UUIDraggableWindow_Base::ValidatePositionInViewport()
{
if (!GEngine || !GEngine->GameViewport) return;
FVector2D ViewportSize;
GEngine->GameViewport->GetViewportSize(ViewportSize);
if (ViewportSize.IsNearlyZero()) return;
// RenderTransform.Translation esta em coordenadas de viewport (pixels),
// independente de DPI/offset do editor — comparacao direta funciona em
// PIE e em build standalone. Assume slot centralizado (default UMG
// stack); meio-viewport e' o quanto a translation pode crescer antes
// da janela sair completamente.
const FVector2D Tr = GetRenderTransform().Translation;
const float HalfX = ViewportSize.X * 0.5f;
const float HalfY = ViewportSize.Y * 0.5f;
const bool bOutOfBounds =
FMath::Abs(Tr.X) > HalfX - MinVisiblePixels ||
FMath::Abs(Tr.Y) > HalfY - MinVisiblePixels;
UE_LOG(LogTemp, Verbose, TEXT("DraggableWindow::Validate translation=(%.0f,%.0f) viewport=(%.0f,%.0f) outOfBounds=%d"),
Tr.X, Tr.Y, ViewportSize.X, ViewportSize.Y, bOutOfBounds ? 1 : 0);
if (bOutOfBounds)
{
SetRenderTranslation(FVector2D::ZeroVector);
PersistCurrentPosition();
}
}