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()
{
// 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())
{
@ -59,7 +59,7 @@ void FImGuiEditor::Register()
SettingsModule->RegisterSettings(SETTINGS_CONTAINER,
LOCTEXT("ImGuiSettingsName", "ImGui"),
LOCTEXT("ImGuiSettingsDescription", "Configure the Unreal ImGui plugin."),
GImGuiSettings);
UImGuiSettings::Get());
}
}

View File

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

View File

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

View File

@ -92,9 +92,10 @@ namespace
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.Alt, KeyEvent.IsAltDown())
&& IsMatching(KeyInfo.Cmd, KeyEvent.IsCommandDown());
@ -103,9 +104,7 @@ namespace
bool UImGuiInputHandler::IsToggleInputEvent(const FKeyEvent& KeyEvent) const
{
return GImGuiSettings
&& (KeyEvent.GetKey() == GImGuiSettings->GetToggleInputKey().Key)
&& AreModifiersMatching(GImGuiSettings->GetToggleInputKey(), KeyEvent);
return IsMatchingEvent(KeyEvent, ModuleManager->GetSettings().GetToggleInputKey());
}
bool UImGuiInputHandler::HasImGuiActiveItem() const

View File

@ -5,23 +5,18 @@
#include "ImGuiInputHandlerFactory.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;
if (GImGuiSettings)
if (HandlerClassReference.IsValid())
{
const auto& HandlerClassReference = GImGuiSettings->GetImGuiInputHandlerClass();
if (HandlerClassReference.IsValid())
{
HandlerClass = HandlerClassReference.TryLoadClass<UImGuiInputHandler>();
HandlerClass = HandlerClassReference.TryLoadClass<UImGuiInputHandler>();
if (!HandlerClass)
{
UE_LOG(LogImGuiInputHandler, Error, TEXT("Couldn't load ImGui Input Handler class '%s'."), *HandlerClassReference.ToString());
}
if (!HandlerClass)
{
UE_LOG(LogImGuiInputHandler, Error, TEXT("Couldn't load ImGui Input Handler class '%s'."), *HandlerClassReference.ToString());
}
}

View File

@ -10,7 +10,7 @@ class FImGuiInputHandlerFactory
{
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);
};

View File

