mirror of
				https://github.com/kevinporetti/UnrealImGui.git
				synced 2025-10-31 05:53:17 +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
	 Sebastian
						Sebastian