Create Editor Settings menu

This commit is contained in:
Jordon Brooks 2024-07-08 21:58:14 +01:00
parent 98fb068e29
commit 76654dbe0a
Signed by: jordon
GPG key ID: DBD9758CD53E786A
10 changed files with 461 additions and 3 deletions

View file

@ -0,0 +1,29 @@
using UnrealBuildTool;
public class HarmonyLinkEditor : ModuleRules
{
public HarmonyLinkEditor(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"InputCore",
"HarmonyLinkSettings",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
}
);
}
}

View file

@ -0,0 +1,44 @@
#include "HarmonyLinkEditor.h"
#include "ISettingsModule.h"
#include "Objects/HarmonyLinkSettings.h"
#include "Objects/HarmonyLinkSettingsCustomization.h"
#define LOCTEXT_NAMESPACE "FHarmonyLinkEditorModule"
void FHarmonyLinkEditorModule::StartupModule()
{
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomClassLayout("HarmonyLinkSettings", FOnGetDetailCustomizationInstance::CreateStatic(&FHarmonyLinkSettingsCustomization::MakeInstance));
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
if (SettingsModule)
{
SettingsModule->RegisterSettings("Project", "Plugins", "HarmonyLink",
LOCTEXT("HarmonyLinkSettingsName", "HarmonyLink Settings"),
LOCTEXT("HarmonyLinkSettingsDescription", "Configure the HarmonyLink plugin settings."),
GetMutableDefault<UHarmonyLinkSettings>()
);
}
}
void FHarmonyLinkEditorModule::ShutdownModule()
{
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
{
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.UnregisterCustomClassLayout("HarmonyLinkSettings");
}
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
if (SettingsModule)
{
SettingsModule->UnregisterSettings("Project", "Plugins", "HarmonyLink");
}
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FHarmonyLinkEditorModule, HarmonyLinkEditor)

View file

@ -0,0 +1,4 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Objects/HarmonyLinkSettings.h"

View file

