Files
UnrealEngine/Engine/Source/Editor/Layers/Private/SLayerBrowser.h
2025-05-18 13:04:45 +08:00

461 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "InputCoreTypes.h"
#include "Layout/Visibility.h"
#include "Styling/SlateColor.h"
#include "Layout/Geometry.h"
#include "Input/Reply.h"
#include "Widgets/SWidget.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SCompoundWidget.h"
#include "Styling/AppStyle.h"
#include "Widgets/Input/SButton.h"
#include "Misc/TextFilter.h"
#include "ActorsAssignedToSpecificLayersFilter.h"
#include "ISceneOutlinerColumn.h"
#include "SceneOutlinerLayerContentsColumn.h"
#include "DragAndDrop/ActorDragDropOp.h"
#include "DragAndDrop/FolderDragDropOp.h"
#include "DragAndDrop/CompositeDragDropOp.h"
#include "SLayersView.h"
#include "SLayersCommandsMenu.h"
#include "EditorActorFolders.h"
class ISceneOutliner;
typedef TTextFilter< const TSharedPtr< FLayerViewModel >& > LayerTextFilter;
namespace ELayerBrowserMode
{
enum Type
{
Layers,
LayerContents,
Count
};
}
/**
*
*/
class SLayerBrowser : public SCompoundWidget
{
public:
SLATE_BEGIN_ARGS( SLayerBrowser ) {}
SLATE_END_ARGS()
~SLayerBrowser()
{
LayerCollectionViewModel->OnLayersChanged().RemoveAll( this );
LayerCollectionViewModel->OnSelectionChanged().RemoveAll( this );
LayerCollectionViewModel->OnRenameRequested().RemoveAll( this );
LayerCollectionViewModel->RemoveFilter( SearchBoxLayerFilter.ToSharedRef() );
}
/**
* Construct this widget. Called by the SNew() Slate macro.
*
* @param InArgs Declaration used by the SNew() macro to construct this widget
* @param InViewModel The UI logic not specific to slate
*/
void Construct( const FArguments& InArgs );
protected:
/** Overridden from SWidget: Called when a key is pressed down */
FReply OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent ) override
{
if( InKeyEvent.GetKey() == EKeys::Escape && Mode == ELayerBrowserMode::LayerContents )
{
SetupLayersMode();
return FReply::Handled();
}
return FReply::Unhandled();
}
/**
* Called during drag and drop when the drag leaves a widget.
*
* @param DragDropEvent The drag and drop event.
*/
virtual void OnDragLeave( const FDragDropEvent& DragDropEvent ) override
{
TSharedPtr< FDecoratedDragDropOp > DragOp = DragDropEvent.GetOperationAs<FDecoratedDragDropOp>();
if (DragOp.IsValid())
{
DragOp->ResetToDefaultToolTip();
}
}
/**
* Called during drag and drop when the the mouse is being dragged over a widget.
*
* @param MyGeometry The geometry of the widget receiving the event.
* @param DragDropEvent The drag and drop event.
*
* @return A reply that indicated whether this event was handled.
*/
virtual FReply OnDragOver( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent ) override
{
if (!SelectedLayerViewModel->GetDataSource().IsValid())
{
return FReply::Unhandled();
}
TArray<TWeakObjectPtr<AActor>> Actors;
TSharedPtr<FActorDragDropOp> ActorDragOp = nullptr;
TSharedPtr<FFolderDragDropOp> FolderDragOp = nullptr;
if (const TSharedPtr<FCompositeDragDropOp> CompositeDragOp = DragDropEvent.GetOperationAs<FCompositeDragDropOp>())
{
ActorDragOp = CompositeDragOp->GetSubOp<FActorDragDropOp>();
FolderDragOp = CompositeDragOp->GetSubOp<FFolderDragDropOp>();
}
else
{
ActorDragOp = DragDropEvent.GetOperationAs<FActorDragDropOp>();
FolderDragOp = DragDropEvent.GetOperationAs<FFolderDragDropOp>();
}
if (ActorDragOp.IsValid() || FolderDragOp.IsValid())
{
const FGeometry LayerContentsHeaderGeometry = SWidget::FindChildGeometry( MyGeometry, LayerContentsHeader.ToSharedRef() );
bool bValidDrop = LayerContentsHeaderGeometry.IsUnderLocation( DragDropEvent.GetScreenSpacePosition() );
if( !bValidDrop && Mode == ELayerBrowserMode::LayerContents )
{
const FGeometry LayerContentsSectionGeometry = SWidget::FindChildGeometry( MyGeometry, LayerContentsSection.ToSharedRef() );
bValidDrop = LayerContentsSectionGeometry.IsUnderLocation( DragDropEvent.GetScreenSpacePosition() );
}
if( bValidDrop && ActorDragOp.IsValid() && ActorDragOp->Actors.Num() > 0)
{
Actors = ActorDragOp->Actors;
}
if (bValidDrop && FolderDragOp.IsValid())
{
if (UWorld* World = FolderDragOp->World.Get())
{
FActorFolders::GetWeakActorsFromFolders(*World, FolderDragOp->Folders, Actors, FolderDragOp->RootObject);
}
}
}
if (Actors.Num() > 0)
{
bool bCanAssign = false;
FText Message;
if (Actors.Num() > 1)
{
bCanAssign = SelectedLayerViewModel->CanAssignActors(Actors, OUT Message);
}
else
{
bCanAssign = SelectedLayerViewModel->CanAssignActor(Actors[0], OUT Message);
}
if (bCanAssign)
{
if (ActorDragOp.IsValid())
{
ActorDragOp->SetToolTip(Message, FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.OK")));
}
if (FolderDragOp.IsValid())
{
FolderDragOp->SetToolTip(Message, FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.OK")));
}
}
else
{
if (ActorDragOp.IsValid())
{
ActorDragOp->SetToolTip(Message, FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error")));
}
if (FolderDragOp.IsValid())
{
FolderDragOp->SetToolTip(Message, FAppStyle::GetBrush(TEXT("Graph.ConnectorFeedback.Error")));
}
}
}
// We leave the event unhandled so the children of the ListView get a chance to grab the drag/drop
return FReply::Unhandled();
}
/**
* Called when the user is dropping something onto a widget; terminates drag and drop.
*
* @param MyGeometry The geometry of the widget receiving the event.
* @param DragDropEvent The drag and drop event.
*
* @return A reply that indicated whether this event was handled.
*/
virtual FReply OnDrop( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent ) override
{
if (!SelectedLayerViewModel->GetDataSource().IsValid())
{
return FReply::Unhandled();
}
bool bHandled = false;
TArray<TWeakObjectPtr<AActor>> ActorsToDrop;
TSharedPtr<FActorDragDropOp> ActorDragOp = nullptr;
TSharedPtr<FFolderDragDropOp> FolderDragOp = nullptr;
if (const TSharedPtr<FCompositeDragDropOp> CompositeDragOp = DragDropEvent.GetOperationAs<FCompositeDragDropOp>())
{
ActorDragOp = CompositeDragOp->GetSubOp<FActorDragDropOp>();
FolderDragOp = CompositeDragOp->GetSubOp<FFolderDragDropOp>();
}
else
{
ActorDragOp = DragDropEvent.GetOperationAs<FActorDragDropOp>();
FolderDragOp = DragDropEvent.GetOperationAs<FFolderDragDropOp>();
}
if (ActorDragOp.IsValid())
{
const FGeometry LayerContentsHeaderGeometry = SWidget::FindChildGeometry(MyGeometry, LayerContentsHeader.ToSharedRef());
bool bValidDrop = LayerContentsHeaderGeometry.IsUnderLocation(DragDropEvent.GetScreenSpacePosition());
if (!bValidDrop && Mode == ELayerBrowserMode::LayerContents)
{
const FGeometry LayerContentsSectionGeometry = SWidget::FindChildGeometry(MyGeometry, LayerContentsSection.ToSharedRef());
bValidDrop = LayerContentsSectionGeometry.IsUnderLocation(DragDropEvent.GetScreenSpacePosition());
}
if (bValidDrop)
{
ActorsToDrop = ActorDragOp->Actors;
bHandled = true;
}
}
if (FolderDragOp.IsValid())
{
if (UWorld* World = FolderDragOp->World.Get())
{
FActorFolders::GetWeakActorsFromFolders(*World, FolderDragOp->Folders, ActorsToDrop, FolderDragOp->RootObject);
bHandled = true;
}
}
if (ActorsToDrop.Num() > 0)
{
SelectedLayerViewModel->AddActors(ActorsToDrop);
}
return bHandled ? FReply::Handled() : FReply::Unhandled();
}
private:
void RemoveActorsFromSelectedLayer( const TArray< TWeakObjectPtr< AActor > >& Actors )
{
SelectedLayerViewModel->RemoveActors( Actors );
}
TSharedRef< ISceneOutlinerColumn > CreateCustomLayerColumn( ISceneOutliner& SceneOutliner ) const
{
return MakeShareable( new FSceneOutlinerLayerContentsColumn( SelectedLayerViewModel.ToSharedRef() ) );
}
FSlateColor GetInvertedForegroundIfHovered() const
{
static const FName InvertedForegroundName("InvertedForeground");
return ( ToggleModeButton.IsValid() && ( ToggleModeButton->IsHovered() || ToggleModeButton->IsPressed() ) ) ? FAppStyle::GetSlateColor(InvertedForegroundName): FSlateColor::UseForeground();
}
const FSlateBrush* GetToggleModeButtonImageBrush() const
{
static const FName ExploreLayerContents("LayerBrowser.ExploreLayerContents");
static const FName ReturnToLayersList("LayerBrowser.ReturnToLayersList");
return ( Mode == ELayerBrowserMode::Layers ) ? FAppStyle::GetBrush( ExploreLayerContents ) : FAppStyle::GetBrush( ReturnToLayersList );
}
FText GetLayerContentsHeaderText() const;
/** Returns the visibility of the See Contents label */
EVisibility IsVisibleIfModeIs( ELayerBrowserMode::Type DesiredMode ) const
{
return ( Mode == DesiredMode ) ? EVisibility::Visible : EVisibility::Collapsed;
}
EVisibility GetLayerContentsHeaderVisibility() const
{
return ( SelectedLayerViewModel->GetDataSource().IsValid() ) ? EVisibility::Visible : EVisibility::Collapsed;
}
FReply ToggleLayerContents()
{
switch( Mode )
{
default:
case ELayerBrowserMode::Layers:
SetupLayerContentsMode();
break;
case ELayerBrowserMode::LayerContents:
SetupLayersMode();
break;
}
return FReply::Handled();
}
void SetupLayersMode()
{
ContentAreaBox->ClearChildren();
ContentAreaBox->AddSlot()
.FillHeight( 1.0f )
[
LayersSection.ToSharedRef()
];
ContentAreaBox->AddSlot()
.AutoHeight()
.VAlign( VAlign_Bottom )
.MaxHeight( 23 )
[
LayerContentsHeader.ToSharedRef()
];
Mode = ELayerBrowserMode::Layers;
}
void SetupLayerContentsMode()
{
ContentAreaBox->ClearChildren();
ContentAreaBox->AddSlot()
.AutoHeight()
.VAlign( VAlign_Top )
.MaxHeight( 23 )
[
LayerContentsHeader.ToSharedRef()
];
ContentAreaBox->AddSlot()
.AutoHeight()
.FillHeight( 1.0f )
[
LayerContentsSection.ToSharedRef()
];
Mode = ELayerBrowserMode::LayerContents;
}
void TransformLayerToString( const TSharedPtr< FLayerViewModel >& Layer, OUT TArray< FString >& OutSearchStrings ) const
{
if( !Layer.IsValid() )
{
return;
}
OutSearchStrings.Add( Layer->GetName() );
}
void UpdateLayerContentsFilter()
{
TArray< FName > LayerNames;
LayerCollectionViewModel->GetSelectedLayerNames( LayerNames );
SelectedLayersFilter->SetLayers( LayerNames );
}
void UpdateSelectedLayer()
{
UpdateLayerContentsFilter();
if( LayerCollectionViewModel->GetSelectedLayers().Num() == 1 )
{
const auto Layers = LayerCollectionViewModel->GetSelectedLayers();
check( Layers.Num() == 1 );
SelectedLayerViewModel->SetDataSource( Layers[ 0 ]->GetDataSource() );
}
else
{
SelectedLayerViewModel->SetDataSource( NULL );
}
}
void OnLayersChanged( const ELayersAction::Type Action, const TWeakObjectPtr< ULayer >& ChangedLayer, const FName& ChangedProperty )
{
if( Action != ELayersAction::Reset && Action != ELayersAction::Delete )
{
if( !ChangedLayer.IsValid() || SelectedLayerViewModel->GetDataSource() == ChangedLayer )
{
UpdateLayerContentsFilter();
}
return;
}
UpdateSelectedLayer();
if( Mode == ELayerBrowserMode::LayerContents && !SelectedLayerViewModel->GetDataSource().IsValid() )
{
SetupLayersMode();
}
}
/** Callback when layers want to be renamed */
void OnRenameRequested()
{
LayersView->RequestRenameOnSelectedLayer();
}
TSharedPtr< SWidget > ConstructLayerContextMenu()
{
return SNew( SLayersCommandsMenu, LayerCollectionViewModel.ToSharedRef() );
}
void OnFilterTextChanged( const FText& InNewText );
private:
/** */
TSharedPtr< SButton > ToggleModeButton;
/** */
TSharedPtr< SVerticalBox > ContentAreaBox;
/** */
TSharedPtr< SBorder > LayersSection;
/** */
TSharedPtr< SBorder > LayerContentsSection;
/** */
TSharedPtr< SBorder > LayerContentsHeader;
/** */
TSharedPtr< class SSearchBox> SearchBoxPtr;
/** */
TSharedPtr< LayerTextFilter > SearchBoxLayerFilter;
/** */
TSharedPtr< FActorsAssignedToSpecificLayersFilter > SelectedLayersFilter;
/** */
TSharedPtr< FLayerCollectionViewModel > LayerCollectionViewModel;
/** */
ELayerBrowserMode::Type Mode;
/** */
TSharedPtr< FLayerViewModel > SelectedLayerViewModel;
/** The layer view widget, displays all the layers in the level */
TSharedPtr< SLayersView > LayersView;
};