You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb patrick.enfedaque #preflight 62878854132db639f5d2a540 #rnx [CL 20293964 by JeanFrancois Dube in ue5-main branch]
478 lines
13 KiB
C++
478 lines
13 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "ActorTreeItem.h"
|
|
#include "Widgets/DeclarativeSyntaxSupport.h"
|
|
#include "Layout/WidgetPath.h"
|
|
#include "Framework/Application/MenuStack.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "Editor.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "SceneOutlinerPublicTypes.h"
|
|
#include "SceneOutlinerDragDrop.h"
|
|
#include "SceneOutlinerStandaloneTypes.h"
|
|
#include "Widgets/Text/SInlineEditableTextBlock.h"
|
|
#include "ActorEditorUtils.h"
|
|
#include "ClassIconFinder.h"
|
|
#include "ISceneOutliner.h"
|
|
#include "ISceneOutlinerMode.h"
|
|
#include "Logging/MessageLog.h"
|
|
#include "SSocketChooser.h"
|
|
#include "LevelInstance/LevelInstanceInterface.h"
|
|
#include "WorldPartition/WorldPartition.h"
|
|
#include "ToolMenu.h"
|
|
#include "Engine/Level.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "SceneOutliner_ActorTreeItem"
|
|
|
|
const FSceneOutlinerTreeItemType FActorTreeItem::Type(&IActorBaseTreeItem::Type);
|
|
|
|
struct SActorTreeLabel : FSceneOutlinerCommonLabelData, public SCompoundWidget
|
|
{
|
|
SLATE_BEGIN_ARGS(SActorTreeLabel) {}
|
|
SLATE_END_ARGS()
|
|
|
|
void Construct(const FArguments& InArgs, FActorTreeItem& ActorItem, ISceneOutliner& SceneOutliner, const STableRow<FSceneOutlinerTreeItemPtr>& InRow)
|
|
{
|
|
WeakSceneOutliner = StaticCastSharedRef<ISceneOutliner>(SceneOutliner.AsShared());
|
|
|
|
TreeItemPtr = StaticCastSharedRef<FActorTreeItem>(ActorItem.AsShared());
|
|
ActorPtr = ActorItem.Actor;
|
|
|
|
HighlightText = SceneOutliner.GetFilterHighlightText();
|
|
|
|
TSharedPtr<SInlineEditableTextBlock> InlineTextBlock;
|
|
|
|
auto MainContent = SNew(SHorizontalBox)
|
|
|
|
// Main actor label
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
[
|
|
SAssignNew(InlineTextBlock, SInlineEditableTextBlock)
|
|
.Text(this, &SActorTreeLabel::GetDisplayText)
|
|
.ToolTipText(this, &SActorTreeLabel::GetTooltipText)
|
|
.HighlightText(HighlightText)
|
|
.ColorAndOpacity(this, &SActorTreeLabel::GetForegroundColor)
|
|
.OnTextCommitted(this, &SActorTreeLabel::OnLabelCommitted)
|
|
.OnVerifyTextChanged(this, &SActorTreeLabel::OnVerifyItemLabelChanged)
|
|
.OnEnterEditingMode(this, &SActorTreeLabel::OnEnterEditingMode)
|
|
.OnExitEditingMode(this, &SActorTreeLabel::OnExitEditingMode)
|
|
.IsSelected(FIsSelected::CreateSP(&InRow, &STableRow<FSceneOutlinerTreeItemPtr>::IsSelectedExclusively))
|
|
.IsReadOnly_Lambda([Item = ActorItem.AsShared(), this]()
|
|
{
|
|
return !CanExecuteRenameRequest(Item.Get());
|
|
})
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.VAlign(VAlign_Center)
|
|
.AutoWidth()
|
|
.Padding(0.0f, 0.f, 3.0f, 0.0f)
|
|
[
|
|
SNew(STextBlock)
|
|
.Text(this, &SActorTreeLabel::GetTypeText)
|
|
.Visibility(this, &SActorTreeLabel::GetTypeTextVisibility)
|
|
.HighlightText(HighlightText)
|
|
];
|
|
|
|
if (WeakSceneOutliner.Pin()->GetMode()->IsInteractive())
|
|
{
|
|
ActorItem.RenameRequestEvent.BindSP(InlineTextBlock.Get(), &SInlineEditableTextBlock::EnterEditingMode);
|
|
}
|
|
|
|
ChildSlot
|
|
[
|
|
SNew(SHorizontalBox)
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.AutoWidth()
|
|
.VAlign(VAlign_Center)
|
|
.Padding(FSceneOutlinerDefaultTreeItemMetrics::IconPadding())
|
|
[
|
|
SNew(SBox)
|
|
.WidthOverride(FSceneOutlinerDefaultTreeItemMetrics::IconSize())
|
|
.HeightOverride(FSceneOutlinerDefaultTreeItemMetrics::IconSize())
|
|
[
|
|
SNew(SImage)
|
|
.Image(this, &SActorTreeLabel::GetIcon)
|
|
.ToolTipText(this, &SActorTreeLabel::GetIconTooltip)
|
|
.ColorAndOpacity(FSlateColor::UseForeground())
|
|
]
|
|
]
|
|
|
|
+ SHorizontalBox::Slot()
|
|
.FillWidth(1.0f)
|
|
.VAlign(VAlign_Center)
|
|
.Padding(0.0f, 0.0f)
|
|
[
|
|
MainContent
|
|
]
|
|
];
|
|
}
|
|
|
|
private:
|
|
TWeakPtr<FActorTreeItem> TreeItemPtr;
|
|
TWeakObjectPtr<AActor> ActorPtr;
|
|
TAttribute<FText> HighlightText;
|
|
|
|
FText GetDisplayText() const
|
|
{
|
|
if (const FSceneOutlinerTreeItemPtr TreeItem = TreeItemPtr.Pin())
|
|
{
|
|
const AActor* Actor = ActorPtr.Get();
|
|
if (const ILevelInstanceInterface* LevelInstance = Cast<ILevelInstanceInterface>(Actor))
|
|
{
|
|
if (!bInEditingMode)
|
|
{
|
|
FText DirtySuffixText = LevelInstance->IsDirty() ? FText(LOCTEXT("IsDirtySuffix", "*")) : FText::GetEmpty();
|
|
FText IsCurrentSuffixText = LevelInstance->GetLoadedLevel() && LevelInstance->GetLoadedLevel()->IsCurrentLevel() ? FText(LOCTEXT("IsCurrentSuffix", " (Current)")) : FText::GetEmpty();
|
|
return FText::Format(LOCTEXT("LevelInstanceDisplay", "{0}{1}{2}"), FText::FromString(TreeItem->GetDisplayString()), DirtySuffixText, IsCurrentSuffixText);
|
|
}
|
|
}
|
|
return FText::FromString(TreeItem->GetDisplayString());
|
|
}
|
|
|
|
return FText();
|
|
}
|
|
|
|
FText GetTooltipText() const
|
|
{
|
|
if (const FSceneOutlinerTreeItemPtr TreeItem = TreeItemPtr.Pin())
|
|
{
|
|
return FText::FromString(TreeItem->GetDisplayString());
|
|
}
|
|
|
|
return FText();
|
|
}
|
|
|
|
FText GetTypeText() const
|
|
{
|
|
if (const AActor* Actor = ActorPtr.Get())
|
|
{
|
|
return FText::FromName(Actor->GetClass()->GetFName());
|
|
}
|
|
|
|
return FText();
|
|
}
|
|
|
|
EVisibility GetTypeTextVisibility() const
|
|
{
|
|
return HighlightText.Get().IsEmpty() ? EVisibility::Collapsed : EVisibility::Visible;
|
|
}
|
|
|
|
const FSlateBrush* GetIcon() const
|
|
{
|
|
if (const AActor* Actor = ActorPtr.Get())
|
|
{
|
|
if (WeakSceneOutliner.IsValid())
|
|
{
|
|
FName IconName = Actor->GetCustomIconName();
|
|
if (IconName == NAME_None)
|
|
{
|
|
IconName = Actor->GetClass()->GetFName();
|
|
}
|
|
|
|
const FSlateBrush* CachedBrush = WeakSceneOutliner.Pin()->GetCachedIconForClass(IconName);
|
|
if (CachedBrush != nullptr)
|
|
{
|
|
return CachedBrush;
|
|
}
|
|
else
|
|
{
|
|
|
|
const FSlateBrush* FoundSlateBrush = FClassIconFinder::FindIconForActor(Actor);
|
|
WeakSceneOutliner.Pin()->CacheIconForClass(IconName, FoundSlateBrush);
|
|
return FoundSlateBrush;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FSlateIconFinder::FindIconForClass(AActor::StaticClass()).GetOptionalIcon();
|
|
}
|
|
}
|
|
|
|
const FSlateBrush* GetIconOverlay() const
|
|
{
|
|
static const FName SequencerActorTag(TEXT("SequencerActor"));
|
|
|
|
if (const AActor* Actor = ActorPtr.Get())
|
|
{
|
|
if (Actor->ActorHasTag(SequencerActorTag))
|
|
{
|
|
return FAppStyle::GetBrush("Sequencer.SpawnableIconOverlay");
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
FText GetIconTooltip() const
|
|
{
|
|
auto TreeItem = TreeItemPtr.Pin();
|
|
if (!TreeItem.IsValid())
|
|
{
|
|
return FText();
|
|
}
|
|
|
|
FText ToolTipText;
|
|
if (AActor* Actor = ActorPtr.Get())
|
|
{
|
|
ToolTipText = FText::FromString(Actor->GetClass()->GetName());
|
|
if (WeakSceneOutliner.Pin()->GetMode()->IsInteractive())
|
|
{
|
|
USceneComponent* RootComponent = Actor->GetRootComponent();
|
|
if (RootComponent)
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("ActorClassName"), ToolTipText);
|
|
|
|
if (RootComponent->Mobility == EComponentMobility::Static)
|
|
{
|
|
ToolTipText = FText::Format(LOCTEXT("ComponentMobility_Static", "{ActorClassName} with static mobility"), Args);
|
|
}
|
|
else if (RootComponent->Mobility == EComponentMobility::Stationary)
|
|
{
|
|
ToolTipText = FText::Format(LOCTEXT("ComponentMobility_Stationary", "{ActorClassName} with stationary mobility"), Args);
|
|
}
|
|
else if (RootComponent->Mobility == EComponentMobility::Movable)
|
|
{
|
|
ToolTipText = FText::Format(LOCTEXT("ComponentMobility_Movable", "{ActorClassName} with movable mobility"), Args);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ToolTipText;
|
|
}
|
|
|
|
FSlateColor GetForegroundColor() const
|
|
{
|
|
AActor* Actor = ActorPtr.Get();
|
|
|
|
// Color LevelInstances differently if they are being edited
|
|
if (const ILevelInstanceInterface* LevelInstance = Cast<ILevelInstanceInterface>(Actor))
|
|
{
|
|
if (LevelInstance->IsEditing())
|
|
{
|
|
return FAppStyle::Get().GetSlateColor("Colors.AccentGreen");
|
|
}
|
|
}
|
|
|
|
auto TreeItem = TreeItemPtr.Pin();
|
|
if (auto BaseColor = FSceneOutlinerCommonLabelData::GetForegroundColor(*TreeItem))
|
|
{
|
|
return BaseColor.GetValue();
|
|
}
|
|
|
|
if (!Actor)
|
|
{
|
|
// Deleted actor!
|
|
return FLinearColor(0.2f, 0.2f, 0.25f);
|
|
}
|
|
|
|
UWorld* OwningWorld = Actor->GetWorld();
|
|
if (!OwningWorld)
|
|
{
|
|
// Deleted world!
|
|
return FLinearColor(0.2f, 0.2f, 0.25f);
|
|
}
|
|
|
|
const bool bRepresentingPIEWorld = TreeItem->Actor->GetWorld()->IsPlayInEditor();
|
|
if (bRepresentingPIEWorld && !TreeItem->bExistsInCurrentWorldAndPIE)
|
|
{
|
|
// Highlight actors that are exclusive to PlayWorld
|
|
return FLinearColor(0.9f, 0.8f, 0.4f);
|
|
}
|
|
|
|
// also darken items that are non selectable in the active mode(s)
|
|
const bool bInSelected = true;
|
|
const bool bSelectEvenIfHidden = true; // @todo outliner: Is this actually OK?
|
|
if (!GEditor->CanSelectActor(Actor, bInSelected, bSelectEvenIfHidden))
|
|
{
|
|
return FSceneOutlinerCommonLabelData::DarkColor;
|
|
}
|
|
|
|
return FSlateColor::UseForeground();
|
|
}
|
|
|
|
bool OnVerifyItemLabelChanged(const FText& InLabel, FText& OutErrorMessage)
|
|
{
|
|
return FActorEditorUtils::ValidateActorName(InLabel, OutErrorMessage);
|
|
}
|
|
|
|
void OnLabelCommitted(const FText& InLabel, ETextCommit::Type InCommitInfo)
|
|
{
|
|
auto* Actor = ActorPtr.Get();
|
|
if (Actor && Actor->IsActorLabelEditable() && !InLabel.ToString().Equals(Actor->GetActorLabel(), ESearchCase::CaseSensitive))
|
|
{
|
|
const FScopedTransaction Transaction(LOCTEXT("SceneOutlinerRenameActorTransaction", "Rename Actor"));
|
|
FActorLabelUtilities::RenameExistingActor(Actor, InLabel.ToString());
|
|
|
|
auto Outliner = WeakSceneOutliner.Pin();
|
|
if (Outliner.IsValid())
|
|
{
|
|
Outliner->SetKeyboardFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnEnterEditingMode()
|
|
{
|
|
bInEditingMode = true;
|
|
}
|
|
|
|
void OnExitEditingMode()
|
|
{
|
|
bInEditingMode = false;
|
|
}
|
|
|
|
bool bInEditingMode = false;
|
|
};
|
|
|
|
FActorTreeItem::FActorTreeItem(AActor* InActor)
|
|
: IActorBaseTreeItem(Type)
|
|
, Actor(InActor)
|
|
, ID(InActor)
|
|
{
|
|
check(InActor);
|
|
|
|
UpdateDisplayStringInternal();
|
|
|
|
Flags.bIsExpanded = InActor->bDefaultOutlinerExpansionState;
|
|
|
|
bExistsInCurrentWorldAndPIE = GEditor->ObjectsThatExistInEditorWorld.Get(InActor);
|
|
}
|
|
|
|
FSceneOutlinerTreeItemID FActorTreeItem::GetID() const
|
|
{
|
|
return ID;
|
|
}
|
|
|
|
FFolder::FRootObject FActorTreeItem::GetRootObject() const
|
|
{
|
|
AActor* ActorPtr = Actor.Get();
|
|
return ActorPtr ? ActorPtr->GetFolderRootObject() : nullptr;
|
|
}
|
|
|
|
FString FActorTreeItem::GetDisplayString() const
|
|
{
|
|
return DisplayString;
|
|
}
|
|
|
|
bool FActorTreeItem::CanInteract() const
|
|
{
|
|
AActor* ActorPtr = Actor.Get();
|
|
if (!ActorPtr || !Flags.bInteractive)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const bool bInSelected = true;
|
|
const bool bSelectEvenIfHidden = true; // @todo outliner: Is this actually OK?
|
|
if (!GEditor->CanSelectActor(ActorPtr, bInSelected, bSelectEvenIfHidden))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedRef<SWidget> FActorTreeItem::GenerateLabelWidget(ISceneOutliner& Outliner, const STableRow<FSceneOutlinerTreeItemPtr>& InRow)
|
|
{
|
|
return SNew(SActorTreeLabel, *this, Outliner, InRow);
|
|
}
|
|
|
|
bool FActorTreeItem::ShouldShowPinnedState() const
|
|
{
|
|
if (const AActor* ActorPtr = Actor.Get())
|
|
{
|
|
const ULevel* Level = ActorPtr->GetLevel();
|
|
const UWorld* World = Level->GetWorld();
|
|
return !World->IsGameWorld() && !!Level->GetWorldPartition();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool FActorTreeItem::ShouldShowVisibilityState() const
|
|
{
|
|
if (const AActor* ActorPtr = Actor.Get())
|
|
{
|
|
const ULevel* Level = ActorPtr->GetLevel();
|
|
return Level->IsPersistentLevel() || !Level->IsInstancedLevel();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FActorTreeItem::OnVisibilityChanged(const bool bNewVisibility)
|
|
{
|
|
// Save the actor to the transaction buffer to support undo/redo, but do
|
|
// not call Modify, as we do not want to dirty the actor's package and
|
|
// we're only editing temporary, transient values
|
|
SaveToTransactionBuffer(Actor.Get(), false);
|
|
Actor->SetIsTemporarilyHiddenInEditor(!bNewVisibility);
|
|
}
|
|
|
|
bool FActorTreeItem::GetVisibility() const
|
|
{
|
|
// We want deleted actors to appear as if they are visible to minimize visual clutter.
|
|
return !Actor.IsValid() || !Actor->IsTemporarilyHiddenInEditor(true);
|
|
}
|
|
|
|
bool FActorTreeItem::GetPinnedState() const
|
|
{
|
|
if (Actor.IsValid())
|
|
{
|
|
if (const UWorld* const World = Actor->GetWorld())
|
|
{
|
|
if (const UWorldPartition* const WorldPartition = World->GetWorldPartition())
|
|
{
|
|
return WorldPartition->IsActorPinned(Actor->GetActorGuid());
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void FActorTreeItem::OnLabelChanged()
|
|
{
|
|
UpdateDisplayString();
|
|
}
|
|
|
|
void FActorTreeItem::GenerateContextMenu(UToolMenu* Menu, SSceneOutliner& Outliner)
|
|
{
|
|
const AActor* ActorPtr = Actor.Get();
|
|
const ILevelInstanceInterface* LevelInstance = Cast<ILevelInstanceInterface>(ActorPtr);
|
|
if (LevelInstance && LevelInstance->IsEditing())
|
|
{
|
|
auto SharedOutliner = StaticCastSharedRef<SSceneOutliner>(Outliner.AsShared());
|
|
const FSlateIcon NewFolderIcon(FAppStyle::GetAppStyleSetName(), "SceneOutliner.NewFolderIcon");
|
|
FToolMenuSection& Section = Menu->AddSection("Section");
|
|
Section.AddMenuEntry("CreateFolder", LOCTEXT("CreateFolder", "Create Folder"), FText(), NewFolderIcon, FUIAction(FExecuteAction::CreateSP(&Outliner, &SSceneOutliner::CreateFolder)));
|
|
}
|
|
}
|
|
|
|
const FGuid& FActorTreeItem::GetGuid() const
|
|
{
|
|
static const FGuid InvalidGuid;
|
|
return Actor.IsValid() ? Actor->GetActorGuid() : InvalidGuid;
|
|
}
|
|
|
|
void FActorTreeItem::UpdateDisplayString()
|
|
{
|
|
UpdateDisplayStringInternal();
|
|
}
|
|
|
|
void FActorTreeItem::UpdateDisplayStringInternal()
|
|
{
|
|
DisplayString = Actor.IsValid() ? Actor->GetActorLabel() : TEXT("None");
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|