// 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(); 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> Actors; TSharedPtr ActorDragOp = nullptr; TSharedPtr FolderDragOp = nullptr; if (const TSharedPtr CompositeDragOp = DragDropEvent.GetOperationAs()) { ActorDragOp = CompositeDragOp->GetSubOp(); FolderDragOp = CompositeDragOp->GetSubOp(); } else { ActorDragOp = DragDropEvent.GetOperationAs(); FolderDragOp = DragDropEvent.GetOperationAs(); } 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> ActorsToDrop; TSharedPtr ActorDragOp = nullptr; TSharedPtr FolderDragOp = nullptr; if (const TSharedPtr CompositeDragOp = DragDropEvent.GetOperationAs()) { ActorDragOp = CompositeDragOp->GetSubOp(); FolderDragOp = CompositeDragOp->GetSubOp(); } else { ActorDragOp = DragDropEvent.GetOperationAs(); FolderDragOp = DragDropEvent.GetOperationAs(); } 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; };