mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 08:20:32 +00:00
Added ImGui Input Handler and ImGui Settings:
- Added ImGui Input Handler class that allows to customize handling of the keyboard and gamepad input. - Added ImGui Settings to allow specify custom input handler implementation. - Added editor support for ImGui Settings. - Input handling in ImGui Widget is divided for querying the handler (more customizable) and actual input processing based on the handler’s response (fixed and simplified). - Removed a need for checking console state in different input handling functions in ImGui Widget by suppressing keyboard focus support when console is opened.
This commit is contained in:
parent
6ddc7f2805
commit
5968c3ce84
@ -11,7 +11,13 @@ public class ImGui : ModuleRules
|
|||||||
public ImGui(TargetInfo Target)
|
public ImGui(TargetInfo Target)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if WITH_FORWARDED_MODULE_RULES_CTOR
|
||||||
|
bool bBuildEditor = Target.bBuildEditor;
|
||||||
|
#else
|
||||||
|
bool bBuildEditor = (Target.Type == TargetRules.TargetType.Editor);
|
||||||
|
#endif
|
||||||
|
|
||||||
PublicIncludePaths.AddRange(
|
PublicIncludePaths.AddRange(
|
||||||
new string[] {
|
new string[] {
|
||||||
"ImGui/Public",
|
"ImGui/Public",
|
||||||
@ -19,8 +25,8 @@ public class ImGui : ModuleRules
|
|||||||
// ... add public include paths required here ...
|
// ... add public include paths required here ...
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
PrivateIncludePaths.AddRange(
|
PrivateIncludePaths.AddRange(
|
||||||
new string[] {
|
new string[] {
|
||||||
"ImGui/Private",
|
"ImGui/Private",
|
||||||
@ -28,8 +34,8 @@ public class ImGui : ModuleRules
|
|||||||
// ... add other private include paths required here ...
|
// ... add other private include paths required here ...
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
PublicDependencyModuleNames.AddRange(
|
PublicDependencyModuleNames.AddRange(
|
||||||
new string[]
|
new string[]
|
||||||
{
|
{
|
||||||
@ -38,8 +44,8 @@ public class ImGui : ModuleRules
|
|||||||
// ... add other public dependencies that you statically link with here ...
|
// ... add other public dependencies that you statically link with here ...
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
PrivateDependencyModuleNames.AddRange(
|
PrivateDependencyModuleNames.AddRange(
|
||||||
new string[]
|
new string[]
|
||||||
{
|
{
|
||||||
@ -51,8 +57,19 @@ public class ImGui : ModuleRules
|
|||||||
// ... add private dependencies that you statically link with here ...
|
// ... add private dependencies that you statically link with here ...
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (bBuildEditor)
|
||||||
|
{
|
||||||
|
PrivateDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"Settings",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DynamicallyLoadedModuleNames.AddRange(
|
DynamicallyLoadedModuleNames.AddRange(
|
||||||
new string[]
|
new string[]
|
||||||
{
|
{
|
||||||
|
106
Source/ImGui/Private/Editor/ImGuiEditor.cpp
Normal file
106
Source/ImGui/Private/Editor/ImGuiEditor.cpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
|
||||||
|
#include "ImGuiEditor.h"
|
||||||
|
|
||||||
|
#include "ImGuiSettings.h"
|
||||||
|
|
||||||
|
#include <ISettingsModule.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define LOCTEXT_NAMESPACE "ImGuiEditor"
|
||||||
|
|
||||||
|
#define SETTINGS_CONTAINER TEXT("Project"), TEXT("Plugins"), TEXT("ImGui")
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
ISettingsModule* GetSettingsModule()
|
||||||
|
{
|
||||||
|
return FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FImGuiEditor::FImGuiEditor()
|
||||||
|
{
|
||||||
|
Register();
|
||||||
|
|
||||||
|
// As a side effect of being part of the ImGui module, we need to support deferred registration (only executed if
|
||||||
|
// module is loaded manually at the very early stage).
|
||||||
|
if (!IsRegistrationCompleted())
|
||||||
|
{
|
||||||
|
CreateRegistrator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FImGuiEditor::~FImGuiEditor()
|
||||||
|
{
|
||||||
|
Unregister();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiEditor::Register()
|
||||||
|
{
|
||||||
|
// Only register after UImGuiSettings class is initialized (necessary to check in early loading stages).
|
||||||
|
if (!bSettingsRegistered && UImGuiSettings::StaticClass()->IsValidLowLevelFast())
|
||||||
|
{
|
||||||
|
if (ISettingsModule* SettingsModule = GetSettingsModule())
|
||||||
|
{
|
||||||
|
bSettingsRegistered = true;
|
||||||
|
|
||||||
|
SettingsModule->RegisterSettings(SETTINGS_CONTAINER,
|
||||||
|
LOCTEXT("ImGuiSettingsName", "ImGui"),
|
||||||
|
LOCTEXT("ImGuiSettingsDescription", "Configure the Unreal ImGui plugin."),
|
||||||
|
GetMutableDefault<UImGuiSettings>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiEditor::Unregister()
|
||||||
|
{
|
||||||
|
if (bSettingsRegistered)
|
||||||
|
{
|
||||||
|
bSettingsRegistered = false;
|
||||||
|
|
||||||
|
if (ISettingsModule* SettingsModule = GetSettingsModule())
|
||||||
|
{
|
||||||
|
SettingsModule->UnregisterSettings(SETTINGS_CONTAINER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiEditor::CreateRegistrator()
|
||||||
|
{
|
||||||
|
if (!RegistratorHandle.IsValid())
|
||||||
|
{
|
||||||
|
RegistratorHandle = FModuleManager::Get().OnModulesChanged().AddLambda([this](FName Name, EModuleChangeReason Reason)
|
||||||
|
{
|
||||||
|
if (Reason == EModuleChangeReason::ModuleLoaded)
|
||||||
|
{
|
||||||
|
Register();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsRegistrationCompleted())
|
||||||
|
{
|
||||||
|
ReleaseRegistrator();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiEditor::ReleaseRegistrator()
|
||||||
|
{
|
||||||
|
if (RegistratorHandle.IsValid())
|
||||||
|
{
|
||||||
|
FModuleManager::Get().OnModulesChanged().Remove(RegistratorHandle);
|
||||||
|
RegistratorHandle.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#undef SETTINGS_CONTAINER
|
||||||
|
#undef LOCTEXT_NAMESPACE
|
||||||
|
|
||||||
|
#endif // WITH_EDITOR
|
30
Source/ImGui/Private/Editor/ImGuiEditor.h
Normal file
30
Source/ImGui/Private/Editor/ImGuiEditor.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
|
||||||
|
// Registers module's settings in editor (due to a small size of this code we don't use a separate editor module).
|
||||||
|
class FImGuiEditor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
FImGuiEditor();
|
||||||
|
~FImGuiEditor();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool IsRegistrationCompleted() const { return bSettingsRegistered; }
|
||||||
|
|
||||||
|
void Register();
|
||||||
|
void Unregister();
|
||||||
|
|
||||||
|
void CreateRegistrator();
|
||||||
|
void ReleaseRegistrator();
|
||||||
|
|
||||||
|
FDelegateHandle RegistratorHandle;
|
||||||
|
|
||||||
|
bool bSettingsRegistered = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WITH_EDITOR
|
42
Source/ImGui/Private/ImGuiInputHandler.cpp
Normal file
42
Source/ImGui/Private/ImGuiInputHandler.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "ImGuiInputHandler.h"
|
||||||
|
|
||||||
|
#include "ImGuiContextProxy.h"
|
||||||
|
#include "ImGuiModuleManager.h"
|
||||||
|
|
||||||
|
#include <Engine/Console.h>
|
||||||
|
#include <Input/Events.h>
|
||||||
|
|
||||||
|
|
||||||
|
FImGuiInputResponse UImGuiInputHandler::OnKeyDown(const FKeyEvent& KeyEvent)
|
||||||
|
{
|
||||||
|
// Ignore console open events, so we don't block it from opening.
|
||||||
|
if (KeyEvent.GetKey() == EKeys::Tilde)
|
||||||
|
{
|
||||||
|
return FImGuiInputResponse{ false, false };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore escape event, if they are not meant for ImGui control.
|
||||||
|
if (KeyEvent.GetKey() == EKeys::Escape && !HasImGuiActiveItem())
|
||||||
|
{
|
||||||
|
return FImGuiInputResponse{ false, false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefaultResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UImGuiInputHandler::HasImGuiActiveItem() const
|
||||||
|
{
|
||||||
|
FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
||||||
|
return ContextProxy && ContextProxy->HasActiveItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UImGuiInputHandler::Initialize(FImGuiModuleManager* InModuleManager, UGameViewportClient* InGameViewport, int32 InContextIndex)
|
||||||
|
{
|
||||||
|
ModuleManager = InModuleManager;
|
||||||
|
GameViewport = InGameViewport;
|
||||||
|
ContextIndex = InContextIndex;
|
||||||
|
}
|
55
Source/ImGui/Private/ImGuiInputHandlerFactory.cpp
Normal file
55
Source/ImGui/Private/ImGuiInputHandlerFactory.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "ImGuiInputHandlerFactory.h"
|
||||||
|
|
||||||
|
#include "ImGuiInputHandler.h"
|
||||||
|
#include "ImGuiSettings.h"
|
||||||
|
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY_STATIC(LogImGuiInputHandlerFactory, Warning, All);
|
||||||
|
|
||||||
|
UImGuiInputHandler* FImGuiInputHandlerFactory::NewHandler(FImGuiModuleManager* ModuleManager, UGameViewportClient* GameViewport, int32 ContextIndex)
|
||||||
|
{
|
||||||
|
UClass* HandlerClass = nullptr;
|
||||||
|
if (UImGuiSettings* Settings = GetMutableDefault<UImGuiSettings>())
|
||||||
|
{
|
||||||
|
const auto& HandlerClassReference = Settings->GetImGuiInputHandlerClass();
|
||||||
|
if (HandlerClassReference.IsValid())
|
||||||
|
{
|
||||||
|
HandlerClass = HandlerClassReference.TryLoadClass<UImGuiInputHandler>();
|
||||||
|
|
||||||
|
if (!HandlerClass)
|
||||||
|
{
|
||||||
|
UE_LOG(LogImGuiInputHandlerFactory, Error, TEXT("Couldn't load ImGui Input Handler class '%s'."), *HandlerClassReference.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HandlerClass)
|
||||||
|
{
|
||||||
|
HandlerClass = UImGuiInputHandler::StaticClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
UImGuiInputHandler* Handler = NewObject<UImGuiInputHandler>(GameViewport, HandlerClass);
|
||||||
|
if (Handler)
|
||||||
|
{
|
||||||
|
Handler->Initialize(ModuleManager, GameViewport, ContextIndex);
|
||||||
|
Handler->AddToRoot();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogImGuiInputHandlerFactory, Error, TEXT("Failed attempt to create Input Handler: HandlerClass = %s."), *GetNameSafe(HandlerClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiInputHandlerFactory::ReleaseHandler(UImGuiInputHandler* Handler)
|
||||||
|
{
|
||||||
|
if (Handler)
|
||||||
|
{
|
||||||
|
Handler->RemoveFromRoot();
|
||||||
|
}
|
||||||
|
}
|
16
Source/ImGui/Private/ImGuiInputHandlerFactory.h
Normal file
16
Source/ImGui/Private/ImGuiInputHandlerFactory.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class FImGuiModuleManager;
|
||||||
|
class UGameViewportClient;
|
||||||
|
class UImGuiInputHandler;
|
||||||
|
|
||||||
|
class FImGuiInputHandlerFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
static UImGuiInputHandler* NewHandler(FImGuiModuleManager* ModuleManager, UGameViewportClient* GameViewport, int32 ContextIndex);
|
||||||
|
|
||||||
|
static void ReleaseHandler(UImGuiInputHandler* Handler);
|
||||||
|
};
|
@ -6,6 +6,10 @@
|
|||||||
#include "Utilities/WorldContext.h"
|
#include "Utilities/WorldContext.h"
|
||||||
#include "Utilities/WorldContextIndex.h"
|
#include "Utilities/WorldContextIndex.h"
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
#include "Editor/ImGuiEditor.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <IPluginManager.h>
|
#include <IPluginManager.h>
|
||||||
|
|
||||||
|
|
||||||
@ -32,6 +36,10 @@ struct EDelegateCategory
|
|||||||
|
|
||||||
static FImGuiModuleManager* ModuleManager = nullptr;
|
static FImGuiModuleManager* ModuleManager = nullptr;
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
static FImGuiEditor* ModuleEditor = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
FImGuiDelegateHandle FImGuiModule::AddEditorImGuiDelegate(const FImGuiDelegate& Delegate)
|
FImGuiDelegateHandle FImGuiModule::AddEditorImGuiDelegate(const FImGuiDelegate& Delegate)
|
||||||
{
|
{
|
||||||
@ -91,17 +99,28 @@ void FImGuiModule::RemoveImGuiDelegate(const FImGuiDelegateHandle& Handle)
|
|||||||
|
|
||||||
void FImGuiModule::StartupModule()
|
void FImGuiModule::StartupModule()
|
||||||
{
|
{
|
||||||
checkf(!ModuleManager, TEXT("Instance of Module Manager already exists. Instance should be created only during module startup."));
|
// Create managers that implements module logic.
|
||||||
|
|
||||||
// Create module manager that implements modules logic.
|
checkf(!ModuleManager, TEXT("Instance of the Module Manager already exists. Instance should be created only during module startup."));
|
||||||
ModuleManager = new FImGuiModuleManager();
|
ModuleManager = new FImGuiModuleManager();
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
checkf(!ModuleEditor, TEXT("Instance of the Module Editor already exists. Instance should be created only during module startup."));
|
||||||
|
ModuleEditor = new FImGuiEditor();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void FImGuiModule::ShutdownModule()
|
void FImGuiModule::ShutdownModule()
|
||||||
{
|
{
|
||||||
checkf(ModuleManager, TEXT("Null Module Manager. Manager instance should be deleted during module shutdown."));
|
// Before we shutdown we need to delete managers that will do all the necessary cleanup.
|
||||||
|
|
||||||
// Before we shutdown we need to delete manager that will do all necessary cleanup.
|
#if WITH_EDITOR
|
||||||
|
checkf(ModuleEditor, TEXT("Null Module Editor. Module editor instance should be deleted during module shutdown."));
|
||||||
|
delete ModuleEditor;
|
||||||
|
ModuleEditor = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
checkf(ModuleManager, TEXT("Null Module Manager. Module manager instance should be deleted during module shutdown."));
|
||||||
delete ModuleManager;
|
delete ModuleManager;
|
||||||
ModuleManager = nullptr;
|
ModuleManager = nullptr;
|
||||||
}
|
}
|
||||||
|
51
Source/ImGui/Private/ImGuiSettings.cpp
Normal file
51
Source/ImGui/Private/ImGuiSettings.cpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#include "ImGuiPrivatePCH.h"
|
||||||
|
|
||||||
|
#include "ImGuiSettings.h"
|
||||||
|
|
||||||
|
|
||||||
|
UImGuiSettings::UImGuiSettings()
|
||||||
|
{
|
||||||
|
#if WITH_EDITOR
|
||||||
|
RegisterPropertyChangedDelegate();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
UImGuiSettings::~UImGuiSettings()
|
||||||
|
{
|
||||||
|
#if WITH_EDITOR
|
||||||
|
UnregisterPropertyChangedDelegate();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
|
||||||
|
void UImGuiSettings::RegisterPropertyChangedDelegate()
|
||||||
|
{
|
||||||
|
if (!FCoreUObjectDelegates::OnObjectPropertyChanged.IsBoundToObject(this))
|
||||||
|
{
|
||||||
|
FCoreUObjectDelegates::OnObjectPropertyChanged.AddUObject(this, &UImGuiSettings::OnPropertyChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UImGuiSettings::UnregisterPropertyChangedDelegate()
|
||||||
|
{
|
||||||
|
FCoreUObjectDelegates::OnObjectPropertyChanged.RemoveAll(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UImGuiSettings::OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent)
|
||||||
|
{
|
||||||
|
if (ObjectBeingModified == this)
|
||||||
|
{
|
||||||
|
static const FName ImGuiInputHandlerPropertyName = GET_MEMBER_NAME_CHECKED(UImGuiSettings, ImGuiInputHandlerClass);
|
||||||
|
|
||||||
|
const FName UpdatedPropertyName = PropertyChangedEvent.MemberProperty ? PropertyChangedEvent.MemberProperty->GetFName() : NAME_None;
|
||||||
|
if (UpdatedPropertyName == ImGuiInputHandlerPropertyName)
|
||||||
|
{
|
||||||
|
OnImGuiInputHandlerClassChanged.Broadcast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WITH_EDITOR
|
56
Source/ImGui/Private/ImGuiSettings.h
Normal file
56
Source/ImGui/Private/ImGuiSettings.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ImGuiInputHandler.h"
|
||||||
|
|
||||||
|
#include <Delegates/Delegate.h>
|
||||||
|
#include <UObject/Object.h>
|
||||||
|
|
||||||
|
// Select right soft class reference header to avoid warning (new header contains FSoftClassPath to FStringClassReference
|
||||||
|
// typedef, so we will use that as a common denominator).
|
||||||
|
#include <Runtime/Launch/Resources/Version.h>
|
||||||
|
#if (ENGINE_MAJOR_VERSION < 4 || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION < 18))
|
||||||
|
#include <StringClassReference.h>
|
||||||
|
#else
|
||||||
|
#include <UObject/SoftObjectPath.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ImGuiSettings.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
// Settings for ImGui module.
|
||||||
|
UCLASS(config=ImGui, defaultconfig)
|
||||||
|
class UImGuiSettings : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
UImGuiSettings();
|
||||||
|
~UImGuiSettings();
|
||||||
|
|
||||||
|
// Path to custom implementation of ImGui Input Handler.
|
||||||
|
const FStringClassReference& GetImGuiInputHandlerClass() const { return ImGuiInputHandlerClass; }
|
||||||
|
|
||||||
|
// Delegate raised when ImGuiInputHandlerClass property has changed.
|
||||||
|
FSimpleMulticastDelegate OnImGuiInputHandlerClassChanged;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
// Path to own implementation of ImGui Input Handler allowing to customize handling of keyboard and gamepad input.
|
||||||
|
// If not set then default handler is used.
|
||||||
|
UPROPERTY(EditAnywhere, config, Category = "Input", meta = (MetaClass = "ImGuiInputHandler"))
|
||||||
|
FStringClassReference ImGuiInputHandlerClass;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
#if WITH_EDITOR
|
||||||
|
|
||||||
|
void RegisterPropertyChangedDelegate();
|
||||||
|
void UnregisterPropertyChangedDelegate();
|
||||||
|
|
||||||
|
void OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent);
|
||||||
|
|
||||||
|
#endif // WITH_EDITOR
|
||||||
|
};
|
@ -7,8 +7,11 @@
|
|||||||
#include "ImGuiContextManager.h"
|
#include "ImGuiContextManager.h"
|
||||||
#include "ImGuiContextProxy.h"
|
#include "ImGuiContextProxy.h"
|
||||||
#include "ImGuiImplementation.h"
|
#include "ImGuiImplementation.h"
|
||||||
|
#include "ImGuiInputHandler.h"
|
||||||
|
#include "ImGuiInputHandlerFactory.h"
|
||||||
#include "ImGuiInteroperability.h"
|
#include "ImGuiInteroperability.h"
|
||||||
#include "ImGuiModuleManager.h"
|
#include "ImGuiModuleManager.h"
|
||||||
|
#include "ImGuiSettings.h"
|
||||||
#include "TextureManager.h"
|
#include "TextureManager.h"
|
||||||
#include "Utilities/Arrays.h"
|
#include "Utilities/Arrays.h"
|
||||||
#include "Utilities/ScopeGuards.h"
|
#include "Utilities/ScopeGuards.h"
|
||||||
@ -106,11 +109,19 @@ void SImGuiWidget::Construct(const FArguments& InArgs)
|
|||||||
checkf(ContextProxy, TEXT("Missing context during widget construction: ContextIndex = %d"), ContextIndex);
|
checkf(ContextProxy, TEXT("Missing context during widget construction: ContextIndex = %d"), ContextIndex);
|
||||||
ContextProxy->OnDraw().AddRaw(this, &SImGuiWidget::OnDebugDraw);
|
ContextProxy->OnDraw().AddRaw(this, &SImGuiWidget::OnDebugDraw);
|
||||||
ContextProxy->SetInputState(&InputState);
|
ContextProxy->SetInputState(&InputState);
|
||||||
|
|
||||||
|
// Create ImGui Input Handler.
|
||||||
|
CreateInputHandler();
|
||||||
|
RegisterInputHandlerChangedDelegate();
|
||||||
}
|
}
|
||||||
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
||||||
|
|
||||||
SImGuiWidget::~SImGuiWidget()
|
SImGuiWidget::~SImGuiWidget()
|
||||||
{
|
{
|
||||||
|
// Release ImGui Input Handler.
|
||||||
|
UnregisterInputHandlerChangedDelegate();
|
||||||
|
ReleaseInputHandler();
|
||||||
|
|
||||||
// Remove binding between this widget and its context proxy.
|
// Remove binding between this widget and its context proxy.
|
||||||
if (auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
if (auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
||||||
{
|
{
|
||||||
@ -142,16 +153,23 @@ void SImGuiWidget::Tick(const FGeometry& AllottedGeometry, const double InCurren
|
|||||||
UpdateInputEnabled();
|
UpdateInputEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
FReply ToSlateReply(const FImGuiInputResponse& HandlingResponse)
|
||||||
|
{
|
||||||
|
return HandlingResponse.HasConsumeRequest() ? FReply::Handled() : FReply::Unhandled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
|
FReply SImGuiWidget::OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent)
|
||||||
{
|
{
|
||||||
if (IsConsoleOpened())
|
const FImGuiInputResponse Response = InputHandler->OnKeyChar(CharacterEvent);
|
||||||
|
if (Response.HasProcessingRequest())
|
||||||
{
|
{
|
||||||
return FReply::Unhandled();
|
InputState.AddCharacter(CharacterEvent.GetCharacter());
|
||||||
}
|
}
|
||||||
|
|
||||||
InputState.AddCharacter(CharacterEvent.GetCharacter());
|
return ToSlateReply(Response);
|
||||||
|
|
||||||
return FReply::Handled();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& KeyEvent)
|
||||||
@ -160,9 +178,13 @@ FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& Key
|
|||||||
{
|
{
|
||||||
if (InputState.IsGamepadNavigationEnabled())
|
if (InputState.IsGamepadNavigationEnabled())
|
||||||
{
|
{
|
||||||
InputState.SetGamepadNavigationKey(KeyEvent, true);
|
const FImGuiInputResponse Response = InputHandler->OnGamepadKeyDown(KeyEvent);
|
||||||
|
if (Response.HasProcessingRequest())
|
||||||
|
{
|
||||||
|
InputState.SetGamepadNavigationKey(KeyEvent, true);
|
||||||
|
}
|
||||||
|
|
||||||
return FReply::Handled();
|
return ToSlateReply(Response);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -171,17 +193,16 @@ FReply SImGuiWidget::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& Key
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (IsConsoleOpened() || IgnoreKeyEvent(KeyEvent))
|
|
||||||
{
|
|
||||||
return FReply::Unhandled();
|
|
||||||
}
|
|
||||||
|
|
||||||
InputState.SetKeyDown(KeyEvent, true);
|
|
||||||
CopyModifierKeys(KeyEvent);
|
|
||||||
|
|
||||||
UpdateCanvasMapMode(KeyEvent);
|
UpdateCanvasMapMode(KeyEvent);
|
||||||
|
|
||||||
return WithMouseLockRequests(FReply::Handled());
|
const FImGuiInputResponse Response = InputHandler->OnKeyDown(KeyEvent);
|
||||||
|
if (Response.HasProcessingRequest())
|
||||||
|
{
|
||||||
|
InputState.SetKeyDown(KeyEvent, true);
|
||||||
|
CopyModifierKeys(KeyEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WithMouseLockRequests(ToSlateReply(Response));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,9 +212,10 @@ FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEv
|
|||||||
{
|
{
|
||||||
if (InputState.IsGamepadNavigationEnabled())
|
if (InputState.IsGamepadNavigationEnabled())
|
||||||
{
|
{
|
||||||
|
// Always handle key up events to protect from leaving accidental keys not cleared in ImGui input state.
|
||||||
InputState.SetGamepadNavigationKey(KeyEvent, false);
|
InputState.SetGamepadNavigationKey(KeyEvent, false);
|
||||||
|
|
||||||
return FReply::Handled();
|
return ToSlateReply(InputHandler->OnGamepadKeyUp(KeyEvent));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -202,15 +224,13 @@ FReply SImGuiWidget::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& KeyEv
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Even if we don't send new keystrokes to ImGui, we still handle key up events, to make sure that we clear keys
|
UpdateCanvasMapMode(KeyEvent);
|
||||||
// pressed before suppressing keyboard input.
|
|
||||||
|
// Always handle key up events to protect from leaving accidental keys not cleared in ImGui input state.
|
||||||
InputState.SetKeyDown(KeyEvent, false);
|
InputState.SetKeyDown(KeyEvent, false);
|
||||||
CopyModifierKeys(KeyEvent);
|
CopyModifierKeys(KeyEvent);
|
||||||
|
|
||||||
UpdateCanvasMapMode(KeyEvent);
|
return WithMouseLockRequests(ToSlateReply(InputHandler->OnKeyUp(KeyEvent)));
|
||||||
|
|
||||||
// If console is opened we notify key change but we also let event trough, so it can be handled by console.
|
|
||||||
return IsConsoleOpened() ? FReply::Unhandled() : WithMouseLockRequests(FReply::Handled());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,9 +238,13 @@ FReply SImGuiWidget::OnAnalogValueChanged(const FGeometry& MyGeometry, const FAn
|
|||||||
{
|
{
|
||||||
if (AnalogInputEvent.GetKey().IsGamepadKey() && InputState.IsGamepadNavigationEnabled())
|
if (AnalogInputEvent.GetKey().IsGamepadKey() && InputState.IsGamepadNavigationEnabled())
|
||||||
{
|
{
|
||||||
InputState.SetGamepadNavigationAxis(AnalogInputEvent, AnalogInputEvent.GetAnalogValue());
|
const FImGuiInputResponse Response = InputHandler->OnGamepadAxis(AnalogInputEvent);
|
||||||
|
if (Response.HasProcessingRequest())
|
||||||
|
{
|
||||||
|
InputState.SetGamepadNavigationAxis(AnalogInputEvent, AnalogInputEvent.GetAnalogValue());
|
||||||
|
}
|
||||||
|
|
||||||
return FReply::Handled();
|
return ToSlateReply(Response);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -275,24 +299,6 @@ FReply SImGuiWidget::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEve
|
|||||||
return FReply::Handled();
|
return FReply::Handled();
|
||||||
}
|
}
|
||||||
|
|
||||||
FCursorReply SImGuiWidget::OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const
|
|
||||||
{
|
|
||||||
EMouseCursor::Type MouseCursor = EMouseCursor::None;
|
|
||||||
if (MouseCursorOverride != EMouseCursor::None)
|
|
||||||
{
|
|
||||||
MouseCursor = MouseCursorOverride;
|
|
||||||
}
|
|
||||||
else if (CVars::DrawMouseCursor.GetValueOnGameThread() <= 0)
|
|
||||||
{
|
|
||||||
if (FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
|
||||||
{
|
|
||||||
MouseCursor = ContextProxy->GetMouseCursor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FCursorReply::Cursor(MouseCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
FReply SImGuiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
FReply SImGuiWidget::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
||||||
{
|
{
|
||||||
if (bCanvasMapMode)
|
if (bCanvasMapMode)
|
||||||
@ -365,6 +371,66 @@ void SImGuiWidget::OnMouseLeave(const FPointerEvent& MouseEvent)
|
|||||||
UpdateInputMode(HasKeyboardFocus() && GameViewport->Viewport->IsForegroundWindow(), false);
|
UpdateInputMode(HasKeyboardFocus() && GameViewport->Viewport->IsForegroundWindow(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FCursorReply SImGuiWidget::OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const
|
||||||
|
{
|
||||||
|
EMouseCursor::Type MouseCursor = EMouseCursor::None;
|
||||||
|
if (MouseCursorOverride != EMouseCursor::None)
|
||||||
|
{
|
||||||
|
MouseCursor = MouseCursorOverride;
|
||||||
|
}
|
||||||
|
else if (CVars::DrawMouseCursor.GetValueOnGameThread() <= 0)
|
||||||
|
{
|
||||||
|
if (FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
||||||
|
{
|
||||||
|
MouseCursor = ContextProxy->GetMouseCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FCursorReply::Cursor(MouseCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SImGuiWidget::CreateInputHandler()
|
||||||
|
{
|
||||||
|
if (!InputHandler.IsValid())
|
||||||
|
{
|
||||||
|
InputHandler = FImGuiInputHandlerFactory::NewHandler(ModuleManager, GameViewport.Get(), ContextIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SImGuiWidget::ReleaseInputHandler()
|
||||||
|
{
|
||||||
|
if (InputHandler.IsValid())
|
||||||
|
{
|
||||||
|
FImGuiInputHandlerFactory::ReleaseHandler(InputHandler.Get());
|
||||||
|
InputHandler.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SImGuiWidget::RecreateInputHandler()
|
||||||
|
{
|
||||||
|
ReleaseInputHandler();
|
||||||
|
CreateInputHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SImGuiWidget::RegisterInputHandlerChangedDelegate()
|
||||||
|
{
|
||||||
|
if (UImGuiSettings* Settings = GetMutableDefault<UImGuiSettings>())
|
||||||
|
{
|
||||||
|
if (!Settings->OnImGuiInputHandlerClassChanged.IsBoundToObject(this))
|
||||||
|
{
|
||||||
|
Settings->OnImGuiInputHandlerClassChanged.AddRaw(this, &SImGuiWidget::RecreateInputHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SImGuiWidget::UnregisterInputHandlerChangedDelegate()
|
||||||
|
{
|
||||||
|
if (UImGuiSettings* Settings = GetMutableDefault<UImGuiSettings>())
|
||||||
|
{
|
||||||
|
Settings->OnImGuiInputHandlerClassChanged.RemoveAll(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FReply SImGuiWidget::WithMouseLockRequests(FReply&& Reply)
|
FReply SImGuiWidget::WithMouseLockRequests(FReply&& Reply)
|
||||||
{
|
{
|
||||||
const bool bNeedMouseLock = bCanvasDragging || bFrameDragging;
|
const bool bNeedMouseLock = bCanvasDragging || bFrameDragging;
|
||||||
@ -404,27 +470,6 @@ bool SImGuiWidget::IsConsoleOpened() const
|
|||||||
return GameViewport->ViewportConsole && GameViewport->ViewportConsole->ConsoleState != NAME_None;
|
return GameViewport->ViewportConsole && GameViewport->ViewportConsole->ConsoleState != NAME_None;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SImGuiWidget::IgnoreKeyEvent(const FKeyEvent& KeyEvent) const
|
|
||||||
{
|
|
||||||
// Ignore console open/close events.
|
|
||||||
if (KeyEvent.GetKey() == EKeys::Tilde)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore escape keys unless they are needed to cancel operations in ImGui.
|
|
||||||
if (KeyEvent.GetKey() == EKeys::Escape)
|
|
||||||
{
|
|
||||||
auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex);
|
|
||||||
if (!ContextProxy || !ContextProxy->HasActiveItem())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SImGuiWidget::SetMouseCursorOverride(EMouseCursor::Type InMouseCursorOverride)
|
void SImGuiWidget::SetMouseCursorOverride(EMouseCursor::Type InMouseCursorOverride)
|
||||||
{
|
{
|
||||||
if (MouseCursorOverride != InMouseCursorOverride)
|
if (MouseCursorOverride != InMouseCursorOverride)
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
|
|
||||||
class FImGuiModuleManager;
|
class FImGuiModuleManager;
|
||||||
|
class UImGuiInputHandler;
|
||||||
|
|
||||||
// Slate widget for rendering ImGui output and storing Slate inputs.
|
// Slate widget for rendering ImGui output and storing Slate inputs.
|
||||||
class SImGuiWidget : public SLeafWidget
|
class SImGuiWidget : public SLeafWidget
|
||||||
@ -45,7 +46,7 @@ public:
|
|||||||
|
|
||||||
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
|
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
|
||||||
|
|
||||||
virtual bool SupportsKeyboardFocus() const override { return bInputEnabled; }
|
virtual bool SupportsKeyboardFocus() const override { return bInputEnabled && !IsConsoleOpened(); }
|
||||||
|
|
||||||
virtual FReply OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent) override;
|
virtual FReply OnKeyChar(const FGeometry& MyGeometry, const FCharacterEvent& CharacterEvent) override;
|
||||||
|
|
||||||
@ -63,8 +64,6 @@ public:
|
|||||||
|
|
||||||
virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
virtual FReply OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||||
|
|
||||||
virtual FCursorReply OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const override;
|
|
||||||
|
|
||||||
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
|
||||||
|
|
||||||
virtual FReply OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& FocusEvent) override;
|
virtual FReply OnFocusReceived(const FGeometry& MyGeometry, const FFocusEvent& FocusEvent) override;
|
||||||
@ -75,6 +74,8 @@ public:
|
|||||||
|
|
||||||
virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;
|
virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override;
|
||||||
|
|
||||||
|
virtual FCursorReply OnCursorQuery(const FGeometry& MyGeometry, const FPointerEvent& CursorEvent) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
enum class EInputMode : uint8
|
enum class EInputMode : uint8
|
||||||
@ -86,6 +87,13 @@ private:
|
|||||||
Full
|
Full
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void CreateInputHandler();
|
||||||
|
void ReleaseInputHandler();
|
||||||
|
void RecreateInputHandler();
|
||||||
|
|
||||||
|
void RegisterInputHandlerChangedDelegate();
|
||||||
|
void UnregisterInputHandlerChangedDelegate();
|
||||||
|
|
||||||
// If needed, add to event reply a mouse lock or unlock request.
|
// If needed, add to event reply a mouse lock or unlock request.
|
||||||
FORCEINLINE FReply WithMouseLockRequests(FReply&& Reply);
|
FORCEINLINE FReply WithMouseLockRequests(FReply&& Reply);
|
||||||
|
|
||||||
@ -94,8 +102,6 @@ private:
|
|||||||
|
|
||||||
bool IsConsoleOpened() const;
|
bool IsConsoleOpened() const;
|
||||||
|
|
||||||
bool IgnoreKeyEvent(const FKeyEvent& KeyEvent) const;
|
|
||||||
|
|
||||||
void SetMouseCursorOverride(EMouseCursor::Type InMouseCursorOverride);
|
void SetMouseCursorOverride(EMouseCursor::Type InMouseCursorOverride);
|
||||||
|
|
||||||
// Update visibility based on input enabled state.
|
// Update visibility based on input enabled state.
|
||||||
@ -147,6 +153,7 @@ private:
|
|||||||
|
|
||||||
FImGuiModuleManager* ModuleManager = nullptr;
|
FImGuiModuleManager* ModuleManager = nullptr;
|
||||||
TWeakObjectPtr<UGameViewportClient> GameViewport;
|
TWeakObjectPtr<UGameViewportClient> GameViewport;
|
||||||
|
TWeakObjectPtr<UImGuiInputHandler> InputHandler;
|
||||||
|
|
||||||
mutable TArray<FSlateVertex> VertexBuffer;
|
mutable TArray<FSlateVertex> VertexBuffer;
|
||||||
mutable TArray<SlateIndex> IndexBuffer;
|
mutable TArray<SlateIndex> IndexBuffer;
|
||||||
|
168
Source/ImGui/Public/ImGuiInputHandler.h
Normal file
168
Source/ImGui/Public/ImGuiInputHandler.h
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <CoreMinimal.h>
|
||||||
|
#include <UObject/Object.h>
|
||||||
|
|
||||||
|
#include "ImGuiInputHandler.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
class FImGuiModuleManager;
|
||||||
|
class UGameViewportClient;
|
||||||
|
|
||||||
|
struct FAnalogInputEvent;
|
||||||
|
struct FCharacterEvent;
|
||||||
|
struct FKeyEvent;
|
||||||
|
|
||||||
|
/** Response used by ImGui Input Handler to communicate input handling requests. */
|
||||||
|
struct IMGUI_API FImGuiInputResponse
|
||||||
|
{
|
||||||
|
/** Create empty response with no requests. */
|
||||||
|
FImGuiInputResponse() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create response with custom request configuration.
|
||||||
|
*
|
||||||
|
* @param bInProcess - State of the processing request.
|
||||||
|
* @param bInConsume - State of the consume request.
|
||||||
|
*/
|
||||||
|
FImGuiInputResponse(bool bInProcess, bool bInConsume)
|
||||||
|
: bProcess(bInProcess)
|
||||||
|
, bConsume(bInConsume)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this response contains processing request.
|
||||||
|
*
|
||||||
|
* @returns True, if processing was requested and false otherwise.
|
||||||
|
*/
|
||||||
|
FORCEINLINE bool HasProcessingRequest() const { return bProcess; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this response contains consume request.
|
||||||
|
*
|
||||||
|
* @returns True, if consume was requested and false otherwise.
|
||||||
|
*/
|
||||||
|
FORCEINLINE bool HasConsumeRequest() const { return bConsume; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the processing request.
|
||||||
|
*
|
||||||
|
* @param bInProcess - True, to request input processing (implicit) and false otherwise.
|
||||||
|
* @returns Reference to this response (for chaining requests).
|
||||||
|
*/
|
||||||
|
FORCEINLINE FImGuiInputResponse& RequestProcessing(bool bInProcess = true) { bProcess = bInProcess; return *this; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the consume request.
|
||||||
|
*
|
||||||
|
* @param bInConsume - True, to request input consume (implicit) and false otherwise.
|
||||||
|
* @returns Reference to this response (for chaining requests).
|
||||||
|
*/
|
||||||
|
FORCEINLINE FImGuiInputResponse& RequestConsume(bool bInConsume = true) { bConsume = bInConsume; return *this; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool bProcess = false;
|
||||||
|
|
||||||
|
bool bConsume = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines behaviour when handling input events. It allows to customize handling of the keyboard and gamepad input,
|
||||||
|
* primarily to support shortcuts in ImGui input mode. Since mouse is not really needed for this functionality and
|
||||||
|
* mouse pointer state and focus are closely connected to input mode, mouse events are left out of this interface.
|
||||||
|
*
|
||||||
|
* When receiving keyboard and gamepad events ImGui Widget calls input handler to query expected behaviour. By default,
|
||||||
|
* with a few exceptions (see @ OnKeyDown) all events are expected to be processed and consumed. Custom implementations
|
||||||
|
* may tweak that behaviour and/or inject custom code.
|
||||||
|
*
|
||||||
|
* Note that returned response is only treated as a hint. In current implementation all consume requests are respected
|
||||||
|
* but to protect from locking ImGui input states, key up events are always processed. Decision about blocking certain
|
||||||
|
* inputs can be taken during key down events and processing corresponding key up events should not make difference.
|
||||||
|
*
|
||||||
|
* Also note that input handler functions are only called when ImGui Widget is receiving input events, what can be for
|
||||||
|
* instance suppressed by opening console.
|
||||||
|
*
|
||||||
|
* See @ Project Settings/Plugins/ImGui/Input/ImGuiInputHandlerClass property to set custom implementation.
|
||||||
|
*/
|
||||||
|
UCLASS()
|
||||||
|
class IMGUI_API UImGuiInputHandler : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when handling character events.
|
||||||
|
*
|
||||||
|
* @returns Response with rules how input should be handled. Default implementation contains requests to process
|
||||||
|
* and consume this event.
|
||||||
|
*/
|
||||||
|
virtual FImGuiInputResponse OnKeyChar(const struct FCharacterEvent& CharacterEvent) { return DefaultResponse(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when handling keyboard key down events.
|
||||||
|
*
|
||||||
|
* @returns Response with rules how input should be handled. Default implementation contains requests to process
|
||||||
|
* and consume most of the key, but unlike other cases it requests to ignore certain events, like those that are
|
||||||
|
* needed to open console or close PIE session in editor.
|
||||||
|
*/
|
||||||
|
virtual FImGuiInputResponse OnKeyDown(const FKeyEvent& KeyEvent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when handling keyboard key up events.
|
||||||
|
*
|
||||||
|
* Note that regardless of returned response, key up events are always processed by ImGui Widget.
|
||||||
|
*
|
||||||
|
* @returns Response with rules how input should be handled. Default implementation contains requests to consume
|
||||||
|
* this event.
|
||||||
|
*/
|
||||||
|
virtual FImGuiInputResponse OnKeyUp(const FKeyEvent& KeyEvent) { return DefaultResponse(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when handling gamepad key down events.
|
||||||
|
*
|
||||||
|
* @returns Response with rules how input should be handled. Default implementation contains requests to process
|
||||||
|
* and consume this event.
|
||||||
|
*/
|
||||||
|
virtual FImGuiInputResponse OnGamepadKeyDown(const FKeyEvent& GamepadKeyEvent) { return DefaultResponse(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when handling gamepad key up events.
|
||||||
|
*
|
||||||
|
* Note that regardless of returned response, key up events are always processed by ImGui Widget.
|
||||||
|
*
|
||||||
|
* @returns Response with rules how input should be handled. Default implementation contains requests to consume
|
||||||
|
* this event.
|
||||||
|
*/
|
||||||
|
virtual FImGuiInputResponse OnGamepadKeyUp(const FKeyEvent& GamepadKeyEvent) { return DefaultResponse(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when handling gamepad analog events.
|
||||||
|
*
|
||||||
|
* @returns Response with rules how input should be handled. Default implementation contains requests to process
|
||||||
|
* and consume this event.
|
||||||
|
*/
|
||||||
|
virtual FImGuiInputResponse OnGamepadAxis(const FAnalogInputEvent& GamepadAxisEvent) { return DefaultResponse(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/** Checks whether corresponding ImGui context has an active item. */
|
||||||
|
bool HasImGuiActiveItem() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void Initialize(FImGuiModuleManager* InModuleManager, UGameViewportClient* InGameViewport, int32 InContextIndex);
|
||||||
|
|
||||||
|
FORCEINLINE FImGuiInputResponse DefaultResponse() { return FImGuiInputResponse{ true, true }; }
|
||||||
|
|
||||||
|
FImGuiModuleManager* ModuleManager = nullptr;
|
||||||
|
|
||||||
|
TWeakObjectPtr<UGameViewportClient> GameViewport;
|
||||||
|
|
||||||
|
int32 ContextIndex = -1;
|
||||||
|
|
||||||
|
friend class FImGuiInputHandlerFactory;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user