// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "MaterialEditorModule.h" #include "Editor/PropertyEditor/Public/PropertyEditing.h" #include "MaterialEditor.h" #include "MaterialEditorDetailCustomization.h" #include "Materials/MaterialParameterCollection.h" #include "Materials/MaterialExpressionScalarParameter.h" #define LOCTEXT_NAMESPACE "MaterialEditor" TSharedRef FMaterialExpressionParameterDetails::MakeInstance(FOnCollectParameterGroups InCollectGroupsDelegate) { return MakeShareable( new FMaterialExpressionParameterDetails(InCollectGroupsDelegate) ); } FMaterialExpressionParameterDetails::FMaterialExpressionParameterDetails(FOnCollectParameterGroups InCollectGroupsDelegate) : CollectGroupsDelegate(InCollectGroupsDelegate) { } void FMaterialExpressionParameterDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { // for expression parameters all their properties are in one category based on their class name. FName DefaultCategory = NAME_None; IDetailCategoryBuilder& Category = DetailLayout.EditCategory( DefaultCategory ); TArray< TWeakObjectPtr > Objects; DetailLayout.GetObjectsBeingCustomized(Objects); if (Objects.Num() > 0) { UMaterialExpressionScalarParameter* ScalarParameter = Cast(Objects[0].Get()); if (ScalarParameter) { // Store these for OnSliderMinMaxEdited ScalarParameterObject = Objects[0]; DefaultValueHandle = DetailLayout.GetProperty("DefaultValue", UMaterialExpressionScalarParameter::StaticClass()); TSharedPtr SliderMinHandle = DetailLayout.GetProperty("SliderMin", UMaterialExpressionScalarParameter::StaticClass()); if (SliderMinHandle.IsValid()) { // Setup a callback when SliderMin changes to update the DefaultValue slider FSimpleDelegate OnSliderMinMaxEditedDelegate = FSimpleDelegate::CreateSP(this, &FMaterialExpressionParameterDetails::OnSliderMinMaxEdited); SliderMinHandle->SetOnPropertyValueChanged(OnSliderMinMaxEditedDelegate); } TSharedPtr SliderMaxHandle = DetailLayout.GetProperty("SliderMax", UMaterialExpressionScalarParameter::StaticClass()); if (SliderMaxHandle.IsValid()) { FSimpleDelegate OnSliderMinMaxEditedDelegate = FSimpleDelegate::CreateSP(this, &FMaterialExpressionParameterDetails::OnSliderMinMaxEdited); SliderMaxHandle->SetOnPropertyValueChanged(OnSliderMinMaxEditedDelegate); } OnSliderMinMaxEdited(); } } Category.AddProperty("ParameterName"); // Get a handle to the property we are about to edit GroupPropertyHandle = DetailLayout.GetProperty( "Group" ); GroupPropertyHandle->MarkHiddenByCustomization(); PopulateGroups(); TSharedPtr NewComboButton; TSharedPtr NewEditBox; TSharedPtr>> NewListView; Category.AddCustomRow( GroupPropertyHandle->GetPropertyDisplayName() ) .NameContent() [ SNew( STextBlock ) .Text( GroupPropertyHandle->GetPropertyDisplayName() ) .Font( IDetailLayoutBuilder::GetDetailFont() ) ] .ValueContent() [ SAssignNew(NewComboButton, SComboButton) .ContentPadding(0) .ButtonContent() [ SAssignNew(NewEditBox, SEditableText) .Text(this, &FMaterialExpressionParameterDetails::OnGetText) .OnTextCommitted(this, &FMaterialExpressionParameterDetails::OnTextCommitted) ] .MenuContent() [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() .MaxHeight(400.0f) [ SAssignNew(NewListView, SListView>) .ListItemsSource(&GroupsSource) .OnGenerateRow(this, &FMaterialExpressionParameterDetails::MakeDetailsGroupViewWidget) .OnSelectionChanged(this, &FMaterialExpressionParameterDetails::OnSelectionChanged) ] ] ]; GroupComboButton = NewComboButton; GroupEditBox = NewEditBox; GroupListView = NewListView; } void FMaterialExpressionParameterDetails::PopulateGroups() { TArray Groups; CollectGroupsDelegate.ExecuteIfBound(&Groups); GroupsSource.Empty(); for (int32 GroupIdx = 0; GroupIdx < Groups.Num(); ++GroupIdx) { GroupsSource.Add(MakeShareable(new FString(Groups[GroupIdx]))); } } TSharedRef< ITableRow > FMaterialExpressionParameterDetails::MakeDetailsGroupViewWidget( TSharedPtr Item, const TSharedRef< STableViewBase >& OwnerTable ) { return SNew(STableRow>, OwnerTable) [ SNew(STextBlock) .Text(FText::FromString(*Item.Get())) ]; } void FMaterialExpressionParameterDetails::OnSelectionChanged( TSharedPtr ProposedSelection, ESelectInfo::Type /*SelectInfo*/ ) { if (ProposedSelection.IsValid()) { GroupPropertyHandle->SetValue(*ProposedSelection.Get()); GroupListView.Pin()->ClearSelection(); GroupComboButton.Pin()->SetIsOpen(false); } } void FMaterialExpressionParameterDetails::OnTextCommitted( const FText& InText, ETextCommit::Type /*CommitInfo*/) { GroupPropertyHandle->SetValue(InText.ToString()); PopulateGroups(); } FString FMaterialExpressionParameterDetails::OnGetString() const { FString OutText; if (GroupPropertyHandle->GetValue(OutText) == FPropertyAccess::MultipleValues) { return LOCTEXT("MultipleValues", "Multiple Values").ToString(); } return OutText; } FText FMaterialExpressionParameterDetails::OnGetText() const { FString NewString = OnGetString(); return FText::FromString(NewString); } void FMaterialExpressionParameterDetails::OnSliderMinMaxEdited() { UMaterialExpressionScalarParameter* ScalarParameter = Cast(ScalarParameterObject.Get()); if (ScalarParameter && DefaultValueHandle.IsValid()) { if (ScalarParameter->SliderMax > ScalarParameter->SliderMin) { // Update the values that SPropertyEditorNumeric reads // Unfortuantly there is no way to recreate the widget to actually update the UI with these new values DefaultValueHandle->SetInstanceMetaData("UIMin",FString::Printf(TEXT("%f"), ScalarParameter->SliderMin)); DefaultValueHandle->SetInstanceMetaData("UIMax",FString::Printf(TEXT("%f"), ScalarParameter->SliderMax)); } else { DefaultValueHandle->SetInstanceMetaData("UIMin", TEXT("")); DefaultValueHandle->SetInstanceMetaData("UIMax", TEXT("")); } } } TSharedRef FMaterialExpressionCollectionParameterDetails::MakeInstance() { return MakeShareable( new FMaterialExpressionCollectionParameterDetails() ); } FMaterialExpressionCollectionParameterDetails::FMaterialExpressionCollectionParameterDetails() { } FText FMaterialExpressionCollectionParameterDetails::GetToolTipText() const { if (ParametersSource.Num() == 1) { return LOCTEXT("SpecifyCollection", "Specify a Collection to get parameter options"); } else { return LOCTEXT("ChooseParameter", "Choose a parameter from the collection"); } } FText FMaterialExpressionCollectionParameterDetails::GetParameterNameString() const { FString CurrentParameterName; FPropertyAccess::Result Result = ParameterNamePropertyHandle->GetValue(CurrentParameterName); if( Result == FPropertyAccess::MultipleValues ) { return NSLOCTEXT("PropertyEditor", "MultipleValues", "Multiple Values"); } return FText::FromString(CurrentParameterName); } bool FMaterialExpressionCollectionParameterDetails::IsParameterNameComboEnabled() const { UMaterialParameterCollection* Collection = nullptr; if (CollectionPropertyHandle->IsValidHandle()) { UObject* CollectionObject = nullptr; verify(CollectionPropertyHandle->GetValue(CollectionObject) == FPropertyAccess::Success); Collection = Cast(CollectionObject); } return Collection != nullptr; } void FMaterialExpressionCollectionParameterDetails::OnCollectionChanged() { PopulateParameters(); } void FMaterialExpressionCollectionParameterDetails::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { // for expression parameters all their properties are in one category based on their class name. FName DefaultCategory = NAME_None; IDetailCategoryBuilder& Category = DetailLayout.EditCategory( DefaultCategory ); // Get a handle to the property we are about to edit ParameterNamePropertyHandle = DetailLayout.GetProperty( "ParameterName" ); check(ParameterNamePropertyHandle.IsValid()); CollectionPropertyHandle = DetailLayout.GetProperty( "Collection" ); check(CollectionPropertyHandle.IsValid()); // Register a changed callback on the collection property since we need to update the PropertyName vertical box when it changes FSimpleDelegate OnCollectionChangedDelegate = FSimpleDelegate::CreateSP( this, &FMaterialExpressionCollectionParameterDetails::OnCollectionChanged ); CollectionPropertyHandle->SetOnPropertyValueChanged( OnCollectionChangedDelegate ); ParameterNamePropertyHandle->MarkHiddenByCustomization(); CollectionPropertyHandle->MarkHiddenByCustomization(); PopulateParameters(); TSharedPtr NewComboButton; TSharedPtr>> NewListView; // This isn't strictly speaking customized, but we need it to appear before the "Parameter Name" property, // so we manually add it and set MarkHiddenByCustomization on it to avoid it being automatically added Category.AddProperty(CollectionPropertyHandle); Category.AddCustomRow( ParameterNamePropertyHandle->GetPropertyDisplayName() ) .NameContent() [ SNew( STextBlock ) .Text( ParameterNamePropertyHandle->GetPropertyDisplayName() ) .Font( IDetailLayoutBuilder::GetDetailFont() ) ] .ValueContent() [ SAssignNew(NewComboButton, SComboButton) .IsEnabled(this, &FMaterialExpressionCollectionParameterDetails::IsParameterNameComboEnabled) .ContentPadding(0) .ButtonContent() [ SNew(STextBlock) .Text(this, &FMaterialExpressionCollectionParameterDetails::GetParameterNameString ) ] .MenuContent() [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() .MaxHeight(400.0f) [ SAssignNew(NewListView, SListView>) .ListItemsSource(&ParametersSource) .OnGenerateRow(this, &FMaterialExpressionCollectionParameterDetails::MakeDetailsGroupViewWidget) .OnSelectionChanged(this, &FMaterialExpressionCollectionParameterDetails::OnSelectionChanged) ] ] ]; ParameterComboButton = NewComboButton; ParameterListView = NewListView; NewComboButton->SetToolTipText(GetToolTipText()); } void FMaterialExpressionCollectionParameterDetails::PopulateParameters() { UMaterialParameterCollection* Collection = nullptr; if (CollectionPropertyHandle->IsValidHandle()) { UObject* CollectionObject = nullptr; verify(CollectionPropertyHandle->GetValue(CollectionObject) == FPropertyAccess::Success); Collection = Cast(CollectionObject); } ParametersSource.Empty(); if (Collection) { for (int32 ParameterIndex = 0; ParameterIndex < Collection->ScalarParameters.Num(); ++ParameterIndex) { ParametersSource.Add(MakeShareable(new FString(Collection->ScalarParameters[ParameterIndex].ParameterName.ToString()))); } for (int32 ParameterIndex = 0; ParameterIndex < Collection->VectorParameters.Num(); ++ParameterIndex) { ParametersSource.Add(MakeShareable(new FString(Collection->VectorParameters[ParameterIndex].ParameterName.ToString()))); } } if (ParametersSource.Num() == 0) { ParametersSource.Add(MakeShareable(new FString(LOCTEXT("NoParameter", "None").ToString()))); } } TSharedRef< ITableRow > FMaterialExpressionCollectionParameterDetails::MakeDetailsGroupViewWidget( TSharedPtr Item, const TSharedRef< STableViewBase >& OwnerTable ) { return SNew(STableRow>, OwnerTable) [ SNew(STextBlock) .Text(FText::FromString(*Item.Get())) ]; } void FMaterialExpressionCollectionParameterDetails::OnSelectionChanged( TSharedPtr ProposedSelection, ESelectInfo::Type /*SelectInfo*/ ) { if (ProposedSelection.IsValid()) { ParameterNamePropertyHandle->SetValue(*ProposedSelection.Get()); ParameterListView.Pin()->ClearSelection(); ParameterComboButton.Pin()->SetIsOpen(false); } } TSharedRef FMaterialDetailCustomization::MakeInstance() { return MakeShareable( new FMaterialDetailCustomization ); } void FMaterialDetailCustomization::CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) { TArray > Objects; DetailLayout.GetObjectsBeingCustomized( Objects ); bool bUIMaterial = true; for( TWeakObjectPtr& Object : Objects ) { UMaterial* Material = Cast( Object.Get() ); if( Material ) { bUIMaterial &= Material->IsUIMaterial(); } else { // this shouldn't happen but just in case, let all properties through bUIMaterial = false; } } if( bUIMaterial ) { DetailLayout.HideCategory( TEXT("TranslucencySelfShadowing") ); DetailLayout.HideCategory( TEXT("Translucency") ); DetailLayout.HideCategory( TEXT("Tessellation") ); DetailLayout.HideCategory( TEXT("Mobile") ); DetailLayout.HideCategory( TEXT("PostProcessMaterial") ); DetailLayout.HideCategory( TEXT("Lightmass") ); DetailLayout.HideCategory( TEXT("Thumbnail") ); DetailLayout.HideCategory( TEXT("MaterialInterface") ); DetailLayout.HideCategory( TEXT("PhysicalMaterial") ); DetailLayout.HideCategory( TEXT("Usage") ); IDetailCategoryBuilder& MaterialCategory = DetailLayout.EditCategory( TEXT("Material") ); TArray> AllProperties; MaterialCategory.GetDefaultProperties( AllProperties ); for( TSharedRef& PropertyHandle : AllProperties ) { UProperty* Property = PropertyHandle->GetProperty(); FName PropertyName = Property->GetFName(); if( PropertyName != GET_MEMBER_NAME_CHECKED(UMaterial, MaterialDomain) && PropertyName != GET_MEMBER_NAME_CHECKED(UMaterial, BlendMode) && PropertyName != GET_MEMBER_NAME_CHECKED(UMaterial, OpacityMaskClipValue) && PropertyName != GET_MEMBER_NAME_CHECKED(UMaterial, NumCustomizedUVs) ) { DetailLayout.HideProperty( PropertyHandle ); } } } } #undef LOCTEXT_NAMESPACE