You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#preflight 6272a74d2f6d177be3c6fdda #rb Matt.Kuhlenschmidt #ROBOMERGE-OWNER: Lauren.Barnes #ROBOMERGE-AUTHOR: lauren.barnes #ROBOMERGE-SOURCE: CL 20057269 via CL 20070159 via CL 20072035 via CL 20072203 #ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v943-19904690) #ROBOMERGE-CONFLICT from-shelf [CL 20105363 by Lauren Barnes in ue5-main branch]
1318 lines
54 KiB
C++
1318 lines
54 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SCurveEditorPanel.h"
|
|
#include "Templates/Tuple.h"
|
|
#include "Algo/Partition.h"
|
|
#include "Rendering/DrawElements.h"
|
|
#include "CurveDrawInfo.h"
|
|
#include "CurveEditorSettings.h"
|
|
#include "CurveEditorCommands.h"
|
|
#include "Framework/Commands/UICommandList.h"
|
|
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
|
#include "Widgets/SNullWidget.h"
|
|
#include "Widgets/SOverlay.h"
|
|
#include "Widgets/SWindow.h"
|
|
#include "Widgets/Layout/SScrollBox.h"
|
|
#include "Widgets/Layout/SSplitter.h"
|
|
#include "Widgets/Text/STextBlock.h"
|
|
#include "Widgets/Docking/SDockTab.h"
|
|
#include "Framework/Docking/TabManager.h"
|
|
#include "Styling/AppStyle.h"
|
|
#include "Misc/Attribute.h"
|
|
#include "Algo/Sort.h"
|
|
#include "CurveEditorEditObjectContainer.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "PropertyEditorModule.h"
|
|
#include "IDetailsView.h"
|
|
#include "IPropertyRowGenerator.h"
|
|
#include "SCurveKeyDetailPanel.h"
|
|
#include "ICurveEditorExtension.h"
|
|
#include "ISequencerWidgetsModule.h"
|
|
#include "CurveEditorScreenSpace.h"
|
|
#include "Widgets/Layout/SBox.h"
|
|
#include "Framework/MultiBox/MultiBoxExtender.h"
|
|
#include "Filters/SCurveEditorFilterPanel.h"
|
|
#include "Filters/CurveEditorFilterBase.h"
|
|
#include "Filters/CurveEditorBakeFilter.h"
|
|
#include "Filters/CurveEditorReduceFilter.h"
|
|
#include "SGridLineSpacingList.h"
|
|
#include "Widgets/SFrameRatePicker.h"
|
|
#include "CommonFrameRates.h"
|
|
#include "CurveEditorViewRegistry.h"
|
|
#include "SCurveEditorViewContainer.h"
|
|
#include "SCurveEditorToolProperties.h"
|
|
#include "Fonts/FontMeasure.h"
|
|
#include "CurveEditorHelpers.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "SCurveEditorPanel"
|
|
|
|
int32 GCurveEditorPinnedViews = 0;
|
|
static FAutoConsoleVariableRef CVarCurveEditorPinnedViews(
|
|
TEXT("CurveEditor.PinnedViews"),
|
|
GCurveEditorPinnedViews,
|
|
TEXT("Whether pinning a curve should also cause it to be exclusively added to a pinned view or not (default: off), rather than simply always remain visible.")
|
|
);
|
|
|
|
int32 GCurveEditorMaxCurvesPerPinnedView = 0;
|
|
static FAutoConsoleVariableRef CVarCurveEditorMaxCurvesPerPinnedView(
|
|
TEXT("CurveEditor.MaxCurvesPerPinnedView"),
|
|
GCurveEditorMaxCurvesPerPinnedView,
|
|
TEXT("When CurveEditor.PinnedViews is 1, defines the maximum number of curves allowed on a pinned view (0 for no maximum).")
|
|
);
|
|
|
|
/**
|
|
* Utility class to hold an overlay which tells the user how to use the editor when there are no curves selected.
|
|
*/
|
|
class SCurveEditorViewOverlay : public SCompoundWidget
|
|
{
|
|
SLATE_BEGIN_ARGS(SCurveEditorViewOverlay)
|
|
{}
|
|
SLATE_END_ARGS()
|
|
|
|
void Construct(const FArguments& InArgs) {}
|
|
|
|
|
|
virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override
|
|
{
|
|
static const FLinearColor BackgroundColor = FLinearColor::Black.CopyWithNewOpacity(0.35f);
|
|
static const FText InstructionText = LOCTEXT("CurveEditorTutorialOverlay", "Select a curve on the left to begin editing.");
|
|
const FSlateBrush* WhiteBrush = FAppStyle::GetBrush("WhiteBrush");
|
|
const TSharedRef<FSlateFontMeasure> FontMeasure = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
|
|
const FSlateFontInfo FontInfo = FCoreStyle::Get().GetFontStyle("FontAwesome.13");
|
|
|
|
// Draw a darkened background
|
|
{
|
|
FSlateDrawElement::MakeBox(OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(), WhiteBrush, ESlateDrawEffect::None, BackgroundColor);
|
|
}
|
|
|
|
// Draw the tutorial text centered
|
|
{
|
|
const FVector2D LabelSize = FontMeasure->Measure(InstructionText, FontInfo);
|
|
const FVector2D GeometrySize = AllottedGeometry.GetLocalSize();
|
|
const FVector2D LabelOffset = FVector2D((GeometrySize.X - LabelSize.X) / 2.f, (GeometrySize.Y - LabelSize.Y) / 2.f);
|
|
const FPaintGeometry LabelGeometry = AllottedGeometry.ToPaintGeometry(FSlateLayoutTransform(LabelOffset));
|
|
|
|
FSlateDrawElement::MakeText(OutDrawElements, LayerId, LabelGeometry, InstructionText, FontInfo);
|
|
}
|
|
|
|
return LayerId + 1;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Implemented as a friend struct to SCurveEditorView to ensure that SCurveEditorPanel is the only thing that can add/remove curves from views
|
|
* whilst disallowing access to any other private members;
|
|
*/
|
|
struct FCurveEditorPanelViewTracker
|
|
{
|
|
static void AddCurveToView(SCurveEditorView* View, FCurveModelID InCurveID)
|
|
{
|
|
View->AddCurve(InCurveID);
|
|
}
|
|
static void RemoveCurveFromView(SCurveEditorView* View, FCurveModelID InCurveID)
|
|
{
|
|
View->RemoveCurve(InCurveID);
|
|
}
|
|
};
|
|
|
|
SCurveEditorPanel::SCurveEditorPanel()
|
|
: bNeedsRefresh(true)
|
|
, CachedActiveCurvesSerialNumber(-1)
|
|
{
|
|
EditObjects = MakeUnique<FCurveEditorEditObjectContainer>();
|
|
bSelectionSupportsWeightedTangents = false;
|
|
}
|
|
|
|
SCurveEditorPanel::~SCurveEditorPanel()
|
|
{
|
|
// Attempt to close a dialog if it's open. It has a weak reference to us and doesn't work well when it's invalid.
|
|
SCurveEditorFilterPanel::CloseDialog();
|
|
}
|
|
|
|
void SCurveEditorPanel::Construct(const FArguments& InArgs, TSharedRef<FCurveEditor> InCurveEditor)
|
|
{
|
|
GridLineTintAttribute = InArgs._GridLineTint;
|
|
DisabledTimeSnapTooltipAttribute = InArgs._DisabledTimeSnapTooltip;
|
|
WeakTabManager = InArgs._TabManager;
|
|
|
|
CachedSelectionSerialNumber = 0;
|
|
CurveEditor = InCurveEditor;
|
|
|
|
CurveEditor->SetPanel(SharedThis(this));
|
|
|
|
CurveEditor->BindCommands();
|
|
CurveEditor->SetTimeSliderController(InArgs._ExternalTimeSliderController);
|
|
|
|
CurveEditor->OnActiveToolChangedDelegate.AddSP(this, &SCurveEditorPanel::OnCurveEditorToolChanged);
|
|
|
|
CommandList = MakeShared<FUICommandList>();
|
|
CommandList->Append(InCurveEditor->GetCommands().ToSharedRef());
|
|
|
|
BindCommands();
|
|
|
|
// Create some Widgets
|
|
ISequencerWidgetsModule& SequencerWidgets = FModuleManager::Get().LoadModuleChecked<ISequencerWidgetsModule>("SequencerWidgets");
|
|
TSharedPtr<SWidget> TopTimeSlider = SNullWidget::NullWidget;
|
|
if (InArgs._ExternalTimeSliderController)
|
|
{
|
|
TopTimeSlider = SequencerWidgets.CreateTimeSlider(InArgs._ExternalTimeSliderController.ToSharedRef(), false /*bMirrorLabels*/);
|
|
}
|
|
|
|
TSharedRef<SScrollBar> ScrollBar = SNew(SScrollBar).Thickness(FVector2D(5.f, 5.f));
|
|
|
|
TSharedRef<SWidget> MainContent =
|
|
SNew(SOverlay)
|
|
|
|
// The main editing area
|
|
+ SOverlay::Slot()
|
|
[
|
|
SNew(SVerticalBox)
|
|
|
|
+ SVerticalBox::Slot()
|
|
.AutoHeight()
|
|
[
|
|
// Top Time Slider
|
|
SNew(SBorder)
|
|
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
|
.BorderBackgroundColor(FLinearColor(.50f, .50f, .50f, 1.0f))
|
|
.Padding(0)
|
|
.Clipping(EWidgetClipping::ClipToBounds)
|
|
[
|
|
TopTimeSlider.ToSharedRef()
|
|
]
|
|
]
|
|
|
|
+ SVerticalBox::Slot()
|
|
[
|
|
SNew(SOverlay)
|
|
|
|
+ SOverlay::Slot()
|
|
[
|
|
SAssignNew(ScrollBox, SScrollBox)
|
|
.ExternalScrollbar(ScrollBar)
|
|
|
|
+ SScrollBox::Slot()
|
|
[
|
|
// Main Curve View Area. The contents of this are dynamically filled based
|
|
// on your current views.
|
|
|
|
SAssignNew(CurveViewsContainer, SCurveEditorViewContainer, InCurveEditor)
|
|
.ExternalTimeSliderController(InArgs._ExternalTimeSliderController)
|
|
.MinimumPanelHeight(InArgs._MinimumViewPanelHeight)
|
|
]
|
|
]
|
|
|
|
+ SOverlay::Slot()
|
|
.HAlign(HAlign_Right)
|
|
[
|
|
ScrollBar
|
|
]
|
|
|
|
+ SOverlay::Slot()
|
|
.Padding(10.0f, 10.0f)
|
|
.HAlign(HAlign_Right)
|
|
.VAlign(VAlign_Bottom)
|
|
[
|
|
SAssignNew(ToolPropertiesPanel, SCurveEditorToolProperties, InCurveEditor, FCurveEditorToolID::Unset())
|
|
]
|
|
]
|
|
]
|
|
|
|
// An overlay for the main area which lets us put system-wide overlays
|
|
+ SOverlay::Slot()
|
|
[
|
|
SNew(SCurveEditorViewOverlay)
|
|
.Visibility(this, &SCurveEditorPanel::ShouldInstructionOverlayBeVisible)
|
|
];
|
|
|
|
|
|
if (InArgs._TreeContent.Widget != SNullWidget::NullWidget)
|
|
{
|
|
ChildSlot
|
|
[
|
|
SNew(SSplitter)
|
|
.Orientation(Orient_Horizontal)
|
|
.Style(FAppStyle::Get(), "SplitterDark")
|
|
.PhysicalSplitterHandleSize(3.0f)
|
|
|
|
+ SSplitter::Slot()
|
|
.Value(InArgs._TreeSplitterWidth)
|
|
[
|
|
InArgs._TreeContent.Widget
|
|
]
|
|
|
|
+ SSplitter::Slot()
|
|
.Value(InArgs._ContentSplitterWidth)
|
|
[
|
|
MainContent
|
|
]
|
|
];
|
|
}
|
|
else
|
|
{
|
|
ChildSlot
|
|
[
|
|
MainContent
|
|
];
|
|
}
|
|
|
|
|
|
|
|
KeyDetailsView = SNew(SCurveKeyDetailPanel, CurveEditor.ToSharedRef())
|
|
.IsEnabled(this, &SCurveEditorPanel::IsInlineEditPanelEditable);
|
|
|
|
UpdateEditBox();
|
|
|
|
// Initializes our Curve Views on the next Tick
|
|
SetViewMode(ECurveEditorViewID::Absolute);
|
|
}
|
|
|
|
TArrayView<const TSharedPtr<SCurveEditorView>> SCurveEditorPanel::GetViews() const
|
|
{
|
|
return CurveViewsContainer->GetViews();
|
|
}
|
|
|
|
void SCurveEditorPanel::ScrollBy(float Amount)
|
|
{
|
|
ScrollBox->SetScrollOffset(ScrollBox->GetScrollOffset() + Amount);
|
|
}
|
|
|
|
void SCurveEditorPanel::BindCommands()
|
|
{
|
|
// Interpolation and tangents
|
|
{
|
|
FExecuteAction SetConstant = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetKeyAttributes, FKeyAttributes().SetInterpMode(RCIM_Constant).SetTangentMode(RCTM_Auto), LOCTEXT("SetInterpConstant", "Set Interp Constant"));
|
|
FExecuteAction SetLinear = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetKeyAttributes, FKeyAttributes().SetInterpMode(RCIM_Linear).SetTangentMode(RCTM_Auto), LOCTEXT("SetInterpLinear", "Set Interp Linear"));
|
|
FExecuteAction SetCubicAuto = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetKeyAttributes, FKeyAttributes().SetInterpMode(RCIM_Cubic).SetTangentMode(RCTM_Auto), LOCTEXT("SetInterpCubic", "Set Interp Auto"));
|
|
FExecuteAction SetCubicUser = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetKeyAttributes, FKeyAttributes().SetInterpMode(RCIM_Cubic).SetTangentMode(RCTM_User), LOCTEXT("SetInterpUser", "Set Interp User"));
|
|
FExecuteAction SetCubicBreak = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetKeyAttributes, FKeyAttributes().SetInterpMode(RCIM_Cubic).SetTangentMode(RCTM_Break), LOCTEXT("SetInterpBreak", "Set Interp Break"));
|
|
|
|
FExecuteAction ToggleWeighted = FExecuteAction::CreateSP(this, &SCurveEditorPanel::ToggleWeightedTangents);
|
|
FCanExecuteAction CanToggleWeighted = FCanExecuteAction::CreateSP(this, &SCurveEditorPanel::CanToggleWeightedTangents);
|
|
|
|
FIsActionChecked IsConstantCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonInterpolationMode, RCIM_Constant);
|
|
FIsActionChecked IsLinearCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonInterpolationMode, RCIM_Linear);
|
|
FIsActionChecked IsCubicAutoCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonTangentMode, RCIM_Cubic, RCTM_Auto);
|
|
FIsActionChecked IsCubicUserCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonTangentMode, RCIM_Cubic, RCTM_User);
|
|
FIsActionChecked IsCubicBreakCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonTangentMode, RCIM_Cubic, RCTM_Break);
|
|
FIsActionChecked IsCubicWeightCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonTangentWeightMode, RCIM_Cubic, RCTWM_WeightedBoth);
|
|
|
|
FCanExecuteAction CanSetKeyTangent = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CanSetKeyInterpolation);
|
|
|
|
CommandList->MapAction(FCurveEditorCommands::Get().InterpolationConstant, SetConstant, CanSetKeyTangent, IsConstantCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().InterpolationLinear, SetLinear, CanSetKeyTangent, IsLinearCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().InterpolationCubicAuto, SetCubicAuto, CanSetKeyTangent, IsCubicAutoCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().InterpolationCubicUser, SetCubicUser, CanSetKeyTangent, IsCubicUserCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().InterpolationCubicBreak, SetCubicBreak, CanSetKeyTangent, IsCubicBreakCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().InterpolationToggleWeighted, ToggleWeighted, CanToggleWeighted, IsCubicWeightCommon);
|
|
|
|
}
|
|
|
|
// Pre Extrapolation Modes
|
|
{
|
|
FExecuteAction SetCycle = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPreExtrapolation(RCCE_Cycle), LOCTEXT("SetPreExtrapCycle", "Set Pre Extrapolation (Cycle)"));
|
|
FExecuteAction SetCycleWithOffset = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPreExtrapolation(RCCE_CycleWithOffset), LOCTEXT("SetPreExtrapCycleWithOffset", "Set Pre Extrapolation (Cycle With Offset)"));
|
|
FExecuteAction SetOscillate = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPreExtrapolation(RCCE_Oscillate), LOCTEXT("SetPreExtrapOscillate", "Set Pre Extrapolation (Oscillate)"));
|
|
FExecuteAction SetLinear = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPreExtrapolation(RCCE_Linear), LOCTEXT("SetPreExtrapLinear", "Set Pre Extrapolation (Linear)"));
|
|
FExecuteAction SetConstant = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPreExtrapolation(RCCE_Constant), LOCTEXT("SetPreExtrapConstant", "Set Pre Extrapolation (Constant)"));
|
|
|
|
FIsActionChecked IsCycleCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPreExtrapolationMode, RCCE_Cycle);
|
|
FIsActionChecked IsCycleWithOffsetCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPreExtrapolationMode, RCCE_CycleWithOffset);
|
|
FIsActionChecked IsOscillateCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPreExtrapolationMode, RCCE_Oscillate);
|
|
FIsActionChecked IsLinearCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPreExtrapolationMode, RCCE_Linear);
|
|
FIsActionChecked IsConstantCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPreExtrapolationMode, RCCE_Constant);
|
|
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPreInfinityExtrapCycle, SetCycle, FCanExecuteAction(), IsCycleCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPreInfinityExtrapCycleWithOffset, SetCycleWithOffset, FCanExecuteAction(), IsCycleWithOffsetCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPreInfinityExtrapOscillate, SetOscillate, FCanExecuteAction(), IsOscillateCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPreInfinityExtrapLinear, SetLinear, FCanExecuteAction(), IsLinearCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPreInfinityExtrapConstant, SetConstant, FCanExecuteAction(), IsConstantCommon);
|
|
}
|
|
|
|
// Post Extrapolation Modes
|
|
{
|
|
FExecuteAction SetCycle = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPostExtrapolation(RCCE_Cycle), LOCTEXT("SetPostExtrapCycle", "Set Post Extrapolation (Cycle)"));
|
|
FExecuteAction SetCycleWithOffset = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPostExtrapolation(RCCE_CycleWithOffset), LOCTEXT("SetPostExtrapCycleWithOffset", "Set Post Extrapolation (Cycle With Offset)"));
|
|
FExecuteAction SetOscillate = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPostExtrapolation(RCCE_Oscillate), LOCTEXT("SetPostExtrapOscillate", "Set Post Extrapolation (Oscillate)"));
|
|
FExecuteAction SetLinear = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPostExtrapolation(RCCE_Linear), LOCTEXT("SetPostExtrapLinear", "Set Post Extrapolation (Linear)"));
|
|
FExecuteAction SetConstant = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetCurveAttributes, FCurveAttributes().SetPostExtrapolation(RCCE_Constant), LOCTEXT("SetPostExtrapConstant", "Set Post Extrapolation (Constant)"));
|
|
|
|
FIsActionChecked IsCycleCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPostExtrapolationMode, RCCE_Cycle);
|
|
FIsActionChecked IsCycleWithOffsetCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPostExtrapolationMode, RCCE_CycleWithOffset);
|
|
FIsActionChecked IsOscillateCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPostExtrapolationMode, RCCE_Oscillate);
|
|
FIsActionChecked IsLinearCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPostExtrapolationMode, RCCE_Linear);
|
|
FIsActionChecked IsConstantCommon = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareCommonPostExtrapolationMode, RCCE_Constant);
|
|
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPostInfinityExtrapCycle, SetCycle, FCanExecuteAction(), IsCycleCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPostInfinityExtrapCycleWithOffset, SetCycleWithOffset, FCanExecuteAction(), IsCycleWithOffsetCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPostInfinityExtrapOscillate, SetOscillate, FCanExecuteAction(), IsOscillateCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPostInfinityExtrapLinear, SetLinear, FCanExecuteAction(), IsLinearCommon);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetPostInfinityExtrapConstant, SetConstant, FCanExecuteAction(), IsConstantCommon);
|
|
}
|
|
|
|
// Absolute, Stacked and Normalized views.
|
|
{
|
|
FExecuteAction SetViewAbsolute = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetViewMode, ECurveEditorViewID::Absolute);
|
|
FIsActionChecked IsViewModeAbsolute = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareViewMode, ECurveEditorViewID::Absolute);
|
|
|
|
FExecuteAction SetViewStacked = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetViewMode, ECurveEditorViewID::Stacked);
|
|
FIsActionChecked IsViewModeStacked = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareViewMode, ECurveEditorViewID::Stacked);
|
|
|
|
FExecuteAction SetViewNormalized = FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetViewMode, ECurveEditorViewID::Normalized);
|
|
FIsActionChecked IsViewModeNormalized = FIsActionChecked::CreateSP(this, &SCurveEditorPanel::CompareViewMode, ECurveEditorViewID::Normalized);
|
|
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetViewModeAbsolute, SetViewAbsolute, FCanExecuteAction(), IsViewModeAbsolute);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetViewModeStacked, SetViewStacked, FCanExecuteAction(), IsViewModeStacked);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetViewModeNormalized, SetViewNormalized, FCanExecuteAction(), IsViewModeNormalized);
|
|
}
|
|
|
|
{
|
|
// Deselect Current Keys
|
|
TSharedPtr<FCurveEditor> LocalCurveEditor = CurveEditor;
|
|
FExecuteAction DeselectAllAction = FExecuteAction::CreateLambda([LocalCurveEditor] { if (LocalCurveEditor.IsValid())
|
|
{
|
|
LocalCurveEditor->GetSelection().Clear();
|
|
}
|
|
});
|
|
CommandList->MapAction(FCurveEditorCommands::Get().DeselectAllKeys, DeselectAllAction);
|
|
}
|
|
|
|
// Presets for Bake and Reduce
|
|
CommandList->MapAction(FCurveEditorCommands::Get().BakeCurve, FExecuteAction::CreateSP(this, &SCurveEditorPanel::ShowCurveFilterUI, TSubclassOf<UCurveEditorFilterBase>(UCurveEditorBakeFilter::StaticClass())));
|
|
CommandList->MapAction(FCurveEditorCommands::Get().ReduceCurve, FExecuteAction::CreateSP(this, &SCurveEditorPanel::ShowCurveFilterUI, TSubclassOf<UCurveEditorFilterBase>(UCurveEditorReduceFilter::StaticClass())));
|
|
|
|
// User Implementable Filter just defaults to Bake since we know it exists...
|
|
CommandList->MapAction(FCurveEditorCommands::Get().OpenUserImplementableFilterWindow, FExecuteAction::CreateSP(this, &SCurveEditorPanel::ShowCurveFilterUI, TSubclassOf<UCurveEditorFilterBase>(UCurveEditorBakeFilter::StaticClass())));
|
|
|
|
// Axis Snapping
|
|
{
|
|
FUIAction SetSnappingNoneAction(FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetAxisSnapping, EAxisList::Type::None));
|
|
FUIAction SetSnappingHorizontalAction(FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetAxisSnapping, EAxisList::Type::X));
|
|
FUIAction SetSnappingVerticalAction(FExecuteAction::CreateSP(this, &SCurveEditorPanel::SetAxisSnapping, EAxisList::Type::Y));
|
|
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetAxisSnappingNone, SetSnappingNoneAction);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetAxisSnappingHorizontal, SetSnappingHorizontalAction);
|
|
CommandList->MapAction(FCurveEditorCommands::Get().SetAxisSnappingVertical, SetSnappingVerticalAction);
|
|
}
|
|
|
|
}
|
|
|
|
void SCurveEditorPanel::SetViewMode(const ECurveEditorViewID NewViewMode)
|
|
{
|
|
DefaultViewID = NewViewMode;
|
|
bNeedsRefresh = true;
|
|
}
|
|
|
|
bool SCurveEditorPanel::CompareViewMode(const ECurveEditorViewID InViewMode) const
|
|
{
|
|
return DefaultViewID == InViewMode;
|
|
}
|
|
|
|
void SCurveEditorPanel::SetAxisSnapping(EAxisList::Type InAxis)
|
|
{
|
|
FCurveEditorAxisSnap Snap = CurveEditor->GetAxisSnap();
|
|
Snap.RestrictedAxisList = InAxis;
|
|
CurveEditor->SetAxisSnap(Snap);
|
|
}
|
|
|
|
void SCurveEditorPanel::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
|
|
{
|
|
bool bWasRefreshed = false;
|
|
if (bNeedsRefresh || CachedActiveCurvesSerialNumber != CurveEditor->GetActiveCurvesSerialNumber())
|
|
{
|
|
RebuildCurveViews();
|
|
|
|
if (CurveEditor->ShouldAutoFrame())
|
|
{
|
|
CurveEditor->ZoomToFitCurves(CurveEditor->GetEditedCurves().Array());
|
|
}
|
|
|
|
bNeedsRefresh = false;
|
|
CachedActiveCurvesSerialNumber = CurveEditor->GetActiveCurvesSerialNumber();
|
|
bWasRefreshed = true;
|
|
}
|
|
|
|
UpdateCommonCurveInfo();
|
|
UpdateEditBox();
|
|
UpdateTime();
|
|
|
|
CachedSelectionSerialNumber = CurveEditor->Selection.GetSerialNumber();
|
|
}
|
|
|
|
void SCurveEditorPanel::RemoveCurveFromViews(FCurveModelID InCurveID)
|
|
{
|
|
for (auto It = CurveViews.CreateKeyIterator(InCurveID); It; ++It)
|
|
{
|
|
FCurveEditorPanelViewTracker::RemoveCurveFromView(&It.Value().Get(), InCurveID);
|
|
It.RemoveCurrent();
|
|
}
|
|
}
|
|
|
|
void SCurveEditorPanel::PostUndo()
|
|
{
|
|
EditObjects->CurveIDToKeyProxies.Empty();
|
|
CachedSelectionSerialNumber = 0;
|
|
}
|
|
|
|
void SCurveEditorPanel::AddView(TSharedRef<SCurveEditorView> ViewToAdd)
|
|
{
|
|
ExternalViews.Add(ViewToAdd);
|
|
bNeedsRefresh = true;
|
|
}
|
|
|
|
void SCurveEditorPanel::RemoveView(TSharedRef<SCurveEditorView> ViewToRemove)
|
|
{
|
|
ExternalViews.Remove(ViewToRemove);
|
|
bNeedsRefresh = true;
|
|
}
|
|
|
|
TSharedPtr<SCurveEditorView> SCurveEditorPanel::CreateViewOfType(FCurveModelID CurveModelID, ECurveEditorViewID ViewTypeID, bool bPinned)
|
|
{
|
|
for (auto It = FreeViewsByType.CreateKeyIterator(ViewTypeID); It; ++It)
|
|
{
|
|
TSharedRef<SCurveEditorView> View = It.Value();
|
|
|
|
if (!GCurveEditorPinnedViews || View->bPinned == bPinned)
|
|
{
|
|
FCurveEditorPanelViewTracker::AddCurveToView(&View.Get(), CurveModelID);
|
|
CurveViews.Add(CurveModelID, View);
|
|
|
|
if (!View->HasCapacity())
|
|
{
|
|
It.RemoveCurrent();
|
|
}
|
|
|
|
return View;
|
|
}
|
|
}
|
|
|
|
TSharedPtr<SCurveEditorView> View = FCurveEditorViewRegistry::Get().ConstructView(ViewTypeID, CurveEditor);
|
|
if (View)
|
|
{
|
|
if (GCurveEditorPinnedViews && bPinned)
|
|
{
|
|
// Pinned views are always a fixed height
|
|
View->bPinned = true;
|
|
View->MaximumCapacity = View->MaximumCapacity == 0 ? GCurveEditorMaxCurvesPerPinnedView : FMath::Min(View->MaximumCapacity, GCurveEditorMaxCurvesPerPinnedView);
|
|
if (!View->FixedHeight.IsSet())
|
|
{
|
|
View->FixedHeight = 100.f;
|
|
}
|
|
}
|
|
View->ViewTypeID = ViewTypeID;
|
|
FCurveEditorPanelViewTracker::AddCurveToView(View.Get(), CurveModelID);
|
|
CurveViews.Add(CurveModelID, View.ToSharedRef());
|
|
|
|
if (View->HasCapacity())
|
|
{
|
|
FreeViewsByType.Add(ViewTypeID, View.ToSharedRef());
|
|
}
|
|
}
|
|
|
|
return View;
|
|
}
|
|
|
|
void SCurveEditorPanel::RebuildCurveViews()
|
|
{
|
|
TSet<TSharedRef<SCurveEditorView>> Views = ExternalViews;
|
|
|
|
for (const TTuple<FCurveModelID, TUniquePtr<FCurveModel>>& CurvePair : CurveEditor->GetCurves())
|
|
{
|
|
FCurveModel* Curve = CurvePair.Value.Get();
|
|
FCurveModelID CurveID = CurvePair.Key;
|
|
|
|
const bool bIsPinned = CurveEditor->IsCurvePinned(CurveID);
|
|
|
|
bool bNeedsView = true;
|
|
|
|
for (auto ViewIt = CurveViews.CreateKeyIterator(CurveID); ViewIt; ++ViewIt)
|
|
{
|
|
TSharedRef<SCurveEditorView> View = ViewIt.Value();
|
|
// @todo: pinning - This code causes curves that have changed their pinned state to be re-added to a correctly (un)pinned views
|
|
if (GCurveEditorPinnedViews && View->bPinned != bIsPinned)
|
|
{
|
|
// No longer the same pinned status as the view it's in - remove it so that it can get added to the correct view (or removed entirely)
|
|
FCurveEditorPanelViewTracker::RemoveCurveFromView(&View.Get(), CurveID);
|
|
ViewIt.RemoveCurrent();
|
|
}
|
|
else if (View->ViewTypeID == DefaultViewID || !EnumHasAnyFlags(View->ViewTypeID, ECurveEditorViewID::ANY_BUILT_IN))
|
|
{
|
|
// Keep this view if it is the default view or any other custom view
|
|
Views.Add(View);
|
|
bNeedsView = false;
|
|
}
|
|
else
|
|
{
|
|
// Built in view which is no longer the selected mode - remove it
|
|
FCurveEditorPanelViewTracker::RemoveCurveFromView(&View.Get(), CurveID);
|
|
ViewIt.RemoveCurrent();
|
|
}
|
|
}
|
|
|
|
if (bNeedsView)
|
|
{
|
|
ECurveEditorViewID SupportedViews = Curve->GetSupportedViews();
|
|
|
|
// Add to the default view if supported, else use the first supported view we can find.
|
|
// @todo: this may require extra work if curves are ever to support multiple views but it's fine for now
|
|
if (EnumHasAnyFlags(SupportedViews, DefaultViewID))
|
|
{
|
|
TSharedPtr<SCurveEditorView> NewView = CreateViewOfType(CurveID, DefaultViewID, bIsPinned);
|
|
if (NewView)
|
|
{
|
|
Views.Add(NewView.ToSharedRef());
|
|
}
|
|
continue;
|
|
}
|
|
|
|
ECurveEditorViewID CustomView = ECurveEditorViewID::CUSTOM_START;
|
|
while (CustomView >= ECurveEditorViewID::CUSTOM_START)
|
|
{
|
|
if (EnumHasAnyFlags(SupportedViews, ECurveEditorViewID(CustomView)))
|
|
{
|
|
TSharedPtr<SCurveEditorView> NewView = CreateViewOfType(CurveID, CustomView, bIsPinned);
|
|
if (NewView)
|
|
{
|
|
Views.Add(NewView.ToSharedRef());
|
|
}
|
|
}
|
|
CustomView = ECurveEditorViewID((__underlying_type(ECurveEditorViewID))(CustomView) << 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove any empty views
|
|
for (auto It = FreeViewsByType.CreateIterator(); It; ++It)
|
|
{
|
|
if (!It.Value()->bAllowEmpty && It.Value()->NumCurves() == 0)
|
|
{
|
|
It.RemoveCurrent();
|
|
}
|
|
}
|
|
|
|
// Sort by pinned, then capacity
|
|
TArray<TSharedRef<SCurveEditorView>> SortedViews = Views.Array();
|
|
Algo::Sort(SortedViews, [](const TSharedPtr<SCurveEditorView>& A, const TSharedPtr<SCurveEditorView>& B)
|
|
{
|
|
if (A->SortBias == B->SortBias)
|
|
{
|
|
if (A->bPinned == B->bPinned)
|
|
{
|
|
return A->RelativeOrder < B->RelativeOrder;
|
|
}
|
|
|
|
return A->bPinned != 0;
|
|
}
|
|
|
|
return A->SortBias > B->SortBias;
|
|
}
|
|
);
|
|
|
|
CurveViewsContainer->Clear();
|
|
for (TSharedPtr<SCurveEditorView> View : SortedViews)
|
|
{
|
|
CurveViewsContainer->AddView(View.ToSharedRef());
|
|
}
|
|
}
|
|
|
|
void SCurveEditorPanel::UpdateCommonCurveInfo()
|
|
{
|
|
// Gather up common extended curve info for the current set of curves
|
|
TOptional<FCurveAttributes> AccumulatedCurveAttributes;
|
|
for (const TTuple<FCurveModelID, FKeyHandleSet>& Pair : CurveEditor->Selection.GetAll())
|
|
{
|
|
FCurveAttributes Attributes;
|
|
|
|
FCurveModel* Curve = CurveEditor->FindCurve(Pair.Key);
|
|
if (Curve)
|
|
{
|
|
Curve->GetCurveAttributes(Attributes);
|
|
|
|
// Some curves don't support extrapolation. We don't count them for determine the accumulated state.
|
|
if (Attributes.HasPreExtrapolation() && Attributes.GetPreExtrapolation() == RCCE_None && Attributes.HasPostExtrapolation() && Attributes.GetPostExtrapolation() == RCCE_None)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!AccumulatedCurveAttributes.IsSet())
|
|
{
|
|
AccumulatedCurveAttributes = Attributes;
|
|
}
|
|
else
|
|
{
|
|
AccumulatedCurveAttributes = FCurveAttributes::MaskCommon(AccumulatedCurveAttributes.GetValue(), Attributes);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset the common curve and key info
|
|
bSelectionSupportsWeightedTangents = false;
|
|
CachedCommonCurveAttributes = AccumulatedCurveAttributes.Get(FCurveAttributes());
|
|
|
|
TOptional<FKeyAttributes> AccumulatedKeyAttributes;
|
|
TArray<FKeyAttributes> AllKeyAttributes;
|
|
|
|
for (const TTuple<FCurveModelID, FKeyHandleSet>& Pair : CurveEditor->Selection.GetAll())
|
|
{
|
|
FCurveModel* Curve = CurveEditor->FindCurve(Pair.Key);
|
|
if (Curve)
|
|
{
|
|
AllKeyAttributes.Reset();
|
|
AllKeyAttributes.SetNum(Pair.Value.Num());
|
|
|
|
Curve->GetKeyAttributes(Pair.Value.AsArray(), AllKeyAttributes);
|
|
for (const FKeyAttributes& Attributes : AllKeyAttributes)
|
|
{
|
|
if (Attributes.HasTangentWeightMode())
|
|
{
|
|
bSelectionSupportsWeightedTangents = true;
|
|
}
|
|
|
|
if (!AccumulatedKeyAttributes.IsSet())
|
|
{
|
|
AccumulatedKeyAttributes = Attributes;
|
|
}
|
|
else
|
|
{
|
|
AccumulatedKeyAttributes = FKeyAttributes::MaskCommon(AccumulatedKeyAttributes.GetValue(), Attributes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset the common curve and key info
|
|
CachedCommonKeyAttributes = AccumulatedKeyAttributes.Get(FKeyAttributes());
|
|
}
|
|
|
|
void SCurveEditorPanel::OnCurveEditorToolChanged(FCurveEditorToolID InToolId)
|
|
{
|
|
ToolPropertiesPanel->OnToolChanged(InToolId);
|
|
}
|
|
|
|
void SCurveEditorPanel::UpdateTime()
|
|
{
|
|
const FCurveEditorSelection& Selection = CurveEditor->Selection;
|
|
if (CachedSelectionSerialNumber == Selection.GetSerialNumber())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (CurveEditor->GetSettings()->GetSnapTimeToSelection())
|
|
{
|
|
CurveEditor->SnapToSelectedKey();
|
|
}
|
|
}
|
|
|
|
void SCurveEditorPanel::UpdateEditBox()
|
|
{
|
|
const FCurveEditorSelection& Selection = CurveEditor->Selection;
|
|
for (TTuple<FCurveModelID, TMap<FKeyHandle, UObject*>>& OuterPair : EditObjects->CurveIDToKeyProxies)
|
|
{
|
|
const FKeyHandleSet* SelectedKeys = Selection.FindForCurve(OuterPair.Key);
|
|
if(SelectedKeys)
|
|
{
|
|
for (TTuple<FKeyHandle, UObject*>& InnerPair : OuterPair.Value)
|
|
{
|
|
if (ICurveEditorKeyProxy* Proxy = Cast<ICurveEditorKeyProxy>(InnerPair.Value))
|
|
{
|
|
Proxy->UpdateValuesFromRawData();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (CachedSelectionSerialNumber == Selection.GetSerialNumber())
|
|
{
|
|
return;
|
|
}
|
|
|
|
TArray<FKeyHandle> KeyHandleScratch;
|
|
TArray<UObject*> NewProxiesScratch;
|
|
|
|
TArray<UObject*> AllEditObjects;
|
|
for (const TTuple<FCurveModelID, FKeyHandleSet>& Pair : Selection.GetAll())
|
|
{
|
|
FCurveModel* Curve = CurveEditor->FindCurve(Pair.Key);
|
|
if (!Curve)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
KeyHandleScratch.Reset();
|
|
NewProxiesScratch.Reset();
|
|
|
|
TMap<FKeyHandle, UObject*>& KeyHandleToEditObject = EditObjects->CurveIDToKeyProxies.FindOrAdd(Pair.Key);
|
|
for (FKeyHandle Handle : Pair.Value.AsArray())
|
|
{
|
|
if (UObject* Existing = KeyHandleToEditObject.FindRef(Handle))
|
|
{
|
|
AllEditObjects.Add(Existing);
|
|
}
|
|
else
|
|
{
|
|
KeyHandleScratch.Add(Handle);
|
|
}
|
|
}
|
|
|
|
if (KeyHandleScratch.Num() > 0)
|
|
{
|
|
NewProxiesScratch.SetNum(KeyHandleScratch.Num());
|
|
Curve->CreateKeyProxies(KeyHandleScratch, NewProxiesScratch);
|
|
|
|
for (int32 Index = 0; Index < KeyHandleScratch.Num(); ++Index)
|
|
{
|
|
if (UObject* NewObject = NewProxiesScratch[Index])
|
|
{
|
|
KeyHandleToEditObject.Add(KeyHandleScratch[Index], NewObject);
|
|
AllEditObjects.Add(NewObject);
|
|
|
|
// Update the proxy immediately after adding it so that it doesn't have the wrong values for 1 tick.
|
|
if (ICurveEditorKeyProxy* Proxy = Cast<ICurveEditorKeyProxy>(NewObject))
|
|
{
|
|
Proxy->UpdateValuesFromRawData();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
KeyDetailsView->GetPropertyRowGenerator()->SetObjects(AllEditObjects);
|
|
}
|
|
|
|
EVisibility SCurveEditorPanel::GetSplitterVisibility() const
|
|
{
|
|
return EVisibility::Visible;
|
|
}
|
|
|
|
void SCurveEditorPanel::SetKeyAttributes(FKeyAttributes KeyAttributes, FText Description)
|
|
{
|
|
FScopedTransaction Transaction(Description);
|
|
|
|
for (const TTuple<FCurveModelID, FKeyHandleSet>& Pair : CurveEditor->Selection.GetAll())
|
|
{
|
|
if (FCurveModel* Curve = CurveEditor->FindCurve(Pair.Key))
|
|
{
|
|
Curve->Modify();
|
|
Curve->SetKeyAttributes(Pair.Value.AsArray(), KeyAttributes);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SCurveEditorPanel::SetCurveAttributes(FCurveAttributes CurveAttributes, FText Description)
|
|
{
|
|
FScopedTransaction Transaction(Description);
|
|
|
|
for (const TTuple<FCurveModelID, FKeyHandleSet>& Pair : CurveEditor->Selection.GetAll())
|
|
{
|
|
if (FCurveModel* Curve = CurveEditor->FindCurve(Pair.Key))
|
|
{
|
|
Curve->Modify();
|
|
Curve->SetCurveAttributes(CurveAttributes);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SCurveEditorPanel::ToggleWeightedTangents()
|
|
{
|
|
FScopedTransaction Transaction(LOCTEXT("ToggleWeightedTangents_Transaction", "Toggle Weighted Tangents"));
|
|
|
|
TMap<FCurveModelID, TArray<FKeyAttributes>> KeyAttributesPerCurve;
|
|
|
|
const TMap<FCurveModelID, FKeyHandleSet>& Selection = CurveEditor->GetSelection().GetAll();
|
|
|
|
// Disable weights unless we find something that doesn't have weights, then add them
|
|
FKeyAttributes KeyAttributesToAssign = FKeyAttributes().SetTangentWeightMode(RCTWM_WeightedNone);
|
|
|
|
// Gather current key attributes
|
|
for (const TTuple<FCurveModelID, FKeyHandleSet>& Pair : Selection)
|
|
{
|
|
FCurveModel* Curve = CurveEditor->FindCurve(Pair.Key);
|
|
if (Curve)
|
|
{
|
|
TArray<FKeyAttributes>& KeyAttributes = KeyAttributesPerCurve.Add(Pair.Key);
|
|
KeyAttributes.SetNum(Pair.Value.Num());
|
|
Curve->GetKeyAttributes(Pair.Value.AsArray(), KeyAttributes);
|
|
|
|
// Check all the key attributes if they support tangent weights, but don't have any. If we find any such keys, we'll enable weights on all.
|
|
if (KeyAttributesToAssign.GetTangentWeightMode() == RCTWM_WeightedNone)
|
|
{
|
|
for (const FKeyAttributes& Attributes : KeyAttributes)
|
|
{
|
|
if (Attributes.HasTangentWeightMode() && !(Attributes.HasArriveTangentWeight() || Attributes.HasLeaveTangentWeight()))
|
|
{
|
|
KeyAttributesToAssign.SetTangentWeightMode(RCTWM_WeightedBoth);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assign the new key attributes to all the selected curves
|
|
for (TTuple<FCurveModelID, TArray<FKeyAttributes>>& Pair : KeyAttributesPerCurve)
|
|
{
|
|
FCurveModel* Curve = CurveEditor->FindCurve(Pair.Key);
|
|
if (Curve)
|
|
{
|
|
for (FKeyAttributes& Attributes : Pair.Value)
|
|
{
|
|
Attributes = KeyAttributesToAssign;
|
|
}
|
|
|
|
TArrayView<const FKeyHandle> KeyHandles = Selection.FindChecked(Pair.Key).AsArray();
|
|
Curve->Modify();
|
|
Curve->SetKeyAttributes(KeyHandles, Pair.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SCurveEditorPanel::CanToggleWeightedTangents() const
|
|
{
|
|
return bSelectionSupportsWeightedTangents && CanSetKeyInterpolation();
|
|
}
|
|
|
|
bool SCurveEditorPanel::CanSetKeyInterpolation() const
|
|
{
|
|
return CurveEditor->GetSelection().Count() > 0;
|
|
}
|
|
|
|
FReply SCurveEditorPanel::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
|
{
|
|
if (InKeyEvent.GetKey() == EKeys::Escape)
|
|
{
|
|
CurveEditor->Selection.Clear();
|
|
return FReply::Handled();
|
|
}
|
|
else if (CommandList->ProcessCommandBindings(InKeyEvent))
|
|
{
|
|
return FReply::Handled();
|
|
}
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
TSharedRef<SWidget> SCurveEditorPanel::MakeCurveEditorCurveViewOptionsMenu()
|
|
{
|
|
// This builds the dropdown menu when looking at the Curve View Options combobox.
|
|
FMenuBuilder MenuBuilder(true, CurveEditor->GetCommands());
|
|
|
|
MenuBuilder.BeginSection("TangentVisibility", LOCTEXT("CurveEditorMenuTangentVisibilityHeader", "Tangent Visibility"));
|
|
{
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetAllTangentsVisibility);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetSelectedKeysTangentVisibility);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetNoTangentsVisibility);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.AddMenuSeparator();
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().ToggleAutoFrameCurveEditor);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().ToggleSnapTimeToSelection);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().ToggleShowBufferedCurves);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().ToggleShowCurveEditorCurveToolTips);
|
|
|
|
MenuBuilder.BeginSection("Organize", LOCTEXT("CurveEditorMenuOrganizeHeader", "Organize"));
|
|
{
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().ToggleExpandCollapseNodes);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().ToggleExpandCollapseNodesAndDescendants);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
MenuBuilder.BeginSection("CurveColors", LOCTEXT("CurveColorsHeader", "Curve Colors"));
|
|
{
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetRandomCurveColorsForSelected);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetCurveColorsForSelected);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
TSharedRef<SWidget> SCurveEditorPanel::MakeCurveExtrapolationMenu(const bool bInPostExtrapolation)
|
|
{
|
|
// This builds the dropdown menu when looking at the Curve View Options combobox.
|
|
FMenuBuilder MenuBuilder(true, CurveEditor->GetCommands());
|
|
|
|
if (!bInPostExtrapolation)
|
|
{
|
|
MenuBuilder.BeginSection("PreInfinity", LOCTEXT("CurveEditorMenuPreInfinityHeader", "Pre-Infinity"));
|
|
{
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPreInfinityExtrapConstant);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPreInfinityExtrapCycle);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPreInfinityExtrapCycleWithOffset);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPreInfinityExtrapLinear);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPreInfinityExtrapOscillate);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
else
|
|
{
|
|
MenuBuilder.BeginSection("PostInfinity", LOCTEXT("CurveEditorMenuPostInfinityHeader", "Post-Infinity"));
|
|
{
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPostInfinityExtrapConstant);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPostInfinityExtrapCycle);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPostInfinityExtrapCycleWithOffset);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPostInfinityExtrapLinear);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetPostInfinityExtrapOscillate);
|
|
}
|
|
MenuBuilder.EndSection();
|
|
}
|
|
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
FSlateIcon SCurveEditorPanel::GetCurveExtrapolationPreIcon() const
|
|
{
|
|
// We check to see if pre/post share a extrapolation mode and return a shared icon, otherwise mixed.
|
|
if (CompareCommonPreExtrapolationMode(RCCE_Constant))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPreInfinityExtrapConstant->GetIcon();
|
|
}
|
|
else if (CompareCommonPreExtrapolationMode(RCCE_Cycle))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPreInfinityExtrapCycle->GetIcon();
|
|
}
|
|
else if (CompareCommonPreExtrapolationMode(RCCE_CycleWithOffset))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPreInfinityExtrapCycleWithOffset->GetIcon();
|
|
}
|
|
else if (CompareCommonPreExtrapolationMode(RCCE_Linear))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPreInfinityExtrapLinear->GetIcon();
|
|
}
|
|
else if (CompareCommonPreExtrapolationMode(RCCE_Oscillate))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPreInfinityExtrapOscillate->GetIcon();
|
|
}
|
|
else
|
|
{
|
|
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "GenericCurveEditor.PreInfinityMixed");
|
|
}
|
|
}
|
|
|
|
FSlateIcon SCurveEditorPanel::GetCurveExtrapolationPostIcon() const
|
|
{
|
|
// We check to see if pre/post share a extrapolation mode and return a shared icon, otherwise mixed.
|
|
if (CompareCommonPostExtrapolationMode(RCCE_Constant))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPostInfinityExtrapConstant->GetIcon();
|
|
}
|
|
else if (CompareCommonPostExtrapolationMode(RCCE_Cycle))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPostInfinityExtrapCycle->GetIcon();
|
|
}
|
|
else if (CompareCommonPostExtrapolationMode(RCCE_CycleWithOffset))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPostInfinityExtrapCycleWithOffset->GetIcon();
|
|
}
|
|
else if (CompareCommonPostExtrapolationMode(RCCE_Linear))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPostInfinityExtrapLinear->GetIcon();
|
|
}
|
|
else if (CompareCommonPostExtrapolationMode(RCCE_Oscillate))
|
|
{
|
|
return FCurveEditorCommands::Get().SetPostInfinityExtrapOscillate->GetIcon();
|
|
}
|
|
else
|
|
{
|
|
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "GenericCurveEditor.PostInfinityMixed");
|
|
}
|
|
}
|
|
|
|
|
|
void SCurveEditorPanel::ShowCurveFilterUI(TSubclassOf<UCurveEditorFilterBase> FilterClass)
|
|
{
|
|
TSharedPtr<FTabManager> TabManager = WeakTabManager.Pin();
|
|
TSharedPtr<SDockTab> OwnerTab = TabManager.IsValid() ? TabManager->GetOwnerTab() : TSharedPtr<SDockTab>();
|
|
TSharedPtr<SWindow> RootWindow = OwnerTab.IsValid() ? OwnerTab->GetParentWindow() : TSharedPtr<SWindow>();
|
|
|
|
SCurveEditorFilterPanel::OpenDialog(RootWindow, CurveEditor.ToSharedRef(), FilterClass);
|
|
}
|
|
|
|
const FGeometry& SCurveEditorPanel::GetScrollPanelGeometry() const
|
|
{
|
|
return ScrollBox->GetCachedGeometry();
|
|
}
|
|
|
|
const FGeometry& SCurveEditorPanel::GetViewContainerGeometry() const
|
|
{
|
|
return CurveViewsContainer->GetCachedGeometry();
|
|
}
|
|
|
|
TSharedPtr<FExtender> SCurveEditorPanel::GetToolbarExtender()
|
|
{
|
|
// We're going to create a new Extender and add the main Curve Editor icons to it.
|
|
// We combine this with the extender provided by the Curve Editor Module as that extender has been extended by tools
|
|
ICurveEditorModule& CurveEditorModule = FModuleManager::Get().LoadModuleChecked<ICurveEditorModule>("CurveEditor");
|
|
TArray<TSharedPtr<FExtender>> ToolbarExtenders;
|
|
for (ICurveEditorModule::FCurveEditorMenuExtender& ExtenderCallback : CurveEditorModule.GetAllToolBarMenuExtenders())
|
|
{
|
|
ToolbarExtenders.Add(ExtenderCallback.Execute(GetCommands().ToSharedRef()));
|
|
}
|
|
TSharedPtr<FExtender> Extender = FExtender::Combine(ToolbarExtenders);
|
|
|
|
struct Local
|
|
{
|
|
static void FillToolbar(FToolBarBuilder& ToolBarBuilder, TSharedRef<SCurveKeyDetailPanel> InKeyDetailsPanel, TSharedRef<SCurveEditorPanel> InEditorPanel)
|
|
{
|
|
ToolBarBuilder.BeginSection("View");
|
|
ToolBarBuilder.BeginStyleOverride("CurveEditorToolbar");
|
|
{
|
|
// Dropdown Menu for choosing your viewing mode
|
|
TAttribute<FSlateIcon> ViewModeIcon;
|
|
ViewModeIcon.Bind(TAttribute<FSlateIcon>::FGetter::CreateLambda([InEditorPanel] {
|
|
switch (InEditorPanel->GetViewMode())
|
|
{
|
|
case ECurveEditorViewID::Absolute:
|
|
return FCurveEditorCommands::Get().SetViewModeAbsolute->GetIcon();
|
|
case ECurveEditorViewID::Stacked:
|
|
return FCurveEditorCommands::Get().SetViewModeStacked->GetIcon();
|
|
case ECurveEditorViewID::Normalized:
|
|
return FCurveEditorCommands::Get().SetViewModeNormalized->GetIcon();
|
|
default: // EKeyGroupMode::None
|
|
return FCurveEditorCommands::Get().SetAxisSnappingNone->GetIcon();
|
|
}
|
|
}));
|
|
|
|
ToolBarBuilder.AddComboButton(
|
|
FUIAction(),
|
|
FOnGetContent::CreateSP(InEditorPanel, &SCurveEditorPanel::MakeViewModeMenu),
|
|
LOCTEXT("ViewModeDropdown", "Curve View Modes"),
|
|
LOCTEXT("ViewModeDropdownToolTip", "Choose the viewing mode for the curves."),
|
|
ViewModeIcon);
|
|
}
|
|
ToolBarBuilder.EndSection();
|
|
|
|
ToolBarBuilder.BeginSection("Framing");
|
|
{
|
|
// Framing
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().ZoomToFit);
|
|
}
|
|
ToolBarBuilder.EndSection();
|
|
|
|
ToolBarBuilder.BeginSection("Visibility");
|
|
{
|
|
// Curve Visibility
|
|
ToolBarBuilder.AddComboButton(
|
|
FUIAction(),
|
|
FOnGetContent::CreateSP(InEditorPanel, &SCurveEditorPanel::MakeCurveEditorCurveViewOptionsMenu),
|
|
LOCTEXT("CurveEditorCurveOptions", "Curves Options"),
|
|
LOCTEXT("CurveEditorCurveOptionsToolTip", "Curve Options"),
|
|
FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.Visibility"));
|
|
|
|
}
|
|
ToolBarBuilder.EndSection();
|
|
|
|
ToolBarBuilder.BeginSection("Key Details");
|
|
{
|
|
ToolBarBuilder.AddWidget(InKeyDetailsPanel);
|
|
}
|
|
ToolBarBuilder.EndSection();
|
|
|
|
ToolBarBuilder.BeginSection("Tools");
|
|
{
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().DeactivateCurrentTool);
|
|
}
|
|
ToolBarBuilder.EndSection();
|
|
|
|
ToolBarBuilder.BeginSection("Adjustment");
|
|
{
|
|
// Toggle Button for Time Snapping
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().ToggleInputSnapping);
|
|
|
|
// Dropdown Menu to choose the snapping scale.
|
|
FUIAction TimeSnapMenuAction(FExecuteAction(), FCanExecuteAction::CreateLambda([InEditorPanel] { return !InEditorPanel->DisabledTimeSnapTooltipAttribute.IsSet(); }));
|
|
ToolBarBuilder.AddComboButton(
|
|
TimeSnapMenuAction,
|
|
FOnGetContent::CreateSP(InEditorPanel, &SCurveEditorPanel::MakeTimeSnapMenu),
|
|
LOCTEXT("TimeSnappingOptions", "Time Snapping"),
|
|
TAttribute<FText>(InEditorPanel, &SCurveEditorPanel::GetTimeSnapMenuTooltip),
|
|
TAttribute<FSlateIcon>(),
|
|
true);
|
|
|
|
// Toggle Button for Value Snapping
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().ToggleOutputSnapping);
|
|
|
|
// Dropdown Menu to choose the snapping scale.
|
|
ToolBarBuilder.AddComboButton(
|
|
FUIAction(),
|
|
FOnGetContent::CreateSP(InEditorPanel, &SCurveEditorPanel::MakeGridSpacingMenu),
|
|
LOCTEXT("GridSnappingOptions", "Grid Snapping"),
|
|
LOCTEXT("GridSnappingOptionsToolTip", "Choose the spacing between horizontal grid lines."),
|
|
TAttribute<FSlateIcon>(),
|
|
true);
|
|
|
|
|
|
// Dropdown Menu for choosing your axis snapping for tool movement
|
|
TAttribute<FSlateIcon> AxisSnappingModeIcon;
|
|
AxisSnappingModeIcon.Bind(TAttribute<FSlateIcon>::FGetter::CreateLambda([InEditorPanel] {
|
|
switch (InEditorPanel->GetCurveEditor()->GetAxisSnap().RestrictedAxisList)
|
|
{
|
|
case EAxisList::Type::X:
|
|
return FCurveEditorCommands::Get().SetAxisSnappingHorizontal->GetIcon();
|
|
case EAxisList::Type::Y:
|
|
return FCurveEditorCommands::Get().SetAxisSnappingVertical->GetIcon();
|
|
default: // EKeyGroupMode::None
|
|
return FCurveEditorCommands::Get().SetAxisSnappingNone->GetIcon();
|
|
}
|
|
}));
|
|
|
|
ToolBarBuilder.AddComboButton(
|
|
FUIAction(),
|
|
FOnGetContent::CreateSP(InEditorPanel, &SCurveEditorPanel::MakeAxisSnapMenu),
|
|
LOCTEXT("AxisSnappingOptions", "Axis Snapping"),
|
|
LOCTEXT("AxisSnappingOptionsToolTip", "Choose which axes movement tools are locked to."),
|
|
AxisSnappingModeIcon);
|
|
}
|
|
ToolBarBuilder.EndSection();
|
|
|
|
ToolBarBuilder.BeginSection("Tangents");
|
|
{
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().InterpolationCubicAuto);
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().InterpolationCubicUser);
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().InterpolationCubicBreak);
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().InterpolationLinear);
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().InterpolationConstant);
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().InterpolationToggleWeighted);
|
|
|
|
// We re-use key interpolation checks here, as you can set them under the same conditions.
|
|
FCanExecuteAction CanSetInfinities = FCanExecuteAction::CreateSP(InEditorPanel, &SCurveEditorPanel::CanSetKeyInterpolation);
|
|
|
|
// Dropdown Menu for choosing your pre infinity for selected curves.
|
|
ToolBarBuilder.AddComboButton(
|
|
FUIAction(FExecuteAction(), CanSetInfinities),
|
|
FOnGetContent::CreateSP(InEditorPanel, &SCurveEditorPanel::MakeCurveExtrapolationMenu, false),
|
|
LOCTEXT("CurveEditorPreInfinityOptions", "Pre Infinity"),
|
|
LOCTEXT("CurveEditorPreInfinityOptionsToolTip", "Choose how the curve is evaluated if sampled before the first key"),
|
|
TAttribute<FSlateIcon>(InEditorPanel, &SCurveEditorPanel::GetCurveExtrapolationPreIcon)
|
|
);
|
|
|
|
// Dropdown menu for choosing your post infinity for selected curves
|
|
ToolBarBuilder.AddComboButton(
|
|
FUIAction(FExecuteAction(), CanSetInfinities),
|
|
FOnGetContent::CreateSP(InEditorPanel, &SCurveEditorPanel::MakeCurveExtrapolationMenu, true),
|
|
LOCTEXT("CurveEditorPostInfinityOptions", "Post Infinity"),
|
|
LOCTEXT("CurveEditorPostInfinityOptionsToolTip", "Choose how the curve is evaluated if sampled after the last key"),
|
|
TAttribute<FSlateIcon>(InEditorPanel, &SCurveEditorPanel::GetCurveExtrapolationPostIcon)
|
|
);
|
|
}
|
|
ToolBarBuilder.EndSection();
|
|
|
|
ToolBarBuilder.BeginSection("Filters");
|
|
{
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().FlattenTangents);
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().StraightenTangents);
|
|
ToolBarBuilder.AddToolBarButton(FCurveEditorCommands::Get().OpenUserImplementableFilterWindow);
|
|
}
|
|
ToolBarBuilder.EndSection();
|
|
ToolBarBuilder.EndStyleOverride();
|
|
}
|
|
|
|
};
|
|
|
|
Extender->AddToolBarExtension(
|
|
"Asset",
|
|
EExtensionHook::After,
|
|
GetCommands(),
|
|
FToolBarExtensionDelegate::CreateStatic(&Local::FillToolbar, KeyDetailsView.ToSharedRef(), SharedThis(this))
|
|
);
|
|
|
|
return Extender;
|
|
}
|
|
|
|
TSharedRef<SWidget> SCurveEditorPanel::MakeTimeSnapMenu()
|
|
{
|
|
TSharedRef<SWidget> InputSnapWidget =
|
|
SNew(SFrameRatePicker)
|
|
.Value_Lambda([this] { return this->CurveEditor->InputSnapRateAttribute.Get(); })
|
|
.OnValueChanged_Lambda([this](FFrameRate InFrameRate) { this->CurveEditor->InputSnapRateAttribute = InFrameRate; })
|
|
.PresetValues({
|
|
// We re-use the common frame rates but omit some of them.
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_12(), LOCTEXT("Snap_Input_Twelve", "82ms (1/12s)"), LOCTEXT("Snap_Input_Description_Twelve", "Snap time values to one twelfth of a second (ie: 12fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_15(), LOCTEXT("Snap_Input_Fifteen", "66ms (1/15s)"), LOCTEXT("Snap_Input_Description_Fifteen", "Snap time values to one fifteenth of a second (ie: 15fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_24(), LOCTEXT("Snap_Input_TwentyFour", "42ms (1/24s)"), LOCTEXT("Snap_Input_Description_TwentyFour", "Snap time values to one twenty-fourth of a second (ie: 24fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_25(), LOCTEXT("Snap_Input_TwentyFive", "40ms (1/25s)"), LOCTEXT("Snap_Input_Description_TwentyFive", "Snap time values to one twenty-fifth of a second (ie: 25fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_30(), LOCTEXT("Snap_Input_Thirty", "33ms (1/30s)"), LOCTEXT("Snap_Input_Description_Thirty", "Snap time values to one thirtieth of a second (ie: 30fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_48(), LOCTEXT("Snap_Input_FourtyEight", "21ms (1/48s)"), LOCTEXT("Snap_Input_Description_FourtyEight", "Snap time values to one fourth-eight of a second (ie: 48fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_50(), LOCTEXT("Snap_Input_Fifty", "20ms (1/50s)"), LOCTEXT("Snap_Input_Description_Fifty", "Snap time values to one fiftieth of a second (ie: 50fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_60(), LOCTEXT("Snap_Input_Sixty", "16ms (1/60s)"), LOCTEXT("Snap_Input_Description_Sixty", "Snap time values to one sixtieth of a second (ie: 60fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_100(), LOCTEXT("Snap_Input_OneHundred", "10ms (1/100s)"), LOCTEXT("Snap_Input_Description_OneHundred", "Snap time values to one one-hundredth of a second (ie: 100fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_120(), LOCTEXT("Snap_Input_OneHundredTwenty", "8ms (1/120s)"), LOCTEXT("Snap_Input_Description_OneHundredTwenty", "Snap time values to one one-hundred-twentieth of a second (ie: 120fps)") },
|
|
FCommonFrameRateInfo{ FCommonFrameRates::FPS_240(), LOCTEXT("Snap_Input_TwoHundredFourty", "4ms (1/240s)"), LOCTEXT("Snap_Input_Description_TwoHundredFourty", "Snap time values to one two-hundred-fourtieth of a second (ie: 240fps)") }
|
|
});
|
|
|
|
return InputSnapWidget;
|
|
}
|
|
|
|
FText SCurveEditorPanel::GetTimeSnapMenuTooltip() const
|
|
{
|
|
// If this is specified then the time snap menu is disabled
|
|
if (DisabledTimeSnapTooltipAttribute.IsSet())
|
|
{
|
|
return DisabledTimeSnapTooltipAttribute.Get();
|
|
}
|
|
|
|
return LOCTEXT("TimeSnappingOptionsToolTip", "Choose what precision the Time axis is snapped to while moving keys.");
|
|
}
|
|
|
|
TSharedRef<SWidget> SCurveEditorPanel::MakeGridSpacingMenu()
|
|
{
|
|
TArray<SGridLineSpacingList::FNamedValue> SpacingAmounts;
|
|
// SnapValues.Add( SNumericDropDown<float>::FNamedValue( 0.001f, LOCTEXT( "Snap_OneThousandth", "0.001" ), LOCTEXT( "SnapDescription_OneThousandth", "Set snap to 1/1000th" ) ) );
|
|
//SnapValues.Add( SNumericDropDown<float>::FNamedValue( 0.01f, LOCTEXT( "Snap_OneHundredth", "0.01" ), LOCTEXT( "SnapDescription_OneHundredth", "Set snap to 1/100th" ) ) );
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(0.1f, LOCTEXT("OneTenth", "0.1"), LOCTEXT("Description_OneTenth", "Set grid spacing to 1/10th")));
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(0.5f, LOCTEXT("OneHalf", "0.5"), LOCTEXT("Description_OneHalf", "Set grid spacing to 1/2")));
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(1.0f, LOCTEXT("One", "1"), LOCTEXT("Description_One", "Set grid spacing to 1")));
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(2.0f, LOCTEXT("Two", "2"), LOCTEXT("Description_Two", "Set grid spacing to 2")));
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(5.0f, LOCTEXT("Five", "5"), LOCTEXT("Description_Five", "Set grid spacing to 5")));
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(10.0f, LOCTEXT("Ten", "10"), LOCTEXT("Description_Ten", "Set grid spacing to 10")));
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(50.0f, LOCTEXT("Fifty", "50"), LOCTEXT("Description_50", "Set grid spacing to 50")));
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(100.0f, LOCTEXT("OneHundred", "100"), LOCTEXT("Description_OneHundred", "Set grid spacing to 100")));
|
|
SpacingAmounts.Add(SGridLineSpacingList::FNamedValue(TOptional<float>(), LOCTEXT("Automatic", "Automatic"), LOCTEXT("Description_Automatic", "Set grid spacing to automatic")));
|
|
|
|
TSharedRef<SWidget> OutputSnapWidget =
|
|
SNew(SGridLineSpacingList)
|
|
.DropDownValues(SpacingAmounts)
|
|
.MinDesiredValueWidth(60)
|
|
.Value_Lambda([this]() -> TOptional<float> { return this->CurveEditor->FixedGridSpacingAttribute.Get(); })
|
|
.OnValueChanged_Lambda([this](TOptional<float> InNewOutputSnap) { this->CurveEditor->FixedGridSpacingAttribute = InNewOutputSnap; })
|
|
.HeaderText(LOCTEXT("CurveEditorMenuGridSpacingHeader", "Grid Spacing"));
|
|
|
|
return OutputSnapWidget;
|
|
}
|
|
|
|
TSharedRef<SWidget> SCurveEditorPanel::MakeAxisSnapMenu()
|
|
{
|
|
// This builds the dropdown menu when looking at the Curve View Options combobox.
|
|
FMenuBuilder MenuBuilder(true, GetCommands());
|
|
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetAxisSnappingNone);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetAxisSnappingHorizontal);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetAxisSnappingVertical);
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
TSharedRef<SWidget> SCurveEditorPanel::MakeViewModeMenu()
|
|
{
|
|
// This builds the dropdown menu when looking at the Curve View Modes
|
|
FMenuBuilder MenuBuilder(true, GetCommands());
|
|
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetViewModeAbsolute);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetViewModeStacked);
|
|
MenuBuilder.AddMenuEntry(FCurveEditorCommands::Get().SetViewModeNormalized);
|
|
|
|
return MenuBuilder.MakeWidget();
|
|
}
|
|
|
|
bool SCurveEditorPanel::IsInlineEditPanelEditable() const
|
|
{
|
|
return CurveEditor->GetSelection().Count() > 0;
|
|
}
|
|
|
|
EVisibility SCurveEditorPanel::ShouldInstructionOverlayBeVisible() const
|
|
{
|
|
// The instruction overlay is visible if they have no selection in the tree.
|
|
const bool bCurvesAreVisible = CurveEditor->GetTreeSelection().Num() > 0 || CurveEditor->GetPinnedCurves().Num() > 0;
|
|
return bCurvesAreVisible ? EVisibility::Hidden : EVisibility::HitTestInvisible;
|
|
}
|
|
#undef LOCTEXT_NAMESPACE |