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.
This commit is contained in:
Sebastian 2020-05-10 21:05:27 +01:00
parent 003eed34d7
commit 4c3905b348
7 changed files with 303 additions and 30 deletions

View File

@ -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 <PropertyCustomizationHelpers.h>
#define LOCTEXT_NAMESPACE "ImGuiEditor"
namespace
{
EImGuiCanvasSizeType GetCanvasSizeTypeEnumValue(const TSharedPtr<IPropertyHandle>& TypeHandle)
{
uint8 ValueAsByte = 0;
if (TypeHandle.IsValid())
{
TypeHandle->GetValue(ValueAsByte);
}
return static_cast<EImGuiCanvasSizeType>(ValueAsByte);
}
bool IsAny(const TSharedPtr<IPropertyHandle>& TypeHandle, EImGuiCanvasSizeType Value)
{
return GetCanvasSizeTypeEnumValue(TypeHandle) == Value;
}
template<typename... TRest>
bool IsAny(const TSharedPtr<IPropertyHandle>& 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<IPropertyTypeCustomization> FImGuiCanvasSizeInfoCustomization::MakeInstance()
{
return MakeShareable(new FImGuiCanvasSizeInfoCustomization);
}
void FImGuiCanvasSizeInfoCustomization::CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
TSharedPtr<IPropertyHandle> TypeHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, SizeType));
TSharedPtr<IPropertyHandle> WidthHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, Width));
TSharedPtr<IPropertyHandle> HeightHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiCanvasSizeInfo, Height));
TSharedPtr<IPropertyHandle> 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<float>::Create([TypeHandle]()
{
return ShowToHeight(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom));
});
auto SizeRowVisibility = TAttribute<EVisibility>::Create([TypeHandle]()
{
return ShowToVisibility(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom));
});
auto ExtendRowHeight = TAttribute<float>::Create([TypeHandle]()
{
return ShowToHeight(IsAny(TypeHandle, EImGuiCanvasSizeType::Custom, EImGuiCanvasSizeType::Desktop));
});
auto ExtendRowVisibility = TAttribute<EVisibility>::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<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils)
{
}
#undef LOCTEXT_NAMESPACE
#endif // WITH_EDITOR

View File

@ -0,0 +1,22 @@
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#pragma once
#if WITH_EDITOR
#include <IPropertyTypeCustomization.h>
#include <PropertyHandle.h>
// Property type customization for FImGuiCanvasSizeInfo.
class FImGuiCanvasSizeInfoCustomization : public IPropertyTypeCustomization
{
public:
static TSharedRef<IPropertyTypeCustomization> MakeInstance();
// IPropertyTypeCustomization interface
virtual void CustomizeHeader(TSharedRef<IPropertyHandle> InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
virtual void CustomizeChildren(TSharedRef<IPropertyHandle> InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) override;
};
#endif // WITH_EDITOR

View File

@ -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");
}
}

View File

@ -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;
}
}

View File

@ -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;
};

View File

@ -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,15 +498,40 @@ void SImGuiWidget::HandleWindowFocusLost()
}
}
void SImGuiWidget::SetAdaptiveCanvasSize(bool bEnabled)
void SImGuiWidget::SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo)
{
if (bAdaptiveCanvasSize != bEnabled)
switch (CanvasSizeInfo.SizeType)
{
bAdaptiveCanvasSize = bEnabled;
case EImGuiCanvasSizeType::Custom:
MinCanvasSize = { static_cast<float>(CanvasSizeInfo.Width), static_cast<float>(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<typename LabelType>
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 };

View File

@ -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<SImGuiCanvasControl> CanvasControlWidget;
TWeakPtr<SWidget> PreviousUserFocusedWidget;