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

2654 lines
84 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AssetThumbnail.h"
#include "AssetThumbnailToolTip.h"
#include "AssetDefinitionRegistry.h"
#include "AssetStatusAssetDataInfoProvider.h"
#include "Engine/Blueprint.h"
#include "GameFramework/Actor.h"
#include "Layout/Margin.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SOverlay.h"
#include "Engine/GameViewportClient.h"
#include "Interfaces/Interface_AsyncCompilation.h"
#include "Modules/ModuleManager.h"
#include "Animation/CurveHandle.h"
#include "Animation/CurveSequence.h"
#include "Textures/SlateTextureData.h"
#include "Fonts/SlateFontInfo.h"
#include "Application/ThrottleManager.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Text/STextBlock.h"
#include "Widgets/SViewport.h"
#include "Styling/AppStyle.h"
#include "RenderingThread.h"
#include "Settings/ContentBrowserSettings.h"
#include "RenderUtils.h"
#include "Editor/UnrealEdEngine.h"
#include "ThumbnailRendering/ThumbnailManager.h"
#include "Editor.h"
#include "UnrealEdGlobals.h"
#include "Slate/SlateTextures.h"
#include "ObjectTools.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "ShaderCompiler.h"
#include "AssetCompilingManager.h"
#include "AssetDefinitionRegistry.h"
#include "AssetDefinitionAssetInfo.h"
#include "AssetDefinitionDefault.h"
#include "IAssetTools.h"
#include "AssetTypeActions_Base.h"
#include "AssetToolsModule.h"
#include "Styling/SlateIconFinder.h"
#include "ClassIconFinder.h"
#include "IAssetSystemInfoProvider.h"
#include "IVREditorModule.h"
#include "SAssetThumbnailEditModeTools.h"
#include "SDocumentationToolTip.h"
#include "Fonts/FontMeasure.h"
#include "Framework/Application/SlateApplication.h"
#include "Widgets/Images/SLayeredImage.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Text/SRichTextBlock.h"
#include "ContentBrowserUtils.h"
namespace AssetThumbnailPool
{
const FObjectThumbnail* LoadThumbnailsFromPackage(const FAssetData& AssetData, FThumbnailMap& OutThumbnailMap)
{
FString PackageFilename;
if (FPackageName::DoesPackageExist(AssetData.PackageName.ToString(), &PackageFilename))
{
const FName ObjectFullName = FName(*AssetData.GetFullName());
TSet<FName> ObjectFullNames;
ObjectFullNames.Add(ObjectFullName);
ThumbnailTools::LoadThumbnailsFromPackage(PackageFilename, ObjectFullNames, OutThumbnailMap);
return OutThumbnailMap.Find(ObjectFullName);
}
return nullptr;
};
}
FName FAssetThumbnailPool::CustomThumbnailTagName = "CustomThumbnail";
class SAssetThumbnail : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS( SAssetThumbnail )
: _Style("AssetThumbnail")
, _ThumbnailPool(nullptr)
, _AllowFadeIn(false)
, _ForceGenericThumbnail(false)
, _AllowHintText(true)
, _AllowAssetSpecificThumbnailOverlay(false)
, _AllowRealTimeOnHovered(true)
, _Label(EThumbnailLabel::ClassName)
, _HighlightedText(FText::GetEmpty())
, _HintColorAndOpacity(FLinearColor(0.0f, 0.0f, 0.0f, 0.0f))
, _ClassThumbnailBrushOverride(NAME_None)
, _AssetTypeColorOverride()
, _Padding(0)
, _BorderPadding(FMargin(2.f))
, _GenericThumbnailSize(64)
, _AllowAssetStatusThumbnailOverlay(false)
, _AdditionalTooltipInSmallView(SNullWidget::NullWidget)
, _ShowAssetColor(false)
, _AssetBorderImageOverride()
, _AlwaysExpandTooltip(false)
, _ColorStripOrientation(EThumbnailColorStripOrientation::HorizontalBottomEdge)
{}
SLATE_ARGUMENT(FName, Style)
SLATE_ARGUMENT(TSharedPtr<FAssetThumbnail>, AssetThumbnail)
SLATE_ARGUMENT(TSharedPtr<FAssetThumbnailPool>, ThumbnailPool)
SLATE_ARGUMENT(bool, AllowFadeIn)
SLATE_ARGUMENT(bool, ForceGenericThumbnail)
SLATE_ARGUMENT(bool, AllowHintText)
SLATE_ATTRIBUTE(bool, AllowAssetSpecificThumbnailOverlay)
SLATE_ATTRIBUTE(bool, AllowAssetSpecificThumbnailOverlayIndicator)
SLATE_ARGUMENT(bool, AllowRealTimeOnHovered)
SLATE_ARGUMENT(EThumbnailLabel::Type, Label)
SLATE_ATTRIBUTE(FText, HighlightedText)
SLATE_ATTRIBUTE(FLinearColor, HintColorAndOpacity)
SLATE_ARGUMENT(FName, ClassThumbnailBrushOverride)
SLATE_ARGUMENT(TOptional<FLinearColor>, AssetTypeColorOverride)
SLATE_ARGUMENT(FMargin, Padding)
SLATE_ATTRIBUTE(FMargin, BorderPadding)
SLATE_ATTRIBUTE(int32, GenericThumbnailSize)
SLATE_ARGUMENT(TSharedPtr<IAssetSystemInfoProvider>, AssetSystemInfoProvider)
SLATE_ATTRIBUTE(bool, AllowAssetStatusThumbnailOverlay)
SLATE_ATTRIBUTE(TSharedPtr<SWidget>, AdditionalTooltipInSmallView)
SLATE_ATTRIBUTE(bool, ShowAssetColor)
SLATE_ATTRIBUTE(const FSlateBrush*, AssetBorderImageOverride)
SLATE_ARGUMENT(bool, CanDisplayEditModePrimitiveTools)
SLATE_ATTRIBUTE(EVisibility, IsEditModeVisible)
SLATE_ATTRIBUTE(bool, AlwaysExpandTooltip)
SLATE_ARGUMENT(EThumbnailColorStripOrientation, ColorStripOrientation)
SLATE_END_ARGS()
/** Constructs this widget with InArgs */
void Construct( const FArguments& InArgs )
{
Style = InArgs._Style;
HighlightedText = InArgs._HighlightedText;
Label = InArgs._Label;
HintColorAndOpacity = InArgs._HintColorAndOpacity;
bAllowHintText = InArgs._AllowHintText;
bAllowRealTimeOnHovered = InArgs._AllowRealTimeOnHovered;
ThumbnailBrush = nullptr;
ClassIconBrush = nullptr;
AssetThumbnail = InArgs._AssetThumbnail;
bHasRenderedThumbnail = false;
WidthLastFrame = 0;
GenericThumbnailBorderPadding = 2.f;
GenericThumbnailSize = InArgs._GenericThumbnailSize;
ColorStripOrientation = InArgs._ColorStripOrientation;
AssetSystemInfoProvider = InArgs._AssetSystemInfoProvider;
AllowAssetSpecificThumbnailOverlay = InArgs._AllowAssetSpecificThumbnailOverlay;
AllowAssetSpecificThumbnailOverlayIndicator = InArgs._AllowAssetSpecificThumbnailOverlayIndicator;
AllowAssetStatusThumbnailOverlay = InArgs._AllowAssetStatusThumbnailOverlay;
AssetBorderImageOverride = InArgs._AssetBorderImageOverride;
ShowAssetColor = InArgs._ShowAssetColor;
EditModeVisibility = InArgs._IsEditModeVisible;
AlwaysExpandTooltip = InArgs._AlwaysExpandTooltip;
AdditionalTooltipInSmallView = InArgs._AdditionalTooltipInSmallView;
// If the old padding was used, use that instead of the new one to avoid positioning issue
BorderPadding = InArgs._Padding == FMargin(0.f) ? InArgs._BorderPadding : InArgs._Padding;
AssetThumbnail->OnAssetDataChanged().AddSP(this, &SAssetThumbnail::OnAssetDataChanged);
const FAssetData& AssetData = AssetThumbnail->GetAssetData();
UClass* Class = FindObjectSafe<UClass>(AssetData.AssetClassPath);
static FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
TSharedPtr<IAssetTypeActions> AssetTypeActions;
if ( Class != NULL )
{
AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(Class).Pin();
}
AssetTypeColorOverride = InArgs._AssetTypeColorOverride;
AssetColor = FLinearColor::White;
if( AssetTypeColorOverride.IsSet() )
{
AssetColor = AssetTypeColorOverride.GetValue();
}
else if ( AssetTypeActions.IsValid() )
{
AssetColor = AssetTypeActions->GetTypeColor();
}
TSharedRef<SOverlay> OverlayWidget = SNew(SOverlay);
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
// Set our tooltip - this will refresh each time it's opened to make sure it's up-to-date
SetToolTip(SNew(SAssetThumbnailToolTip)
.AssetThumbnail(SharedThis(this))
.AlwaysExpandTooltip(AlwaysExpandTooltip));
}
UpdateThumbnailClass(AssetTypeActions.Get());
ClassThumbnailBrushOverride = InArgs._ClassThumbnailBrushOverride;
AssetBackgroundBrushName = *(Style.ToString() + TEXT(".AssetBackground"));
ClassBackgroundBrushName = *(Style.ToString() + TEXT(".ClassBackground"));
// The generic representation of the thumbnail, for use before the rendered version, if it exists
OverlayWidget->AddSlot()
PRAGMA_DISABLE_DEPRECATION_WARNINGS
.Padding(InArgs._Padding)
PRAGMA_ENABLE_DEPRECATION_WARNINGS
[
SAssignNew(AssetBackgroundWidget, SBorder)
.BorderImage(GetAssetBackgroundBrush())
.Padding(GenericThumbnailBorderPadding)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
.Visibility(this, &SAssetThumbnail::GetGenericThumbnailVisibility)
[
SNew(SOverlay)
+SOverlay::Slot()
[
SAssignNew(GenericLabelTextBlock, STextBlock)
.Text(GetLabelText())
.Font(GetTextFont())
.Justification(ETextJustify::Center)
.ColorAndOpacity(FAppStyle::GetColor(Style, ".ColorAndOpacity"))
.HighlightText(HighlightedText)
]
+SOverlay::Slot()
[
SAssignNew(GenericThumbnailImage, SImage)
.DesiredSizeOverride(this, &SAssetThumbnail::GetGenericThumbnailDesiredSize)
.Image(this, &SAssetThumbnail::GetClassThumbnailBrush)
]
]
];
if ( InArgs._ThumbnailPool.IsValid() && !InArgs._ForceGenericThumbnail )
{
ViewportFadeAnimation = FCurveSequence();
ViewportFadeCurve = ViewportFadeAnimation.AddCurve(0.f, 0.25f, ECurveEaseFunction::QuadOut);
TSharedPtr<SViewport> Viewport =
SNew( SViewport )
.EnableGammaCorrection(false)
// In VR editor every widget is in the world and gamma corrected by the scene renderer. Thumbnails will have already been gamma
// corrected and so they need to be reversed
.ReverseGammaCorrection(IVREditorModule::Get().IsVREditorModeActive())
.EnableBlending(true)
.ViewportSize(AssetThumbnail->GetSize());
Viewport->SetViewportInterface( AssetThumbnail.ToSharedRef() );
AssetThumbnail->GetViewportRenderTargetTexture(); // Access the render texture to push it on the stack if it isn't already rendered
InArgs._ThumbnailPool->OnThumbnailRendered().AddSP(this, &SAssetThumbnail::OnThumbnailRendered);
InArgs._ThumbnailPool->OnThumbnailRenderFailed().AddSP(this, &SAssetThumbnail::OnThumbnailRenderFailed);
if ( ShouldRender() && (!InArgs._AllowFadeIn || InArgs._ThumbnailPool->IsRendered(AssetThumbnail)) )
{
bHasRenderedThumbnail = true;
ViewportFadeAnimation.JumpToEnd();
}
// The viewport for the rendered thumbnail, if it exists
OverlayWidget->AddSlot()
[
SAssignNew(RenderedThumbnailWidget, SBorder)
PRAGMA_DISABLE_DEPRECATION_WARNINGS
.Padding(InArgs._Padding)
PRAGMA_ENABLE_DEPRECATION_WARNINGS
.BorderImage(FStyleDefaults::GetNoBrush())
.ColorAndOpacity(this, &SAssetThumbnail::GetViewportColorAndOpacity)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
Viewport.ToSharedRef()
]
];
}
if (ThumbnailClass.Get() && bIsClassType)
{
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
FAssetDisplayInfo ClassInfo;
ClassInfo.StatusIcon = MakeAttributeSP(this, &SAssetThumbnail::GetClassIconBrush);
ClassInfo.Priority = FAssetStatusPriority(EStatusSeverity::Info, 1);
ClassInfo.StatusDescription = MakeAttributeSP(this, &SAssetThumbnail::GetClassName);
ClassInfo.IsVisible = EVisibility::Visible;
OverlayInfo.Add(ClassInfo);
}
else
{
OverlayWidget->AddSlot()
.VAlign(VAlign_Bottom)
.HAlign(HAlign_Right)
.Padding(GetClassIconPadding())
[
SAssignNew(ClassIconWidget, SBorder)
.BorderImage(FAppStyle::GetNoBrush())
[
SNew(SImage)
.Image(this, &SAssetThumbnail::GetClassIconBrush)
]
];
}
}
if( bAllowHintText )
{
OverlayWidget->AddSlot()
.HAlign(HAlign_Center)
.VAlign(VAlign_Top)
.Padding(FMargin(2, 2, 2, 2))
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush(Style, ".HintBackground"))
.BorderBackgroundColor(this, &SAssetThumbnail::GetHintBackgroundColor) //Adjust the opacity of the border itself
.ColorAndOpacity(HintColorAndOpacity) //adjusts the opacity of the contents of the border
.Visibility(this, &SAssetThumbnail::GetHintTextVisibility)
.Padding(0)
[
SAssignNew(HintTextBlock, STextBlock)
.Text(GetLabelText())
.Font(GetHintTextFont())
.ColorAndOpacity(FAppStyle::GetColor(Style, ".HintColorAndOpacity"))
.HighlightText(HighlightedText)
]
];
}
TSharedRef<SWidget> ContentWidget = OverlayWidget;
auto AddAssetColor = [&]()
{
TAttribute<FMargin> ColorStripMargin = MakeAttributeSP(this, &SAssetThumbnail::GetAssetColorPadding);
// The asset color strip
OverlayWidget->AddSlot()
.HAlign(ColorStripOrientation == EThumbnailColorStripOrientation::HorizontalBottomEdge ? HAlign_Fill : HAlign_Right)
.VAlign(ColorStripOrientation == EThumbnailColorStripOrientation::HorizontalBottomEdge ? VAlign_Bottom : VAlign_Fill)
[
SAssignNew(AssetColorStripWidget, SBorder)
.BorderImage(FAppStyle::GetBrush("WhiteBrush"))
.BorderBackgroundColor(AssetColor)
.Padding(ColorStripMargin)
.Visibility(TAttribute<EVisibility>::CreateSP(this, &SAssetThumbnail::GetAssetColorVisibility))
];
};
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
if (ShowAssetColor.IsSet())
{
AddAssetColor();
// Image overlay to match the Hovered/Selected status, by design only part of the asset color should be highlighted
const TSharedRef<SImage> OverlayImage = SNew(SImage)
.Image(this, &SAssetThumbnail::GetThumbnailBorderBrush)
.Visibility(EVisibility::HitTestInvisible);
OverlayWidget->AddSlot()
.Padding(TAttribute<FMargin>::CreateSP(this, &SAssetThumbnail::GetAssetColorMargin))
[
OverlayImage
];
// Border used to contain the Thumbnail in the ratio
const TSharedRef<SBorder> OverlayBorder = SNew(SBorder)
.Padding(TAttribute<FMargin>::CreateSP(this, &SAssetThumbnail::GetOverlayBorderMargin))
.BorderImage(this, &SAssetThumbnail::GetThumbnailBorderBrush);
if (AssetBorderImageOverride.IsSet())
{
OverlayImage->SetImage(AssetBorderImageOverride);
OverlayBorder->SetBorderImage(AssetBorderImageOverride);
}
OverlayBorder->SetContent(OverlayWidget);
ContentWidget = OverlayBorder;
}
}
else
{
AddAssetColor();
}
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
// AssetEditMode, do not create it if there is no config argument to show it
if (EditModeVisibility.IsSet())
{
OverlayWidget->AddSlot()
[
SAssignNew(AssetThumbnailEditMode, SAssetThumbnailEditModeTools, AssetThumbnail)
.SmallView(InArgs._CanDisplayEditModePrimitiveTools)
.Visibility(EditModeVisibility)
];
}
}
const UAssetDefinition* AssetDefinition = UAssetDefinitionRegistry::Get()->GetAssetDefinitionForClass(Class);
if (!AssetDefinition)
{
AssetDefinition = GetDefault<UAssetDefinitionDefault>();
}
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
if (AllowAssetStatusThumbnailOverlay.IsSet() && AssetDefinition)
{
const TSharedPtr<FAssetStatusAssetDataInfoProvider> AssetDataInfoProvider = MakeShared<FAssetStatusAssetDataInfoProvider>(AssetData);
AssetDefinition->GetAssetStatusInfo(AssetDataInfoProvider, OverlayInfo);
// Sort the list based on Status Priority
auto SortStatus = [] (const FAssetDisplayInfo& InFirstStatus, const FAssetDisplayInfo& InSecondStatus)
{
if (!InFirstStatus.Priority.IsSet())
{
return false;
}
if (!InSecondStatus.Priority.IsSet())
{
return true;
}
return InSecondStatus.Priority.Get() < InFirstStatus.Priority.Get();
};
OverlayInfo.Sort(SortStatus);
const TSharedRef<SHorizontalBox> HorizontalBox = SNew(SHorizontalBox);
for (int32 StatusIndex = 0; StatusIndex < OverlayInfo.Num(); StatusIndex++)
{
Statuses.Add(CreateStatusWidget(StatusIndex, OverlayInfo[StatusIndex]));
HorizontalBox->AddSlot()
[
Statuses[StatusIndex].ToSharedRef()
];
}
StatusOverflowWidget = CreateStatusOverflowWidget();
HorizontalBox->AddSlot()
[
StatusOverflowWidget.ToSharedRef()
];
OverlayWidget->AddSlot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Bottom)
.Padding(StatusPadding)
[
SNew(SBorder)
.Padding(StatusBorderPadding)
.BorderImage(FAppStyle::GetBrush(Style, ".AssetThumbnailStatusBar"))
.Visibility(this, &SAssetThumbnail::GetStatusBorderVisibility)
[
HorizontalBox
]
];
}
}
if (AllowAssetSpecificThumbnailOverlay.IsSet())
{
FAssetActionThumbnailOverlayInfo OutThumbnailInfo;
// Skip ALL older overlay if the new style is enabled
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
if (AssetDefinition && AssetDefinition->GetThumbnailActionOverlay(AssetData, OutThumbnailInfo))
{
constexpr int32 OverlayZOrder = 1;
if (OutThumbnailInfo.ActionImageWidget.IsValid())
{
constexpr float PaddingFromTopLeftBorder = 4.f;
OverlayWidget->AddSlot()
.ZOrder(OverlayZOrder)
.VAlign(VAlign_Top)
.HAlign(HAlign_Left)
.Padding(PaddingFromTopLeftBorder, PaddingFromTopLeftBorder, 0.f, 0.f)
[
SNew(SBox)
.WidthOverride(this, &SAssetThumbnail::GetPlayIndicatorSize)
.HeightOverride(this, &SAssetThumbnail::GetPlayIndicatorSize)
[
SNew(SBorder)
.BorderImage(FAppStyle::GetBrush(Style, ".AssetThumbnailBar"))
.Padding(this, &SAssetThumbnail::GetPlayIndicatorPadding)
.Visibility(this, &SAssetThumbnail::GetActionIconOverlayVisibility)
[
OutThumbnailInfo.ActionImageWidget.ToSharedRef()
]
]
];
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (OutThumbnailInfo.ActionButtonWidget.IsValid())
{
constexpr float CenterImageSize = 32.f;
OverlayWidget->AddSlot()
.ZOrder(OverlayZOrder)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
[
SNew(SBorder)
.Padding(0.f)
.BorderImage(FStyleDefaults::GetNoBrush())
.Visibility(this, &SAssetThumbnail::GetActionButtonVisibility)
[
SNew(SBox)
.WidthOverride(CenterImageSize)
.HeightOverride(CenterImageSize)
[
OutThumbnailInfo.ActionButtonWidget.ToSharedRef()
]
]
];
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
else
{
// Set the default style and padding for the Button
OutThumbnailInfo.ActionButtonArgs
.ButtonStyle(&FAppStyle::GetWidgetStyle<FButtonStyle>(Style, ".Action.Button"))
.ContentPadding(this, &SAssetThumbnail::GetPlayButtonContentPadding);
TAttribute<EVisibility> ActionVisibility = TAttribute<EVisibility>::CreateSP(this, &SAssetThumbnail::GetActionButtonVisibility);
TSharedRef<SButton> ActionButton = SNew(SButton);
ActionButton->Construct(OutThumbnailInfo.ActionButtonArgs);
ActionButton->SetVisibility(ActionVisibility);
ActionButton->SetToolTipText(OutThumbnailInfo.ActionButtonArgs._ToolTipText);
ActionButton->SetToolTip(OutThumbnailInfo.ActionButtonArgs._ToolTip);
ActionButton->SetCursor(EMouseCursor::Default);
TSharedRef<SWidget> ActionImageWidget = OutThumbnailInfo.ActionImageWidget.IsValid()
? OutThumbnailInfo.ActionImageWidget.ToSharedRef()
: SNew(SImage).Image(FAppStyle::GetBrush("ContentBrowser.AssetAction.PlayIcon"));
ActionButton->SetContent(ActionImageWidget);
constexpr float CenterImageSize = 32.f;
OverlayWidget->AddSlot()
.ZOrder(OverlayZOrder)
.VAlign(VAlign_Center)
.HAlign(HAlign_Center)
[
SNew(SBox)
.WidthOverride(CenterImageSize)
.HeightOverride(CenterImageSize)
[
ActionButton
]
];
}
}
}
else if (AssetTypeActions.IsValid())
{
// Does the asset provide an additional thumbnail overlay?
PRAGMA_DISABLE_DEPRECATION_WARNINGS
TSharedPtr<SWidget> AssetSpecificThumbnailOverlay = AssetTypeActions->GetThumbnailOverlay(AssetData);
PRAGMA_ENABLE_DEPRECATION_WARNINGS
if (AssetSpecificThumbnailOverlay.IsValid())
{
OverlayWidget->AddSlot()
[
AssetSpecificThumbnailOverlay.ToSharedRef()
];
}
}
}
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
ChildSlot
[
ContentWidget
];
}
else
{
ChildSlot
[
OverlayWidget
];
}
UpdateThumbnailVisibilities();
}
void UpdateThumbnailClass(const IAssetTypeActions* AssetTypeActions)
{
const FAssetData& AssetData = AssetThumbnail->GetAssetData();
ThumbnailClass = MakeWeakObjectPtr(const_cast<UClass*>(FClassIconFinder::GetIconClassForAssetData(AssetData, &bIsClassType)));
if (ThumbnailClass.IsValid())
{
ClassName = FText::FromString(ThumbnailClass->GetName());
}
const FName AssetClassName = AssetThumbnail->GetAssetData().AssetClassPath.GetAssetName();
ClassIconBrush = nullptr;
ThumbnailBrush = nullptr;
if (AssetTypeActions)
{
if (const FSlateBrush* AssetTypeThumbnail = AssetTypeActions->GetThumbnailBrush(AssetData, AssetClassName))
{
ThumbnailBrush = AssetTypeThumbnail;
}
if (const FSlateBrush* AssetTypeIcon = AssetTypeActions->GetIconBrush(AssetData, AssetClassName))
{
ClassIconBrush = AssetTypeIcon;
}
}
if (!ThumbnailBrush)
{
// For non-class types, use the default based upon the actual asset class
// This has the side effect of not showing a class icon for assets that don't have a proper thumbnail image available
const FName DefaultThumbnail = (bIsClassType) ? NAME_None : FName(*FString::Printf(TEXT("ClassThumbnail.%s"), *AssetClassName.ToString()));
ThumbnailBrush = FClassIconFinder::FindThumbnailForClass(ThumbnailClass.Get(), DefaultThumbnail);
}
if (!ClassIconBrush)
{
ClassIconBrush = FSlateIconFinder::FindIconBrushForClass(ThumbnailClass.Get());
}
}
FSlateColor GetHintBackgroundColor() const
{
const FLinearColor Color = HintColorAndOpacity.Get();
return FSlateColor( FLinearColor( Color.R, Color.G, Color.B, FMath::Lerp( 0.0f, 0.5f, Color.A ) ) );
}
virtual void OnMouseEnter( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) override
{
SCompoundWidget::OnMouseEnter( MyGeometry, MouseEvent );
if (AssetThumbnailEditMode.IsValid())
{
SetHover(true);
}
if ( bAllowRealTimeOnHovered )
{
AssetThumbnail->SetRealTime( true );
}
}
virtual void OnMouseLeave( const FPointerEvent& MouseEvent ) override
{
SCompoundWidget::OnMouseLeave( MouseEvent );
if (AssetThumbnailEditMode.IsValid())
{
SetHover(AssetThumbnailEditMode->IsEditingThumbnail());
}
if ( bAllowRealTimeOnHovered )
{
AssetThumbnail->SetRealTime( false );
}
}
virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override
{
if ( WidthLastFrame != AllottedGeometry.Size.X )
{
WidthLastFrame = static_cast<float>(AllottedGeometry.Size.X);
// The width changed, update the font
if ( GenericLabelTextBlock.IsValid() )
{
GenericLabelTextBlock->SetFont( GetTextFont() );
GenericLabelTextBlock->SetWrapTextAt( GetTextWrapWidth() );
}
if ( HintTextBlock.IsValid() )
{
HintTextBlock->SetFont( GetHintTextFont() );
HintTextBlock->SetWrapTextAt( GetTextWrapWidth() );
}
if (!OverlayInfo.IsEmpty())
{
const float ThumbnailWidth = WidthLastFrame - (StatusPadding * 2) - (StatusBorderPadding * 2) - GetAssetThumbnailBorderPadding();
const int32 MaxShownStatus = FMath::FloorToInt32(ThumbnailWidth / DefaultStatusSize);
constexpr int32 CutoffNumberBeforeResizing = 3;
if (MaxShownStatus < CutoffNumberBeforeResizing)
{
StatusSize = ThumbnailWidth / 3.f;
}
else
{
StatusSize = DefaultStatusSize;
}
StatusSize = FMath::FloorToFloat(StatusSize);
}
if (WidthLastFrame < PlayIndicatorMaxSizeThreshold)
{
PlayIndicatorSize = (PlayIndicatorDefaultSize * WidthLastFrame) / PlayIndicatorMaxSizeThreshold;
PlayIndicatorPadding = (PlayIndicatorDefaultPadding * WidthLastFrame) / PlayIndicatorMaxSizeThreshold;
PlayButtonContentPadding = (PlayButtonContentDefaultPadding * WidthLastFrame) / PlayIndicatorMaxSizeThreshold;
}
else
{
PlayIndicatorSize = PlayIndicatorDefaultSize;
PlayIndicatorPadding = PlayIndicatorDefaultPadding;
PlayButtonContentPadding = PlayButtonContentDefaultPadding;
}
PlayButtonContentPadding = FMath::FloorToFloat(PlayButtonContentDefaultPadding);
PlayIndicatorPadding = FMath::FloorToFloat(PlayIndicatorPadding);
PlayIndicatorSize = FMath::FloorToFloat(PlayIndicatorSize);
}
}
FOptionalSize GetPlayIndicatorSize() const
{
return FOptionalSize(PlayIndicatorSize);
}
FMargin GetPlayIndicatorPadding() const
{
return FMargin(PlayIndicatorPadding);
}
FMargin GetPlayButtonContentPadding() const
{
return FMargin(PlayButtonContentPadding);
}
TSharedRef<SDocumentationToolTip> GetDefaultToolTip() const
{
const FAssetData& AssetData = AssetThumbnail->GetAssetData();
const UClass* Class = FindObjectSafe<UClass>(AssetData.AssetClassPath);
TArray<FAssetDisplayInfo> OutSystemInfo;
TSharedRef<SBox> PromptBox = SNew(SBox);
TSharedRef<SVerticalBox> OverallTooltipVBox = SNew(SVerticalBox);
{
const FSlateBrush* ClassIcon = FAppStyle::GetDefaultBrush();
TOptional<FLinearColor> Color;
if (const UAssetDefinition* AssetDefinition = UAssetDefinitionRegistry::Get()->GetAssetDefinitionForClass(AssetData.GetClass()))
{
ClassIcon = AssetDefinition->GetIconBrush(AssetData, AssetData.AssetClassPath.GetAssetName());
Color = AssetDefinition->GetAssetColor();
}
if (AssetSystemInfoProvider.IsValid())
{
AssetSystemInfoProvider->PopulateAssetInfo(OutSystemInfo);
}
if (ClassIcon == nullptr || ClassIcon == FAppStyle::GetDefaultBrush())
{
ClassIcon = FSlateIconFinder::FindIconForClass(AssetData.GetClass()).GetIcon();
}
FText ClassNameText = NSLOCTEXT("AssetThumbnail", "ClassNameText", "Not Found");
if (Class != NULL)
{
ClassNameText = Class->GetDisplayNameText();
}
else if (!AssetData.AssetClassPath.IsNull())
{
ClassNameText = FText::FromString(AssetData.AssetClassPath.ToString());
}
const FText NameText = FText::FromString(AssetData.AssetName.ToString());
// Name/Type Slot
OverallTooltipVBox->AddSlot()
.AutoHeight()
[
SNew(SVerticalBox)
+ SVerticalBox::Slot()
.Padding(0.f, 0.f, 0.f, 6.f)
.AutoHeight()
[
SNew(STextBlock)
.Text(NameText)
.ColorAndOpacity(FStyleColors::White)
]
+ SVerticalBox::Slot()
.Padding(0.f, 0.f, 0.f, 6.f)
.AutoHeight()
[
SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.f, 0.f, 4.f, 0.f)
[
SNew(SBox)
.WidthOverride(16.f)
.HeightOverride(16.f)
[
SNew(SImage)
.Image(ClassIcon)
.ColorAndOpacity_Lambda([Color] () { return Color.IsSet() ? Color.GetValue() : FStyleColors::White;})
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(ClassNameText)
]
]
];
if (AdditionalTooltipInSmallView.IsSet())
{
OverallTooltipVBox->AddSlot()
[
SNew(SBox)
.Padding(this, &SAssetThumbnail::GetAdditionalSmallViewTooltipMargin)
[
AdditionalTooltipInSmallView.Get().ToSharedRef()
]
];
}
TSharedRef<SVerticalBox> StatusVerticalBox = SNew(SVerticalBox);
for (const FAssetDisplayInfo& AssetStatusInfo : OverlayInfo)
{
TSharedRef<SLayeredImage> StatusLayeredImage = SNew(SLayeredImage).Image(AssetStatusInfo.StatusIcon);
StatusLayeredImage->AddLayer(AssetStatusInfo.StatusIconOverlay);
StatusVerticalBox->AddSlot()
.Padding(0.f, 0.f, 0.f, 6.f)
.AutoHeight()
[
SNew(SHorizontalBox)
.Visibility(AssetStatusInfo.IsVisible)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(0.f, 0.f, 4.f, 0.f)
[
SNew(SBox)
.WidthOverride(16.f)
.HeightOverride(16.f)
[
StatusLayeredImage
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
.VAlign(VAlign_Center)
[
SNew(STextBlock)
.Text(AssetStatusInfo.StatusDescription)
]
];
}
// Status
OverallTooltipVBox->AddSlot()
.AutoHeight()
[
StatusVerticalBox
];
// Separator
OverallTooltipVBox->AddSlot()
.Padding(0.f,0.f, 0.f, 6.f)
.AutoHeight()
[
SNew(SSeparator)
.Orientation(Orient_Horizontal)
.Thickness(1.f)
.ColorAndOpacity(COLOR("#484848FF"))
.SeparatorImage(FAppStyle::Get().GetBrush("WhiteBrush"))
];
// More info
if (!OutSystemInfo.IsEmpty())
{
PromptBox->SetPadding(FMargin(0.f, 2.f, 0.f, 0.f));
PromptBox->SetContent(
SNew(SRichTextBlock)
.TextStyle(&FAppStyle::GetWidgetStyle<FTextBlockStyle>(Style, ".Tooltip.MoreInfoText"))
.Justification(ETextJustify::Center)
.Text(NSLOCTEXT("AssetThumbnail", "MoreInfoTooltip", "Hold<WrappedCommand/>for more"))
+ SRichTextBlock::WidgetDecorator(TEXT("WrappedCommand"), this, &SAssetThumbnail::OnCreateWidgetDecoratorWidget)
);
}
}
TSharedRef<SVerticalBox> ExtendedToolTipVerticalBox = SNew(SVerticalBox);
TSharedRef<SBox> ExtendedToolTip = SNew(SBox)
.Padding(FMargin(9.f, -9.f, 10.f, 6.f))
[
ExtendedToolTipVerticalBox
];
bool bWasSeparatorAddedForCollection = false;
for (const FAssetDisplayInfo& SystemInfo : OutSystemInfo)
{
if (!SystemInfo.StatusTitle.IsSet() || !SystemInfo.StatusDescription.IsSet())
{
continue;
}
// StatusTitle currently used to add a separator for Collection, need to be changed in future version to allow more configurability
const FName TitleName = FName(SystemInfo.StatusTitle.Get().ToString());
if (!bWasSeparatorAddedForCollection && TitleName == TEXT("Collection(s)"))
{
bWasSeparatorAddedForCollection = true;
// Separator
ExtendedToolTipVerticalBox->AddSlot()
.Padding(0.f,0.f, 0.f, 6.f)
.AutoHeight()
[
SNew(SSeparator)
.Orientation(Orient_Horizontal)
.Thickness(1.f)
.ColorAndOpacity(COLOR("#484848FF"))
.SeparatorImage(FAppStyle::Get().GetBrush("WhiteBrush"))
];
continue;
}
if ((SystemInfo.IsVisible.IsSet() && SystemInfo.IsVisible.Get().IsVisible()) || !SystemInfo.IsVisible.IsSet())
{
AddToExtendedToolTipInfoBox(ExtendedToolTipVerticalBox, SystemInfo.StatusIcon, SystemInfo.StatusTitle.Get(), SystemInfo.StatusDescription.Get());
}
}
return SNew(SDocumentationToolTip)
.OverrideExtendedToolTipContent(ExtendedToolTip)
.OverridePromptContent(PromptBox)
.AlwaysExpandTooltip(AlwaysExpandTooltip)
[
OverallTooltipVBox
];
}
private:
FMargin GetAdditionalSmallViewTooltipMargin() const
{
if (AdditionalTooltipInSmallView.IsSet())
{
const TSharedPtr<SWidget> AdditionalTooltip = AdditionalTooltipInSmallView.Get();
const bool bIsValidAndNotNullWidget = AdditionalTooltip.IsValid() && AdditionalTooltip != SNullWidget::NullWidget;
return bIsValidAndNotNullWidget && AdditionalTooltip->GetVisibility().IsVisible() ? FMargin(0.f, 0.f, 0.f, 6.f) : FMargin(0.f);
}
return FMargin(0.f);
}
FSlateWidgetRun::FWidgetRunInfo OnCreateWidgetDecoratorWidget(const FTextRunInfo& InRunInfo, const ISlateStyle* InStyle) const
{
TSharedRef<SWidget> CtrlAltWidget = SNew(SBox)
.Padding(5.f, 0.f)
[
SNew(SBorder)
.VAlign(VAlign_Center)
.BorderImage(FAppStyle::GetBrush(Style, ".ToolTip.CommandBorder"))
[
SNew(STextBlock)
.TextStyle(&FAppStyle::GetWidgetStyle<FTextBlockStyle>(Style, ".Tooltip.MoreInfoText"))
#if PLATFORM_MAC
.Text(NSLOCTEXT("AssetThumbnail", "CommandOptionLabel", " Command + Option "))
#else
.Text(NSLOCTEXT("AssetThumbnail", "CtrlAltLabel", " Ctrl + Alt "))
#endif
]
];
const TSharedRef<FSlateFontMeasure> FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
const int16 Baseline = FontMeasure->GetBaseline(FStyleDefaults::GetFontInfo());
return FSlateWidgetRun::FWidgetRunInfo(CtrlAltWidget, Baseline - 2);
}
void AddToExtendedToolTipInfoBox(const TSharedRef<SVerticalBox>& InfoBox, const TAttribute<const FSlateBrush*>& Icon, const FText& Key, const FText& Value) const
{
InfoBox->AddSlot()
.Padding(0.f, 0.f, 0.f, 6.f)
.AutoHeight()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(SBox)
.Visibility(Icon.IsSet()? EVisibility::Visible : EVisibility::Collapsed)
.WidthOverride(16.f)
.HeightOverride(16.f)
[
SNew(SImage)
.Image(Icon)
]
]
+SHorizontalBox::Slot()
.AutoWidth()
.Padding(0, 0, 4, 0)
[
SNew(STextBlock)
.Text(FText::Format(NSLOCTEXT("AssetThumbnailToolTip", "AssetViewTooltipFormat", "{0}:"), Key))
]
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(STextBlock)
.ColorAndOpacity(FStyleColors::White)
.Text(Value)
.WrapTextAt(700.0f)
]
];
}
const FSlateBrush* GetThumbnailBorderBrush() const
{
const FString ThumbnailBorder = TEXT(".AssetBorder");
return FAppStyle::GetBrush(Style, StringCast<ANSICHAR>(*ThumbnailBorder).Get());
}
EVisibility GetAssetColorVisibility() const
{
return ShowAssetColor.Get() ? EVisibility::Visible : EVisibility::Collapsed;
}
FMargin GetAssetColorMargin() const
{
constexpr float DefaultPadding = -2.f;
// Need this to be negative for the Overlay so that it overlaps with the actual border which will keep the ratio correct and won't let the thumbnail over the limits.
if (BorderPadding.IsSet())
{
// Get the negative of it so that the overlay border is always aligned correctly
return BorderPadding.Get() * -1.f;
}
return FMargin(DefaultPadding);
}
FMargin GetOverlayBorderMargin() const
{
constexpr float DefaultPadding = 2.f;
// Asset strip margin
if (BorderPadding.IsSet())
{
return BorderPadding.Get();
}
return FMargin(DefaultPadding);
}
EVisibility GetStatusBorderVisibility() const
{
// If not allowed hide it
if (AllowAssetStatusThumbnailOverlay.Get())
{
for (const FAssetDisplayInfo& AssetStatus : OverlayInfo)
{
if (AssetStatus.IsVisible.IsSet() && AssetStatus.IsVisible.Get().IsVisible())
{
if (!EditModeVisibility.IsSet() || !EditModeVisibility.Get().IsVisible())
{
return EVisibility::Visible;
}
}
}
}
return EVisibility::Collapsed;
}
EVisibility GetActionIconOverlayVisibility() const
{
const bool bIsEditModeVisible = EditModeVisibility.IsSet() && EditModeVisibility.Get().IsVisible();
return AllowAssetSpecificThumbnailOverlayIndicator.Get() && !IsHovered() && !bIsEditModeVisible ? EVisibility::Visible : EVisibility::Collapsed;
}
EVisibility GetActionButtonVisibility() const
{
return IsHovered() && AllowAssetSpecificThumbnailOverlay.Get() && (!EditModeVisibility.IsSet() || !EditModeVisibility.Get().IsVisible()) ? EVisibility::Visible : EVisibility::Collapsed;
}
TSharedRef<SWidget> CreateStatusWidget(int32 StatusIndex, const FAssetDisplayInfo& InStatusInfo) const
{
TSharedRef<SLayeredImage> StatusLayeredImage = SNew(SLayeredImage)
.Image(InStatusInfo.StatusIcon)
.DesiredSizeOverride(this, &SAssetThumbnail::GetStatusSizeForImage);
StatusLayeredImage->AddLayer(InStatusInfo.StatusIconOverlay);
return SNew(SBox)
.WidthOverride(this, &SAssetThumbnail::GetStatusSize)
.HeightOverride(this, &SAssetThumbnail::GetStatusSize)
.Visibility(this, &SAssetThumbnail::GetStatusVisibility, StatusIndex)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
StatusLayeredImage
];
}
EVisibility GetStatusVisibility(int32 StatusIndex) const
{
if (OverlayInfo.IsValidIndex(StatusIndex))
{
const FAssetDisplayInfo& AssetStatus = OverlayInfo[StatusIndex];
if (AssetStatus.IsVisible.IsSet() && AssetStatus.IsVisible.Get() == EVisibility::Visible)
{
return GetStatusVisibilityBasedOnGeometry(StatusIndex);
}
}
return EVisibility::Collapsed;
}
TSharedRef<SWidget> CreateStatusOverflowWidget() const
{
return SNew(SBox)
.WidthOverride(this, &SAssetThumbnail::GetStatusSize)
.HeightOverride(this, &SAssetThumbnail::GetStatusSize)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.Visibility(this, &SAssetThumbnail::GetStatusOverflowVisibility)
[
SNew(STextBlock)
.Font(this, &SAssetThumbnail::GetStatusOverflowFont)
.Text(this, &SAssetThumbnail::GetStatusOverflowText)
.ColorAndOpacity(FStyleColors::Foreground)
];
}
FSlateFontInfo GetStatusOverflowFont() const
{
FString FontStyle = TEXT(".StatusOverflowFont");
if (StatusSize != DefaultStatusSize)
{
FontStyle = TEXT(".StatusOverflowFontSmall");
}
return FAppStyle::GetFontStyle(Style, StringCast<ANSICHAR>(*FontStyle).Get());
}
FOptionalSize GetStatusSize() const
{
return FOptionalSize(StatusSize);
}
TOptional<FVector2D> GetStatusSizeForImage() const
{
return TOptional(FVector2D(StatusSize));
}
float GetAssetThumbnailBorderPadding() const
{
// Already take into account Left and Right
constexpr float DefaultPadding = 2.f;
// Asset strip margin
if (BorderPadding.IsSet())
{
// Get the negative of it so that the overlay border is always aligned correctly
const FMargin BorderMargin = BorderPadding.Get();
return BorderMargin.Left + BorderMargin.Right;
}
return DefaultPadding;
}
EVisibility GetStatusOverflowVisibility() const
{
const FGeometry ThumbnailGeometry = GetPaintSpaceGeometry();
const float ThumbnailWidth = ThumbnailGeometry.GetAbsoluteSize().X - (StatusPadding * 2) - (StatusBorderPadding * 2) - GetAssetThumbnailBorderPadding();
const int32 MaxShownStatus = FMath::FloorToInt32(ThumbnailWidth / StatusSize);
int32 ShownStatus = 0;
for (const FAssetDisplayInfo& Status : OverlayInfo)
{
if (Status.IsVisible.IsSet() && Status.IsVisible.Get().IsVisible())
{
ShownStatus++;
}
}
return ShownStatus > MaxShownStatus? EVisibility::Visible : EVisibility::Collapsed;
}
FText GetStatusOverflowText() const
{
FGeometry ThumbnailGeometry = GetPaintSpaceGeometry();
const float ThumbnailWidth = ThumbnailGeometry.GetAbsoluteSize().X - (StatusPadding * 2) - (StatusBorderPadding * 2) - GetAssetThumbnailBorderPadding();
const int32 MaxShownStatus = FMath::FloorToInt32(ThumbnailWidth / StatusSize);
int32 ShownStatus = 0;
for (const FAssetDisplayInfo& Status : OverlayInfo)
{
if (Status.IsVisible.IsSet() && Status.IsVisible.Get().IsVisible())
{
ShownStatus++;
}
}
// We need to add 1 to the HiddenStatus since the Overflow "status" will occupy 1 extra slot
constexpr int32 OccupiedStatusSlot = 1;
const int32 HiddenStatus = ShownStatus - MaxShownStatus + OccupiedStatusSlot;
return FText::Format(NSLOCTEXT("AssetThumbnail", "StatusOverflowText", "+{0}"), HiddenStatus);
}
EVisibility GetStatusVisibilityBasedOnGeometry(int32 InStatusIndex) const
{
int32 CollapsedStatusBeforeThis = 0;
for (int32 StatusIndex = 0; StatusIndex < InStatusIndex; StatusIndex++)
{
CollapsedStatusBeforeThis += Statuses[StatusIndex]->GetVisibility().IsVisible() ? 0 : 1;
}
const FGeometry ThumbnailGeometry = GetPaintSpaceGeometry();
const float ThumbnailWidth = ThumbnailGeometry.GetAbsoluteSize().X - (StatusPadding * 2) - (StatusBorderPadding * 2) - GetAssetThumbnailBorderPadding();
const int32 StatusIndexConsideringHiddenOnes = (InStatusIndex - CollapsedStatusBeforeThis);
const int32 ShownStatus = FMath::FloorToInt32(ThumbnailWidth / StatusSize);
const int32 StatusIndexConsideringClippedStatusIfVisible = StatusOverflowWidget.IsValid() && StatusOverflowWidget->GetVisibility().IsVisible() ? StatusIndexConsideringHiddenOnes + 1 : StatusIndexConsideringHiddenOnes;
return StatusIndexConsideringClippedStatusIfVisible < ShownStatus ? EVisibility::Visible : EVisibility::Collapsed;
}
void OnAssetDataChanged()
{
if ( GenericLabelTextBlock.IsValid() )
{
GenericLabelTextBlock->SetText( GetLabelText() );
}
if ( HintTextBlock.IsValid() )
{
HintTextBlock->SetText( GetLabelText() );
}
// Check if the asset has a thumbnail.
const FObjectThumbnail* ObjectThumbnail = NULL;
FThumbnailMap ThumbnailMap;
if( AssetThumbnail->GetAsset() )
{
FName FullAssetName = FName( *(AssetThumbnail->GetAssetData().GetFullName()) );
TArray<FName> ObjectNames;
ObjectNames.Add( FullAssetName );
ThumbnailTools::ConditionallyLoadThumbnailsForObjects(ObjectNames, ThumbnailMap);
ObjectThumbnail = ThumbnailMap.Find( FullAssetName );
}
bHasRenderedThumbnail = ObjectThumbnail && !ObjectThumbnail->IsEmpty();
ViewportFadeAnimation.JumpToEnd();
AssetThumbnail->GetViewportRenderTargetTexture(); // Access the render texture to push it on the stack if it isnt already rendered
const FAssetData& AssetData = AssetThumbnail->GetAssetData();
UClass* Class = FindObject<UClass>(AssetData.AssetClassPath);
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(TEXT("AssetTools"));
TSharedPtr<IAssetTypeActions> AssetTypeActions;
if ( Class != NULL )
{
TWeakPtr<IAssetTypeActions> TypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(Class);
if (TypeActions.IsValid())
{
AssetTypeActions = TypeActions.Pin();
}
}
UpdateThumbnailClass(AssetTypeActions.Get());
AssetColor = FLinearColor::White;
if( AssetTypeColorOverride.IsSet() )
{
AssetColor = AssetTypeColorOverride.GetValue();
}
else if ( AssetTypeActions.IsValid() )
{
AssetColor = AssetTypeActions->GetTypeColor();
}
if (AssetColorStripWidget.IsValid())
{
AssetColorStripWidget->SetBorderBackgroundColor(AssetColor);
}
UpdateThumbnailVisibilities();
}
FSlateFontInfo GetTextFont() const
{
return FAppStyle::GetFontStyle( WidthLastFrame <= 64 ? FAppStyle::Join(Style, ".FontSmall") : FAppStyle::Join(Style, ".Font") );
}
FSlateFontInfo GetHintTextFont() const
{
return FAppStyle::GetFontStyle( WidthLastFrame <= 64 ? FAppStyle::Join(Style, ".HintFontSmall") : FAppStyle::Join(Style, ".HintFont") );
}
float GetTextWrapWidth() const
{
return WidthLastFrame - GenericThumbnailBorderPadding * 2.f;
}
const FSlateBrush* GetAssetBackgroundBrush() const
{
return FAppStyle::GetBrush(AssetBackgroundBrushName);
}
const FSlateBrush* GetClassBackgroundBrush() const
{
return FAppStyle::GetBrush(ClassBackgroundBrushName);
}
FLinearColor GetViewportColorAndOpacity() const
{
return FLinearColor(1, 1, 1, ViewportFadeCurve.GetLerp());
}
EVisibility GetViewportVisibility() const
{
return bHasRenderedThumbnail ? EVisibility::Visible : EVisibility::Collapsed;
}
/** The height of the color element (if it's oriented along the bottom edge) or its width (if it's oriented along the right edge) */
float GetAssetColorThickness() const
{
if (BorderPadding.IsSet())
{
// Get half the vertical or horizontal padding value
// The user specifies the intended line thickness with a uniform padding value, so we need to compensate
return ColorStripOrientation == EThumbnailColorStripOrientation::HorizontalBottomEdge
? BorderPadding.Get().GetTotalSpaceAlong<EOrientation::Orient_Vertical>() / 2.f
: BorderPadding.Get().GetTotalSpaceAlong<Orient_Horizontal>() / 2.f;
}
return 2.0f;
}
FMargin GetAssetColorPadding() const
{
if (ColorStripOrientation == EThumbnailColorStripOrientation::HorizontalBottomEdge)
{
const float Height = GetAssetColorThickness();
return FMargin(0, Height, 0, 0);
}
else
{
const float Width = GetAssetColorThickness();
return FMargin(Width, 0, 0, 0);
}
}
const FSlateBrush* GetClassThumbnailBrush() const
{
if (ClassThumbnailBrushOverride.IsNone())
{
return ThumbnailBrush;
}
else
{
// Instead of getting the override thumbnail directly from the editor style here get it from the
// ClassIconFinder since it may have additional styles registered which can be searched by passing
// it as a default with no class to search for.
return FClassIconFinder::FindThumbnailForClass(nullptr, ClassThumbnailBrushOverride);
}
}
EVisibility GetClassThumbnailVisibility() const
{
if(!bHasRenderedThumbnail)
{
const FSlateBrush* ClassThumbnailBrush = GetClassThumbnailBrush();
if( (ClassThumbnailBrush && ThumbnailClass.Get()) || !ClassThumbnailBrushOverride.IsNone() )
{
return EVisibility::Visible;
}
}
return EVisibility::Collapsed;
}
EVisibility GetGenericThumbnailVisibility() const
{
return (bHasRenderedThumbnail && ViewportFadeAnimation.IsAtEnd()) ? EVisibility::Collapsed : EVisibility::Visible;
}
FText GetClassName() const
{
return ClassName;
}
const FSlateBrush* GetClassIconBrush() const
{
return ClassIconBrush;
}
FMargin GetClassIconPadding() const
{
if (ColorStripOrientation == EThumbnailColorStripOrientation::HorizontalBottomEdge)
{
const float Height = GetAssetColorThickness();
return FMargin(0, 0, 0, Height);
}
else
{
const float Width = GetAssetColorThickness();
return FMargin(0, 0, Width, 0);
}
}
EVisibility GetHintTextVisibility() const
{
if ( bAllowHintText && ( bHasRenderedThumbnail || !GenericLabelTextBlock.IsValid() ) && HintColorAndOpacity.Get().A > 0 )
{
return EVisibility::Visible;
}
return EVisibility::Collapsed;
}
void OnThumbnailRendered(const FAssetData& AssetData)
{
if ( !bHasRenderedThumbnail && AssetData == AssetThumbnail->GetAssetData() && ShouldRender() )
{
OnRenderedThumbnailChanged( true );
ViewportFadeAnimation.Play( this->AsShared() );
}
}
void OnThumbnailRenderFailed(const FAssetData& AssetData)
{
if ( bHasRenderedThumbnail && AssetData == AssetThumbnail->GetAssetData() )
{
OnRenderedThumbnailChanged( false );
}
}
bool ShouldRender() const
{
const FAssetData& AssetData = AssetThumbnail->GetAssetData();
// Never render a thumbnail for an invalid asset
if ( !AssetData.IsValid() )
{
return false;
}
if( AssetData.IsAssetLoaded() )
{
// Loaded asset, return true if there is a rendering info for it
UObject* Asset = AssetData.GetAsset();
FThumbnailRenderingInfo* RenderInfo = GUnrealEd->GetThumbnailManager()->GetRenderingInfo( Asset );
if ( RenderInfo != NULL && RenderInfo->Renderer != NULL )
{
return true;
}
}
const FObjectThumbnail* CachedThumbnail = ThumbnailTools::FindCachedThumbnail(*AssetData.GetFullName());
if ( CachedThumbnail != NULL )
{
// There is a cached thumbnail for this asset, we should render it
return !CachedThumbnail->IsEmpty();
}
if ( AssetData.AssetClassPath != UBlueprint::StaticClass()->GetClassPathName() )
{
// If we are not a blueprint, see if the CDO of the asset's class has a rendering info
// Blueprints can't do this because the rendering info is based on the generated class
UClass* AssetClass = FindObject<UClass>(AssetData.AssetClassPath);
if ( AssetClass )
{
FThumbnailRenderingInfo* RenderInfo = GUnrealEd->GetThumbnailManager()->GetRenderingInfo( AssetClass->GetDefaultObject() );
if ( RenderInfo != NULL && RenderInfo->Renderer != NULL )
{
return true;
}
}
}
// Always render thumbnails with custom thumbnails
FString CustomThumbnailTagValue;
if (AssetData.GetTagValue(FAssetThumbnailPool::CustomThumbnailTagName, CustomThumbnailTagValue))
{
return true;
}
// Unloaded blueprint or asset that may have a custom thumbnail, check to see if there is a thumbnail in the package to render
FString PackageFilename;
if ( FPackageName::DoesPackageExist(AssetData.PackageName.ToString(), &PackageFilename) )
{
TSet<FName> ObjectFullNames;
FThumbnailMap ThumbnailMap;
FName ObjectFullName = FName(*AssetData.GetFullName());
ObjectFullNames.Add(ObjectFullName);
ThumbnailTools::LoadThumbnailsFromPackage(PackageFilename, ObjectFullNames, ThumbnailMap);
const FObjectThumbnail* ThumbnailPtr = ThumbnailMap.Find(ObjectFullName);
if (ThumbnailPtr)
{
const FObjectThumbnail& ObjectThumbnail = *ThumbnailPtr;
return ObjectThumbnail.GetImageWidth() > 0 && ObjectThumbnail.GetImageHeight() > 0 && ObjectThumbnail.GetCompressedDataSize() > 0;
}
}
return false;
}
FText GetLabelText() const
{
if( Label != EThumbnailLabel::NoLabel )
{
if ( Label == EThumbnailLabel::ClassName )
{
return GetAssetClassDisplayName();
}
else if ( Label == EThumbnailLabel::AssetName )
{
return GetAssetDisplayName();
}
}
return FText::GetEmpty();
}
FText GetDisplayNameForClass( UClass* Class, const FAssetData* AssetData = nullptr) const
{
FText ClassDisplayName;
if ( Class )
{
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
TWeakPtr<IAssetTypeActions> AssetTypeActions = AssetToolsModule.Get().GetAssetTypeActionsForClass(Class);
if ( AssetTypeActions.IsValid() )
{
if (AssetData != nullptr)
{
ClassDisplayName = AssetTypeActions.Pin()->GetDisplayNameFromAssetData(*AssetData);
}
if (ClassDisplayName.IsEmpty())
{
ClassDisplayName = AssetTypeActions.Pin()->GetName();
}
}
if ( ClassDisplayName.IsEmpty() )
{
ClassDisplayName = Class->GetDisplayNameText();
}
}
return ClassDisplayName;
}
FText GetAssetClassDisplayName() const
{
const FAssetData& AssetData = AssetThumbnail->GetAssetData();
FTopLevelAssetPath AssetClass = AssetData.AssetClassPath;
UClass* Class = FindObjectSafe<UClass>(AssetClass);
if ( Class )
{
return GetDisplayNameForClass( Class, &AssetData );
}
return FText::FromString(AssetClass.GetAssetName().ToString());
}
FText GetAssetDisplayName() const
{
const FAssetData& AssetData = AssetThumbnail->GetAssetData();
if ( AssetData.GetClass() == UClass::StaticClass() )
{
UClass* Class = Cast<UClass>( AssetData.GetAsset() );
return GetDisplayNameForClass( Class );
}
return FText::FromName(AssetData.AssetName);
}
void OnRenderedThumbnailChanged( bool bInHasRenderedThumbnail )
{
bHasRenderedThumbnail = bInHasRenderedThumbnail;
UpdateThumbnailVisibilities();
}
void UpdateThumbnailVisibilities()
{
// Either the generic label or thumbnail should be shown, but not both at once
const EVisibility ClassThumbnailVisibility = GetClassThumbnailVisibility();
if( GenericThumbnailImage.IsValid() )
{
GenericThumbnailImage->SetVisibility( ClassThumbnailVisibility );
}
if( GenericLabelTextBlock.IsValid() )
{
GenericLabelTextBlock->SetVisibility( (ClassThumbnailVisibility == EVisibility::Visible) ? EVisibility::Collapsed : EVisibility::Visible );
}
const EVisibility ViewportVisibility = GetViewportVisibility();
if( RenderedThumbnailWidget.IsValid() )
{
RenderedThumbnailWidget->SetVisibility( ViewportVisibility );
if( ClassIconWidget.IsValid() )
{
static const IConsoleVariable* CVarEnableContentBrowserNewStyle = IConsoleManager::Get().FindConsoleVariable(TEXT("ContentBrowser.EnableNewStyle"));
const bool bEnableContentBrowserNewStyle = CVarEnableContentBrowserNewStyle && CVarEnableContentBrowserNewStyle->GetBool();
if (bEnableContentBrowserNewStyle)
{
ClassIconWidget->SetVisibility(TAttribute<EVisibility>::CreateLambda([this, ViewportVisibility] () { return !EditModeVisibility.IsSet() || !EditModeVisibility.Get().IsVisible() ? ViewportVisibility : EVisibility::Collapsed; }));
}
else
{
ClassIconWidget->SetVisibility( ViewportVisibility );
}
}
}
}
TOptional<FVector2D> GetGenericThumbnailDesiredSize() const
{
const int32 Size = GenericThumbnailSize.Get();
return FVector2D(Size, Size);
}
private:
TSharedPtr<SAssetThumbnailEditModeTools> AssetThumbnailEditMode;
TSharedPtr<STextBlock> GenericLabelTextBlock;
TSharedPtr<STextBlock> HintTextBlock;
TSharedPtr<SImage> GenericThumbnailImage;
TSharedPtr<SBorder> ClassIconWidget;
TSharedPtr<SBorder> RenderedThumbnailWidget;
TSharedPtr<SBorder> AssetBackgroundWidget;
TSharedPtr<SBorder> AssetColorStripWidget;
TSharedPtr<FAssetThumbnail> AssetThumbnail;
FCurveSequence ViewportFadeAnimation;
FCurveHandle ViewportFadeCurve;
FLinearColor AssetColor;
TOptional<FLinearColor> AssetTypeColorOverride;
float WidthLastFrame;
float GenericThumbnailBorderPadding;
bool bHasRenderedThumbnail;
FName Style;
TAttribute< FText > HighlightedText;
EThumbnailLabel::Type Label;
TAttribute< FLinearColor > HintColorAndOpacity;
TAttribute<int32> GenericThumbnailSize;
EThumbnailColorStripOrientation ColorStripOrientation;
TSharedPtr<SWidget> StatusOverflowWidget;
float StatusSize = 16.f;
const float DefaultStatusSize = 16.f;
const float StatusPadding = 4.f;
const float StatusBorderPadding = 2.f;
float PlayIndicatorPadding = 4.f;
const float PlayIndicatorDefaultPadding = 4.f;
float PlayButtonContentPadding = 8.f;
const float PlayButtonContentDefaultPadding = 8.f;
float PlayIndicatorSize = 20.f;
const float PlayIndicatorMaxSizeThreshold = 64.f;
const float PlayIndicatorDefaultSize = 20.f;
TAttribute<FMargin> BorderPadding;
TArray<FAssetDisplayInfo> OverlayInfo;
TArray<TSharedPtr<SWidget>> Statuses;
TSharedPtr<IAssetSystemInfoProvider> AssetSystemInfoProvider;
TAttribute<bool> ShowAssetColor;
TAttribute<const FSlateBrush*> AssetBorderImageOverride;
TAttribute<EVisibility> EditModeVisibility;
TAttribute<bool> AlwaysExpandTooltip;
TAttribute<TSharedPtr<SWidget>> AdditionalTooltipInSmallView;
TAttribute<bool> AllowAssetSpecificThumbnailOverlay;
TAttribute<bool> AllowAssetSpecificThumbnailOverlayIndicator;
TAttribute<bool> AllowAssetStatusThumbnailOverlay;
bool bAllowHintText;
bool bAllowRealTimeOnHovered;
/** The name of the thumbnail which should be used instead of the class thumbnail. */
FName ClassThumbnailBrushOverride;
FName AssetBackgroundBrushName;
FName ClassBackgroundBrushName;
const FSlateBrush* ThumbnailBrush;
const FSlateBrush* ClassIconBrush;
FText ClassName;
/** The class to use when finding the thumbnail. */
TWeakObjectPtr<UClass> ThumbnailClass;
/** Are we showing a class type? (UClass, UBlueprint) */
bool bIsClassType;
};
void SAssetThumbnailToolTip::Construct(const FArguments& InArgs)
{
AssetThumbnail = InArgs._AssetThumbnail;
SToolTip::Construct(
SToolTip::FArguments()
.TextMargin(FMargin(1.f, -3.f))
.BorderImage(FAppStyle::GetBrush("AssetThumbnail.Tooltip.Border"))
);
}
bool SAssetThumbnailToolTip::IsEmpty() const
{
return !AssetThumbnail.IsValid();
}
void SAssetThumbnailToolTip::OnOpening()
{
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
TSharedPtr<SAssetThumbnail> AssetViewItemPin = AssetThumbnail.Pin();
if (AssetViewItemPin.IsValid())
{
TSharedRef<SDocumentationToolTip> AssetToolTipRef = AssetViewItemPin->GetDefaultToolTip();
SetContentWidget(AssetToolTipRef);
AssetToolTip = AssetToolTipRef.ToSharedPtr();
}
}
}
void SAssetThumbnailToolTip::OnClosed()
{
ResetContentWidget();
}
bool SAssetThumbnailToolTip::IsInteractive() const
{
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
TSharedPtr<SAssetThumbnail> AssetViewItemPin = AssetThumbnail.Pin();
// Use the SDocumentationTooltip IsInteractive only if we have something to show and if it should not be expanded by default.
// This way we can avoid our tooltip to be kept in place when hovering.
const bool bShouldAlwaysBeExpanded = AlwaysExpandTooltip.IsSet() && AlwaysExpandTooltip.Get();
if (!IsEmpty() && AssetViewItemPin.IsValid() && AssetToolTip.IsValid() && !bShouldAlwaysBeExpanded)
{
return AssetToolTip->IsInteractive();
}
return false;
}
return false;
}
FAssetThumbnail::FAssetThumbnail( UObject* InAsset, uint32 InWidth, uint32 InHeight, const TSharedPtr<class FAssetThumbnailPool>& InThumbnailPool )
: ThumbnailPool(InThumbnailPool)
, AssetData(InAsset ? FAssetData(InAsset) : FAssetData())
, Width( InWidth )
, Height( InHeight )
{
if ( InThumbnailPool.IsValid() )
{
InThumbnailPool->AddReferencer(*this);
}
}
FAssetThumbnail::FAssetThumbnail( const FAssetData& InAssetData , uint32 InWidth, uint32 InHeight, const TSharedPtr<class FAssetThumbnailPool>& InThumbnailPool )
: ThumbnailPool( InThumbnailPool )
, AssetData ( InAssetData )
, Width( InWidth )
, Height( InHeight )
{
if ( InThumbnailPool.IsValid() )
{
InThumbnailPool->AddReferencer(*this);
}
}
FAssetThumbnail::~FAssetThumbnail()
{
if ( ThumbnailPool.IsValid() )
{
ThumbnailPool.Pin()->RemoveReferencer(*this);
}
}
FIntPoint FAssetThumbnail::GetSize() const
{
return FIntPoint( Width, Height );
}
FSlateShaderResource* FAssetThumbnail::GetViewportRenderTargetTexture() const
{
FSlateTexture2DRHIRef* Texture = NULL;
if ( ThumbnailPool.IsValid() )
{
Texture = ThumbnailPool.Pin()->AccessTexture( AssetData, Width, Height );
}
if( !Texture || !Texture->IsValid() )
{
return NULL;
}
return Texture;
}
UObject* FAssetThumbnail::GetAsset() const
{
if ( AssetData.IsValid() )
{
return AssetData.GetSoftObjectPath().ResolveObject();
}
else
{
return NULL;
}
}
const FAssetData& FAssetThumbnail::GetAssetData() const
{
return AssetData;
}
void FAssetThumbnail::SetAsset( const UObject* InAsset )
{
SetAsset( FAssetData(InAsset) );
}
void FAssetThumbnail::SetAsset( const FAssetData& InAssetData )
{
if ( ThumbnailPool.IsValid() )
{
ThumbnailPool.Pin()->RemoveReferencer(*this);
}
if ( InAssetData.IsValid() )
{
AssetData = InAssetData;
if ( ThumbnailPool.IsValid() )
{
ThumbnailPool.Pin()->AddReferencer(*this);
}
}
else
{
AssetData = FAssetData();
}
AssetDataChangedEvent.Broadcast();
}
TSharedRef<SWidget> FAssetThumbnail::MakeThumbnailWidget( const FAssetThumbnailConfig& InConfig )
{
PRAGMA_DISABLE_DEPRECATION_WARNINGS
const TAttribute<bool> AssetThumbnailOverlayAttribute =
InConfig.AllowAssetSpecificThumbnailOverlay.IsSet()
? InConfig.AllowAssetSpecificThumbnailOverlay
: InConfig.bAllowAssetSpecificThumbnailOverlay;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
// If not set use the PlayButton attribute instead
const TAttribute<bool> AssetThumbnailOverlayIndicatorAttribute =
InConfig.AllowAssetSpecificThumbnailOverlayIndicator.IsSet()
? InConfig.AllowAssetSpecificThumbnailOverlayIndicator
: AssetThumbnailOverlayAttribute;
TSharedPtr<SWidget> ThumbnailWidget = nullptr;
if (UE::Editor::ContentBrowser::IsNewStyleEnabled())
{
ThumbnailWidget =
SNew(SAssetThumbnail)
.AssetThumbnail(SharedThis(this))
.ThumbnailPool(ThumbnailPool.Pin())
.AllowFadeIn(InConfig.bAllowFadeIn)
.ForceGenericThumbnail(InConfig.bForceGenericThumbnail)
.Label(InConfig.ThumbnailLabel)
.HighlightedText(InConfig.HighlightedText)
.HintColorAndOpacity(InConfig.HintColorAndOpacity)
.AllowHintText(InConfig.bAllowHintText)
.AllowRealTimeOnHovered(InConfig.bAllowRealTimeOnHovered)
.ClassThumbnailBrushOverride(InConfig.ClassThumbnailBrushOverride)
.AllowAssetSpecificThumbnailOverlay(AssetThumbnailOverlayAttribute)
.AllowAssetSpecificThumbnailOverlayIndicator(AssetThumbnailOverlayIndicatorAttribute)
.AssetTypeColorOverride(InConfig.AssetTypeColorOverride)
PRAGMA_DISABLE_DEPRECATION_WARNINGS
.Padding(InConfig.Padding)
PRAGMA_ENABLE_DEPRECATION_WARNINGS
.BorderPadding(InConfig.BorderPadding)
.GenericThumbnailSize(InConfig.GenericThumbnailSize)
.AssetSystemInfoProvider(InConfig.AssetSystemInfoProvider)
.AdditionalTooltipInSmallView(InConfig.AdditionalTooltipInSmallView)
.AllowAssetStatusThumbnailOverlay(InConfig.AllowAssetStatusThumbnailOverlay)
.ShowAssetColor(InConfig.ShowAssetColor)
.CanDisplayEditModePrimitiveTools(InConfig.bCanDisplayEditModePrimitiveTools)
.IsEditModeVisible(InConfig.IsEditModeVisible)
.AssetBorderImageOverride(InConfig.AssetBorderImageOverride)
.AlwaysExpandTooltip(InConfig.AlwaysExpandTooltip)
.ColorStripOrientation(InConfig.ColorStripOrientation);
}
else
{
ThumbnailWidget =
SNew(SAssetThumbnail)
.AssetThumbnail(SharedThis(this))
.ThumbnailPool(ThumbnailPool.Pin())
.AllowFadeIn(InConfig.bAllowFadeIn)
.ForceGenericThumbnail(InConfig.bForceGenericThumbnail)
.Label(InConfig.ThumbnailLabel)
.HighlightedText(InConfig.HighlightedText)
.HintColorAndOpacity(InConfig.HintColorAndOpacity)
.AllowHintText(InConfig.bAllowHintText)
.AllowRealTimeOnHovered(InConfig.bAllowRealTimeOnHovered)
.ClassThumbnailBrushOverride(InConfig.ClassThumbnailBrushOverride)
.AllowAssetSpecificThumbnailOverlay(AssetThumbnailOverlayAttribute)
.AssetTypeColorOverride(InConfig.AssetTypeColorOverride)
PRAGMA_DISABLE_DEPRECATION_WARNINGS
.Padding(InConfig.Padding)
PRAGMA_ENABLE_DEPRECATION_WARNINGS
.GenericThumbnailSize(InConfig.GenericThumbnailSize)
.ColorStripOrientation(InConfig.ColorStripOrientation);
}
return ThumbnailWidget.ToSharedRef();
}
void FAssetThumbnail::RefreshThumbnail()
{
if ( ThumbnailPool.IsValid() && AssetData.IsValid() )
{
ThumbnailPool.Pin()->RefreshThumbnail( SharedThis(this) );
}
}
void FAssetThumbnail::SetRealTime(bool bRealTime)
{
if (ThumbnailPool.IsValid() && AssetData.IsValid())
{
ThumbnailPool.Pin()->SetRealTimeThumbnail( SharedThis(this), bRealTime );
}
}
FAssetThumbnailPool::FAssetThumbnailPool( uint32 InNumInPool, double InMaxFrameTimeAllowance, uint32 InMaxRealTimeThumbnailsPerFrame )
: NumInPool( InNumInPool )
, MaxRealTimeThumbnailsPerFrame( InMaxRealTimeThumbnailsPerFrame )
, MaxFrameTimeAllowance( InMaxFrameTimeAllowance )
{
FCoreUObjectDelegates::OnAssetLoaded.AddRaw(this, &FAssetThumbnailPool::OnAssetLoaded);
UThumbnailManager::Get().GetOnThumbnailDirtied().AddRaw(this, &FAssetThumbnailPool::OnThumbnailDirtied);
// Add the custom thumbnail tag to the list of tags that the asset registry can parse
TSet<FName>& MetaDataTagsForAssetRegistry = UObject::GetMetaDataTagsForAssetRegistry();
MetaDataTagsForAssetRegistry.Add(CustomThumbnailTagName);
}
FAssetThumbnailPool::~FAssetThumbnailPool()
{
UThumbnailManager* ThumbnailManager = UThumbnailManager::TryGet();
if ( IsValid( ThumbnailManager ) &&
!ThumbnailManager->HasAnyFlags( RF_BeginDestroyed | RF_FinishDestroyed ) )
{
ThumbnailManager->GetOnThumbnailDirtied().RemoveAll(this);
}
FCoreUObjectDelegates::OnAssetLoaded.RemoveAll(this);
// Release all the texture resources
ReleaseResources();
}
FAssetThumbnailPool::FThumbnailInfo::~FThumbnailInfo()
{
if( ThumbnailTexture )
{
delete ThumbnailTexture;
ThumbnailTexture = NULL;
}
if( ThumbnailRenderTarget )
{
delete ThumbnailRenderTarget;
ThumbnailRenderTarget = NULL;
}
}
void FAssetThumbnailPool::ReleaseResources()
{
// Clear all pending render requests
ThumbnailsToRenderStack.Empty();
RealTimeThumbnails.Empty();
RealTimeThumbnailsToRender.Empty();
TArray< TSharedRef<FThumbnailInfo> > ThumbnailsToRelease;
for( auto ThumbIt = ThumbnailToTextureMap.CreateConstIterator(); ThumbIt; ++ThumbIt )
{
ThumbnailsToRelease.Add(ThumbIt.Value());
}
ThumbnailToTextureMap.Empty();
for( auto ThumbIt = FreeThumbnails.CreateConstIterator(); ThumbIt; ++ThumbIt )
{
ThumbnailsToRelease.Add(*ThumbIt);
}
FreeThumbnails.Empty();
for ( auto ThumbIt = ThumbnailsToRelease.CreateConstIterator(); ThumbIt; ++ThumbIt )
{
const TSharedRef<FThumbnailInfo>& Thumb = *ThumbIt;
// Release rendering resources
FThumbnailInfo_RenderThread ThumbInfo = Thumb.Get();
ENQUEUE_RENDER_COMMAND(ReleaseThumbnailResources)(
[ThumbInfo](FRHICommandListImmediate& RHICmdList)
{
ThumbInfo.ThumbnailTexture->ClearTextureData();
ThumbInfo.ThumbnailTexture->ReleaseResource();
ThumbInfo.ThumbnailRenderTarget->ReleaseResource();
});
}
// Wait for all resources to be released
FlushRenderingCommands();
// Make sure there are no more references to any of our thumbnails now that rendering commands have been flushed
for ( auto ThumbIt = ThumbnailsToRelease.CreateConstIterator(); ThumbIt; ++ThumbIt )
{
const TSharedRef<FThumbnailInfo>& Thumb = *ThumbIt;
if ( !Thumb.IsUnique() )
{
ensureMsgf(0, TEXT("Thumbnail info for '%s' is still referenced by '%d' other objects"), *Thumb->AssetData.GetObjectPathString(), Thumb.GetSharedReferenceCount());
}
}
}
TStatId FAssetThumbnailPool::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT( FAssetThumbnailPool, STATGROUP_Tickables );
}
bool FAssetThumbnailPool::IsTickable() const
{
return RecentlyLoadedAssets.Num() > 0 || ThumbnailsToRenderStack.Num() > 0 || RealTimeThumbnails.Num() > 0 || bWereShadersCompilingLastFrame || (GShaderCompilingManager && GShaderCompilingManager->IsCompiling());
}
void FAssetThumbnailPool::Tick( float DeltaTime )
{
// If throttling do not tick unless drag dropping which could have a thumbnail as the cursor decorator
if (FSlateApplication::IsInitialized() && !FSlateApplication::Get().IsDragDropping() && !FSlateThrottleManager::Get().IsAllowingExpensiveTasks() && !FSlateApplication::Get().AnyMenusVisible())
{
return;
}
const bool bAreShadersCompiling = (GShaderCompilingManager && GShaderCompilingManager->IsCompiling());
if (bWereShadersCompilingLastFrame && !bAreShadersCompiling)
{
ThumbnailsToRenderStack.Reset();
// Reschedule visible thumbnails to be rerendered now that shaders are finished compiling
for (auto ThumbIt = ThumbnailToTextureMap.CreateIterator(); ThumbIt; ++ThumbIt)
{
ThumbnailsToRenderStack.Push(ThumbIt.Value());
}
}
bWereShadersCompilingLastFrame = bAreShadersCompiling;
TRACE_CPUPROFILER_EVENT_SCOPE(FAssetThumbnailPool::Tick);
// If there were any assets loaded since last frame that we are currently displaying thumbnails for, push them on the render stack now.
if ( RecentlyLoadedAssets.Num() > 0 )
{
for ( int32 LoadedAssetIdx = 0; LoadedAssetIdx < RecentlyLoadedAssets.Num(); ++LoadedAssetIdx )
{
RefreshThumbnailsFor(RecentlyLoadedAssets[LoadedAssetIdx]);
}
RecentlyLoadedAssets.Empty();
}
// If we have dynamic thumbnails and we are done rendering the last batch of dynamic thumbnails, start a new batch as long as real-time thumbnails are enabled
const bool bIsInPIEOrSimulate = GEditor->PlayWorld != NULL || GEditor->bIsSimulatingInEditor;
const bool bShouldUseRealtimeThumbnails = GetDefault<UContentBrowserSettings>()->RealTimeThumbnails && !bIsInPIEOrSimulate;
if ( bShouldUseRealtimeThumbnails && RealTimeThumbnails.Num() > 0 && RealTimeThumbnailsToRender.Num() == 0 )
{
double CurrentTime = FPlatformTime::Seconds();
for ( int32 ThumbIdx = RealTimeThumbnails.Num() - 1; ThumbIdx >= 0; --ThumbIdx )
{
const TSharedRef<FThumbnailInfo>& Thumb = RealTimeThumbnails[ThumbIdx];
if ( Thumb->AssetData.IsAssetLoaded() )
{
// Only render thumbnails that have been requested recently
if ( (CurrentTime - Thumb->LastAccessTime) < 1.f )
{
RealTimeThumbnailsToRender.Add(Thumb);
}
}
else
{
RealTimeThumbnails.RemoveAt(ThumbIdx);
}
}
}
uint32 NumRealTimeThumbnailsRenderedThisFrame = 0;
// If there are any thumbnails to render, pop one off the stack and render it.
if( ThumbnailsToRenderStack.Num() + RealTimeThumbnailsToRender.Num() > 0 )
{
double FrameStartTime = FPlatformTime::Seconds();
// Render as many thumbnails as we are allowed to
while( ThumbnailsToRenderStack.Num() + RealTimeThumbnailsToRender.Num() > 0 && FPlatformTime::Seconds() - FrameStartTime < MaxFrameTimeAllowance )
{
TSharedPtr<FThumbnailInfo> Info;
if ( ThumbnailsToRenderStack.Num() > 0 )
{
Info = ThumbnailsToRenderStack.Pop();
}
else if (RealTimeThumbnailsToRender.Num() > 0 && NumRealTimeThumbnailsRenderedThisFrame < MaxRealTimeThumbnailsPerFrame )
{
Info = RealTimeThumbnailsToRender.Pop();
NumRealTimeThumbnailsRenderedThisFrame++;
}
else
{
// No thumbnails left to render or we don't want to render any more
break;
}
if( Info.IsValid() )
{
bool bIsAssetStillCompiling = false;
TSharedRef<FThumbnailInfo> InfoRef = Info.ToSharedRef();
if ( InfoRef->AssetData.IsValid() )
{
FAssetData CustomThumbnailAsset;
// Check if a different asset should be used to generate the thumbnail for this asset
FString CustomThumbnailTagValue;
if (InfoRef->AssetData.GetTagValue(CustomThumbnailTagName, CustomThumbnailTagValue))
{
if (FPackageName::IsValidObjectPath(CustomThumbnailTagValue))
{
CustomThumbnailAsset = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(AssetRegistryConstants::ModuleName).Get().GetAssetByObjectPath(FSoftObjectPath(CustomThumbnailTagValue));
}
}
bool bLoadedThumbnail = LoadThumbnail(InfoRef, bIsAssetStillCompiling, CustomThumbnailAsset);
// If we failed to load a custom thumbnail, then load the custom thumbnail's asset and try again
if (!bLoadedThumbnail && !bIsAssetStillCompiling && CustomThumbnailAsset.IsValid())
{
if (UPackage* CustomThumbnailPackage = CustomThumbnailAsset.GetPackage())
{
UObject* CustomThumbnail = FindObjectFast<UObject>(CustomThumbnailPackage, CustomThumbnailAsset.AssetName);
if (!CustomThumbnail)
{
// Because the custom thumbnail asset can be GCed (RF_Standalone flag cleared),
// its package might need to be reloaded
CustomThumbnailPackage = LoadPackage(NULL, *CustomThumbnailAsset.PackageName.ToString(), LOAD_None);
CustomThumbnail = FindObjectFast<UObject>(CustomThumbnailPackage, CustomThumbnailAsset.AssetName);
}
if (CustomThumbnail)
{
bLoadedThumbnail = LoadThumbnail(InfoRef, bIsAssetStillCompiling, CustomThumbnailAsset);
if (!bIsAssetStillCompiling)
{
// Clear RF_Standalone flag on the loaded custom thumbnail asset so it gets GCed eventually
CustomThumbnail->ClearFlags(RF_Standalone);
}
}
}
}
if ( bLoadedThumbnail )
{
// Mark it as updated
InfoRef->LastUpdateTime = FPlatformTime::Seconds();
// Notify listeners that a thumbnail has been rendered
ThumbnailRenderedEvent.Broadcast(InfoRef->AssetData);
}
// Do not send a failure event for this asset yet if shaders are still compiling or the asset itself is compiling.
// The failure event will disable the rendering of this asset for good and we need to have a chance to
// re-render it when everything settles down.
else if (!bAreShadersCompiling && !bIsAssetStillCompiling)
{
// Notify listeners that a thumbnail render has failed
ThumbnailRenderFailedEvent.Broadcast(InfoRef->AssetData);
}
}
}
}
}
}
bool FAssetThumbnailPool::LoadThumbnail(TSharedRef<FThumbnailInfo> ThumbnailInfo, bool& bIsAssetStillCompiling, const FAssetData& CustomAssetToRender)
{
const FAssetData& AssetData = CustomAssetToRender.IsValid() ? CustomAssetToRender : ThumbnailInfo->AssetData;
FThumbnailMap ThumbnailMap;
const FObjectThumbnail* FoundThumbnail = nullptr;
// Prioritize thumbnail found from the on-disk package if it's cooked because the asset cannot change and
// rendering it without editor-only data might not give the same result as when it was rendered uncooked.
if (AssetData.PackageFlags & PKG_Cooked)
{
FoundThumbnail = AssetThumbnailPool::LoadThumbnailsFromPackage(AssetData, ThumbnailMap);
}
if (!FoundThumbnail)
{
// Render a fresh thumbnail from the loaded asset if possible
UObject* Asset = AssetData.FastGetAsset();
if (Asset && !IsValidChecked(Asset))
{
Asset = nullptr;
}
const bool bAreShadersCompiling = (GShaderCompilingManager && GShaderCompilingManager->IsCompiling());
if (Asset && !bAreShadersCompiling)
{
//Avoid rendering the thumbnail of an asset that is currently edited asynchronously
const IInterface_AsyncCompilation* Interface_AsyncCompilation = Cast<IInterface_AsyncCompilation>(Asset);
bIsAssetStillCompiling = Interface_AsyncCompilation && Interface_AsyncCompilation->IsCompiling();
if (!bIsAssetStillCompiling)
{
FThumbnailRenderingInfo* RenderInfo = GUnrealEd->GetThumbnailManager()->GetRenderingInfo(Asset);
if (RenderInfo != nullptr && RenderInfo->Renderer != nullptr)
{
FThumbnailInfo_RenderThread ThumbInfo = ThumbnailInfo.Get();
auto EnqueueThumbnailRender = [Asset, ThumbInfo, ThumbnailInfo]()
{
ENQUEUE_RENDER_COMMAND(SyncSlateTextureCommand)(
[ThumbInfo](FRHICommandListImmediate& RHICmdList)
{
if (ThumbInfo.ThumbnailTexture->GetTypedResource() != ThumbInfo.ThumbnailRenderTarget->GetTextureRHI())
{
ThumbInfo.ThumbnailTexture->ClearTextureData();
ThumbInfo.ThumbnailTexture->ReleaseRHI();
ThumbInfo.ThumbnailTexture->SetRHIRef(ThumbInfo.ThumbnailRenderTarget->GetTextureRHI(), ThumbInfo.Width, ThumbInfo.Height);
}
});
// // We have to wait for the render command to finish since thumbnail rendering cannot be done on the GPU currently
FRenderCommandFence Fence;
Fence.BeginFence();
Fence.Wait();
//@todo: this should be done on the GPU only but it is not supported by thumbnail tools yet
ThumbnailTools::RenderThumbnail(
Asset,
ThumbnailInfo->Width,
ThumbnailInfo->Height,
ThumbnailTools::EThumbnailTextureFlushMode::NeverFlush,
ThumbnailInfo->ThumbnailRenderTarget
);
};
EThumbnailRenderFrequency ThumbnailRenderFrequency = RenderInfo->Renderer->GetThumbnailRenderFrequency(Asset);
switch (ThumbnailRenderFrequency)
{
case EThumbnailRenderFrequency::Realtime:
{
EnqueueThumbnailRender();
return true;
}
case EThumbnailRenderFrequency::OnPropertyChange:
{
if (ThumbnailInfo->LastUpdateTime <= 0.0f)
{
EnqueueThumbnailRender();
return true;
}
break;
}
case EThumbnailRenderFrequency::OnAssetSave:
{
// OnAssetSave is default behavior below, so nohing to do
break;
}
case EThumbnailRenderFrequency::Once:
{
// Eagerly return if we aren't interested in cached thumbnails
return true;
}
default:
{
break;
}
}
}
}
}
}
// If we could not render a fresh thumbnail, see if we already have a cached one to load
if (!FoundThumbnail)
{
FoundThumbnail = ThumbnailTools::FindCachedThumbnail(AssetData.GetFullName());
}
// If we don't have a thumbnail cached in memory, try to find it on disk
if (!FoundThumbnail && !(AssetData.PackageFlags & PKG_Cooked))
{
FoundThumbnail = AssetThumbnailPool::LoadThumbnailsFromPackage(AssetData, ThumbnailMap);
}
if (FoundThumbnail)
{
FImageView Image = FoundThumbnail->GetImage();
if ( Image.GetNumPixels() > 0 )
{
// Make bulk data for updating the texture memory later
FSlateTextureData* BulkData = new FSlateTextureData(Image);
// Update the texture RHI
FThumbnailInfo_RenderThread ThumbInfo = ThumbnailInfo.Get();
ENQUEUE_RENDER_COMMAND(ClearSlateTextureCommand)(
[ThumbInfo, BulkData](FRHICommandListImmediate& RHICmdList)
{
if (ThumbInfo.ThumbnailTexture->GetTypedResource() == ThumbInfo.ThumbnailRenderTarget->GetTextureRHI())
{
ThumbInfo.ThumbnailTexture->SetRHIRef(NULL, ThumbInfo.Width, ThumbInfo.Height);
}
ThumbInfo.ThumbnailTexture->SetTextureData(MakeShareable(BulkData));
ThumbInfo.ThumbnailTexture->UpdateRHI(RHICmdList);
});
return true;
}
}
return false;
}
FSlateTexture2DRHIRef* FAssetThumbnailPool::AccessTexture( const FAssetData& AssetData, uint32 Width, uint32 Height )
{
if(!AssetData.IsValid() || Width == 0 || Height == 0)
{
return NULL;
}
else
{
FThumbId ThumbId( AssetData.GetSoftObjectPath(), Width, Height ) ;
// Check to see if a thumbnail for this asset exists. If so we don't need to render it
const TSharedRef<FThumbnailInfo>* ThumbnailInfoPtr = ThumbnailToTextureMap.Find( ThumbId );
TSharedPtr<FThumbnailInfo> ThumbnailInfo;
if( ThumbnailInfoPtr )
{
ThumbnailInfo = *ThumbnailInfoPtr;
}
else
{
// If a the max number of thumbnails allowed by the pool exists then reuse its rendering resource for the new thumbnail
if( FreeThumbnails.Num() == 0 && ThumbnailToTextureMap.Num() == NumInPool )
{
// Find the thumbnail which was accessed last and use it for the new thumbnail
double LastAccessTime = std::numeric_limits<double>::max();
const FThumbId* AssetToRemove = NULL;
for( TMap< FThumbId, TSharedRef<FThumbnailInfo> >::TConstIterator It(ThumbnailToTextureMap); It; ++It )
{
if( It.Value()->LastAccessTime < LastAccessTime )
{
LastAccessTime = It.Value()->LastAccessTime;
AssetToRemove = &It.Key();
}
}
check( AssetToRemove );
// Remove the old mapping
ThumbnailInfo = ThumbnailToTextureMap.FindAndRemoveChecked( *AssetToRemove );
}
else if( FreeThumbnails.Num() > 0 )
{
ThumbnailInfo = FreeThumbnails.Pop();
FSlateTextureRenderTarget2DResource* ThumbnailRenderTarget = ThumbnailInfo->ThumbnailRenderTarget;
ENQUEUE_RENDER_COMMAND(SlateUpdateThumbSizeCommand)(
[ThumbnailRenderTarget, Width, Height](FRHICommandListImmediate& RHICmdList)
{
ThumbnailRenderTarget->SetSize(Width, Height);
});
}
else
{
// There are no free thumbnail resources
check( (uint32)ThumbnailToTextureMap.Num() <= NumInPool );
check( !ThumbnailInfo.IsValid() );
// The pool isn't used up so just make a new texture
// Make new thumbnail info if it doesn't exist
// This happens when the pool is not yet full
ThumbnailInfo = MakeShareable( new FThumbnailInfo );
// Set the thumbnail and asset on the info. It is NOT safe to change or NULL these pointers until ReleaseResources.
ThumbnailInfo->ThumbnailTexture = new FSlateTexture2DRHIRef( Width, Height, PF_B8G8R8A8, NULL, TexCreate_None );
ThumbnailInfo->ThumbnailRenderTarget = new FSlateTextureRenderTarget2DResource(FLinearColor::Black, Width, Height, PF_B8G8R8A8, SF_Point, TA_Wrap, TA_Wrap, 0.0f);
BeginInitResource( ThumbnailInfo->ThumbnailTexture );
BeginInitResource( ThumbnailInfo->ThumbnailRenderTarget );
}
check( ThumbnailInfo.IsValid() );
TSharedRef<FThumbnailInfo> ThumbnailRef = ThumbnailInfo.ToSharedRef();
// Map the object to its thumbnail info
ThumbnailToTextureMap.Add( ThumbId, ThumbnailRef );
ThumbnailInfo->AssetData = AssetData;
ThumbnailInfo->Width = Width;
ThumbnailInfo->Height = Height;
// Mark this thumbnail as needing to be updated
ThumbnailInfo->LastUpdateTime = -1.f;
// Request that the thumbnail be rendered as soon as possible
ThumbnailsToRenderStack.Push( ThumbnailRef );
}
// This thumbnail was accessed, update its last time to the current time
// We'll use LastAccessTime to determine the order to recycle thumbnails if the pool is full
ThumbnailInfo->LastAccessTime = FPlatformTime::Seconds();
return ThumbnailInfo->ThumbnailTexture;
}
}
void FAssetThumbnailPool::AddReferencer( const FAssetThumbnail& AssetThumbnail )
{
FIntPoint Size = AssetThumbnail.GetSize();
if ( !AssetThumbnail.GetAssetData().IsValid() || Size.X == 0 || Size.Y == 0 )
{
// Invalid referencer
return;
}
// Generate a key and look up the number of references in the RefCountMap
FThumbId ThumbId( AssetThumbnail.GetAssetData().GetSoftObjectPath(), Size.X, Size.Y ) ;
int32* RefCountPtr = RefCountMap.Find(ThumbId);
if ( RefCountPtr )
{
// Already in the map, increment a reference
(*RefCountPtr)++;
}
else
{
// New referencer, add it to the map with a RefCount of 1
RefCountMap.Add(ThumbId, 1);
}
}
void FAssetThumbnailPool::RemoveReferencer( const FAssetThumbnail& AssetThumbnail )
{
FIntPoint Size = AssetThumbnail.GetSize();
const FSoftObjectPath ObjectPath = AssetThumbnail.GetAssetData().GetSoftObjectPath();
if ( ObjectPath.IsNull() || Size.X == 0 || Size.Y == 0 )
{
// Invalid referencer
return;
}
// Generate a key and look up the number of references in the RefCountMap
FThumbId ThumbId( ObjectPath, Size.X, Size.Y ) ;
int32* RefCountPtr = RefCountMap.Find(ThumbId);
// This should complement an AddReferencer so this entry should be in the map
if ( RefCountPtr )
{
// Decrement the RefCount
(*RefCountPtr)--;
// If we reached zero, free the thumbnail and remove it from the map.
if ( (*RefCountPtr) <= 0 )
{
RefCountMap.Remove(ThumbId);
FreeThumbnail(ObjectPath, Size.X, Size.Y);
}
}
else
{
// This FAssetThumbnail did not reference anything or was deleted after the pool was deleted.
}
}
bool FAssetThumbnailPool::IsInRenderStack( const TSharedPtr<FAssetThumbnail>& Thumbnail ) const
{
const FAssetData& AssetData = Thumbnail->GetAssetData();
const uint32 Width = Thumbnail->GetSize().X;
const uint32 Height = Thumbnail->GetSize().Y;
if ( ensure(AssetData.IsValid()) && ensure(Width > 0) && ensure(Height > 0) )
{
FThumbId ThumbId(AssetData.GetSoftObjectPath(), Width, Height);
const TSharedRef<FThumbnailInfo>* ThumbnailInfoPtr = ThumbnailToTextureMap.Find( ThumbId );
if ( ThumbnailInfoPtr )
{
return ThumbnailsToRenderStack.Contains(*ThumbnailInfoPtr);
}
}
return false;
}
bool FAssetThumbnailPool::IsRendered(const TSharedPtr<FAssetThumbnail>& Thumbnail) const
{
const FAssetData& AssetData = Thumbnail->GetAssetData();
const uint32 Width = Thumbnail->GetSize().X;
const uint32 Height = Thumbnail->GetSize().Y;
if (ensure(AssetData.IsValid()) && ensure(Width > 0) && ensure(Height > 0))
{
FThumbId ThumbId(AssetData.GetSoftObjectPath(), Width, Height);
const TSharedRef<FThumbnailInfo>* ThumbnailInfoPtr = ThumbnailToTextureMap.Find(ThumbId);
if (ThumbnailInfoPtr)
{
return (*ThumbnailInfoPtr).Get().LastUpdateTime >= 0.f;
}
}
return false;
}
void FAssetThumbnailPool::PrioritizeThumbnails( const TArray< TSharedPtr<FAssetThumbnail> >& ThumbnailsToPrioritize, uint32 Width, uint32 Height )
{
if ( ensure(Width > 0) && ensure(Height > 0) )
{
TSet<FSoftObjectPath> ObjectPathList;
for ( int32 ThumbIdx = 0; ThumbIdx < ThumbnailsToPrioritize.Num(); ++ThumbIdx )
{
ObjectPathList.Add(ThumbnailsToPrioritize[ThumbIdx]->GetAssetData().GetSoftObjectPath());
}
TArray< TSharedRef<FThumbnailInfo> > FoundThumbnails;
for ( int32 ThumbIdx = ThumbnailsToRenderStack.Num() - 1; ThumbIdx >= 0; --ThumbIdx )
{
const TSharedRef<FThumbnailInfo>& ThumbnailInfo = ThumbnailsToRenderStack[ThumbIdx];
if (ThumbnailInfo->Width == Width && ThumbnailInfo->Height == Height && ObjectPathList.Contains(ThumbnailInfo->AssetData.GetSoftObjectPath()))
{
FoundThumbnails.Add(ThumbnailInfo);
ThumbnailsToRenderStack.RemoveAt(ThumbIdx);
}
}
for ( int32 ThumbIdx = 0; ThumbIdx < FoundThumbnails.Num(); ++ThumbIdx )
{
ThumbnailsToRenderStack.Push(FoundThumbnails[ThumbIdx]);
}
}
}
void FAssetThumbnailPool::RefreshThumbnail( const TSharedPtr<FAssetThumbnail>& ThumbnailToRefresh )
{
const FAssetData& AssetData = ThumbnailToRefresh->GetAssetData();
const uint32 Width = ThumbnailToRefresh->GetSize().X;
const uint32 Height = ThumbnailToRefresh->GetSize().Y;
if ( ensure(AssetData.IsValid()) && ensure(Width > 0) && ensure(Height > 0) )
{
FThumbId ThumbId(AssetData.GetSoftObjectPath(), Width, Height);
const TSharedRef<FThumbnailInfo>* ThumbnailInfoPtr = ThumbnailToTextureMap.Find( ThumbId );
if ( ThumbnailInfoPtr )
{
ThumbnailsToRenderStack.AddUnique(*ThumbnailInfoPtr);
}
}
}
void FAssetThumbnailPool::SetRealTimeThumbnail( const TSharedPtr<FAssetThumbnail>& Thumbnail, bool bRealTimeThumbnail )
{
const FAssetData& AssetData = Thumbnail->GetAssetData();
const uint32 Width = Thumbnail->GetSize().X;
const uint32 Height = Thumbnail->GetSize().Y;
if (ensure(AssetData.IsValid()) && ensure(Width > 0) && ensure(Height > 0) )
{
FThumbId ThumbId(AssetData.GetSoftObjectPath(), Width, Height);
const TSharedRef<FThumbnailInfo>* ThumbnailInfoPtr = ThumbnailToTextureMap.Find( ThumbId );
if ( ThumbnailInfoPtr )
{
if ( bRealTimeThumbnail )
{
RealTimeThumbnails.AddUnique(*ThumbnailInfoPtr);
}
else
{
RealTimeThumbnails.Remove(*ThumbnailInfoPtr);
}
}
}
}
void FAssetThumbnailPool::FreeThumbnail( const FSoftObjectPath& ObjectPath, uint32 Width, uint32 Height )
{
if(ObjectPath.IsValid() && Width != 0 && Height != 0)
{
FThumbId ThumbId( ObjectPath, Width, Height ) ;
const TSharedRef<FThumbnailInfo>* ThumbnailInfoPtr = ThumbnailToTextureMap.Find(ThumbId);
if( ThumbnailInfoPtr )
{
TSharedRef<FThumbnailInfo> ThumbnailInfo = *ThumbnailInfoPtr;
ThumbnailToTextureMap.Remove(ThumbId);
ThumbnailsToRenderStack.Remove(ThumbnailInfo);
RealTimeThumbnails.Remove(ThumbnailInfo);
RealTimeThumbnailsToRender.Remove(ThumbnailInfo);
FSlateTexture2DRHIRef* ThumbnailTexture = ThumbnailInfo->ThumbnailTexture;
ENQUEUE_RENDER_COMMAND(ReleaseThumbnailTextureData)(
[ThumbnailTexture](FRHICommandListImmediate& RHICmdList)
{
ThumbnailTexture->ClearTextureData();
});
FreeThumbnails.Add( ThumbnailInfo );
}
}
}
void FAssetThumbnailPool::RefreshThumbnailsFor( const FSoftObjectPath& ObjectPath )
{
for ( auto ThumbIt = ThumbnailToTextureMap.CreateIterator(); ThumbIt; ++ThumbIt)
{
if ( ThumbIt.Key().ObjectPath == ObjectPath )
{
ThumbnailsToRenderStack.AddUnique( ThumbIt.Value() );
}
}
}
void FAssetThumbnailPool::OnAssetLoaded( UObject* Asset )
{
if ( Asset != NULL )
{
RecentlyLoadedAssets.Add( FSoftObjectPath(Asset) );
}
}
void FAssetThumbnailPool::OnThumbnailDirtied( const FSoftObjectPath& ObjectPath )
{
RefreshThumbnailsFor( ObjectPath );
}