Lots of work on focus management
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
[/Script/Engine.Engine]
|
||||
GameViewportClientClassName=/Script/CommonUI.CommonGameViewportClient
|
||||
GameViewportClientClassName=/Script/Integration.lxViewportClient
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
GameDefaultMap=/Game/LpxLevel.LpxLevel
|
||||
|
||||
Binary file not shown.
@@ -1,10 +1,10 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// InputEvents.cpp
|
||||
// InputModeRequest.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "InputEvents.h"
|
||||
#include "InputModeRequest.h"
|
||||
#include "Common.h"
|
||||
|
||||
bool FlxInputModeRequest::operator==(const FlxInputModeRequest &Other) const
|
||||
@@ -12,7 +12,8 @@ bool FlxInputModeRequest::operator==(const FlxInputModeRequest &Other) const
|
||||
return (Widget == Other.Widget) &&
|
||||
(Focus == Other.Focus) &&
|
||||
(ShowPointer == Other.ShowPointer) &&
|
||||
(BlockInput == Other.BlockInput);
|
||||
(BlockInput == Other.BlockInput) &&
|
||||
(EnableInputComponent == Other.EnableInputComponent);
|
||||
}
|
||||
|
||||
bool FlxInputModeRequests::SanityCheck(const FlxInputModeRequest &Request)
|
||||
@@ -42,60 +43,55 @@ void FlxInputModeRequests::Request(const FlxInputModeRequest &NewRequest)
|
||||
View High, Low;
|
||||
SplitHighLow(High, Low);
|
||||
|
||||
// This is a simple test to see if anything is going to change.
|
||||
// If not, we return early and avoid setting the dirty bit.
|
||||
if (IsHigh)
|
||||
{
|
||||
if ((High.Num() > 0) && (High[0] == NewRequest)) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((Low.Num() > 0) && (Low[0] == NewRequest)) return;
|
||||
}
|
||||
// Stamp this request with a fresh sequence number.
|
||||
FlxInputModeRequest Stamped = NewRequest;
|
||||
Stamped.SequenceNumber = ++NextSequenceNumber;
|
||||
|
||||
// We're going to build a new version of the requests array.
|
||||
TArray<FlxInputModeRequest> Updated;
|
||||
|
||||
// Add all high priority requests to the updated array, new request first.
|
||||
if (IsHigh) Updated.Add(NewRequest);
|
||||
if (IsHigh) Updated.Add(Stamped);
|
||||
for (const FlxInputModeRequest &Req : High)
|
||||
if (Req.Widget != NewRequest.Widget) Updated.Add(Req);
|
||||
|
||||
// Add all low priority requests to the updated array, new request first.
|
||||
if (!IsHigh) Updated.Add(NewRequest);
|
||||
if (!IsHigh) Updated.Add(Stamped);
|
||||
for (const FlxInputModeRequest &Req : Low)
|
||||
if (Req.Widget != NewRequest.Widget) Updated.Add(Req);
|
||||
|
||||
Swap(Requests, Updated);
|
||||
Dirty = true;
|
||||
}
|
||||
|
||||
void FlxInputModeRequests::EnsureWidget(UUserWidget *Widget)
|
||||
void FlxInputModeRequests::SetEnableInputComponent(UUserWidget *Widget, bool EnableInputComponent)
|
||||
{
|
||||
for (const FlxInputModeRequest &Req : Requests)
|
||||
for (FlxInputModeRequest &Req : Requests)
|
||||
{
|
||||
if (Req.Widget == Widget) return;
|
||||
if (Req.Widget == Widget)
|
||||
{
|
||||
Req.EnableInputComponent = EnableInputComponent;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Request(FlxInputModeRequest(Widget, nullptr, false, false));
|
||||
FlxInputModeRequest NewReq;
|
||||
NewReq.Widget = Widget;
|
||||
NewReq.EnableInputComponent = EnableInputComponent;
|
||||
Request(NewReq);
|
||||
}
|
||||
|
||||
void FlxInputModeRequests::Remove(UUserWidget *Widget)
|
||||
{
|
||||
int32 N = Requests.Num();
|
||||
Requests.RemoveAll([Widget](const FlxInputModeRequest &Entry)
|
||||
{
|
||||
return Entry.Widget == Widget;
|
||||
});
|
||||
if (Requests.Num() < N) Dirty = true;
|
||||
}
|
||||
|
||||
void FlxInputModeRequests::GarbageCollect()
|
||||
{
|
||||
int32 N = Requests.Num();
|
||||
Requests.RemoveAll([](const FlxInputModeRequest &Entry)
|
||||
{
|
||||
return !IsValid(Entry.Widget);
|
||||
});
|
||||
if (Requests.Num() < N) Dirty = true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// InputEvents.h
|
||||
// InputModeRequest.h
|
||||
//
|
||||
// Custom input event dispatching system. Uses Unreal's
|
||||
// built-in input modes (GameOnly / UIOnly) with an
|
||||
@@ -12,21 +12,17 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "InputEvents.generated.h"
|
||||
#include "InputModeRequest.generated.h"
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FlxInputModeRequest
|
||||
//
|
||||
// A widget's declaration of interest in input events.
|
||||
//
|
||||
// Widget: The widget that wants to receive events.
|
||||
// Focus: The widget that should receive keyboard focus
|
||||
// while this request is active.
|
||||
// ShowPointer: If true, the mouse pointer should be
|
||||
// visible when this widget has control.
|
||||
// BlockInput: If true, input actions should be blocked from
|
||||
// reaching lower-priority consumers (e.g. the pawn).
|
||||
// Using this struct, a Widget can express a need for a
|
||||
// particular input mode. These requests go to the player
|
||||
// controller, which arbitrates.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -36,8 +32,6 @@ struct FlxInputModeRequest
|
||||
GENERATED_BODY()
|
||||
|
||||
FlxInputModeRequest() = default;
|
||||
FlxInputModeRequest(UUserWidget *InWidget, UWidget *InFocus, bool InShowPointer, bool InBlockInput)
|
||||
: Widget(InWidget), Focus(InFocus), ShowPointer(InShowPointer), BlockInput(InBlockInput) {}
|
||||
|
||||
bool operator == (const FlxInputModeRequest &Other) const;
|
||||
|
||||
@@ -56,6 +50,13 @@ struct FlxInputModeRequest
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
bool BlockInput = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
bool EnableInputComponent = true;
|
||||
|
||||
UPROPERTY()
|
||||
int32 SequenceNumber = 0;
|
||||
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
@@ -65,12 +66,11 @@ struct FlxInputModeRequests
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
// High priority requests are always before low-priority.
|
||||
// Otherwise, these are in order of most recent first.
|
||||
// Sorted by highest priority first, then most recent first.
|
||||
TArray<FlxInputModeRequest> Requests;
|
||||
|
||||
UPROPERTY()
|
||||
bool Dirty = true;
|
||||
int32 NextSequenceNumber = 0;
|
||||
|
||||
public:
|
||||
using View = TArrayView<FlxInputModeRequest>;
|
||||
@@ -87,22 +87,13 @@ public:
|
||||
// Apply a request. Replaces any previous request by the same widget.
|
||||
void Request(const FlxInputModeRequest &NewRequest);
|
||||
|
||||
// Ensure the specified widget has a request in the array.
|
||||
// If it isn't already present, register a low-priority request for it.
|
||||
void EnsureWidget(UUserWidget *Widget);
|
||||
// Find the specified widget, and modify the 'EnableInputComponent'
|
||||
// flag. Adds the widget if it's not already present.
|
||||
void SetEnableInputComponent(UUserWidget *Widget, bool EnableInputComponent);
|
||||
|
||||
// Remove all requests by the specified widget.
|
||||
void Remove(UUserWidget *Widget);
|
||||
|
||||
// Remove any requests by dead widgets or widgets with no parents.
|
||||
void GarbageCollect();
|
||||
|
||||
// Return true if the requests have changed since the last ClearDirty.
|
||||
bool IsDirty() { return Dirty; }
|
||||
|
||||
// Clear the dirty flag.
|
||||
void ClearDirty() { Dirty = false; }
|
||||
|
||||
// Set the dirty flag.
|
||||
void SetDirty() { Dirty = true; }
|
||||
};
|
||||
62
Source/Integration/LuprexViewportClient.cpp
Normal file
62
Source/Integration/LuprexViewportClient.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// LuprexViewportClient.cpp
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#include "LuprexViewportClient.h"
|
||||
#include "Common.h"
|
||||
#include "PlayerControllerBase.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Layout/WidgetPath.h"
|
||||
#include "Widgets/SViewport.h"
|
||||
|
||||
UlxViewportClient::UlxViewportClient(const FObjectInitializer &ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
UE_LOG(LogLuprexIntegration, Display, TEXT("UlxViewportClient constructed"));
|
||||
}
|
||||
|
||||
bool UlxViewportClient::InputKey(const FInputKeyEventArgs &EventArgs)
|
||||
{
|
||||
UE_LOG(LogLuprexIntegration, Display, TEXT("UlxViewportClient::InputKey key=%s event=%d"),
|
||||
*EventArgs.Key.ToString(), (int32)EventArgs.Event);
|
||||
|
||||
// Only act on left mouse button presses that bubbled up to the
|
||||
// viewport unhandled. Walk the widget path under the cursor and
|
||||
// find the nearest focusable ancestor of whatever was hit. If it
|
||||
// isn't the game viewport itself, hand it to the player controller
|
||||
// to apply on its next UpdateInputMode pass; that's the point in
|
||||
// the frame where we can override SViewport's own click-focus
|
||||
// behaviour without fighting it.
|
||||
if ((EventArgs.Event == IE_Pressed) && (EventArgs.Key == EKeys::LeftMouseButton))
|
||||
{
|
||||
FSlateApplication &Slate = FSlateApplication::Get();
|
||||
FVector2D MousePos = Slate.GetCursorPos();
|
||||
FWidgetPath Path = Slate.LocateWindowUnderMouse(
|
||||
MousePos, Slate.GetInteractiveTopLevelWindows());
|
||||
|
||||
if (Path.IsValid())
|
||||
{
|
||||
TSharedPtr<SViewport> ViewportWidget = GetGameViewportWidget();
|
||||
for (int32 Idx = Path.Widgets.Num() - 1; Idx >= 0; --Idx)
|
||||
{
|
||||
TSharedRef<SWidget> Widget = Path.Widgets[Idx].Widget;
|
||||
if (!Widget->SupportsKeyboardFocus()) continue;
|
||||
if (ViewportWidget.IsValid() && Widget == ViewportWidget) break;
|
||||
if (UGameInstance *GI = GetGameInstance())
|
||||
{
|
||||
if (AlxPlayerControllerBase *PC = Cast<AlxPlayerControllerBase>(
|
||||
GI->GetFirstLocalPlayerController(GetWorld())))
|
||||
{
|
||||
PC->ClickToFocus(Widget);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Super::InputKey(EventArgs);
|
||||
}
|
||||
28
Source/Integration/LuprexViewportClient.h
Normal file
28
Source/Integration/LuprexViewportClient.h
Normal file
@@ -0,0 +1,28 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//
|
||||
// LuprexViewportClient.h
|
||||
//
|
||||
// Custom game viewport client. Implements a project-wide
|
||||
// click-to-focus rule: when a left-mouse-button click is
|
||||
// not handled by any widget and bubbles up to the viewport,
|
||||
// we hit-test under the cursor and focus the nearest
|
||||
// focusable ancestor of whatever was hit.
|
||||
//
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "LuprexViewportClient.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class INTEGRATION_API UlxViewportClient : public UGameViewportClient
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UlxViewportClient(const FObjectInitializer &ObjectInitializer);
|
||||
|
||||
virtual bool InputKey(const FInputKeyEventArgs &EventArgs) override;
|
||||
};
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "Engine/GameViewportClient.h"
|
||||
#include "Engine/LevelScriptActor.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Framework/Application/SlateUser.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Widgets/SViewport.h"
|
||||
|
||||
@@ -59,6 +61,40 @@ FVector2D AlxPlayerControllerBase::GetLookAtPixel(const UObject *Context)
|
||||
return ScreenPosition;
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (FSlateApplication::IsInitialized())
|
||||
{
|
||||
FocusChangingHandle = FSlateApplication::Get().OnFocusChanging().AddUObject(
|
||||
this, &AlxPlayerControllerBase::HandleFocusChanging);
|
||||
}
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (FocusChangingHandle.IsValid() && FSlateApplication::IsInitialized())
|
||||
{
|
||||
FSlateApplication::Get().OnFocusChanging().Remove(FocusChangingHandle);
|
||||
FocusChangingHandle.Reset();
|
||||
}
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::HandleFocusChanging(
|
||||
const FFocusEvent &FocusEvent,
|
||||
const FWeakWidgetPath &OldPath,
|
||||
const TSharedPtr<SWidget> &OldFocusedWidget,
|
||||
const FWidgetPath &NewPath,
|
||||
const TSharedPtr<SWidget> &NewFocusedWidget)
|
||||
{
|
||||
UE_LOG(LogLuprexIntegration, Display,
|
||||
TEXT("Focus changing: '%s' -> '%s' (cause: %s)"),
|
||||
OldFocusedWidget.IsValid() ? *OldFocusedWidget->GetTypeAsString() : TEXT("<none>"),
|
||||
NewFocusedWidget.IsValid() ? *NewFocusedWidget->GetTypeAsString() : TEXT("<none>"),
|
||||
*UEnum::GetValueAsString(FocusEvent.GetCause()));
|
||||
}
|
||||
|
||||
UInputComponent* AlxPlayerControllerBase::GetWidgetInputComponent(UUserWidget *Widget)
|
||||
{
|
||||
if (!IsValid(Widget)) return nullptr;
|
||||
@@ -75,7 +111,7 @@ UInputComponent* AlxPlayerControllerBase::GetWidgetInputComponent(UUserWidget *W
|
||||
return Cast<UInputComponent>(Value);
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::WidgetRequestInputMode(UUserWidget *Widget, bool ShowPointer, bool BlockInput, UWidget *Focus)
|
||||
void AlxPlayerControllerBase::WidgetRequestInputMode(UUserWidget *Widget, bool ShowPointer, bool BlockInput, UWidget *Focus, bool EnableInputComponent)
|
||||
{
|
||||
if (!IsValid(Widget))
|
||||
{
|
||||
@@ -91,7 +127,13 @@ void AlxPlayerControllerBase::WidgetRequestInputMode(UUserWidget *Widget, bool S
|
||||
*Widget->GetName(), *GetNameSafe(OwningPC));
|
||||
return;
|
||||
}
|
||||
PC->InputModeRequests.Request(FlxInputModeRequest(Widget, Focus, ShowPointer, BlockInput));
|
||||
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)
|
||||
@@ -103,7 +145,7 @@ void AlxPlayerControllerBase::PushInputComponent(UInputComponent* InInputCompone
|
||||
// focus, pointer visibility, and input blocking across them.
|
||||
if (UUserWidget *Widget = Cast<UUserWidget>(InInputComponent->GetOuter()))
|
||||
{
|
||||
InputModeRequests.EnsureWidget(Widget);
|
||||
InputModeRequests.SetEnableInputComponent(Widget, true);
|
||||
return;
|
||||
}
|
||||
CurrentInputStack.RemoveSingle(InInputComponent);
|
||||
@@ -115,6 +157,12 @@ bool AlxPlayerControllerBase::PopInputComponent(UInputComponent* InInputComponen
|
||||
{
|
||||
if (InInputComponent)
|
||||
{
|
||||
if (UUserWidget *Widget = Cast<UUserWidget>(InInputComponent->GetOuter()))
|
||||
{
|
||||
InputModeRequests.SetEnableInputComponent(Widget, false);
|
||||
InInputComponent->ClearBindingValues();
|
||||
return true;
|
||||
}
|
||||
if (CurrentInputStack.RemoveSingle(InInputComponent) > 0)
|
||||
{
|
||||
InInputComponent->ClearBindingValues();
|
||||
@@ -197,11 +245,13 @@ void AlxPlayerControllerBase::BuildInputStack(TArray<UInputComponent*>& InputSta
|
||||
const TArray<FlxInputModeRequest> &Requests = InputModeRequests.GetRequests();
|
||||
for (int32 Idx = Requests.Num() - 1; Idx >= 0; --Idx)
|
||||
{
|
||||
UUserWidget *Widget = Requests[Idx].Widget;
|
||||
const FlxInputModeRequest &Req = Requests[Idx];
|
||||
if (!Req.EnableInputComponent) continue;
|
||||
UUserWidget *Widget = Req.Widget;
|
||||
if (!Widget->IsInViewport()) continue;
|
||||
if (UInputComponent *IC = GetWidgetInputComponent(Widget))
|
||||
{
|
||||
IC->bBlockInput = Requests[Idx].BlockInput;
|
||||
IC->bBlockInput = Req.BlockInput;
|
||||
InputStack.Push(IC);
|
||||
}
|
||||
}
|
||||
@@ -220,6 +270,8 @@ void AlxPlayerControllerBase::UpdateInputMode()
|
||||
if (!ViewportWidget.IsValid()) return;
|
||||
TSharedRef<SViewport> ViewportWidgetRef = ViewportWidget.ToSharedRef();
|
||||
FReply &SlateOperations = LocalPlayer->GetSlateOperations();
|
||||
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,
|
||||
@@ -235,41 +287,66 @@ void AlxPlayerControllerBase::UpdateInputMode()
|
||||
|
||||
if (Top->ShowPointer)
|
||||
{
|
||||
// Pointer-visible mode: full macroexpansion of SetInputModeGameAndUI
|
||||
// — the BP wrapper, APlayerController::SetInputMode, and
|
||||
// FInputModeGameAndUI::ApplyInputMode all inlined. Defaults:
|
||||
// MouseLockMode=DoNotLock, WidgetToFocus=null, bHideCursorDuringCapture=true.
|
||||
// Only release capture if the viewport is currently holding it
|
||||
// (e.g. we just came from GameOnly). A blanket ReleaseMouseCapture
|
||||
// every tick would yank capture away from widgets mid-gesture
|
||||
// (scrollbar drags, slider drags, etc.).
|
||||
if (SlateUser->DoesWidgetHaveAnyCapture(ViewportWidget))
|
||||
{
|
||||
SlateOperations.ReleaseMouseCapture();
|
||||
}
|
||||
SlateOperations.ReleaseMouseLock();
|
||||
SlateOperations.ReleaseMouseCapture();
|
||||
GameViewportClient->SetMouseLockMode(EMouseLockMode::DoNotLock);
|
||||
GameViewportClient->SetHideCursorDuringCapture(false);
|
||||
GameViewportClient->SetMouseCaptureMode(EMouseCaptureMode::CaptureDuringMouseDown);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pointer-invisible mode: full macroexpansion of SetInputModeGameOnly
|
||||
// — the BP wrapper, APlayerController::SetInputMode, and
|
||||
// FInputModeGameOnly::ApplyInputMode all inlined.
|
||||
// Also captures the mouse to the viewport.
|
||||
SlateOperations.UseHighPrecisionMouseMovement(ViewportWidgetRef);
|
||||
SlateOperations.LockMouseToWidget(ViewportWidgetRef);
|
||||
GameViewportClient->SetMouseLockMode(EMouseLockMode::LockOnCapture);
|
||||
GameViewportClient->SetMouseCaptureMode(EMouseCaptureMode::CapturePermanently);
|
||||
}
|
||||
|
||||
// UWidget holds its live SWidget in a cached TSharedPtr; we need a
|
||||
// TSharedRef for SetUserFocus. Fall back to the viewport if the
|
||||
// target has never been constructed (cached widget is null).
|
||||
TSharedPtr<SWidget> SlateFocus = Top->Focus ? Top->Focus->GetCachedWidget() : nullptr;
|
||||
if (SlateFocus.IsValid())
|
||||
{
|
||||
SlateOperations.SetUserFocus(SlateFocus.ToSharedRef());
|
||||
}
|
||||
else
|
||||
{
|
||||
SlateOperations.SetUserFocus(ViewportWidgetRef);
|
||||
}
|
||||
|
||||
GameViewportClient->SetIgnoreInput(false);
|
||||
|
||||
// How we handle focus depends on whether we're showing the pointer.
|
||||
// In pointer mode, we set focus to the desired state just once,
|
||||
// and then we let the pointer control it from there on. In
|
||||
// no-pointer mode, we set focus to the desired state and
|
||||
// keep putting it back forever.
|
||||
//
|
||||
// If the user clicks the mouse on a focusable widget, the
|
||||
// viewport client notifies us of that fact. We then focus the
|
||||
// widget if possible.
|
||||
//
|
||||
if ((!Top->ShowPointer) || (LastRequestGrantedFocus != Top->SequenceNumber))
|
||||
{
|
||||
if (Top->Focus)
|
||||
{
|
||||
if (TSharedPtr<SWidget> SlateFocus = Top->Focus->GetCachedWidget())
|
||||
{
|
||||
SlateOperations.SetUserFocus(SlateFocus.ToSharedRef());
|
||||
LastRequestGrantedFocus = Top->SequenceNumber;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SlateOperations.SetUserFocus(ViewportWidgetRef);
|
||||
LastRequestGrantedFocus = Top->SequenceNumber;
|
||||
}
|
||||
}
|
||||
else if (TSharedPtr<SWidget> ClickedWidget = ClickToFocusTarget.Pin())
|
||||
{
|
||||
SlateOperations.SetUserFocus(ClickedWidget.ToSharedRef());
|
||||
}
|
||||
ClickToFocusTarget.Reset();
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::ClickToFocus(TSharedRef<SWidget> Widget)
|
||||
{
|
||||
ClickToFocusTarget = Widget.ToWeakPtr();
|
||||
}
|
||||
|
||||
void AlxPlayerControllerBase::UpdateLookAt()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "Engine/HitResult.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "InputEvents.h"
|
||||
#include "InputModeRequest.h"
|
||||
#include "PlayerControllerBase.generated.h"
|
||||
|
||||
UCLASS(BlueprintType, Blueprintable)
|
||||
@@ -41,6 +41,20 @@ public:
|
||||
// eventually reconcile focus, pointer, and capture state.
|
||||
void UpdateInputMode();
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
private:
|
||||
FDelegateHandle FocusChangingHandle;
|
||||
void HandleFocusChanging(
|
||||
const struct FFocusEvent &FocusEvent,
|
||||
const class FWeakWidgetPath &OldPath,
|
||||
const TSharedPtr<class SWidget> &OldFocusedWidget,
|
||||
const class FWidgetPath &NewPath,
|
||||
const TSharedPtr<class SWidget> &NewFocusedWidget);
|
||||
|
||||
public:
|
||||
|
||||
// Input stack overrides: unsorted, append-on-push.
|
||||
virtual void PushInputComponent(UInputComponent* InInputComponent) override;
|
||||
virtual bool PopInputComponent(UInputComponent* InInputComponent) override;
|
||||
@@ -55,8 +69,8 @@ public:
|
||||
// (thanks to DefaultToSelf + HideSelfPin): the widget self-binds,
|
||||
// we find its owning PlayerController, and register the request.
|
||||
UFUNCTION(BlueprintCallable, Category = "Luprex|Input Mode",
|
||||
meta = (DefaultToSelf = "Widget", HideSelfPin = "true"))
|
||||
static void WidgetRequestInputMode(class UUserWidget *Widget, bool ShowPointer, bool BlockInput, class UWidget *Focus);
|
||||
meta = (DefaultToSelf = "Widget", HideSelfPin = "true", EnableInputComponent = "true"))
|
||||
static void WidgetRequestInputMode(class UUserWidget *Widget, bool ShowPointer, bool BlockInput, class UWidget *Focus, bool EnableInputComponent);
|
||||
|
||||
// Get the player controller, cast to AlxPlayerControllerBase.
|
||||
static AlxPlayerControllerBase *FromContext(const UObject *Context);
|
||||
@@ -64,8 +78,22 @@ public:
|
||||
UPROPERTY()
|
||||
FHitResult CurrentLookAt;
|
||||
|
||||
// Input mode requests - see InputModeRequest.h for an explanation.
|
||||
UPROPERTY()
|
||||
FlxInputModeRequests InputModeRequests;
|
||||
|
||||
// The last input mode request whose focus request was granted.
|
||||
UPROPERTY()
|
||||
int32 LastRequestGrantedFocus = 0;
|
||||
|
||||
// The viewport client uses this to notify us that the user
|
||||
// clicked on a focusable widget.
|
||||
void ClickToFocus(TSharedRef<SWidget> Widget);
|
||||
|
||||
private:
|
||||
TWeakPtr<SWidget> ClickToFocusTarget;
|
||||
|
||||
public:
|
||||
|
||||
bool MustCallLookAtChanged = false;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user