mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-02-22 20:20:32 +00:00
Improved hot-reload stability and support for reloading after recompiling outside of the editor:
- Hot-reloading from inside of the editor and after recompiling from the outside should be equally supported and work together. - Improved robustness of the delegates redirections. There should be no lost delegates when reloading (either after in-editor or outside recompilation). - Refactored code to use the same redirection template for context and delegates container.
This commit is contained in:
parent
9eed8f93b9
commit
a76f4bc451
@ -8,6 +8,8 @@ Change History
|
|||||||
Version: 1.21 (2020/07)
|
Version: 1.21 (2020/07)
|
||||||
Improving stability
|
Improving stability
|
||||||
- Fixed a crash in the input handler caused by invalidated by hot-reload instance trying to unregister a delegate.
|
- Fixed a crash in the input handler caused by invalidated by hot-reload instance trying to unregister a delegate.
|
||||||
|
- Improved hot-reload stability and support for reloading after recompiling outside of the editor. Both methods should be equally supported and work together.
|
||||||
|
- Improved behaviour of delegates when hot-reloading.
|
||||||
|
|
||||||
Version: 1.20 (2020/06)
|
Version: 1.20 (2020/06)
|
||||||
Transition to IWYU and maintenance:
|
Transition to IWYU and maintenance:
|
||||||
|
@ -2,25 +2,49 @@
|
|||||||
|
|
||||||
#include "ImGuiDelegatesContainer.h"
|
#include "ImGuiDelegatesContainer.h"
|
||||||
|
|
||||||
|
#include "ImGuiModule.h"
|
||||||
|
#include "Utilities/RedirectingHandle.h"
|
||||||
#include "Utilities/WorldContextIndex.h"
|
#include "Utilities/WorldContextIndex.h"
|
||||||
|
|
||||||
|
|
||||||
FImGuiDelegatesContainer FImGuiDelegatesContainer::DefaultInstance;
|
// Redirecting handle which will automatically bind to another one, if a different instance of the module is loaded.
|
||||||
|
struct FImGuiDelegatesContainerHandle : Utilities::TRedirectingHandle<FImGuiDelegatesContainer>
|
||||||
|
{
|
||||||
|
FImGuiDelegatesContainerHandle(FImGuiDelegatesContainer& InDefaultContainer)
|
||||||
|
: Utilities::TRedirectingHandle<FImGuiDelegatesContainer>(InDefaultContainer)
|
||||||
|
{
|
||||||
|
if (FImGuiModule* Module = FModuleManager::GetModulePtr<FImGuiModule>("ImGui"))
|
||||||
|
{
|
||||||
|
SetParent(&Module->GetDelegatesContainerHandle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
FImGuiDelegatesContainer* FImGuiDelegatesContainer::InstancePtr = &FImGuiDelegatesContainer::DefaultInstance;
|
static FImGuiDelegatesContainer DelegatesContainer;
|
||||||
|
static FImGuiDelegatesContainerHandle DelegatesHandle(DelegatesContainer);
|
||||||
|
|
||||||
void FImGuiDelegatesContainer::MoveContainer(FImGuiDelegatesContainer& Dst)
|
FImGuiDelegatesContainer& FImGuiDelegatesContainer::Get()
|
||||||
|
{
|
||||||
|
return GetHandle().Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
FImGuiDelegatesContainerHandle& FImGuiDelegatesContainer::GetHandle()
|
||||||
|
{
|
||||||
|
return DelegatesHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiDelegatesContainer::MoveContainer(FImGuiDelegatesContainerHandle& OtherContainerHandle)
|
||||||
{
|
{
|
||||||
// Only move data if pointer points to default instance, otherwise our data has already been moved and we only
|
// Only move data if pointer points to default instance, otherwise our data has already been moved and we only
|
||||||
// keep pointer to a more recent version.
|
// keep pointer to a more recent version.
|
||||||
if (InstancePtr == &DefaultInstance)
|
if (GetHandle().IsDefault())
|
||||||
{
|
{
|
||||||
Dst = MoveTemp(DefaultInstance);
|
OtherContainerHandle.Get() = MoveTemp(GetHandle().Get());
|
||||||
DefaultInstance.Clear();
|
GetHandle().Get().Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update pointer to the most recent version.
|
// Update pointer to the most recent version.
|
||||||
InstancePtr = &Dst;
|
GetHandle().SetParent(&OtherContainerHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 FImGuiDelegatesContainer::GetContextIndex(UWorld* World)
|
int32 FImGuiDelegatesContainer::GetContextIndex(UWorld* World)
|
||||||
|
@ -6,15 +6,20 @@
|
|||||||
#include <Delegates/Delegate.h>
|
#include <Delegates/Delegate.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct FImGuiDelegatesContainerHandle;
|
||||||
|
|
||||||
struct FImGuiDelegatesContainer
|
struct FImGuiDelegatesContainer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Get the current instance (can change during hot-reloading).
|
// Get the current instance (can change during hot-reloading).
|
||||||
static FImGuiDelegatesContainer& Get() { return *InstancePtr; }
|
static FImGuiDelegatesContainer& Get();
|
||||||
|
|
||||||
// If this is an active container move its data to a destination and redirect all future calls to that instance.
|
// Get the handle to the container instance (can attach to other handles in hot-reloaded modules).
|
||||||
static void MoveContainer(FImGuiDelegatesContainer& Dst);
|
static FImGuiDelegatesContainerHandle& GetHandle();
|
||||||
|
|
||||||
|
// Redirect to the other container and if this one is still active move its data to the other one.
|
||||||
|
static void MoveContainer(FImGuiDelegatesContainerHandle& OtherContainerHandle);
|
||||||
|
|
||||||
// Get delegate to ImGui world early debug event from known world instance.
|
// Get delegate to ImGui world early debug event from known world instance.
|
||||||
FSimpleMulticastDelegate& OnWorldEarlyDebug(UWorld* World) { return OnWorldEarlyDebug(GetContextIndex(World)); }
|
FSimpleMulticastDelegate& OnWorldEarlyDebug(UWorld* World) { return OnWorldEarlyDebug(GetContextIndex(World)); }
|
||||||
@ -44,10 +49,4 @@ private:
|
|||||||
TMap<int32, FSimpleMulticastDelegate> WorldDebugDelegates;
|
TMap<int32, FSimpleMulticastDelegate> WorldDebugDelegates;
|
||||||
FSimpleMulticastDelegate MultiContextEarlyDebugDelegate;
|
FSimpleMulticastDelegate MultiContextEarlyDebugDelegate;
|
||||||
FSimpleMulticastDelegate MultiContextDebugDelegate;
|
FSimpleMulticastDelegate MultiContextDebugDelegate;
|
||||||
|
|
||||||
// Default container instance.
|
|
||||||
static FImGuiDelegatesContainer DefaultInstance;
|
|
||||||
|
|
||||||
// Pointer to the container instance that can be overwritten during hot-reloading.
|
|
||||||
static FImGuiDelegatesContainer* InstancePtr;
|
|
||||||
};
|
};
|
||||||
|
@ -18,12 +18,28 @@
|
|||||||
#endif // PLATFORM_WINDOWS
|
#endif // PLATFORM_WINDOWS
|
||||||
|
|
||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
// Global ImGui context pointer.
|
|
||||||
ImGuiContext* GImGuiContextPtr = nullptr;
|
#include "ImGuiModule.h"
|
||||||
// Handle to the global ImGui context pointer.
|
#include "Utilities/RedirectingHandle.h"
|
||||||
ImGuiContext** GImGuiContextPtrHandle = &GImGuiContextPtr;
|
|
||||||
|
// Redirecting handle which will automatically bind to another one, if a different instance of the module is loaded.
|
||||||
|
struct FImGuiContextHandle : public Utilities::TRedirectingHandle<ImGuiContext*>
|
||||||
|
{
|
||||||
|
FImGuiContextHandle(ImGuiContext*& InDefaultContext)
|
||||||
|
: Utilities::TRedirectingHandle<ImGuiContext*>(InDefaultContext)
|
||||||
|
{
|
||||||
|
if (FImGuiModule* Module = FModuleManager::GetModulePtr<FImGuiModule>("ImGui"))
|
||||||
|
{
|
||||||
|
SetParent(&Module->GetImGuiContextHandle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static ImGuiContext* ImGuiContextPtr = nullptr;
|
||||||
|
static FImGuiContextHandle ImGuiContextPtrHandle(ImGuiContextPtr);
|
||||||
|
|
||||||
// Get the global ImGui context pointer (GImGui) indirectly to allow redirections in obsolete modules.
|
// Get the global ImGui context pointer (GImGui) indirectly to allow redirections in obsolete modules.
|
||||||
#define GImGui (*GImGuiContextPtrHandle)
|
#define GImGui (ImGuiContextPtrHandle.Get())
|
||||||
#endif // WITH_EDITOR
|
#endif // WITH_EDITOR
|
||||||
|
|
||||||
#include "imgui.cpp"
|
#include "imgui.cpp"
|
||||||
@ -41,14 +57,14 @@ ImGuiContext** GImGuiContextPtrHandle = &GImGuiContextPtr;
|
|||||||
namespace ImGuiImplementation
|
namespace ImGuiImplementation
|
||||||
{
|
{
|
||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
ImGuiContext** GetImGuiContextHandle()
|
FImGuiContextHandle& GetContextHandle()
|
||||||
{
|
{
|
||||||
return GImGuiContextPtrHandle;
|
return ImGuiContextPtrHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetImGuiContextHandle(ImGuiContext** Handle)
|
void SetParentContextHandle(FImGuiContextHandle& Parent)
|
||||||
{
|
{
|
||||||
GImGuiContextPtrHandle = Handle;
|
ImGuiContextPtrHandle.SetParent(&Parent);
|
||||||
}
|
}
|
||||||
#endif // WITH_EDITOR
|
#endif // WITH_EDITOR
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,16 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
struct ImGuiContext;
|
struct FImGuiContextHandle;
|
||||||
|
|
||||||
// Gives access to selected ImGui implementation features.
|
// Gives access to selected ImGui implementation features.
|
||||||
namespace ImGuiImplementation
|
namespace ImGuiImplementation
|
||||||
{
|
{
|
||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
// Get the handle to the ImGui Context pointer.
|
// Get the handle to the ImGui Context pointer.
|
||||||
ImGuiContext** GetImGuiContextHandle();
|
FImGuiContextHandle& GetContextHandle();
|
||||||
|
|
||||||
// Set the ImGui Context pointer handle.
|
// Set the ImGui Context pointer handle.
|
||||||
void SetImGuiContextHandle(ImGuiContext** Handle);
|
void SetParentContextHandle(FImGuiContextHandle& Parent);
|
||||||
#endif // WITH_EDITOR
|
#endif // WITH_EDITOR
|
||||||
}
|
}
|
||||||
|
@ -149,9 +149,9 @@ void FImGuiModule::ShutdownModule()
|
|||||||
if (&LoadedModule != this)
|
if (&LoadedModule != this)
|
||||||
{
|
{
|
||||||
// Statically bound functions will be still made to the obsolete module so we need to
|
// Statically bound functions will be still made to the obsolete module so we need to
|
||||||
ImGuiImplementation::SetImGuiContextHandle(LoadedModule.GetImGuiContextHandle());
|
ImGuiImplementation::SetParentContextHandle(LoadedModule.GetImGuiContextHandle());
|
||||||
|
|
||||||
FImGuiDelegatesContainer::MoveContainer(LoadedModule.GetDelegatesContainer());
|
FImGuiDelegatesContainer::MoveContainer(LoadedModule.GetDelegatesContainerHandle());
|
||||||
|
|
||||||
if (bMoveProperties)
|
if (bMoveProperties)
|
||||||
{
|
{
|
||||||
@ -170,14 +170,14 @@ void FImGuiModule::SetProperties(const FImGuiModuleProperties& Properties)
|
|||||||
ImGuiModuleManager->GetProperties() = Properties;
|
ImGuiModuleManager->GetProperties() = Properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGuiContext** FImGuiModule::GetImGuiContextHandle()
|
FImGuiContextHandle& FImGuiModule::GetImGuiContextHandle()
|
||||||
{
|
{
|
||||||
return ImGuiImplementation::GetImGuiContextHandle();
|
return ImGuiImplementation::GetContextHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
FImGuiDelegatesContainer& FImGuiModule::GetDelegatesContainer()
|
FImGuiDelegatesContainerHandle& FImGuiModule::GetDelegatesContainerHandle()
|
||||||
{
|
{
|
||||||
return FImGuiDelegatesContainer::Get();
|
return FImGuiDelegatesContainer::GetHandle();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
96
Source/ImGui/Private/Utilities/RedirectingHandle.h
Normal file
96
Source/ImGui/Private/Utilities/RedirectingHandle.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Delegates/Delegate.h>
|
||||||
|
#include <Delegates/DelegateCombinations.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Utilities
|
||||||
|
{
|
||||||
|
// Handle initialized as a pointer to a default value, but once attached it will follow the parent handle.
|
||||||
|
// When detached it will revert back to the default value. Intended for cross-module redirections.
|
||||||
|
template<typename T>
|
||||||
|
struct TRedirectingHandle
|
||||||
|
{
|
||||||
|
TRedirectingHandle(T& InDefaultValue)
|
||||||
|
: Handle(&InDefaultValue)
|
||||||
|
, DefaultHandle(&InDefaultValue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~TRedirectingHandle()
|
||||||
|
{
|
||||||
|
// Broadcast null pointer as a request to detach.
|
||||||
|
OnRedirectionUpdate.Broadcast(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether this handle points to the default value.
|
||||||
|
bool IsDefault() const
|
||||||
|
{
|
||||||
|
return (Handle == DefaultHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the current value.
|
||||||
|
T& Get() const
|
||||||
|
{
|
||||||
|
return *Handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the other handle as a parent to this one. Passing null or itself will result with detaching from
|
||||||
|
// the current parent (if any) and reverting back to the default value.
|
||||||
|
void SetParent(TRedirectingHandle* InParent)
|
||||||
|
{
|
||||||
|
if (InParent != Parent)
|
||||||
|
{
|
||||||
|
if (Parent)
|
||||||
|
{
|
||||||
|
Parent->OnRedirectionUpdate.RemoveAll(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protecting from setting itself as a parent.
|
||||||
|
Parent = (Parent != this) ? InParent : nullptr;
|
||||||
|
|
||||||
|
if (Parent)
|
||||||
|
{
|
||||||
|
Parent->OnRedirectionUpdate.AddRaw(this, &TRedirectingHandle::UpdateRedirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetHandle((Parent) ? Parent->Handle : DefaultHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
void UpdateRedirection(T* InHandle)
|
||||||
|
{
|
||||||
|
if (InHandle)
|
||||||
|
{
|
||||||
|
SetHandle(InHandle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Interpreting null as a signal to detach.
|
||||||
|
SetParent(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetHandle(T* InHandle)
|
||||||
|
{
|
||||||
|
if (InHandle != Handle)
|
||||||
|
{
|
||||||
|
Handle = InHandle;
|
||||||
|
OnRedirectionUpdate.Broadcast(Handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T* Handle;
|
||||||
|
T* DefaultHandle;
|
||||||
|
|
||||||
|
TRedirectingHandle* Parent = nullptr;
|
||||||
|
|
||||||
|
// Event with a new handle value or null pointer as a signal to detach.
|
||||||
|
DECLARE_MULTICAST_DELEGATE_OneParam(FRedirectionUpdateDelegate, T*);
|
||||||
|
FRedirectionUpdateDelegate OnRedirectionUpdate;
|
||||||
|
};
|
||||||
|
}
|
@ -164,11 +164,13 @@ public:
|
|||||||
virtual void StartupModule() override;
|
virtual void StartupModule() override;
|
||||||
virtual void ShutdownModule() override;
|
virtual void ShutdownModule() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
|
friend struct FImGuiContextHandle;
|
||||||
|
friend struct FImGuiDelegatesContainerHandle;
|
||||||
virtual void SetProperties(const FImGuiModuleProperties& Properties);
|
virtual void SetProperties(const FImGuiModuleProperties& Properties);
|
||||||
virtual struct ImGuiContext** GetImGuiContextHandle();
|
virtual FImGuiContextHandle& GetImGuiContextHandle();
|
||||||
virtual struct FImGuiDelegatesContainer& GetDelegatesContainer();
|
virtual FImGuiDelegatesContainerHandle& GetDelegatesContainerHandle();
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user