// Copyright Epic Games, Inc. All Rights Reserved. #include "Animation/WidgetMaterialTrackUtilities.h" #include "UObject/ObjectMacros.h" #include "UObject/UnrealType.h" #include "Materials/MaterialInterface.h" #include "Styling/SlateBrush.h" #include "Components/Widget.h" #include "Fonts/SlateFontInfo.h" template struct TMaterialStructType { static FName GetTypeName() { static_assert(!sizeof(T), "TMaterialStructType trait must be specialized for this type."); return NAME_None; } }; template<> struct TMaterialStructType { static FName GetTypeName() { static const FName TypeName = "SlateBrush"; return TypeName; } static FString GetPropertyName() { // Note: usign a custom name here. FSlateBrush::ResourceObject is not a descriptive name return NSLOCTEXT("WidgetMaterialTrackUtilities", "BrushMaterialName", "Brush Material").ToString(); } static UMaterialInterface* GetMaterial(void* Data) { FSlateBrush* Brush = (FSlateBrush*)Data; return Cast(Brush->GetResourceObject()); } static void SetMaterial(void* Data, UMaterialInterface* Material) { FSlateBrush* Brush = (FSlateBrush*)Data; Brush->SetResourceObject(Material); } }; template<> struct TMaterialStructType { static FName GetTypeName() { static const FName TypeName = "SlateFontInfo"; return TypeName; } static FString GetPropertyName() { return GET_MEMBER_NAME_STRING_CHECKED(FSlateFontInfo, FontMaterial); } static UMaterialInterface* GetMaterial(void* Data) { FSlateFontInfo* Font = (FSlateFontInfo*)Data; return Cast(Font->FontMaterial); } static void SetMaterial(void* Data, UMaterialInterface* Material) { FSlateFontInfo* Font = (FSlateFontInfo*)Data; Font->FontMaterial = Material; } }; template<> struct TMaterialStructType { static FName GetTypeName() { static const FName TypeName = "FontOutlineSettings"; return TypeName; } static FString GetPropertyName() { return GET_MEMBER_NAME_STRING_CHECKED(FFontOutlineSettings, OutlineMaterial); } static UMaterialInterface* GetMaterial(void* Data) { FFontOutlineSettings* OutlineSettings = (FFontOutlineSettings*)Data; return Cast(OutlineSettings->OutlineMaterial); } static void SetMaterial(void* Data, UMaterialInterface* Material) { FFontOutlineSettings* OutlineSettings = (FFontOutlineSettings*)Data; OutlineSettings->OutlineMaterial = Material; } }; UMaterialInterface* FWidgetMaterialHandle::GetMaterial() const { if(TypeName == TMaterialStructType::GetTypeName()) { return TMaterialStructType::GetMaterial(Data); } else if(TypeName == TMaterialStructType::GetTypeName()) { return TMaterialStructType::GetMaterial(Data); } else if(TypeName == TMaterialStructType::GetTypeName()) { return TMaterialStructType::GetMaterial(Data); } else { return nullptr; } } void FWidgetMaterialHandle::SetMaterial(UMaterialInterface* InMaterial, UWidget* OwnerWidget) { if(TypeName == TMaterialStructType::GetTypeName()) { TMaterialStructType::SetMaterial(Data, InMaterial); } else if(TypeName == TMaterialStructType::GetTypeName()) { TMaterialStructType::SetMaterial(Data, InMaterial); } else if(TypeName == TMaterialStructType::GetTypeName()) { TMaterialStructType::SetMaterial(Data, InMaterial); } TSharedPtr RawWidget = OwnerWidget->GetCachedWidget(); if (RawWidget.IsValid()) { RawWidget->Invalidate(EInvalidateWidget::LayoutAndVolatility); OwnerWidget->SynchronizeProperties(); } } FWidgetMaterialHandle GetPropertyValueByPath(void* DataObject, UStruct* PropertySource, TArrayView PropertyPath, int32 PathIndex ) { if ( DataObject != nullptr && PathIndex < PropertyPath.Num() ) { for ( TFieldIterator PropertyIterator( PropertySource ); PropertyIterator; ++PropertyIterator ) { FProperty* Property = *PropertyIterator; if ( Property != nullptr && Property->GetFName() == PropertyPath[PathIndex] ) { // Only struct properties are relevant for the search. FStructProperty* StructProperty = CastField( Property ); if ( StructProperty == nullptr ) { return FWidgetMaterialHandle(); } if ( PathIndex == PropertyPath.Num() - 1 ) { const FName StructName = StructProperty->Struct->GetFName(); if (StructName == TMaterialStructType::GetTypeName() || StructName == TMaterialStructType::GetTypeName() || StructName == TMaterialStructType::GetTypeName() ) { FWidgetMaterialHandle Handle(StructName, StructProperty->ContainerPtrToValuePtr(DataObject)); return Handle; } else { return FWidgetMaterialHandle(); } } else { return GetPropertyValueByPath(Property->ContainerPtrToValuePtr( DataObject ), StructProperty->Struct, PropertyPath, PathIndex + 1 ); } } } } return FWidgetMaterialHandle(); } FWidgetMaterialHandle WidgetMaterialTrackUtilities::GetMaterialHandle(UWidget* Widget, TArrayView BrushPropertyNamePath) { return GetPropertyValueByPath(Widget, Widget->GetClass(), BrushPropertyNamePath, 0); } void GetMaterialBrushPropertyPathsRecursive(void* DataObject, UStruct* PropertySource, TArray& PropertyPath, TArray& MaterialBrushPropertyPaths) { if ( DataObject != nullptr ) { for ( TFieldIterator PropertyIterator( PropertySource ); PropertyIterator; ++PropertyIterator ) { FProperty* Property = *PropertyIterator; if ( Property != nullptr && Property->HasAnyPropertyFlags( CPF_Deprecated ) == false ) { PropertyPath.Add( Property ); FStructProperty* StructProperty = CastField( Property ); if ( StructProperty != nullptr ) { const FName StructName = StructProperty->Struct->GetFName(); void* Data = Property->ContainerPtrToValuePtr(DataObject); UMaterialInterface* MaterialInterface = nullptr; FString PropertyName; if(StructName == TMaterialStructType::GetTypeName()) { MaterialInterface = TMaterialStructType::GetMaterial(Data); PropertyName = TMaterialStructType::GetPropertyName(); } else if(StructName == TMaterialStructType::GetTypeName()) { MaterialInterface =TMaterialStructType::GetMaterial(Data); PropertyName = TMaterialStructType::GetPropertyName(); } else if(StructName == TMaterialStructType::GetTypeName()) { MaterialInterface =TMaterialStructType::GetMaterial(Data); PropertyName = TMaterialStructType::GetPropertyName(); } if(MaterialInterface) { MaterialBrushPropertyPaths.Emplace(PropertyPath, PropertyName); } GetMaterialBrushPropertyPathsRecursive( StructProperty->ContainerPtrToValuePtr( DataObject ), StructProperty->Struct, PropertyPath, MaterialBrushPropertyPaths); } PropertyPath.RemoveAt( PropertyPath.Num() - 1 ); } } } } void WidgetMaterialTrackUtilities::GetMaterialBrushPropertyPaths( UWidget* Widget, TArray& MaterialBrushPropertyPaths ) { TArray PropertyPath; GetMaterialBrushPropertyPathsRecursive( Widget, Widget->GetClass(), PropertyPath, MaterialBrushPropertyPaths ); } FName WidgetMaterialTrackUtilities::GetTrackNameFromPropertyNamePath( TArrayView PropertyNamePath ) { if ( PropertyNamePath.Num() == 0 ) { return FName(); } FString TrackName = PropertyNamePath[0].ToString(); for ( int32 i = 1; i < PropertyNamePath.Num(); i++ ) { TrackName.AppendChar( '.' ); TrackName.Append( PropertyNamePath[i].ToString() ); } return FName( *TrackName ); }