- 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>
132 lines
4.1 KiB
C++
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();
|
|
}
|
|
}
|