Working on new root canvas stuff
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
#include "PlayerControllerBase.h"
|
||||
#include "Common.h"
|
||||
#include "RootCanvas.h"
|
||||
#include "Tangible.h"
|
||||
#include "TangibleManager.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "Blueprint/WidgetTree.h"
|
||||
#include "Components/InputComponent.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
@@ -63,7 +65,17 @@ FVector2D AlxPlayerControllerBase::GetLookAtPixel(const UObject *Context)
|
||||
|
||||
void AlxPlayerControllerBase::BeginPlay()
|
||||
{
|
||||
// Build the root UMG stack BEFORE Super::BeginPlay. Super calls
|
||||
// ReceiveBeginPlay, which fires the Blueprint Event BeginPlay;
|
||||
// BP code there may immediately try to add widgets to RootCanvas,
|
||||
// so the canvas must already exist.
|
||||
RootWidget = CreateWidget<UlxRootWidget>(this);
|
||||
RootCanvas = RootWidget->WidgetTree->ConstructWidget<UlxRootCanvasPanel>();
|
||||
RootWidget->WidgetTree->RootWidget = RootCanvas;
|
||||
RootWidget->AddToViewport(0);
|
||||
|
||||
Super::BeginPlay();
|
||||
|
||||
if (FSlateApplication::IsInitialized())
|
||||
{
|
||||
FocusChangingHandle = FSlateApplication::Get().OnFocusChanging().AddUObject(
|
||||
@@ -78,6 +90,14 @@ void AlxPlayerControllerBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
FSlateApplication::Get().OnFocusChanging().Remove(FocusChangingHandle);
|
||||
FocusChangingHandle.Reset();
|
||||
}
|
||||
|
||||
if (IsValid(RootWidget))
|
||||
{
|
||||
RootWidget->RemoveFromParent();
|
||||
}
|
||||
RootWidget = nullptr;
|
||||
RootCanvas = nullptr;
|
||||
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
@@ -111,11 +131,18 @@ UInputComponent* AlxPlayerControllerBase::GetWidgetInputComponent(UUserWidget *W
|
||||
return Cast<UInputComponent>(Value);
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::WidgetRequestInputMode(UUserWidget *Widget, bool ShowPointer, bool BlockInput, UWidget *Focus, bool EnableInputComponent)
|
||||
void AlxPlayerControllerBase::RestoreFocusToFrontWidget(const UObject *Context)
|
||||
{
|
||||
// This will trigger UpdateInputMode to shift focus back to
|
||||
// the front window, if the front window wants focus.
|
||||
FromContext(Context)->LastWidgetGrantedFocus = nullptr;
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::AddWidgetToRoot(UUserWidget *Widget)
|
||||
{
|
||||
if (!IsValid(Widget))
|
||||
{
|
||||
UE_LOG(LogLuprexIntegration, Error, TEXT("WidgetRequestInputMode called with an invalid widget."));
|
||||
UE_LOG(LogLuprexIntegration, Error, TEXT("AddWidgetToRoot called with an invalid widget."));
|
||||
return;
|
||||
}
|
||||
APlayerController *OwningPC = Widget->GetOwningPlayer();
|
||||
@@ -123,53 +150,21 @@ void AlxPlayerControllerBase::WidgetRequestInputMode(UUserWidget *Widget, bool S
|
||||
if (PC == nullptr)
|
||||
{
|
||||
UE_LOG(LogLuprexIntegration, Error,
|
||||
TEXT("WidgetRequestInputMode: widget '%s' owning player is not an AlxPlayerControllerBase (got %s)."),
|
||||
TEXT("AddWidgetToRoot: widget '%s' owning player is not an AlxPlayerControllerBase (got %s)."),
|
||||
*Widget->GetName(), *GetNameSafe(OwningPC));
|
||||
return;
|
||||
}
|
||||
FlxInputModeRequest Req;
|
||||
Req.Widget = Widget;
|
||||
Req.Focus = Focus;
|
||||
Req.ShowPointer = ShowPointer;
|
||||
Req.BlockInput = BlockInput;
|
||||
Req.EnableInputComponent = EnableInputComponent;
|
||||
PC->InputModeRequests.Request(Req);
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::PushInputComponent(UInputComponent* InInputComponent)
|
||||
{
|
||||
if (InInputComponent)
|
||||
if (PC->RootCanvas == nullptr)
|
||||
{
|
||||
// Widgets don't go on the current input stack. Instead, they're
|
||||
// tracked in InputModeRequests, where the PlayerController can arbitrate
|
||||
// focus, pointer visibility, and input blocking across them.
|
||||
if (UUserWidget *Widget = Cast<UUserWidget>(InInputComponent->GetOuter()))
|
||||
{
|
||||
InputModeRequests.SetEnableInputComponent(Widget, true);
|
||||
return;
|
||||
}
|
||||
CurrentInputStack.RemoveSingle(InInputComponent);
|
||||
CurrentInputStack.Add(InInputComponent);
|
||||
UE_LOG(LogLuprexIntegration, Error,
|
||||
TEXT("AddWidgetToRoot: root canvas is not initialized, this is probably an initialization order issue"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool AlxPlayerControllerBase::PopInputComponent(UInputComponent* InInputComponent)
|
||||
{
|
||||
if (InInputComponent)
|
||||
{
|
||||
if (UUserWidget *Widget = Cast<UUserWidget>(InInputComponent->GetOuter()))
|
||||
{
|
||||
InputModeRequests.SetEnableInputComponent(Widget, false);
|
||||
InInputComponent->ClearBindingValues();
|
||||
return true;
|
||||
}
|
||||
if (CurrentInputStack.RemoveSingle(InInputComponent) > 0)
|
||||
{
|
||||
InInputComponent->ClearBindingValues();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
if (Widget->GetParent() == PC->RootCanvas) return;
|
||||
|
||||
UlxRootCanvasSlot *Slot = PC->RootCanvas->AddChildToRootCanvas(Widget);
|
||||
Slot->SetZOrder(0);
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::BuildInputStack(TArray<UInputComponent*>& InputStack)
|
||||
@@ -216,51 +211,51 @@ void AlxPlayerControllerBase::BuildInputStack(TArray<UInputComponent*>& InputSta
|
||||
InputStack.Push(InputComponent);
|
||||
}
|
||||
|
||||
// The current input stack is unlikely to have anything,
|
||||
// given that we've moved widgets to their own mechanism.
|
||||
// But, if there's anything here, sort it and push it.
|
||||
if (!CurrentInputStack.IsEmpty())
|
||||
// Get the widget slots.
|
||||
TArray<UlxRootCanvasSlot*> WidgetSlots = RootCanvas->GetSortedUserWidgets();
|
||||
|
||||
// Generate a set of input components that are being managed by the WidgetSlots.
|
||||
TSet<UInputComponent*> WidgetManagedComponents;
|
||||
for (UlxRootCanvasSlot *Slot : WidgetSlots)
|
||||
{
|
||||
TArray<UInputComponent*, TInlineAllocator<20>> Pushed;
|
||||
for (int32 Idx = 0; Idx < CurrentInputStack.Num(); ++Idx)
|
||||
{
|
||||
UInputComponent* IC = CurrentInputStack[Idx].Get();
|
||||
if (IsValid(IC)) Pushed.Add(IC);
|
||||
else CurrentInputStack.RemoveAt(Idx--);
|
||||
}
|
||||
|
||||
Pushed.StableSort([](const UInputComponent& A, const UInputComponent& B)
|
||||
{
|
||||
if (A.bBlockInput != B.bBlockInput) return !A.bBlockInput;
|
||||
return A.Priority < B.Priority;
|
||||
});
|
||||
|
||||
InputStack.Append(Pushed);
|
||||
UUserWidget *Widget = Cast<UUserWidget>(Slot->GetContent());
|
||||
UInputComponent *IC = GetWidgetInputComponent(Widget);
|
||||
if (IC) WidgetManagedComponents.Add(IC);
|
||||
}
|
||||
|
||||
// Push the widget input components from InputModeRequests. Requests
|
||||
// are ordered high-priority first; the input stack is processed
|
||||
// from top (last index) down, so we push back-to-front: low
|
||||
// priority first, high priority last (ending up on top).
|
||||
const TArray<FlxInputModeRequest> &Requests = InputModeRequests.GetRequests();
|
||||
for (int32 Idx = Requests.Num() - 1; Idx >= 0; --Idx)
|
||||
// Add components in the CurrentInputStack, *unless* they are being managed
|
||||
// by widgets. If they're being managed by widgets, they get added later.
|
||||
for (int32 Idx=0; Idx<CurrentInputStack.Num(); ++Idx)
|
||||
{
|
||||
const FlxInputModeRequest &Req = Requests[Idx];
|
||||
if (!Req.EnableInputComponent) continue;
|
||||
UUserWidget *Widget = Req.Widget;
|
||||
if (!Widget->IsInViewport()) continue;
|
||||
if (UInputComponent *IC = GetWidgetInputComponent(Widget))
|
||||
UInputComponent* IC = CurrentInputStack[Idx].Get();
|
||||
if (IsValid(IC))
|
||||
{
|
||||
IC->bBlockInput = Req.BlockInput;
|
||||
InputStack.Push(IC);
|
||||
if (!WidgetManagedComponents.Contains(IC)) InputStack.Push(IC);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentInputStack.RemoveAt(Idx--);
|
||||
}
|
||||
}
|
||||
|
||||
// Now add the widget-managed input components.
|
||||
for (UlxRootCanvasSlot *Slot : ReverseIterate(WidgetSlots))
|
||||
{
|
||||
if (Slot->EnableEnhancedInput)
|
||||
{
|
||||
UUserWidget *Widget = Cast<UUserWidget>(Slot->GetContent());
|
||||
UInputComponent *IC = GetWidgetInputComponent(Widget);
|
||||
if (IC)
|
||||
{
|
||||
IC->bBlockInput = Slot->BlockInput;
|
||||
InputStack.Push(IC);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::UpdateInputMode()
|
||||
{
|
||||
InputModeRequests.GarbageCollect();
|
||||
|
||||
// Get all the various objects we need to be able to manipulate
|
||||
// the input mode.
|
||||
UGameViewportClient *GameViewportClient = GetWorld()->GetGameViewport();
|
||||
@@ -273,19 +268,24 @@ void AlxPlayerControllerBase::UpdateInputMode()
|
||||
TSharedPtr<FSlateUser> SlateUser = LocalPlayer->GetSlateUser();
|
||||
if (!SlateUser.IsValid()) return;
|
||||
|
||||
// The first active entry in InputModeRequests dictates the
|
||||
// pointer / capture / focus state. If there are no requests at all,
|
||||
// fall back to a static default-constructed request.
|
||||
static const FlxInputModeRequest EmptyRequest;
|
||||
const FlxInputModeRequest *Top = &EmptyRequest;
|
||||
for (const FlxInputModeRequest &Req : InputModeRequests.GetRequests())
|
||||
|
||||
// Get the desired configuration from the first widget.
|
||||
// TODO: Maybe we don't have to sort the whole array.
|
||||
TArray<UlxRootCanvasSlot*> WidgetSlots = RootCanvas->GetSortedUserWidgets();
|
||||
UUserWidget *Widget = nullptr;
|
||||
UWidget *Focus = nullptr;
|
||||
bool ShowPointer = false;
|
||||
if (!WidgetSlots.IsEmpty())
|
||||
{
|
||||
if (Req.Widget->IsInViewport()) { Top = &Req; break; }
|
||||
UlxRootCanvasSlot *Top = WidgetSlots[0];
|
||||
Widget = Cast<UUserWidget>(Top->GetContent());
|
||||
Focus = Widget->GetDesiredFocusWidget();
|
||||
ShowPointer = Top->ShowPointer;
|
||||
}
|
||||
|
||||
SetShowMouseCursor(Top->ShowPointer);
|
||||
SetShowMouseCursor(ShowPointer);
|
||||
|
||||
if (Top->ShowPointer)
|
||||
if (ShowPointer)
|
||||
{
|
||||
// Only release capture if the viewport is currently holding it
|
||||
// (e.g. we just came from GameOnly). A blanket ReleaseMouseCapture
|
||||
@@ -321,20 +321,20 @@ void AlxPlayerControllerBase::UpdateInputMode()
|
||||
// viewport client notifies us of that fact. We then focus the
|
||||
// widget if possible.
|
||||
//
|
||||
if ((!Top->ShowPointer) || (LastRequestGrantedFocus != Top->SequenceNumber))
|
||||
if ((!ShowPointer) || (LastWidgetGrantedFocus != Focus))
|
||||
{
|
||||
if (Top->Focus)
|
||||
if (Focus)
|
||||
{
|
||||
if (TSharedPtr<SWidget> SlateFocus = Top->Focus->GetCachedWidget())
|
||||
if (TSharedPtr<SWidget> SlateFocus = Focus->GetCachedWidget())
|
||||
{
|
||||
SlateOperations.SetUserFocus(SlateFocus.ToSharedRef());
|
||||
LastRequestGrantedFocus = Top->SequenceNumber;
|
||||
LastWidgetGrantedFocus = Focus;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SlateOperations.SetUserFocus(ViewportWidgetRef);
|
||||
LastRequestGrantedFocus = Top->SequenceNumber;
|
||||
LastWidgetGrantedFocus = nullptr;
|
||||
}
|
||||
}
|
||||
else if (TSharedPtr<SWidget> ClickedWidget = ClickToFocusTarget.Pin())
|
||||
|
||||
Reference in New Issue
Block a user