// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Styling/SlateColor.h" #include "Layout/Visibility.h" #include "Layout/SlateRect.h" #include "Input/Reply.h" #include "Layout/Margin.h" #include "Widgets/SCompoundWidget.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Fonts/SlateFontInfo.h" #include "Widgets/SToolTip.h" #include "Misc/NotifyHook.h" #include "Animation/AnimationAsset.h" #include "PersonaDelegates.h" #include "StatusBarSubsystem.h" class FPaintArgs; class FSlateWindowElementList; class SBox; class UAnimSequence; class UBlendSpace; DECLARE_DELEGATE_ThreeParams(FOnSampleMoved, const int32 /*SampleIndex*/, const FVector& /*SampleValue*/, bool /*bIsInteractive*/); DECLARE_DELEGATE_TwoParams(FOnSampleAnimationChanged, UAnimSequence*, const FVector&); class SBlendSpaceGridWidget : public SCompoundWidget, public FNotifyHook { public: friend class FBlendSampleDetails; public: SLATE_BEGIN_ARGS(SBlendSpaceGridWidget) : _BlendSpaceBase(nullptr) , _NotifyHook(nullptr) , _ReadOnly(false) , _ShowAxisLabels(true) , _ShowSettingsButtons(true) , _StatusBarName(TEXT("AssetEditor.AnimationEditor.MainMenu")) {} SLATE_ATTRIBUTE(const UBlendSpace*, BlendSpaceBase) SLATE_ATTRIBUTE(FVector, Position) SLATE_ATTRIBUTE(FVector, FilteredPosition) SLATE_ARGUMENT(FNotifyHook*, NotifyHook) SLATE_ARGUMENT(bool, ReadOnly) SLATE_ARGUMENT(bool, ShowAxisLabels) SLATE_ARGUMENT(bool, ShowSettingsButtons) SLATE_EVENT(FOnExtendBlendSpaceSampleTooltip, OnExtendSampleTooltip) SLATE_EVENT(FOnSampleMoved, OnSampleMoved) SLATE_EVENT(FOnBlendSpaceSampleRemoved, OnSampleRemoved) SLATE_EVENT(FOnBlendSpaceSampleAdded, OnSampleAdded) SLATE_EVENT(FOnBlendSpaceSampleDuplicated, OnSampleDuplicated) SLATE_EVENT(FOnBlendSpaceSampleReplaced, OnSampleReplaced) SLATE_EVENT(FOnBlendSpaceNavigateUp, OnNavigateUp) SLATE_EVENT(FOnBlendSpaceNavigateDown, OnNavigateDown) SLATE_EVENT(FOnBlendSpaceCanvasDoubleClicked, OnCanvasDoubleClicked) SLATE_EVENT(FOnBlendSpaceSampleDoubleClicked, OnSampleDoubleClicked) SLATE_EVENT(FOnGetBlendSpaceSampleName, OnGetBlendSpaceSampleName) SLATE_ARGUMENT(FName, StatusBarName) SLATE_END_ARGS() protected: enum class EGridType { SingleAxis, TwoAxis }; /** Represents the different states of a drag operation */ enum class EDragState { /** The user has clicked a mouse button, but hasn't moved more then the drag threshold. */ PreDrag, /** The user is dragging the selected sample. */ DragSample, /** The user is setting the preview value */ Preview, /** The user is dropping a new sample onto the grid */ DragDrop, /** The user is dropping a new animation to an existing sample on the grid */ DragDropOverride, /** The user is dropping an invalid animation sequence onto the grid */ InvalidDragDrop, /** There is no active drag operation. */ None }; public: ~SBlendSpaceGridWidget(); /** * Construct this widget * * @param InArgs The declaration data for this widget */ void Construct(const FArguments& InArgs); // SWidget Interface 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 OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; virtual FReply OnDragOver(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; virtual void OnDragLeave(const FDragDropEvent& DragDropEvent) override; virtual FReply OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override; virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnMouseButtonDoubleClick(const FGeometry& InMyGeometry, const FPointerEvent& InMouseEvent) override; virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual FReply OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; virtual FReply OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) override; virtual void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; virtual void OnMouseLeave(const FPointerEvent& MouseEvent) override; virtual void OnFocusLost(const FFocusEvent& InFocusEvent) override; virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override; virtual bool SupportsKeyboardFocus() const override; // End of SWidget /** Returns the sample value for previewing the blend space */ const FVector GetPreviewPosition() const; /** Records the state of the blend space being used for previewing so it can be displayed */ void SetPreviewingState(const FVector& InPosition, const FVector& InFilteredPosition); /** Flag whether or not the user is actively previewing the blend space (moving the sample value) */ const bool IsPreviewing() const; int32 GetSelectedSampleIndex() const { return SelectedSampleIndex; } void InvalidateCachedData(); void InvalidateState(); protected: /** Drawing functionality for grid, legend, key and triangulation **/ void PaintBackgroundAndGrid(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32& DrawLayerId) const; void PaintSampleKeys(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32& DrawLayerId) const; void PaintAxisText(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32& DrawLayerId) const; void PaintTriangulation(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32& DrawLayerId) const; void PaintAnimationNames(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32& DrawLayerId) const; void PaintGridSampleWeights(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32& DrawLayerId) const; /** Validation for drag and drop operation, will populate InvalidOperationText and return false in case it is invalid */ const bool IsValidDragDropOperation(const FDragDropEvent& DragDropEvent, FText& InvalidOperationText); /** Validation functionality for the given animation sequence, will populate InvalidOperationText and return false in case it is invalid */ bool ValidateAnimationSequence(const UAnimSequence* AnimationSequence, FText& InvalidOperationText) const; /** Preview toggling*/ void StartPreviewing(); void StopPreviewing(); /** Common view options for the context menus */ void MakeViewContextMenuEntries(FMenuBuilder& InMenuBuilder); /** Blend sample context menu creation */ TSharedPtr CreateBlendSampleContextMenu(); /** Create a new blend sample via the context menu */ TSharedPtr CreateNewBlendSampleContextMenu(const FVector2D& InMousePosition); /** Construct the grid widget to change the grid position for the selected sample */ TSharedPtr CreateGridEntryBox(const int32 BoxIndex, const bool bShowLabel); /** Handles a mouse click operation on mouse up */ FReply ProcessClick(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent); /** Toggle for drawing the triangulation (only available for 2D blendspace) */ FReply ToggleTriangulationVisibility(); /** Returns whether or not the triangulation button should be visible, see above*/ EVisibility GetTriangulationButtonVisibility() const; /** Returns whether or not the animation names button should be visible */ EVisibility GetAnimationNamesButtonVisibility() const; /** Toggle for how the grid area is determined (only available for 2D blendspace) */ FReply ToggleFittingType(); /** Returns whether or not the fitting button should be visible */ EVisibility GetFittingButtonVisibility() const; /** Calculates a margin offset according to whether or not we should take into account the largest axis when creating the grid area*/ void UpdateGridRatioMargin(const FVector2D& GeometrySize); FText GetFittingTypeButtonToolTipText() const; /** Toggles the animation labels being shown */ FReply ToggleShowAnimationNames(); /** Calculates the screen space grid points */ void CalculateGridPoints(); /** Snaps the given screen position to the closest grid point */ const FVector2D SnapScreenPositionToGrid(const FVector2D& InPosition, bool ForceSnap) const; /** Snaps the given screen position to the sample value on the grid */ const FVector ScreenPositionToSampleValueWithSnapping(const FVector2D& InPosition, bool bForceSnap) const; /** Returns the index of the closest grid point to the given mouse position */ int32 FindClosestGridPointIndexFromScreenPosition(const FVector2D& InPosition) const; /** Converts the given sample value to a screen space position */ const FVector2D SampleValueToScreenPosition(const FVector& SampleValue) const; /** Converts the given sample value to a normalized position (between 0 and 1) */ const FVector2D SampleValueToNormalizedPosition(const FVector& SampleValue) const; /** Converts a screen space (grid) position to a sample value, with optional clamping */ const FVector ScreenPositionToSampleValue(const FVector2D& GridPosition, bool bClamp) const; /** Returns the (calculated) grid rectangle given the supplied geometry, and calls UpdateGridRatioMargin */ const FSlateRect GetGridRectangleFromGeometry(const FGeometry& MyGeometry); /** Checks whether or not the blendspace sample value is within the range of the mouse position */ bool IsSampleValueWithinMouseRange(const FVector& SampleValue, float& OutDistance) const; /** Get the sample index that most closely corresponds to the mouse position */ int32 GetClosestSamplePointIndexToMouse() const; /** Sets the tooltip instance on the underlying widget instance */ void ShowToolTip(); /** Resets the tooltip instance on the underlying widget instance to nullptr*/ void ResetToolTip(); /** Tool tip text content getters*/ FText GetToolTipAnimationName() const; FText GetToolTipSampleValue() const; FText GetToolTipSampleValidity() const; FText GetSampleErrorMessage(const struct FBlendSample& BlendSample) const; /** Functionality for the grid input box widgets (also used by FBlendSampleDetails) */ EVisibility GetInputBoxVisibility(const int32 ParameterIndex) const; TOptional GetInputBoxValue(const int32 ParameterIndex) const; TOptional GetInputBoxMinValue(const int32 ParameterIndex) const; TOptional GetInputBoxMaxValue(const int32 ParameterIndex) const; float GetInputBoxDelta(const int32 ParameterIndex) const; void OnInputBoxValueCommited(const float NewValue, ETextCommit::Type CommitType, const int32 ParameterIndex); void OnInputBoxValueChanged(const float NewValue, const int32 ParameterIndex, bool bIsInteractive); void OnInputSliderBegin(const int32 ParameterIndex); void OnInputSliderEnd(const float NewValue, const int32 ParameterIndex); /** Binds commands to functions in this class */ void BindCommands(); /** Handlers for blend sample actions*/ void OnBlendSampleCut(); void OnBlendSampleCopy(); void OnBlendSamplePaste(); void OnBlendSampleDelete(); /** Returns whether or not the action is possible */ bool CanBlendSampleCutCopy(); bool CanBlendSamplePaste(); bool CanBlendSampleDelete(); /** Returns whether or not the sample tool tips should be visible */ EVisibility GetSampleToolTipVisibility() const; EVisibility GetPreviewToolTipVisibility() const; /** Updates the cached blend parameter data */ void UpdateCachedBlendParameterData(); /** Gets the name to display for a sample */ FText GetSampleName(const FBlendSample& InBlendSample, int32 InSampleIndex) const; /** Enable/disable the status bar message */ void EnableStatusBarMessage(bool bEnable); /** * This returns the weight for this sample if a lookup was made on its position. Ideally this would be 1 * (and will be for triangulation). It will not always be so when using the grid. */ float GetSampleLookupWeight(int32 SampleIndex) const; private: /** Currently visualized blendspace (const to ensure changes to it are only made within SAnimationBlendSpace */ TAttribute BlendSpaceBase; /** Previous blendspace, cached here to allow us to update cached data when it changes. */ TWeakObjectPtr PreviousBlendSpaceBase; /** Current BlendSpace params (driven from outside the widget) */ TAttribute TargetPosition; /** Current BlendSpace state - filtered version of Position (driven from outside the widget) */ TAttribute FilteredPosition; /** Notify hook (ptr to SAnimationBlendSpace instance), which is required for transacting FBlendSample object when edited using the context-menu/structure details panel */ FNotifyHook* NotifyHook; /** Number of Blend Parameters to draw */ uint32 BlendParametersToDraw; /** Grid type (either 1D or 2D) */ EGridType GridType; /** Cached mouse interaction data */ FVector2D LastMousePosition; FVector2D LocalMousePosition; FVector2D MouseDownPosition; bool bMouseIsOverGeometry; /** Selection and highlight sample index/state */ int32 SelectedSampleIndex; int32 HighlightedSampleIndex; int32 ToolTipSampleIndex; bool bHighlightPreviewPin; /** Drag state and data (not drag/drop) */ EDragState DragState; int32 DraggedSampleIndex; FVector LastDragPosition; /** Currently set preview blend sample value and state data */ bool bSamplePreviewing; FVector2D LastPreviewingMousePosition; FVector PreviewPosition; bool bAdvancedPreview; TArray PreviewedSamples; /** Previous position in the triangulation/segmentation */ int32 CachedTriangulationIndex = -1; /** Preview state that may have been returned */ FVector PreviewFilteredPosition; /** Tooltip ptr which is shown when hovering/dropping/dragging a sample*/ TSharedPtr ToolTip; /** Container box used to extend a tooltip */ TSharedPtr ToolTipExtensionContainer; /** Drag and drop data */ FText InvalidDragDropText; FText InvalidSamplePositionDragDropText; FText DragDropAnimationName; FText HoveredAnimationName; UAnimSequence* DragDropAnimationSequence; /** Cached values for the grid input boxes */ float CachedInputBoxValues[2]; bool bSliderMovement[2] = {false, false}; // Cached grid data (derived from the blend space) FSlateRect CachedGridRectangle; FVector2D SampleValueMin; FVector2D SampleValueMax; FVector2D SampleValueRange; FVector2D SampleGridDelta; bool bSampleSnapToGrid[2]; FIntPoint SampleGridDivisions; FText ParameterXName; FText ParameterYName; TArray CachedGridPoints; TArray CachedSamplePoints; /** Commands that are bound to delegates */ TSharedPtr UICommandList; /** Whether or not the cached data should be refreshed on the next tick*/ bool bRefreshCachedData; /** Cached draw-able axis information */ FVector2D XAxisTextSize; float MaxVerticalAxisTextWidth; float MaxHorizontalAxisTextHeight; float HorizontalAxisMaxTextWidth; FVector2D YAxisTextSize; /** Delegates populated from SAnimationBlendSpace and used as callbacks */ FOnBlendSpaceSampleAdded OnSampleAdded; FOnBlendSpaceSampleDuplicated OnSampleDuplicated; FOnSampleMoved OnSampleMoved; FOnBlendSpaceSampleRemoved OnSampleRemoved; FOnBlendSpaceSampleReplaced OnSampleReplaced; FOnBlendSpaceNavigateUp OnNavigateUp; FOnBlendSpaceNavigateDown OnNavigateDown; FOnBlendSpaceCanvasDoubleClicked OnCanvasDoubleClicked; FOnBlendSpaceSampleDoubleClicked OnSampleDoubleClicked; FOnGetBlendSpaceSampleName OnGetBlendSpaceSampleName; FOnExtendBlendSpaceSampleTooltip OnExtendSampleTooltip; /** Threshold values for hovering, click and dragging samples */ float DragThreshold; float ClickAndHighlightThreshold; /** Sample drawing data */ FVector2D KeySize; FVector2D PreviewSize; const FSlateBrush* KeyBrush; const FSlateBrush* PreviewBrush; const FSlateBrush* BackgroundImage; const FSlateBrush* LabelBrush; enum class EArrowDirection : uint8 { Left, Right, Up, Down, Max }; const FSlateBrush* ArrowBrushes[(int32)EArrowDirection::Max]; /** Individual sample state colours */ FSlateColor HighlightKeyColor; FSlateColor KeyColor; FSlateColor SelectKeyColor; FSlateColor PreDragKeyColor; FSlateColor DragKeyColor; FSlateColor InvalidColor; FSlateColor DropKeyColor; FSlateColor PreviewKeyColor; /** Grid drawing and layout data */ FMargin GridMargin; FLinearColor GridLinesColor; FLinearColor GridOutlineColor; FSlateColor TriangulationColor; FSlateColor TriangulationCurrentColor; FSlateFontInfo FontInfo; float TextMargin; bool bShowTriangulation; bool bShowAnimationNames; bool bStretchToFit; FMargin GridRatioMargin; bool bReadOnly; bool bShowAxisLabels; bool bShowSettingsButtons; FStatusBarMessageHandle StatusBarMessageHandle; FName StatusBarName; };