text: // Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
  
  #pragma once
  
  #include "VersionCompatibility.h"
  
  #include <Curves/CurveFloat.h>
  #include <Delegates/Delegate.h>
  #include <InputCoreTypes.h>
  #include <Styling/SlateTypes.h>
  #include <UObject/Object.h>
  
  // We use FStringClassReference, which is supported by older and newer engine versions. Starting from 4.18, it is
  // a typedef of FSoftClassPath, which is also recognized by UHT.
  #if ENGINE_COMPATIBILITY_LEGACY_STRING_CLASS_REF
  #include <StringClassReference.h>
  #else
  #include <UObject/SoftObjectPath.h>
  #endif
  
  #include "ImGuiModuleSettings.generated.h"
  
  
  /**
   * Struct containing key information that can be used for key binding. Using 'Undetermined' value for modifier keys
   * means that those keys should be ignored when testing for a match.
   */
  USTRUCT()
  struct FImGuiKeyInfo
  {
  	GENERATED_BODY()
  
  	UPROPERTY(EditAnywhere, Category = "Input")
  	FKey Key;
  
  	UPROPERTY(EditAnywhere, Category = "Input")
  	ECheckBoxState Shift = ECheckBoxState::Undetermined;
  
  	UPROPERTY(EditAnywhere, Category = "Input")
  	ECheckBoxState Ctrl = ECheckBoxState::Undetermined;
  
  	UPROPERTY(EditAnywhere, Category = "Input")
  	ECheckBoxState Alt = ECheckBoxState::Undetermined;
  
  	UPROPERTY(EditAnywhere, Category = "Input")
  	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);
  	}
  };
  
  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); }
  };
  
  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;
  
  	// An optional scale to apply on top or instead of the curve-based scale.
  	UPROPERTY(EditAnywhere, Category = "DPI Scale", meta = (ClampMin = 0, UIMin = 0))
  	float Scale = 1.f;
  
  	// Curve mapping resolution height to scale.
  	UPROPERTY(config, EditAnywhere, Category = "DPI Scale", meta = (XAxisName = "Resolution Height", YAxisName = "Scale", EditCondition = "bScaleWithCurve"))
  	FRuntimeFloatCurve DPICurve;
  
  	// Whether to use curve-based scaling. If enabled, Scale will be multiplied by a value read from the DPICurve.
  	// If disabled, only the Scale property will be used.
  	UPROPERTY(config, EditAnywhere, Category = "DPI Scale")
  	bool bScaleWithCurve = true;
  
  public:
  
  	FImGuiDPIScaleInfo();
  
  	float GetImGuiScale() const { return ShouldScaleInSlate() ? 1.f : CalculateScale(); }
  
  	float GetSlateScale() const { return ShouldScaleInSlate() ? CalculateScale() : 1.f; }
  
  	bool ShouldScaleInSlate() const { return ScalingMethod == EImGuiDPIScaleMethod::Slate; }
  
  private:
  
  	float CalculateScale() const { return Scale * CalculateResolutionBasedScale(); }
  
  	float CalculateResolutionBasedScale() const;
  };
  
  // UObject used for loading and saving ImGui settings. To access actual settings use FImGuiModuleSettings interface.
  UCLASS(config=Game, defaultconfig)
  class UImGuiSettings : public UObject
  {
  	GENERATED_BODY()
  
  public:
  
  	// Get default instance or null if it is not loaded.
  	static UImGuiSettings* Get() { return DefaultInstance; }
  
  	// Delegate raised when default instance is loaded.
  	static FSimpleMulticastDelegate OnSettingsLoaded;
  
  	virtual void PostInitProperties() override;
  	virtual void BeginDestroy() override;
  
  protected:
  
  	// Path to own implementation of ImGui Input Handler allowing to customize handling of keyboard and gamepad input.
  	// If not set then default handler is used.
  	UPROPERTY(EditAnywhere, config, Category = "Extensions", meta = (MetaClass = "ImGuiInputHandler"))
  	FStringClassReference ImGuiInputHandlerClass;
  
  	// Whether ImGui should share keyboard input with game.
  	// This defines initial behaviour which can be later changed using 'ImGui.ToggleKeyboardInputSharing' command or
  	// module properties interface.
  	UPROPERTY(EditAnywhere, config, Category = "Input")
  	bool bShareKeyboardInput = false;
  
  	// Whether ImGui should share gamepad input with game.
  	// This defines initial behaviour which can be later changed using 'ImGui.ToggleGamepadInputSharing' command or
  	// module properties interface.
  	UPROPERTY(EditAnywhere, config, Category = "Input")
  	bool bShareGamepadInput = false;
  
  	// Whether ImGui should share mouse input with game.
  	// This defines initial behaviour which can be later changed using 'ImGui.ToggleMouseInputSharing' command or
  	// module properties interface.
  	UPROPERTY(EditAnywhere, config, Category = "Input")
  	bool bShareMouseInput = false;
  
  	// If true, then in input mode ImGui will draw its own cursor in place of the hardware one.
  	// When disabled (default) there is a noticeable difference between cursor position seen by ImGui and position on
  	// the screen. Enabling this option removes that effect but with lower frame-rates UI becomes quickly unusable.
  	UPROPERTY(EditAnywhere, config, Category = "Input", AdvancedDisplay)
  	bool bUseSoftwareCursor = false;
  
  	// Define a shortcut key to 'ImGui.ToggleInput' command. Binding is only set if the key field is valid.
  	// Note that modifier key properties can be set to one of the three values: undetermined means that state of the given
  	// modifier is not important, checked means that it needs to be pressed and unchecked means that it cannot be pressed.
  	//
  	// This binding is using Player Input's DebugExecBindings which only works in non-shipment builds.
  	UPROPERTY(EditAnywhere, config, Category = "Keyboard Shortcuts")
  	FImGuiKeyInfo ToggleInput;
  
  	// Chose how to define the ImGui canvas size. Select between custom, desktop and viewport.
  	UPROPERTY(EditAnywhere, config, Category = "Canvas Size")
  	FImGuiCanvasSizeInfo CanvasSize;
  
  	// Setup DPI Scale.
  	UPROPERTY(EditAnywhere, config, Category = "DPI Scale", Meta = (ShowOnlyInnerProperties))
  	FImGuiDPIScaleInfo DPIScale;
  
  	static UImGuiSettings* DefaultInstance;
  
  	friend class FImGuiModuleSettings;
  };
  
  
  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&);
  	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.
  	//
  	// @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; }
  
  	// Get the information how to calculate the canvas size.
  	const FImGuiCanvasSizeInfo& GetCanvasSizeInfo() const { return CanvasSize; }
  
  	// Get the DPI Scale information.
  	const FImGuiDPIScaleInfo& GetDPIScaleInfo() const { return DPIScale; }
  
  	// Delegate raised when ImGui Input Handle is changed.
  	FStringClassReferenceChangeDelegate OnImGuiInputHandlerClassChanged;
  
  	// Delegate raised when software cursor configuration is changed.
  	FBoolChangeDelegate OnUseSoftwareCursorChanged;
  
  	// Delegate raised when information how to calculate the canvas size is changed.
  	FImGuiCanvasSizeInfoChangeDelegate OnCanvasSizeChangedDelegate;
  
  	// Delegate raised when the DPI scale is changed.
  	FImGuiDPIScaleInfoChangeDelegate OnDPIScaleChangedDelegate;
  
  private:
  
  	void InitializeAllSettings();
  	void UpdateSettings();
  	void UpdateDPIScaleInfo();
  
  	void SetImGuiInputHandlerClass(const FStringClassReference& ClassReference);
  	void SetShareKeyboardInput(bool bShare);
  	void SetShareGamepadInput(bool bShare);
  	void SetShareMouseInput(bool bShare);
  	void SetUseSoftwareCursor(bool bUse);
  	void SetToggleInputKey(const FImGuiKeyInfo& KeyInfo);
  	void SetCanvasSizeInfo(const FImGuiCanvasSizeInfo& CanvasSizeInfo);
  	void SetDPIScaleInfo(const FImGuiDPIScaleInfo& ScaleInfo);
  
  #if WITH_EDITOR
  	void OnPropertyChanged(class UObject* ObjectBeingModified, struct FPropertyChangedEvent& PropertyChangedEvent);
  #endif // WITH_EDITOR
  
  	FImGuiModuleProperties& Properties;
  	FImGuiModuleCommands& Commands;
  
  	FStringClassReference ImGuiInputHandlerClass;
  	FImGuiKeyInfo ToggleInputKey;
  	FImGuiCanvasSizeInfo CanvasSize;
  	FImGuiDPIScaleInfo DPIScale;
  	bool bShareKeyboardInput = false;
  	bool bShareGamepadInput = false;
  	bool bShareMouseInput = false;
  	bool bUseSoftwareCursor = false;
  };
  