#nullable enable
using T3.Core.Animation;
using T3.Core.DataTypes;
using T3.Core.Operator;
using T3.Editor.Gui.Interaction.WithCurves;
namespace T3.Editor.Gui.Windows.TimeLine;
///
/// Links to AnimationParameters to editors like DopeSheets or >
///
internal abstract class AnimationParameterEditing : CurveEditing
{
protected AnimationParameterEditing(VersionedKeyframeSet sharedSelection) : base(sharedSelection) { }
protected override IEnumerable GetAllCurves()
{
foreach (TimeLineCanvas.AnimationParameter param in AnimationParameters)
{
foreach (var curve in param.Curves)
{
yield return curve;
}
}
}
///
/// For some operations like copy and paste between curves, we need more context.
///
protected override IEnumerable GetAllCurvesWithDetails()
{
foreach (var param in AnimationParameters)
{
var index = 0;
foreach (var curve in param.Curves)
{
yield return new KeyframeCopyAndPasting.CurveWithDetails(curve, param.Instance.SymbolChildId, param.Input.Id, index++);
}
}
}
protected override void PasteKeyframes()
{
if (!KeyframeCopyAndPasting.TryPasteTo(AnimationParameters, out var newKeyframes))
return;
RebuildCurveTables();
SelectedKeyframes.Clear();
SelectedKeyframes.UnionWith(newKeyframes);
}
protected override void DeleteSelectedKeyframes(Instance composition)
{
TimeLineCanvas.DeleteSelectedElements(composition);
}
public TimeRange GetSelectionTimeRange()
{
var timeRange = TimeRange.Undefined;
foreach (var s in SelectedKeyframes)
{
timeRange.Unite((float)s.U);
}
return timeRange;
}
public void UpdateDragStretchCommand(double scaleU, double scaleV, double originU, double originV)
{
foreach (var vDefinition in SelectedKeyframes)
{
vDefinition.U = originU + (vDefinition.U - originU) * scaleU;
}
RebuildCurveTables();
}
protected override void ViewAllOrSelectedKeys(bool alsoChangeTimeRange = false)
{
var hasSomeKeys = TryGetBoundsOnCanvas(GetSelectedOrAllPoints(), out var bounds);
if (this is DopeSheetArea dopeSheet)
{
if (dopeSheet.TimeLineCanvas.ClipArea.TryGetBounds(out var clipBounds, !hasSomeKeys))
{
if (hasSomeKeys)
{
bounds.Min.X = MathF.Min(bounds.Min.X, clipBounds.Min.X);
bounds.Max.X = MathF.Max(bounds.Max.X, clipBounds.Max.X);
}
else
{
bounds = clipBounds;
}
}
}
// useStoredWindowSize: this also runs from the timeline context-menu "View All" (inside a popup), where
// the live ImGui window is the menu, not the canvas — using the canvas's own size keeps the fit correct.
TimeLineCanvas.Current?.SetScopeToCanvasArea(bounds, flipY: true, 300, 50, useStoredWindowSize: true);
}
//
// Selection helpers — shared by DopeSheetArea and TimelineCurveEditor so the timeline's
// SelectionRangeIndicator / TimeSelectionArea can operate in either mode.
//
public bool IsKeyframeSelected(VDefinition v) => SelectedKeyframes.Contains(v);
public int SelectionChangeCounter => SelectedKeyframes.ChangeCounter;
public int SelectedKeyframeCount => SelectedKeyframes.Count;
public IEnumerable EnumerateSelectedKeyframes() => SelectedKeyframes;
public IEnumerable EnumerateAllKeyframes() => GetAllKeyframes();
public void CopyAllCurvesTo(List buffer)
{
buffer.Clear();
foreach (var curve in GetAllCurves())
buffer.Add(curve);
}
public void CopyKeyframeSelectionTo(List buffer)
{
buffer.Clear();
foreach (var v in SelectedKeyframes)
buffer.Add(v);
}
public TimeRange GetAllKeyframesTimeRange()
{
var range = TimeRange.Undefined;
foreach (var v in GetAllKeyframes())
range.Unite((float)v.U);
return range;
}
public void ReplaceKeyframeSelection(IEnumerable keys)
{
SelectedKeyframes.Clear();
SelectedKeyframes.UnionWith(keys);
TimeLineCanvas.Current?.OnKeyframeSelectionReplaced();
}
public void AddToKeyframeSelection(IReadOnlyList keys)
{
for (var i = 0; i < keys.Count; i++)
SelectedKeyframes.Add(keys[i]);
}
public void RemoveFromKeyframeSelection(IReadOnlyList keys)
{
for (var i = 0; i < keys.Count; i++)
SelectedKeyframes.Remove(keys[i]);
}
public void SelectAllKeyframes()
{
SelectedKeyframes.Clear();
SelectedKeyframes.UnionWith(GetAllKeyframes());
}
/// Shifts the given keyframes in time and rebuilds curve tables. Caller owns undo wrapping.
public void ApplyKeyframeTimeOffset(IReadOnlyList keys, double deltaU)
{
for (var i = 0; i < keys.Count; i++)
keys[i].U += deltaU;
RebuildCurveTables();
}
/// Public pass-through to the protected
/// so that can trigger a rebuild on this editor.
public void RebuildCurves() => RebuildCurveTables();
protected List AnimationParameters = [];
protected TimeLineCanvas TimeLineCanvas = null!; // This gets initialized in constructor of implementations
public static bool CurvesTablesNeedsRefresh;
}