Files
UnrealEngine/Engine/Source/Editor/DetailCustomizations/Private/AssetViewerSettingsCustomization.cpp
2025-05-18 13:04:45 +08:00

196 lines
7.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AssetViewerSettingsCustomization.h"
#include "AssetViewerSettings.h"
#include "Containers/Array.h"
#include "Containers/Map.h"
#include "Delegates/Delegate.h"
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "Editor/EditorPerProjectUserSettings.h"
#include "Fonts/SlateFontInfo.h"
#include "GenericPlatform/GenericPlatformFile.h"
#include "HAL/PlatformCrt.h"
#include "HAL/PlatformFileManager.h"
#include "IDetailGroup.h"
#include "IDetailPropertyRow.h"
#include "Internationalization/Internationalization.h"
#include "Misc/AssertionMacros.h"
#include "Misc/Attribute.h"
#include "PropertyHandle.h"
#include "SSettingsEditorCheckoutNotice.h"
#include "ScopedTransaction.h"
#include "UObject/NameTypes.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/UnrealType.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Input/SEditableTextBox.h"
#define LOCTEXT_NAMESPACE "AssetViewerSettingsCustomizations"
TSharedRef<IDetailCustomization> FAssetViewerSettingsCustomization::MakeInstance()
{
return MakeShareable(new FAssetViewerSettingsCustomization);
}
void FAssetViewerSettingsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
ViewerSettings = UAssetViewerSettings::Get();
// Create the watcher widget for the default config file (checks file status / SCC state)
FileWatcherWidget =
SNew(SSettingsEditorCheckoutNotice)
.ConfigFilePath(this, &FAssetViewerSettingsCustomization::SharedProfileConfigFilePath)
.Visibility(this, &FAssetViewerSettingsCustomization::ShowFileWatcherWidget);
// Find profiles array property handle and hide it from settings view
TSharedPtr<IPropertyHandle> ProfileHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UAssetViewerSettings, Profiles));
check(ProfileHandle->IsValidHandle());
ProfileHandle->MarkHiddenByCustomization();
// Create new category
IDetailCategoryBuilder& CategoryBuilder = DetailBuilder.EditCategory("Settings", LOCTEXT("AssetViewerSettingsCategory", "Settings"), ECategoryPriority::Important);
const UEditorPerProjectUserSettings* PerProjectUserSettings = GetDefault<UEditorPerProjectUserSettings>();
ensureMsgf(ViewerSettings && ViewerSettings->Profiles.IsValidIndex(PerProjectUserSettings->AssetViewerProfileIndex), TEXT("Invalid default settings pointer or current profile index"));
check(ViewerSettings != nullptr);
ProfileIndex = ViewerSettings->Profiles.IsValidIndex(PerProjectUserSettings->AssetViewerProfileIndex) ? PerProjectUserSettings->AssetViewerProfileIndex : 0;
// Add current active profile property child nodes (rest of profiles remain hidden)
TSharedPtr<IPropertyHandle> ProfilePropertyHandle = ProfileHandle->GetChildHandle(ProfileIndex);
checkf(ProfileHandle->IsValidHandle(), TEXT("Invalid indexing into profiles child properties"));
uint32 PropertyCount = 0;
ProfilePropertyHandle->GetNumChildren(PropertyCount);
TMap<FName, IDetailGroup*> SubcategoryToGroupMap;
FName NamePropertyName = GET_MEMBER_NAME_CHECKED(FPreviewSceneProfile, ProfileName);
FName SharedProfilePropertyName = GET_MEMBER_NAME_CHECKED(FPreviewSceneProfile, bSharedProfile);
for (uint32 PropertyIndex = 0; PropertyIndex < PropertyCount; ++PropertyIndex)
{
TSharedPtr<IPropertyHandle> ProfileProperty = ProfilePropertyHandle->GetChildHandle(PropertyIndex);
if (ProfileProperty->GetProperty()->GetFName() == NamePropertyName)
{
NameProperty = ProfileProperty;
CategoryBuilder.AddCustomRow(LOCTEXT("PreviewSceneProfileDetails_ProfileNameLabel", "Profile Name"))
.NameContent()
[
ProfileProperty->CreatePropertyNameWidget()
]
.ValueContent()
.MaxDesiredWidth(250.0f)
[
// PropertyHandle->CreatePropertyValueWidget()
SAssignNew(NameEditTextBox, SEditableTextBox)
.IsEnabled_Lambda([this]()
{
return !ViewerSettings->Profiles[ProfileIndex].bIsEngineDefaultProfile;
})
.Text(this, &FAssetViewerSettingsCustomization::OnGetProfileName)
.OnTextChanged(this, &FAssetViewerSettingsCustomization::OnProfileNameChanged)
.OnTextCommitted(this, &FAssetViewerSettingsCustomization::OnProfileNameCommitted)
.Font(IDetailLayoutBuilder::GetDetailFont())
];
}
else if (ProfileProperty->GetProperty()->GetFName() == SharedProfilePropertyName)
{
IDetailPropertyRow& Row = CategoryBuilder.AddProperty(ProfileProperty);
Row.EditCondition(TAttribute<bool>(this, &FAssetViewerSettingsCustomization::CanSetSharedProfile), nullptr);
CategoryBuilder.AddCustomRow(LOCTEXT("PreviewSceneProfileName_CheckoutRow", "Checkout Default Config"))
.Visibility(TAttribute<EVisibility>(this, &FAssetViewerSettingsCustomization::ShowFileWatcherWidget))
[
FileWatcherWidget->AsShared()
];
}
else
{
FName DefaultCategoryName = ProfileProperty->GetDefaultCategoryName();
IDetailGroup*& Group = SubcategoryToGroupMap.FindOrAdd(DefaultCategoryName);
if (!Group)
{
Group = &CategoryBuilder.AddGroup(DefaultCategoryName, ProfileProperty->GetDefaultCategoryText());
}
Group->AddPropertyRow(ProfileProperty.ToSharedRef());
}
}
}
FText FAssetViewerSettingsCustomization::OnGetProfileName() const
{
return FText::FromString(ViewerSettings->Profiles[ProfileIndex].ProfileName);
}
void FAssetViewerSettingsCustomization::OnProfileNameChanged(const FText& InNewText)
{
bValidProfileName = IsProfileNameValid(InNewText.ToString());
if (!bValidProfileName)
{
NameEditTextBox->SetError(LOCTEXT("PreviewSceneProfileName_NotValid", "This name is already in use"));
}
else
{
NameEditTextBox->SetError(FText::GetEmpty());
}
}
void FAssetViewerSettingsCustomization::OnProfileNameCommitted(const FText& InNewText, ETextCommit::Type InTextCommit)
{
if (bValidProfileName && InTextCommit != ETextCommit::OnCleared)
{
const FScopedTransaction Transaction(LOCTEXT("RenameProfile", "Rename Profile"));
ViewerSettings->Modify();
ViewerSettings->Profiles[ProfileIndex].ProfileName = InNewText.ToString();
FPropertyChangedEvent PropertyEvent(NameProperty->GetProperty());
ViewerSettings->PostEditChangeProperty(PropertyEvent);
}
bValidProfileName = false;
NameEditTextBox->SetError(FText::GetEmpty());
}
const bool FAssetViewerSettingsCustomization::IsProfileNameValid(const FString& NewName)
{
for (int32 CheckProfileIndex = 0; CheckProfileIndex < ViewerSettings->Profiles.Num(); ++CheckProfileIndex)
{
if (ProfileIndex != CheckProfileIndex && ViewerSettings->Profiles[CheckProfileIndex].ProfileName == NewName)
{
return false;
}
}
return true;
}
bool FAssetViewerSettingsCustomization::CanSetSharedProfile() const
{
const bool bIsEngineDefaultProfile = ViewerSettings->Profiles[ProfileIndex].bIsEngineDefaultProfile;
const bool bIsConfigWriteable = !FPlatformFileManager::Get().GetPlatformFile().IsReadOnly(*SharedProfileConfigFilePath());
return bIsConfigWriteable && !bIsEngineDefaultProfile;
}
EVisibility FAssetViewerSettingsCustomization::ShowFileWatcherWidget() const
{
return CanSetSharedProfile() ? EVisibility::Collapsed : EVisibility::Visible;
}
FString FAssetViewerSettingsCustomization::SharedProfileConfigFilePath() const
{
if (ViewerSettings != nullptr)
{
return ViewerSettings->GetDefaultConfigFilename();
}
return FString();
}
#undef LOCTEXT_NAMESPACE