diff --git a/Source/StevesUEHelpers/Private/StevesUI/MenuStack.cpp b/Source/StevesUEHelpers/Private/StevesUI/MenuStack.cpp index ff0740c..adbc45e 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/MenuStack.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/MenuStack.cpp @@ -12,13 +12,6 @@ void UMenuStack::NativeConstruct() { Super::NativeConstruct(); - if (!InputPreprocessor.IsValid()) - { - InputPreprocessor = MakeShareable(new FUiInputPreprocessor()); - InputPreprocessor->OnUiKeyDown.BindUObject(this, &UMenuStack::HandleKeyDownEvent); - } - FSlateApplication::Get().RegisterInputPreProcessor(InputPreprocessor); - // We could technically do input change detection in our own input processor, but since this already does it nicely... auto GS = GetStevesGameSubsystem(GetWorld()); if (GS) @@ -35,7 +28,6 @@ void UMenuStack::NativeConstruct() void UMenuStack::NativeDestruct() { Super::NativeDestruct(); - FSlateApplication::Get().UnregisterInputPreProcessor(InputPreprocessor); auto GS = GetStevesGameSubsystem(GetWorld()); if (GS) @@ -96,6 +88,8 @@ void UMenuStack::ApplyGamePauseChange(EGamePauseChange Change) const bool UMenuStack::HandleKeyDownEvent(const FKeyEvent& InKeyEvent) { + Super::HandleKeyDownEvent(InKeyEvent); + // Hardcoding the Back / Exit menu navigation inputs because input mappings can't be trusted in UMG // This is probably OK though, no-one redefines menu controls, right? FKey Key = InKeyEvent.GetKey(); diff --git a/Source/StevesUEHelpers/Public/StevesUI/FocusableInputInterceptorUserWidget.cpp b/Source/StevesUEHelpers/Public/StevesUI/FocusableInputInterceptorUserWidget.cpp new file mode 100644 index 0000000..7e23dfe --- /dev/null +++ b/Source/StevesUEHelpers/Public/StevesUI/FocusableInputInterceptorUserWidget.cpp @@ -0,0 +1,29 @@ +#include "FocusableInputInterceptorUserWidget.h" + + +void UFocusableInputInterceptorUserWidget::NativeConstruct() +{ + Super::NativeConstruct(); + + if (!InputPreprocessor.IsValid()) + { + InputPreprocessor = MakeShareable(new FUiInputPreprocessor()); + InputPreprocessor->OnUiKeyDown.BindUObject(this, &UFocusableInputInterceptorUserWidget::HandleKeyDownEvent); + } + FSlateApplication::Get().RegisterInputPreProcessor(InputPreprocessor); + +} + +void UFocusableInputInterceptorUserWidget::NativeDestruct() +{ + Super::NativeDestruct(); + + FSlateApplication::Get().UnregisterInputPreProcessor(InputPreprocessor); +} + + +bool UFocusableInputInterceptorUserWidget::HandleKeyDownEvent(const FKeyEvent& InKeyEvent) +{ + // Do nothing by default + return false; +} diff --git a/Source/StevesUEHelpers/Public/StevesUI/FocusableInputInterceptorUserWidget.h b/Source/StevesUEHelpers/Public/StevesUI/FocusableInputInterceptorUserWidget.h new file mode 100644 index 0000000..b9e46f0 --- /dev/null +++ b/Source/StevesUEHelpers/Public/StevesUI/FocusableInputInterceptorUserWidget.h @@ -0,0 +1,43 @@ +#pragma once + +#include "CoreMinimal.h" + +#include "FocusableUserWidget.h" +#include "Framework/Application/IInputProcessor.h" + +#include "FocusableInputInterceptorUserWidget.generated.h" + +UCLASS(Blueprintable, BlueprintType) +class STEVESUEHELPERS_API UFocusableInputInterceptorUserWidget : public UFocusableUserWidget +{ + GENERATED_BODY() + + // Nested class which we'll use to poke into input events before anything else eats them + // Without this it seems impossible to pick up e.g. Gamepad "B" button with UMG up + // It means we hardcode the controls for menus but that's OK in practice + class FUiInputPreprocessor : public IInputProcessor, public TSharedFromThis + { + public: + DECLARE_DELEGATE_RetVal_OneParam(bool, FOnUiKeyDown, const FKeyEvent&); + FOnUiKeyDown OnUiKeyDown; + + virtual bool HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent) override + { + return OnUiKeyDown.Execute(InKeyEvent); + } + // Required by IInputProcessor but we don't need + virtual void Tick(const float DeltaTime, FSlateApplication& SlateApp, TSharedRef Crs) override{} + }; + +protected: + TSharedPtr InputPreprocessor; + +public: + + virtual void NativeConstruct() override; + virtual void NativeDestruct(); + + UFUNCTION() + virtual bool HandleKeyDownEvent(const FKeyEvent& InKeyEvent); + +}; diff --git a/Source/StevesUEHelpers/Public/StevesUI/MenuStack.h b/Source/StevesUEHelpers/Public/StevesUI/MenuStack.h index 90729c2..4c3accb 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/MenuStack.h +++ b/Source/StevesUEHelpers/Public/StevesUI/MenuStack.h @@ -2,7 +2,8 @@ #include "CoreMinimal.h" -#include "FocusableUserWidget.h" + +#include "FocusableInputInterceptorUserWidget.h" #include "Framework/Application/IInputProcessor.h" #include "StevesHelperCommon.h" @@ -20,34 +21,15 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMenuStackClosed, class UMenuStac /// Create a Blueprint subclass of this and make sure you include a UContentWidget with the name /// "MenuContainer" somewhere in the tree, which is where the menu contents will be placed. UCLASS(Abstract, BlueprintType) -class STEVESUEHELPERS_API UMenuStack : public UFocusableUserWidget +class STEVESUEHELPERS_API UMenuStack : public UFocusableInputInterceptorUserWidget { GENERATED_BODY() - // Nested class which we'll use to poke into input events before anything else eats them - // Without this it seems impossible to pick up e.g. Gamepad "B" button with UMG up - // It means we hardcode the controls for menus but that's OK in practice - class FUiInputPreprocessor : public IInputProcessor, public TSharedFromThis - { - public: - DECLARE_DELEGATE_RetVal_OneParam(bool, FOnUiKeyDown, const FKeyEvent&); - FOnUiKeyDown OnUiKeyDown; - - virtual bool HandleKeyDownEvent(FSlateApplication& SlateApp, const FKeyEvent& InKeyEvent) override - { - return OnUiKeyDown.Execute(InKeyEvent); - } - // Required by IInputProcessor but we don't need - virtual void Tick(const float DeltaTime, FSlateApplication& SlateApp, TSharedRef Crs) override{} - }; - protected: EInputMode LastInputMode; TArray Menus; - TSharedPtr InputPreprocessor; - virtual void LastMenuClosed(bool bWasCancel); virtual void NativeConstruct() override; @@ -57,8 +39,7 @@ protected: virtual void ApplyMousePointerVisibility(EMousePointerVisibilityChange Change) const; virtual void ApplyGamePauseChange(EGamePauseChange Change) const; - UFUNCTION() - bool HandleKeyDownEvent(const FKeyEvent& InKeyEvent); + virtual bool HandleKeyDownEvent(const FKeyEvent& InKeyEvent) override; UFUNCTION() void InputModeChanged(int PlayerIndex, EInputMode NewMode);