From 0063ec35272e9a5585b9ed75ccbc3c7dc052b9ae Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 24 Aug 2021 17:14:41 +0200 Subject: [PATCH] Add shared rectangle selection for all timeline tracks to select keyframes #519 --- Source/Editor/GUI/CurveEditor.Base.cs | 22 ++- Source/Editor/GUI/CurveEditor.Contents.cs | 105 +++++++++---- Source/Editor/GUI/CurveEditor.cs | 53 ++++++- Source/Editor/GUI/IKeyframesEditor.cs | 44 ++++++ Source/Editor/GUI/IKeyframesEditorContext.cs | 39 +++++ .../GUI/Timeline/GUI/KeyframesEditor.cs | 144 +++++++++++------- Source/Editor/GUI/Timeline/Timeline.cs | 46 +++++- .../Editor/GUI/Timeline/Tracks/AudioTrack.cs | 33 +++- .../GUI/Timeline/Tracks/CurvePropertyTrack.cs | 35 ++++- .../Editor/GUI/Timeline/Tracks/EventTrack.cs | 33 +++- .../Timeline/Tracks/KeyframesPropertyTrack.cs | 33 +++- 11 files changed, 493 insertions(+), 94 deletions(-) create mode 100644 Source/Editor/GUI/IKeyframesEditor.cs create mode 100644 Source/Editor/GUI/IKeyframesEditorContext.cs diff --git a/Source/Editor/GUI/CurveEditor.Base.cs b/Source/Editor/GUI/CurveEditor.Base.cs index 7891eacb1..1cdd72761 100644 --- a/Source/Editor/GUI/CurveEditor.Base.cs +++ b/Source/Editor/GUI/CurveEditor.Base.cs @@ -10,7 +10,7 @@ namespace FlaxEditor.GUI /// The base class for editors. Allows to use generic curve editor without type information at compile-time. /// [HideInEditor] - public abstract class CurveEditorBase : ContainerControl + public abstract class CurveEditorBase : ContainerControl, IKeyframesEditor { /// /// The UI use mode flags. @@ -124,6 +124,11 @@ namespace FlaxEditor.GUI /// public abstract int KeyframesCount { get; } + /// + /// Clears the selection. + /// + public abstract void ClearSelection(); + /// /// Called when curve gets edited. /// @@ -256,5 +261,20 @@ namespace FlaxEditor.GUI (mode & UseMode.Vertical) == UseMode.Vertical ? value.Y : defaultValue.Y ); } + + /// + public IKeyframesEditorContext KeyframesEditorContext { get; set; } + + /// + public abstract void OnKeyframesDeselect(IKeyframesEditor editor); + + /// + public abstract void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection); + + /// + public abstract int OnKeyframesSelectionCount(); + + /// + public abstract void OnKeyframesDelete(IKeyframesEditor editor); } } diff --git a/Source/Editor/GUI/CurveEditor.Contents.cs b/Source/Editor/GUI/CurveEditor.Contents.cs index 96b796f9f..c914e6c25 100644 --- a/Source/Editor/GUI/CurveEditor.Contents.cs +++ b/Source/Editor/GUI/CurveEditor.Contents.cs @@ -40,7 +40,14 @@ namespace FlaxEditor.GUI private void UpdateSelectionRectangle() { var selectionRect = Rectangle.FromPoints(_leftMouseDownPos, _mousePos); + if (_editor.KeyframesEditorContext != null) + _editor.KeyframesEditorContext.OnKeyframesSelection(_editor, this, selectionRect); + else + UpdateSelection(ref selectionRect); + } + internal void UpdateSelection(ref Rectangle selectionRect) + { // Find controls to select for (int i = 0; i < Children.Count; i++) { @@ -49,7 +56,6 @@ namespace FlaxEditor.GUI p.IsSelected = p.Bounds.Intersects(ref selectionRect); } } - _editor.UpdateTangents(); } @@ -74,6 +80,21 @@ namespace FlaxEditor.GUI { _mousePos = location; + // Start moving selection if movement started from the keyframe + if (_leftMouseDown && !_isMovingSelection && GetChildAt(_leftMouseDownPos) is KeyframePoint) + { + // Start moving selected nodes + _isMovingSelection = true; + _movedKeyframes = false; + var viewRect = _editor._mainPanel.GetClientArea(); + _movingSelectionStart = PointToKeyframes(location, ref viewRect); + if (_movingSelectionOffsets == null || _movingSelectionOffsets.Length != _editor._points.Count) + _movingSelectionOffsets = new Vector2[_editor._points.Count]; + for (int i = 0; i < _movingSelectionOffsets.Length; i++) + _movingSelectionOffsets[i] = _editor._points[i].Point - _movingSelectionStart; + _editor.OnEditingStart(); + } + // Moving view if (_rightMouseDown) { @@ -256,30 +277,32 @@ namespace FlaxEditor.GUI // Check if user is pressing control if (Root.GetKey(KeyboardKeys.Control)) { - // Add to selection - keyframe.IsSelected = true; + // Toggle selection + keyframe.IsSelected = !keyframe.IsSelected; _editor.UpdateTangents(); } // Check if node isn't selected else if (!keyframe.IsSelected) { // Select node - _editor.ClearSelection(); + if (_editor.KeyframesEditorContext != null) + _editor.KeyframesEditorContext.OnKeyframesDeselect(_editor); + else + _editor.ClearSelection(); keyframe.IsSelected = true; _editor.UpdateTangents(); } - - // Start moving selected nodes + if (_editor.ShowCollapsed) + { + // Synchronize selection for curve points when collapsed so all points fo the keyframe are selected + for (var i = 0; i < _editor._points.Count; i++) + { + var p = _editor._points[i]; + if (p.Index == keyframe.Index) + p.IsSelected = keyframe.IsSelected; + } + } StartMouseCapture(); - _isMovingSelection = true; - _movedKeyframes = false; - var viewRect = _editor._mainPanel.GetClientArea(); - _movingSelectionStart = PointToKeyframes(location, ref viewRect); - if (_movingSelectionOffsets == null || _movingSelectionOffsets.Length != _editor._points.Count) - _movingSelectionOffsets = new Vector2[_editor._points.Count]; - for (int i = 0; i < _movingSelectionOffsets.Length; i++) - _movingSelectionOffsets[i] = _editor._points[i].Point - _movingSelectionStart; - _editor.OnEditingStart(); Focus(); Tooltip?.Hide(); return true; @@ -306,7 +329,10 @@ namespace FlaxEditor.GUI { // Start selecting StartMouseCapture(); - _editor.ClearSelection(); + if (_editor.KeyframesEditorContext != null) + _editor.KeyframesEditorContext.OnKeyframesDeselect(_editor); + else + _editor.ClearSelection(); _editor.UpdateTangents(); Focus(); return true; @@ -354,11 +380,6 @@ namespace FlaxEditor.GUI _editor.OnEditingEnd(); } } - // Selecting - else - { - UpdateSelectionRectangle(); - } _isMovingSelection = false; _isMovingTangent = false; @@ -388,18 +409,15 @@ namespace FlaxEditor.GUI var cm = new ContextMenu.ContextMenu(); cm.AddButton("Add keyframe", () => _editor.AddKeyframe(_cmShowPos)).Enabled = _editor.KeyframesCount < _editor.MaxKeyframes; - if (selectionCount == 0) + if (selectionCount > 0) { + cm.AddButton(selectionCount == 1 ? "Edit keyframe" : "Edit keyframes", () => _editor.EditKeyframes(this, location)); } - else if (selectionCount == 1) + var totalSelectionCount = _editor.KeyframesEditorContext?.OnKeyframesSelectionCount() ?? selectionCount; + Debug.Log(totalSelectionCount); + if (totalSelectionCount > 0) { - cm.AddButton("Edit keyframe", () => _editor.EditKeyframes(this, location)); - cm.AddButton("Remove keyframe", _editor.RemoveKeyframes); - } - else - { - cm.AddButton("Edit keyframes", () => _editor.EditKeyframes(this, location)); - cm.AddButton("Remove keyframes", _editor.RemoveKeyframes); + cm.AddButton(totalSelectionCount == 1 ? "Remove keyframe" : "Remove keyframes", _editor.RemoveKeyframes); } cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location)); if (_editor.EnableZoom != UseMode.Off || _editor.EnablePanning != UseMode.Off) @@ -464,5 +482,32 @@ namespace FlaxEditor.GUI ); } } + + /// + public override void OnKeyframesDeselect(IKeyframesEditor editor) + { + ClearSelection(); + } + + /// + public override void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection) + { + if (_points.Count == 0) + return; + var selectionRect = Rectangle.FromPoints(_contents.PointFromParent(control, selection.UpperLeft), _contents.PointFromParent(control, selection.BottomRight)); + _contents.UpdateSelection(ref selectionRect); + } + + /// + public override int OnKeyframesSelectionCount() + { + return SelectionCount; + } + + /// + public override void OnKeyframesDelete(IKeyframesEditor editor) + { + RemoveKeyframesInner(); + } } } diff --git a/Source/Editor/GUI/CurveEditor.cs b/Source/Editor/GUI/CurveEditor.cs index b1b0a5a52..edbff94b5 100644 --- a/Source/Editor/GUI/CurveEditor.cs +++ b/Source/Editor/GUI/CurveEditor.cs @@ -394,7 +394,24 @@ namespace FlaxEditor.GUI UpdateKeyframes(); UpdateTangents(); if (value) + { + // Synchronize selection for curve points when collapsed so all points fo the keyframe are selected + for (var i = 0; i < _points.Count; i++) + { + var p = _points[i]; + if (p.IsSelected) + { + for (var j = 0; j < _points.Count; j++) + { + var q = _points[j]; + if (q.Index == p.Index) + q.IsSelected = true; + } + } + } + ShowWholeCurve(); + } } } @@ -590,6 +607,16 @@ namespace FlaxEditor.GUI private void RemoveKeyframes() { + if (KeyframesEditorContext != null) + KeyframesEditorContext.OnKeyframesDelete(this); + else + RemoveKeyframesInner(); + } + + private void RemoveKeyframesInner() + { + if (SelectionCount == 0) + return; var indicesToRemove = new HashSet(); for (int i = 0; i < _points.Count; i++) { @@ -600,8 +627,6 @@ namespace FlaxEditor.GUI indicesToRemove.Add(p.Index); } } - if (indicesToRemove.Count == 0) - return; OnEditingStart(); RemoveKeyframesInternal(indicesToRemove); @@ -630,14 +655,24 @@ namespace FlaxEditor.GUI get { int result = 0; - for (int i = 0; i < _points.Count; i++) - if (_points[i].IsSelected) - result++; + if (ShowCollapsed) + { + for (int i = 0; i < _points.Count; i++) + if (_points[i].Component == 0 && _points[i].IsSelected) + result++; + } + else + { + for (int i = 0; i < _points.Count; i++) + if (_points[i].IsSelected) + result++; + } return result; } } - private void ClearSelection() + /// + public override void ClearSelection() { for (int i = 0; i < _points.Count; i++) { @@ -645,7 +680,10 @@ namespace FlaxEditor.GUI } } - private void SelectAll() + /// + /// Selects all keyframes. + /// + public void SelectAll() { for (int i = 0; i < _points.Count; i++) { @@ -888,6 +926,7 @@ namespace FlaxEditor.GUI TickSteps = null; _tickStrengths = null; KeyframesChanged = null; + KeyframesEditorContext = null; base.OnDestroy(); } diff --git a/Source/Editor/GUI/IKeyframesEditor.cs b/Source/Editor/GUI/IKeyframesEditor.cs new file mode 100644 index 000000000..f23be8dd1 --- /dev/null +++ b/Source/Editor/GUI/IKeyframesEditor.cs @@ -0,0 +1,44 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.GUI +{ + /// + /// Interface for keyframes/curves editors. + /// + public interface IKeyframesEditor + { + /// + /// Gets or sets the keyframes editor collection used by this editor. + /// + IKeyframesEditorContext KeyframesEditorContext { get; set; } + + /// + /// Called when keyframes selection should be cleared for editor. + /// + /// The source editor. + void OnKeyframesDeselect(IKeyframesEditor editor); + + /// + /// Called when keyframes selection rectangle gets updated. + /// + /// The source editor. + /// The source selection control. + /// The source selection rectangle (in source control local space). + void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection); + + /// + /// Called to calculate the total amount of selected keyframes in the editor. + /// + /// The selected keyframes amount. + int OnKeyframesSelectionCount(); + + /// + /// Called when keyframes selection should be deleted for all editors. + /// + /// The source editor. + void OnKeyframesDelete(IKeyframesEditor editor); + } +} diff --git a/Source/Editor/GUI/IKeyframesEditorContext.cs b/Source/Editor/GUI/IKeyframesEditorContext.cs new file mode 100644 index 000000000..ed4ee3676 --- /dev/null +++ b/Source/Editor/GUI/IKeyframesEditorContext.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.GUI +{ + /// + /// Interface for context for collection of . + /// + public interface IKeyframesEditorContext + { + /// + /// Called when keyframes selection should be cleared for all editors. + /// + /// The source editor. + void OnKeyframesDeselect(IKeyframesEditor editor); + + /// + /// Called when keyframes selection rectangle gets updated. + /// + /// The source editor. + /// The source selection control. + /// The source selection rectangle (in source control local space). + void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection); + + /// + /// Called to calculate the total amount of selected keyframes in the editor. + /// + /// The selected keyframes amount. + int OnKeyframesSelectionCount(); + + /// + /// Called when keyframes selection should be deleted for all editors. + /// + /// The source editor. + void OnKeyframesDelete(IKeyframesEditor editor); + } +} diff --git a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs index 6942b0fc0..ecec528ad 100644 --- a/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs +++ b/Source/Editor/GUI/Timeline/GUI/KeyframesEditor.cs @@ -15,7 +15,7 @@ namespace FlaxEditor.GUI /// /// [HideInEditor] - public class KeyframesEditor : ContainerControl + public class KeyframesEditor : ContainerControl, IKeyframesEditor { /// /// A single keyframe. @@ -91,7 +91,14 @@ namespace FlaxEditor.GUI private void UpdateSelectionRectangle() { var selectionRect = Rectangle.FromPoints(_leftMouseDownPos, _mousePos); + if (_editor.KeyframesEditorContext != null) + _editor.KeyframesEditorContext.OnKeyframesSelection(_editor, this, selectionRect); + else + UpdateSelection(ref selectionRect); + } + internal void UpdateSelection(ref Rectangle selectionRect) + { // Find controls to select for (int i = 0; i < Children.Count; i++) { @@ -123,6 +130,21 @@ namespace FlaxEditor.GUI { _mousePos = location; + // Start moving selection if movement started from the keyframe + if (_leftMouseDown && !_isMovingSelection && GetChildAt(_leftMouseDownPos) is KeyframePoint) + { + // Start moving selected nodes + _isMovingSelection = true; + _movedKeyframes = false; + var viewRect = _editor._mainPanel.GetClientArea(); + _movingSelectionStart = PointToKeyframes(location, ref viewRect).X; + if (_movingSelectionOffsets == null || _movingSelectionOffsets.Length != _editor._keyframes.Count) + _movingSelectionOffsets = new float[_editor._keyframes.Count]; + for (int i = 0; i < _movingSelectionOffsets.Length; i++) + _movingSelectionOffsets[i] = _editor._keyframes[i].Time - _movingSelectionStart; + _editor.OnEditingStart(); + } + // Moving view if (_rightMouseDown) { @@ -248,28 +270,20 @@ namespace FlaxEditor.GUI // Check if user is pressing control if (Root.GetKey(KeyboardKeys.Control)) { - // Add to selection - keyframe.Select(); + // Toggle selection + keyframe.IsSelected = !keyframe.IsSelected; } // Check if node isn't selected else if (!keyframe.IsSelected) { // Select node - _editor.ClearSelection(); - keyframe.Select(); + if (_editor.KeyframesEditorContext != null) + _editor.KeyframesEditorContext.OnKeyframesDeselect(_editor); + else + _editor.ClearSelection(); + keyframe.IsSelected = true; } - - // Start moving selected nodes StartMouseCapture(); - _isMovingSelection = true; - _movedKeyframes = false; - var viewRect = _editor._mainPanel.GetClientArea(); - _movingSelectionStart = PointToKeyframes(location, ref viewRect).X; - if (_movingSelectionOffsets == null || _movingSelectionOffsets.Length != _editor._keyframes.Count) - _movingSelectionOffsets = new float[_editor._keyframes.Count]; - for (int i = 0; i < _movingSelectionOffsets.Length; i++) - _movingSelectionOffsets[i] = _editor._keyframes[i].Time - _movingSelectionStart; - _editor.OnEditingStart(); Focus(); Tooltip?.Hide(); return true; @@ -281,7 +295,10 @@ namespace FlaxEditor.GUI { // Start selecting StartMouseCapture(); - _editor.ClearSelection(); + if (_editor.KeyframesEditorContext != null) + _editor.KeyframesEditorContext.OnKeyframesDeselect(_editor); + else + _editor.ClearSelection(); Focus(); return true; } @@ -319,11 +336,6 @@ namespace FlaxEditor.GUI _editor.UpdateKeyframes(); } } - // Selecting - else - { - UpdateSelectionRectangle(); - } _isMovingSelection = false; _movedKeyframes = false; @@ -343,7 +355,7 @@ namespace FlaxEditor.GUI { // Select node selectionCount = 1; - point.Select(); + point.IsSelected = true; } var viewRect = _editor._mainPanel.GetClientArea(); @@ -351,18 +363,15 @@ namespace FlaxEditor.GUI var cm = new ContextMenu.ContextMenu(); cm.AddButton("Add keyframe", () => _editor.AddKeyframe(_cmShowPos)).Enabled = _editor.Keyframes.Count < _editor.MaxKeyframes; - if (selectionCount == 0) + if (selectionCount > 0) { + cm.AddButton(selectionCount == 1 ? "Edit keyframe" : "Edit keyframes", () => _editor.EditKeyframes(this, location)); } - else if (selectionCount == 1) + var totalSelectionCount = _editor.KeyframesEditorContext?.OnKeyframesSelectionCount() ?? selectionCount; + Debug.Log(totalSelectionCount); + if (totalSelectionCount > 0) { - cm.AddButton("Edit keyframe", () => _editor.EditKeyframes(this, location)); - cm.AddButton("Remove keyframe", _editor.RemoveKeyframes); - } - else - { - cm.AddButton("Edit keyframes", () => _editor.EditKeyframes(this, location)); - cm.AddButton("Remove keyframes", _editor.RemoveKeyframes); + cm.AddButton(totalSelectionCount == 1 ? "Remove keyframe" : "Remove keyframes", _editor.RemoveKeyframes); } cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location)); if (_editor.EnableZoom && _editor.EnablePanning) @@ -482,16 +491,6 @@ namespace FlaxEditor.GUI return false; } - public void Select() - { - IsSelected = true; - } - - public void Deselect() - { - IsSelected = false; - } - /// /// Updates the tooltip. /// @@ -980,7 +979,16 @@ namespace FlaxEditor.GUI private void RemoveKeyframes() { - bool edited = false; + if (KeyframesEditorContext != null) + KeyframesEditorContext.OnKeyframesDelete(this); + else + RemoveKeyframesInner(); + } + + private void RemoveKeyframesInner() + { + if (SelectionCount == 0) + return; var keyframes = new Dictionary(_keyframes.Count); for (int i = 0; i < _points.Count; i++) { @@ -991,12 +999,9 @@ namespace FlaxEditor.GUI } else { - p.Deselect(); - edited = true; + p.IsSelected = false; } } - if (!edited) - return; OnEditingStart(); _keyframes.Clear(); @@ -1088,19 +1093,25 @@ namespace FlaxEditor.GUI } } - private void ClearSelection() + /// + /// Clears the selection. + /// + public void ClearSelection() { for (int i = 0; i < _points.Count; i++) { - _points[i].Deselect(); + _points[i].IsSelected = false; } } - private void SelectAll() + /// + /// Selects all keyframes. + /// + public void SelectAll() { for (int i = 0; i < _points.Count; i++) { - _points[i].Select(); + _points[i].IsSelected = true; } } @@ -1182,8 +1193,39 @@ namespace FlaxEditor.GUI // Cleanup _points.Clear(); _keyframes.Clear(); + KeyframesEditorContext = null; base.OnDestroy(); } + + /// + public IKeyframesEditorContext KeyframesEditorContext { get; set; } + + /// + public void OnKeyframesDeselect(IKeyframesEditor editor) + { + ClearSelection(); + } + + /// + public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection) + { + if (_keyframes.Count == 0) + return; + var selectionRect = Rectangle.FromPoints(_contents.PointFromParent(control, selection.UpperLeft), _contents.PointFromParent(control, selection.BottomRight)); + _contents.UpdateSelection(ref selectionRect); + } + + /// + public int OnKeyframesSelectionCount() + { + return SelectionCount; + } + + /// + public void OnKeyframesDelete(IKeyframesEditor editor) + { + RemoveKeyframesInner(); + } } } diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs index 7ded468e7..21b936c75 100644 --- a/Source/Editor/GUI/Timeline/Timeline.cs +++ b/Source/Editor/GUI/Timeline/Timeline.cs @@ -21,7 +21,7 @@ namespace FlaxEditor.GUI.Timeline /// /// [HideInEditor] - public class Timeline : ContainerControl + public class Timeline : ContainerControl, IKeyframesEditorContext { private static readonly KeyValuePair[] FPSValues = { @@ -2393,5 +2393,49 @@ namespace FlaxEditor.GUI.Timeline base.OnDestroy(); } + + /// + public void OnKeyframesDeselect(IKeyframesEditor editor) + { + for (int i = 0; i < _tracks.Count; i++) + { + if (_tracks[i] is IKeyframesEditorContext trackContext) + trackContext.OnKeyframesDeselect(editor); + } + } + + /// + public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection) + { + var globalControl = _backgroundArea; + var globalRect = Rectangle.FromPoints(control.PointToParent(globalControl, selection.UpperLeft), control.PointToParent(globalControl, selection.BottomRight)); + for (int i = 0; i < _tracks.Count; i++) + { + if (_tracks[i] is IKeyframesEditorContext trackContext) + trackContext.OnKeyframesSelection(editor, globalControl, globalRect); + } + } + + /// + public int OnKeyframesSelectionCount() + { + int result = 0; + for (int i = 0; i < _tracks.Count; i++) + { + if (_tracks[i] is IKeyframesEditorContext trackContext) + result += trackContext.OnKeyframesSelectionCount(); + } + return result; + } + + /// + public void OnKeyframesDelete(IKeyframesEditor editor) + { + for (int i = 0; i < _tracks.Count; i++) + { + if (_tracks[i] is IKeyframesEditorContext trackContext) + trackContext.OnKeyframesDelete(editor); + } + } } } diff --git a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs index 74e9483e8..6b7185d65 100644 --- a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs @@ -225,7 +225,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks /// The child volume track for audio track. Used to animate audio volume over time. /// /// - class AudioVolumeTrack : Track + class AudioVolumeTrack : Track, IKeyframesEditorContext { /// /// Gets the archetype. @@ -478,7 +478,11 @@ namespace FlaxEditor.GUI.Timeline.Tracks return; Curve.Visible = Visible; if (!Visible) + { + Curve.ClearSelection(); return; + } + Curve.KeyframesEditorContext = Timeline; Curve.CustomViewPanning = Timeline.OnKeyframesViewPanning; Curve.Bounds = new Rectangle(_audioMedia.X, Y + 1.0f, _audioMedia.Width, Height - 2.0f); @@ -639,5 +643,32 @@ namespace FlaxEditor.GUI.Timeline.Tracks base.OnDestroy(); } + + /// + public void OnKeyframesDeselect(IKeyframesEditor editor) + { + if (Curve != null && Curve.Visible) + Curve.OnKeyframesDeselect(editor); + } + + /// + public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection) + { + if (Curve != null && Curve.Visible) + Curve.OnKeyframesSelection(editor, control, selection); + } + + /// + public int OnKeyframesSelectionCount() + { + return Curve != null && Curve.Visible ? Curve.OnKeyframesSelectionCount() : 0; + } + + /// + public void OnKeyframesDelete(IKeyframesEditor editor) + { + if (Curve != null && Curve.Visible) + Curve.OnKeyframesDelete(editor); + } } } diff --git a/Source/Editor/GUI/Timeline/Tracks/CurvePropertyTrack.cs b/Source/Editor/GUI/Timeline/Tracks/CurvePropertyTrack.cs index 251a3a76e..47d077ece 100644 --- a/Source/Editor/GUI/Timeline/Tracks/CurvePropertyTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/CurvePropertyTrack.cs @@ -15,7 +15,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks /// The timeline track for animating object property via Curve. /// /// - public abstract class CurvePropertyTrackBase : MemberTrack + public abstract class CurvePropertyTrackBase : MemberTrack, IKeyframesEditorContext { private sealed class Splitter : Control { @@ -226,8 +226,12 @@ namespace FlaxEditor.GUI.Timeline.Tracks return; Curve.Visible = Visible; if (!Visible) + { + Curve.ClearSelection(); return; + } var expanded = IsExpanded; + Curve.KeyframesEditorContext = Timeline; Curve.CustomViewPanning = Timeline.OnKeyframesViewPanning; Curve.Bounds = new Rectangle(Timeline.StartOffset, Y + 1.0f, Timeline.Duration * Timeline.UnitsPerSecond * Timeline.Zoom, Height - 2.0f); Curve.ViewScale = new Vector2(Timeline.Zoom, Curve.ViewScale.Y); @@ -240,11 +244,13 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (expanded) { if (_splitter == null) + { _splitter = new Splitter { _track = this, Parent = Curve, }; + } var splitterHeight = 4.0f; _splitter.Bounds = new Rectangle(0, Curve.Height - splitterHeight, Curve.Width, splitterHeight); } @@ -424,6 +430,33 @@ namespace FlaxEditor.GUI.Timeline.Tracks base.OnDestroy(); } + + /// + public void OnKeyframesDeselect(IKeyframesEditor editor) + { + if (Curve != null && Curve.Visible) + Curve.OnKeyframesDeselect(editor); + } + + /// + public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection) + { + if (Curve != null && Curve.Visible) + Curve.OnKeyframesSelection(editor, control, selection); + } + + /// + public int OnKeyframesSelectionCount() + { + return Curve != null && Curve.Visible ? Curve.OnKeyframesSelectionCount() : 0; + } + + /// + public void OnKeyframesDelete(IKeyframesEditor editor) + { + if (Curve != null && Curve.Visible) + Curve.OnKeyframesDelete(editor); + } } /// diff --git a/Source/Editor/GUI/Timeline/Tracks/EventTrack.cs b/Source/Editor/GUI/Timeline/Tracks/EventTrack.cs index 255f23bb3..736d8c3c7 100644 --- a/Source/Editor/GUI/Timeline/Tracks/EventTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/EventTrack.cs @@ -16,7 +16,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks /// The timeline track for invoking events on a certain points in the time. /// /// - public class EventTrack : MemberTrack + public class EventTrack : MemberTrack, IKeyframesEditorContext { /// /// Gets the archetype. @@ -285,7 +285,11 @@ namespace FlaxEditor.GUI.Timeline.Tracks return; Events.Visible = Visible; if (!Visible) + { + Events.ClearSelection(); return; + } + Events.KeyframesEditorContext = Timeline; Events.CustomViewPanning = Timeline.OnKeyframesViewPanning; Events.Bounds = new Rectangle(Timeline.StartOffset, Y + 1.0f, Timeline.Duration * Timeline.UnitsPerSecond * Timeline.Zoom, Height - 2.0f); Events.ViewScale = new Vector2(Timeline.Zoom, 1.0f); @@ -407,5 +411,32 @@ namespace FlaxEditor.GUI.Timeline.Tracks base.OnDestroy(); } + + /// + public void OnKeyframesDeselect(IKeyframesEditor editor) + { + if (Events != null && Events.Visible) + Events.OnKeyframesDeselect(editor); + } + + /// + public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection) + { + if (Events != null && Events.Visible) + Events.OnKeyframesSelection(editor, control, selection); + } + + /// + public int OnKeyframesSelectionCount() + { + return Events != null && Events.Visible ? Events.OnKeyframesSelectionCount() : 0; + } + + /// + public void OnKeyframesDelete(IKeyframesEditor editor) + { + if (Events != null && Events.Visible) + Events.OnKeyframesDelete(editor); + } } } diff --git a/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs b/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs index 2c5490f62..e760f6ea1 100644 --- a/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/KeyframesPropertyTrack.cs @@ -15,7 +15,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks /// The timeline track for animating object property via keyframes collection. /// /// - public class KeyframesPropertyTrack : MemberTrack + public class KeyframesPropertyTrack : MemberTrack, IKeyframesEditorContext { /// /// Gets the archetype. @@ -252,7 +252,11 @@ namespace FlaxEditor.GUI.Timeline.Tracks return; Keyframes.Visible = Visible; if (!Visible) + { + Keyframes.ClearSelection(); return; + } + Keyframes.KeyframesEditorContext = Timeline; Keyframes.CustomViewPanning = Timeline.OnKeyframesViewPanning; Keyframes.Bounds = new Rectangle(Timeline.StartOffset, Y + 1.0f, Timeline.Duration * Timeline.UnitsPerSecond * Timeline.Zoom, Height - 2.0f); Keyframes.ViewScale = new Vector2(Timeline.Zoom, 1.0f); @@ -387,5 +391,32 @@ namespace FlaxEditor.GUI.Timeline.Tracks base.OnDestroy(); } + + /// + public void OnKeyframesDeselect(IKeyframesEditor editor) + { + if (Keyframes != null && Keyframes.Visible) + Keyframes.OnKeyframesDeselect(editor); + } + + /// + public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection) + { + if (Keyframes != null && Keyframes.Visible) + Keyframes.OnKeyframesSelection(editor, control, selection); + } + + /// + public int OnKeyframesSelectionCount() + { + return Keyframes != null && Keyframes.Visible ? Keyframes.OnKeyframesSelectionCount() : 0; + } + + /// + public void OnKeyframesDelete(IKeyframesEditor editor) + { + if (Keyframes != null && Keyframes.Visible) + Keyframes.OnKeyframesDelete(editor); + } } }