From 9ab12850fb5caaa2a931faf9b3285dfeff5c6f0b Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 31 Jul 2018 22:09:01 +0100 Subject: [PATCH] Added custom property type layout for FImGuiKeyInfo. --- Source/ImGui/ImGui.Build.cs | 1 + Source/ImGui/Private/Editor/ImGuiEditor.cpp | 27 +++ Source/ImGui/Private/Editor/ImGuiEditor.h | 3 +- .../Editor/ImGuiKeyInfoCustomization.cpp | 211 ++++++++++++++++++ .../Editor/ImGuiKeyInfoCustomization.h | 26 +++ Source/ImGui/Private/ImGuiSettings.h | 14 +- Source/ImGui/Public/ImGuiInputHandler.h | 2 +- 7 files changed, 275 insertions(+), 9 deletions(-) create mode 100644 Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.cpp create mode 100644 Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.h diff --git a/Source/ImGui/ImGui.Build.cs b/Source/ImGui/ImGui.Build.cs index b9bba6c..554b232 100644 --- a/Source/ImGui/ImGui.Build.cs +++ b/Source/ImGui/ImGui.Build.cs @@ -64,6 +64,7 @@ public class ImGui : ModuleRules PrivateDependencyModuleNames.AddRange( new string[] { + "EditorStyle", "Settings", "UnrealEd", } diff --git a/Source/ImGui/Private/Editor/ImGuiEditor.cpp b/Source/ImGui/Private/Editor/ImGuiEditor.cpp index 781ea74..71c3605 100644 --- a/Source/ImGui/Private/Editor/ImGuiEditor.cpp +++ b/Source/ImGui/Private/Editor/ImGuiEditor.cpp @@ -6,6 +6,7 @@ #include "ImGuiEditor.h" +#include "ImGuiKeyInfoCustomization.h" #include "ImGuiSettings.h" #include @@ -22,6 +23,11 @@ namespace { return FModuleManager::GetModulePtr("Settings"); } + + FPropertyEditorModule* GetPropertyEditorModule() + { + return FModuleManager::GetModulePtr("PropertyEditor"); + } } FImGuiEditor::FImGuiEditor() @@ -56,6 +62,17 @@ void FImGuiEditor::Register() GetMutableDefault()); } } + + if (!bCustomPropertyTypeLayoutsRegistered) + { + if (FPropertyEditorModule* PropertyModule = GetPropertyEditorModule()) + { + bCustomPropertyTypeLayoutsRegistered = true; + + PropertyModule->RegisterCustomPropertyTypeLayout("ImGuiKeyInfo", + FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FImGuiKeyInfoCustomization::MakeInstance)); + } + } } void FImGuiEditor::Unregister() @@ -69,6 +86,16 @@ void FImGuiEditor::Unregister() SettingsModule->UnregisterSettings(SETTINGS_CONTAINER); } } + + if (bCustomPropertyTypeLayoutsRegistered) + { + bCustomPropertyTypeLayoutsRegistered = false; + + if (FPropertyEditorModule* PropertyModule = GetPropertyEditorModule()) + { + PropertyModule->UnregisterCustomPropertyTypeLayout("ImGuiKeyInfo"); + } + } } void FImGuiEditor::CreateRegistrator() diff --git a/Source/ImGui/Private/Editor/ImGuiEditor.h b/Source/ImGui/Private/Editor/ImGuiEditor.h index 61a2aee..b7350b6 100644 --- a/Source/ImGui/Private/Editor/ImGuiEditor.h +++ b/Source/ImGui/Private/Editor/ImGuiEditor.h @@ -14,7 +14,7 @@ public: private: - bool IsRegistrationCompleted() const { return bSettingsRegistered; } + bool IsRegistrationCompleted() const { return bSettingsRegistered && bCustomPropertyTypeLayoutsRegistered; } void Register(); void Unregister(); @@ -25,6 +25,7 @@ private: FDelegateHandle RegistratorHandle; bool bSettingsRegistered = false; + bool bCustomPropertyTypeLayoutsRegistered = false; }; #endif // WITH_EDITOR diff --git a/Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.cpp b/Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.cpp new file mode 100644 index 0000000..d10cc78 --- /dev/null +++ b/Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.cpp @@ -0,0 +1,211 @@ +// Distributed under the MIT License (MIT) (see accompanying LICENSE file) + +#include "ImGuiPrivatePCH.h" + +#if WITH_EDITOR + +#include "ImGuiKeyInfoCustomization.h" + +#include "ImGuiSettings.h" + +#include +#include +#include +#include + + +#define LOCTEXT_NAMESPACE "ImGuiEditor" + + +//---------------------------------------------------------------------------------------------------- +// Helper widgets +//---------------------------------------------------------------------------------------------------- + +namespace +{ + class SPropertyKeySelector : public SCompoundWidget + { + public: + SLATE_BEGIN_ARGS(SPropertyKeySelector) + {} + SLATE_ARGUMENT(TSharedPtr, KeyHandle) + SLATE_ATTRIBUTE(FSlateFontInfo, Font) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs) + { + KeyHandle = InArgs._KeyHandle; + + ChildSlot + [ + SNew(SKeySelector) + .CurrentKey(this, &SPropertyKeySelector::GetCurrentKey) + .OnKeyChanged(this, &SPropertyKeySelector::OnKeyChanged) + .Font(InArgs._Font) + ]; + } + + TOptional GetCurrentKey() const + { + TArray RawPtrs; + KeyHandle->AccessRawData(RawPtrs); + + if (RawPtrs.Num()) + { + FKey* KeyPtr = static_cast(RawPtrs[0]); + + if (KeyPtr) + { + for (int32 Index = 1; Index < RawPtrs.Num(); Index++) + { + if (*static_cast(RawPtrs[Index]) != *KeyPtr) + { + return TOptional(); + } + } + + return *KeyPtr; + } + } + + return FKey(); + } + + void OnKeyChanged(TSharedPtr SelectedKey) + { + KeyHandle->SetValueFromFormattedString(SelectedKey->ToString()); + } + + private: + + TSharedPtr KeyHandle; + }; + + class SPropertyTriStateCheckBox : public SCompoundWidget + { + public: + + using FCheckBoxStateRaw = std::underlying_type::type; + + SLATE_BEGIN_ARGS(SPropertyTriStateCheckBox) + {} + SLATE_ARGUMENT(TSharedPtr, PropertyHandle) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs) + { + PropertyHandle = InArgs._PropertyHandle; + + // We are abusing SCheckBox a bit but our GetPropertyValue with custom OnCheckBoxStateChanged implementation + // gives a checkbox that allows to cycle between all three states. + ChildSlot + [ + SNew(SCheckBox) + .IsChecked(this, &SPropertyTriStateCheckBox::GetPropertyValue) + .OnCheckStateChanged(this, &SPropertyTriStateCheckBox::OnCheckBoxStateChanged) + ]; + } + + FCheckBoxStateRaw GetPropertyRawValue() const + { + FCheckBoxStateRaw Value; + PropertyHandle.Get()->GetValue(Value); + return Value; + } + + ECheckBoxState GetPropertyValue() const + { + return static_cast(GetPropertyRawValue()); + } + + void OnCheckBoxStateChanged(ECheckBoxState State) + { + const FCheckBoxStateRaw PrevEnumValue = (GetPropertyRawValue() + 2) % 3; + PropertyHandle.Get()->SetValue(PrevEnumValue); + } + + private: + + TSharedPtr PropertyHandle; + }; +} + + +//---------------------------------------------------------------------------------------------------- +// FImGuiKeyInfoCustomization implementation +//---------------------------------------------------------------------------------------------------- + +namespace InputConstants +{ + static const FMargin PropertyPadding(2.0f, 0.0f, 2.0f, 0.0f); +} + +TSharedRef FImGuiKeyInfoCustomization::MakeInstance() +{ + return MakeShareable(new FImGuiKeyInfoCustomization); +} + +void FImGuiKeyInfoCustomization::CustomizeHeader(TSharedRef InStructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ + TSharedPtr KeyHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Key)); + TSharedPtr ShiftHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Shift)); + TSharedPtr CtrlHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Ctrl)); + TSharedPtr AltHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Alt)); + TSharedPtr CmdHandle = InStructPropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FImGuiKeyInfo, Cmd)); + +#define ADD_CHECK_BOX_SLOT(Handle) \ + + SHorizontalBox::Slot()\ + .Padding(InputConstants::PropertyPadding)\ + .HAlign(HAlign_Left)\ + .VAlign(VAlign_Center)\ + .AutoWidth()\ + [\ + Handle->CreatePropertyNameWidget()\ + ]\ + + SHorizontalBox::Slot()\ + .Padding(InputConstants::PropertyPadding)\ + .HAlign(HAlign_Left)\ + .VAlign(VAlign_Center)\ + .AutoWidth()\ + [\ + SNew(SPropertyTriStateCheckBox).PropertyHandle(Handle)\ + ] + + HeaderRow + .NameContent() + [ + InStructPropertyHandle->CreatePropertyNameWidget() + ] + .ValueContent() + .MaxDesiredWidth(400.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .Padding(InputConstants::PropertyPadding) + .AutoWidth() + [ + SNew(SBox) + .WidthOverride(200.f) + [ + SNew(SPropertyKeySelector) + .KeyHandle(KeyHandle) + .Font(StructCustomizationUtils.GetRegularFont()) + ] + ] + ADD_CHECK_BOX_SLOT(ShiftHandle) + ADD_CHECK_BOX_SLOT(CtrlHandle) + ADD_CHECK_BOX_SLOT(AltHandle) + ADD_CHECK_BOX_SLOT(CmdHandle) + ]; + +#undef ADD_CHECK_BOX_SLOT +} + +void FImGuiKeyInfoCustomization::CustomizeChildren(TSharedRef InStructPropertyHandle, IDetailChildrenBuilder& StructBuilder, IPropertyTypeCustomizationUtils& StructCustomizationUtils) +{ +} + + +#undef LOCTEXT_NAMESPACE + +#endif // WITH_EDITOR diff --git a/Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.h b/Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.h new file mode 100644 index 0000000..70c3924 --- /dev/null +++ b/Source/ImGui/Private/Editor/ImGuiKeyInfoCustomization.h @@ -0,0 +1,26 @@ +// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. + +#pragma once + +#if WITH_EDITOR + +#include +#include + + +class FDetailWidgetRow; +class IDetailChildrenBuilder; +class IPropertyHandle; + +// Property type customization for FImGuiKeyInfo. +class FImGuiKeyInfoCustomization : 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/ImGuiSettings.h b/Source/ImGui/Private/ImGuiSettings.h index 39daccd..feb1eab 100644 --- a/Source/ImGui/Private/ImGuiSettings.h +++ b/Source/ImGui/Private/ImGuiSettings.h @@ -70,19 +70,19 @@ 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 = "Input", meta = (MetaClass = "ImGuiInputHandler")) + UPROPERTY(EditAnywhere, config, Category = "Extensions", meta = (MetaClass = "ImGuiInputHandler")) FStringClassReference ImGuiInputHandlerClass; - // Define a custom key binding to 'ImGui.SwitchInputMode' command. Mapping will be only set if Key property in this - // structure is set to a valid key. Modifier keys can be either completely ignored (Undetermined), required to be - // pressed (Checked) or required to be not pressed (Unchecked). - UPROPERTY(EditAnywhere, config, Category = "Input") + // Define a custom key binding to 'ImGui.SwitchInputMode' command. Binding is only set if key 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 tested, 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 SwitchInputModeKey; private: - void UpdateSwitchInputModeBinding(); - #if WITH_EDITOR void RegisterPropertyChangedDelegate(); diff --git a/Source/ImGui/Public/ImGuiInputHandler.h b/Source/ImGui/Public/ImGuiInputHandler.h index 5622d66..ce30f83 100644 --- a/Source/ImGui/Public/ImGuiInputHandler.h +++ b/Source/ImGui/Public/ImGuiInputHandler.h @@ -90,7 +90,7 @@ private: * Also note that input handler functions are only called when ImGui Widget is receiving input events, what can be for * instance suppressed by opening console. * - * See @ Project Settings/Plugins/ImGui/Input/ImGuiInputHandlerClass property to set custom implementation. + * See @ Project Settings/Plugins/ImGui/Extensions/ImGuiInputHandlerClass property to set custom implementation. */ UCLASS() class IMGUI_API UImGuiInputHandler : public UObject