From 4c3905b348bbc3120dccc9f0408fd30c4298230c Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 10 May 2020 21:05:27 +0100 Subject: [PATCH] Added more options to control the canvas size: - Canvas size can be set to custom width and height, desktop size or viewport size. - The old Adaptive Canvas Size is replaced by the viewport size type. - Custom and desktop size types can be also extended to the viewport size but they will never be smaller than their base sizes. --- .../ImGuiCanvasSizeInfoCustomization.cpp | 160 ++++++++++++++++++ .../Editor/ImGuiCanvasSizeInfoCustomization.h | 22 +++ Source/ImGui/Private/Editor/ImGuiEditor.cpp | 4 + Source/ImGui/Private/ImGuiModuleSettings.cpp | 8 +- Source/ImGui/Private/ImGuiModuleSettings.h | 53 +++++- Source/ImGui/Private/Widgets/SImGuiWidget.cpp | 80 ++++++--- Source/ImGui/Private/Widgets/SImGuiWidget.h | 6 +- 7 files changed, 303 insertions(+), 30 deletions(-) create mode 100644 Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.cpp create mode 100644 Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.h diff --git a/Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.cpp b/Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.cpp new file mode 100644 index 0000000..7c24ef2 --- /dev/null +++ b/Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.cpp @@ -0,0 +1,160 @@ +// Distributed under the MIT License (MIT) (see accompanying LICENSE file) + +#include "ImGuiPrivatePCH.h" + +#if WITH_EDITOR + +#include "ImGuiCanvasSizeInfoCustomization.h" + +#include "ImGuiModuleSettings.h" + +#include + + +#define LOCTEXT_NAMESPACE "ImGuiEditor" + + +namespace +{ + EImGuiCanvasSizeType GetCanvasSizeTypeEnumValue(const TSharedPtr& TypeHandle) + { + uint8 ValueAsByte = 0; + if (TypeHandle.IsValid()) + { + TypeHandle->GetValue(ValueAsByte); + } + return static_cast(ValueAsByte); + } + + bool IsAny(const TSharedPtr& TypeHandle, EImGuiCanvasSizeType Value) + { + return GetCanvasSizeTypeEnumValue(TypeHandle) == Value; + } + + template + bool IsAny(const TSharedPtr& TypeHandle, EImGuiCanvasSizeType First, TRest... Rest) + { + return IsAny(TypeHandle, First) || IsAny(TypeHandle, Rest...); + } + + float ShowToHeight(bool bShow) + { + return bShow ? 0.f /* Infinity */ : SMALL_NUMBER; + } + + EVisibility ShowToVisibility(bool bShow) + { + return bShow ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden; + } +} + +//---------------------------------------------------------------------------------------------------- +// FImGuiKeyInfoCustomization implementation +//---------------------------------------------------------------------------------------------------- + +TSharedRef FImGuiCanvasSizeInfoCustomization::MakeInstance() +{ + return MakeShareable(new FImGuiCanvasSizeInfoCustomization); +} + +void FImGuiCanvasSizeInfoCustomization::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + TSharedPtr TypeHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, SizeType)); + TSharedPtr WidthHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, Width)); + TSharedPtr HeightHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, Height)); + TSharedPtr ExtendToViewportHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, bExtendToViewport)); + +#define ADD_DIMENSION_SLOT(Handle, LPadding) \ + + SHorizontalBox::Slot()\ + .Padding(LPadding, 0.f, 0.f, 0.f)\ + .HAlign(HAlign_Fill)\ + .MaxWidth(80.f)\ + [\ + SNew(SVerticalBox)\ + + SVerticalBox::Slot()\ + .AutoHeight()\ + [\ + Handle->CreatePropertyNameWidget()\ + ]\ + + SVerticalBox::Slot()\ + .HAlign(HAlign_Fill)\ + .AutoHeight()\ + [\ + Handle->CreatePropertyValueWidget()\ + ]\ + ] + + auto SizeRowHeight = TAttribute::Create([TypeHandle]() + { + return ShowToHeight(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom)); + }); + + auto SizeRowVisibility = TAttribute::Create([TypeHandle]() + { + return ShowToVisibility(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom)); + }); + + auto ExtendRowHeight = TAttribute::Create([TypeHandle]() + { + return ShowToHeight(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom, EImGuiCanvasSizeType::Desktop)); + }); + + auto ExtendRowVisibility = TAttribute::Create([TypeHandle]() + { + return ShowToVisibility(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom, EImGuiCanvasSizeType::Desktop)); + }); + + HeaderRow + .NameContent() + [ + InStructPropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + .MinDesiredWidth(168.f) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + [ + TypeHandle->CreatePropertyValueWidget() + ] + + SVerticalBox::Slot() + .AutoHeight() + .MaxHeight(SizeRowHeight) + [ + SNew(SHorizontalBox) + .Visibility(SizeRowVisibility) + ADD_DIMENSION_SLOT(WidthHandle, 0.f) + ADD_DIMENSION_SLOT(HeightHandle, 6.f) + ] + + SVerticalBox::Slot() + .AutoHeight() + .MaxHeight(ExtendRowHeight) + [ + SNew(SHorizontalBox) + .Visibility(ExtendRowVisibility) + + SHorizontalBox::Slot() + .AutoWidth() + [ + ExtendToViewportHandle->CreatePropertyValueWidget() + ] + + SHorizontalBox::Slot() + .Padding(4.f, 0.f, 0.f, 0.f) + .AutoWidth() + [ + ExtendToViewportHandle->CreatePropertyNameWidget() + ] + ] + ]; + +#undef ADD_DIMENSION_SLOT +} + +void FImGuiCanvasSizeInfoCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ +} + + +#undef LOCTEXT_NAMESPACE + +#endif // WITH_EDITOR diff --git a/Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.h b/Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.h new file mode 100644 index 0000000..950b828 --- /dev/null +++ b/Source/ImGui/Private/Editor/ImGuiCanvasSizeInfoCustomization.h @@ -0,0 +1,22 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#if WITH_EDITOR + +#include +#include + + +// Property type customization for FImGuiCanvasSizeInfo. +class FImGuiCanvasSizeInfoCustomization : public IPropertyTypeCustomization +{ +public: + static TSharedRef MakeInstance(); + + // IPropertyTypeCustomization interface + virtual void CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; + virtual void CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override; +}; + +#endif // WITH_EDITOR diff --git a/Source/ImGui/Private/Editor/ImGuiEditor.cpp b/Source/ImGui/Private/Editor/ImGuiEditor.cpp index 1c91cc6..b8c2afb 100644 --- a/Source/ImGui/Private/Editor/ImGuiEditor.cpp +++ b/Source/ImGui/Private/Editor/ImGuiEditor.cpp @@ -6,6 +6,7 @@ #include "ImGuiEditor.h" +#include "ImGuiCanvasSizeInfoCustomization.h" #include "ImGuiKeyInfoCustomization.h" #include "ImGuiModuleSettings.h" @@ -69,6 +70,8 @@ void FImGuiEditor::Register() { bCustomPropertyTypeLayoutsRegistered = true; + PropertyModule->RegisterCustomPropertyTypeLayout("ImGuiCanvasSizeInfo", + FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FImGuiCanvasSizeInfoCustomization::MakeInstance)); PropertyModule->RegisterCustomPropertyTypeLayout("ImGuiKeyInfo", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FImGuiKeyInfoCustomization::MakeInstance)); } @@ -93,6 +96,7 @@ void FImGuiEditor::Unregister() if (FPropertyEditorModule* PropertyModule = GetPropertyEditorModule()) { + PropertyModule->UnregisterCustomPropertyTypeLayout("ImGuiCanvasSizeInfo"); PropertyModule->UnregisterCustomPropertyTypeLayout("ImGuiKeyInfo"); } } diff --git a/Source/ImGui/Private/ImGuiModuleSettings.cpp b/Source/ImGui/Private/ImGuiModuleSettings.cpp index 8c91636..8cfd79d 100644 --- a/Source/ImGui/Private/ImGuiModuleSettings.cpp +++ b/Source/ImGui/Private/ImGuiModuleSettings.cpp @@ -103,7 +103,7 @@ void FImGuiModuleSettings::UpdateSettings() SetShareMouseInput(SettingsObject->bShareMouseInput); SetUseSoftwareCursor(SettingsObject->bUseSoftwareCursor); SetToggleInputKey(SettingsObject->ToggleInput); - SetAdaptiveCanvasSize(SettingsObject->bAdaptiveCanvasSize); + SetCanvasSizeInfo(SettingsObject->CanvasSize); } } @@ -161,11 +161,11 @@ void FImGuiModuleSettings::SetToggleInputKey(const FImGuiKeyInfo& KeyInfo) } } -void FImGuiModuleSettings::SetAdaptiveCanvasSize(bool bAdaptive) +void FImGuiModuleSettings::SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo) { - if (bAdaptiveCanvasSize != bAdaptive) + if (CanvasSize != CanvasSizeInfo) { - bAdaptiveCanvasSize = bAdaptive; + CanvasSize = CanvasSizeInfo; } } diff --git a/Source/ImGui/Private/ImGuiModuleSettings.h b/Source/ImGui/Private/ImGuiModuleSettings.h index d951e1d..2d80e8c 100644 --- a/Source/ImGui/Private/ImGuiModuleSettings.h +++ b/Source/ImGui/Private/ImGuiModuleSettings.h @@ -47,6 +47,47 @@ struct FImGuiKeyInfo } }; +UENUM(BlueprintType) +enum class EImGuiCanvasSizeType : uint8 +{ + Custom UMETA(ToolTip = "Canvas will have a custom width and height."), + Desktop UMETA(ToolTip = "Canvas will have the same width and height as the desktop."), + Viewport UMETA(ToolTip = "Canvas will always have the same width and height as the viewport."), +}; + +/** + * Struct with information how to calculate canvas size. + */ +USTRUCT() +struct FImGuiCanvasSizeInfo +{ + GENERATED_BODY() + + // Select how to specify canvas size. + UPROPERTY(EditAnywhere, Category = "Canvas Size") + EImGuiCanvasSizeType SizeType = EImGuiCanvasSizeType::Desktop; + + // Custom canvas width. + UPROPERTY(EditAnywhere, Category = "Canvas Size", meta = (ClampMin = 0, UIMin = 0)) + int32 Width = 3840; + + // Custom canvas height. + UPROPERTY(EditAnywhere, Category = "Canvas Size", meta = (ClampMin = 0, UIMin = 0)) + int32 Height = 2160; + + // If this is true, canvas width or height may be extended, if the viewport size is larger. + UPROPERTY(EditAnywhere, Category = "Canvas Size", meta = (ClampMin = 0, UIMin = 0)) + bool bExtendToViewport = true; + + bool operator==(const FImGuiCanvasSizeInfo& Other) const + { + return (SizeType == Other.SizeType) && (Width == Other.Width) + && (Height == Other.Height) && (bExtendToViewport == Other.bExtendToViewport); + } + + bool operator!=(const FImGuiCanvasSizeInfo& 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 @@ -103,9 +144,9 @@ protected: UPROPERTY(EditAnywhere, config, Category = "Keyboard Shortcuts") FImGuiKeyInfo ToggleInput; - // If true, the size of ImGui canvas will be adaptive to game viewport. + // Chose how to define the ImGui canvas size. Select between custom, desktop and viewport. UPROPERTY(EditAnywhere, config, Category = "Canvas Size") - bool bAdaptiveCanvasSize = false; + FImGuiCanvasSizeInfo CanvasSize; // Deprecated name for ToggleInput. Kept temporarily to automatically move old configuration. UPROPERTY(config) @@ -152,8 +193,8 @@ public: // Get the shortcut configuration for 'ImGui.ToggleInput' command. const FImGuiKeyInfo& GetToggleInputKey() const { return ToggleInputKey; } - // Get the adaptive canvas size configuration. - bool AdaptiveCanvasSize() const { return bAdaptiveCanvasSize; } + // Get the information how to calculate the canvas size. + const FImGuiCanvasSizeInfo& GetCanvasSizeInfo() const { return CanvasSize; } // Delegate raised when ImGui Input Handle is changed. FStringClassReferenceChangeDelegate OnImGuiInputHandlerClassChanged; @@ -171,7 +212,7 @@ private: void SetShareMouseInput(bool bShare); void SetUseSoftwareCursor(bool bUse); void SetToggleInputKey(const FImGuiKeyInfo& KeyInfo); - void SetAdaptiveCanvasSize(bool bAdaptive); + void SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo); #if WITH_EDITOR void OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent); @@ -182,9 +223,9 @@ private: FStringClassReference ImGuiInputHandlerClass; FImGuiKeyInfo ToggleInputKey; + FImGuiCanvasSizeInfo CanvasSize; bool bShareKeyboardInput = false; bool bShareGamepadInput = false; bool bShareMouseInput = false; bool bUseSoftwareCursor = false; - bool bAdaptiveCanvasSize = false; }; diff --git a/Source/ImGui/Private/Widgets/SImGuiWidget.cpp b/Source/ImGui/Private/Widgets/SImGuiWidget.cpp index 404afac..fcebdb7 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()); - SetAdaptiveCanvasSize(Settings.AdaptiveCanvasSize()); + SetCanvasSizeInfo(Settings.GetCanvasSizeInfo()); // Initialize state. UpdateVisibility(); @@ -498,14 +498,39 @@ void SImGuiWidget::HandleWindowFocusLost() } } -void SImGuiWidget::SetAdaptiveCanvasSize(bool bEnabled) +void SImGuiWidget::SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo) { - if (bAdaptiveCanvasSize != bEnabled) + switch (CanvasSizeInfo.SizeType) { - bAdaptiveCanvasSize = bEnabled; - bUpdateCanvasSize = true; - UpdateCanvasSize(); + case EImGuiCanvasSizeType::Custom: + MinCanvasSize = { static_cast(CanvasSizeInfo.Width), static_cast(CanvasSizeInfo.Height) }; + bAdaptiveCanvasSize = CanvasSizeInfo.bExtendToViewport; + bCanvasControlEnabled = true; + break; + + case EImGuiCanvasSizeType::Desktop: + MinCanvasSize = (GEngine && GEngine->GameUserSettings) + ? GEngine->GameUserSettings->GetDesktopResolution() : FVector2D::ZeroVector; + bAdaptiveCanvasSize = CanvasSizeInfo.bExtendToViewport; + bCanvasControlEnabled = true; + break; + + case EImGuiCanvasSizeType::Viewport: + default: + MinCanvasSize = FVector2D::ZeroVector; + bAdaptiveCanvasSize = true; + bCanvasControlEnabled = false; } + + // We only update canvas control widget when canvas control is enabled. Make sure that we will not leave + // that widget active after disabling canvas control. + if (CanvasControlWidget && !bCanvasControlEnabled) + { + CanvasControlWidget->SetActive(false); + } + + bUpdateCanvasSize = true; + UpdateCanvasSize(); } void SImGuiWidget::UpdateCanvasSize() @@ -514,28 +539,30 @@ void SImGuiWidget::UpdateCanvasSize() { if (auto* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex)) { - if (bAdaptiveCanvasSize) + CanvasSize = MinCanvasSize; + if (bAdaptiveCanvasSize && GameViewport.IsValid()) { - if (GameViewport.IsValid()) - { - FVector2D DisplaySize; - GameViewport->GetViewportSize(DisplaySize); - ContextProxy->SetDisplaySize(DisplaySize); - } + FVector2D ViewportSize; + GameViewport->GetViewportSize(ViewportSize); + CanvasSize = FVector2D::Max(CanvasSize, ViewportSize); } else { - ContextProxy->ResetDisplaySize(); - // No need for more updates, if we successfully processed fixed-canvas mode. + // No need for more updates, if we successfully processed fixed-canvas size. bUpdateCanvasSize = false; } + + ContextProxy->SetDisplaySize(CanvasSize); } } } void SImGuiWidget::UpdateCanvasControlMode(const FInputEvent& InputEvent) { - CanvasControlWidget->SetActive(!bAdaptiveCanvasSize && InputEvent.IsLeftAltDown() && InputEvent.IsLeftShiftDown()); + if (bCanvasControlEnabled) + { + CanvasControlWidget->SetActive(InputEvent.IsLeftAltDown() && InputEvent.IsLeftShiftDown()); + } } void SImGuiWidget::OnPostImGuiUpdate() @@ -628,7 +655,7 @@ int32 SImGuiWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeo FVector2D SImGuiWidget::ComputeDesiredSize(float Scale) const { - return FVector2D{ 3840.f, 2160.f } * Scale; + return CanvasSize * Scale; } #if IMGUI_WIDGET_DEBUG @@ -744,6 +771,13 @@ namespace TwoColumns LabelText(Label); ImGui::NextColumn(); ImGui::Text("%ls", Value); ImGui::NextColumn(); } + + template + static void ValueWidthHeight(LabelType&& Label, const FVector2D& Value) + { + LabelText(Label); ImGui::NextColumn(); + ImGui::Text("Width = %.0f, Height = %.0f", Value.X, Value.Y); ImGui::NextColumn(); + } } namespace Styles @@ -770,11 +804,19 @@ void SImGuiWidget::OnDebugDraw() if (CVars::DebugWidget.GetValueOnGameThread() > 0) { bool bDebug = true; - ImGui::SetNextWindowSize(ImVec2(380, 480), ImGuiSetCond_Once); + ImGui::SetNextWindowSize(ImVec2(380, 480), ImGuiCond_Once); if (ImGui::Begin("ImGui Widget Debug", &bDebug)) { ImGui::Spacing(); + TwoColumns::CollapsingGroup("Canvas Size", [&]() + { + TwoColumns::Value("Is Adaptive", bAdaptiveCanvasSize); + TwoColumns::Value("Is Updating", bUpdateCanvasSize); + TwoColumns::ValueWidthHeight("Min Canvas Size", MinCanvasSize); + TwoColumns::ValueWidthHeight("Canvas Size", CanvasSize); + }); + TwoColumns::CollapsingGroup("Context", [&]() { TwoColumns::Value("Context Index", ContextIndex); @@ -821,7 +863,7 @@ void SImGuiWidget::OnDebugDraw() FImGuiInputState& InputState = ContextProxy->GetInputState(); bool bDebug = true; - ImGui::SetNextWindowSize(ImVec2(460, 480), ImGuiSetCond_Once); + ImGui::SetNextWindowSize(ImVec2(460, 480), ImGuiCond_Once); if (ImGui::Begin("ImGui Input State", &bDebug)) { const ImVec4 HiglightColor{ 1.f, 1.f, 0.5f, 1.f }; diff --git a/Source/ImGui/Private/Widgets/SImGuiWidget.h b/Source/ImGui/Private/Widgets/SImGuiWidget.h index a36a1de..e1b63a6 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 SetAdaptiveCanvasSize(bool bEnabled); + void SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo); void UpdateCanvasSize(); void UpdateCanvasControlMode(const FInputEvent& InputEvent); @@ -134,12 +134,16 @@ private: int32 ContextIndex = 0; + FVector2D MinCanvasSize = FVector2D::ZeroVector; + FVector2D CanvasSize = FVector2D::ZeroVector; + bool bInputEnabled = false; bool bForegroundWindow = false; bool bHideMouseCursor = true; bool bTransparentMouseInput = false; bool bAdaptiveCanvasSize = false; bool bUpdateCanvasSize = false; + bool bCanvasControlEnabled = false; TSharedPtr CanvasControlWidget; TWeakPtr PreviousUserFocusedWidget;