@ -4,118 +4,70 @@
#include "ImGuiModuleCommands.h"
#include "ImGuiModuleManager.h"
#include "ImGuiSettings.h"
#include "Utilities/DebugExecBindings.h"
namespace CommandNames
{
namespace
{
const TCHAR* ToggleInput = TEXT("ImGui.ToggleInput");
const TCHAR* ToggleKeyboardNavigation = TEXT("ImGui.ToggleKeyboardNavigation");
const TCHAR* ToggleGamepadNavigation = TEXT("ImGui.ToggleGamepadNavigation");
const TCHAR* ToggleKeyboardInputSharing = TEXT("ImGui.ToggleKeyboardInputSharing");
const TCHAR* ToggleGamepadInputSharing = TEXT("ImGui.ToggleGamepadInputSharing");
const TCHAR* ToggleDemo = TEXT("ImGui.ToggleDemo");
}
}
const TCHAR* const FImGuiModuleCommands::ToggleInput = TEXT("ImGui.ToggleInput");
const TCHAR* const FImGuiModuleCommands::ToggleKeyboardNavigation = TEXT("ImGui.ToggleKeyboardNavigation");
const TCHAR* const FImGuiModuleCommands::ToggleGamepadNavigation = TEXT("ImGui.ToggleGamepadNavigation");
const TCHAR* const FImGuiModuleCommands::ToggleKeyboardInputSharing = TEXT("ImGui.ToggleKeyboardInputSharing");
const TCHAR* const FImGuiModuleCommands::ToggleGamepadInputSharing = TEXT("ImGui.ToggleGamepadInputSharing");
const TCHAR* const FImGuiModuleCommands::ToggleDemo = TEXT("ImGui.ToggleDemo");
FImGuiModuleCommands::FImGuiModuleCommands(FImGuiModuleManager& InModuleManager)
: ModuleManager(InModuleManager)
, ToggleInputCommand(CommandNames::ToggleInput,
FImGuiModuleCommands::FImGuiModuleCommands(FImGuiModuleProperties& InProperties)
: Properties(InProperties)
, ToggleInputCommand(ToggleInput,
TEXT("Toggle ImGui input mode."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleInput))
, ToggleKeyboardNavigationCommand(CommandNames::ToggleKeyboardNavigation,
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleInputImpl))
, ToggleKeyboardNavigationCommand(ToggleKeyboardNavigation,
TEXT("Toggle ImGui keyboard navigation."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardNavigation))
, ToggleGamepadNavigationCommand(CommandNames::ToggleGamepadNavigation,
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardNavigationImpl))
, ToggleGamepadNavigationCommand(ToggleGamepadNavigation,
TEXT("Toggle ImGui gamepad navigation."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadNavigation))
, ToggleKeyboardInputSharingCommand(CommandNames::ToggleKeyboardInputSharing,
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadNavigationImpl))
, ToggleKeyboardInputSharingCommand(ToggleKeyboardInputSharing,
TEXT("Toggle ImGui keyboard input sharing."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardInputSharing))
, ToggleGamepadInputSharingCommand(CommandNames::ToggleGamepadInputSharing,
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleKeyboardInputSharingImpl))
, ToggleGamepadInputSharingCommand(ToggleGamepadInputSharing,
TEXT("Toggle ImGui gamepad input sharing."),
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadInputSharing))
, ToggleDemoCommand(CommandNames::ToggleDemo,
FConsoleCommandDelegate::CreateRaw(this, &FImGuiModuleCommands::ToggleGamepadInputSharingImpl))
, ToggleDemoCommand(ToggleDemo,
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);
UnregisterSettingsDelegates();
DebugExecBindings::UpdatePlayerInputs(KeyInfo, CommandName);
}
void FImGuiModuleCommands::InitializeSettings()
void FImGuiModuleCommands::ToggleInputImpl()
{
RegisterSettingsDelegates();
// 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();
Properties.ToggleInput();
}
void FImGuiModuleCommands::RegisterSettingsDelegates()
void FImGuiModuleCommands::ToggleKeyboardNavigationImpl()
{
if (GImGuiSettings && !GImGuiSettings->OnToggleInputKeyChanged.IsBoundToObject(this))
{
GImGuiSettings->OnToggleInputKeyChanged.AddRaw(this, &FImGuiModuleCommands::UpdateToggleInputKeyBinding);
}
Properties.ToggleKeyboardNavigation();
}
void FImGuiModuleCommands::UnregisterSettingsDelegates()
void FImGuiModuleCommands::ToggleGamepadNavigationImpl()
{
if (GImGuiSettings)
{
GImGuiSettings->OnToggleInputKeyChanged.RemoveAll(this);
}
Properties.ToggleGamepadNavigation();
}
void FImGuiModuleCommands::UpdateToggleInputKeyBinding()
void FImGuiModuleCommands::ToggleKeyboardInputSharingImpl()
{
if (GImGuiSettings)
{
DebugExecBindings::UpdatePlayerInputs(GImGuiSettings->GetToggleInputKey(), CommandNames::ToggleInput);
}
Properties.ToggleKeyboardInputSharing();
}
void FImGuiModuleCommands::ToggleInput()
void FImGuiModuleCommands::ToggleGamepadInputSharingImpl()
{
ModuleManager.GetProperties().ToggleInput();
Properties.ToggleGamepadInputSharing();
}
void FImGuiModuleCommands::ToggleKeyboardNavigation()
void FImGuiModuleCommands::ToggleDemoImpl()
{
ModuleManager.GetProperties().ToggleKeyboardNavigation();
}
void FImGuiModuleCommands::ToggleGamepadNavigation()
{
ModuleManager.GetProperties().ToggleGamepadNavigation();
}
void FImGuiModuleCommands::ToggleKeyboardInputSharing()
{
ModuleManager.GetProperties().ToggleKeyboardInputSharing();
}
void FImGuiModuleCommands::ToggleGamepadInputSharing()
{
ModuleManager.GetProperties().ToggleGamepadInputSharing();
}
void FImGuiModuleCommands::ToggleDemo()
{
ModuleManager.GetProperties().ToggleDemo();
Properties.ToggleDemo();
}

