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

392 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "UObject/UObjectGlobals.h"
#include "Misc/Attribute.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Input/Reply.h"
#include "Widgets/SWidget.h"
#include "Styling/AppStyle.h"
#include "Framework/MarqueeRect.h"
#include "SAnimTrackPanel.h"
#include "SAnimEditorBase.h"
#include "AssetRegistry/AssetData.h"
#include "Framework/Commands/Commands.h"
#include "SAnimTimingPanel.h"
#include "EditorUndoClient.h"
#include "AnimTimeline/AnimModel.h"
#include "Containers/ArrayView.h"
class FSlateWindowElementList;
class SAnimNotifyNode;
class SAnimNotifyTrack;
class SBorder;
class SScrollBar;
struct Rect;
DECLARE_DELEGATE_OneParam( FOnSelectionChanged, const TArray<UObject*>& )
DECLARE_DELEGATE( FOnTrackSelectionChanged )
DECLARE_DELEGATE( FOnUpdatePanel )
DECLARE_DELEGATE_RetVal( float, FOnGetScrubValue )
DECLARE_DELEGATE( FRefreshOffsetsRequest )
DECLARE_DELEGATE( FDeleteNotify )
DECLARE_DELEGATE_RetVal( bool, FOnGetIsAnimNotifySelectionValidForReplacement )
DECLARE_DELEGATE_TwoParams( FReplaceWithNotify, FString, UClass* )
DECLARE_DELEGATE_TwoParams( FReplaceWithBlueprintNotify, FString, FString )
DECLARE_DELEGATE_OneParam( FReplaceWithSyncMarker, FString )
DECLARE_DELEGATE( FDeselectAllNotifies )
DECLARE_DELEGATE_OneParam( FOnGetBlueprintNotifyData, TArray<FAssetData>& )
DECLARE_DELEGATE_OneParam( FOnGetNativeNotifyClasses, TArray<UClass*>&)
DECLARE_DELEGATE_RetVal_FourParams(bool, FOnSnapPosition, float& /*InOutTimeToSnap*/, float /*InSnapMargin*/, TArrayView<const FName> /*InSkippedSnapTypes*/, FName& /*OutSnapType*/)
class SAnimNotifyNode;
class SAnimNotifyTrack;
class SNotifyEdTrack;
class FNotifyDragDropOp;
namespace ENotifyPasteMode
{
enum Type
{
MousePosition,
OriginalTime
};
}
namespace ENotifyPasteMultipleMode
{
enum Type
{
Relative,
Absolute
};
}
namespace ENotifyStateHandleHit
{
enum Type
{
Start,
End,
None
};
}
struct FNotifyMarqueeOperation
{
FNotifyMarqueeOperation()
: Operation(Add)
, bActive(false)
{
}
enum Type
{
/** Holding down Ctrl removes nodes */
Remove,
/** Holding down Shift adds to the selection */
Add,
/** When nothing is pressed, marquee replaces selection */
Replace
} Operation;
bool IsValid() const
{
return Rect.IsValid() && bActive;
}
void Start(const FVector2D& InStartLocation, FNotifyMarqueeOperation::Type InOperationType, TArray<TSharedPtr<SAnimNotifyNode>>& InOriginalSelection)
{
Rect = FMarqueeRect(InStartLocation);
Operation = InOperationType;
OriginalSelection = InOriginalSelection;
}
void End()
{
Rect = FMarqueeRect();
}
/** Given a mouse event, figure out what the marquee selection should do based on the state of Shift and Ctrl keys */
static FNotifyMarqueeOperation::Type OperationTypeFromMouseEvent(const FPointerEvent& MouseEvent)
{
if(MouseEvent.IsControlDown())
{
return FNotifyMarqueeOperation::Remove;
}
else if(MouseEvent.IsShiftDown())
{
return FNotifyMarqueeOperation::Add;
}
else
{
return FNotifyMarqueeOperation::Replace;
}
}
public:
/** The marquee rectangle being dragged by the user */
FMarqueeRect Rect;
/** Whether the marquee has been activated, usually by a drag */
bool bActive;
/** The original selection state before the marquee selection */
TArray<TSharedPtr<SAnimNotifyNode>> OriginalSelection;
};
DECLARE_DELEGATE_FourParams(FOnNotifyStateHandleBeingDragged, TSharedPtr<SAnimNotifyNode> /*NotifyNode*/, const FPointerEvent& /*PointerEvent*/, ENotifyStateHandleHit::Type /*Handle*/, float /*Time*/)
DECLARE_DELEGATE_FourParams(FOnNotifyNodesBeingDragged, const TArray<TSharedPtr<SAnimNotifyNode>>& /*NotifyNodes*/, const class FDragDropEvent& /*DragDropEvent*/, float /*XPosition*/, float /*Time*/)
//////////////////////////////////////////////////////////////////////////
// SAnimNotifyPanel
class FAnimNotifyPanelCommands : public TCommands<FAnimNotifyPanelCommands>
{
public:
FAnimNotifyPanelCommands()
: TCommands<FAnimNotifyPanelCommands>("AnimNotifyPanel", NSLOCTEXT("Contexts", "AnimNotifyPanel", "Anim Notify Panel"), NAME_None, FAppStyle::GetAppStyleSetName())
{
}
TSharedPtr<FUICommandInfo> DeleteNotify;
TSharedPtr<FUICommandInfo> CopyNotifies;
TSharedPtr<FUICommandInfo> CutNotifies;
TSharedPtr<FUICommandInfo> PasteNotifies;
virtual void RegisterCommands() override;
};
// @todo anim : register when it's opened for the animsequence
// broadcast when animsequence changed, so that we refresh for multiple window
class SAnimNotifyPanel: public SAnimTrackPanel, public FEditorUndoClient
{
public:
SLATE_BEGIN_ARGS( SAnimNotifyPanel )
: _Sequence()
, _CurrentPosition()
, _ViewInputMin()
, _ViewInputMax()
, _InputMin()
, _InputMax()
, _OnSetInputViewRange()
, _OnSelectionChanged()
, _OnGetScrubValue()
, _OnRequestRefreshOffsets()
{}
SLATE_ARGUMENT( class UAnimSequenceBase*, Sequence)
SLATE_ARGUMENT( float, WidgetWidth )
SLATE_ATTRIBUTE( float, CurrentPosition )
SLATE_ATTRIBUTE( float, ViewInputMin )
SLATE_ATTRIBUTE( float, ViewInputMax )
SLATE_ATTRIBUTE( float, InputMin )
SLATE_ATTRIBUTE( float, InputMax )
SLATE_EVENT( FOnSetInputViewRange, OnSetInputViewRange )
SLATE_EVENT( FOnSelectionChanged, OnSelectionChanged )
SLATE_EVENT( FOnGetScrubValue, OnGetScrubValue )
SLATE_EVENT( FRefreshOffsetsRequest, OnRequestRefreshOffsets )
SLATE_EVENT( FOnGetTimingNodeVisibility, OnGetTimingNodeVisibility )
SLATE_EVENT( FOnInvokeTab, OnInvokeTab )
SLATE_EVENT( FSimpleDelegate, OnNotifiesChanged )
SLATE_EVENT( FOnSnapPosition, OnSnapPosition )
SLATE_EVENT( FOnNotifyStateHandleBeingDragged, OnNotifyStateHandleBeingDragged)
SLATE_EVENT( FOnNotifyNodesBeingDragged, OnNotifyNodesBeingDragged)
SLATE_END_ARGS()
void Construct(const FArguments& InArgs, const TSharedRef<FAnimModel>& InModel);
virtual ~SAnimNotifyPanel();
void SetSequence(class UAnimSequenceBase * InSequence);
// Generate a new track name (smallest integer number that isn't currently used)
FName GetNewTrackName() const;
FReply AddTrack();
FReply InsertTrack(int32 TrackIndexToInsert);
FReply DeleteTrack(int32 TrackIndexToDelete);
bool CanDeleteTrack(int32 TrackIndexToDelete);
// Handler function for renaming a notify track
void OnCommitTrackName(const FText& InText, ETextCommit::Type CommitInfo, int32 TrackIndexToName);
// Request a deferred Update call
void RequestUpdate();
void Update();
/** Returns the position of the notify node currently being dragged. Returns -1 if no node is being dragged */
float CalculateDraggedNodePos() const;
/**Handler for when a notify node drag has been initiated */
FReply OnNotifyNodeDragStarted(TArray<TSharedPtr<SAnimNotifyNode>> NotifyNodes, TSharedRef<SWidget> Decorator, const FVector2D& ScreenCursorPos, const FVector2D& ScreenNodePosition, const bool bDragOnMarker);
virtual float GetSequenceLength() const override;
void CutSelectedNodesToClipboard();
void CopySelectedNodesToClipboard() const;
void OnPasteNodes(SAnimNotifyTrack* RequestTrack, float ClickTime, ENotifyPasteMode::Type PasteMode, ENotifyPasteMultipleMode::Type MultiplePasteType);
/** SWidget Interface */
virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override;
virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual FReply OnDragDetected(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override;
virtual void OnFocusLost(const FFocusEvent& InFocusEvent) override;
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override;
/** End SWidget Interface */
void RefreshMarqueeSelectedNodes(const FGeometry& PanelGeo);
void OnNotifyObjectChanged(UObject* EditorBaseObj, bool bRebuild);
/** Check to make sure the current AnimNotify selection is a valid selection for replacing (i.e., AnimNotifies and AnimNotifyStates aren't mixed together in the selection) */
bool IsNotifySelectionValidForReplacement();
/** Handler for replacing with notify */
void OnReplaceSelectedWithNotify(FString NewNotifyName, UClass* NewNotifyClass);
/** Handler for replacing with notify blueprint */
void OnReplaceSelectedWithNotifyBlueprint(FString NewBlueprintNotifyName, FString NewBlueprintNotifyClass);
/** Handler for replacing with sync marker */
void OnReplaceSelectedWithSyncMarker(FString NewBlueprintNotifyName);
void HandleObjectsSelected(const TArray<UObject*>& InObjects);
TSharedRef<FUICommandList> GetCommandList() const { return CommandList.ToSharedRef(); }
private:
friend struct FScopedSavedNotifySelection;
TWeakPtr<FAnimModel> WeakModel;
TSharedPtr<SBorder> PanelArea;
TSharedPtr<SScrollBar> NotifyTrackScrollBar;
class UAnimSequenceBase* Sequence;
float WidgetWidth;
TAttribute<float> CurrentPosition;
FOnSelectionChanged OnSelectionChanged;
FOnGetScrubValue OnGetScrubValue;
FOnGetTimingNodeVisibility OnGetTimingNodeVisibility;
/** Manager for mouse controlled marquee selection */
FNotifyMarqueeOperation Marquee;
/** Delegate to request a refresh of the offsets calculated for notifies */
FRefreshOffsetsRequest OnRequestRefreshOffsets;
/** Store the position of a currently dragged node for display across tracks */
float CurrentDragXPosition;
/** Cached list of anim tracks for notify node drag drop */
TArray<TSharedPtr<SAnimNotifyTrack>> NotifyAnimTracks;
/** Cached list of Notify editor tracks */
TArray<TSharedPtr<SNotifyEdTrack>> NotifyEditorTracks;
// Request a deferred RefreshNotifyTracks call
void RequestRefresh();
// this just refresh notify tracks - UI purpose only
// do not call this from here. This gets called by asset.
void RefreshNotifyTracks();
/** FEditorUndoClient interface */
virtual void PostUndo( bool bSuccess ) override;
virtual void PostRedo( bool bSuccess ) override;
/** Handler for delete command */
void OnDeletePressed();
/** Deletes all currently selected notifies in the panel */
void DeleteSelectedNodeObjects();
/** We support keyboard focus to detect when we should process key commands like delete */
virtual bool SupportsKeyboardFocus() const override
{
return true;
}
// Called when a track changes it's selection; iterates all tracks collecting selected items
void OnTrackSelectionChanged();
// Called to deselect all notifies across all tracks
void DeselectAllNotifies();
// Binds the UI commands for this widget to delegates
void BindCommands();
/** Populates the given class array with all classes deriving from those originally present
* @param InAssetsToSearch Assets to search to detect child classes
* @param InOutAllowedClassNames Classes to allow, this will be expanded to cover all derived classes of those originally present
*/
void PopulateNotifyBlueprintClasses(TArray<FString>& InOutAllowedClasses);
/** Find blueprints matching allowed classes and all derived blueprints
* @param OutNotifyData Asset data matching allowed classes and their children
* @param InOutAllowedClassNames Classes to allow, this will be expanded to cover all derived classes of those originally present
*/
void OnGetNotifyBlueprintData(TArray<FAssetData>& OutNotifyData, TArray<FString>* InOutAllowedClassNames);
/** Find classes that inherit from NotifyOutermost and add correctly formatted class name to OutAllowedBlueprintClassNames
* to allow us to find blueprints inherited from those types without loading the blueprints.
* @param OutClasses Array of classes that inherit from NotifyOutermost
* @param NotifyOutermost Outermost notify class to detect children of
* @param OutAllowedBlueprintClassNames list of class names to add the native class names to
*/
void OnGetNativeNotifyData(TArray<UClass*>& OutClasses, UClass* NotifyOutermost, TArray<FString>* OutAllowedBlueprintClassNames);
void OnNotifyTrackScrolled(float InScrollOffsetFraction);
virtual void InputViewRangeChanged(float ViewMin, float ViewMax) override;
/** Delegate used to snap when dragging */
FOnSnapPosition OnSnapPosition;
/** UI commands for this widget */
TSharedPtr<FUICommandList> CommandList;
/** Classes that are known to be derived from blueprint notifies */
TArray<FString> NotifyClassNames;
/** Classes that are known to be derived from blueprint state notifies */
TArray<FString> NotifyStateClassNames;
/** Handle to the registered OnPropertyChangedHandle delegate */
FDelegateHandle OnPropertyChangedHandleDelegateHandle;
/** Delegate used to invoke a tab */
FOnInvokeTab OnInvokeTab;
/** Delegate used to inform others that notifies have changed (for timing) */
FSimpleDelegate OnNotifiesChanged;
/** Delegate used to inform others that a notify state handle is being dragged */
FOnNotifyStateHandleBeingDragged OnNotifyStateHandleBeingDragged;
/** Delegate used to inform others that one or more notifies nodes are being dragged */
FOnNotifyNodesBeingDragged OnNotifyNodesBeingDragged;
/** Recursion guard for selection */
bool bIsSelecting;
/** Recursion guard for updating */
bool bIsUpdating;
/** Flags to handle deferred updates */
bool bUpdateRequested;
bool bRefreshRequested;
};