Files
UnrealEngine/Engine/Source/Editor/CurveEditor/Public/SCurveEditorView.h
2025-05-18 13:04:45 +08:00

506 lines
19 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Containers/Array.h"
#include "Containers/ContainerAllocationPolicies.h"
#include "Containers/SortedMap.h"
#include "CurveEditorScreenSpace.h"
#include "CurveEditorTypes.h"
#include "HAL/Platform.h"
#include "Math/NumericLimits.h"
#include "Math/TransformCalculus2D.h"
#include "Math/Vector2D.h"
#include "Misc/Attribute.h"
#include "Misc/Optional.h"
#include "Templates/Less.h"
#include "Templates/SharedPointer.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/SCompoundWidget.h"
#include "CurveEditorSettings.h"
#include "CurveDrawInfo.h"
class FCurveEditor;
class FSlateRect;
class FText;
class SCurveEditorPanel;
class FCurveModel;
class FCurveEditorAxis;
class SRetainerWidget;
class SCurveEditorView;
enum class ECurveEditorAxisOrientation : uint8;
namespace UE::CurveEditor { class FCurveDrawParamsCache; }
/**
* Identifier for a specific axis on a view. These identifiers are transient and should not be stored externally.
*/
struct FCurveEditorViewAxisID
{
FCurveEditorViewAxisID()
: Index(std::numeric_limits<uint8>::max())
{}
explicit FCurveEditorViewAxisID(uint8 InIndex)
: Index(InIndex)
{
check(InIndex < std::numeric_limits<uint8>::max());
}
explicit operator bool() const
{
return Index != std::numeric_limits<uint8>::max();
}
friend uint32 GetTypeHash(FCurveEditorViewAxisID In)
{
return GetTypeHash(In.Index);
}
friend bool operator==(FCurveEditorViewAxisID A, FCurveEditorViewAxisID B)
{
return A.Index == B.Index;
}
friend bool operator!=(FCurveEditorViewAxisID A, FCurveEditorViewAxisID B)
{
return A.Index != B.Index;
}
friend bool operator<(FCurveEditorViewAxisID A, FCurveEditorViewAxisID B)
{
return A.Index < B.Index;
}
private:
operator int32() const
{
check(Index != std::numeric_limits<uint8>::max());
return Index;
}
FCurveEditorViewAxisID& operator=(int32 InIndex)
{
check(InIndex >= 0 && InIndex < 255);
Index = static_cast<uint8>(InIndex);
return *this;
}
friend SCurveEditorView;
uint8 Index;
};
/**
* This is the base widget type for all views that exist on a curve editor panel. A view may contain 0 or more curves (stored in CurveInfoByID).
* SCurveEditorViews are directly housed within a single SCurveEditorViewContainer which arranges each view vertically in order.
*
* View types:
* A view may have a centrally registered ID (See FCurveEditorViewRegistry and FCurveModel::GetSupportedViews) which allows any external curve model type to express support for any other view type.
* 3 built-in views are provided:
* Absolute: Shows 1 or more curves on a single 2D-scrollable grid using a single zoomable view-space.
* Normalized: Shows 1 or more curves on a single grid scrollable horizontally with all curves normalized to the extents of the view
* Stacked: Shows 1 or more curves on separate fixed-height grids scrollable horizontally each curve normalized to the extents of its own grid
* Unregistered curve views may still be added to the view by calling SCurveEditorPanel::AddView
*
* Space Transformations:
* Views must define a valid 2-dimensional space (ViewSpace) that is used to convert from a pixel position to the virtual view space.
* Views may additionally implement per-curve transformations from view space to curve space, to allow for specific layouts/scales of curves within them.
* Per-curve transforms are stored inside FCurveInfo::ViewToCurveTransform
*
* Sizing:
* Views may be size in one of 2 ways:
* * Auto sized (bAutoSize=true): views derive their height from the widget's desired size;
* * Stretched (bAutoSize=false): views will be stretched to the size of the parent panel with a sensible minimum height.
*/
class CURVEEDITOR_API SCurveEditorView : public SCompoundWidget
{
public:
/**
* Default constructor
*/
SCurveEditorView();
~SCurveEditorView();
/**
* Get the default screen space ultility that defines this view's input, output and pixel metrics
*/
FCurveEditorScreenSpace GetViewSpace() const;
/**
* Get the screen space ultility that defines this view's input, output and pixel metrics for the specified axis combination
*/
FCurveEditorScreenSpace GetViewSpace(const FName& InHorizontalAxis, const FName& InVerticalAxis) const;
/**
* Get the screen space ultility that defines the specified curves's input, output and pixel metrics.
* The resulting struct defines the transformation from this view's widget pixel space to the curve input/output space
*/
FCurveEditorScreenSpace GetCurveSpace(FCurveModelID CurveID) const;
/** @return The transform used to translate view space (absolute key coordinates) to the curve space (e.g. 0-1 range in normalized view). */
FTransform2d GetViewToCurveTransform(FCurveModelID CurveID) const;
/**
* Retrieve the horizontal screen-space information for the specified axis
*/
FCurveEditorScreenSpaceH GetHorizontalAxisSpace(FCurveEditorViewAxisID ID) const;
/**
* Retrieve the vertical screen-space information for the specified axis
*/
FCurveEditorScreenSpaceV GetVerticalAxisSpace(FCurveEditorViewAxisID ID) const;
/**
* Retrueve the axis ID assigned to the specified curve and orientation
*/
FCurveEditorViewAxisID GetAxisForCurve(FCurveModelID CurveID, ECurveEditorAxisOrientation AxisOrientation) const;
/**
* Retrueve the axis associated with the specified ID and orientation
*/
TSharedPtr<FCurveEditorAxis> GetAxis(FCurveEditorViewAxisID ID, ECurveEditorAxisOrientation AxisOrientation) const;
/**
* Check whether this view should auto size to fit its FixedHeight or child slot content
*/
bool ShouldAutoSize() const
{
return bAutoSize;
}
/**
* Check whether this view can be interacted with. This should be checked by any tool or edit mode attempting to manipulated curves on this view.
*/
bool IsInteractive() const
{
return bInteractive;
}
/**
* Check whether this view has space for any more curves
*/
bool HasCapacity() const
{
return MaximumCapacity == 0 || CurveInfoByID.Num() < MaximumCapacity;
}
/**
* Retrieve the number of curves that exist on this view
*/
int32 NumCurves() const
{
return CurveInfoByID.Num();
}
/**
* Retrieve this view's input bounds, accounting for any offsets or padding from the outer container
*/
void GetInputBounds(double& OutInputMin, double& OutInputMax) const;
/**
* Retrieve this view's minimum output bound in view space
*/
double GetOutputMin() const
{
return OutputMin;
}
/**
* Retrieve this view's maximum output bound in view space
*/
double GetOutputMax() const
{
return OutputMax;
}
/**
* Set this view's output bounds
*/
void SetOutputBounds(double InOutputMin, double InOutputMax, FCurveEditorViewAxisID AxisID = FCurveEditorViewAxisID());
/**
* Set this view's input bounds
*/
void SetInputBounds(double InInputMin, double InInputMax, FCurveEditorViewAxisID AxisID = FCurveEditorViewAxisID());
/**
* Zoom this view in or out around its center point
*
* @param Amount The amount to zoom by horizontally and vertically as a factor of the current size
*/
void Zoom(const FVector2D& Amount);
/**
* Zoom this view in or out around the specified point
*
* @param Amount The amount to zoom by horizontally and vertically as a factor of the current size
* @param TimeOrigin The time origin to zoom around
* @param ValueOrigin The value origin to zoom around
*/
void ZoomAround(const FVector2D& Amount, double InputOrigin, double OutputOrigin);
/** This should be called every tick by an owning widget, to see if the cache is valid, which will then recreate it and invalidate widget*/
virtual void CheckCacheAndInvalidateIfNeeded();
UE_DEPRECATED(5.3, "Use UpdateViewToTransformCurves(double InputMin, double InputMax) instead.")
virtual void UpdateViewToTransformCurves() {}
/** Function to make sure to update the view to the transform curves, we need to do this before we cache*/
virtual void UpdateViewToTransformCurves(double InputMin, double InputMax) {};
/** Frame the view vertially by the input and output bounds, peformaing any custom clipping as needed */
virtual void FrameVertical(double InOutputMin, double InOutputMax, FCurveEditorViewAxisID AxisID = FCurveEditorViewAxisID());
/** Frame the view horizontall by the input and output bounds, peformaing any custom clipping as needed */
virtual void FrameHorizontal(double InInputMin, double InInputMax, FCurveEditorViewAxisID AxisID = FCurveEditorViewAxisID());
/** Returns the cure editor associated with this view, or nullptr if the view is not specific to a curve editor */
TSharedPtr<FCurveEditor> GetCurveEditor() const;
public:
/**
* Retrieve all the curve points that overlap the specified rectangle in widget space
*
* @param WidgetRectangle The rectangle to hit test against. May not hit points that would exist outside of the view's visible bounds.
* @param OutPoints (required) pointer to an array to populate with overlapping points
* @return Whether any points were within the widget range
*/
virtual bool GetPointsWithinWidgetRange(const FSlateRect& WidgetRectangle, TArray<FCurvePointHandle>* OutPoints) const
{ return false; }
/**
* Retrieve all the curve points, if any of the interpolating points overlap the specified rectangle in widget space
*
* @param WidgetRectangle The rectangle to hit test against. May not hit points that would exist outside of the view's visible bounds.
* @param OutPoints (required) pointer to an array to populate with overlapping points
* @return Whether any points were within the widget range
*/
virtual bool GetCurveWithinWidgetRange(const FSlateRect& WidgetRectangle, TArray<FCurvePointHandle>* OutPoints) const
{ return false; }
/**
* Tries to retrieve all curve points that overlap the rectangle.
* If no points are found, selects the overlapping curves instead.
*
* @param WidgetRectangle The rectangle to hit test against. May not hit points that would exist outside of the view's visible bounds.
* @param OutPoints (required) pointer to an array to populate with overlapping points
*
* @return Whether anything was selected.
*/
bool GetPointsThenCurveWithinWidgetRange(const FSlateRect& WidgetRectangle, TArray<FCurvePointHandle>* OutPoints) const
{
return GetPointsWithinWidgetRange(WidgetRectangle, OutPoints) || GetCurveWithinWidgetRange(WidgetRectangle, OutPoints);
}
/**
* Retrieve the id of the hovered curve
*/
virtual TOptional<FCurveModelID> GetHoveredCurve() const { return TOptional<FCurveModelID>(); }
/**
* Bind UI commands for this view
*/
virtual void BindCommands()
{}
/**
* Invoked when a curve has been added or removed from this list.
* @note: Care should be taken here to not impede performance as this will get called each time a curve is added/removed
*/
virtual void OnCurveListChanged()
{}
/**
* Should the tools respect the global snapping rules for Time (X axis) input when manipulating a curve in this view. Can be set to false to ignore the snap setting.
*/
virtual bool IsTimeSnapEnabled() const
{
return true;
}
/**
* Should the tools respect the global snapping rules for Value (Y axis) input when manipulating a curve in this view. Can be set to false to ignore the snap setting.
*/
virtual bool IsValueSnapEnabled() const
{
return true;
}
virtual void GetGridLinesX(TSharedRef<const FCurveEditor> CurveEditor, TArray<float>& MajorGridLines, TArray<float>& MinorGridLines, TArray<FText>* MajorGridLabels = nullptr) const {}
virtual void GetGridLinesY(TSharedRef<const FCurveEditor> CurveEditor, TArray<float>& MajorGridLines, TArray<float>& MinorGridLines, TArray<FText>* MajorGridLabels = nullptr) const {}
/** Request a new render from the retainer widget */
void RefreshRetainer();
/** Update the custom axes if necessary */
void UpdateCustomAxes();
protected:
/** Gets info about the curves being drawn. Converts actual curves into an abstract series of lines/points/handles/etc. */
UE_DEPRECATED(5.6, "Instead use the CurveDrawParamsCache, use FCurveDrawParamsCache::GetCurveDrawParamsSynchronous to get Curve Draw Parms as with this function.")
void GetCurveDrawParams(TArray<FCurveDrawParams>& OutDrawParams);
/** Gets curve draw params get the cache by calling a single curve */
UE_DEPRECATED(5.6, "Instead use GetCurveDrawParamsCache, use FCurveDrawParamsCache::GetCurveDrawParamSynchronous to get Curve Draw Parm as with this function.")
void GetCurveDrawParam(TSharedPtr<FCurveEditor>& CurveEditor, const FCurveModelID& ModelID, FCurveModel* CurveModel, FCurveDrawParams& OutDrawParam) const;
/** Update all the curve view transforms from curve models */
void UpdateCurveViewTransformsFromModels();
// ~SWidget interface
virtual FVector2D ComputeDesiredSize(float LayoutScaleMultiplier) const override;
virtual void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) override;
private:
/** Only the curve editor panel can add curves to views to ensure internal consistency between the views and SCurveEditorPanel::CurveViews */
friend struct FCurveEditorPanelViewTracker;
/** Add a curve to this view */
void AddCurve(FCurveModelID CurveID);
/** Add a curve from this view */
void RemoveCurve(FCurveModelID CurveID);
public:
/** (Default: false) When true, this view has been pinned to the top of the view container. Only takes affect from the next call to SCurveEditorPanel::RebuildCurveViews. */
uint8 bPinned : 1;
/** (Default: true) When true, this view should accept interactive operations such as drags and tool interaction */
uint8 bInteractive : 1;
/** (Default: false) When true, this view has fixed vertical bounds that should never be changed by zooming or panning */
uint8 bFixedOutputBounds : 1;
/** (Default: true) See class notes - defines whether this view should size to its desired size (true) or stretch to the height of the panel (false). Only takes affect from the next call to SCurveEditorPanel::RebuildCurveViews. */
uint8 bAutoSize : 1;
/** (Default: false) Defines whether this view should remain on the panel UI even if it does not represent any curves. */
uint8 bAllowEmpty : 1;
/** (Default: false) When true, custom axes need to be rebuilt before use. */
uint8 bAllowModelViewTransforms : 1;
/** (Default: false) When true, custom axes need to be rebuilt before use. */
uint8 bUpdateModelViewTransforms : 1;
/** (Default: true) When true, this view has models that need the default grid lines drawing. */
uint8 bNeedsDefaultGridLinesH : 1;
/** (Default: true) When true, this view has models that need the default grid lines drawing. */
uint8 bNeedsDefaultGridLinesV : 1;
/** (Default: 0) Should be assigned on Construction of derived types. Defines a custom sort bias to use when sorting the stack of views (before sorting by pinned/unpinned state) */
int8 SortBias = 0;
/** This view's type identifier. Assigned by the curve editor panel after construction. */
ECurveEditorViewID ViewTypeID = ECurveEditorViewID::Invalid;
/** Transient integer that is assigned each time the view container order is changed, to guarantee relative ordering of views when the list changes */
int32 RelativeOrder = TNumericLimits<int32>::Max();
/** The maximum number of curves allowed on this view, or 0 for no limit */
int32 MaximumCapacity = 0;
/** (Optional) Attribute that defines a fixed height for this view. Overrides ChildSlot's desired size when set */
TAttribute<float> FixedHeight;
protected:
/** The curve editor associated with this view. */
TWeakPtr<FCurveEditor> WeakCurveEditor;
/** This view's minimum visible output value */
double OutputMin = 0.0;
/** This view's maximum visible output value */
double OutputMax = 1.0;
struct FCurveInfo
{
/** The linear index of the curve within this view determined by the order curves were added. */
int32 CurveIndex;
FTransform2d ViewToCurveTransform;
FCurveEditorViewAxisID HorizontalAxis;
FCurveEditorViewAxisID VerticalAxis;
};
/** Map from curve identifier to specific info pertaining to that curve for this view.
* @note: Should only be added to or removed from in AddCurve/RemoveCurve. Derived types must only change the FCurveInfo contained within this map.
*/
TSortedMap<FCurveModelID, FCurveInfo, TInlineAllocator<1>> CurveInfoByID;
struct FAxisInfo
{
TSharedPtr<FCurveEditorAxis> Axis;
double Min = 0.0;
double Max = 1.0;
int32 UseCount = 0;
};
TArray<FAxisInfo> CustomHorizontalAxes;
TArray<FAxisInfo> CustomVerticalAxes;
FAxisInfo& GetHorizontalAxisInfo(FCurveEditorViewAxisID ID)
{
check(ID);
return CustomHorizontalAxes[ID];
}
FAxisInfo& GetVerticalAxisInfo(FCurveEditorViewAxisID ID)
{
check(ID);
return CustomVerticalAxes[ID];
}
/** Flag enum signifying how the curve cache has changed since it was last generated
Note for a data change it may only effect certain data(curves) not every drawn curve*/
enum class UE_DEPRECATED(5.6, "Instead use the CurveDrawParamsCache, call FCurveDrawParamsCache::ECurveCacheFlags to get a corresponding type.") ECurveCacheFlags : uint8
{
CheckCurves = 0, // The cache may be valid need to check each curve to see if they are still valid
All = 1 << 0, // Get all
};
/** Curve cache flags that change based upon data or view getting modified*/
UE_DEPRECATED(5.6, "Instead use the CurveDrawParamsCache, call FCurveDrawParamsCache::GetCurveCacheFlags to get current flags. Use FCurveDrawParamsCache::Update to update them.")
PRAGMA_DISABLE_DEPRECATION_WARNINGS
ECurveCacheFlags CurveCacheFlags;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
/** Curve draw parameters that are re-generated on tick if the cache has changed. We generate them once and then they're used in multiple places per frame. */
UE_DEPRECATED(5.5, "Instead use the CurveDrawParamsCache, call GetCurveDrawParamsSynchronous.")
TArray<FCurveDrawParams> CachedDrawParams;
/** Cache for curve draw params */
TSharedPtr<UE::CurveEditor::FCurveDrawParamsCache> CurveDrawParamsCache;
/** Set of Cached values we need to check each tick to see if we need to redo cache*/
struct UE_DEPRECATED(5.6, "Instead use the CurveDrawParamsCache, call FCurveDrawParamsCache::FCachedCurveEditorData to get a corresponding type.") FCachedValuesToCheck
{
/** Serial number cached from FCurveEditor::GetActiveCurvesSerialNumber() on tick */
uint32 CachedActiveCurvesSerialNumber;
/** Serial number bached from CurveEditorSelecstion::GetSerialNumber */
uint32 CachedSelectionSerialNumber;
/** Cached Tangent Visibility*/
ECurveEditorTangentVisibility CachedTangentVisibility;
/** Cached input and output min max values to see if we need to recalc curves, though we need to poll it's safer*/
double CachedInputMin, CachedInputMax, CachedOutputMin, CachedOutputMax;
/** Cached Geometry Size*/
FVector2D CachedGeometrySize;
};
UE_DEPRECATED(5.6, "Instead use the CurveDrawParamsCache, use FCurveDrawParamsCache::GetCurveEditorData to get cached values. Use FCurveDrawParamsCache::Update to update them.")
PRAGMA_DISABLE_DEPRECATION_WARNINGS
FCachedValuesToCheck CachedValues;
PRAGMA_ENABLE_DEPRECATION_WARNINGS
/** Possible pointer to a retainer widget that we may need to force update*/
TSharedPtr<SRetainerWidget> RetainerWidget;
public:
void SetRetainerWidget(TSharedPtr<SRetainerWidget>& InWidget)
{
RetainerWidget = InWidget;
}
};