Files
UnrealEngineUWP/Engine/Source/Editor/ContentBrowser/Private/NativeClassHierarchy.h
Jamie Dale 55c3728d11 Added support for showing game and engine classes in the Content Browser
UE-7184 - Show game c++ classes and engine c++ classes in the content browser

The Content Browser now has extra entries for "Game C++ Classes" and "Engine C++ Classes" (with "Game" and "Engine" being renamed to "Game Content" and "Engine Content" respectively). These new folders serve as hosts for the list of available C++ modules, with each module internally mirroring the folder structure on disk.

For example:
- Game C++ Classes
    - ShooterGame
        - Classes
            - Bots
                - ShooterBot
                - ShooterAIController
                - [...]
            - [...]

The Content Browser allows you to navigate and search these classes like you can with assets, and provides convenient access to either edit an existing class, or create a new class (either within a selected folder, or derived from a selected class).

As the Content Browser only shows you known UClass types, any new classes need to be compiled into a loaded module before they will appear. This means that adding a new class will now automatically hot-reload your target module. Should you prefer to handle building and loading your modules manually, you can disable the automatic hot-reload via "Editor Settings" -> "Miscellaneous" -> "Hot Reload" -> "Automatically Hot Reload New Classes" (see UEditorUserSettings::bAutomaticallyHotReloadNewClasses).

[CL 2409386 by Jamie Dale in Main branch]
2015-01-16 15:39:47 -05:00

