// Copyright Epic Games, Inc. All Rights Reserved. #include "SAdvancedPreviewDetailsTab.h" #include "Editor/EditorPerProjectUserSettings.h" #include "Modules/ModuleManager.h" #include "Widgets/SBoxPanel.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/STextComboBox.h" #include "IDetailsView.h" #include "PropertyEditorModule.h" #include "ScopedTransaction.h" #include "AssetViewerSettings.h" #include "AdvancedPreviewScene.h" #include "IDetailRootObjectCustomization.h" #define LOCTEXT_NAMESPACE "SPrettyPreview" SAdvancedPreviewDetailsTab::SAdvancedPreviewDetailsTab() { DefaultSettings = UAssetViewerSettings::Get(); if (DefaultSettings) { RefreshDelegate = DefaultSettings->OnAssetViewerSettingsChanged().AddRaw(this, &SAdvancedPreviewDetailsTab::OnAssetViewerSettingsRefresh); AddRemoveProfileDelegate = DefaultSettings->OnAssetViewerProfileAddRemoved().AddLambda([this]() { this->Refresh(); }); PostUndoDelegate = DefaultSettings->OnAssetViewerSettingsPostUndo().AddRaw(this, &SAdvancedPreviewDetailsTab::OnAssetViewerSettingsPostUndo); } PerProjectSettings = GetMutableDefault(); } SAdvancedPreviewDetailsTab::~SAdvancedPreviewDetailsTab() { DefaultSettings = UAssetViewerSettings::Get(); if (DefaultSettings) { DefaultSettings->OnAssetViewerSettingsChanged().Remove(RefreshDelegate); DefaultSettings->OnAssetViewerProfileAddRemoved().Remove(AddRemoveProfileDelegate); DefaultSettings->OnAssetViewerSettingsPostUndo().Remove(PostUndoDelegate); DefaultSettings->Save(); } } void SAdvancedPreviewDetailsTab::Construct(const FArguments& InArgs, const TSharedRef& InPreviewScene) { PreviewScenePtr = InPreviewScene; DefaultSettings = UAssetViewerSettings::Get(); AdditionalSettings = InArgs._AdditionalSettings; ProfileIndex = PerProjectSettings->AssetViewerProfileIndex; DetailCustomizations = InArgs._DetailCustomizations; PropertyTypeCustomizations = InArgs._PropertyTypeCustomizations; Delegates = InArgs._Delegates; CreateSettingsView(); this->ChildSlot [ SNew(SVerticalBox) + SVerticalBox::Slot() .Padding(2.0f, 1.0f, 2.0f, 1.0f) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() [ SettingsView->AsShared() ] ] +SVerticalBox::Slot() .Padding(2.0f, 1.0f, 2.0f, 1.0f) .AutoHeight() [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .Padding(2.0f) [ SNew(SHorizontalBox) .ToolTipText(LOCTEXT("SceneProfileComboBoxToolTip", "Allows for switching between scene environment and lighting profiles.")) + SHorizontalBox::Slot() .Padding(0.0f, 0.0f, 2.0f, 0.0f) .AutoWidth() .VAlign(VAlign_Center) [ SNew(STextBlock) .Text(LOCTEXT("SceneProfileSettingsLabel", "Profile")) ] + SHorizontalBox::Slot() .VAlign(VAlign_Fill) [ SAssignNew(ProfileComboBox, STextComboBox) .OptionsSource(&ProfileNames) .OnSelectionChanged(this, &SAdvancedPreviewDetailsTab::ComboBoxSelectionChanged) .IsEnabled_Lambda([this]() -> bool { return ProfileNames.Num() > 1; }) ] ] + SHorizontalBox::Slot() .Padding(2.0f) .AutoWidth() [ SNew(SButton) .OnClicked(this, &SAdvancedPreviewDetailsTab::AddProfileButtonClick) .Text(LOCTEXT("AddProfileButton", "Add Profile")) .ToolTipText(LOCTEXT("SceneProfileAddProfile", "Adds a new profile.")) ] + SHorizontalBox::Slot() .Padding(2.0f) .AutoWidth() [ SNew(SButton) .OnClicked(this, &SAdvancedPreviewDetailsTab::RemoveOrResetProfileButtonClick) .Text_Lambda([this]() { if (DefaultSettings->Profiles[ProfileIndex].bIsEngineDefaultProfile) { return LOCTEXT("ResetProfileButton", "Reset Profile"); } return LOCTEXT("RemoveProfileButton", "Remove Profile"); }) .ToolTipText_Lambda([this]() { if (DefaultSettings->Profiles[ProfileIndex].bIsEngineDefaultProfile) { return LOCTEXT("SceneProfileResetProfile", "Resets this engine profile to default settings. Cannot delete engine profiles."); } return LOCTEXT("SceneProfileRemoveProfile", "Removes the currently selected profile."); }) .IsEnabled_Lambda([this]()->bool { return ProfileNames.Num() > 1; }) ] ] ]; UpdateProfileNames(); UpdateSettingsView(); } void SAdvancedPreviewDetailsTab::ComboBoxSelectionChanged(TSharedPtr NewSelection, ESelectInfo::Type SelectInfo) { int32 NewSelectionIndex; if (ProfileNames.Find(NewSelection, NewSelectionIndex)) { ProfileIndex = NewSelectionIndex; PerProjectSettings->AssetViewerProfileIndex = ProfileIndex; UpdateSettingsView(); check(PreviewScenePtr.IsValid()); if (SelectInfo == ESelectInfo::Type::OnMouseClick) { PreviewScenePtr.Pin()->SetProfileIndex(ProfileIndex); } } } void SAdvancedPreviewDetailsTab::UpdateSettingsView() { TArray Objects; if (AdditionalSettings) { Objects.Add(AdditionalSettings); } Objects.Add(DefaultSettings); SettingsView->SetObjects(Objects, true); } void SAdvancedPreviewDetailsTab::UpdateProfileNames() { checkf(DefaultSettings->Profiles.Num(), TEXT("There should always be at least one profile available")); ProfileNames.Empty(); for (FPreviewSceneProfile& Profile : DefaultSettings->Profiles) { FString Suffix = TEXT(""); if (Profile.bSharedProfile) { Suffix = TEXT(" (Shared)"); } if (Profile.bIsEngineDefaultProfile) { Suffix = TEXT(" (Engine Default)"); } ProfileNames.Add(TSharedPtr(new FString(Profile.ProfileName + Suffix))); } ProfileComboBox->RefreshOptions(); ProfileComboBox->SetSelectedItem(ProfileNames[ProfileIndex]); } FReply SAdvancedPreviewDetailsTab::AddProfileButtonClick() { const FScopedTransaction Transaction(LOCTEXT("AddSceneProfile", "Adding Preview Scene Profile")); DefaultSettings->Modify(); // Add new profile to settings instance ProfileIndex = DefaultSettings->Profiles.AddDefaulted(); FPreviewSceneProfile& NewProfile = DefaultSettings->Profiles.Last(); // Try to create a valid profile name when one is added bool bFoundValidName = false; int32 ProfileAppendNum = FMath::Max(0, DefaultSettings->Profiles.Num() - 1); FString NewProfileName; while (!bFoundValidName) { NewProfileName = FString::Printf(TEXT("Profile_%i"), ProfileAppendNum); bool bValidName = true; for (const FPreviewSceneProfile& Profile : DefaultSettings->Profiles) { if (Profile.ProfileName == NewProfileName) { bValidName = false; break; } } if (!bValidName) { ++ProfileAppendNum; } bFoundValidName = bValidName; } NewProfile.ProfileName = NewProfileName; PerProjectSettings->AssetViewerProfileIndex = ProfileIndex; DefaultSettings->PostEditChange(); // Change selection to new profile so the user directly sees the profile that was added Refresh(); ProfileComboBox->SetSelectedItem(ProfileNames.Last()); return FReply::Handled(); } FReply SAdvancedPreviewDetailsTab::RemoveOrResetProfileButtonClick() { const FPreviewSceneProfile& CurrentProfile = DefaultSettings->Profiles[ProfileIndex]; if (CurrentProfile.bIsEngineDefaultProfile) { const FScopedTransaction Transaction(LOCTEXT("ResetSceneProfile", "Reset Preview Scene Profile")); DefaultSettings->Modify(); const FPreviewSceneProfile* DefaultEditorProfile = GetMutableDefault()->GetProfile(CurrentProfile.ProfileName); if (DefaultEditorProfile) { // Reset currently selected profile DefaultSettings->Profiles[ProfileIndex] = *DefaultEditorProfile; DefaultSettings->PostEditChange(); return FReply::Handled(); } // if we get here, it means an engine-provided default profile was removed from the engine, // in which case we should remove it... } const FScopedTransaction Transaction(LOCTEXT("RemoveSceneProfile", "Remove Preview Scene Profile")); DefaultSettings->Modify(); // Remove currently selected profile DefaultSettings->Profiles.RemoveAt(ProfileIndex); ProfileIndex = DefaultSettings->Profiles.IsValidIndex(ProfileIndex - 1 ) ? ProfileIndex - 1 : 0; PerProjectSettings->AssetViewerProfileIndex = ProfileIndex; DefaultSettings->PostEditChange(); return FReply::Handled(); } void SAdvancedPreviewDetailsTab::OnAssetViewerSettingsRefresh(const FName& InPropertyName) { if (!PreviewScenePtr.IsValid()) { // this callback can fire when the editor is forcibly closed and tool modes revert the active profile // when this happens, the preview scene is null even though this details tab hasn't been destroyed yet (and unregistered this delegate) return; } if (InPropertyName == GET_MEMBER_NAME_CHECKED(FPreviewSceneProfile, ProfileName) || InPropertyName == GET_MEMBER_NAME_CHECKED(FPreviewSceneProfile, bSharedProfile)) { Refresh(); } } void SAdvancedPreviewDetailsTab::CreateSettingsView() { // Create a property view FPropertyEditorModule& EditModule = FModuleManager::Get().GetModuleChecked("PropertyEditor"); FDetailsViewArgs DetailsViewArgs; DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea; DetailsViewArgs.bHideSelectionTip = true; DetailsViewArgs.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Automatic; DetailsViewArgs.bShowOptions = false; DetailsViewArgs.bAllowMultipleTopLevelObjects = true; SettingsView = EditModule.CreateDetailView(DetailsViewArgs); for (const FAdvancedPreviewSceneModule::FDetailCustomizationInfo& DetailCustomizationInfo : DetailCustomizations) { SettingsView->RegisterInstancedCustomPropertyLayout(DetailCustomizationInfo.Struct, DetailCustomizationInfo.OnGetDetailCustomizationInstance); } for (const FAdvancedPreviewSceneModule::FPropertyTypeCustomizationInfo& PropertyTypeCustomizationInfo : PropertyTypeCustomizations) { SettingsView->RegisterInstancedCustomPropertyTypeLayout(PropertyTypeCustomizationInfo.StructName, PropertyTypeCustomizationInfo.OnGetPropertyTypeCustomizationInstance); } for (FAdvancedPreviewSceneModule::FDetailDelegates& DetailDelegate : Delegates) { DetailDelegate.OnPreviewSceneChangedDelegate.AddSP(this, &SAdvancedPreviewDetailsTab::OnPreviewSceneChanged); } UpdateSettingsView(); } void SAdvancedPreviewDetailsTab::Refresh() { PerProjectSettings->AssetViewerProfileIndex = DefaultSettings->Profiles.IsValidIndex(PerProjectSettings->AssetViewerProfileIndex) ? PerProjectSettings->AssetViewerProfileIndex : 0; ProfileIndex = PerProjectSettings->AssetViewerProfileIndex; UpdateProfileNames(); PreviewScenePtr.Pin()->SetProfileIndex(ProfileIndex); UpdateSettingsView(); } void SAdvancedPreviewDetailsTab::OnAssetViewerSettingsPostUndo() { Refresh(); PreviewScenePtr.Pin()->UpdateScene(DefaultSettings->Profiles[ProfileIndex]); } void SAdvancedPreviewDetailsTab::OnPreviewSceneChanged(TSharedRef PreviewScene) { PreviewScenePtr = PreviewScene; Refresh(); } #undef LOCTEXT_NAMESPACE