View File

@ -5,39 +5,35 @@
#include <IConsoleManager.h>
class FImGuiModuleManager;
struct FImGuiKeyInfo;
class FImGuiModuleProperties;
// Manges ImGui module console commands.
class FImGuiModuleCommands
{
public:
FImGuiModuleCommands(FImGuiModuleManager& InModuleManager);
~FImGuiModuleCommands();
static const TCHAR* const ToggleInput;
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:
FImGuiModuleCommands(const FImGuiModuleCommands&) = delete;
FImGuiModuleCommands& operator=(const FImGuiModuleCommands&) = delete;
void ToggleInputImpl();
void ToggleKeyboardNavigationImpl();
void ToggleGamepadNavigationImpl();
void ToggleKeyboardInputSharingImpl();
void ToggleGamepadInputSharingImpl();
void ToggleDemoImpl();
FImGuiModuleCommands(FImGuiModuleCommands&&) = delete;
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;
FImGuiModuleProperties& Properties;
FAutoConsoleCommand ToggleInputCommand;
FAutoConsoleCommand ToggleKeyboardNavigationCommand;

View File

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

View File

@ -6,6 +6,7 @@
#include "ImGuiDemo.h"
#include "ImGuiModuleCommands.h"
#include "ImGuiModuleProperties.h"
#include "ImGuiSettings.h"
#include "SImGuiWidget.h"
#include "TextureManager.h"
@ -18,6 +19,9 @@ class FImGuiModuleManager
public:
// Get interface to module settings.
FImGuiModuleSettings& GetSettings() { return Settings; }
// Get interface to module state properties.
FImGuiModuleProperties& GetProperties() { return Properties; }
@ -68,7 +72,10 @@ private:
FImGuiModuleProperties Properties;
// 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.
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 <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
// 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"
UImGuiSettings* GImGuiSettings = nullptr;
FSimpleMulticastDelegate& UImGuiSettings::OnSettingsLoaded()
{
static FSimpleMulticastDelegate Instance;
return Instance;
}
#include "ImGuiModuleCommands.h"
#include "ImGuiModuleProperties.h"
UImGuiSettings::UImGuiSettings()
{
#if WITH_EDITOR
RegisterPropertyChangedDelegate();
#endif
}
//====================================================================================================
// UImGuiSettings
//====================================================================================================
UImGuiSettings::~UImGuiSettings()
{
#if WITH_EDITOR
UnregisterPropertyChangedDelegate();
#endif
}
UImGuiSettings* UImGuiSettings::DefaultInstance = nullptr;
FSimpleMulticastDelegate UImGuiSettings::OnSettingsLoaded;
void UImGuiSettings::PostInitProperties()
{
@ -60,8 +48,8 @@ void UImGuiSettings::PostInitProperties()
if (IsTemplate())
{
GImGuiSettings = this;
OnSettingsLoaded().Broadcast();
DefaultInstance = this;
OnSettingsLoaded.Broadcast();
}
}
@ -69,53 +57,106 @@ void UImGuiSettings::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
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);
}
}
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();
}
UpdateSettings();
}
}

View File

