About Slate classes
Since all slate widgets are contained in
TSharedPtr
s, I won’t always typeTSharedPtr<SSomeWIdget>
butSSomeWidget
.
Small disclaimer
This topic has a LOT going on under the hood, I won’t mention EVERYTHING that happens when doing X with Y, I talk about the important/relevant stuff happening.
Base
My Blueprint
When you open an BP in editor it runs FBlueprintEditorModule::CreateBlueprintEditor
which creates a TSharedRef<FBlueprintEditor>
.
The “My Blueprint” tab slate widget is made in FMyBlueprintSummoner::CreateTabBody
which returns a SMyBlueprint
(that was previously made by FBlueprintEditor
)
Categories and content
The categories “GRAPHS”, “FUNCTIONS”, “MACROS”, “VARIABLES” and “EVENT DISPATCHERS” are all of type SCategoryHeaderTableRow
and the entries inside are STableRow<TSharedPtr<FGraphActionNode>>
Example:
”+” buttons
The ”+” buttons of each of these categories are made in SMyBlueprint::CreateAddToSectionButton
(its called on Paint)
When you click on any of them the call-back will be SMyBlueprint::OnAddButtonClickedOnSection
. It then runs a ExecuteAction
on the CommandList
member var of type FUICommandList
using the FBlueprintEditorCommands
, the only thing that changes between the different categories ”+” buttons is what AddNewXXX
FUICommandInfo
will be called (for example for the ”+” variable button it gets AddNewVariable
).
These commands are made in
FBlueprintEditorCommands::RegisterCommands
Variables
Adding a new variable in BP
About
FEdGraphPinType
See
UEdGraphSchema_K2
for a full and detailed list In the editor the type of a variable is defined by aFEdGraphPinType
.
PinCategory
will be whatever “category” is displayed in the picker, it can bebyte
(the literal type and for enums),int
,int64
,real
,struct
,object
(orsoftobject
),class
(orsoftclass
),interface
, …PinSubCategory
depends, usuallyNone
but if selectingfloat
it will bedouble
PinSubCategoryObject
will hold aUObject
weak ref to the selected object/class, if selecting a struct it will be aUScript
After the preliminary process mentioned above in Bones->"+" buttons
the editor calls FBlueprintEditor::OnAddNewVariable
. This function does the following:
- It will find a unique default var name using
FBlueprintEditorUtils::FindUniqueKismetName
. - Then it calls
FBlueprintEditorUtils::AddMemberVariable
and passes theUBlueprint
we are editing, the var name and the last type we used. - And finally if it succeeds it will call
FBlueprintEditor::RenameNewlyAddedAction
manually (that’s’ why you can directly type to rename the variable name)
In depth of FBlueprintEditorUtils::AddMemberVariable
:
- It calls
Modify
- It creates a new var as a
FBPVariableDescription
and set name, GUID, property flags, and more (last pin type is either default (a boolean) or the last type set fromSBlueprintPalette::OnVarTypeChanged
). - It adds this var in a an array named
NewVariables
. - It calls
FBlueprintEditorUtils::ValidateBlueprintChildVariables
andFBlueprintEditorUtils::MarkBlueprintAsStructurallyModified
. The latter callsFBlueprintCompilationManager::CompileSynchronously
andMarkBlueprintAsModified
.
NewVariables
is used in A LOT of places, so here I will not cover all usages (duplicating, etc).
Here are some places where the array is used when inside FBlueprintEditorUtils::AddMemberVariable
and after it:
UBlueprint::Serialize
FKismetCompilerContext::CreateClassVariablesFromBlueprint
FBlueprintEditorUtils::GetClassVariableList
FKismetCompilerContext::CreateClassVariablesFromBlueprint
will iterate the new variables and call FKismetCompilerContext::CreateVariable
(which calls FKismetCompilerUtilities::CreatePropertyOnScope
and FKismetCompilerUtilities::LinkAddedProperty
) which returns a FProperty
.
These functions are also used to create the variables of our components (and more?)
More on FKismetCompilerUtilities::CreatePropertyOnScope
:
- It does a early check to be sure that no object on the same scope has a name equal to our property name, it will find a fixed name if necessary (see
ValidatedPropertyName
var andFKismetCompilerUtilities::CheckPropertyNameOnScope
). - Once we are sure to have a valid property name, we are handling special extra vars type if the property is a container (Map/Set/Array), see
NewContainerProperty
var. - We are also setting the value of
PropertyScope
PropertyScope
is aFFieldVariant
because if the property is a container the scope is aFField
(FMapProperty
/FSetProperty
/FArrayProperty
) but it we are a regular property the scope is aUStruct
.
- If our property type isn’t a delegate, we call
FKismetCompilerUtilities::CreatePrimitiveProperty
and setNewProperty
with the result- Depending on the
PinCategory
the property is made differently. - For basic types like
Int64
,Float
orString
its always calling the same code but using a different struct property (ex:FInt64Property
/FFloatProperty
/FStrProperty
). - For struct type it has some extra checks but ultimately it uses
FStructProperty
and set theStruct
member variable of the property to theUScriptType* SubType
(casted fromPinSubCategoryObject
) - For an Object/Interface/SoftObject type it gets the
UClass* SubType
fromSelfClass
or fromPinSubCategoryObject
and use the following property structs:FInterfaceProperty
/FSoftObjectProperty
/FWeakObjectProperty
/FObjectProperty
(they are all derived fromFObjectPropertyBase
) - For an Class/SoftClass type it gets the
UClass* SubType
fromPinSubCategoryObject
and use the following property structs:FSoftClassProperty
/FClassProperty
- Depending on the
- If we are a container,
NewProperty
is used for some shady stuff then replaced by the container property (NewMapProperty
/NewSetProperty
/NewArrayProperty
) - Returns the
NewProperty
More on FKismetCompilerUtilities::LinkAddedProperty
:
- It sets the
Next
member var of the new property to be theChildProperties
member var of ourUStruct*
(owner of the property, here aUBlueprintGeneratedClass
) - it sets the
ChildProperties
member var of the structure to the the new property See FField and UStruct for more info on these member vars
Editing a variable type
Slate type picker
The type picker is a
SBlueprintPaletteItem
(with internallySPinTypeSelectorHelper
, made inSBlueprintPaletteItem::Construct
) (this slate widget is used in other scenarios).
When clicking the picker to open the selector UEdGraphSchema_K2::GetVariableTypeTree
is called. This is where all types are added (one by one, or all sub types using GatherPinsImpl::FindStructs
for structs, GatherPinsImpl::FindObjectsAndInterfaces
for objects, classes and interfaces and GatherPinsImpl::FindEnums
for enums)
After selecting a new type in the type picker, SBlueprintPalette::OnVarTypeChanged
is called. Since here we are editing a BP class variable FBlueprintEditorUtils::ChangeMemberVariableType
will be called.
Inside it will get back our var as a FBPVariableDescription
.
For each UBlueprint
that is a child of our Blueprint, or using it as an interface we get all UK2Node
nodes (Get/Set nodes).
This is used later to warn the user that changing the type will break at least 1 node connection.
Then we create a new transaction and call Modify
.
All lot of checks are run for validation and easy transfer/conversion between types.
But eventually the new type is assigned to the variable and FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified
is called on the edited BP and any BP with broken connections. UEdGraphSchema_K2::ReconstructNode
is also called on all previously found nodes.