284 lines
10 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#pragma once
/**
* Type of hierarchy node
*/
enum class ENativeClassHierarchyNodeType : uint8
{
Folder,
Class,
};
/**
* Single node in the class hierarchy
*/
struct FNativeClassHierarchyNode
{
/** Helper function to make a folder node entry */
static TSharedRef<FNativeClassHierarchyNode> MakeFolderEntry(FName InEntryName, FString InEntryPath);
/** Helper function to make a class node entry */
static TSharedRef<FNativeClassHierarchyNode> MakeClassEntry(UClass* InClass, FName InClassModuleName, FString InClassModuleRelativePath, FString InEntryPath);
void AddChild(TSharedRef<FNativeClassHierarchyNode> ChildEntry);
/** Type of node, folder or class */
ENativeClassHierarchyNodeType Type;
/** The class this node is for (Type == Class) */
UClass* Class;
/** The name of the module the class is in (Type == Class) */
FName ClassModuleName;
/** Folder this class is in, relative to the class module (Type == Class) */
FString ClassModuleRelativePath;
/** Name used when showing this entry in the UI */
FName EntryName;
/** Path to this entry in the class hierarchy (not the same as the location on disk) */
FString EntryPath;
/** Child entries (Type == Folder) */
TMap<FName, TSharedPtr<FNativeClassHierarchyNode>> Children;
};
/**
* A filter used when querying the native class hierarchy.
* Each component element is processed as an 'OR' operation while all the components are processed together as an 'AND' operation.
*/
struct FNativeClassHierarchyFilter
{
/** The filter component for class paths */
TArray<FName> ClassPaths;
/** If true, ClassPaths components will be recursive */
bool bRecursivePaths;
FNativeClassHierarchyFilter()
{
bRecursivePaths = false;
}
/** Appends the other filter to this one */
void Append(const FNativeClassHierarchyFilter& Other)
{
ClassPaths.Append(Other.ClassPaths);
bRecursivePaths |= Other.bRecursivePaths;
}
/** Returns true if this filter has no entries */
bool IsEmpty() const
{
return ClassPaths.Num() == 0;
}
/** Clears this filter of all entries */
void Clear()
{
ClassPaths.Empty();
bRecursivePaths = false;
ensure(IsEmpty());
}
};
/**
* Generates a hierarchical tree of native UObject classes based on their location in the file system as used by the asset view when showing C++ classes
* This keeps its class hierarchy up-to-date as modules are loaded/unloaded
*/
class FNativeClassHierarchy
{
public:
/** Constructor and destructor */
FNativeClassHierarchy();
~FNativeClassHierarchy();
/** Get the delegate called when classes are added or removed from this class hierarchy */
FSimpleMulticastDelegate& OnClassHierarchyUpdated()
{
return ClassHierarchyUpdatedDelegate;
}
/**
* Work out which classes known to the class hierarchy match the given filter
*
* @param Filter - The filter to apply when working out which classes match
* @param OutClasses - Array to be populated with matching classes
*/
void GetMatchingClasses(const FNativeClassHierarchyFilter& Filter, TArray<UClass*>& OutClasses) const;
/**
* Work out which folders known to the class hierarchy match the given filter
*
* @param Filter - The filter to apply when working out which folders match
* @param OutFolders - Array to be populated with matching folder paths (these are in the form "/Classes_Name/ModuleName/SubFolder")
*/
void GetMatchingFolders(const FNativeClassHierarchyFilter& Filter, TArray<FString>& OutFolders) const;
/**
* Get all folders known to the class hierarchy
*
* @param OutClassRoots - Array to be populated with the known class path root folders (these are in the form "/Classes_Name")
* @param OutClassFolders - Array to be populated with known class folder paths (these are in the form "/Classes_Name/ModuleName/SubFolder")
* @param bIncludeEngineClasses - Whether we should include the "/Classes_Engine" root (and its associated folders) in the returned results
* @param bIncludePluginClasses - Whether we should include plugin class path roots (and their associated folders) in the returned results (these are all roots except for "/Classes_Game" and "/Classes_Engine")
*/
void GetClassFolders(TArray<FName>& OutClassRoots, TArray<FString>& OutClassFolders, const bool bIncludeEngineClasses = true, const bool bIncludePluginClasses = true) const;
/**
* Given a class path, work out the corresponding filesystem path on disk
* eg) Given "/Classes_Game/MyGame/MyAwesomeCode", we might resolve that to "../../../MyGame/Source/MyGame/MyAwesomeCode"
*
* @param InClassPath - The class path to try and resolve, eg) "/Classes_Game/MyGame/MyAwesomeCode"
* @param OutFileSystemPath - The string to fill with the resolved filesystem path, eg) "../../../MyGame/Source/MyGame/MyAwesomeCode"
*
* @return true if the file system path could be resolved and OutFileSystemPath was filled in, false otherwise
*/
bool GetFileSystemPath(const FString& InClassPath, FString& OutFileSystemPath) const;
/**
* Work out the class path that should be used for the given class
*
* @param InClass - The class path to try and get the class path for
* @param OutClassPath - The string to fill with the resolved class path, eg) "/Classes_Game/MyGame/MyAwesomeCode/MyAwesomeClass"
* @param bIncludeClassName - true to include the class name on the returned class path, false to get the class path of the containing folder
*
* @return true if the class path could be resolved and OutClassPath was filled in, false otherwise
*/
bool GetClassPath(UClass* InClass, FString& OutClassPath, const bool bIncludeClassName = true) const;
/**
* This will add a transient folder into the hierarchy
* The folder will be lost unless a class is added to it before the hierarchy is next re-populated
*
* @param InClassPath - The location of the new folder (in class path form - eg) "/Classes_Game/MyGame/MyAwesomeCode")
*/
void AddFolder(const FString& InClassPath);
private:
struct FAddClassMetrics
{
FAddClassMetrics()
: StartTime(FPlatformTime::Seconds())
, NumClassesAdded(0)
, NumFoldersAdded(0)
{
}
double StartTime;
int32 NumClassesAdded;
int32 NumFoldersAdded;
};
/**
* Update OutClasses with any classes that are children of the given node
*
* @param HierarchyNode - The node to add the children of
* @param OutClasses - Array to be populated with the child classes
* @param bRecurse - True to recurse into sub-folders, false to only check the given node
*/
static void GetClassesRecursive(const TSharedRef<FNativeClassHierarchyNode>& HierarchyNode, TArray<UClass*>& OutClasses, const bool bRecurse = true);
/**
* Update OutFolders with any folders that are children of the given node
*
* @param HierarchyNode - The node to add the children of
* @param OutFolders - Array to be populated with the child folders
* @param bRecurse - True to recurse into sub-folders, false to only check the given node
*/
static void GetFoldersRecursive(const TSharedRef<FNativeClassHierarchyNode>& HierarchyNode, TArray<FString>& OutFolders, const bool bRecurse = true);
/**
* Populate OutMatchingNodes with the nodes that correspond to the given class paths
*
* @param InClassPaths - The class paths to find the nodes for, or an empty list to get all root nodes
* @param OutMatchingNodes - Array to be populated the nodes that correspond to the given class paths
*/
void GatherMatchingNodesForPaths(const TArray<FName>& InClassPaths, TArray<TSharedRef<FNativeClassHierarchyNode>>& OutMatchingNodes) const;
/**
* Completely clear and re-populate the known class hierarchy
*/
void PopulateHierarchy();
/**
* Append any classes from the given module to the known class hierarchy
*
* @param InModuleName - The name of the module to add
*/
void AddClassesForModule(const FName& InModuleName);
/**
* Remove any classes in the given module from the known class hierarchy
*
* @param InModuleName - The name of the module to remove
*/
void RemoveClassesForModule(const FName& InModuleName);
/**
* Add a single class to the known class hierarchy, creating any folders as required
*
* @param InClass - The class that is to be added
* @param InGameModules - The list of modules that belong to the "/Classes_Game" root
* @param InPluginModules - The list of modules that belong to neither the "/Classes_Game" or "/Classes_Engine" roots
* @param AddClassMetrics - Metrics to update as new classes and folders are added (used to report population performance)
*/
void AddClass(UClass* InClass, const TSet<FName>& InGameModules, const TMap<FName, FName>& InPluginModules, FAddClassMetrics& AddClassMetrics);
/**
* Called when we're notified that a module has changed status
*
* @param InModuleName - The module that changed status
* @param InModuleChangeReason - Why the module changed status
*/
void OnModulesChanged(FName InModuleName, EModuleChangeReason InModuleChangeReason);
/**
* Called when we're notified that a module has been hot-reloaded
*
* @param bWasTriggeredAutomatically - True if the hot-reload was automatically triggered, or false if it was from a user action
*/
void OnHotReload(bool bWasTriggeredAutomatically);
/**
* Given a class, work out which module it belongs to
*
* @param InClass - The class we want to find the module for
*
* @return The name of the module that holds the class, eg) "CoreUObject"
*/
static FName GetClassModuleName(UClass* InClass);
/**
* Given a module, work out which root path it should use as a parent
*
* @param InModuleName - The module we want to find the root path for
* @param InGameModules - The list of modules that belong to the "/Classes_Game" root
* @param InPluginModules - The list of modules that belong to neither the "/Classes_Game" or "/Classes_Engine" roots
*
* @return The name of the root path to use, eg "/Classes_Game"
*/
static FName GetClassPathRootForModule(const FName& InModuleName, const TSet<FName>& InGameModules, const TMap<FName, FName>& InPluginModules);
/**
* Calculate the list of modules that belong to the "/Classes_Game" root
*/
static TSet<FName> GetGameModules();
/**
* Calculate the list of modules that belong to neither the "/Classes_Game" or "/Classes_Engine" roots
*/
static TMap<FName, FName> GetPluginModules();
/** Root level nodes corresponding to the root folders used by the Content Browser, eg) Classes_Engine, Classes_Game, etc */
TMap<FName, TSharedPtr<FNativeClassHierarchyNode>> RootNodes;
/** Delegate called when the class hierarchy is updated */
FSimpleMulticastDelegate ClassHierarchyUpdatedDelegate;
};