Refactorization of ImGui settings:

- Divided ImGui settings into UImGuiSettings persistent object and FImGuiModuleSettings proxy that provides interface for other classes and handles delayed loading of UImGuiSettings.
- Removed from FImGuiModuleProperties and FImGuiModuleCommands direct dependencies on UImGuiSettings.
- Simplified FImGuiModuleProperties making it more robust when moving data after hot-reloading.
- Inverted binding logic by injecting FImGuiModuleProperties and FImGuiModuleCommands into FImGuiModuleSettings and letting it take care of synchronization. Dependencies are setup by module manager.
- Added to module manager FImGuiModuleSettings and interface to access it.
- Cleaned interface of FImGuiInputHandlerFactory and removed direct dependency on settings.
This commit is contained in:
Sebastian 2018-12-08 21:03:18 +00:00
parent 0bf940de81
commit 09572a8ef9
17 changed files with 306 additions and 370 deletions

View File

@ -50,7 +50,7 @@ FImGuiEditor::~FImGuiEditor()
void FImGuiEditor::Register() void FImGuiEditor::Register()
{ {
// Only register after UImGuiSettings class is initialized (necessary to check in early loading stages). // Only register after UImGuiSettings class is initialized (necessary to check in early loading stages).
if (!bSettingsRegistered && GImGuiSettings) if (!bSettingsRegistered && UImGuiSettings::Get())
{ {
if (ISettingsModule* SettingsModule = GetSettingsModule()) if (ISettingsModule* SettingsModule = GetSettingsModule())
{ {
@ -59,7 +59,7 @@ void FImGuiEditor::Register()
SettingsModule->RegisterSettings(SETTINGS_CONTAINER, SettingsModule->RegisterSettings(SETTINGS_CONTAINER,
LOCTEXT("ImGuiSettingsName", "ImGui"), LOCTEXT("ImGuiSettingsName", "ImGui"),
LOCTEXT("ImGuiSettingsDescription", "Configure the Unreal ImGui plugin."), LOCTEXT("ImGuiSettingsDescription", "Configure the Unreal ImGui plugin."),
GImGuiSettings); UImGuiSettings::Get());
} }
} }

View File

@ -4,13 +4,13 @@
#include "ImGuiDemo.h" #include "ImGuiDemo.h"
#include "ImGuiModuleManager.h" #include "ImGuiModuleProperties.h"
// Demo copied (with minor modifications) from ImGui examples. See https://github.com/ocornut/imgui. // Demo copied (with minor modifications) from ImGui examples. See https://github.com/ocornut/imgui.
void FImGuiDemo::DrawControls(int32 ContextIndex) void FImGuiDemo::DrawControls(int32 ContextIndex)
{ {
if (ModuleManager.GetProperties().ShowDemo()) if (Properties.ShowDemo())
{ {
const int32 ContextBit = ContextIndex < 0 ? 0 : 1 << ContextIndex; const int32 ContextBit = ContextIndex < 0 ? 0 : 1 << ContextIndex;

View File

@ -4,15 +4,15 @@
#include <imgui.h> #include <imgui.h>
class FImGuiModuleManager; class FImGuiModuleProperties;
// Widget drawing ImGui demo. // Widget drawing ImGui demo.
class FImGuiDemo class FImGuiDemo
{ {
public: public:
FImGuiDemo(FImGuiModuleManager& InModuleManager) FImGuiDemo(FImGuiModuleProperties& InProperties)
: ModuleManager(InModuleManager) : Properties(InProperties)
{ {
} }
@ -20,7 +20,7 @@ public:
private: private:
FImGuiModuleManager& ModuleManager; FImGuiModuleProperties& Properties;
ImVec4 ClearColor = ImColor{ 114, 144, 154 }; ImVec4 ClearColor = ImColor{ 114, 144, 154 };

View File

@ -92,9 +92,10 @@ namespace
return (CheckBoxState == ECheckBoxState::Undetermined) || ((CheckBoxState == ECheckBoxState::Checked) == bValue); return (CheckBoxState == ECheckBoxState::Undetermined) || ((CheckBoxState == ECheckBoxState::Checked) == bValue);
} }
bool AreModifiersMatching(const FImGuiKeyInfo& KeyInfo, const FKeyEvent& KeyEvent) bool IsMatchingEvent(const FKeyEvent& KeyEvent, const FImGuiKeyInfo& KeyInfo)
{ {
return IsMatching(KeyInfo.Shift, KeyEvent.IsShiftDown()) return (KeyInfo.Key == KeyEvent.GetKey())
&& IsMatching(KeyInfo.Shift, KeyEvent.IsShiftDown())
&& IsMatching(KeyInfo.Ctrl, KeyEvent.IsControlDown()) && IsMatching(KeyInfo.Ctrl, KeyEvent.IsControlDown())
&& IsMatching(KeyInfo.Alt, KeyEvent.IsAltDown()) && IsMatching(KeyInfo.Alt, KeyEvent.IsAltDown())
&& IsMatching(KeyInfo.Cmd, KeyEvent.IsCommandDown()); && IsMatching(KeyInfo.Cmd, KeyEvent.IsCommandDown());
@ -103,9 +104,7 @@ namespace
bool UImGuiInputHandler::IsToggleInputEvent(const FKeyEvent& KeyEvent) const bool UImGuiInputHandler::IsToggleInputEvent(const FKeyEvent& KeyEvent) const
{ {
return GImGuiSettings return IsMatchingEvent(KeyEvent, ModuleManager->GetSettings().GetToggleInputKey());
&& (KeyEvent.GetKey() == GImGuiSettings->GetToggleInputKey().Key)
&& AreModifiersMatching(GImGuiSettings->GetToggleInputKey(), KeyEvent);
} }
bool UImGuiInputHandler::HasImGuiActiveItem() const bool UImGuiInputHandler::HasImGuiActiveItem() const

View File

@ -5,15 +5,11 @@
#include "ImGuiInputHandlerFactory.h" #include "ImGuiInputHandlerFactory.h"
#include "ImGuiInputHandler.h" #include "ImGuiInputHandler.h"
#include "ImGuiSettings.h"
UImGuiInputHandler* FImGuiInputHandlerFactory::NewHandler(FImGuiModuleManager* ModuleManager, UGameViewportClient* GameViewport, int32 ContextIndex) UImGuiInputHandler* FImGuiInputHandlerFactory::NewHandler(const FStringClassReference& HandlerClassReference, FImGuiModuleManager* ModuleManager, UGameViewportClient* GameViewport, int32 ContextIndex)
{ {
UClass* HandlerClass = nullptr; UClass* HandlerClass = nullptr;
if (GImGuiSettings)
{
const auto& HandlerClassReference = GImGuiSettings->GetImGuiInputHandlerClass();
if (HandlerClassReference.IsValid()) if (HandlerClassReference.IsValid())
{ {
HandlerClass = HandlerClassReference.TryLoadClass<UImGuiInputHandler>(); HandlerClass = HandlerClassReference.TryLoadClass<UImGuiInputHandler>();
@ -23,7 +19,6 @@ UImGuiInputHandler* FImGuiInputHandlerFactory::NewHandler(FImGuiModuleManager* M
UE_LOG(LogImGuiInputHandler, Error, TEXT("Couldn't load ImGui Input Handler class '%s'."), *HandlerClassReference.ToString()); UE_LOG(LogImGuiInputHandler, Error, TEXT("Couldn't load ImGui Input Handler class '%s'."), *HandlerClassReference.ToString());
} }
} }
}
if (!HandlerClass) if (!HandlerClass)
{ {

View File

@ -10,7 +10,7 @@ class FImGuiInputHandlerFactory
{ {
public: public:
static UImGuiInputHandler* NewHandler(FImGuiModuleManager* ModuleManager, UGameViewportClient* GameViewport, int32 ContextIndex); static UImGuiInputHandler* NewHandler(const FStringClassReference& HandlerClassReference, FImGuiModuleManager* ModuleManager, UGameViewportClient* GameViewport, int32 ContextIndex);
static void ReleaseHandler(UImGuiInputHandler* Handler); static void ReleaseHandler(UImGuiInputHandler* Handler);
}; };

View File

@ -4,118 +4,70 @@
#include "ImGuiModuleCommands.h" #include "ImGuiModuleCommands.h"
#include "ImGuiModuleManager.h"
#include "ImGuiSettings.h"
#include "Utilities/DebugExecBindings.h" #include "Utilities/DebugExecBindings.h"
namespace CommandNames const TCHAR* const FImGuiModuleCommands::ToggleInput = TEXT("ImGui.ToggleInput");
{ const TCHAR* const FImGuiModuleCommands::ToggleKeyboardNavigation = TEXT("ImGui.ToggleKeyboardNavigation");
namespace const TCHAR* const FImGuiModuleCommands::ToggleGamepadNavigation = TEXT("ImGui.ToggleGamepadNavigation");
{ const TCHAR* const FImGuiModuleCommands::ToggleKeyboardInputSharing = TEXT("ImGui.ToggleKeyboardInputSharing");
const TCHAR* ToggleInput = TEXT("ImGui.ToggleInput"); const TCHAR* const FImGuiModuleCommands::ToggleGamepadInputSharing = TEXT("ImGui.ToggleGamepadInputSharing");
const TCHAR* ToggleKeyboardNavigation = TEXT("ImGui.ToggleKeyboardNavigation"); const TCHAR* const FImGuiModuleCommands::ToggleDemo = TEXT("ImGui.ToggleDemo");
const TCHAR* ToggleGamepadNavigation = TEXT("ImGui.ToggleGamepadNavigation");
const TCHAR* ToggleKeyboardInputSharing = TEXT("ImGui.ToggleKeyboardInputSharing");
const TCHAR* ToggleGamepadInputSharing = TEXT("ImGui.ToggleGamepadInputSharing");
const TCHAR* ToggleDemo = TEXT("ImGui.ToggleDemo");
}
}
FImGuiModuleCommands::FImGuiModuleCommands(FImGuiModuleManager& InModuleManager) FImGuiModuleCommands::FImGuiModuleCommands(FImGuiModuleProperties& InProperties)
: ModuleManager(InModuleManager) : Properties(InProperties)
, ToggleInputCommand(CommandNames::ToggleInput, , ToggleInputCommand(ToggleInput,
TEXT("Toggle ImGui input mode."), TEXT("Toggle ImGui input mode."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleInput)) FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleInputImpl))
, ToggleKeyboardNavigationCommand(CommandNames::ToggleKeyboardNavigation, , ToggleKeyboardNavigationCommand(ToggleKeyboardNavigation,
TEXT("Toggle ImGui keyboard navigation."), TEXT("Toggle ImGui keyboard navigation."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardNavigation)) FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardNavigationImpl))
, ToggleGamepadNavigationCommand(CommandNames::ToggleGamepadNavigation, , ToggleGamepadNavigationCommand(ToggleGamepadNavigation,
TEXT("Toggle ImGui gamepad navigation."), TEXT("Toggle ImGui gamepad navigation."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadNavigation)) FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadNavigationImpl))
, ToggleKeyboardInputSharingCommand(CommandNames::ToggleKeyboardInputSharing, , ToggleKeyboardInputSharingCommand(ToggleKeyboardInputSharing,
TEXT("Toggle ImGui keyboard input sharing."), TEXT("Toggle ImGui keyboard input sharing."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardInputSharing)) FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardInputSharingImpl))
, ToggleGamepadInputSharingCommand(CommandNames::ToggleGamepadInputSharing, , ToggleGamepadInputSharingCommand(ToggleGamepadInputSharing,
TEXT("Toggle ImGui gamepad input sharing."), TEXT("Toggle ImGui gamepad input sharing."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadInputSharing)) FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadInputSharingImpl))
, ToggleDemoCommand(CommandNames::ToggleDemo, , ToggleDemoCommand(ToggleDemo,
TEXT("Toggle ImGui demo."), TEXT("Toggle ImGui demo."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleDemo)) FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleDemoImpl))
{ {
// Delegate initializer to support settings loaded after this object creation (in stand-alone builds) and potential
// reloading of settings.
UImGuiSettings::OnSettingsLoaded().AddRaw(this, &FImGuiModuleCommands::InitializeSettings);
// Call initializer to support settings already loaded (editor).
InitializeSettings();
} }
FImGuiModuleCommands::~FImGuiModuleCommands() void FImGuiModuleCommands::SetKeyBinding(const TCHAR* CommandName, const FImGuiKeyInfo& KeyInfo)
{ {
UImGuiSettings::OnSettingsLoaded().RemoveAll(this); DebugExecBindings::UpdatePlayerInputs(KeyInfo, CommandName);
UnregisterSettingsDelegates();
} }
void FImGuiModuleCommands::InitializeSettings() void FImGuiModuleCommands::ToggleInputImpl()
{ {
RegisterSettingsDelegates(); Properties.ToggleInput();
// We manually update key bindings based on ImGui settings rather than using input configuration. This works out
// of the box in packed and staged builds and it helps to avoid ambiguities where ImGui settings are stored.
UpdateToggleInputKeyBinding();
} }
void FImGuiModuleCommands::RegisterSettingsDelegates() void FImGuiModuleCommands::ToggleKeyboardNavigationImpl()
{ {
if (GImGuiSettings && !GImGuiSettings->OnToggleInputKeyChanged.IsBoundToObject(this)) Properties.ToggleKeyboardNavigation();
{
GImGuiSettings->OnToggleInputKeyChanged.AddRaw(this, &FImGuiModuleCommands::UpdateToggleInputKeyBinding);
}
} }
void FImGuiModuleCommands::UnregisterSettingsDelegates() void FImGuiModuleCommands::ToggleGamepadNavigationImpl()
{ {
if (GImGuiSettings) Properties.ToggleGamepadNavigation();
{
GImGuiSettings->OnToggleInputKeyChanged.RemoveAll(this);
}
} }
void FImGuiModuleCommands::UpdateToggleInputKeyBinding() void FImGuiModuleCommands::ToggleKeyboardInputSharingImpl()
{ {
if (GImGuiSettings) Properties.ToggleKeyboardInputSharing();
{
DebugExecBindings::UpdatePlayerInputs(GImGuiSettings->GetToggleInputKey(), CommandNames::ToggleInput);
}
} }
void FImGuiModuleCommands::ToggleInput() void FImGuiModuleCommands::ToggleGamepadInputSharingImpl()
{ {
ModuleManager.GetProperties().ToggleInput(); Properties.ToggleGamepadInputSharing();
} }
void FImGuiModuleCommands::ToggleKeyboardNavigation() void FImGuiModuleCommands::ToggleDemoImpl()
{ {
ModuleManager.GetProperties().ToggleKeyboardNavigation(); Properties.ToggleDemo();
}
void FImGuiModuleCommands::ToggleGamepadNavigation()
{
ModuleManager.GetProperties().ToggleGamepadNavigation();
}
void FImGuiModuleCommands::ToggleKeyboardInputSharing()
{
ModuleManager.GetProperties().ToggleKeyboardInputSharing();
}
void FImGuiModuleCommands::ToggleGamepadInputSharing()
{
ModuleManager.GetProperties().ToggleGamepadInputSharing();
}
void FImGuiModuleCommands::ToggleDemo()
{
ModuleManager.GetProperties().ToggleDemo();
} }

View File

@ -5,39 +5,35 @@
#include <IConsoleManager.h> #include <IConsoleManager.h>
class FImGuiModuleManager; struct FImGuiKeyInfo;
class FImGuiModuleProperties;
// Manges ImGui module console commands. // Manges ImGui module console commands.
class FImGuiModuleCommands class FImGuiModuleCommands
{ {
public: public:
FImGuiModuleCommands(FImGuiModuleManager& InModuleManager); static const TCHAR* const ToggleInput;
~FImGuiModuleCommands(); static const TCHAR* const ToggleKeyboardNavigation;
static const TCHAR* const ToggleGamepadNavigation;
static const TCHAR* const ToggleKeyboardInputSharing;
static const TCHAR* const ToggleGamepadInputSharing;
static const TCHAR* const ToggleDemo;
FImGuiModuleCommands(FImGuiModuleProperties& InProperties);
void SetKeyBinding(const TCHAR* CommandName, const FImGuiKeyInfo& KeyInfo);
private: private:
FImGuiModuleCommands(const FImGuiModuleCommands&) = delete; void ToggleInputImpl();
FImGuiModuleCommands& operator=(const FImGuiModuleCommands&) = delete; void ToggleKeyboardNavigationImpl();
void ToggleGamepadNavigationImpl();
void ToggleKeyboardInputSharingImpl();
void ToggleGamepadInputSharingImpl();
void ToggleDemoImpl();
FImGuiModuleCommands(FImGuiModuleCommands&&) = delete; FImGuiModuleProperties& Properties;
FImGuiModuleCommands& operator=(FImGuiModuleCommands&&) = delete;
void InitializeSettings();
void RegisterSettingsDelegates();
void UnregisterSettingsDelegates();
void UpdateToggleInputKeyBinding();
void ToggleInput();
void ToggleKeyboardNavigation();
void ToggleGamepadNavigation();
void ToggleKeyboardInputSharing();
void ToggleGamepadInputSharing();
void ToggleDemo();
FImGuiModuleManager& ModuleManager;
FAutoConsoleCommand ToggleInputCommand; FAutoConsoleCommand ToggleInputCommand;
FAutoConsoleCommand ToggleKeyboardNavigationCommand; FAutoConsoleCommand ToggleKeyboardNavigationCommand;

View File

@ -13,8 +13,9 @@
FImGuiModuleManager::FImGuiModuleManager() FImGuiModuleManager::FImGuiModuleManager()
: ModuleCommands(*this) : Commands(Properties)
, ImGuiDemo(*this) , Settings(Properties, Commands)
, ImGuiDemo(Properties)
{ {
// Register in context manager to get information whenever a new context proxy is created. // Register in context manager to get information whenever a new context proxy is created.
ContextManager.OnContextProxyCreated().AddRaw(this, &FImGuiModuleManager::OnContextProxyCreated); ContextManager.OnContextProxyCreated().AddRaw(this, &FImGuiModuleManager::OnContextProxyCreated);

View File

@ -6,6 +6,7 @@
#include "ImGuiDemo.h" #include "ImGuiDemo.h"
#include "ImGuiModuleCommands.h" #include "ImGuiModuleCommands.h"
#include "ImGuiModuleProperties.h" #include "ImGuiModuleProperties.h"
#include "ImGuiSettings.h"
#include "SImGuiWidget.h" #include "SImGuiWidget.h"
#include "TextureManager.h" #include "TextureManager.h"
@ -18,6 +19,9 @@ class FImGuiModuleManager
public: public:
// Get interface to module settings.
FImGuiModuleSettings& GetSettings() { return Settings; }
// Get interface to module state properties. // Get interface to module state properties.
FImGuiModuleProperties& GetProperties() { return Properties; } FImGuiModuleProperties& GetProperties() { return Properties; }
@ -68,7 +72,10 @@ private:
FImGuiModuleProperties Properties; FImGuiModuleProperties Properties;
// Tying module console commands to life-cycle of this manager and module. // Tying module console commands to life-cycle of this manager and module.
FImGuiModuleCommands ModuleCommands; FImGuiModuleCommands Commands;
// ImGui settings proxy (valid in every loading stage).
FImGuiModuleSettings Settings;
// Widget that we add to all created contexts to draw ImGui demo. // Widget that we add to all created contexts to draw ImGui demo.
FImGuiDemo ImGuiDemo; FImGuiDemo ImGuiDemo;

View File

@ -1,59 +0,0 @@
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
#include "ImGuiPrivatePCH.h"
#include "ImGuiModuleProperties.h"
#include "ImGuiSettings.h"
FImGuiModuleProperties::FImGuiModuleProperties()
{
// Delegate initializer to support settings loaded after this object creation (in stand-alone builds) and potential
// reloading of settings.
UImGuiSettings::OnSettingsLoaded().AddRaw(this, &FImGuiModuleProperties::InitializeSettings);
// Call initializer to support already loaded settings (editor).
InitializeSettings();
}
FImGuiModuleProperties::~FImGuiModuleProperties()
{
UImGuiSettings::OnSettingsLoaded().RemoveAll(this);
UnregisterSettingsDelegates();
}
void FImGuiModuleProperties::InitializeSettings()
{
if (GImGuiSettings)
{
RegisterSettingsDelegates();
bKeyboardInputShared = GImGuiSettings->ShareKeyboardInput();
bGamepadInputShared = GImGuiSettings->ShareGamepadInput();
}
}
void FImGuiModuleProperties::RegisterSettingsDelegates()
{
if (GImGuiSettings)
{
if (!GImGuiSettings->OnShareKeyboardInputChanged.IsBoundToObject(this))
{
GImGuiSettings->OnShareKeyboardInputChanged.AddRaw(this, &FImGuiModuleProperties::SetKeyboardInputShared);
}
if (!GImGuiSettings->OnShareGamepadInputChanged.IsBoundToObject(this))
{
GImGuiSettings->OnShareGamepadInputChanged.AddRaw(this, &FImGuiModuleProperties::SetGamepadInputShared);
}
}
}
void FImGuiModuleProperties::UnregisterSettingsDelegates()
{
if (GImGuiSettings)
{
GImGuiSettings->OnShareKeyboardInputChanged.RemoveAll(this);
GImGuiSettings->OnShareGamepadInputChanged.RemoveAll(this);
}
}

View File

@ -11,5 +11,13 @@
#include <Core.h> #include <Core.h>
#include <Engine.h> #include <Engine.h>
// For backward compatibility we will use FStringClassReference which in newer engine versions is a typedef for
// FSoftClassPath. Include right soft class reference header to avoid warnings in newer engine version.
#if ENGINE_COMPATIBILITY_LEGACY_STRING_CLASS_REF
#include <StringClassReference.h>
#else
#include <UObject/SoftObjectPath.h>
#endif
// You should place include statements to your module's private header files here. You only need to // You should place include statements to your module's private header files here. You only need to
// add includes for headers that are used in most of your module's source files though. // add includes for headers that are used in most of your module's source files though.

View File

@ -4,29 +4,17 @@
#include "ImGuiSettings.h" #include "ImGuiSettings.h"
#include "ImGuiModuleCommands.h"
UImGuiSettings* GImGuiSettings = nullptr; #include "ImGuiModuleProperties.h"
FSimpleMulticastDelegate& UImGuiSettings::OnSettingsLoaded()
{
static FSimpleMulticastDelegate Instance;
return Instance;
}
UImGuiSettings::UImGuiSettings() //====================================================================================================
{ // UImGuiSettings
#if WITH_EDITOR //====================================================================================================
RegisterPropertyChangedDelegate();
#endif
}
UImGuiSettings::~UImGuiSettings() UImGuiSettings* UImGuiSettings::DefaultInstance = nullptr;
{
#if WITH_EDITOR FSimpleMulticastDelegate UImGuiSettings::OnSettingsLoaded;
UnregisterPropertyChangedDelegate();
#endif
}
void UImGuiSettings::PostInitProperties() void UImGuiSettings::PostInitProperties()
{ {
@ -60,8 +48,8 @@ void UImGuiSettings::PostInitProperties()
if (IsTemplate()) if (IsTemplate())
{ {
GImGuiSettings = this; DefaultInstance = this;
OnSettingsLoaded().Broadcast(); OnSettingsLoaded.Broadcast();
} }
} }
@ -69,53 +57,106 @@ void UImGuiSettings::BeginDestroy()
{ {
Super::BeginDestroy(); Super::BeginDestroy();
if (GImGuiSettings == this) if (DefaultInstance == this)
{ {
GImGuiSettings = nullptr; DefaultInstance = nullptr;
}
}
//====================================================================================================
// FImGuiModuleSettings
//====================================================================================================
FImGuiModuleSettings::FImGuiModuleSettings(FImGuiModuleProperties& InProperties, FImGuiModuleCommands& InCommands)
: Properties(InProperties)
, Commands(InCommands)
{
#if WITH_EDITOR
FCoreUObjectDelegates::OnObjectPropertyChanged.AddRaw(this, &FImGuiModuleSettings::OnPropertyChanged);
#endif
// Delegate initializer to support settings loaded after this object creation (in stand-alone builds) and potential
// reloading of settings.
UImGuiSettings::OnSettingsLoaded.AddRaw(this, &FImGuiModuleSettings::UpdateSettings);
// Call initializer to support settings already loaded (editor).
UpdateSettings();
}
FImGuiModuleSettings::~FImGuiModuleSettings()
{
UImGuiSettings::OnSettingsLoaded.RemoveAll(this);
#if WITH_EDITOR
FCoreUObjectDelegates::OnObjectPropertyChanged.RemoveAll(this);
#endif
}
void FImGuiModuleSettings::UpdateSettings()
{
if (UImGuiSettings* SettingsObject = UImGuiSettings::Get())
{
SetImGuiInputHandlerClass(SettingsObject->ImGuiInputHandlerClass);
SetShareKeyboardInput(SettingsObject->bShareKeyboardInput);
SetShareGamepadInput(SettingsObject->bShareGamepadInput);
SetUseSoftwareCursor(SettingsObject->bUseSoftwareCursor);
SetToggleInputKey(SettingsObject->ToggleInput);
}
}
void FImGuiModuleSettings::SetImGuiInputHandlerClass(const FStringClassReference& ClassReference)
{
if (ImGuiInputHandlerClass != ClassReference)
{
ImGuiInputHandlerClass = ClassReference;
OnImGuiInputHandlerClassChanged.Broadcast(ClassReference);
}
}
void FImGuiModuleSettings::SetShareKeyboardInput(bool bShare)
{
if (bShareKeyboardInput != bShare)
{
bShareKeyboardInput = bShare;
Properties.SetKeyboardInputShared(bShare);
}
}
void FImGuiModuleSettings::SetShareGamepadInput(bool bShare)
{
if (bShareGamepadInput != bShare)
{
bShareGamepadInput = bShare;
Properties.SetGamepadInputShared(bShare);
}
}
void FImGuiModuleSettings::SetUseSoftwareCursor(bool bUse)
{
if (bUseSoftwareCursor != bUse)
{
bUseSoftwareCursor = bUse;
OnUseSoftwareCursorChanged.Broadcast(bUse);
}
}
void FImGuiModuleSettings::SetToggleInputKey(const FImGuiKeyInfo& KeyInfo)
{
if (ToggleInputKey != KeyInfo)
{
ToggleInputKey = KeyInfo;
Commands.SetKeyBinding(FImGuiModuleCommands::ToggleInput, ToggleInputKey);
} }
} }
#if WITH_EDITOR #if WITH_EDITOR
void UImGuiSettings::RegisterPropertyChangedDelegate() void FImGuiModuleSettings::OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent)
{ {
if (!FCoreUObjectDelegates::OnObjectPropertyChanged.IsBoundToObject(this)) if (ObjectBeingModified == UImGuiSettings::Get())
{ {
FCoreUObjectDelegates::OnObjectPropertyChanged.AddUObject(this, &UImGuiSettings::OnPropertyChanged); UpdateSettings();
}
}
void UImGuiSettings::UnregisterPropertyChangedDelegate()
{
FCoreUObjectDelegates::OnObjectPropertyChanged.RemoveAll(this);
}
void UImGuiSettings::OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent)
{
if (ObjectBeingModified == this)
{
const FName UpdatedPropertyName = PropertyChangedEvent.MemberProperty ? PropertyChangedEvent.MemberProperty->GetFName() : NAME_None;
if (UpdatedPropertyName == GET_MEMBER_NAME_CHECKED(UImGuiSettings, ImGuiInputHandlerClass))
{
OnImGuiInputHandlerClassChanged.Broadcast();
}
else if (UpdatedPropertyName == GET_MEMBER_NAME_CHECKED(UImGuiSettings, bShareKeyboardInput))
{
OnShareKeyboardInputChanged.Broadcast(bShareKeyboardInput);
}
else if (UpdatedPropertyName == GET_MEMBER_NAME_CHECKED(UImGuiSettings, bShareGamepadInput))
{
OnShareGamepadInputChanged.Broadcast(bShareGamepadInput);
}
else if (UpdatedPropertyName == GET_MEMBER_NAME_CHECKED(UImGuiSettings, bUseSoftwareCursor))
{
OnSoftwareCursorChanged.Broadcast();
}
else if (UpdatedPropertyName == GET_MEMBER_NAME_CHECKED(UImGuiSettings, ToggleInput))
{
OnToggleInputKeyChanged.Broadcast();
}
} }
} }

View File

@ -2,18 +2,9 @@
#pragma once #pragma once
#include "ImGuiInputHandler.h"
#include <Delegates/Delegate.h> #include <Delegates/Delegate.h>
#include <UObject/Object.h> #include <UObject/Object.h>
// Select right soft class reference header to avoid warning.
#if ENGINE_COMPATIBILITY_LEGACY_STRING_CLASS_REF
#include <StringClassReference.h>
#else
#include <UObject/SoftObjectPath.h>
#endif
#include "ImGuiSettings.generated.h" #include "ImGuiSettings.generated.h"
@ -40,9 +31,23 @@ struct FImGuiKeyInfo
UPROPERTY(EditAnywhere) UPROPERTY(EditAnywhere)
ECheckBoxState Cmd = ECheckBoxState::Undetermined; ECheckBoxState Cmd = ECheckBoxState::Undetermined;
friend bool operator==(const FImGuiKeyInfo& Lhs, const FImGuiKeyInfo& Rhs)
{
return Lhs.Key == Rhs.Key
&& Lhs.Shift == Rhs.Shift
&& Lhs.Ctrl == Rhs.Ctrl
&& Lhs.Alt == Rhs.Alt
&& Lhs.Cmd == Rhs.Cmd;
}
friend bool operator!=(const FImGuiKeyInfo& Lhs, const FImGuiKeyInfo& Rhs)
{
return !(Lhs == Rhs);
}
}; };
// Settings for ImGui module. // UObject used for loading and saving ImGui settings. To access actual settings use FImGuiModuleSettings interface.
UCLASS(config=ImGui, defaultconfig) UCLASS(config=ImGui, defaultconfig)
class UImGuiSettings : public UObject class UImGuiSettings : public UObject
{ {
@ -50,44 +55,11 @@ class UImGuiSettings : public UObject
public: public:
// Generic delegate used to notify changes of boolean properties. // Get default instance or null if it is not loaded.
DECLARE_MULTICAST_DELEGATE_OneParam(FBoolChangeDelegate, bool); static UImGuiSettings* Get() { return DefaultInstance; }
// Delegate raised when settings instance is loaded. // Delegate raised when default instance is loaded.
static FSimpleMulticastDelegate& OnSettingsLoaded(); static FSimpleMulticastDelegate OnSettingsLoaded;
UImGuiSettings();
~UImGuiSettings();
// Get the path to custom implementation of ImGui Input Handler.
const FStringClassReference& GetImGuiInputHandlerClass() const { return ImGuiInputHandlerClass; }
// Get the keyboard input sharing configuration.
bool ShareKeyboardInput() const { return bShareKeyboardInput; }
// Get the gamepad input sharing configuration.
bool ShareGamepadInput() const { return bShareGamepadInput; }
// Get the software cursor configuration.
bool UseSoftwareCursor() const { return bUseSoftwareCursor; }
// Get the shortcut configuration for 'ImGui.ToggleInput' command.
const FImGuiKeyInfo& GetToggleInputKey() const { return ToggleInput; }
// Delegate raised when ImGui Input Handle is changed.
FSimpleMulticastDelegate OnImGuiInputHandlerClassChanged;
// Delegate raised when keyboard input sharing configuration is changed.
FBoolChangeDelegate OnShareKeyboardInputChanged;
// Delegate raised when gamepad input sharing configuration is changed.
FBoolChangeDelegate OnShareGamepadInputChanged;
// Delegate raised when software cursor configuration is changed.
FSimpleMulticastDelegate OnSoftwareCursorChanged;
// Delegate raised when shortcut configuration for 'ImGui.ToggleInput' command is changed.
FSimpleMulticastDelegate OnToggleInputKeyChanged;
virtual void PostInitProperties() override; virtual void PostInitProperties() override;
virtual void BeginDestroy() override; virtual void BeginDestroy() override;
@ -129,18 +101,73 @@ protected:
UPROPERTY(config) UPROPERTY(config)
FImGuiKeyInfo SwitchInputModeKey_DEPRECATED; FImGuiKeyInfo SwitchInputModeKey_DEPRECATED;
private: static UImGuiSettings* DefaultInstance;
#if WITH_EDITOR friend class FImGuiModuleSettings;
void RegisterPropertyChangedDelegate();
void UnregisterPropertyChangedDelegate();
void OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent);
#endif // WITH_EDITOR
}; };
// Pointer to the settings instance (default class object) assigned right after it is initialized and valid until
// it is destroyed. class FImGuiModuleCommands;
extern UImGuiSettings* GImGuiSettings; class FImGuiModuleProperties;
// Interface for ImGui module settings. It shadows all the settings and keep them in sync after UImGuiSettings class is
// loaded, but it can also work before that time what simplifies workflow in early-loading scenarios.
// It binds to module properties and commands objects that need to be passed during construction.
class FImGuiModuleSettings
{
public:
// Generic delegate used to notify changes of boolean properties.
DECLARE_MULTICAST_DELEGATE_OneParam(FBoolChangeDelegate, bool);
DECLARE_MULTICAST_DELEGATE_OneParam(FStringClassReferenceChangeDelegate, const FStringClassReference&);
// Constructor for ImGui module settings. It will bind to instances of module properties and commands and will
// update them every time when settings are changed.
//
// @param InProperties - Instance of module properties that will be bound and updated by this object.
// @param InCommands - Instance of module commands that will be bound and updated by this object.
FImGuiModuleSettings(FImGuiModuleProperties& InProperties, FImGuiModuleCommands& InCommands);
~FImGuiModuleSettings();
// It doesn't offer interface for settings that define initial values for properties, as those are passed during
// start-up and should be accessed trough properties interface. Remaining settings can have getter and/or change
// event that are defined depending on needs.
// Get the path to custom implementation of ImGui Input Handler.
const FStringClassReference& GetImGuiInputHandlerClass() const { return ImGuiInputHandlerClass; }
// Get the software cursor configuration.
bool UseSoftwareCursor() const { return bUseSoftwareCursor; }
// Get the shortcut configuration for 'ImGui.ToggleInput' command.
const FImGuiKeyInfo& GetToggleInputKey() const { return ToggleInputKey; }
// Delegate raised when ImGui Input Handle is changed.
FStringClassReferenceChangeDelegate OnImGuiInputHandlerClassChanged;
// Delegate raised when software cursor configuration is changed.
FBoolChangeDelegate OnUseSoftwareCursorChanged;
private:
void UpdateSettings();
void SetImGuiInputHandlerClass(const FStringClassReference& ClassReference);
void SetShareKeyboardInput(bool bShare);
void SetShareGamepadInput(bool bShare);
void SetUseSoftwareCursor(bool bUse);
void SetToggleInputKey(const FImGuiKeyInfo& KeyInfo);
#if WITH_EDITOR
void OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent);
#endif // WITH_EDITOR
FImGuiModuleProperties& Properties;
FImGuiModuleCommands& Commands;
FStringClassReference ImGuiInputHandlerClass;
FImGuiKeyInfo ToggleInputKey;
bool bShareKeyboardInput = false;
bool bShareGamepadInput = false;
bool bUseSoftwareCursor = false;
};

View File

@ -102,14 +102,16 @@ void SImGuiWidget::Construct(const FArguments& InArgs)
#endif // IMGUI_WIDGET_DEBUG #endif // IMGUI_WIDGET_DEBUG
ContextProxy->SetInputState(&InputState); ContextProxy->SetInputState(&InputState);
// Cache locally software cursor mode.
UpdateSoftwareCursorMode();
// Create ImGui Input Handler.
CreateInputHandler();
// Register for settings change. // Register for settings change.
RegisterImGuiSettingsDelegates(); RegisterImGuiSettingsDelegates();
const auto& Settings = ModuleManager->GetSettings();
// Cache locally software cursor mode.
SetUseSoftwareCursor(Settings.UseSoftwareCursor());
// Create ImGui Input Handler.
CreateInputHandler(Settings.GetImGuiInputHandlerClass());
} }
END_SLATE_FUNCTION_BUILD_OPTIMIZATION END_SLATE_FUNCTION_BUILD_OPTIMIZATION
@ -390,11 +392,13 @@ FCursorReply SImGuiWidget::OnCursorQuery(const FGeometry& MyGeometry, const FPoi
return FCursorReply::Cursor(MouseCursor); return FCursorReply::Cursor(MouseCursor);
} }
void SImGuiWidget::CreateInputHandler() void SImGuiWidget::CreateInputHandler(const FStringClassReference& HandlerClassReference)
{ {
ReleaseInputHandler();
if (!InputHandler.IsValid()) if (!InputHandler.IsValid())
{ {
InputHandler = FImGuiInputHandlerFactory::NewHandler(ModuleManager, GameViewport.Get(), ContextIndex); InputHandler = FImGuiInputHandlerFactory::NewHandler(HandlerClassReference, ModuleManager, GameViewport.Get(), ContextIndex);
} }
} }
@ -407,40 +411,26 @@ void SImGuiWidget::ReleaseInputHandler()
} }
} }
void SImGuiWidget::RecreateInputHandler()
{
ReleaseInputHandler();
CreateInputHandler();
}
void SImGuiWidget::UpdateSoftwareCursorMode()
{
UImGuiSettings* Settings = GetMutableDefault<UImGuiSettings>();
bUseSoftwareCursor = Settings && Settings->UseSoftwareCursor();
}
void SImGuiWidget::RegisterImGuiSettingsDelegates() void SImGuiWidget::RegisterImGuiSettingsDelegates()
{ {
if (UImGuiSettings* Settings = GetMutableDefault<UImGuiSettings>()) auto& Settings = ModuleManager->GetSettings();
if (!Settings.OnImGuiInputHandlerClassChanged.IsBoundToObject(this))
{ {
if (!Settings->OnImGuiInputHandlerClassChanged.IsBoundToObject(this)) Settings.OnImGuiInputHandlerClassChanged.AddRaw(this, &SImGuiWidget::CreateInputHandler);
{
Settings->OnImGuiInputHandlerClassChanged.AddRaw(this, &SImGuiWidget::RecreateInputHandler);
} }
if (!Settings->OnSoftwareCursorChanged.IsBoundToObject(this)) if (!Settings.OnUseSoftwareCursorChanged.IsBoundToObject(this))
{ {
Settings->OnSoftwareCursorChanged.AddRaw(this, &SImGuiWidget::UpdateSoftwareCursorMode); Settings.OnUseSoftwareCursorChanged.AddRaw(this, &SImGuiWidget::SetUseSoftwareCursor);
}
} }
} }
void SImGuiWidget::UnregisterImGuiSettingsDelegates() void SImGuiWidget::UnregisterImGuiSettingsDelegates()
{ {
if (UImGuiSettings* Settings = GetMutableDefault<UImGuiSettings>()) auto& Settings = ModuleManager->GetSettings();
{
Settings->OnImGuiInputHandlerClassChanged.RemoveAll(this); Settings.OnImGuiInputHandlerClassChanged.RemoveAll(this);
Settings->OnSoftwareCursorChanged.RemoveAll(this); Settings.OnUseSoftwareCursorChanged.RemoveAll(this);
}
} }
FReply SImGuiWidget::WithMouseLockRequests(FReply&& Reply) FReply SImGuiWidget::WithMouseLockRequests(FReply&& Reply)

View File

@ -4,6 +4,7 @@
#include "ImGuiInputState.h" #include "ImGuiInputState.h"
#include "ImGuiModuleDebug.h" #include "ImGuiModuleDebug.h"
#include "ImGuiSettings.h"
#include <Widgets/SLeafWidget.h> #include <Widgets/SLeafWidget.h>
@ -91,11 +92,10 @@ private:
Full Full
}; };
void CreateInputHandler(); void CreateInputHandler(const FStringClassReference& HandlerClassReference);
void ReleaseInputHandler(); void ReleaseInputHandler();
void RecreateInputHandler();
void UpdateSoftwareCursorMode(); void SetUseSoftwareCursor(bool bUse) { bUseSoftwareCursor = bUse; }
void RegisterImGuiSettingsDelegates(); void RegisterImGuiSettingsDelegates();
void UnregisterImGuiSettingsDelegates(); void UnregisterImGuiSettingsDelegates();

View File

@ -64,23 +64,6 @@ public:
private: private:
// Allow private state copying to support hot-reloading but otherwise it is disabled due to non-trivial constructor
// and destructor.
FImGuiModuleProperties();
~FImGuiModuleProperties();
FImGuiModuleProperties(const FImGuiModuleProperties&) = default;
FImGuiModuleProperties& operator=(const FImGuiModuleProperties&) = default;
FImGuiModuleProperties(FImGuiModuleProperties&&) = default;
FImGuiModuleProperties& operator=(FImGuiModuleProperties&&) = default;
void InitializeSettings();
void RegisterSettingsDelegates();
void UnregisterSettingsDelegates();
bool bInputEnabled = false; bool bInputEnabled = false;
bool bKeyboardNavigationEnabled = false; bool bKeyboardNavigationEnabled = false;
@ -90,8 +73,4 @@ private:
bool bGamepadInputShared = false; bool bGamepadInputShared = false;
bool bShowDemo = false; bool bShowDemo = false;
// Allow FImGuiModuleManager and FImGuiModule to create and copy properties.
friend class FImGuiModule;
friend class FImGuiModuleManager;
}; };