// Copyright Epic Games, Inc. All Rights Reserved. #include "SNiagaraEmitterRendererThumbnails.h" #include "NiagaraEmitterHandle.h" #include "SlateOptMacros.h" #include "ViewModels/NiagaraEmitterViewModel.h" #include "ViewModels/Stack/NiagaraStackRenderItemGroup.h" #include "ViewModels/Stack/NiagaraStackRoot.h" #include "ViewModels/Stack/NiagaraStackViewModel.h" #include "Widgets/SToolTip.h" #include "Materials/Material.h" #include "ViewModels/NiagaraOverviewGraphViewModel.h" #include "ViewModels/NiagaraSystemSelectionViewModel.h" #include "ViewModels/NiagaraSystemViewModel.h" #include "Widgets/Layout/SScaleBox.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION static constexpr float RendererThumbnailSize = 24.f; #define LOCTEXT_NAMESPACE "NiagaraEmitterRendererThumbnails" FNiagaraRendererStackEntryObserver::FNiagaraRendererStackEntryObserver() { } FNiagaraRendererStackEntryObserver::~FNiagaraRendererStackEntryObserver() { if(EmitterHandleViewModelWeak.IsValid()) { if(UNiagaraStackViewModel* StackViewModel = EmitterHandleViewModelWeak.Pin()->GetEmitterStackViewModel()) { StackViewModel->OnDataObjectChanged().RemoveAll(this); StackViewModel->OnStructureChanged().RemoveAll(this); } } UMaterial::OnMaterialCompilationFinished().RemoveAll(this); } void FNiagaraRendererStackEntryObserver::Initialize(TSharedRef InEmitterHandleViewModel) { EmitterHandleViewModelWeak = InEmitterHandleViewModel; if(UNiagaraStackViewModel* StackViewModel = EmitterHandleViewModelWeak.Pin()->GetEmitterStackViewModel()) { StackViewModel->OnDataObjectChanged().AddSP(this, &FNiagaraRendererStackEntryObserver::OnStackDataObjectChanged); StackViewModel->OnStructureChanged().AddSP(this, &FNiagaraRendererStackEntryObserver::OnRenderGroupStructureChanged); } UMaterial::OnMaterialCompilationFinished().AddSP(this, &FNiagaraRendererStackEntryObserver::OnMaterialCompiled); } void FNiagaraRendererStackEntryObserver::OnRenderGroupStructureChanged(ENiagaraStructureChangedFlags NiagaraStructureChangedFlags) const { OnRenderersChanged.Broadcast(); } void FNiagaraRendererStackEntryObserver::OnMaterialCompiled(class UMaterialInterface* MaterialInterface) const { if (EmitterHandleViewModelWeak.IsValid()) { bool bUsingThisMaterial = false; TArray RendererPreviewStackEntries; EmitterHandleViewModelWeak.Pin()->GetRendererEntries(RendererPreviewStackEntries); FNiagaraEmitterInstance* InInstance = EmitterHandleViewModelWeak.Pin()->GetEmitterViewModel()->GetSimulation().IsValid() ? EmitterHandleViewModelWeak.Pin()->GetEmitterViewModel()->GetSimulation().Pin().Get() : nullptr; for (UNiagaraStackEntry* Entry : RendererPreviewStackEntries) { if (UNiagaraStackRendererItem* RendererItem = Cast(Entry)) { TArray Materials; RendererItem->GetRendererProperties()->GetUsedMaterials(InInstance, Materials); if (Materials.Contains(MaterialInterface)) { bUsingThisMaterial = true; break; } } } if (bUsingThisMaterial) { OnRenderersChanged.Broadcast(); } } } void FNiagaraRendererStackEntryObserver::OnStackDataObjectChanged(TArray ChangedObjects, ENiagaraDataObjectChange NiagaraDataObjectChange) const { for (UObject* ChangedObject : ChangedObjects) { if (ChangedObject->IsA()) { OnRenderersChanged.Broadcast(); break; } } } void SNiagaraEmitterRendererThumbnails::Construct(const FArguments& InArgs, TSharedRef InEmitterHandleViewModel) { EmitterHandleViewModelWeak = InEmitterHandleViewModel; OnRendererThumbnailClickedDelegate = InArgs._OnRendererThumbnailClicked; RendererObserver = MakeShared(); RendererObserver->Initialize(InEmitterHandleViewModel); RendererObserver->OnRenderersChanged.AddSP(this, &SNiagaraEmitterRendererThumbnails::RefreshThumbnails); ChildSlot [ SAssignNew(Content, SBox) ]; RefreshThumbnails(); } void SNiagaraEmitterRendererThumbnails::RefreshThumbnails() { TArray RendererPreviewStackEntries; EmitterHandleViewModelWeak.Pin()->GetRendererEntries(RendererPreviewStackEntries); FNiagaraEmitterInstance* InInstance = EmitterHandleViewModelWeak.Pin()->GetEmitterViewModel()->GetSimulation().IsValid() ? EmitterHandleViewModelWeak.Pin()->GetEmitterViewModel()->GetSimulation().Pin().Get() : nullptr; FToolBarBuilder ToolBarBuilder(nullptr, FMultiBoxCustomization::None, nullptr, true); ToolBarBuilder.SetLabelVisibility(EVisibility::Collapsed); for (int32 StackEntryIndex = 0; StackEntryIndex < RendererPreviewStackEntries.Num(); StackEntryIndex++) { UNiagaraStackEntry* Entry = RendererPreviewStackEntries[StackEntryIndex]; if (UNiagaraStackRendererItem* RendererItem = Cast(Entry)) { TArray> Widgets; RendererItem->GetRendererProperties()->GetRendererWidgets(InInstance, Widgets, UThumbnailManager::Get().GetSharedThumbnailPool()); TArray> TooltipWidgets; RendererItem->GetRendererProperties()->GetRendererTooltipWidgets(InInstance, TooltipWidgets, UThumbnailManager::Get().GetSharedThumbnailPool()); // Fallback widget & tooltip if(Widgets.Num() == 0) { RendererItem->GetRendererProperties()->CreateDefaultRendererWidget(Widgets); } if(TooltipWidgets.Num() == 0) { FText BaseText = LOCTEXT("DefaultRendererTooltip", "{0}"); TooltipWidgets.Add(SNew(STextBlock).Text(FText::FormatOrdered(BaseText, RendererItem->GetRendererProperties()->GetClass()->GetDisplayNameText()))); } check(Widgets.Num() == TooltipWidgets.Num()); for (int32 WidgetIndex = 0; WidgetIndex < Widgets.Num(); WidgetIndex++) { SetupRendererThumbnailTooltip(Widgets[WidgetIndex], TooltipWidgets[WidgetIndex]); // We wrap the thumbnail in a scalebox so it will scale to whatever its parent dictates TSharedPtr RendererThumbnailWidget = SNew(SBox) .VAlign(VAlign_Center) .HAlign(HAlign_Center) .HeightOverride(RendererThumbnailSize) .WidthOverride(RendererThumbnailSize) [ SNew(SScaleBox) .Stretch(EStretch::ScaleToFit) [ Widgets[WidgetIndex].ToSharedRef() ] ]; // If we have on click functionality, we wrap the thumbnail in a button if(OnRendererThumbnailClickedDelegate.IsBound()) { RendererThumbnailWidget = SNew(SButton) .ButtonStyle(&FAppStyle::GetWidgetStyle("HoverHintOnly")) .ContentPadding(FMargin(1.f)) .OnClicked(this, &SNiagaraEmitterRendererThumbnails::OnRendererThumbnailClicked, RendererItem) [ RendererThumbnailWidget.ToSharedRef() ]; } ToolBarBuilder.AddWidget( RendererThumbnailWidget.ToSharedRef() ); } // if we had a widget for this entry, add a separator for the next entry's widgets, except for the last entry if(Widgets.Num() > 0 && StackEntryIndex < RendererPreviewStackEntries.Num() - 1) { ToolBarBuilder.AddSeparator(); } } } Content->SetContent(ToolBarBuilder.MakeWidget()); } void SNiagaraEmitterRendererThumbnails::SetupRendererThumbnailTooltip(TSharedPtr InWidget, TSharedPtr InTooltipWidget) { TSharedPtr ThumbnailTooltipWidget; // If this is just text, don't constrain the size if (InTooltipWidget->GetType() == TEXT("STextBlock")) { ThumbnailTooltipWidget = SNew(SToolTip) .Content() [ InTooltipWidget.ToSharedRef() ]; } else { ThumbnailTooltipWidget = SNew(SToolTip) .Content() [ SNew(SBox) .MaxDesiredHeight(64.0f) .MinDesiredHeight(64.0f) .MaxDesiredWidth(64.0f) .MinDesiredWidth(64.0f) [ InTooltipWidget.ToSharedRef() ] ]; } InWidget->SetToolTip(ThumbnailTooltipWidget); } FReply SNiagaraEmitterRendererThumbnails::OnRendererThumbnailClicked(UNiagaraStackRendererItem* StackRendererItem) const { if(OnRendererThumbnailClickedDelegate.IsBound()) { return OnRendererThumbnailClickedDelegate.Execute(StackRendererItem); } return FReply::Unhandled(); } #undef LOCTEXT_NAMESPACE END_SLATE_FUNCTION_BUILD_OPTIMIZATION