void USoundWave::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); static FName CompressionQualityFName = FName( TEXT( "CompressionQuality" ) ); static FName StreamingFName = GET_MEMBER_NAME_CHECKED(USoundWave, bStreaming); // Prevent constant re-compression of SoundWave while properties are being changed interactively if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive) { UProperty* PropertyThatChanged = PropertyChangedEvent.Property; // Regenerate on save any compressed sound formats if ( PropertyThatChanged && PropertyThatChanged->GetFName() == CompressionQualityFName ) { InvalidateCompressedData(); FreeResources(); UpdatePlatformData(); MarkPackageDirty(); } else if (PropertyThatChanged && PropertyThatChanged->GetFName() == StreamingFName) { FreeResources(); UpdatePlatformData(); MarkPackageDirty(); } } }
void UCanvasPanelSlot::PostEditChangeChainProperty(struct FPropertyChangedChainEvent& PropertyChangedEvent) { SynchronizeProperties(); static FName AnchorsProperty(TEXT("Anchors")); FEditPropertyChain::TDoubleLinkedListNode* AnchorNode = PropertyChangedEvent.PropertyChain.GetHead()->GetNextNode(); if ( !AnchorNode ) { return; } FEditPropertyChain::TDoubleLinkedListNode* LayoutDataNode = AnchorNode->GetNextNode(); if ( !LayoutDataNode ) { return; } UProperty* AnchorProperty = LayoutDataNode->GetValue(); if ( AnchorProperty && AnchorProperty->GetFName() == AnchorsProperty ) { RebaseLayout(); } Super::PostEditChangeProperty(PropertyChangedEvent); }
/** * Finds a child property node from the provided parent node (does not recurse into grandchildren) * * @param InParentNode The parent node to locate the child from * @param PropertyName The property name to find * @param Index The index of the property if its in an array */ static TSharedPtr<FPropertyNode> FindChildPropertyNode( FPropertyNode& InParentNode, const FString& PropertyName, int32 Index ) { TSharedPtr<FPropertyNode> FoundNode(NULL); // search each child for a property with the provided name for( int32 ChildIndex = 0; ChildIndex < InParentNode.GetNumChildNodes(); ++ChildIndex ) { TSharedPtr<FPropertyNode>& ChildNode = InParentNode.GetChildNode(ChildIndex); UProperty* Property = ChildNode->GetProperty(); if( Property && Property->GetFName() == *PropertyName ) { FoundNode = ChildNode; break; } } // Find the array element. if( FoundNode.IsValid() && Index != INDEX_NONE ) { // The found node is the top array so get its child which is the actual node FoundNode = FoundNode->GetChildNode( Index ); } return FoundNode; }
static UProperty *GetPropertyByNameRecurse( UStruct *InStruct, const FString &TokenString, void ** hContainerPtr, int32 &OutArrayIndex ) { FString FirstToken; FString RemainingTokens; int32 SplitIndex; if ( TokenString.FindChar( '.', SplitIndex ) ) { FirstToken = TokenString.LeftChop( TokenString.Len()-SplitIndex ); RemainingTokens = TokenString.RightChop(SplitIndex+1); } else { FirstToken = TokenString; RemainingTokens = FString(TEXT("")); } //get the array index if there is any int32 ArrayIndex = 0; if ( FirstToken.FindChar( '[', SplitIndex ) ) { FString ArrayIndexString = FirstToken.RightChop( SplitIndex+1 ); ArrayIndexString = ArrayIndexString.LeftChop( 1 ); FDefaultValueHelper::ParseInt( ArrayIndexString, ArrayIndex ); FirstToken = FirstToken.LeftChop( FirstToken.Len()-SplitIndex ); } for (TFieldIterator<UProperty> PropertyIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; FName PropertyName = Property->GetFName(); if ( FirstToken == PropertyName.ToString() ) { if ( RemainingTokens.Len() == 0 ) { check( *hContainerPtr != NULL ); OutArrayIndex = ArrayIndex; return Property; } else { UStructProperty *StructProp = Cast<UStructProperty>(Property); if ( StructProp ) { check( *hContainerPtr != NULL ); *hContainerPtr = Property->ContainerPtrToValuePtr<void>( *hContainerPtr, ArrayIndex ); return GetPropertyByNameRecurse( StructProp->Struct, RemainingTokens, hContainerPtr, OutArrayIndex ); } } } } return NULL; }
UProperty* FindScriptPropertyHelper(UClass* Class, FName PropertyName) { for (TFieldIterator<UProperty> PropertyIt(Class, EFieldIteratorFlags::ExcludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; if (Property->GetFName() == PropertyName) { return Property; } } return NULL; }
void FBlueprintCompileReinstancer::SaveClassFieldMapping(UClass* InClassToReinstance) { check(InClassToReinstance); for (UProperty* Prop = InClassToReinstance->PropertyLink; Prop && (Prop->GetOuter() == InClassToReinstance); Prop = Prop->PropertyLinkNext) { PropertyMap.Add(Prop->GetFName(), Prop); } for (auto Function : TFieldRange<UFunction>(InClassToReinstance, EFieldIteratorFlags::ExcludeSuper)) { FunctionMap.Add(Function->GetFName(),Function); } }
void UEditorLiveStreamingSettings::PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent ) { UProperty* PropertyThatChanged = PropertyChangedEvent.Property; const FName Name = PropertyThatChanged ? PropertyThatChanged->GetFName() : NAME_None; // if( Name == ??? ) // { // ... // } // Save config to file, but only if we are not the build machine since game agnostic settings may put the builder in an unclean state if( !GIsBuildMachine ) { this->SaveConfig(); } }
void ADEPRECATED_VolumeAdaptiveBuilder::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); UProperty* PropertyThatChanged = PropertyChangedEvent.Property; FName PropertyName = PropertyThatChanged != NULL ? PropertyThatChanged->GetFName() : NAME_None; if (PropertyName == GET_MEMBER_NAME_CHECKED(ADEPRECATED_VolumeAdaptiveBuilder, IsVisibleNavigableVolumes) || PropertyName == GET_MEMBER_NAME_CHECKED(ADEPRECATED_VolumeAdaptiveBuilder, IsVisibleBlockedVolumes) || PropertyName == GET_MEMBER_NAME_CHECKED(ADEPRECATED_VolumeAdaptiveBuilder, IsVisibleInGame) ) { for (UDoNNavigationVolumeComponent* volume : NAVVolumeComponents) { if (!volume) continue; if (volume->CanNavigate) volume->SetVisibility(IsVisibleNavigableVolumes); else volume->SetVisibility(IsVisibleBlockedVolumes); volume->SetHiddenInGame(!IsVisibleInGame); } } if (GenerateNavigationVolumes) { ADEPRECATED_VolumeAdaptiveBuilder::ConstructBuilder(); GenerateNavigationVolumes = false; } if (RegenerateNAVNetwork || PropertyName == GET_MEMBER_NAME_CHECKED(ADEPRECATED_VolumeAdaptiveBuilder, DisplayNAVNeighborGraph)) { FlushPersistentDebugLines(GetWorld()); BuildNAVNetwork(); RegenerateNAVNetwork = false; } if (CleanUpAllData) { CleanUp(); CleanUpAllData = false; } }
bool FConfigPropertyCustomColumn::Supports(const TSharedRef< IPropertyTableColumn >& Column, const TSharedRef< IPropertyTableUtilities >& Utilities) const { bool IsSupported = false; if (Column->GetDataSource()->IsValid()) { TSharedPtr< FPropertyPath > PropertyPath = Column->GetDataSource()->AsPropertyPath(); if (PropertyPath.IsValid() && PropertyPath->GetNumProperties() > 0) { const FPropertyInfo& PropertyInfo = PropertyPath->GetRootProperty(); UProperty* Property = PropertyInfo.Property.Get(); IsSupported = Property->GetFName() == TEXT("ExternalProperty"); } } return IsSupported; }
void ULevelStreaming::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { UProperty* OutermostProperty = PropertyChangedEvent.Property; if ( OutermostProperty != NULL ) { const FName PropertyName = OutermostProperty->GetFName(); if (PropertyName == GET_MEMBER_NAME_CHECKED(ULevelStreaming, LevelTransform)) { GetWorld()->UpdateLevelStreaming(); } if (PropertyName == GET_MEMBER_NAME_CHECKED(ULevelStreaming, EditorStreamingVolumes)) { RemoveStreamingVolumeDuplicates(); // Update levels references in each streaming volume for (TActorIterator<ALevelStreamingVolume> It(GetWorld()); It; ++It) { (*It)->UpdateStreamingLevelsRefs(); } } else if (PropertyName == GET_MEMBER_NAME_CHECKED(ULevelStreaming, LevelColor)) { // Make sure the level's Level Color change is applied immediately by reregistering the // components of the actor's in the level if( LoadedLevel != NULL ) { UPackage* Package = LoadedLevel->GetOutermost(); for( TObjectIterator<UActorComponent> It; It; ++It ) { if( It->IsIn( Package ) ) { UActorComponent* ActorComponent = Cast<UActorComponent>( *It ); if( ActorComponent ) { ActorComponent->RecreateRenderState_Concurrent(); } } } } } } Super::PostEditChangeProperty(PropertyChangedEvent); }
void FAIDataProviderValue::GetMatchingProperties(TArray<FName>& MatchingProperties) const { if (DataBinding) { for (UProperty* Prop = DataBinding->GetClass()->PropertyLink; Prop; Prop = Prop->PropertyLinkNext) { if (Prop->HasAnyPropertyFlags(CPF_Edit)) { continue; } if (IsMatchingType(Prop)) { MatchingProperties.Add(Prop->GetFName()); } } } }
static UProperty *GetPropertyByNameRecurse( UStruct *InStruct, const FString &TokenString ) { FString FirstToken; FString RemainingTokens; int32 SplitIndex; if ( TokenString.FindChar( '.', SplitIndex ) ) { FirstToken = TokenString.LeftChop( TokenString.Len()-SplitIndex ); RemainingTokens = TokenString.RightChop(SplitIndex+1); } else { FirstToken = TokenString; RemainingTokens = FString(TEXT("")); } if ( FirstToken.FindChar( '[', SplitIndex ) ) { FirstToken = FirstToken.LeftChop( FirstToken.Len()-SplitIndex ); } for (TFieldIterator<UProperty> PropertyIt(InStruct, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) { UProperty* Property = *PropertyIt; FName PropertyName = Property->GetFName(); if ( FirstToken == PropertyName.ToString() ) { if ( RemainingTokens.Len() == 0 ) { return Property; } else { UStructProperty *StructProp = Cast<UStructProperty>(Property); if ( StructProp ) { return GetPropertyByNameRecurse( StructProp->Struct, RemainingTokens ); } } } } return NULL; }
/** Returns the column property where PropertyName matches the name of the column property. Returns NULL if no match is found or the match is not a supported table property */ UProperty* UDataTable::FindTableProperty(const FName& PropertyName) const { UProperty* Property = NULL; for (TFieldIterator<UProperty> It(RowStruct); It; ++It) { Property = *It; check(Property != NULL); if (PropertyName == Property->GetFName()) { break; } } if (!DataTableUtils::IsSupportedTableProperty(Property)) { Property = NULL; } return Property; }
void UGAAttributesBase::CopyFromStruct(UStruct* StructType, void* StructObject) { for (TFieldIterator<UProperty> StrIt(StructType); StrIt; ++StrIt) { UProperty* Property = *StrIt; if (UStructProperty* StructProp = Cast<UStructProperty>(Property)) { FAFAttributeBase* StructAttr = StructProp->ContainerPtrToValuePtr<FAFAttributeBase>(StructObject); UProperty* ThisProp = FindProperty(FGAAttribute(Property->GetFName())); if (ThisProp) { FAFAttributeBase* ThisAttribute = ThisProp->ContainerPtrToValuePtr<FAFAttributeBase>(this); if (StructAttr && ThisAttribute) { ThisAttribute->CopyFromOther(StructAttr); } } } } }
void RegisterDelegateNet(FKismetFunctionContext& Context, UK2Node_DelegateSet* DelegateNode) { check(DelegateNode); UEdGraphPin* DelegatePin = DelegateNode->GetDelegateOwner(); check(DelegatePin); // Find the property on the specified scope UProperty* BoundProperty = NULL; for (TFieldIterator<UProperty> It(DelegateNode->DelegatePropertyClass, EFieldIteratorFlags::IncludeSuper); It; ++It) { UProperty* Prop = *It; if( Prop->GetFName() == DelegateNode->DelegatePropertyName ) { check(Prop->HasAllPropertyFlags(CPF_BlueprintAssignable)); BoundProperty = Prop; break; } } // Create a term for this property if( BoundProperty != NULL ) { FBPTerminal* Term = new(Context.VariableReferences) FBPTerminal(); Term->CopyFromPin(DelegatePin, DelegatePin->PinName); Term->AssociatedVarProperty = BoundProperty; Context.NetMap.Add(DelegatePin, Term); // Find the context for this term (the object owning the delegate property) FBPTerminal** pContextTerm = Context.NetMap.Find(FEdGraphUtilities::GetNetFromPin(DelegatePin)); if( pContextTerm ) { Term->Context = *pContextTerm; } else { CompilerContext.MessageLog.Error(*FString(*LOCTEXT("FindDynamicallyBoundDelegate_Error", "Couldn't find target for dynamically bound delegate node @@").ToString()), DelegateNode); } } }
FWidgetMaterialHandle GetPropertyValueByPath(void* DataObject, UStruct* PropertySource, const TArray<FName>& PropertyPath, int32 PathIndex ) { if ( DataObject != nullptr && PathIndex < PropertyPath.Num() ) { for ( TFieldIterator<UProperty> PropertyIterator( PropertySource ); PropertyIterator; ++PropertyIterator ) { UProperty* Property = *PropertyIterator; if ( Property != nullptr && Property->GetFName() == PropertyPath[PathIndex] ) { // Only struct properties are relevant for the search. UStructProperty* StructProperty = Cast<UStructProperty>( Property ); if ( StructProperty == nullptr ) { return FWidgetMaterialHandle(); } if ( PathIndex == PropertyPath.Num() - 1 ) { const FName StructName = StructProperty->Struct->GetFName(); if (StructName == TMaterialStructType<FSlateFontInfo>::GetTypeName() || StructName == TMaterialStructType<FSlateBrush>::GetTypeName() || StructName == TMaterialStructType<FFontOutlineSettings>::GetTypeName() ) { FWidgetMaterialHandle Handle(StructName, StructProperty->ContainerPtrToValuePtr<void>(DataObject)); return Handle; } else { return FWidgetMaterialHandle(); } } else { return GetPropertyValueByPath(Property->ContainerPtrToValuePtr<void>( DataObject ), StructProperty->Struct, PropertyPath, PathIndex + 1 ); } } } } return FWidgetMaterialHandle(); }
void UParticleModuleTrailSource::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { InitializeDefaults(); // SourceOffsetCount // SourceOffsetDefaults UProperty* PropertyThatChanged = PropertyChangedEvent.Property; if (PropertyThatChanged) { if (PropertyThatChanged->GetFName() == FName(TEXT("SourceOffsetCount"))) { if (SourceOffsetDefaults.Num() > 0) { if (SourceOffsetDefaults.Num() < SourceOffsetCount) { // Add additional slots SourceOffsetDefaults.InsertZeroed(SourceOffsetDefaults.Num(), SourceOffsetCount - SourceOffsetDefaults.Num()); } else if (SourceOffsetDefaults.Num() > SourceOffsetCount) { // Remove the required slots int32 RemoveIndex = SourceOffsetCount ? (SourceOffsetCount - 1) : 0; SourceOffsetDefaults.RemoveAt(RemoveIndex, SourceOffsetDefaults.Num() - SourceOffsetCount); } } else { if (SourceOffsetCount > 0) { // Add additional slots SourceOffsetDefaults.InsertZeroed(0, SourceOffsetCount); } } } } Super::PostEditChangeProperty(PropertyChangedEvent); }
// Finds a property by name, starting in the specified scope, returning NULL if it's not found UProperty* FKismetCompilerUtilities::FindNamedPropertyInScope(UStruct* Scope, FName PropertyName) { while (Scope != NULL) { for (TFieldIterator<UProperty> It(Scope, EFieldIteratorFlags::IncludeSuper); It; ++It) { UProperty* Property = *It; // If we match by name, and var is not deprecated... if (Property->GetFName() == PropertyName && !Property->HasAllPropertyFlags(CPF_Deprecated)) { return Property; } } // Functions don't automatically check their class when using a field iterator UFunction* Function = Cast<UFunction>(Scope); Scope = (Function != NULL) ? Cast<UStruct>(Function->GetOuter()) : NULL; } return NULL; }
void FOptionalPinManager::RebuildPropertyList(TArray<FOptionalPinFromProperty>& Properties, UStruct* SourceStruct) { // Save the old visibility TMap<FName, bool> OldVisibility; for (auto ExtraPropertyIt = Properties.CreateIterator(); ExtraPropertyIt; ++ExtraPropertyIt) { FOptionalPinFromProperty& PropertyEntry = *ExtraPropertyIt; OldVisibility.Add(PropertyEntry.PropertyName, PropertyEntry.bShowPin); } // Rebuild the property list Properties.Empty(); for (TFieldIterator<UProperty> It(SourceStruct, EFieldIteratorFlags::IncludeSuper); It; ++It) { UProperty* TestProperty = *It; if (CanTreatPropertyAsOptional(TestProperty)) { FOptionalPinFromProperty* Record = new (Properties) FOptionalPinFromProperty; Record->PropertyName = TestProperty->GetFName(); Record->PropertyFriendlyName = UEditorEngine::GetFriendlyName(TestProperty, SourceStruct); Record->PropertyTooltip = TestProperty->GetToolTipText(); // Get the defaults GetRecordDefaults(TestProperty, *Record); // If this is a refresh, propagate the old visibility if (Record->bCanToggleVisibility) { if (bool* pShowHide = OldVisibility.Find(Record->PropertyName)) { Record->bShowPin = *pShowHide; } } } } }
void FTODAssetDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { const IDetailsView& DetailView = DetailLayout.GetDetailsView(); TWeakObjectPtr<UObject> InspectedObject; for (TWeakObjectPtr<UObject> inspObj : DetailView.GetSelectedObjects()) { InspectedObject = inspObj; break; } UTODAsset* TODAsset = Cast<UTODAsset>(InspectedObject.Get()); if (TODAsset) { for (TFieldIterator<UProperty> PropIt(TODAsset->GetClass()); PropIt; ++PropIt) { UProperty* prop = *PropIt; DetailLayout.HideProperty(prop->GetFName()); } } FName CurrentPropertyName = TEXT("SunIntensityCurve");// NAME_None; //if (OnGetCurrentProperty.IsBound()) //{ // CurrentPropertyName = OnGetCurrentProperty.Execute(); //} if (CurrentPropertyName != NAME_None) { TSharedPtr<IPropertyHandle> PropHandle = DetailLayout.GetProperty(CurrentPropertyName); check(PropHandle.IsValid()); IDetailCategoryBuilder& DetailCategoryBuilder = DetailLayout.EditCategory("Property Detail"); DetailCategoryBuilder.AddProperty(PropHandle); } }
void FTODAssetPropertyDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { const IDetailsView& DetailView = DetailLayout.GetDetailsView(); //first find asset we are going to edit. TWeakObjectPtr<UObject> InspectedObject; for (TWeakObjectPtr<UObject> inspObj : DetailView.GetSelectedObjects()) { InspectedObject = inspObj; break; } UTODAsset* TODAsset = Cast<UTODAsset>(InspectedObject.Get()); CurrentTODAsset = Cast<UTODAsset>(InspectedObject.Get()); if (TODAsset) { for (TFieldIterator<UProperty> PropIt(TODAsset->GetClass()); PropIt; ++PropIt) { UProperty* prop = *PropIt; DetailLayout.HideProperty(prop->GetFName()); //PropertyHandles.Add(DetailLayout.GetProperty(prop->GetFName())); UStructProperty* structProp = Cast<UStructProperty>(prop); if (structProp) { FRuntimeFloatCurve* floatCurve = structProp->ContainerPtrToValuePtr<FRuntimeFloatCurve>(TODAsset); if (floatCurve) { TSharedPtr<FTODFloatCurveProperty> tempFloatProp = MakeShareable(new FTODFloatCurveProperty()); tempFloatProp->PropertyHandle = DetailLayout.GetProperty(prop->GetFName()); tempFloatProp->TODAsset = TODAsset; tempFloatProp->CategoryName = tempFloatProp->PropertyHandle->GetMetaData(TEXT("Category")); FloatCurves.Add(tempFloatProp); } } } } IDetailCategoryBuilder& DetailCategoryBuilder = DetailLayout.EditCategory("Property Detail"); FDetailWidgetRow& DetailRow = DetailCategoryBuilder.AddCustomRow(FString("Custom Row")); ////now customize each property //FRuntimeFloatCurve* floatCurve; TSharedPtr<IPropertyHandle> hour = DetailLayout.GetProperty(TEXT("Hour")); DetailCategoryBuilder.AddProperty(hour); IDetailCategoryBuilder& SunCategoryBuilder = DetailLayout.EditCategory("Sun"); IDetailCategoryBuilder& AFCategoryBuilder = DetailLayout.EditCategory("Atmospheric Fog"); IDetailCategoryBuilder& HFCategoryBuilder = DetailLayout.EditCategory("Height Fog"); IDetailCategoryBuilder& PPCategoryBuilder = DetailLayout.EditCategory("Post Process"); IDetailCategoryBuilder& SkyLightCategoryBuilder = DetailLayout.EditCategory("SkyLight"); IDetailCategoryBuilder& MoonCategoryBuilder = DetailLayout.EditCategory("Moon"); for (TSharedPtr<FTODFloatCurveProperty> floatCurves : FloatCurves) { if (floatCurves->CategoryName == FString("Sun")) floatCurves->ConstructWidget(SunCategoryBuilder); if (floatCurves->CategoryName == FString("Atmospheric Fog")) floatCurves->ConstructWidget(AFCategoryBuilder); if (floatCurves->CategoryName == FString("Height Fog")) floatCurves->ConstructWidget(HFCategoryBuilder); if (floatCurves->CategoryName == FString("Post Process")) floatCurves->ConstructWidget(PPCategoryBuilder); if (floatCurves->CategoryName == FString("SkyLight")) floatCurves->ConstructWidget(SkyLightCategoryBuilder); if (floatCurves->CategoryName == FString("Moon")) floatCurves->ConstructWidget(MoonCategoryBuilder); } }
void FPaperTileMapDetailsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) { const TArray< TWeakObjectPtr<UObject> >& SelectedObjects = DetailLayout.GetDetailsView().GetSelectedObjects(); MyDetailLayout = &DetailLayout; FNotifyHook* NotifyHook = DetailLayout.GetPropertyUtilities()->GetNotifyHook(); bool bEditingActor = false; UPaperTileMap* TileMap = nullptr; UPaperTileMapComponent* TileComponent = nullptr; for (int32 ObjectIndex = 0; ObjectIndex < SelectedObjects.Num(); ++ObjectIndex) { UObject* TestObject = SelectedObjects[ObjectIndex].Get(); if (AActor* CurrentActor = Cast<AActor>(TestObject)) { if (UPaperTileMapComponent* CurrentComponent = CurrentActor->FindComponentByClass<UPaperTileMapComponent>()) { bEditingActor = true; TileComponent = CurrentComponent; TileMap = CurrentComponent->TileMap; break; } } else if (UPaperTileMapComponent* TestComponent = Cast<UPaperTileMapComponent>(TestObject)) { TileComponent = TestComponent; TileMap = TestComponent->TileMap; break; } else if (UPaperTileMap* TestTileMap = Cast<UPaperTileMap>(TestObject)) { TileMap = TestTileMap; break; } } TileMapPtr = TileMap; TileMapComponentPtr = TileComponent; IDetailCategoryBuilder& TileMapCategory = DetailLayout.EditCategory("Tile Map"); TAttribute<EVisibility> InternalInstanceVis = TAttribute<EVisibility>::Create(TAttribute<EVisibility>::FGetter::CreateSP(this, &FPaperTileMapDetailsCustomization::GetVisibilityForInstancedOnlyProperties)); if (TileComponent != nullptr) { TileMapCategory .AddCustomRow(LOCTEXT( "TileMapInstancingControlsSearchText", "Edit New Promote Asset")) [ SNew(SVerticalBox) + SVerticalBox::Slot() .Padding(0.0f, 2.0f, 0.0f, 0.0f) .FillHeight(1.0f) .VAlign(VAlign_Center) [ SNew(SHorizontalBox) // Edit button +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::EnterTileMapEditingMode) .Visibility(this, &FPaperTileMapDetailsCustomization::GetNonEditModeVisibility) .Text( LOCTEXT("EditAsset", "Edit") ) .ToolTipText( LOCTEXT("EditAssetToolTip", "Edit this tile map") ) ] // Create new tile map button +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::OnNewButtonClicked) .Visibility(this, &FPaperTileMapDetailsCustomization::GetNewButtonVisiblity) .Text(LOCTEXT("CreateNewInstancedMap", "New")) .ToolTipText( LOCTEXT("CreateNewInstancedMapToolTip", "Create a new tile map") ) ] // Promote to asset button +SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(VAlign_Center) .OnClicked(this, &FPaperTileMapDetailsCustomization::OnPromoteButtonClicked) .Visibility(this, &FPaperTileMapDetailsCustomization::GetVisibilityForInstancedOnlyProperties) .Text(LOCTEXT("PromoteToAsset", "Promote to asset")) .ToolTipText(LOCTEXT("PromoteToAssetToolTip", "Save this tile map as a reusable asset")) ] ] ]; TileMapCategory.AddProperty(GET_MEMBER_NAME_CHECKED(UPaperTileMapComponent, TileMap)); } // Add the layer browser if (TileMap != nullptr) { TAttribute<EVisibility> LayerBrowserVis; LayerBrowserVis.Set(EVisibility::Visible); if (TileComponent != nullptr) { LayerBrowserVis = InternalInstanceVis; } FText TileLayerListText = LOCTEXT("TileLayerList", "Tile layer list"); TileMapCategory.AddCustomRow(TileLayerListText) .Visibility(LayerBrowserVis) [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() [ SNew(STextBlock) .Font(DetailLayout.GetDetailFont()) .Text(TileLayerListText) ] +SVerticalBox::Slot() [ SNew(STileLayerList, TileMap, NotifyHook) ] ]; } // Add all of the properties from the inline tilemap if ((TileComponent != nullptr) && (TileComponent->OwnsTileMap())) { TArray<UObject*> ListOfTileMaps; ListOfTileMaps.Add(TileMap); for (TFieldIterator<UProperty> PropIt(UPaperTileMap::StaticClass()); PropIt; ++PropIt) { UProperty* TestProperty = *PropIt; if (TestProperty->HasAnyPropertyFlags(CPF_Edit)) { FName CategoryName(*TestProperty->GetMetaData(TEXT("Category"))); IDetailCategoryBuilder& Category = DetailLayout.EditCategory(CategoryName); if (IDetailPropertyRow* ExternalRow = Category.AddExternalProperty(ListOfTileMaps, TestProperty->GetFName())) { ExternalRow->Visibility(InternalInstanceVis); } } } } // Make sure the setup category is near the top DetailLayout.EditCategory("Setup"); }
void AActor::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { UProperty* PropertyThatChanged = PropertyChangedEvent.Property; FName PropertyName = PropertyThatChanged != NULL ? PropertyThatChanged->GetFName() : NAME_None; const bool bTransformationChanged = (PropertyName == Name_RelativeLocation || PropertyName == Name_RelativeRotation || PropertyName == Name_RelativeScale3D); // During SIE, allow components to reregistered and reconstructed in PostEditChangeProperty. // This is essential as construction is deferred during spawning / duplication when in SIE. if ((GEditor && GEditor->bIsSimulatingInEditor) || ReregisterComponentsWhenModified()) { // In the Undo case we have an annotation storing information about constructed components and we do not want // to improperly apply out of date changes so we need to skip registration of all blueprint created components // and defer instance components attached to them until after rerun if (CurrentTransactionAnnotation.IsValid()) { UnregisterAllComponents(); TInlineComponentArray<UActorComponent*> Components; GetComponents(Components); Components.Sort([](UActorComponent& A, UActorComponent& B) { if (&B == B.GetOwner()->GetRootComponent()) { return false; } if (USceneComponent* ASC = Cast<USceneComponent>(&A)) { if (ASC->GetAttachParent() == &B) { return false; } } return true; }); bool bRequiresReregister = false; for (UActorComponent* Component : Components) { if (Component->CreationMethod == EComponentCreationMethod::Native) { Component->RegisterComponent(); } else if (Component->CreationMethod == EComponentCreationMethod::Instance) { USceneComponent* SC = Cast<USceneComponent>(Component); if (SC == nullptr || SC == RootComponent || (SC->GetAttachParent() && SC->GetAttachParent()->IsRegistered())) { Component->RegisterComponent(); } else { bRequiresReregister = true; } } else { bRequiresReregister = true; } } RerunConstructionScripts(); if (bRequiresReregister) { ReregisterAllComponents(); } } else { UnregisterAllComponents(); RerunConstructionScripts(); ReregisterAllComponents(); } } // Let other systems know that an actor was moved if (bTransformationChanged) { GEngine->BroadcastOnActorMoved( this ); } if (GetWorld()) { GetWorld()->bDoDelayedUpdateCullDistanceVolumes = true; } FEditorSupportDelegates::UpdateUI.Broadcast(); Super::PostEditChangeProperty(PropertyChangedEvent); }
void UUserDefinedStructEditorData::RecreateDefaultInstance(FString* OutLog) { UStruct* ScriptStruct = GetOwnerStruct(); DefaultStructInstance.Recreate(ScriptStruct); uint8* StructData = DefaultStructInstance.GetStructMemory(); ensure(DefaultStructInstance.IsValid() && DefaultStructInstance.GetStruct() == ScriptStruct); if (DefaultStructInstance.IsValid() && StructData && ScriptStruct) { DefaultStructInstance.SetPackage(ScriptStruct->GetOutermost()); for (TFieldIterator<UProperty> It(ScriptStruct); It; ++It) { UProperty* Property = *It; if (Property) { auto VarDesc = VariablesDescriptions.FindByPredicate(FStructureEditorUtils::FFindByNameHelper<FStructVariableDescription>(Property->GetFName())); if (VarDesc && !VarDesc->CurrentDefaultValue.IsEmpty()) { if (!FBlueprintEditorUtils::PropertyValueFromString(Property, VarDesc->CurrentDefaultValue, StructData)) { const FString Message = FString::Printf(TEXT("Cannot parse value. Property: %s String: \"%s\" ") , (Property ? *Property->GetDisplayNameText().ToString() : TEXT("None")) , *VarDesc->CurrentDefaultValue); UE_LOG(LogClass, Warning, TEXT("UUserDefinedStructEditorData::RecreateDefaultInstance %s Struct: %s "), *Message, *GetPathNameSafe(ScriptStruct)); if (OutLog) { OutLog->Append(Message); } } } } } } }
void AWorldSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { UProperty* PropertyThatChanged = PropertyChangedEvent.Property; if (PropertyThatChanged) { if (PropertyThatChanged->GetFName()==GET_MEMBER_NAME_CHECKED(AWorldSettings,bForceNoPrecomputedLighting) && bForceNoPrecomputedLighting) { FMessageDialog::Open( EAppMsgType::Ok, LOCTEXT("bForceNoPrecomputedLightingIsEnabled", "bForceNoPrecomputedLighting is now enabled, build lighting once to propagate the change (will remove existing precomputed lighting data).")); } else if (PropertyThatChanged->GetFName()==GET_MEMBER_NAME_CHECKED(AWorldSettings,bEnableWorldComposition)) { if (UWorldComposition::EnableWorldCompositionEvent.IsBound()) { bEnableWorldComposition = UWorldComposition::EnableWorldCompositionEvent.Execute(GetWorld(), bEnableWorldComposition); } else { bEnableWorldComposition = false; } } } LightmassSettings.NumIndirectLightingBounces = FMath::Clamp(LightmassSettings.NumIndirectLightingBounces, 0, 100); LightmassSettings.IndirectLightingSmoothness = FMath::Clamp(LightmassSettings.IndirectLightingSmoothness, .25f, 10.0f); LightmassSettings.VolumeLightSamplePlacementScale = FMath::Clamp(LightmassSettings.VolumeLightSamplePlacementScale, .1f, 100.0f); LightmassSettings.IndirectLightingQuality = FMath::Clamp(LightmassSettings.IndirectLightingQuality, .1f, 100.0f); LightmassSettings.StaticLightingLevelScale = FMath::Clamp(LightmassSettings.StaticLightingLevelScale, .001f, 1000.0f); LightmassSettings.EmissiveBoost = FMath::Max(LightmassSettings.EmissiveBoost, 0.0f); LightmassSettings.DiffuseBoost = FMath::Max(LightmassSettings.DiffuseBoost, 0.0f); LightmassSettings.DirectIlluminationOcclusionFraction = FMath::Clamp(LightmassSettings.DirectIlluminationOcclusionFraction, 0.0f, 1.0f); LightmassSettings.IndirectIlluminationOcclusionFraction = FMath::Clamp(LightmassSettings.IndirectIlluminationOcclusionFraction, 0.0f, 1.0f); LightmassSettings.OcclusionExponent = FMath::Max(LightmassSettings.OcclusionExponent, 0.0f); LightmassSettings.FullyOccludedSamplesFraction = FMath::Clamp(LightmassSettings.FullyOccludedSamplesFraction, 0.0f, 1.0f); LightmassSettings.MaxOcclusionDistance = FMath::Max(LightmassSettings.MaxOcclusionDistance, 0.0f); LightmassSettings.EnvironmentIntensity = FMath::Max(LightmassSettings.EnvironmentIntensity, 0.0f); // Ensure texture size is power of two between 512 and 4096. PackedLightAndShadowMapTextureSize = FMath::Clamp<uint32>( FMath::RoundUpToPowerOfTwo( PackedLightAndShadowMapTextureSize ), 512, 4096 ); if (PropertyThatChanged != nullptr && GetWorld() != nullptr && GetWorld()->PersistentLevel->GetWorldSettings() == this) { if (GIsEditor) { GEngine->DeferredCommands.AddUnique(TEXT("UpdateLandscapeSetup")); } if (PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(FHierarchicalSimplification,TransitionScreenSize)) { GEditor->BroadcastHLODTransitionScreenSizeChanged(); } else if (PropertyThatChanged->GetFName() == GET_MEMBER_NAME_CHECKED(AWorldSettings,HierarchicalLODSetup)) { GEditor->BroadcastHLODLevelsArrayChanged(); NumHLODLevels = HierarchicalLODSetup.Num(); } } if (PropertyThatChanged != nullptr && GetWorld() != nullptr && GetWorld()->Scene) { GetWorld()->Scene->UpdateSceneSettings(this); } Super::PostEditChangeProperty(PropertyChangedEvent); }
/** Get array of UProperties that corresponds to columns in the table */ TArray<UProperty*> UDataTable::GetTablePropertyArray(const TArray<const TCHAR*>& Cells, UStruct* InRowStruct, TArray<FString>& OutProblems) { TArray<UProperty*> ColumnProps; // Get list of all expected properties from the struct TArray<FName> ExpectedPropNames = DataTableUtils::GetStructPropertyNames(InRowStruct); // Need at least 2 columns, first column is skipped, will contain row names if(Cells.Num() > 1) { ColumnProps.AddZeroed( Cells.Num() ); // first element always NULL - as first column is row names for (int32 ColIdx = 1; ColIdx < Cells.Num(); ++ColIdx) { const TCHAR* ColumnValue = Cells[ColIdx]; FName PropName = DataTableUtils::MakeValidName(ColumnValue); if(PropName == NAME_None) { OutProblems.Add(FString::Printf(TEXT("Missing name for column %d."), ColIdx)); } else { UProperty* ColumnProp = FindField<UProperty>(InRowStruct, PropName); for (TFieldIterator<UProperty> It(InRowStruct); It && !ColumnProp; ++It) { const auto DisplayName = DataTableUtils::GetPropertyDisplayName(*It, FString()); ColumnProp = (!DisplayName.IsEmpty() && DisplayName == ColumnValue) ? *It : NULL; } // Didn't find a property with this name, problem.. if(ColumnProp == NULL) { OutProblems.Add(FString::Printf(TEXT("Cannot find Property for column '%s' in struct '%s'."), *PropName.ToString(), *InRowStruct->GetName())); } // Found one! else { // Check we don't have this property already if(ColumnProps.Contains(ColumnProp)) { OutProblems.Add(FString::Printf(TEXT("Duplicate column '%s'."), *ColumnProp->GetName())); } // Check we support this property type else if( !DataTableUtils::IsSupportedTableProperty(ColumnProp) ) { OutProblems.Add(FString::Printf(TEXT("Unsupported Property type for struct member '%s'."), *ColumnProp->GetName())); } // Looks good, add to array else { ColumnProps[ColIdx] = ColumnProp; } // Track that we found this one ExpectedPropNames.Remove(ColumnProp->GetFName()); } } } } // Generate warning for any properties in struct we are not filling in for(int32 PropIdx=0; PropIdx < ExpectedPropNames.Num(); PropIdx++) { const UProperty* const ColumnProp = FindField<UProperty>(InRowStruct, ExpectedPropNames[PropIdx]); const FString DisplayName = DataTableUtils::GetPropertyDisplayName(ColumnProp, ExpectedPropNames[PropIdx].ToString()); OutProblems.Add(FString::Printf(TEXT("Expected column '%s' not found in input."), *DisplayName)); } return ColumnProps; }
void UTexture::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { Super::PostEditChangeProperty(PropertyChangedEvent); SetLightingGuid(); // Determine whether any property that requires recompression of the texture, or notification to Materials has changed. bool RequiresNotifyMaterials = false; bool DeferCompressionWasEnabled = false; UProperty* PropertyThatChanged = PropertyChangedEvent.Property; if( PropertyThatChanged ) { static const FName CompressionSettingsName("CompressionSettings"); static const FName LODGroupName("LODGroup"); static const FName DeferCompressionName("DeferCompression"); #if WITH_EDITORONLY_DATA static const FName MaxTextureSizeName("MaxTextureSize"); #endif // #if WITH_EDITORONLY_DATA const FName PropertyName = PropertyThatChanged->GetFName(); if (PropertyName == CompressionSettingsName || PropertyName == LODGroupName) { RequiresNotifyMaterials = true; } else if (PropertyName == DeferCompressionName) { DeferCompressionWasEnabled = DeferCompression; } #if WITH_EDITORONLY_DATA else if (PropertyName == MaxTextureSizeName) { if (MaxTextureSize <= 0) { MaxTextureSize = 0; } else { MaxTextureSize = FMath::Min<int32>(FMath::RoundUpToPowerOfTwo(MaxTextureSize), GetMaximumDimension()); } } #endif // #if WITH_EDITORONLY_DATA bool bPreventSRGB = (CompressionSettings == TC_Alpha || CompressionSettings == TC_Normalmap || CompressionSettings == TC_Masks || CompressionSettings == TC_HDR || CompressionSettings == TC_HDR_Compressed); if(bPreventSRGB && SRGB == true) { SRGB = false; } } else { FMaterialUpdateContext UpdateContext; // Update any material that uses this texture TSet<UMaterial*> BaseMaterialsThatUseThisTexture; for (TObjectIterator<UMaterialInterface> It; It; ++It) { UMaterialInterface* MaterialInterface = *It; if (DoesMaterialUseTexture(MaterialInterface, this)) { UMaterial *Material = MaterialInterface->GetMaterial(); bool MaterialAlreadyCompute = false; BaseMaterialsThatUseThisTexture.Add(Material, &MaterialAlreadyCompute); if (!MaterialAlreadyCompute) { UpdateContext.AddMaterial(Material); if (Material->IsTextureForceRecompileCacheRessource(this)) { Material->UpdateMaterialShaderCacheAndTextureReferences(); } } } } //If the DDC key was different the material is already recompile here RequiresNotifyMaterials = false; } NumCinematicMipLevels = FMath::Max<int32>( NumCinematicMipLevels, 0 ); // Don't update the texture resource if we've turned "DeferCompression" on, as this // would cause it to immediately update as an uncompressed texture if( !DeferCompressionWasEnabled && (PropertyChangedEvent.ChangeType & EPropertyChangeType::Interactive) == 0 ) { // Update the texture resource. This will recache derived data if necessary // which may involve recompressing the texture. UpdateResource(); } // Notify any loaded material instances if changed our compression format if (RequiresNotifyMaterials) { TArray<UMaterialInterface*> MaterialsThatUseThisTexture; // Create a material update context to safely update materials. { FMaterialUpdateContext UpdateContext; // Notify any material that uses this texture TSet<UMaterial*> BaseMaterialsThatUseThisTexture; for (TObjectIterator<UMaterialInterface> It; It; ++It) { UMaterialInterface* MaterialInterface = *It; if (DoesMaterialUseTexture(MaterialInterface,this)) { MaterialsThatUseThisTexture.Add(MaterialInterface); // This is a bit tricky. We want to make sure all materials using this texture are // updated. Materials are always updated. Material instances may also have to be // updated and if they have static permutations their children must be updated // whether they use the texture or not! The safe thing to do is to add the instance's // base material to the update context causing all materials in the tree to update. BaseMaterialsThatUseThisTexture.Add(MaterialInterface->GetMaterial()); } } // Go ahead and update any base materials that need to be. for (TSet<UMaterial*>::TConstIterator It(BaseMaterialsThatUseThisTexture); It; ++It) { UpdateContext.AddMaterial(*It); (*It)->PostEditChange(); } } // Now that all materials and instances have updated send necessary callbacks. for (int32 i = 0; i < MaterialsThatUseThisTexture.Num(); ++i) { FEditorSupportDelegates::MaterialTextureSettingsChanged.Broadcast(MaterialsThatUseThisTexture[i]); } } #if WITH_EDITORONLY_DATA // any texture that is referencing this texture as AssociatedNormalMap needs to be informed { TArray<UTexture*> TexturesThatUseThisTexture; for (TObjectIterator<UTexture> It; It; ++It) { UTexture* Tex = *It; if(Tex != this && Tex->CompositeTexture == this && Tex->CompositeTextureMode != CTM_Disabled) { TexturesThatUseThisTexture.Add(Tex); } } for (int32 i = 0; i < TexturesThatUseThisTexture.Num(); ++i) { TexturesThatUseThisTexture[i]->PostEditChange(); } } #endif }
void FFbxImportUIDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) { CachedDetailBuilder = &DetailBuilder; TArray<TWeakObjectPtr<UObject>> EditingObjects; DetailBuilder.GetObjectsBeingCustomized(EditingObjects); check(EditingObjects.Num() == 1); ImportUI = Cast<UFbxImportUI>(EditingObjects[0].Get()); // Handle mesh category IDetailCategoryBuilder& MeshCategory = DetailBuilder.EditCategory("Mesh", FText::GetEmpty(), ECategoryPriority::Important); IDetailCategoryBuilder& TransformCategory = DetailBuilder.EditCategory("Transform"); TArray<TSharedRef<IPropertyHandle>> CategoryDefaultProperties; TArray<TSharedPtr<IPropertyHandle>> ExtraProperties; // Grab and hide per-type import options TSharedRef<IPropertyHandle> StaticMeshDataProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, StaticMeshImportData)); TSharedRef<IPropertyHandle> SkeletalMeshDataProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, SkeletalMeshImportData)); TSharedRef<IPropertyHandle> AnimSequenceDataProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, AnimSequenceImportData)); DetailBuilder.HideProperty(StaticMeshDataProp); DetailBuilder.HideProperty(SkeletalMeshDataProp); DetailBuilder.HideProperty(AnimSequenceDataProp); MeshCategory.GetDefaultProperties(CategoryDefaultProperties); switch(ImportUI->MeshTypeToImport) { case FBXIT_StaticMesh: CollectChildPropertiesRecursive(StaticMeshDataProp, ExtraProperties); break; case FBXIT_SkeletalMesh: if(ImportUI->bImportMesh) { CollectChildPropertiesRecursive(SkeletalMeshDataProp, ExtraProperties); } else { ImportUI->MeshTypeToImport = FBXIT_Animation; } break; default: break; } EFBXImportType ImportType = ImportUI->MeshTypeToImport; if(ImportUI->OriginalImportType == FBXIT_SkeletalMesh) { TSharedRef<IPropertyHandle> ImportMeshProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, bImportMesh)); ImportMeshProp->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFbxImportUIDetails::ImportMeshToggleChanged)); MeshCategory.AddProperty(ImportMeshProp); } if(ImportType != FBXIT_Animation) { TSharedRef<IPropertyHandle> ImportSkeletalProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, bImportAsSkeletal)); ImportSkeletalProp->SetOnPropertyValueChanged(FSimpleDelegate::CreateSP(this, &FFbxImportUIDetails::MeshImportModeChanged)); MeshCategory.AddProperty(ImportSkeletalProp); } for(TSharedRef<IPropertyHandle> Handle : CategoryDefaultProperties) { FString MetaData = Handle->GetMetaData(TEXT("ImportType")); if(!IsImportTypeMetaDataValid(ImportType, MetaData)) { DetailBuilder.HideProperty(Handle); } } for(TSharedPtr<IPropertyHandle> Handle : ExtraProperties) { FString ImportTypeMetaData = Handle->GetMetaData(TEXT("ImportType")); FString CategoryMetaData = Handle->GetMetaData(TEXT("ImportCategory")); if(IsImportTypeMetaDataValid(ImportType, ImportTypeMetaData)) { // Decide on category if(!CategoryMetaData.IsEmpty()) { // Populate custom categories. IDetailCategoryBuilder& CustomCategory = DetailBuilder.EditCategory(*CategoryMetaData); CustomCategory.AddProperty(Handle); } else { // No override, add to default mesh category IDetailPropertyRow& PropertyRow = MeshCategory.AddProperty(Handle); UProperty* Property = Handle->GetProperty(); if (Property != nullptr) { if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(UFbxStaticMeshImportData, StaticMeshLODGroup)) { SetStaticMeshLODGroupWidget(PropertyRow, Handle); } if (Property->GetFName() == GET_MEMBER_NAME_CHECKED(UFbxStaticMeshImportData, VertexOverrideColor)) { // Cache the VertexColorImportOption property VertexColorImportOptionHandle = StaticMeshDataProp->GetChildHandle(GET_MEMBER_NAME_CHECKED(UFbxStaticMeshImportData, VertexColorImportOption)); PropertyRow.IsEnabled(TAttribute<bool>(this, &FFbxImportUIDetails::GetVertexOverrideColorEnabledState)); } } } } } // Animation Category IDetailCategoryBuilder& AnimCategory = DetailBuilder.EditCategory("Animation", FText::GetEmpty(), ECategoryPriority::Important); CategoryDefaultProperties.Empty(); AnimCategory.GetDefaultProperties(CategoryDefaultProperties); for(TSharedRef<IPropertyHandle> Handle : CategoryDefaultProperties) { FString MetaData = Handle->GetMetaData(TEXT("ImportType")); if(!IsImportTypeMetaDataValid(ImportType, MetaData)) { DetailBuilder.HideProperty(Handle); } } if(ImportType == FBXIT_Animation || ImportType == FBXIT_SkeletalMesh) { ExtraProperties.Empty(); CollectChildPropertiesRecursive(AnimSequenceDataProp, ExtraProperties); // Before we add the import data properties we need to re-add any properties we want to appear above them in the UI TSharedRef<IPropertyHandle> ImportAnimProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, bImportAnimations)); // If we're importing an animation file we really don't need to ask this DetailBuilder.HideProperty(ImportAnimProp); if(ImportType == FBXIT_Animation) { ImportUI->bImportAnimations = true; } else { AnimCategory.AddProperty(ImportAnimProp); } for(TSharedPtr<IPropertyHandle> Handle : ExtraProperties) { FString CategoryMetaData = Handle->GetMetaData(TEXT("ImportCategory")); if(Handle->GetProperty()->GetOuter() == UFbxAnimSequenceImportData::StaticClass() && CategoryMetaData.IsEmpty()) { // Add to default anim category if no override specified IDetailPropertyRow& PropertyRow = AnimCategory.AddProperty(Handle); } else if(ImportType == FBXIT_Animation && !CategoryMetaData.IsEmpty()) { // Override category is available IDetailCategoryBuilder& CustomCategory = DetailBuilder.EditCategory(*CategoryMetaData); CustomCategory.AddProperty(Handle); } } } else { // Hide animation options CategoryDefaultProperties.Empty(); AnimCategory.GetDefaultProperties(CategoryDefaultProperties); for(TSharedRef<IPropertyHandle> Handle : CategoryDefaultProperties) { DetailBuilder.HideProperty(Handle); } } // Material Category IDetailCategoryBuilder& MaterialCategory = DetailBuilder.EditCategory("Material"); if(ImportType == FBXIT_Animation) { // In animation-only mode, hide the material display CategoryDefaultProperties.Empty(); MaterialCategory.GetDefaultProperties(CategoryDefaultProperties); for(TSharedRef<IPropertyHandle> Handle : CategoryDefaultProperties) { DetailBuilder.HideProperty(Handle); } } else { TSharedRef<IPropertyHandle> TextureDataProp = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFbxImportUI, TextureImportData)); DetailBuilder.HideProperty(TextureDataProp); ExtraProperties.Empty(); CollectChildPropertiesRecursive(TextureDataProp, ExtraProperties); for(TSharedPtr<IPropertyHandle> Handle : ExtraProperties) { // We ignore base import data for this window. if(Handle->GetProperty()->GetOuter() == UFbxTextureImportData::StaticClass()) { MaterialCategory.AddProperty(Handle); } } } }
void SDetailsViewBase::UpdatePropertyMapRecursive(FPropertyNode& InNode, FDetailLayoutBuilderImpl& InDetailLayout, FName CurCategory, FComplexPropertyNode* CurObjectNode) { UProperty* ParentProperty = InNode.GetProperty(); UStructProperty* ParentStructProp = Cast<UStructProperty>(ParentProperty); for (int32 ChildIndex = 0; ChildIndex < InNode.GetNumChildNodes(); ++ChildIndex) { TSharedPtr<FPropertyNode> ChildNodePtr = InNode.GetChildNode(ChildIndex); FPropertyNode& ChildNode = *ChildNodePtr; UProperty* Property = ChildNode.GetProperty(); { FObjectPropertyNode* ObjNode = ChildNode.AsObjectNode(); FCategoryPropertyNode* CategoryNode = ChildNode.AsCategoryNode(); if (ObjNode) { // Currently object property nodes do not provide any useful information other than being a container for its children. We do not draw anything for them. // When we encounter object property nodes, add their children instead of adding them to the tree. UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CurCategory, ObjNode); } else if (CategoryNode) { // For category nodes, we just set the current category and recurse through the children UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CategoryNode->GetCategoryName(), CurObjectNode); } else { // Whether or not the property can be visible in the default detail layout bool bVisibleByDefault = IsVisibleStandaloneProperty(ChildNode, InNode); // Whether or not the property is a struct UStructProperty* StructProperty = Cast<UStructProperty>(Property); bool bIsStruct = StructProperty != NULL; static FName ShowOnlyInners("ShowOnlyInnerProperties"); bool bIsChildOfCustomizedStruct = false; bool bIsCustomizedStruct = false; const UStruct* Struct = StructProperty ? StructProperty->Struct : NULL; const UStruct* ParentStruct = ParentStructProp ? ParentStructProp->Struct : NULL; if (Struct || ParentStruct) { FPropertyEditorModule& ParentPlugin = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor"); if (Struct) { bIsCustomizedStruct = ParentPlugin.IsCustomizedStruct(Struct, SharedThis( this ) ); } if (ParentStruct) { bIsChildOfCustomizedStruct = ParentPlugin.IsCustomizedStruct(ParentStruct, SharedThis( this ) ); } } // Whether or not to push out struct properties to their own categories or show them inside an expandable struct bool bPushOutStructProps = bIsStruct && !bIsCustomizedStruct && !ParentStructProp && Property->HasMetaData(ShowOnlyInners); // Is the property edit inline new const bool bIsEditInlineNew = SPropertyEditorEditInline::Supports(&ChildNode, ChildNode.GetArrayIndex()); // Is this a property of an array bool bIsChildOfArray = PropertyEditorHelpers::IsChildOfArray(ChildNode); // Edit inline new properties should be visible by default bVisibleByDefault |= bIsEditInlineNew; // Children of arrays are not visible directly, bVisibleByDefault &= !bIsChildOfArray; FPropertyAndParent PropertyAndParent(*Property, ParentProperty); const bool bIsUserVisible = IsPropertyVisible(PropertyAndParent); // Inners of customized in structs should not be taken into consideration for customizing. They are not designed to be individually customized when their parent is already customized if (!bIsChildOfCustomizedStruct) { // Add any object classes with properties so we can ask them for custom property layouts later ClassesWithProperties.Add(Property->GetOwnerStruct()); } // If there is no outer object then the class is the object root and there is only one instance FName InstanceName = NAME_None; if (CurObjectNode && CurObjectNode->GetParentNode()) { InstanceName = CurObjectNode->GetParentNode()->GetProperty()->GetFName(); } else if (ParentStructProp) { InstanceName = ParentStructProp->GetFName(); } // Do not add children of customized in struct properties or arrays if (!bIsChildOfCustomizedStruct && !bIsChildOfArray) { // Get the class property map FClassInstanceToPropertyMap& ClassInstanceMap = ClassToPropertyMap.FindOrAdd(Property->GetOwnerStruct()->GetFName()); FPropertyNodeMap& PropertyNodeMap = ClassInstanceMap.FindOrAdd(InstanceName); if (!PropertyNodeMap.ParentProperty) { PropertyNodeMap.ParentProperty = CurObjectNode; } else { ensure(PropertyNodeMap.ParentProperty == CurObjectNode); } checkSlow(!PropertyNodeMap.Contains(Property->GetFName())); PropertyNodeMap.Add(Property->GetFName(), ChildNodePtr); } if (bVisibleByDefault && bIsUserVisible && !bPushOutStructProps) { FName CategoryName = CurCategory; // For properties inside a struct, add them to their own category unless they just take the name of the parent struct. // In that case push them to the parent category FName PropertyCatagoryName = FObjectEditorUtils::GetCategoryFName(Property); if (!ParentStructProp || (PropertyCatagoryName != ParentStructProp->Struct->GetFName())) { CategoryName = PropertyCatagoryName; } if (IsPropertyReadOnly(PropertyAndParent)) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } // Add a property to the default category FDetailCategoryImpl& CategoryImpl = InDetailLayout.DefaultCategory(CategoryName); CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName); } bool bRecurseIntoChildren = !bIsChildOfCustomizedStruct // Don't recurse into built in struct children, we already know what they are and how to display them && !bIsCustomizedStruct // Don't recurse into customized structs && !bIsChildOfArray // Do not recurse into arrays, the children are drawn by the array property parent && !bIsEditInlineNew // Edit inline new children are not supported for customization yet && bIsUserVisible // Properties must be allowed to be visible by a user if they are not then their children are not visible either && (!bIsStruct || bPushOutStructProps); // Only recurse into struct properties if they are going to be displayed as standalone properties in categories instead of inside an expandable area inside a category if (bRecurseIntoChildren) { // Built in struct properties or children of arras UpdatePropertyMapRecursive(ChildNode, InDetailLayout, CurCategory, CurObjectNode); } } } } }
void SDetailsViewBase::UpdateSinglePropertyMapRecursive(FPropertyNode& InNode, FDetailLayoutData& LayoutData, FName CurCategory, FComplexPropertyNode* CurObjectNode, bool bEnableFavoriteSystem, bool bUpdateFavoriteSystemOnly) { FDetailLayoutBuilderImpl& DetailLayout = *LayoutData.DetailLayout; UProperty* ParentProperty = InNode.GetProperty(); UStructProperty* ParentStructProp = Cast<UStructProperty>(ParentProperty); for(int32 ChildIndex = 0; ChildIndex < InNode.GetNumChildNodes(); ++ChildIndex) { //Use the original value for each child bool LocalUpdateFavoriteSystemOnly = bUpdateFavoriteSystemOnly; TSharedPtr<FPropertyNode> ChildNodePtr = InNode.GetChildNode(ChildIndex); FPropertyNode& ChildNode = *ChildNodePtr; UProperty* Property = ChildNode.GetProperty(); { FObjectPropertyNode* ObjNode = ChildNode.AsObjectNode(); FCategoryPropertyNode* CategoryNode = ChildNode.AsCategoryNode(); if(ObjNode) { // Currently object property nodes do not provide any useful information other than being a container for its children. We do not draw anything for them. // When we encounter object property nodes, add their children instead of adding them to the tree. UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CurCategory, ObjNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } else if(CategoryNode) { if(!LocalUpdateFavoriteSystemOnly) { FName InstanceName = NAME_None; FName CategoryName = CurCategory; FString CategoryDelimiterString; CategoryDelimiterString.AppendChar(FPropertyNodeConstants::CategoryDelimiterChar); if(CurCategory != NAME_None && CategoryNode->GetCategoryName().ToString().Contains(CategoryDelimiterString)) { // This property is child of another property so add it to the parent detail category FDetailCategoryImpl& CategoryImpl = DetailLayout.DefaultCategory(CategoryName); CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName); } } // For category nodes, we just set the current category and recurse through the children UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CategoryNode->GetCategoryName(), CurObjectNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } else { // Whether or not the property can be visible in the default detail layout bool bVisibleByDefault = IsVisibleStandaloneProperty(ChildNode, InNode); // Whether or not the property is a struct UStructProperty* StructProperty = Cast<UStructProperty>(Property); bool bIsStruct = StructProperty != NULL; static FName ShowOnlyInners("ShowOnlyInnerProperties"); bool bIsChildOfCustomizedStruct = false; bool bIsCustomizedStruct = false; const UStruct* Struct = StructProperty ? StructProperty->Struct : NULL; const UStruct* ParentStruct = ParentStructProp ? ParentStructProp->Struct : NULL; if(Struct || ParentStruct) { FPropertyEditorModule& ParentPlugin = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor"); if(Struct) { bIsCustomizedStruct = ParentPlugin.IsCustomizedStruct(Struct, SharedThis(this)); } if(ParentStruct) { bIsChildOfCustomizedStruct = ParentPlugin.IsCustomizedStruct(ParentStruct, SharedThis(this)); } } // Whether or not to push out struct properties to their own categories or show them inside an expandable struct bool bPushOutStructProps = bIsStruct && !bIsCustomizedStruct && !ParentStructProp && Property->HasMetaData(ShowOnlyInners); // Is the property edit inline new const bool bIsEditInlineNew = ChildNode.HasNodeFlags(EPropertyNodeFlags::ShowInnerObjectProperties) || SPropertyEditorEditInline::Supports(&ChildNode, ChildNode.GetArrayIndex()); // Is this a property of a container property bool bIsChildOfContainer = PropertyEditorHelpers::IsChildOfArray(ChildNode) || PropertyEditorHelpers::IsChildOfSet(ChildNode) || PropertyEditorHelpers::IsChildOfMap(ChildNode); // Edit inline new properties should be visible by default bVisibleByDefault |= bIsEditInlineNew; // Children of arrays are not visible directly, bVisibleByDefault &= !bIsChildOfContainer; FPropertyAndParent PropertyAndParent(*Property, ParentProperty); const bool bIsUserVisible = IsPropertyVisible(PropertyAndParent); // Inners of customized in structs should not be taken into consideration for customizing. They are not designed to be individually customized when their parent is already customized if(!bIsChildOfCustomizedStruct && !LocalUpdateFavoriteSystemOnly) { // Add any object classes with properties so we can ask them for custom property layouts later LayoutData.ClassesWithProperties.Add(Property->GetOwnerStruct()); } // If there is no outer object then the class is the object root and there is only one instance FName InstanceName = NAME_None; if(CurObjectNode && CurObjectNode->GetParentNode()) { InstanceName = CurObjectNode->GetParentNode()->GetProperty()->GetFName(); } else if(ParentStructProp) { InstanceName = ParentStructProp->GetFName(); } // Do not add children of customized in struct properties or arrays if(!bIsChildOfCustomizedStruct && !bIsChildOfContainer && !LocalUpdateFavoriteSystemOnly) { // Get the class property map FClassInstanceToPropertyMap& ClassInstanceMap = LayoutData.ClassToPropertyMap.FindOrAdd(Property->GetOwnerStruct()->GetFName()); FPropertyNodeMap& PropertyNodeMap = ClassInstanceMap.FindOrAdd(InstanceName); if(!PropertyNodeMap.ParentProperty) { PropertyNodeMap.ParentProperty = CurObjectNode; } else { ensure(PropertyNodeMap.ParentProperty == CurObjectNode); } checkSlow(!PropertyNodeMap.Contains(Property->GetFName())); PropertyNodeMap.Add(Property->GetFName(), ChildNodePtr); } bool bCanDisplayFavorite = false; if(bVisibleByDefault && bIsUserVisible && !bPushOutStructProps) { FName CategoryName = CurCategory; // For properties inside a struct, add them to their own category unless they just take the name of the parent struct. // In that case push them to the parent category FName PropertyCatagoryName = FObjectEditorUtils::GetCategoryFName(Property); if(!ParentStructProp || (PropertyCatagoryName != ParentStructProp->Struct->GetFName())) { CategoryName = PropertyCatagoryName; } if(!LocalUpdateFavoriteSystemOnly) { if(IsPropertyReadOnly(PropertyAndParent)) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } // Add a property to the default category FDetailCategoryImpl& CategoryImpl = DetailLayout.DefaultCategory(CategoryName); CategoryImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), InstanceName); } bCanDisplayFavorite = true; if(bEnableFavoriteSystem) { if(bIsCustomizedStruct) { bCanDisplayFavorite = false; //CustomizedStruct child are not categorize since they are under an object but we have to put them in favorite category if the user want to favorite them LocalUpdateFavoriteSystemOnly = true; } else if(ChildNodePtr->IsFavorite()) { //Find or create the favorite category, we have to duplicate favorite property row under this category FString CategoryFavoritesName = TEXT("Favorites"); FName CatFavName = *CategoryFavoritesName; FDetailCategoryImpl& CategoryFavImpl = DetailLayout.DefaultCategory(CatFavName); CategoryFavImpl.SetSortOrder(0); CategoryFavImpl.SetCategoryAsSpecialFavorite(); //Add the property to the favorite FObjectPropertyNode *RootObjectParent = ChildNodePtr->FindRootObjectItemParent(); FName RootInstanceName = NAME_None; if(RootObjectParent != nullptr) { RootInstanceName = RootObjectParent->GetObjectBaseClass()->GetFName(); } if(LocalUpdateFavoriteSystemOnly) { if(IsPropertyReadOnly(PropertyAndParent)) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } else { //If the parent has a condition that is not met, make the child as readonly FDetailLayoutCustomization ParentTmpCustomization; ParentTmpCustomization.PropertyRow = MakeShareable(new FDetailPropertyRow(InNode.AsShared(), CategoryFavImpl.AsShared())); if(ParentTmpCustomization.PropertyRow->GetPropertyEditor()->IsPropertyEditingEnabled() == false) { ChildNode.SetNodeFlags(EPropertyNodeFlags::IsReadOnly, true); } } } //Duplicate the row CategoryFavImpl.AddPropertyNode(ChildNodePtr.ToSharedRef(), RootInstanceName); } if(bIsStruct) { LocalUpdateFavoriteSystemOnly = true; } } } ChildNodePtr->SetCanDisplayFavorite(bCanDisplayFavorite); bool bRecurseIntoChildren = !bIsChildOfCustomizedStruct // Don't recurse into built in struct children, we already know what they are and how to display them && !bIsCustomizedStruct // Don't recurse into customized structs && !bIsChildOfContainer // Do not recurse into containers, the children are drawn by the container property parent && !bIsEditInlineNew // Edit inline new children are not supported for customization yet && bIsUserVisible // Properties must be allowed to be visible by a user if they are not then their children are not visible either && (!bIsStruct || bPushOutStructProps); // Only recurse into struct properties if they are going to be displayed as standalone properties in categories instead of inside an expandable area inside a category if(bRecurseIntoChildren || LocalUpdateFavoriteSystemOnly) { // Built in struct properties or children of arras UpdateSinglePropertyMapRecursive(ChildNode, LayoutData, CurCategory, CurObjectNode, bEnableFavoriteSystem, LocalUpdateFavoriteSystemOnly); } } } } }