diff --git a/Source/ImGui/Private/ImGuiContextManager.cpp b/Source/ImGui/Private/ImGuiContextManager.cpp index e2f9b1e..fded091 100644 --- a/Source/ImGui/Private/ImGuiContextManager.cpp +++ b/Source/ImGui/Private/ImGuiContextManager.cpp @@ -6,6 +6,7 @@ #include "ImGuiDelegatesContainer.h" #include "ImGuiImplementation.h" +#include "ImGuiModuleSettings.h" #include "Utilities/ScopeGuards.h" #include "Utilities/WorldContext.h" #include "Utilities/WorldContextIndex.h" @@ -59,11 +60,13 @@ namespace #endif // WITH_EDITOR } -FImGuiContextManager::FImGuiContextManager() +FImGuiContextManager::FImGuiContextManager(FImGuiModuleSettings& InSettings) + : Settings(InSettings) { - unsigned char* Pixels; - int Width, Height, Bpp; - FontAtlas.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp); + Settings.OnDPIScaleChangedDelegate.AddRaw(this, &FImGuiContextManager::SetDPIScale); + + SetDPIScale(Settings.GetDPIScaleInfo()); + BuildFontAtlas(); FWorldDelegates::OnWorldTickStart.AddRaw(this, &FImGuiContextManager::OnWorldTickStart); #if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK @@ -73,6 +76,8 @@ FImGuiContextManager::FImGuiContextManager() FImGuiContextManager::~FImGuiContextManager() { + Settings.OnDPIScaleChangedDelegate.RemoveAll(this); + // Order matters because contexts can be created during World Tick Start events. FWorldDelegates::OnWorldTickStart.RemoveAll(this); #if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK @@ -98,6 +103,13 @@ void FImGuiContextManager::Tick(float DeltaSeconds) FImGuiDelegatesContainer::Get().OnWorldDebug(Pair.Key).Clear(); } } + + // Once all context tick they should use new fonts and we can release the old resources. Extra countdown is added + // wait for contexts that ticked outside of this function, before rebuilding fonts. + if (FontResourcesReleaseCountdown > 0 && !--FontResourcesReleaseCountdown) + { + FontResourcesToRelease.Empty(); + } } #if ENGINE_COMPATIBILITY_LEGACY_WORLD_ACTOR_TICK @@ -142,8 +154,8 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetEditorContextData() if (UNLIKELY(!Data)) { - Data = &Contexts.Emplace(Utilities::EDITOR_CONTEXT_INDEX, FContextData{ GetEditorContextName(), Utilities::EDITOR_CONTEXT_INDEX, DrawMultiContextEvent, FontAtlas, -1 }); - ContextProxyCreatedEvent.Broadcast(Utilities::EDITOR_CONTEXT_INDEX, *Data->ContextProxy); + Data = &Contexts.Emplace(Utilities::EDITOR_CONTEXT_INDEX, FContextData{ GetEditorContextName(), Utilities::EDITOR_CONTEXT_INDEX, OnDrawMultiContext, FontAtlas, -1 }); + OnContextProxyCreated.Broadcast(Utilities::EDITOR_CONTEXT_INDEX, *Data->ContextProxy); } return *Data; @@ -199,8 +211,8 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetWorldContextData(co #if WITH_EDITOR if (UNLIKELY(!Data)) { - Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), Index, DrawMultiContextEvent, FontAtlas, WorldContext->PIEInstance }); - ContextProxyCreatedEvent.Broadcast(Index, *Data->ContextProxy); + Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), Index, OnDrawMultiContext, FontAtlas, WorldContext->PIEInstance }); + OnContextProxyCreated.Broadcast(Index, *Data->ContextProxy); } else { @@ -221,3 +233,95 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetWorldContextData(co } return *Data; } + +struct FGuardContext +{ + FGuardContext() + : OldContext(ImGui::GetCurrentContext()) + { + } + + ~FGuardContext() + { + if (bRestore) + { + ImGui::SetCurrentContext(OldContext); + } + } + + FGuardContext(FGuardContext&& Other) + : OldContext(MoveTemp(Other.OldContext)) + { + Other.bRestore = false; + } + + FGuardContext& operator=(FGuardContext&&) = delete; + + FGuardContext(const FGuardContext&) = delete; + FGuardContext& operator=(const FGuardContext&) = delete; + +private: + + ImGuiContext* OldContext = nullptr; + bool bRestore = true; +}; + +void FImGuiContextManager::SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo) +{ + const float Scale = ScaleInfo.GetImGuiScale(); + if (DPIScale != Scale) + { + DPIScale = Scale; + + // Only rebuild font atlas if it is already built. Otherwise allow the other logic to pick a moment. + if (FontAtlas.IsBuilt()) + { + RebuildFontAtlas(); + } + + for (auto& Pair : Contexts) + { + if (Pair.Value.ContextProxy) + { + ImGuiStyle NewStyle = ImGuiStyle(); + NewStyle.ScaleAllSizes(DPIScale); + + FGuardContext GuardContext; + Pair.Value.ContextProxy->SetAsCurrent(); + ImGui::GetStyle() = MoveTemp(NewStyle); + } + } + } +} + +void FImGuiContextManager::BuildFontAtlas() +{ + if (!FontAtlas.IsBuilt()) + { + ImFontConfig FontConfig = {}; + FontConfig.SizePixels = FMath::RoundFromZero(13.f * DPIScale); + FontAtlas.AddFontDefault(&FontConfig); + + unsigned char* Pixels; + int Width, Height, Bpp; + FontAtlas.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp); + + OnFontAtlasBuilt.Broadcast(); + } +} + +void FImGuiContextManager::RebuildFontAtlas() +{ + if (FontAtlas.IsBuilt()) + { + // Keep the old resources alive for a few frames to give all contexts a chance to bind to new ones. + FontResourcesToRelease.Add(TUniquePtr(new ImFontAtlas())); + Swap(*FontResourcesToRelease.Last(), FontAtlas); + + // Typically, one frame should be enough but since we allow for custom ticking, we need at least to frames to + // wait for contexts that already ticked and will not do that before the end of the next tick of this manager. + FontResourcesReleaseCountdown = 3; + } + + BuildFontAtlas(); +} diff --git a/Source/ImGui/Private/ImGuiContextManager.h b/Source/ImGui/Private/ImGuiContextManager.h index e9627d4..e08c324 100644 --- a/Source/ImGui/Private/ImGuiContextManager.h +++ b/Source/ImGui/Private/ImGuiContextManager.h @@ -5,6 +5,8 @@ #include "ImGuiContextProxy.h" +class FImGuiModuleSettings; + // TODO: It might be useful to broadcast FContextProxyCreatedDelegate to users, to support similar cases to our ImGui // demo, but we would need to remove from that interface internal classes. @@ -18,7 +20,7 @@ class FImGuiContextManager { public: - FImGuiContextManager(); + FImGuiContextManager(FImGuiModuleSettings& InSettings); FImGuiContextManager(const FImGuiContextManager&) = delete; FImGuiContextManager& operator=(const FImGuiContextManager&) = delete; @@ -31,7 +33,6 @@ public: ImFontAtlas& GetFontAtlas() { return FontAtlas; } const ImFontAtlas& GetFontAtlas() const { return FontAtlas; } - #if WITH_EDITOR // Get or create editor ImGui context proxy. FORCEINLINE FImGuiContextProxy& GetEditorContextProxy() { return *GetEditorContextData().ContextProxy; } @@ -57,10 +58,13 @@ public: // Delegate called for all contexts in manager, right after calling context specific draw event. Allows listeners // draw the same content to multiple contexts. - FSimpleMulticastDelegate& OnDrawMultiContext() { return DrawMultiContextEvent; } + FSimpleMulticastDelegate OnDrawMultiContext; - // Delegate called when new context proxy is created. - FContextProxyCreatedDelegate& OnContextProxyCreated() { return ContextProxyCreatedEvent; } + // Delegate called when a new context proxy is created. + FContextProxyCreatedDelegate OnContextProxyCreated; + + // Delegate called after font atlas is built. + FSimpleMulticastDelegate OnFontAtlasBuilt; void Tick(float DeltaSeconds); @@ -99,11 +103,17 @@ private: FContextData& GetWorldContextData(const UWorld& World, int32* OutContextIndex = nullptr); + void SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo); + void BuildFontAtlas(); + void RebuildFontAtlas(); + TMap Contexts; - FSimpleMulticastDelegate DrawMultiContextEvent; - - FContextProxyCreatedDelegate ContextProxyCreatedEvent; - ImFontAtlas FontAtlas; + TArray> FontResourcesToRelease; + + FImGuiModuleSettings& Settings; + + float DPIScale = -1.f; + int32 FontResourcesReleaseCountdown = 0; }; diff --git a/Source/ImGui/Private/ImGuiModule.cpp b/Source/ImGui/Private/ImGuiModule.cpp index caa0c79..6711161 100644 --- a/Source/ImGui/Private/ImGuiModule.cpp +++ b/Source/ImGui/Private/ImGuiModule.cpp @@ -95,7 +95,7 @@ FImGuiDelegateHandle FImGuiModule::AddMultiContextImGuiDelegate(const FImGuiDele #else checkf(ImGuiModuleManager, TEXT("Null pointer to internal module implementation. Is module available?")); - return { ImGuiModuleManager->GetContextManager().OnDrawMultiContext().Add(Delegate), EDelegateCategory::MultiContext }; + return { ImGuiModuleManager->GetContextManager().OnDrawMultiContext.Add(Delegate), EDelegateCategory::MultiContext }; #endif } @@ -115,7 +115,7 @@ void FImGuiModule::RemoveImGuiDelegate(const FImGuiDelegateHandle& Handle) { if (Handle.Category == EDelegateCategory::MultiContext) { - ImGuiModuleManager->GetContextManager().OnDrawMultiContext().Remove(Handle.Handle); + ImGuiModuleManager->GetContextManager().OnDrawMultiContext.Remove(Handle.Handle); } else if (auto* Proxy = ImGuiModuleManager->GetContextManager().GetContextProxy(Handle.Index)) { @@ -135,7 +135,13 @@ FImGuiTextureHandle FImGuiModule::FindTextureHandle(const FName& Name) FImGuiTextureHandle FImGuiModule::RegisterTexture(const FName& Name, class UTexture2D* Texture, bool bMakeUnique) { - const TextureIndex Index = ImGuiModuleManager->GetTextureManager().CreateTextureResources(Name, Texture, bMakeUnique); + FTextureManager& TextureManager = ImGuiModuleManager->GetTextureManager(); + + checkf(!bMakeUnique || TextureManager.FindTextureIndex(Name) == INDEX_NONE, + TEXT("Trying to register a texture with a name '%s' that is already used. Chose a different name ") + TEXT("or use bMakeUnique false, to update existing texture resources."), *Name.ToString()); + + const TextureIndex Index = TextureManager.CreateTextureResources(Name, Texture); return FImGuiTextureHandle{ Name, ImGuiInterops::ToImTextureID(Index) }; } diff --git a/Source/ImGui/Private/ImGuiModuleManager.cpp b/Source/ImGui/Private/ImGuiModuleManager.cpp index 87e234b..f58e239 100644 --- a/Source/ImGui/Private/ImGuiModuleManager.cpp +++ b/Source/ImGui/Private/ImGuiModuleManager.cpp @@ -15,14 +15,18 @@ // High enough z-order guarantees that ImGui output is rendered on top of the game UI. constexpr int32 IMGUI_WIDGET_Z_ORDER = 10000; +// Module texture names. +const static FName PlainTextureName = "ImGuiModule_Plain"; +const static FName FontAtlasTextureName = "ImGuiModule_FontAtlas"; FImGuiModuleManager::FImGuiModuleManager() : Commands(Properties) , Settings(Properties, Commands) , ImGuiDemo(Properties) + , ContextManager(Settings) { // 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); // Typically we will use viewport created events to add widget to new game viewports. ViewportCreatedHandle = UGameViewportClient::OnViewportCreated().AddRaw(this, &FImGuiModuleManager::OnViewportCreated); @@ -43,6 +47,8 @@ FImGuiModuleManager::FImGuiModuleManager() FImGuiModuleManager::~FImGuiModuleManager() { + ContextManager.OnFontAtlasBuilt.RemoveAll(this); + // We are no longer interested with adding widgets to viewports. if (ViewportCreatedHandle.IsValid()) { @@ -80,22 +86,30 @@ void FImGuiModuleManager::LoadTextures() TextureManager.InitializeErrorTexture(FColor::Magenta); // Create an empty texture at index 0. We will use it for ImGui outputs with null texture id. - TextureManager.CreatePlainTexture(FName{ "ImGuiModule_Plain" }, 2, 2, FColor::White); + TextureManager.CreatePlainTexture(PlainTextureName, 2, 2, FColor::White); - // Create a font atlas texture. - ImFontAtlas& Fonts = ContextManager.GetFontAtlas(); + // Register for atlas built events, so we can rebuild textures. + ContextManager.OnFontAtlasBuilt.AddRaw(this, &FImGuiModuleManager::BuildFontAtlasTexture); - unsigned char* Pixels; - int Width, Height, Bpp; - Fonts.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp); - - TextureIndex FontsTexureIndex = TextureManager.CreateTexture(FName{ "ImGuiModule_FontAtlas" }, Width, Height, Bpp, Pixels); - - // Set font texture index in ImGui. - Fonts.TexID = ImGuiInterops::ToImTextureID(FontsTexureIndex); + BuildFontAtlasTexture(); } } +void FImGuiModuleManager::BuildFontAtlasTexture() +{ + // Create a font atlas texture. + ImFontAtlas& Fonts = ContextManager.GetFontAtlas(); + + unsigned char* Pixels; + int Width, Height, Bpp; + Fonts.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp); + + const TextureIndex FontsTexureIndex = TextureManager.CreateTexture(FontAtlasTextureName, Width, Height, Bpp, Pixels); + + // Set the font texture index in the ImGui. + Fonts.TexID = ImGuiInterops::ToImTextureID(FontsTexureIndex); +} + void FImGuiModuleManager::RegisterTick() { // Slate Post-Tick is a good moment to end and advance ImGui frame as it minimises a tearing. diff --git a/Source/ImGui/Private/ImGuiModuleManager.h b/Source/ImGui/Private/ImGuiModuleManager.h index 7b42450..8e8bc6a 100644 --- a/Source/ImGui/Private/ImGuiModuleManager.h +++ b/Source/ImGui/Private/ImGuiModuleManager.h @@ -46,6 +46,7 @@ private: FImGuiModuleManager& operator=(FImGuiModuleManager&&) = delete; void LoadTextures(); + void BuildFontAtlasTexture(); bool IsTickRegistered() { return TickDelegateHandle.IsValid(); } void RegisterTick(); diff --git a/Source/ImGui/Private/ImGuiModuleSettings.cpp b/Source/ImGui/Private/ImGuiModuleSettings.cpp index 8d6530e..bfeba33 100644 --- a/Source/ImGui/Private/ImGuiModuleSettings.cpp +++ b/Source/ImGui/Private/ImGuiModuleSettings.cpp @@ -104,7 +104,7 @@ void FImGuiModuleSettings::UpdateSettings() SetUseSoftwareCursor(SettingsObject->bUseSoftwareCursor); SetToggleInputKey(SettingsObject->ToggleInput); SetCanvasSizeInfo(SettingsObject->CanvasSize); - SetDPIScale(SettingsObject->DPIScale); + SetDPIScaleInfo(SettingsObject->DPIScale); } } @@ -167,16 +167,16 @@ void FImGuiModuleSettings::SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasS if (CanvasSize != CanvasSizeInfo) { CanvasSize = CanvasSizeInfo; - OnCanvasSizeInfoChangeDelegate.Broadcast(CanvasSize); + OnCanvasSizeChangedDelegate.Broadcast(CanvasSize); } } -void FImGuiModuleSettings::SetDPIScale(float Scale) +void FImGuiModuleSettings::SetDPIScaleInfo(const FImGuiDPIScaleInfo& ScaleInfo) { - if (DPIScale != Scale) + if (DPIScale != ScaleInfo) { - DPIScale = Scale; - OnDPIScaleChangeDelegate.Broadcast(DPIScale); + DPIScale = ScaleInfo; + OnDPIScaleChangedDelegate.Broadcast(DPIScale); } } diff --git a/Source/ImGui/Private/ImGuiModuleSettings.h b/Source/ImGui/Private/ImGuiModuleSettings.h index 691a258..321bf67 100644 --- a/Source/ImGui/Private/ImGuiModuleSettings.h +++ b/Source/ImGui/Private/ImGuiModuleSettings.h @@ -88,6 +88,49 @@ struct FImGuiCanvasSizeInfo bool operator!=(const FImGuiCanvasSizeInfo& Other) const { return !(*this == Other); } }; +UENUM(BlueprintType) +enum class EImGuiDPIScaleMethod : uint8 +{ + ImGui UMETA(DisplayName = "ImGui", ToolTip = "Scale ImGui fonts and styles."), + Slate UMETA(ToolTip = "Scale in Slate. ImGui canvas size will be adjusted to get the screen size that is the same as defined in the Canvas Size property.") +}; + +/** + * Struct with DPI scale data. + */ +USTRUCT() +struct FImGuiDPIScaleInfo +{ + GENERATED_BODY() + +protected: + + // Whether to scale in ImGui or in Slate. Scaling in ImGui gives better looking results but Slate might be a better + // option when layouts do not account for different fonts and styles. When scaling in Slate, ImGui canvas size will + // be adjusted to get the screen size that is the same as defined in the Canvas Size property. + UPROPERTY(EditAnywhere, Category = "DPI Scale") + EImGuiDPIScaleMethod ScalingMethod = EImGuiDPIScaleMethod::ImGui; + + // Fixed scale. + UPROPERTY(EditAnywhere, Category = "DPI Scale", meta = (ClampMin = 0, UIMin = 0)) + float Scale = 1.f; + +public: + + float GetImGuiScale() const { return ShouldScaleInSlate() ? 1.f : Scale; } + + float GetSlateScale() const { return ShouldScaleInSlate() ? Scale : 1.f; } + + bool ShouldScaleInSlate() const { return ScalingMethod == EImGuiDPIScaleMethod::Slate; } + + bool operator==(const FImGuiDPIScaleInfo& Other) const + { + return (Scale == Other.Scale) && (ScalingMethod == Other.ScalingMethod); + } + + bool operator!=(const FImGuiDPIScaleInfo& Other) const { return !(*this == Other); } +}; + // UObject used for loading and saving ImGui settings. To access actual settings use FImGuiModuleSettings interface. UCLASS(config=ImGui, defaultconfig) class UImGuiSettings : public UObject @@ -148,13 +191,9 @@ protected: UPROPERTY(EditAnywhere, config, Category = "Canvas Size") FImGuiCanvasSizeInfo CanvasSize; - // DPI scale for the ImGui widgets. - // - // Note that when this scale is other than 1.0, canvas size will be scaled before it is passed to the ImGui. - // It will be scaled to keep the same screen size as defined by the Canvas Size property. If the default - // canvas size is 3840x2160 and the DPI scale is 2.0, the size passed to the ImGui will be 1920x1080. - UPROPERTY(EditAnywhere, Category = "DPI Scale", meta = (ClampMin = 0, UIMin = 0)) - float DPIScale = 1.f; + // Setup DPI Scale. + UPROPERTY(EditAnywhere, config, Category = "DPI Scale", Meta = (ShowOnlyInnerProperties)) + FImGuiDPIScaleInfo DPIScale; // Deprecated name for ToggleInput. Kept temporarily to automatically move old configuration. UPROPERTY(config) @@ -178,9 +217,9 @@ public: // Generic delegate used to notify changes of boolean properties. DECLARE_MULTICAST_DELEGATE_OneParam(FBoolChangeDelegate, bool); - DECLARE_MULTICAST_DELEGATE_OneParam(FFloatChangeDelegate, float); DECLARE_MULTICAST_DELEGATE_OneParam(FStringClassReferenceChangeDelegate, const FStringClassReference&); DECLARE_MULTICAST_DELEGATE_OneParam(FImGuiCanvasSizeInfoChangeDelegate, const FImGuiCanvasSizeInfo&); + DECLARE_MULTICAST_DELEGATE_OneParam(FImGuiDPIScaleInfoChangeDelegate, const FImGuiDPIScaleInfo&); // 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. @@ -206,8 +245,8 @@ public: // Get the information how to calculate the canvas size. const FImGuiCanvasSizeInfo& GetCanvasSizeInfo() const { return CanvasSize; } - // Get the DPI Scale. - float GetDPIScale() const { return DPIScale; } + // Get the DPI Scale information. + const FImGuiDPIScaleInfo& GetDPIScaleInfo() const { return DPIScale; } // Delegate raised when ImGui Input Handle is changed. FStringClassReferenceChangeDelegate OnImGuiInputHandlerClassChanged; @@ -216,10 +255,10 @@ public: FBoolChangeDelegate OnUseSoftwareCursorChanged; // Delegate raised when information how to calculate the canvas size is changed. - FImGuiCanvasSizeInfoChangeDelegate OnCanvasSizeInfoChangeDelegate; + FImGuiCanvasSizeInfoChangeDelegate OnCanvasSizeChangedDelegate; // Delegate raised when the DPI scale is changed. - FFloatChangeDelegate OnDPIScaleChangeDelegate; + FImGuiDPIScaleInfoChangeDelegate OnDPIScaleChangedDelegate; private: @@ -232,7 +271,7 @@ private: void SetUseSoftwareCursor(bool bUse); void SetToggleInputKey(const FImGuiKeyInfo& KeyInfo); void SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo); - void SetDPIScale(float DPIScale); + void SetDPIScaleInfo(const FImGuiDPIScaleInfo& ScaleInfo); #if WITH_EDITOR void OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent); @@ -244,7 +283,7 @@ private: FStringClassReference ImGuiInputHandlerClass; FImGuiKeyInfo ToggleInputKey; FImGuiCanvasSizeInfo CanvasSize; - float DPIScale = 1.f; + FImGuiDPIScaleInfo DPIScale; bool bShareKeyboardInput = false; bool bShareGamepadInput = false; bool bShareMouseInput = false; diff --git a/Source/ImGui/Private/TextureManager.cpp b/Source/ImGui/Private/TextureManager.cpp index 405cad1..dee91cb 100644 --- a/Source/ImGui/Private/TextureManager.cpp +++ b/Source/ImGui/Private/TextureManager.cpp @@ -15,7 +15,6 @@ void FTextureManager::InitializeErrorTexture(const FColor& Color) TextureIndex FTextureManager::CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction SrcDataCleanup) { checkf(Name != NAME_None, TEXT("Trying to create a texture with a name 'NAME_None' is not allowed.")); - checkf(FindTextureIndex(Name) == INDEX_NONE, TEXT("Trying to create texture using name '%s' that is already registered."), *Name.ToString()); return CreateTextureInternal(Name, Width, Height, SrcBpp, SrcData, SrcDataCleanup); } @@ -23,24 +22,17 @@ TextureIndex FTextureManager::CreateTexture(const FName& Name, int32 Width, int3 TextureIndex FTextureManager::CreatePlainTexture(const FName& Name, int32 Width, int32 Height, FColor Color) { checkf(Name != NAME_None, TEXT("Trying to create a texture with a name 'NAME_None' is not allowed.")); - checkf(FindTextureIndex(Name) == INDEX_NONE, TEXT("Trying to create texture using name '%s' that is already registered."), *Name.ToString()); return CreatePlainTextureInternal(Name, Width, Height, Color); } -TextureIndex FTextureManager::CreateTextureResources(const FName& Name, UTexture2D* Texture, bool bMakeUnique) +TextureIndex FTextureManager::CreateTextureResources(const FName& Name, UTexture2D* Texture) { checkf(Name != NAME_None, TEXT("Trying to create texture resources with a name 'NAME_None' is not allowed.")); checkf(Texture, TEXT("Null Texture.")); - if (bMakeUnique) - { - checkf(FindTextureIndex(Name) == INDEX_NONE, TEXT("Trying to create texture resources using name '%s' that is already registered.") - TEXT(" Consider using different name or set bMakeUnique parameter to false."), *Name.ToString()); - } - // Create an entry for the texture. - return AddTextureEntry(Name, Texture, false, true); + return AddTextureEntry(Name, Texture, false); } void FTextureManager::ReleaseTextureResources(TextureIndex Index) @@ -75,7 +67,7 @@ TextureIndex FTextureManager::CreateTextureInternal(const FName& Name, int32 Wid } else { - return AddTextureEntry(Name, Texture, true, false); + return AddTextureEntry(Name, Texture, true); } } @@ -94,18 +86,18 @@ TextureIndex FTextureManager::CreatePlainTextureInternal(const FName& Name, int3 return CreateTextureInternal(Name, Width, Height, Bpp, SrcData, SrcDataCleanup); } -TextureIndex FTextureManager::AddTextureEntry(const FName& Name, UTexture2D* Texture, bool bAddToRoot, bool bUpdate) +TextureIndex FTextureManager::AddTextureEntry(const FName& Name, UTexture2D* Texture, bool bAddToRoot) { - // If we update try to find entry with that name. - TextureIndex Index = bUpdate ? FindTextureIndex(Name) : INDEX_NONE; + // Try to find an entry with that name. + TextureIndex Index = FindTextureIndex(Name); - // If we didn't find, try to find and entry to reuse. + // If this is a new name, try to find an entry to reuse. if (Index == INDEX_NONE) { Index = FindTextureIndex(NAME_None); } - // Either update/reuse entry or add a new one. + // Either update/reuse an entry or add a new one. if (Index != INDEX_NONE) { TextureResources[Index] = { Name, Texture, bAddToRoot }; diff --git a/Source/ImGui/Private/TextureManager.h b/Source/ImGui/Private/TextureManager.h index 73258d1..65cf531 100644 --- a/Source/ImGui/Private/TextureManager.h +++ b/Source/ImGui/Private/TextureManager.h @@ -60,7 +60,7 @@ public: return IsValidTexture(Index) ? TextureResources[Index].ResourceHandle : ErrorTexture.ResourceHandle; } - // Create a texture from raw data. Throws exception if there is already a texture with that name. + // Create a texture from raw data. // @param Name - The texture name // @param Width - The texture width // @param Height - The texture height @@ -70,7 +70,7 @@ public: // @returns The index of a texture that was created TextureIndex CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction SrcDataCleanup = [](uint8*) {}); - // Create a plain texture. Throws exception if there is already a texture with that name. + // Create a plain texture. // @param Name - The texture name // @param Width - The texture width // @param Height - The texture height @@ -78,16 +78,11 @@ public: // @returns The index of a texture that was created TextureIndex CreatePlainTexture(const FName& Name, int32 Width, int32 Height, FColor Color); - // Create Slate resources to an existing texture, managed externally. As part of an external interface it allows - // to loosen resource verification policy. By default (consistently with other create function) it protects from - // creating resources with name that is already registered. If bMakeUnique is false, then existing resources are - // updated/replaced. Throws exception, if name argument is NAME_None or texture is null. + // Create Slate resources to an existing texture, managed externally. // @param Name - The texture name // @param Texture - The texture - // @param bMakeUnique - If true (default) and there is already a texture with given name, then exception is thrown, - // otherwise existing resources are updated. // @returns The index to created/updated texture resources - TextureIndex CreateTextureResources(const FName& Name, UTexture2D* Texture, bool bMakeUnique = true); + TextureIndex CreateTextureResources(const FName& Name, UTexture2D* Texture); // Release resources for given texture. Ignores invalid indices. // @param Index - The index of a texture resources @@ -110,7 +105,7 @@ private: // @param Texture - The texture // @param bAddToRoot - If true, we should add texture to root to prevent garbage collection (use for own textures) // @returns The index of the entry that we created or reused - TextureIndex AddTextureEntry(const FName& Name, UTexture2D* Texture, bool bAddToRoot, bool bUpdate); + TextureIndex AddTextureEntry(const FName& Name, UTexture2D* Texture, bool bAddToRoot); // Check whether index is in range allocated for TextureResources (it doesn't mean that resources are valid). FORCEINLINE bool IsInRange(TextureIndex Index) const diff --git a/Source/ImGui/Private/Widgets/SImGuiLayout.cpp b/Source/ImGui/Private/Widgets/SImGuiLayout.cpp index b5b5269..6f02730 100644 --- a/Source/ImGui/Private/Widgets/SImGuiLayout.cpp +++ b/Source/ImGui/Private/Widgets/SImGuiLayout.cpp @@ -23,10 +23,10 @@ void SImGuiLayout::Construct(const FArguments& InArgs) if (ModuleManager) { auto& Settings = ModuleManager->GetSettings(); - SetDPIScale(Settings.GetDPIScale()); - if (!Settings.OnDPIScaleChangeDelegate.IsBoundToObject(this)) + SetDPIScale(Settings.GetDPIScaleInfo()); + if (!Settings.OnDPIScaleChangedDelegate.IsBoundToObject(this)) { - Settings.OnDPIScaleChangeDelegate.AddRaw(this, &SImGuiLayout::SetDPIScale); + Settings.OnDPIScaleChangedDelegate.AddRaw(this, &SImGuiLayout::SetDPIScale); } } @@ -71,7 +71,7 @@ SImGuiLayout::~SImGuiLayout() { if (ModuleManager) { - ModuleManager->GetSettings().OnDPIScaleChangeDelegate.RemoveAll(this); + ModuleManager->GetSettings().OnDPIScaleChangedDelegate.RemoveAll(this); } } diff --git a/Source/ImGui/Private/Widgets/SImGuiLayout.h b/Source/ImGui/Private/Widgets/SImGuiLayout.h index 4aca466..1edcd3e 100644 --- a/Source/ImGui/Private/Widgets/SImGuiLayout.h +++ b/Source/ImGui/Private/Widgets/SImGuiLayout.h @@ -31,8 +31,7 @@ public: private: float GetDPIScale() const { return DPIScale; } - void SetDPIScale(float Scale) { DPIScale = Scale; } - + void SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo) { DPIScale = ScaleInfo.GetSlateScale(); } FImGuiModuleManager* ModuleManager = nullptr; TWeakObjectPtr GameViewport; diff --git a/Source/ImGui/Private/Widgets/SImGuiWidget.cpp b/Source/ImGui/Private/Widgets/SImGuiWidget.cpp index 32ed095..d690b64 100644 --- a/Source/ImGui/Private/Widgets/SImGuiWidget.cpp +++ b/Source/ImGui/Private/Widgets/SImGuiWidget.cpp @@ -84,7 +84,7 @@ void SImGuiWidget::Construct(const FArguments& InArgs) const auto& Settings = ModuleManager->GetSettings(); SetHideMouseCursor(Settings.UseSoftwareCursor()); CreateInputHandler(Settings.GetImGuiInputHandlerClass()); - SetDPIScale(Settings.GetDPIScale()); + SetDPIScale(Settings.GetDPIScaleInfo()); SetCanvasSizeInfo(Settings.GetCanvasSizeInfo()); // Initialize state. @@ -284,13 +284,13 @@ void SImGuiWidget::RegisterImGuiSettingsDelegates() { Settings.OnUseSoftwareCursorChanged.AddRaw(this, &SImGuiWidget::SetHideMouseCursor); } - if (!Settings.OnDPIScaleChangeDelegate.IsBoundToObject(this)) + if (!Settings.OnDPIScaleChangedDelegate.IsBoundToObject(this)) { - Settings.OnDPIScaleChangeDelegate.AddRaw(this, &SImGuiWidget::SetDPIScale); + Settings.OnDPIScaleChangedDelegate.AddRaw(this, &SImGuiWidget::SetDPIScale); } - if (!Settings.OnCanvasSizeInfoChangeDelegate.IsBoundToObject(this)) + if (!Settings.OnCanvasSizeChangedDelegate.IsBoundToObject(this)) { - Settings.OnCanvasSizeInfoChangeDelegate.AddRaw(this, &SImGuiWidget::SetCanvasSizeInfo); + Settings.OnCanvasSizeChangedDelegate.AddRaw(this, &SImGuiWidget::SetCanvasSizeInfo); } } @@ -300,8 +300,8 @@ void SImGuiWidget::UnregisterImGuiSettingsDelegates() Settings.OnImGuiInputHandlerClassChanged.RemoveAll(this); Settings.OnUseSoftwareCursorChanged.RemoveAll(this); - Settings.OnDPIScaleChangeDelegate.RemoveAll(this); - Settings.OnCanvasSizeInfoChangeDelegate.RemoveAll(this); + Settings.OnDPIScaleChangedDelegate.RemoveAll(this); + Settings.OnCanvasSizeChangedDelegate.RemoveAll(this); } void SImGuiWidget::SetHideMouseCursor(bool bHide) @@ -509,8 +509,9 @@ void SImGuiWidget::HandleWindowFocusLost() } } -void SImGuiWidget::SetDPIScale(float Scale) +void SImGuiWidget::SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo) { + const float Scale = ScaleInfo.GetSlateScale(); if (DPIScale != Scale) { DPIScale = Scale; diff --git a/Source/ImGui/Private/Widgets/SImGuiWidget.h b/Source/ImGui/Private/Widgets/SImGuiWidget.h index 941b414..014c5cd 100644 --- a/Source/ImGui/Private/Widgets/SImGuiWidget.h +++ b/Source/ImGui/Private/Widgets/SImGuiWidget.h @@ -103,7 +103,7 @@ private: void UpdateTransparentMouseInput(const FGeometry& AllottedGeometry); void HandleWindowFocusLost(); - void SetDPIScale(float Scale); + void SetDPIScale(const FImGuiDPIScaleInfo& ScaleInfo); void SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo); void UpdateCanvasSize();