void UComponentDelegateBinding::BindDynamicDelegates(AActor* InInstance) const { for(int32 BindIdx=0; BindIdx<ComponentDelegateBindings.Num(); BindIdx++) { const FBlueprintComponentDelegateBinding& Binding = ComponentDelegateBindings[BindIdx]; // Get the function we want to bind UFunction* FunctionToBind = FindField<UFunction>(InInstance->GetClass(), Binding.FunctionNameToBind); // Get the property that points to the component we want to assign to UObjectProperty* ObjProp = FindField<UObjectProperty>(InInstance->GetClass(), Binding.ComponentPropertyName); // If we have both of those.. if(ObjProp != NULL && FunctionToBind != NULL) { // ..see if there is actually a component assigned UActorComponent* Component = Cast<UActorComponent>(ObjProp->GetObjectPropertyValue_InContainer(InInstance)); if(Component != NULL) { // If there is, find the delegate property on it UMulticastDelegateProperty* DelegateProp = FindField<UMulticastDelegateProperty>(Component->GetClass(), Binding.DelegatePropertyName); if(DelegateProp) { // Found that, finally bind function on the actor to this delegate FMulticastScriptDelegate* TargetDelegate = DelegateProp->GetPropertyValuePtr_InContainer(Component); FScriptDelegate Delegate; Delegate.SetFunctionName(Binding.FunctionNameToBind); Delegate.SetObject(InInstance); TargetDelegate->AddUnique(Delegate); } } } } }
void FAnimBlueprintNodeOptionalPinManager::PostRemovedOldPin(FOptionalPinFromProperty& Record, int32 ArrayIndex, UProperty* Property, uint8* PropertyAddress) const { check(PropertyAddress != NULL); check(!Record.bShowPin); if (Record.bCanToggleVisibility && (OldPins != NULL)) { const FString OldPinName = (ArrayIndex != INDEX_NONE) ? FString::Printf(TEXT("%s_%d"), *(Record.PropertyName.ToString()), ArrayIndex) : Record.PropertyName.ToString(); if (UEdGraphPin* OldPin = OldPinMap.FindRef(OldPinName)) { // Pin was visible but it's now hidden UObjectProperty* ObjectProperty = Cast<UObjectProperty>(Property); UAnimGraphNode_AssetPlayerBase* AssetPlayerNode = Cast<UAnimGraphNode_AssetPlayerBase>(BaseNode); if (AssetPlayerNode && ObjectProperty && ObjectProperty->PropertyClass->IsChildOf<UAnimationAsset>()) { // If this is an anim asset pin, dont store a hard reference to the asset in the node, as this ends up referencing things we might not want to load any more UObject* AssetReference = AssetPlayerNode->GetAssetReferenceForPinRestoration(); if (AssetReference) { ObjectProperty->SetObjectPropertyValue(PropertyAddress, AssetReference); } } else { // Convert DefaultValue/DefaultValueObject and push back into the struct FBlueprintEditorUtils::ImportKismetDefaultValueToProperty(OldPin, Property, PropertyAddress, BaseNode); } } } }
UK2Node_VariableGet* UGameplayAbilityGraphSchema::SpawnVariableGetNode(const FVector2D GraphPosition, class UEdGraph* ParentGraph, FName VariableName, UStruct* Source) const { // Perform handling to create custom nodes for some classes UProperty* VarProp = FindField<UProperty>(Source, VariableName); UObjectProperty* ObjProp = Cast<UObjectProperty>(VarProp); if (ObjProp) { UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(ParentGraph); TSubclassOf<UObject> GenClass = Blueprint->GeneratedClass; UObject* ActiveObject = GenClass->GetDefaultObject(); // If the variable is a GameplayEffect create a custom node to show it FString PropType; ObjProp->GetCPPMacroType(PropType); if (PropType == "UGameplayEffect") { UK2Node_GameplayEffectVariable* NodeTemplate = NewObject<UK2Node_GameplayEffectVariable>(); UEdGraphSchema_K2::ConfigureVarNode(NodeTemplate, VariableName, Source, Blueprint); UK2Node_GameplayEffectVariable* VariableNode = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node_GameplayEffectVariable>(ParentGraph, NodeTemplate, GraphPosition); UGameplayEffect* GameplayEffect = Cast<UGameplayEffect>(ObjProp->GetObjectPropertyValue_InContainer(ActiveObject)); if (GameplayEffect) { VariableNode->GameplayEffect = GameplayEffect; } return VariableNode; } } // Couldn't find an appropriate custom node for this variable, use the generic case return Super::SpawnVariableGetNode(GraphPosition, ParentGraph, VariableName, Source); }
USceneComponent* FComponentEditorUtils::GetSceneComponent( UObject* Object, UObject* SubObject /*= NULL*/ ) { if( Object ) { AActor* Actor = Cast<AActor>( Object ); if( Actor ) { if( SubObject ) { if( SubObject->HasAnyFlags( RF_DefaultSubObject ) ) { UObject* ClassDefaultObject = SubObject->GetOuter(); if( ClassDefaultObject ) { for( TFieldIterator<UObjectProperty> ObjPropIt( ClassDefaultObject->GetClass() ); ObjPropIt; ++ObjPropIt ) { UObjectProperty* ObjectProperty = *ObjPropIt; if( SubObject == ObjectProperty->GetObjectPropertyValue_InContainer( ClassDefaultObject ) ) { return CastChecked<USceneComponent>( ObjectProperty->GetObjectPropertyValue_InContainer( Actor ) ); } } } } else if( UBlueprint* Blueprint = Cast<UBlueprint>( SubObject->GetOuter() ) ) { if( Blueprint->SimpleConstructionScript ) { TArray<USCS_Node*> SCSNodes = Blueprint->SimpleConstructionScript->GetAllNodes(); for( int32 SCSNodeIndex = 0; SCSNodeIndex < SCSNodes.Num(); ++SCSNodeIndex ) { USCS_Node* SCS_Node = SCSNodes[ SCSNodeIndex ]; if( SCS_Node && SCS_Node->ComponentTemplate == SubObject ) { return SCS_Node->GetParentComponentTemplate( Blueprint ); } } } } } return Actor->GetRootComponent(); } else if( Object->IsA<USceneComponent>() ) { return CastChecked<USceneComponent>( Object ); } } return NULL; }
/** * Applies settings to an object by finding UProperties by name and calling ImportText * * @param InObject - The object to search for matching properties * @param PropertyChain - The list UProperty names recursively to search through * @param Value - The value to import on the found property */ void ApplyCustomFactorySetting(UObject* InObject, TArray<FString>& PropertyChain, const FString& Value) { const FString PropertyName = PropertyChain[0]; PropertyChain.RemoveAt(0); UProperty* TargetProperty = FindField<UProperty>(InObject->GetClass(), *PropertyName); if (TargetProperty) { if (PropertyChain.Num() == 0) { TargetProperty->ImportText(*Value, TargetProperty->ContainerPtrToValuePtr<uint8>(InObject), 0, InObject); } else { UStructProperty* StructProperty = Cast<UStructProperty>(TargetProperty); UObjectProperty* ObjectProperty = Cast<UObjectProperty>(TargetProperty); UObject* SubObject = NULL; bool bValidPropertyType = true; if (StructProperty) { SubObject = StructProperty->Struct; } else if (ObjectProperty) { SubObject = ObjectProperty->GetObjectPropertyValue(ObjectProperty->ContainerPtrToValuePtr<UObject>(InObject)); } else { //Unknown nested object type bValidPropertyType = false; UE_LOG(LogAutomationEditorCommon, Error, TEXT("ERROR: Unknown nested object type for property: %s"), *PropertyName); } if (SubObject) { ApplyCustomFactorySetting(SubObject, PropertyChain, Value); } else if (bValidPropertyType) { UE_LOG(LogAutomationEditorCommon, Error, TEXT("Error accessing null property: %s"), *PropertyName); } } } else { UE_LOG(LogAutomationEditorCommon, Error, TEXT("ERROR: Could not find factory property: %s"), *PropertyName); } }
//------------------------------------------------------------------------------ static bool ContextMenuTargetProfileImpl::HasAnyExposedComponents(UClass* TargetClass) { for (TFieldIterator<UObjectProperty> PropertyIt(TargetClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UObjectProperty* ObjectProperty = *PropertyIt; if (!ObjectProperty->HasAnyPropertyFlags(CPF_BlueprintVisible) || !ObjectProperty->PropertyClass->IsChildOf<UActorComponent>()) { continue; } return true; } return false; }
void GetEditableComponents( const AActor* InActor, TArray<UActorComponent*>& OutEditableComponents ) { for( TFieldIterator<UObjectProperty> PropIt(InActor->GetClass(), EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt ) { UObjectProperty* ObjectProp = *PropIt; if( ObjectProp->HasAnyPropertyFlags(CPF_Edit) ) { UObject* ObjPtr = ObjectProp->GetObjectPropertyValue(ObjectProp->ContainerPtrToValuePtr<void>(InActor)); if( ObjPtr && ObjPtr->IsA<UActorComponent>() ) { OutEditableComponents.Add( CastChecked<UActorComponent>( ObjPtr ) ); } } } }
FComponentVisualizer::FPropertyNameAndIndex FComponentVisualizer::GetComponentPropertyName(const UActorComponent* Component) { if (Component) { const AActor* CompOwner = Component->GetOwner(); if (CompOwner) { // Iterate over UObject* fields of this actor UClass* ActorClass = CompOwner->GetClass(); for (TFieldIterator<UObjectProperty> It(ActorClass); It; ++It) { // See if this property points to the component in question UObjectProperty* ObjectProp = *It; for (int32 Index = 0; Index < ObjectProp->ArrayDim; ++Index) { UObject* Object = ObjectProp->GetObjectPropertyValue(ObjectProp->ContainerPtrToValuePtr<void>(CompOwner, Index)); if (Object == Component) { // It does! Return this name return FPropertyNameAndIndex(ObjectProp->GetFName(), Index); } } } // If nothing found, look in TArray<UObject*> fields for (TFieldIterator<UArrayProperty> It(ActorClass); It; ++It) { UArrayProperty* ArrayProp = *It; if (UObjectProperty* InnerProp = Cast<UObjectProperty>(It->Inner)) { FScriptArrayHelper ArrayHelper(ArrayProp, ArrayProp->ContainerPtrToValuePtr<void>(CompOwner)); for (int32 Index = 0; Index < ArrayHelper.Num(); ++Index) { UObject* Object = InnerProp->GetObjectPropertyValue(ArrayHelper.GetRawPtr(Index)); if (Object == Component) { return FPropertyNameAndIndex(ArrayProp->GetFName(), Index); } } } } } } // Didn't find actor property referencing this component return FPropertyNameAndIndex(); }
FName FComponentEditorUtils::FindVariableNameGivenComponentInstance(UActorComponent* ComponentInstance) { check(ComponentInstance != nullptr); // First see if the name just works if (AActor* OwnerActor = ComponentInstance->GetOwner()) { UClass* OwnerActorClass = OwnerActor->GetClass(); if (UObjectProperty* TestProperty = FindField<UObjectProperty>(OwnerActorClass, ComponentInstance->GetFName())) { if (ComponentInstance->GetClass()->IsChildOf(TestProperty->PropertyClass)) { return TestProperty->GetFName(); } } } // Name mismatch, try finding a differently named variable pointing to the the component (the mismatch should only be possible for native components) if (UActorComponent* Archetype = Cast<UActorComponent>(ComponentInstance->GetArchetype())) { if (AActor* OwnerActor = Archetype->GetOwner()) { UClass* OwnerClass = OwnerActor->GetClass(); AActor* OwnerCDO = CastChecked<AActor>(OwnerClass->GetDefaultObject()); check(OwnerCDO->HasAnyFlags(RF_ClassDefaultObject)); for (TFieldIterator<UObjectProperty> PropIt(OwnerClass, EFieldIteratorFlags::IncludeSuper); PropIt; ++PropIt) { UObjectProperty* TestProperty = *PropIt; if (Archetype->GetClass()->IsChildOf(TestProperty->PropertyClass)) { void* TestPropertyInstanceAddress = TestProperty->ContainerPtrToValuePtr<void>(OwnerCDO); UObject* ObjectPointedToByProperty = TestProperty->GetObjectPropertyValue(TestPropertyInstanceAddress); if (ObjectPointedToByProperty == Archetype) { // This property points to the component archetype, so it's an anchor even if it was named wrong return TestProperty->GetFName(); } } } } } return NAME_None; }
void FAnimBlueprintNodeOptionalPinManager::PostInitNewPin(UEdGraphPin* Pin, FOptionalPinFromProperty& Record, int32 ArrayIndex, UProperty* Property, uint8* PropertyAddress) const { check(PropertyAddress != NULL); check(Record.bShowPin); if (OldPins == NULL) { // Initial construction of a visible pin; copy values from the struct FBlueprintEditorUtils::ExportPropertyToKismetDefaultValue(Pin, Property, PropertyAddress, BaseNode); } else if (Record.bCanToggleVisibility) { if (UEdGraphPin* OldPin = OldPinMap.FindRef(Pin->PinName)) { // Was already visible } else { // Showing a pin that was previously hidden, during a reconstruction UObjectProperty* ObjectProperty = Cast<UObjectProperty>(Property); UAnimGraphNode_AssetPlayerBase* AssetPlayerNode = Cast<UAnimGraphNode_AssetPlayerBase>(BaseNode); const bool bIsAnimationAsset = AssetPlayerNode != nullptr && ObjectProperty != nullptr && ObjectProperty->PropertyClass->IsChildOf<UAnimationAsset>(); // Store the old reference to an animation asset in the node's asset reference if (bIsAnimationAsset) { AssetPlayerNode->SetAssetReferenceForPinRestoration(ObjectProperty->GetObjectPropertyValue(PropertyAddress)); } // Convert the struct property into DefaultValue/DefaultValueObject FBlueprintEditorUtils::ExportPropertyToKismetDefaultValue(Pin, Property, PropertyAddress, BaseNode); // clear the asset reference on the node if (bIsAnimationAsset) { ObjectProperty->SetObjectPropertyValue(PropertyAddress, nullptr); } } } }
//------------------------------------------------------------------------------ static FBlueprintActionFilter BlueprintActionMenuUtilsImpl::MakeCallOnMemberFilter(FBlueprintActionFilter const& MainMenuFilter) { FBlueprintActionFilter CallOnMemberFilter; CallOnMemberFilter.Context = MainMenuFilter.Context; CallOnMemberFilter.PermittedNodeTypes.Add(UK2Node_CallFunction::StaticClass()); CallOnMemberFilter.AddRejectionTest(FBlueprintActionFilter::FRejectionTestDelegate::CreateStatic(IsUnBoundSpawner)); const UBlueprintEditorSettings* BlueprintSettings = GetDefault<UBlueprintEditorSettings>(); // instead of looking for "ExposeFunctionCategories" on component properties, // we just expose functions for all components, but we still need to check // for "ExposeFunctionCategories" on any non-component properties... if (BlueprintSettings->bExposeAllMemberComponentFunctions) { CallOnMemberFilter.AddRejectionTest(FBlueprintActionFilter::FRejectionTestDelegate::CreateStatic(IsUnexposedNonComponentAction)); } else { CallOnMemberFilter.AddRejectionTest(FBlueprintActionFilter::FRejectionTestDelegate::CreateStatic(IsUnexposedMemberAction)); } for (UClass const* TargetClass : MainMenuFilter.TargetClasses) { for (TFieldIterator<UObjectProperty> PropertyIt(TargetClass, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UObjectProperty* ObjectProperty = *PropertyIt; if (!ObjectProperty->HasAnyPropertyFlags(CPF_BlueprintVisible) || !(ObjectProperty->PropertyClass->IsChildOf<UActorComponent>() || ObjectProperty->HasMetaData(FBlueprintMetadata::MD_ExposeFunctionCategories))) { continue; } CallOnMemberFilter.Context.SelectedObjects.Add(ObjectProperty); } } return CallOnMemberFilter; }
bool ANUTActor::NotifyControlMessage(UNetConnection* Connection, uint8 MessageType, FInBunch& Bunch) { bool bHandledMessage = false; if (MessageType == NMT_NUTControl) { uint8 CmdType = 0; FString Command; FNetControlMessage<NMT_NUTControl>::Receive(Bunch, CmdType, Command); // Console command if (CmdType == ENUTControlCommand::Command_NoResult || CmdType == ENUTControlCommand::Command_SendResult) { UE_LOG(LogUnitTest, Log, TEXT("NMT_NUTControl: Executing command: %s"), *Command); FStringOutputDevice CmdResult; CmdResult.SetAutoEmitLineTerminator(true); bool bCmdSuccess = false; bCmdSuccess = GEngine->Exec(GetWorld(), *Command, CmdResult); UE_LOG(LogUnitTest, Log, TEXT("NMT_NUTControl: Command result: %s"), *CmdResult); bool bSendResult = CmdType == ENUTControlCommand::Command_SendResult; if (bSendResult) { uint8 ReturnCmdType = (bCmdSuccess ? ENUTControlCommand::CommandResult_Success : ENUTControlCommand::CommandResult_Failed); FNetControlMessage<NMT_NUTControl>::Send(Connection, ReturnCmdType, CmdResult); } } // Console command result else if (CmdType == ENUTControlCommand::CommandResult_Failed || CmdType == ENUTControlCommand::CommandResult_Success) { bool bCmdSuccess = CmdType == ENUTControlCommand::CommandResult_Success; if (bCmdSuccess) { UE_LOG(LogUnitTest, Log, TEXT("NMT_NUTControl: Got command result:")); UE_LOG(LogUnitTest, Log, TEXT("%s"), *Command); } else { UE_LOG(LogUnitTest, Log, TEXT("NMT_NUTControl: Failed to execute command")); } } // Ping request else if (CmdType == ENUTControlCommand::Ping) { uint8 TempCmdType = ENUTControlCommand::Pong; FString Dud; FNetControlMessage<NMT_NUTControl>::Send(Connection, TempCmdType, Dud); } // Pong reply - this should only be implemented by custom unit tests; hence the assert else if (CmdType == ENUTControlCommand::Pong) { UNIT_ASSERT(false); } // Custom implemented events, with the result triggered through 'NotifyEvent' else if (CmdType == ENUTControlCommand::WatchEvent) { // NOTE: Only the last NetConnection to request a WatchEvent, will receive notifications EventWatcher = Connection; // Watch for the end of seamless travel if (Command == TEXT("SeamlessTravelEnd")) { FCoreUObjectDelegates::PostLoadMap.AddStatic(&ANUTActor::NotifyPostLoadMap); } } // Event watch notification - should only be implemented by custom unit tests else if (CmdType == ENUTControlCommand::NotifyEvent) { UNIT_ASSERT(false); } // Create an actor instance (the 'summon' console command, doesn't work without a cheat manager) else if (CmdType == ENUTControlCommand::Summon) { const TCHAR* Cmd = *Command; FString SpawnClassName = FParse::Token(Cmd, false); bool bForceBeginPlay = FParse::Param(Cmd, TEXT("ForceBeginPlay")); // Hack specifically for getting the GameplayDebugger working - think the mainline code is broken bool bGameplayDebuggerHack = FParse::Param(Cmd, TEXT("GameplayDebuggerHack")); UClass* SpawnClass = FindObject<UClass>(NULL, *SpawnClassName); if (SpawnClass != NULL) { FActorSpawnParameters SpawnParms; SpawnParms.Owner = GetOwner(); AActor* NewActor = GetWorld()->SpawnActor<AActor>(SpawnClass, SpawnParms); if (NewActor != NULL) { UE_LOG(LogUnitTest, Log, TEXT("Successfully summoned actor of class '%s'"), *SpawnClassName); if (bForceBeginPlay && !NewActor->HasActorBegunPlay()) { UE_LOG(LogUnitTest, Log, TEXT("Forcing call to 'BeginPlay' on newly spawned actor.")); NewActor->BeginPlay(); } if (bGameplayDebuggerHack) { // Assign the LocalPlayerOwner property, to the PC owning this NUTActor, using reflection (to avoid dependency) UObjectProperty* LocalPlayerOwnerProp = FindField<UObjectProperty>(NewActor->GetClass(), TEXT("LocalPlayerOwner")); if (LocalPlayerOwnerProp != NULL) { LocalPlayerOwnerProp->SetObjectPropertyValue( LocalPlayerOwnerProp->ContainerPtrToValuePtr<UObject*>(NewActor), GetOwner()); } else { UE_LOG(LogUnitTest, Log, TEXT("WARNING: Failed to find 'LocalPlayerOwner' property. Unit test broken.")); } // Also hack-disable ticking, so that the replicator doesn't spawn a second replicator NewActor->SetActorTickEnabled(false); } } else { UE_LOG(LogUnitTest, Log, TEXT("SpawnActor failed for class '%s'"), *Command); } } else { UE_LOG(LogUnitTest, Log, TEXT("Could not find actor class '%s'"), *Command); } } // Suspend the game, until a resume request is received (used for giving time, to attach a debugger) else if (CmdType == ENUTControlCommand::SuspendProcess) { UE_LOG(LogUnitTest, Log, TEXT("Suspend start.")); // Setup a named pipe, to monitor for the resume request FString ResumePipeName = FString::Printf(TEXT("%s%u"), NUT_SUSPEND_PIPE, FPlatformProcess::GetCurrentProcessId()); FPlatformNamedPipe ResumePipe; bool bPipeCreated = ResumePipe.Create(ResumePipeName, true, false); if (bPipeCreated) { if (!ResumePipe.OpenConnection()) { UE_LOG(LogUnitTest, Log, TEXT("WARNING: Failed to open pipe connection.")); } } else { UE_LOG(LogUnitTest, Log, TEXT("WARNING: Failed to create resume pipe.")); } // Spin/sleep (effectively suspended) until a resume request is received while (true) { if (bPipeCreated && ResumePipe.IsReadyForRW()) { int32 ResumeVal = 0; if (ResumePipe.ReadInt32(ResumeVal) && !!ResumeVal) { UE_LOG(LogUnitTest, Log, TEXT("Got resume request.")); break; } } FPlatformProcess::Sleep(1.0f); } ResumePipe.Destroy(); UE_LOG(LogUnitTest, Log, TEXT("Suspend end.")); } bHandledMessage = true; } return bHandledMessage; }
void SKismetInspector::UpdateFromObjects(const TArray<UObject*>& PropertyObjects, struct FKismetSelectionInfo& SelectionInfo, const FShowDetailsOptions& Options) { // If we're using the unified blueprint editor, there's not an explicit point where // we ender a kind of component editing mode, so instead, just look at what we're selecting. // If we select a component, then enable the customization. if ( GetDefault<UEditorExperimentalSettings>()->bUnifiedBlueprintEditor ) { bool bEnableComponentCustomization = false; TSharedPtr<FBlueprintEditor> BlueprintEditor = BlueprintEditorPtr.Pin(); if ( BlueprintEditor.IsValid() ) { if ( BlueprintEditor->CanAccessComponentsMode() ) { for ( UObject* PropertyObject : PropertyObjects ) { if ( PropertyObject->IsA<UActorComponent>() ) { bEnableComponentCustomization = true; break; } } } } EnableComponentDetailsCustomization(bEnableComponentCustomization); } PropertyView->OnFinishedChangingProperties().Clear(); PropertyView->OnFinishedChangingProperties().Add( UserOnFinishedChangingProperties ); if (!Options.bForceRefresh) { // Early out if the PropertyObjects and the SelectedObjects are the same bool bEquivalentSets = (PropertyObjects.Num() == SelectedObjects.Num()); if (bEquivalentSets) { // Verify the elements of the sets are equivalent for (int32 i = 0; i < PropertyObjects.Num(); i++) { if (PropertyObjects[i] != SelectedObjects[i].Get()) { bEquivalentSets = false; break; } } } if (bEquivalentSets) { return; } } // Proceed to update SelectedObjects.Empty(); for (auto ObjectIt = PropertyObjects.CreateConstIterator(); ObjectIt; ++ObjectIt) { if (UObject* Object = *ObjectIt) { if (!Object->IsValidLowLevel()) { ensureMsg(false, TEXT("Object in KismetInspector is invalid, see TTP 281915")); continue; } SelectedObjects.Add(Object); if (USCS_Node* SCSNode = Cast<USCS_Node>(Object)) { // Edit the component template UActorComponent* NodeComponent = SCSNode->ComponentTemplate; if (NodeComponent != NULL) { SelectionInfo.ObjectsForPropertyEditing.Add(NodeComponent); SelectionInfo.EditableComponentTemplates.Add(NodeComponent); } } else if (UK2Node* K2Node = Cast<UK2Node>(Object)) { // Edit the component template if it exists if (UActorComponent* Template = K2Node->GetTemplateFromNode()) { SelectionInfo.ObjectsForPropertyEditing.Add(Template); SelectionInfo.EditableComponentTemplates.Add(Template); } // See if we should edit properties of the node if (K2Node->ShouldShowNodeProperties()) { SelectionInfo.ObjectsForPropertyEditing.Add(Object); } } else if (UActorComponent* ActorComponent = Cast<UActorComponent>(Object)) { AActor* Owner = ActorComponent->GetOwner(); if(Owner != NULL && Owner->HasAnyFlags(RF_ClassDefaultObject)) { // We're editing a component that's owned by a CDO, so set the CDO to the property editor (so that propagation works) and then filter to just the component property that we want to edit SelectionInfo.ObjectsForPropertyEditing.AddUnique(Owner); SelectionInfo.EditableComponentTemplates.Add(ActorComponent); } else { // We're editing a component that exists outside of a CDO, so just edit the component instance directly SelectionInfo.ObjectsForPropertyEditing.AddUnique(ActorComponent); } } else { // Editing any UObject* SelectionInfo.ObjectsForPropertyEditing.AddUnique(Object); } } } // By default, no property filtering SelectedObjectProperties.Empty(); // Add to the property filter list for any editable component templates if(SelectionInfo.EditableComponentTemplates.Num()) { for(auto CompIt = SelectionInfo.EditableComponentTemplates.CreateIterator(); CompIt; ++CompIt) { UActorComponent* EditableComponentTemplate = *CompIt; check(EditableComponentTemplate != NULL); // Add all properties belonging to the component template class for(TFieldIterator<UProperty> PropIt(EditableComponentTemplate->GetClass()); PropIt; ++PropIt) { UProperty* Property = *PropIt; check(Property != NULL); AddPropertiesRecursive(Property); } // Attempt to locate a matching property for the current component template for(auto ObjIt = SelectionInfo.ObjectsForPropertyEditing.CreateIterator(); ObjIt; ++ObjIt) { UObject* Object = *ObjIt; check(Object != NULL); if(Object != EditableComponentTemplate) { for(TFieldIterator<UObjectProperty> ObjPropIt(Object->GetClass()); ObjPropIt; ++ObjPropIt) { UObjectProperty* ObjectProperty = *ObjPropIt; check(ObjectProperty != NULL); // If the property value matches the current component template, add it as a selected property for filtering if(EditableComponentTemplate == ObjectProperty->GetObjectPropertyValue_InContainer(Object)) { SelectedObjectProperties.Add(ObjectProperty); } } } } } } PropertyViewTitle = Options.ForcedTitle; bShowComponents = Options.bShowComponents; // Update our context-sensitive editing widget ContextualEditingBorderWidget->SetContent( MakeContextualEditingWidget(SelectionInfo, Options) ); }