@ -2,18 +2,9 @@
#pragma once
#include "ImGuiInputHandler.h"
#include <Delegates/Delegate.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"
@ -40,9 +31,23 @@ struct FImGuiKeyInfo
UPROPERTY(EditAnywhere)
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)
class UImGuiSettings : public UObject
{
@ -50,44 +55,11 @@ class UImGuiSettings : public UObject
public:
// Generic delegate used to notify changes of boolean properties.
DECLARE_MULTICAST_DELEGATE_OneParam(FBoolChangeDelegate, bool);
// Get default instance or null if it is not loaded.
static UImGuiSettings* Get() { return DefaultInstance; }
// Delegate raised when settings instance is loaded.
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;
// Delegate raised when default instance is loaded.
static FSimpleMulticastDelegate OnSettingsLoaded;
virtual void PostInitProperties() override;
virtual void BeginDestroy() override;
@ -129,18 +101,73 @@ protected:
UPROPERTY(config)
FImGuiKeyInfo SwitchInputModeKey_DEPRECATED;
private:
static UImGuiSettings* DefaultInstance;
#if WITH_EDITOR
void RegisterPropertyChangedDelegate();
void UnregisterPropertyChangedDelegate();
void OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent);
#endif // WITH_EDITOR
friend class FImGuiModuleSettings;
};
// Pointer to the settings instance (default class object) assigned right after it is initialized and valid until
// it is destroyed.
extern UImGuiSettings* GImGuiSettings;
class FImGuiModuleCommands;
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
ContextProxy->SetInputState(&InputState);
// Cache locally software cursor mode.
UpdateSoftwareCursorMode();
// Create ImGui Input Handler.
CreateInputHandler();
// Register for settings change.
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
@ -390,11 +392,13 @@ FCursorReply SImGuiWidget::OnCursorQuery(const FGeometry& MyGeometry, const FPoi
return FCursorReply::Cursor(MouseCursor);
}
void SImGuiWidget::CreateInputHandler()
void SImGuiWidget::CreateInputHandler(const FStringClassReference& HandlerClassReference)
{
ReleaseInputHandler();
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()
{
if (UImGuiSettings* Settings = GetMutableDefault<UImGuiSettings>())
auto& Settings = ModuleManager->GetSettings();
if (!Settings.OnImGuiInputHandlerClassChanged.IsBoundToObject(this))
{
if (!Settings->OnImGuiInputHandlerClassChanged.IsBoundToObject(this))
{
Settings->OnImGuiInputHandlerClassChanged.AddRaw(this, &SImGuiWidget::RecreateInputHandler);
}
if (!Settings->OnSoftwareCursorChanged.IsBoundToObject(this))
{
Settings->OnSoftwareCursorChanged.AddRaw(this, &SImGuiWidget::UpdateSoftwareCursorMode);
}
Settings.OnImGuiInputHandlerClassChanged.AddRaw(this, &SImGuiWidget::CreateInputHandler);
}
if (!Settings.OnUseSoftwareCursorChanged.IsBoundToObject(this))
{
Settings.OnUseSoftwareCursorChanged.AddRaw(this, &SImGuiWidget::SetUseSoftwareCursor);
}
}
void SImGuiWidget::UnregisterImGuiSettingsDelegates()
{
if (UImGuiSettings* Settings = GetMutableDefault<UImGuiSettings>())
{
Settings->OnImGuiInputHandlerClassChanged.RemoveAll(this);
Settings->OnSoftwareCursorChanged.RemoveAll(this);
}
auto& Settings = ModuleManager->GetSettings();
Settings.OnImGuiInputHandlerClassChanged.RemoveAll(this);
Settings.OnUseSoftwareCursorChanged.RemoveAll(this);
}
FReply SImGuiWidget::WithMouseLockRequests(FReply&& Reply)

View File

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

View File

@ -64,23 +64,6 @@ public:
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 bKeyboardNavigationEnabled = false;
@ -90,8 +73,4 @@ private:
bool bGamepadInputShared = false;
bool bShowDemo = false;
// Allow FImGuiModuleManager and FImGuiModule to create and copy properties.
friend class FImGuiModule;
friend class FImGuiModuleManager;
};