2019-12-26 15:33:43 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2015-02-09 11:42:01 -05:00
# include "DragDropHandler.h"
2022-08-30 23:03:03 -04:00
# include "AssetViewUtils.h"
2024-04-05 09:31:38 -04:00
# include "CollectionViewUtils.h"
2022-08-30 23:03:03 -04:00
# include "Containers/Array.h"
# include "Containers/Map.h"
2020-06-23 18:40:00 -04:00
# include "ContentBrowserDataDragDropOp.h"
# include "ContentBrowserDataMenuContexts.h"
2022-08-30 23:03:03 -04:00
# include "ContentBrowserDataSource.h"
# include "ContentBrowserItem.h"
# include "ContentBrowserItemData.h"
# include "ContentBrowserUtils.h"
# include "Delegates/Delegate.h"
# include "Framework/Application/MenuStack.h"
# include "Framework/Application/SlateApplication.h"
# include "Framework/Commands/UIAction.h"
# include "HAL/Platform.h"
# include "HAL/PlatformCrt.h"
# include "Input/DragAndDrop.h"
# include "Internationalization/Internationalization.h"
# include "Internationalization/Text.h"
# include "Layout/SlateRect.h"
# include "Layout/WidgetPath.h"
# include "Math/Vector2D.h"
# include "Misc/AssertionMacros.h"
# include "Misc/EnumClassFlags.h"
# include "Styling/AppStyle.h"
2022-08-30 23:04:40 -04:00
# include "Templates/Casts.h"
2022-08-30 23:03:03 -04:00
# include "Templates/Invoke.h"
# include "Templates/Tuple.h"
# include "Textures/SlateIcon.h"
# include "ToolMenu.h"
# include "ToolMenuContext.h"
# include "ToolMenuDelegates.h"
# include "ToolMenuSection.h"
# include "ToolMenus.h"
# include "UObject/NameTypes.h"
# include "UObject/UObjectGlobals.h"
2020-06-23 18:40:00 -04:00
2015-02-09 11:42:01 -05:00
# define LOCTEXT_NAMESPACE "ContentBrowser"
2024-04-05 09:31:38 -04:00
namespace DragDropHandlerUtilsPrivate
{
void RetrieveFolderInformation ( const FContentBrowserItem & InFolder , FName & OutFolderBrushName , FName & OutFolderShadowName , FLinearColor & OutFolderOverrideColor )
{
// We are just dragging folders so add information on the FolderBrush to use and the color
if ( InFolder . IsValid ( ) & & InFolder . IsFolder ( ) )
{
ContentBrowserUtils : : TryGetFolderBrushAndShadowName ( InFolder , OutFolderBrushName , OutFolderShadowName ) ;
const bool bCollectionFolder = EnumHasAnyFlags ( InFolder . GetItemCategory ( ) , EContentBrowserItemFlags : : Category_Collection ) ;
if ( bCollectionFolder )
{
FName CollectionName ;
ECollectionShareType : : Type CollectionFolderShareType = ECollectionShareType : : CST_All ;
ContentBrowserUtils : : IsCollectionPath ( InFolder . GetVirtualPath ( ) . ToString ( ) , & CollectionName , & CollectionFolderShareType ) ;
if ( TOptional < FLinearColor > Color = CollectionViewUtils : : GetCustomColor ( CollectionName , CollectionFolderShareType ) )
{
OutFolderOverrideColor = Color . GetValue ( ) ;
}
}
else
{
if ( TOptional < FLinearColor > Color = ContentBrowserUtils : : GetPathColor ( InFolder . GetInvariantPath ( ) . ToString ( ) ) )
{
OutFolderOverrideColor = Color . GetValue ( ) ;
}
}
}
}
}
2020-06-23 18:40:00 -04:00
namespace DragDropHandler
2015-02-09 11:42:01 -05:00
{
2015-06-19 07:33:02 -04:00
2020-06-23 18:40:00 -04:00
TSharedPtr < FDragDropOperation > CreateDragOperation ( TArrayView < const FContentBrowserItem > InItems )
{
if ( InItems . Num ( ) = = 0 )
{
return nullptr ;
}
// Batch these by their data sources
TMap < UContentBrowserDataSource * , TArray < FContentBrowserItemData > > SourcesAndItems ;
for ( const FContentBrowserItem & Item : InItems )
{
FContentBrowserItem : : FItemDataArrayView ItemDataArray = Item . GetInternalItems ( ) ;
for ( const FContentBrowserItemData & ItemData : ItemDataArray )
{
if ( UContentBrowserDataSource * ItemDataSource = ItemData . GetOwnerDataSource ( ) )
{
TArray < FContentBrowserItemData > & ItemsForSource = SourcesAndItems . FindOrAdd ( ItemDataSource ) ;
ItemsForSource . Add ( ItemData ) ;
}
}
}
// Custom handling via a data source?
for ( const auto & SourceAndItemsPair : SourcesAndItems )
{
if ( TSharedPtr < FDragDropOperation > CustomDragOp = SourceAndItemsPair . Key - > CreateCustomDragOperation ( SourceAndItemsPair . Value ) )
{
return CustomDragOp ;
}
}
// Generic handling
2024-04-05 09:31:38 -04:00
FName FolderBrushName = NAME_None ;
FName FolderShadowBrushName = NAME_None ;
FLinearColor FolderColor = ContentBrowserUtils : : GetDefaultColor ( ) ;
// This information will be later used by the ContentBrowserDragAnDropOp in case you are dragging only folders
if ( InItems . Num ( ) > 0 )
{
DragDropHandlerUtilsPrivate : : RetrieveFolderInformation ( InItems [ 0 ] , FolderBrushName , FolderShadowBrushName , FolderColor ) ;
}
FThumbnailOverrideParams FolderThumbnailOverrideParams ;
FolderThumbnailOverrideParams . FolderBrushName = FolderBrushName ;
FolderThumbnailOverrideParams . FolderShadowBrushName = FolderShadowBrushName ;
FolderThumbnailOverrideParams . FolderColorOverride = FolderColor ;
TSharedRef < FContentBrowserDataDragDropOp > ContentBrowserDragAndDrop = FContentBrowserDataDragDropOp : : New ( InItems , FolderThumbnailOverrideParams ) ;
return ContentBrowserDragAndDrop ;
2020-06-23 18:40:00 -04:00
}
template < typename FuncType >
bool HandleDragEventOverride ( const FContentBrowserItem & InItem , const FDragDropEvent & InDragDropEvent , FuncType Func )
{
FContentBrowserItem : : FItemDataArrayView ItemDataArray = InItem . GetInternalItems ( ) ;
for ( const FContentBrowserItemData & ItemData : ItemDataArray )
{
if ( UContentBrowserDataSource * ItemDataSource = ItemData . GetOwnerDataSource ( ) )
{
if ( Invoke ( Func , ItemDataSource , ItemData , InDragDropEvent ) )
{
return true ;
}
}
}
return false ;
}
bool ValidateGenericDragEvent ( const FContentBrowserItem & InItem , const FDragDropEvent & InDragDropEvent )
{
if ( ! InItem . IsFolder ( ) )
2015-02-09 11:42:01 -05:00
{
return false ;
}
2020-06-23 18:40:00 -04:00
if ( TSharedPtr < FContentBrowserDataDragDropOp > ContentDragDropOp = InDragDropEvent . GetOperationAs < FContentBrowserDataDragDropOp > ( ) )
2015-02-09 11:42:01 -05:00
{
2020-06-23 18:40:00 -04:00
if ( EnumHasAnyFlags ( InItem . GetItemCategory ( ) , EContentBrowserItemFlags : : Category_Collection ) )
2015-02-09 11:42:01 -05:00
{
2022-05-09 13:12:28 -04:00
ContentDragDropOp - > SetToolTip ( LOCTEXT ( " OnDragFoldersOverFolder_CannotDropOnCollectionFolder " , " Cannot drop onto a collection folder. Drop onto the collection in the collection view instead. " ) , FAppStyle : : GetBrush ( TEXT ( " Graph.ConnectorFeedback.Error " ) ) ) ;
2015-02-09 11:42:01 -05:00
}
2020-06-23 18:40:00 -04:00
else if ( ContentDragDropOp - > GetDraggedItems ( ) . Num ( ) = = 1 & & ContentDragDropOp - > GetDraggedItems ( ) [ 0 ] . GetVirtualPath ( ) = = InItem . GetVirtualPath ( ) )
2015-02-09 11:42:01 -05:00
{
2022-05-09 13:12:28 -04:00
ContentDragDropOp - > SetToolTip ( LOCTEXT ( " OnDragFoldersOverFolder_CannotSelfDrop " , " Cannot move or copy a folder onto itself " ) , FAppStyle : : GetBrush ( TEXT ( " Graph.ConnectorFeedback.Error " ) ) ) ;
2015-02-09 11:42:01 -05:00
}
else
{
2020-06-23 18:40:00 -04:00
int32 NumDraggedItems = ContentDragDropOp - > GetDraggedItems ( ) . Num ( ) ;
int32 NumCanMoveOrCopy = 0 ;
for ( const FContentBrowserItem & DraggedItem : ContentDragDropOp - > GetDraggedItems ( ) )
{
const bool bCanMoveOrCopy = DraggedItem . CanMove ( InItem . GetVirtualPath ( ) ) | | DraggedItem . CanCopy ( InItem . GetVirtualPath ( ) ) ;
if ( bCanMoveOrCopy )
{
+ + NumCanMoveOrCopy ;
}
}
if ( NumCanMoveOrCopy = = 0 )
{
2022-05-09 13:12:28 -04:00
ContentDragDropOp - > SetToolTip ( LOCTEXT ( " OnDragFoldersOverFolder_CannotDrop " , " Cannot move or copy to this folder " ) , FAppStyle : : GetBrush ( TEXT ( " Graph.ConnectorFeedback.Error " ) ) ) ;
2020-06-23 18:40:00 -04:00
}
else
{
const FText FirstItemText = ContentDragDropOp - > GetDraggedItems ( ) [ 0 ] . GetDisplayName ( ) ;
const FText MoveOrCopyText = ( NumCanMoveOrCopy > 1 )
? FText : : Format ( LOCTEXT ( " OnDragAssetsOverFolder_MultipleItems " , " Move or copy '{0}' and {1} {1}|plural(one=other,other=others) " ) , FirstItemText , NumDraggedItems - 1 )
: FText : : Format ( LOCTEXT ( " OnDragAssetsOverFolder_SingularItems " , " Move or copy '{0}' " ) , FirstItemText ) ;
if ( NumCanMoveOrCopy < NumDraggedItems )
{
2022-05-09 13:12:28 -04:00
ContentDragDropOp - > SetToolTip ( FText : : Format ( LOCTEXT ( " OnDragAssetsOverFolder_SomeInvalidItems " , " {0} \n \n {1} {1}|plural(one=item,other=items) will be ignored as they cannot be moved or copied " ) , MoveOrCopyText , NumDraggedItems - NumCanMoveOrCopy ) , FAppStyle : : GetBrush ( TEXT ( " Graph.ConnectorFeedback.OKWarn " ) ) ) ;
2020-06-23 18:40:00 -04:00
}
else
{
2022-05-09 13:12:28 -04:00
ContentDragDropOp - > SetToolTip ( MoveOrCopyText , FAppStyle : : GetBrush ( TEXT ( " Graph.ConnectorFeedback.OK " ) ) ) ;
2020-06-23 18:40:00 -04:00
}
}
}
return true ;
}
return false ;
}
bool HandleDragEnterItem ( const FContentBrowserItem & InItem , const FDragDropEvent & InDragDropEvent )
{
// Custom handling via a data source?
if ( HandleDragEventOverride ( InItem , InDragDropEvent , & UContentBrowserDataSource : : HandleDragEnterItem ) )
{
return true ;
}
// Generic handling
return ValidateGenericDragEvent ( InItem , InDragDropEvent ) ;
}
bool HandleDragOverItem ( const FContentBrowserItem & InItem , const FDragDropEvent & InDragDropEvent )
{
// Custom handling via a data source?
if ( HandleDragEventOverride ( InItem , InDragDropEvent , & UContentBrowserDataSource : : HandleDragOverItem ) )
{
return true ;
}
// Generic handling
return ValidateGenericDragEvent ( InItem , InDragDropEvent ) ;
}
bool HandleDragLeaveItem ( const FContentBrowserItem & InItem , const FDragDropEvent & InDragDropEvent )
{
// Custom handling via a data source?
if ( HandleDragEventOverride ( InItem , InDragDropEvent , & UContentBrowserDataSource : : HandleDragLeaveItem ) )
{
return true ;
}
if ( ! InItem . IsFolder ( ) )
{
return false ;
}
// Generic handling
if ( TSharedPtr < FContentBrowserDataDragDropOp > ContentDragDropOp = InDragDropEvent . GetOperationAs < FContentBrowserDataDragDropOp > ( ) )
{
ContentDragDropOp - > ResetToDefaultToolTip ( ) ;
return true ;
}
return false ;
}
template < typename CanMoveOrCopyFuncType , typename BulkMoveOrCopyFuncType >
void HandleDragDropMoveOrCopy ( const FContentBrowserItem & InDropTargetItem , const TArray < FContentBrowserItem > & InDraggedItems , const TSharedPtr < SWidget > & InParentWidget , const FText InMoveOrCopyMsg , CanMoveOrCopyFuncType InCanMoveOrCopyFunc , BulkMoveOrCopyFuncType InBulkMoveOrCopyFunc )
{
const FName InDropTargetPath = InDropTargetItem . GetVirtualPath ( ) ;
// Batch these by their data sources
TMap < UContentBrowserDataSource * , TArray < FContentBrowserItemData > > SourcesAndItems ;
for ( const FContentBrowserItem & DraggedItem : InDraggedItems )
{
FContentBrowserItem : : FItemDataArrayView ItemDataArray = DraggedItem . GetInternalItems ( ) ;
for ( const FContentBrowserItemData & ItemData : ItemDataArray )
{
if ( UContentBrowserDataSource * ItemDataSource = ItemData . GetOwnerDataSource ( ) )
{
FText MoveOrCopyErrorMsg ;
if ( Invoke ( InCanMoveOrCopyFunc , * ItemDataSource , ItemData , InDropTargetPath , & MoveOrCopyErrorMsg ) )
{
TArray < FContentBrowserItemData > & ItemsForSource = SourcesAndItems . FindOrAdd ( ItemDataSource ) ;
ItemsForSource . Add ( ItemData ) ;
}
else
{
AssetViewUtils : : ShowErrorNotifcation ( MoveOrCopyErrorMsg ) ;
}
}
2015-02-09 11:42:01 -05:00
}
}
2020-06-23 18:40:00 -04:00
// Execute the operation now
int32 NumMovedOrCopiedItems = 0 ;
for ( const auto & SourceAndItemsPair : SourcesAndItems )
2015-02-09 11:42:01 -05:00
{
2020-06-23 18:40:00 -04:00
if ( Invoke ( InBulkMoveOrCopyFunc , * SourceAndItemsPair . Key , SourceAndItemsPair . Value , InDropTargetPath ) )
{
// This assumes that everything passed is moved or copied, which may not be true, but we've validated as best we can when building this array
NumMovedOrCopiedItems + = SourceAndItemsPair . Value . Num ( ) ;
}
2015-02-09 11:42:01 -05:00
}
2020-06-23 18:40:00 -04:00
// Show a message if the move or copy was successful
if ( NumMovedOrCopiedItems > 0 & & InParentWidget )
2015-02-09 11:42:01 -05:00
{
2020-06-23 18:40:00 -04:00
const FText Message = FText : : Format ( InMoveOrCopyMsg , NumMovedOrCopiedItems , FText : : FromName ( InDropTargetPath ) ) ;
2023-02-06 12:05:08 -05:00
const FVector2f & CursorPos = FSlateApplication : : Get ( ) . GetCursorPos ( ) ;
2020-06-23 18:40:00 -04:00
FSlateRect MessageAnchor ( CursorPos . X , CursorPos . Y , CursorPos . X , CursorPos . Y ) ;
ContentBrowserUtils : : DisplayMessage ( Message , MessageAnchor , InParentWidget . ToSharedRef ( ) ) ;
2015-02-09 11:42:01 -05:00
}
}
2020-06-23 18:40:00 -04:00
void HandleDragDropMove ( const FContentBrowserItem & InDropTargetItem , const TArray < FContentBrowserItem > & InDraggedItems , const TSharedPtr < SWidget > & InParentWidget )
2015-02-09 11:42:01 -05:00
{
2020-06-23 18:40:00 -04:00
return HandleDragDropMoveOrCopy ( InDropTargetItem , InDraggedItems , InParentWidget , LOCTEXT ( " ItemsDroppedMove " , " {0} {0}|plural(one=item,other=items) moved to ' { 1 } ' " ), &UContentBrowserDataSource::CanMoveItem, &UContentBrowserDataSource::BulkMoveItems);
2015-02-09 11:42:01 -05:00
}
2020-06-23 18:40:00 -04:00
void HandleDragDropCopy ( const FContentBrowserItem & InDropTargetItem , const TArray < FContentBrowserItem > & InDraggedItems , const TSharedPtr < SWidget > & InParentWidget )
{
return HandleDragDropMoveOrCopy ( InDropTargetItem , InDraggedItems , InParentWidget , LOCTEXT ( " ItemsDroppedCopy " , " {0} {0}|plural(one=item,other=items) copied to ' { 1 } ' " ), &UContentBrowserDataSource::CanCopyItem, &UContentBrowserDataSource::BulkCopyItems);
}
bool HandleDragDropOnItem ( const FContentBrowserItem & InItem , const FDragDropEvent & InDragDropEvent , const TSharedRef < SWidget > & InParentWidget )
{
// Custom handling via a data source?
if ( HandleDragEventOverride ( InItem , InDragDropEvent , & UContentBrowserDataSource : : HandleDragDropOnItem ) )
{
return true ;
}
if ( ! InItem . IsFolder ( ) )
{
return false ;
}
// Generic handling
if ( TSharedPtr < FContentBrowserDataDragDropOp > ContentDragDropOp = InDragDropEvent . GetOperationAs < FContentBrowserDataDragDropOp > ( ) )
{
static const FName MenuName = " ContentBrowser.DragDropContextMenu " ;
UToolMenus * ToolMenus = UToolMenus : : Get ( ) ;
if ( ! ToolMenus - > IsMenuRegistered ( MenuName ) )
{
UToolMenu * Menu = ToolMenus - > RegisterMenu ( MenuName ) ;
FToolMenuSection & Section = Menu - > AddSection ( " MoveCopy " , LOCTEXT ( " MoveCopyMenuHeading_Generic " , " Move/Copy... " ) ) ;
Section . AddDynamicEntry ( " DragDropMoveCopy_Dynamic " , FNewToolMenuSectionDelegate : : CreateLambda ( [ ] ( FToolMenuSection & InSection )
{
const UContentBrowserDataMenuContext_DragDropMenu * ContextObject = InSection . FindContext < UContentBrowserDataMenuContext_DragDropMenu > ( ) ;
checkf ( ContextObject , TEXT ( " Required context UContentBrowserDataMenuContext_DragDropMenu was missing! " ) ) ;
if ( ContextObject - > bCanMove )
{
InSection . AddMenuEntry (
" DragDropMove " ,
LOCTEXT ( " DragDropMove " , " Move Here " ) ,
LOCTEXT ( " DragDropMoveTooltip " , " Move the dragged items to this folder, preserving the structure of any copied folders. " ) ,
FSlateIcon ( ) ,
FUIAction ( FExecuteAction : : CreateLambda ( [ DropTargetItem = ContextObject - > DropTargetItem , DraggedItems = ContextObject - > DraggedItems , ParentWidget = ContextObject - > ParentWidget ] ( ) { HandleDragDropMove ( DropTargetItem , DraggedItems , ParentWidget . Pin ( ) ) ; } ) )
) ;
}
if ( ContextObject - > bCanCopy )
{
InSection . AddMenuEntry (
" DragDropCopy " ,
LOCTEXT ( " DragDropCopy " , " Copy Here " ) ,
LOCTEXT ( " DragDropCopyTooltip " , " Copy the dragged items to this folder, preserving the structure of any copied folders. " ) ,
FSlateIcon ( ) ,
FUIAction ( FExecuteAction : : CreateLambda ( [ DropTargetItem = ContextObject - > DropTargetItem , DraggedItems = ContextObject - > DraggedItems , ParentWidget = ContextObject - > ParentWidget ] ( ) { HandleDragDropCopy ( DropTargetItem , DraggedItems , ParentWidget . Pin ( ) ) ; } ) )
) ;
}
} ) ) ;
}
if ( UToolMenu * Menu = ToolMenus - > ExtendMenu ( MenuName ) )
{
// Update the section display name for the current drop target
Menu - > AddSection ( " MoveCopy " , FText : : Format ( LOCTEXT ( " MoveCopyMenuHeading_Fmt " , " Move/Copy to {0} " ) , InItem . GetDisplayName ( ) ) ) ;
}
UContentBrowserDataMenuContext_DragDropMenu * ContextObject = NewObject < UContentBrowserDataMenuContext_DragDropMenu > ( ) ;
ContextObject - > DropTargetItem = InItem ;
ContextObject - > DraggedItems = ContentDragDropOp - > GetDraggedItems ( ) ;
ContextObject - > bCanMove = false ;
ContextObject - > bCanCopy = false ;
for ( const FContentBrowserItem & DraggedItem : ContextObject - > DraggedItems )
{
ContextObject - > bCanMove | = DraggedItem . CanMove ( ContextObject - > DropTargetItem . GetVirtualPath ( ) ) ;
ContextObject - > bCanCopy | = DraggedItem . CanCopy ( ContextObject - > DropTargetItem . GetVirtualPath ( ) ) ;
if ( ContextObject - > bCanMove & & ContextObject - > bCanCopy )
{
break ;
}
}
ContextObject - > ParentWidget = InParentWidget ;
FToolMenuContext MenuContext ( ContextObject ) ;
TSharedRef < SWidget > MenuWidget = ToolMenus - > GenerateWidget ( MenuName , MenuContext ) ;
FSlateApplication : : Get ( ) . PushMenu (
InParentWidget ,
FWidgetPath ( ) ,
MenuWidget ,
FSlateApplication : : Get ( ) . GetCursorPos ( ) ,
FPopupTransitionEffect ( FPopupTransitionEffect : : ContextMenu )
) ;
return true ;
}
return false ;
}
} // namespace DragDropHandler
2015-02-09 11:42:01 -05:00
# undef LOCTEXT_NAMESPACE