@ -0,0 +1,265 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Objects/HarmonyLinkSettingsCustomization.h"
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "Objects/HarmonyLinkGraphics.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Input/SSpinBox.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/Input/SEditableTextBox.h"
#include "Widgets/Input/SComboBox.h"
#include "Widgets/Input/SButton.h"
TArray<TSharedPtr<FString>> FHarmonyLinkSettingsCustomization::ComboBoxOptions;
TSharedRef<IDetailCustomization> FHarmonyLinkSettingsCustomization::MakeInstance()
{
return MakeShareable(new FHarmonyLinkSettingsCustomization);
}
void FHarmonyLinkSettingsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
CustomizeSettings(DetailBuilder);
}
void FHarmonyLinkSettingsCustomization::CustomizeSettings(IDetailLayoutBuilder& DetailBuilder)
{
IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("HarmonyLink Settings");
// Access the HarmonyLinkGraphics singleton instance
UHarmonyLinkGraphics* HarmonyLinkGraphics = UHarmonyLinkGraphics::GetSettings();
if (!HarmonyLinkGraphics)
{
return;
}
// Display Automatic Profile Switch
Category.AddCustomRow(FText::FromString("Automatic Profile Switch"))
.NameContent()
[
SNew(STextBlock)
.Text(FText::FromString("Automatic Profile Switch"))
]
.ValueContent()
[
SNew(SCheckBox)
.IsChecked_Lambda([HarmonyLinkGraphics]() -> ECheckBoxState
{
return HarmonyLinkGraphics->GetAutomaticSwitching() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
})
.OnCheckStateChanged_Lambda([HarmonyLinkGraphics](ECheckBoxState NewState)
{
HarmonyLinkGraphics->SetAutomaticSwitching(NewState == ECheckBoxState::Checked);
})
];
// Iterate over profiles and display their settings
const TMap<EProfile, FSettingsProfile>& Profiles = HarmonyLinkGraphics->GetProfiles();
for (const auto& ProfilePair : Profiles)
{
const EProfile ProfileEnum = ProfilePair.Key;
const FSettingsProfile& Profile = ProfilePair.Value;
Category.AddCustomRow(FText::FromString(Profile.SectionName.ToString()))
.NameContent()
[
SNew(STextBlock)
.Text(FText::FromString(Profile.SectionName.ToString()))
];
// Create a box to contain the table
TSharedRef<SVerticalBox> Table = SNew(SVerticalBox);
// Add table header
Table->AddSlot()
.AutoHeight()
.Padding(2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(2)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(FText::FromString("Name"))
]
+ SHorizontalBox::Slot()
.Padding(2)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(FText::FromString("Value"))
]
+ SHorizontalBox::Slot()
.Padding(2)
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(FText::FromString("Type"))
]
];
// Add rows for each setting in the profile
for (const auto& SettingPair : Profile.Settings)
{
const FName& SettingName = SettingPair.Key;
const FHLConfigValue& SettingValue = SettingPair.Value;
TSharedPtr<SEditableTextBox> NameTextBox;
TSharedPtr<SWidget> ValueWidget;
TSharedPtr<SComboBox<TSharedPtr<FString>>> TypeComboBox;
// Create the value widget based on the type
switch (SettingValue.GetType())
{
case EConfigValueType::Int:
ValueWidget = SNew(SSpinBox<int32>)
.Value(SettingValue.GetValue<int32>())
.OnValueCommitted_Lambda([HarmonyLinkGraphics, ProfileEnum, SettingName](int32 NewValue, ETextCommit::Type CommitType)
{
HarmonyLinkGraphics->SetSetting(ProfileEnum, SettingName, FHLConfigValue(NewValue));
});
break;
case EConfigValueType::Float:
ValueWidget = SNew(SSpinBox<float>)
.Value(SettingValue.GetValue<float>())
.OnValueCommitted_Lambda([HarmonyLinkGraphics, ProfileEnum, SettingName](float NewValue, ETextCommit::Type CommitType)
{
HarmonyLinkGraphics->SetSetting(ProfileEnum, SettingName, FHLConfigValue(NewValue));
});
break;
case EConfigValueType::Bool:
ValueWidget = SNew(SCheckBox)
.IsChecked(SettingValue.GetValue<bool>() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked)
.OnCheckStateChanged_Lambda([HarmonyLinkGraphics, ProfileEnum, SettingName](ECheckBoxState NewState)
{
bool NewValue = NewState == ECheckBoxState::Checked;
HarmonyLinkGraphics->SetSetting(ProfileEnum, SettingName, FHLConfigValue(NewValue));
});
break;
case EConfigValueType::String:
ValueWidget = SNew(SEditableTextBox)
.Text(FText::FromString(SettingValue.GetValue<FString>()))
.OnTextCommitted_Lambda([HarmonyLinkGraphics, ProfileEnum, SettingName](const FText& NewText, ETextCommit::Type CommitType)
{
FString NewValue = NewText.ToString();
HarmonyLinkGraphics->SetSetting(ProfileEnum, SettingName, FHLConfigValue(NewValue));
});
break;
}
// Add the row for the setting
Table->AddSlot()
.AutoHeight()
.Padding(2)
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.Padding(2)
.VAlign(VAlign_Center)
[
SAssignNew(NameTextBox, SEditableTextBox)
.Text(FText::FromString(SettingName.ToString()))
.OnTextCommitted_Lambda([HarmonyLinkGraphics, ProfileEnum, SettingName](const FText& NewText, ETextCommit::Type CommitType)
{
FString NewName = NewText.ToString();
HarmonyLinkGraphics->RenameSetting(ProfileEnum, SettingName, FName(*NewName));
})
]
+ SHorizontalBox::Slot()
.Padding(2)
.VAlign(VAlign_Center)
[
ValueWidget.ToSharedRef()
]
+ SHorizontalBox::Slot()
.Padding(2)
.VAlign(VAlign_Center)
[
SAssignNew(TypeComboBox, SComboBox<TSharedPtr<FString>>)
.OptionsSource(GetComboBoxOptions()) // Use static member
.OnGenerateWidget_Lambda([](TSharedPtr<FString> InOption)
{
return SNew(STextBlock).Text(FText::FromString(*InOption));
})
.OnSelectionChanged_Lambda([this, HarmonyLinkGraphics, ProfileEnum, SettingName](TSharedPtr<FString> NewSelection, ESelectInfo::Type SelectInfo)
{
if (NewSelection.IsValid())
{
EConfigValueType NewType = this->GetConfigValueTypeFromString(*NewSelection);
HarmonyLinkGraphics->ChangeSettingType(ProfileEnum, SettingName, NewType);
}
})
.Content()
[
SNew(STextBlock)
.Text(FText::FromString(GetConfigValueTypeAsString(SettingValue.GetType())))
]
]
];
}
Category.AddCustomRow(FText::FromString(Profile.SectionName.ToString() + " Table"))
[
Table
];
}
}
TArray<TSharedPtr<FString>>* FHarmonyLinkSettingsCustomization::GetComboBoxOptions()
{
if (ComboBoxOptions.Num() == 0) // Initialize only once
{
ComboBoxOptions.Add(MakeShareable(new FString("int")));
ComboBoxOptions.Add(MakeShareable(new FString("float")));
ComboBoxOptions.Add(MakeShareable(new FString("bool")));
ComboBoxOptions.Add(MakeShareable(new FString("string")));
}
return &ComboBoxOptions;
}
EConfigValueType FHarmonyLinkSettingsCustomization::GetConfigValueTypeFromString(const FString& TypeString)
{
if (TypeString == "int")
{
return EConfigValueType::Int;
}
if (TypeString == "float")
{
return EConfigValueType::Float;
}
if (TypeString == "bool")
{
return EConfigValueType::Bool;
}
if (TypeString == "string")
{
return EConfigValueType::String;
}
return EConfigValueType::None;
}
FString FHarmonyLinkSettingsCustomization::GetConfigValueTypeAsString(EConfigValueType Type)
{
switch (Type)
{
case EConfigValueType::Int:
return "int";
case EConfigValueType::Float:
return "float";
case EConfigValueType::Bool:
return "bool";
case EConfigValueType::String:
return "string";
default:
return "none";
}
}

