Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ekinertac/c4ce1cf430f61cd9eb941cb7cfbb2748 to your computer and use it in GitHub Desktop.
Save ekinertac/c4ce1cf430f61cd9eb941cb7cfbb2748 to your computer and use it in GitHub Desktop.
Automatically adding category sections to editor
// add this code to your StartupModule() function of your EDITOR module
// if it doesnt work, try setting loading phase to "postdefault" -- no idea if this will produce side effects though.
// basically idea is looping all CDOs (i.e. UClasses) and their properties, getting their "category" meta value and registering them to
// property sections
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
static constexpr bool bAddSubcategories = false; // you probably want this to be false
auto ProcessCategory = [&PropertyModule](const FName ClassName, const FString& Category)
{
const TSharedRef<FPropertySection> Section = PropertyModule.FindOrCreateSection(ClassName, *Category, FText::FromString(Category));
if (!Section->HasAddedCategory(*Category))
{
Section->AddCategory(*Category);
}
};
auto IsValidCategory = [](const FString& Category)
{
return !Category.IsEmpty() && Category != TEXT("None");
};
auto IsPropertyAllowed = [](const FProperty* Property)
{
return Property->HasAnyPropertyFlags(CPF_BlueprintVisible | CPF_Edit);
};
for (TObjectIterator<UClass> ClassIterator; ClassIterator; ++ClassIterator)
{
const UClass* Class = *ClassIterator;
if (Class->HasAnyClassFlags(CLASS_Abstract | CLASS_Deprecated | CLASS_NewerVersionExists))
{
continue;
}
for (const FProperty* Property = Class->PropertyLink; Property != nullptr; Property = Property->PropertyLinkNext)
{
if (IsPropertyAllowed(Property))
{
// prepare values
const FName ClassName = Property->GetOwnerUObject()->GetFName();
const FString CategoryString = Property->GetMetaData("Category");
if (!IsValidCategory(CategoryString)) // skip empty categories
{
continue;
}
// if we have sub categories, parse it into array and add them individually
TArray<FString> Subcategories;
if (CategoryString.ParseIntoArray(Subcategories, TEXT("|")) > 0)
{
if (bAddSubcategories)
{
for (const FString& Subcategory : Subcategories)
{
if (IsValidCategory(Subcategory))
{
ProcessCategory(ClassName, Subcategory);
}
}
}
else
{
FString Subcategory = Subcategories[0]; // just use first subcategory
if (IsValidCategory(Subcategory))
{
ProcessCategory(ClassName, Subcategory);
}
}
}
else // otherwise just process single category
{
ProcessCategory(ClassName, CategoryString);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment