#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; }