View file

@ -0,0 +1,11 @@
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
class FHarmonyLinkEditorModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View file

@ -0,0 +1,18 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "HarmonyLinkSettings.generated.h"
/**
*
*/
UCLASS(MinimalAPI)
class UHarmonyLinkSettings : public UObject
{
GENERATED_BODY()
};

View file

@ -0,0 +1,27 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "IDetailCustomization.h"
#include "Structs/HLConfigValue.h"
/**
*
*/
class FHarmonyLinkSettingsCustomization : public IDetailCustomization
{
public:
static TSharedRef<IDetailCustomization> MakeInstance();
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
private:
void CustomizeSettings(IDetailLayoutBuilder& DetailBuilder);
static TArray<TSharedPtr<FString>>* GetComboBoxOptions();
static EConfigValueType GetConfigValueTypeFromString(const FString& TypeString);
static FString GetConfigValueTypeAsString(EConfigValueType Type);
static TArray<TSharedPtr<FString>> ComboBoxOptions; // Static member for options
};

View file

@ -64,6 +64,11 @@ UHarmonyLinkGraphics::~UHarmonyLinkGraphics()
FWorldDelegates::OnPreWorldFinishDestroy.RemoveAll(this);
}
const TMap<EProfile, FSettingsProfile>& UHarmonyLinkGraphics::GetProfiles()
{
return _Profiles;
}
void UHarmonyLinkGraphics::LoadConfig(const bool bForceReload)
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("LoadConfig called."));
@ -259,6 +264,53 @@ UHarmonyLinkGraphics* UHarmonyLinkGraphics::GetSettings()
return _INSTANCE;
}
void UHarmonyLinkGraphics::RenameSetting(EProfile Profile, FName OldName, FName NewName)
{
if (FSettingsProfile* SettingsProfile = _Profiles.Find(Profile))
{
if (SettingsProfile->Settings.Contains(OldName))
{
FHLConfigValue Value = SettingsProfile->Settings[OldName];
SettingsProfile->Settings.Remove(OldName);
SettingsProfile->Settings.Add(NewName, Value);
SaveConfig();
}
}
}
void UHarmonyLinkGraphics::ChangeSettingType(EProfile Profile, FName SettingName, EConfigValueType NewType)
{
if (FSettingsProfile* SettingsProfile = _Profiles.Find(Profile))
{
if (SettingsProfile->Settings.Contains(SettingName))
{
FHLConfigValue OldValue = SettingsProfile->Settings[SettingName];
SettingsProfile->Settings.Remove(SettingName);
FHLConfigValue NewValue;
switch (NewType)
{
case EConfigValueType::Int:
NewValue = FHLConfigValue(0); // Default to conversion from float
break;
case EConfigValueType::Float:
NewValue = FHLConfigValue(0.f); // Default to conversion from int
break;
case EConfigValueType::Bool:
NewValue = FHLConfigValue(false); // Default to conversion from int
break;
case EConfigValueType::String:
NewValue = FHLConfigValue(FString()); // Default to string
break;
default:
return;
}
SettingsProfile->Settings.Add(SettingName, NewValue);
SaveConfig();
}
}
}
FSettingsProfile UHarmonyLinkGraphics::GetSettingProfile(const EProfile Profile)
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("GetSettingProfile called."));

View file

@ -195,6 +195,11 @@ public:
*/
static void DestroySettings();
protected:
static const TMap<EProfile, FSettingsProfile>& GetProfiles();
void RenameSetting(EProfile Profile, FName OldName, FName NewName);
void ChangeSettingType(EProfile Profile, FName SettingName, EConfigValueType NewType);
private:
/**
* @brief Initializes the UHarmonyLinkGraphics settings.

View file

@ -8,6 +8,7 @@
UENUM(BlueprintType)
enum class EConfigValueType : uint8
{
None,
Int,
Float,
Bool,
@ -42,11 +43,13 @@ private:
public:
FHLConfigValue() : Type(EConfigValueType::String), IntValue(0), FloatValue(0.0f), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(int32 Value) : Type(EConfigValueType::Int), IntValue(Value), FloatValue(0.0f), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(const EConfigValueType Type) : Type(Type), IntValue(0), FloatValue(0.0f), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(float Value) : Type(EConfigValueType::Float), IntValue(0), FloatValue(Value), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(const int32 Value) : Type(EConfigValueType::Int), IntValue(Value), FloatValue(0.0f), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(bool Value) : Type(EConfigValueType::Bool), IntValue(0), FloatValue(0.0f), BoolValue(Value), StringValue(TEXT("")) {}
FHLConfigValue(const float Value) : Type(EConfigValueType::Float), IntValue(0), FloatValue(Value), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(const bool Value) : Type(EConfigValueType::Bool), IntValue(0), FloatValue(0.0f), BoolValue(Value), StringValue(TEXT("")) {}
FHLConfigValue(const FString& Value) : Type(EConfigValueType::String), IntValue(0), FloatValue(0.0f), BoolValue(false), StringValue(Value) {}