// Copyright Epic Games, Inc. All Rights Reserved. #include "EditorViewportSelectability.h" #include "CanvasTypes.h" #include "Editor/EditorPerProjectUserSettings.h" #include "Elements/Framework/EngineElementsLibrary.h" #include "Elements/Framework/TypedElementRegistry.h" #include "Elements/Framework/TypedElementSelectionSet.h" #include "Elements/Interfaces/TypedElementObjectInterface.h" #include "Elements/Interfaces/TypedElementWorldInterface.h" #include "Engine/Canvas.h" #include "EngineUtils.h" #include "GameFramework/Actor.h" #include "GameFramework/Volume.h" #include "LevelEditorSubsystem.h" #include "LevelEditorViewport.h" #include "Math/Color.h" #include "ScopedTransaction.h" #include "Selection.h" #include "Settings/LevelEditorViewportSettings.h" #include "UnrealWidget.h" #include "UObject/WeakObjectPtr.h" #define LOCTEXT_NAMESPACE "SequencerSelectabilityTool" const FText FEditorViewportSelectability::DefaultLimitedSelectionText = LOCTEXT("DefaultSelectionLimitedHelp", "Viewport Selection Limited"); FEditorViewportSelectability::FEditorViewportSelectability(const FOnGetWorld& InOnGetWorld, const FOnIsObjectSelectableInViewport& InOnIsObjectSelectableInViewport) : OnGetWorld(InOnGetWorld) , OnIsObjectSelectableInViewportDelegate(InOnIsObjectSelectableInViewport) { } void FEditorViewportSelectability::EnableLimitedSelection(const bool bInEnabled) { bSelectionLimited = bInEnabled; if (bSelectionLimited) { DeselectNonSelectableActors(); } UpdateSelectionLimitedVisuals(!bInEnabled); } bool FEditorViewportSelectability::IsObjectSelectableInViewport(UObject* const InObject) const { if (OnIsObjectSelectableInViewportDelegate.IsBound()) { return OnIsObjectSelectableInViewportDelegate.Execute(InObject); } return true; } void FEditorViewportSelectability::UpdatePrimitiveVisuals(const bool bInSelectedLimited, UPrimitiveComponent* const InPrimitive, const TOptional& InColor) { if (bInSelectedLimited && InColor.IsSet()) { // @TODO: Need to resolve rendering issue before this can be used //InPrimitive->SetOverlayColor(InColor.GetValue()); InPrimitive->PushHoveredToProxy(true); } else { // @TODO: Need to resolve rendering issue before this can be used //InPrimitive->RemoveOverlayColor(); InPrimitive->PushHoveredToProxy(false); } } bool FEditorViewportSelectability::UpdateHoveredPrimitive(const bool bInSelectedLimited , UPrimitiveComponent* const InPrimitiveComponent , TMap, TOptional>& InOutHoveredPrimitiveComponents , const TFunctionRef& InSelectablePredicate) { bool bValid = IsValid(InPrimitiveComponent); // Save the current overlay color to restore when unhovered TMap> PrimitiveComponentsToAdd; if (bValid && bInSelectedLimited && InSelectablePredicate(InPrimitiveComponent)) { TOptional UnhoveredColor; if (InPrimitiveComponent->bWantsEditorEffects) { UnhoveredColor = InPrimitiveComponent->OverlayColor; } PrimitiveComponentsToAdd.Add(const_cast(InPrimitiveComponent), UnhoveredColor); bValid = true; } // Get the set of components to remove that aren't in the newly hovered set from the currently hovered compenents TMap> PrimitiveComponentsToRemove; for (const TPair, TOptional>& HoveredPair : InOutHoveredPrimitiveComponents) { UPrimitiveComponent* const PrimitiveComponent = HoveredPair.Key.Get(); if (IsValid(PrimitiveComponent) && !PrimitiveComponentsToAdd.Contains(PrimitiveComponent)) { PrimitiveComponentsToRemove.Add(PrimitiveComponent, HoveredPair.Value); } } // Hover new primitives, unhover old primitives InOutHoveredPrimitiveComponents.Empty(); for (const TPair>& AddPair : PrimitiveComponentsToAdd) { InOutHoveredPrimitiveComponents.Add(AddPair); // Using white becaue we've stripped out the visual element until the rendering issue can be resolved UpdatePrimitiveVisuals(bInSelectedLimited, AddPair.Key, FColor::White); } for (const TPair>& RemovePair : PrimitiveComponentsToRemove) { UpdatePrimitiveVisuals(bInSelectedLimited, RemovePair.Key); } return bValid; } bool FEditorViewportSelectability::UpdateHoveredActorPrimitives(const bool bInSelectedLimited , AActor* const InActor , TMap, TOptional>& InOutHoveredPrimitiveComponents , const TFunctionRef& InSelectablePredicate) { bool bValid = false; // Save the current overlay color to restore when unhovered TMap> PrimitiveComponentsToAdd; if (IsValid(InActor) && bInSelectedLimited) { if (InSelectablePredicate(InActor)) { bValid = true; } InActor->ForEachComponent(/*bIncludeFromChildActors=*/true, [&InSelectablePredicate, &bValid, &PrimitiveComponentsToAdd](UPrimitiveComponent* const InPrimitiveComponent) { if (bValid || InSelectablePredicate(InPrimitiveComponent)) { TOptional UnhoveredColor; if (InPrimitiveComponent->bWantsEditorEffects) { UnhoveredColor = InPrimitiveComponent->OverlayColor; } PrimitiveComponentsToAdd.Add(InPrimitiveComponent, UnhoveredColor); bValid = true; } }); } // Get the set of components to remove that aren't in the newly hovered set from the currently hovered compenents TMap> PrimitiveComponentsToRemove; for (const TPair, TOptional>& HoveredPair : InOutHoveredPrimitiveComponents) { UPrimitiveComponent* const PrimitiveComponent = HoveredPair.Key.Get(); if (IsValid(PrimitiveComponent) && !PrimitiveComponentsToAdd.Contains(PrimitiveComponent)) { PrimitiveComponentsToRemove.Add(PrimitiveComponent, HoveredPair.Value); } } // Hover new primitives, unhover old primitives InOutHoveredPrimitiveComponents.Empty(); for (const TPair>& AddPair : PrimitiveComponentsToAdd) { InOutHoveredPrimitiveComponents.Add(AddPair); // Using white becaue we've stripped out the visual element until the rendering issue can be resolved UpdatePrimitiveVisuals(bInSelectedLimited, AddPair.Key, FColor::White); } for (const TPair>& RemovePair : PrimitiveComponentsToRemove) { UpdatePrimitiveVisuals(bInSelectedLimited, RemovePair.Key); } return bValid; } void FEditorViewportSelectability::UpdateHoveredActorPrimitives(AActor* const InActor) { UpdateHoveredActorPrimitives(bSelectionLimited, InActor, HoveredPrimitiveComponents, [this](UObject* const InObject) -> bool { return IsObjectSelectableInViewport(InObject); }); } void FEditorViewportSelectability::UpdateSelectionLimitedVisuals(const bool bInClearHovered) { if (bInClearHovered) { UpdateHoveredActorPrimitives(nullptr); } for (const TPair, TOptional>& HoveredPair : HoveredPrimitiveComponents) { UPrimitiveComponent* const PrimitiveComponent = HoveredPair.Key.Get(); if (IsValid(PrimitiveComponent)) { if (bSelectionLimited && (IsObjectSelectableInViewport(PrimitiveComponent) || IsObjectSelectableInViewport(PrimitiveComponent->GetOwner()))) { UpdatePrimitiveVisuals(bSelectionLimited, PrimitiveComponent, HoveredPair.Value); } else { UpdatePrimitiveVisuals(bSelectionLimited, PrimitiveComponent); } } } } void FEditorViewportSelectability::DeselectNonSelectableActors() { if (!bSelectionLimited) { return; } USelection* const ActorSelection = GEditor ? GEditor->GetSelectedActors() : nullptr; if (!ActorSelection || ActorSelection->Num() == 0) { return; } TArray SelectedActors; ActorSelection->GetSelectedObjects(SelectedActors); UWorld* const World = OnGetWorld.IsBound() ? OnGetWorld.Execute() : nullptr; SelectActorsByPredicate(World, /*bInSelect=*/true, /*bInClearSelection=*/true , [this](AActor* const InActor) -> bool { return IsObjectSelectableInViewport(InActor); } , SelectedActors); } bool FEditorViewportSelectability::SelectActorsByPredicate(UWorld* const InWorld , const bool bInSelect , const bool bInClearSelection , const TFunctionRef InPredicate , const TArray& InActors) { if (!GEditor || !IsValid(InWorld)) { return false; } USelection* const ActorSelection = GEditor->GetSelectedActors(); if (!ActorSelection) { return false; } const FText TransactionText = bInSelect ? LOCTEXT("SelectActors_Internal", "Select Actor(s)") : LOCTEXT("DeselectActors_Internal", "Deselect Actor(s)"); FScopedTransaction ScopedTransaction(TransactionText, !GIsTransacting); bool bSomethingSelected = false; ActorSelection->BeginBatchSelectOperation(); ActorSelection->Modify(); if (bInClearSelection) { ActorSelection->DeselectAll(); } // Early out for specific deselect case if (!bInSelect && bInClearSelection) { ActorSelection->EndBatchSelectOperation(); GEditor->NoteSelectionChange(); return true; } auto SelectIfPossible = [bInSelect, ActorSelection, &InPredicate, &bSomethingSelected](AActor* const InActor) { if (IsValid(InActor) && ActorSelection->IsSelected(InActor) != bInSelect && InPredicate(InActor)) { bSomethingSelected = true; GEditor->SelectActor(InActor, bInSelect, true); } }; if (InActors.IsEmpty()) { for (FActorIterator Iter(InWorld); Iter; ++Iter) { AActor* const Actor = *Iter; SelectIfPossible(Actor); } } else { for (AActor* const Actor : InActors) { SelectIfPossible(Actor); } } ActorSelection->EndBatchSelectOperation(); GEditor->NoteSelectionChange(); if (!bSomethingSelected) { ScopedTransaction.Cancel(); } return bSomethingSelected; } bool FEditorViewportSelectability::IsActorSelectableClass(const AActor& InActor) { const bool bInvalidClass = InActor.IsA(); return !bInvalidClass; } bool FEditorViewportSelectability::IsActorInLevelHiddenLayer(const AActor& InActor, FLevelEditorViewportClient* const InLevelEditorViewportClient) { if (!InLevelEditorViewportClient) { return false; } for (const FName Layer : InActor.Layers) { if (InLevelEditorViewportClient->ViewHiddenLayers.Contains(Layer)) { return true; } } return false; } TTypedElement FEditorViewportSelectability::GetTypedWorldElementFromActor(const AActor& InActor) { const FTypedElementHandle ActorElementHandle = UEngineElementsLibrary::AcquireEditorActorElementHandle(&InActor); if (!ActorElementHandle) { return TTypedElement(); } const UTypedElementRegistry* const TypedElementRegistry = UTypedElementRegistry::GetInstance(); if (!TypedElementRegistry) { return TTypedElement(); } return TypedElementRegistry->GetElement(ActorElementHandle); } bool FEditorViewportSelectability::GetCursorForHovered(EMouseCursor::Type& OutCursor) const { if (bSelectionLimited && MouseCursor.IsSet()) { OutCursor = MouseCursor.GetValue(); return true; } return false; } void FEditorViewportSelectability::UpdateHoverFromHitProxy(HHitProxy* const InHitProxy) { AActor* Actor = nullptr; bool bIsGizmoHit = false; bool bIsActorHit = false; if (InHitProxy) { if (InHitProxy->IsA(HWidgetAxis::StaticGetType())) { if (bSelectionLimited) { bIsGizmoHit = true; } } else if (InHitProxy->IsA(HActor::StaticGetType())) { const HActor* const ActorHitProxy = static_cast(InHitProxy); if (ActorHitProxy && IsValid(ActorHitProxy->Actor)) { if (bSelectionLimited) { bIsActorHit = true; } Actor = ActorHitProxy->Actor; } } } UpdateHoveredActorPrimitives(Actor); // Set mouse cursor after hovered primitive component list has been updated if (bIsGizmoHit) { MouseCursor = EMouseCursor::CardinalCross; } else if (bIsActorHit) { MouseCursor = HoveredPrimitiveComponents.IsEmpty() ? EMouseCursor::SlashedCircle : EMouseCursor::Crosshairs; } else if (bSelectionLimited) { MouseCursor = EMouseCursor::SlashedCircle; } else { MouseCursor.Reset(); } } bool FEditorViewportSelectability::HandleClick(FEditorViewportClient* const InViewportClient, HHitProxy* const InHitProxy, const FViewportClick& InClick) { if (!InViewportClient) { return false; } UWorld* const World = InViewportClient->GetWorld(); if (!IsValid(World)) { return false; } // Disable actor selection when sequencer is limiting selection const int32 HitX = InViewportClient->Viewport->GetMouseX(); const int32 HitY = InViewportClient->Viewport->GetMouseY(); const HHitProxy* const HitResult = InViewportClient->Viewport->GetHitProxy(HitX, HitY); if (!HitResult) { return false; } if (HitResult->IsA(HWidgetAxis::StaticGetType()) || !HitResult->IsA(HActor::StaticGetType())) { return false; } // Check for translucent actors if we don't want to allow them to be selected const UEditorPerProjectUserSettings* const Settings = GetDefault(); if (!Settings->bAllowSelectTranslucent && HitResult->IsA(HTranslucentActor::StaticGetType())) { // Return true to disable selection of valid translucent actors const HTranslucentActor* const TranslucentActorHitProxy = static_cast(HitResult); return IsValid(TranslucentActorHitProxy->Actor); } const HActor* const ActorHitProxy = static_cast(HitResult); if (!ActorHitProxy || !IsValid(ActorHitProxy->Actor)) { return false; } const bool bNotSelectable = !IsObjectSelectableInViewport(ActorHitProxy->Actor); if (bNotSelectable) { SelectActorsByPredicate(World, false, true , [this](AActor* const InActor) -> bool { return false; }); } return bNotSelectable; } void FEditorViewportSelectability::StartTracking(FEditorViewportClient* const InViewportClient, FViewport* const InViewport) { FIntPoint MousePosition; InViewport->GetMousePos(MousePosition); DragStartPosition = FVector(MousePosition.X, MousePosition.Y, 0); DragEndPosition = DragStartPosition; } void FEditorViewportSelectability::EndTracking(FEditorViewportClient* const InViewportClient, FViewport* const InViewport) { FIntPoint MousePosition; InViewport->GetMousePos(MousePosition); DragEndPosition = FVector(MousePosition); if (DragStartPosition.X > DragEndPosition.X) { Swap(DragStartPosition.X, DragEndPosition.X); } if (DragStartPosition.Y > DragEndPosition.Y) { Swap(DragStartPosition.Y, DragEndPosition.Y); } // Extend the endpoint of the rect to get the actual line const int32 MinX = UE::LWC::FloatToIntCastChecked(FMath::Max(0.0, DragStartPosition.X)); const int32 MinY = UE::LWC::FloatToIntCastChecked(FMath::Max(0.0, DragStartPosition.Y)); const FIntPoint ViewportSize = InViewport->GetSizeXY(); const int32 MaxX = FMath::Min(ViewportSize.X, FMath::TruncToInt32(DragEndPosition.X + 1.0)); const int32 MaxY = FMath::Min(ViewportSize.Y, FMath::TruncToInt32(DragEndPosition.Y + 1.0)); const FIntPoint Min { MinX, MinY }; const FIntPoint Max { MaxX, MaxY }; DragSelectionRect = { Min, Max }; } bool FEditorViewportSelectability::BoxSelectWorldActors(FBox& InBox, FEditorViewportClient* const InEditorViewportClient, const bool bInSelect) { if (!GEditor || !InEditorViewportClient || InEditorViewportClient->IsInGameView()) { return false; } UTypedElementRegistry* const TypedElementRegistry = UTypedElementRegistry::GetInstance(); if (!ensure(TypedElementRegistry)) { return false; } UTypedElementSelectionSet* const SelectionSet = GetLevelEditorSelectionSet(); if (!IsValid(SelectionSet)) { return false; } const ULevelEditorViewportSettings* const LevelEditorViewportSettings = GetDefault(); if (!IsValid(LevelEditorViewportSettings)) { return false; } SelectionSet->Modify(); const bool bUseStrictSelection = LevelEditorViewportSettings->bStrictBoxSelection; FWorldSelectionElementArgs SelectionArgs { SelectionSet, ETypedElementSelectionMethod::Primary, FTypedElementSelectionOptions(), &InEditorViewportClient->EngineShowFlags, bUseStrictSelection, false }; TArray ElementsToSelect; auto AddToElementsToSelect = [this, SelectionSet, &ElementsToSelect](const FTypedElementHandle& InElement) { if (IsTypedElementSelectable(InElement)) { ElementsToSelect.Add(SelectionSet->GetSelectionElement(InElement, ETypedElementSelectionMethod::Primary)); } }; if (LevelEditorViewportSettings->bTransparentBoxSelection) { // Get a list of box-culled elements for (FActorIterator It(InEditorViewportClient->GetWorld()); It; ++It) { AActor* const Actor = *It; GetSelectionElements(Actor, [this, &InBox, &SelectionArgs, &AddToElementsToSelect] (const TTypedElement& InWorldElement) { for (const FTypedElementHandle& Element : InWorldElement.GetSelectionElementsInBox(InBox, SelectionArgs)) { AddToElementsToSelect(Element); } }); } } else { const FTypedElementListRef ElementList = TypedElementRegistry->CreateElementList(); InEditorViewportClient->Viewport->GetElementHandlesInRect(DragSelectionRect, ElementList); if (bUseStrictSelection) { ElementList->ForEachElement([this, bUseStrictSelection, &InBox, &AddToElementsToSelect] (const TTypedElement& InElement) { if (InElement.IsElementInBox(InBox, bUseStrictSelection)) { AddToElementsToSelect(InElement); } return true; }); } else { ElementList->ForEachElementHandle([this, &AddToElementsToSelect] (const FTypedElementHandle& InElement) { AddToElementsToSelect(InElement); return true; }); } } const bool bShiftDown = InEditorViewportClient->Viewport->KeyState(EKeys::LeftShift) || InEditorViewportClient->Viewport->KeyState(EKeys::RightShift); if (!bShiftDown) { // If the user is selecting, but isn't hold down SHIFT, remove the previous selections SelectionSet->SetSelection(MoveTemp(ElementsToSelect), FTypedElementSelectionOptions()); } else { SelectionSet->SelectElements(MoveTemp(ElementsToSelect), FTypedElementSelectionOptions()); } return true; } bool FEditorViewportSelectability::FrustumSelectWorldActors(const FConvexVolume& InFrustum, FEditorViewportClient* const InEditorViewportClient, const bool bInSelect) { if (!GEditor || !InEditorViewportClient || InEditorViewportClient->IsInGameView()) { return false; } UTypedElementRegistry* const TypedElementRegistry = UTypedElementRegistry::GetInstance(); if (!ensure(TypedElementRegistry)) { return false; } UTypedElementSelectionSet* const SelectionSet = GetLevelEditorSelectionSet(); if (!IsValid(SelectionSet)) { return false; } const ULevelEditorViewportSettings* const LevelEditorViewportSettings = GetDefault(); if (!IsValid(LevelEditorViewportSettings)) { return false; } SelectionSet->Modify(); const bool bUseStrictSelection = LevelEditorViewportSettings->bStrictBoxSelection; FWorldSelectionElementArgs SelectionArgs { SelectionSet, ETypedElementSelectionMethod::Primary, FTypedElementSelectionOptions(), &InEditorViewportClient->EngineShowFlags, bUseStrictSelection, false }; TArray ElementsToSelect; auto AddToElementsToSelect = [this, SelectionSet, &ElementsToSelect](const FTypedElementHandle& InElement) { if (IsTypedElementSelectable(InElement)) { ElementsToSelect.Add(SelectionSet->GetSelectionElement(InElement, ETypedElementSelectionMethod::Primary)); } }; if (LevelEditorViewportSettings->bTransparentBoxSelection) { // Get a list of frustum-culled elements for (FActorIterator It(InEditorViewportClient->GetWorld()); It; ++It) { AActor* Actor = *It; GetSelectionElements(Actor, [this, &InFrustum, &SelectionArgs, &AddToElementsToSelect] (const TTypedElement& InWorldElement) { for (const FTypedElementHandle& Element : InWorldElement.GetSelectionElementsInConvexVolume(InFrustum, SelectionArgs)) { AddToElementsToSelect(Element); } }); } } else { const FTypedElementListRef ElementList = TypedElementRegistry->CreateElementList(); InEditorViewportClient->Viewport->GetElementHandlesInRect(DragSelectionRect, ElementList); if (bUseStrictSelection) { ElementList->ForEachElement([this, bUseStrictSelection, &InFrustum, &AddToElementsToSelect] (const TTypedElement& InElement) { if (InElement.IsElementInConvexVolume(InFrustum, bUseStrictSelection)) { AddToElementsToSelect(InElement); } return true; }); } else { ElementList->ForEachElementHandle([this, &AddToElementsToSelect] (const FTypedElementHandle& InElement) { AddToElementsToSelect(InElement); return true; }); } } const bool bShiftDown = InEditorViewportClient->Viewport->KeyState(EKeys::LeftShift) || InEditorViewportClient->Viewport->KeyState(EKeys::RightShift); if (!bShiftDown) { // If the user is selecting, but isn't hold down SHIFT, remove the previous selections SelectionSet->SetSelection(MoveTemp(ElementsToSelect), FTypedElementSelectionOptions()); } else { SelectionSet->SelectElements(MoveTemp(ElementsToSelect), FTypedElementSelectionOptions()); } return true; } void FEditorViewportSelectability::DrawEnabledTextNotice(FCanvas* const InCanvas, const FText& InText) { const FStringView HelpString = *InText.ToString(); FTextSizingParameters SizingParameters(GEngine->GetLargeFont(), 1.f, 1.f); UCanvas::CanvasStringSize(SizingParameters, HelpString); const float ViewWidth = InCanvas->GetViewRect().Width() / InCanvas->GetDPIScale(); const float DrawX = FMath::FloorToFloat((ViewWidth - SizingParameters.DrawXL) * 0.5f); InCanvas->DrawShadowedString(DrawX, 34.f, HelpString, GEngine->GetLargeFont(), FLinearColor::White); } FText FEditorViewportSelectability::GetLimitedSelectionText(const TSharedPtr& InToggleAction, const FText& InDefaultText) { if (!InToggleAction.IsValid()) { return FText(); } FText HelpText = InDefaultText.IsEmpty() ? DefaultLimitedSelectionText : InDefaultText; if (InToggleAction.IsValid()) { const TSharedRef ActiveChord = InToggleAction->GetFirstValidChord(); if (ActiveChord->IsValidChord()) { HelpText = FText::Format(LOCTEXT("LimitedSelectionActionKeyHelp", "{0} ({1} to toggle)"), HelpText, ActiveChord->GetInputText(true)); } } return HelpText; } UTypedElementSelectionSet* FEditorViewportSelectability::GetLevelEditorSelectionSet() { if (!ensure(GEditor)) { return nullptr; } UTypedElementRegistry* const TypedElementRegistry = UTypedElementRegistry::GetInstance(); if (!ensure(TypedElementRegistry)) { return nullptr; } ULevelEditorSubsystem* const EditorSubsystem = GEditor->GetEditorSubsystem(); if (!ensure(IsValid(EditorSubsystem))) { return nullptr; } return EditorSubsystem->GetSelectionSet(); } bool FEditorViewportSelectability::IsTypedElementSelectable(const FTypedElementHandle& InElementHandle) const { UTypedElementRegistry* const TypedElementRegistry = UTypedElementRegistry::GetInstance(); if (!ensure(TypedElementRegistry)) { return false; } ITypedElementObjectInterface* const ObjectInterface = TypedElementRegistry->GetElementInterface(InElementHandle); if (!ObjectInterface) { return false; } UPrimitiveComponent* const PrimitiveComponent = ObjectInterface->GetObjectAs(InElementHandle); if (!IsValid(PrimitiveComponent)) { return false; } AActor* const Actor = PrimitiveComponent->GetOwner(); if (!IsValid(Actor)) { return false; } if (Actor->IsHiddenEd() || !IsActorSelectableClass(*Actor)) { return false; } if (GCurrentLevelEditingViewportClient && IsActorInLevelHiddenLayer(*Actor, GCurrentLevelEditingViewportClient)) { return false; } return IsObjectSelectableInViewport(Actor); } void FEditorViewportSelectability::GetSelectionElements(AActor* const InActor, const TFunctionRef&)> InPredicate) { if (!IsValid(InActor)) { return; } if (InActor->IsA(AVolume::StaticClass())) { if (!GCurrentLevelEditingViewportClient || !GCurrentLevelEditingViewportClient->IsVolumeVisibleInViewport(*InActor)) { return; } } UTypedElementRegistry* const TypedElementRegistry = UTypedElementRegistry::GetInstance(); if (!ensure(TypedElementRegistry)) { return; } if (const FTypedElementHandle ElementHandle = UEngineElementsLibrary::AcquireEditorActorElementHandle(InActor)) { if (const TTypedElement WorldElement = TypedElementRegistry->GetElement(ElementHandle)) { InPredicate(WorldElement); } } } #undef LOCTEXT_NAMESPACE