diff --git a/Source/Editor/GUI/Timeline/Timeline.Data.cs b/Source/Editor/GUI/Timeline/Timeline.Data.cs
new file mode 100644
index 000000000..e1a4b69a7
--- /dev/null
+++ b/Source/Editor/GUI/Timeline/Timeline.Data.cs
@@ -0,0 +1,230 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using System;
+using System.IO;
+using FlaxEngine;
+
+namespace FlaxEditor.GUI.Timeline
+{
+ partial class Timeline
+ {
+ ///
+ /// Loads the timeline data.
+ ///
+ /// The version.
+ /// The input stream.
+ protected virtual void LoadTimelineData(int version, BinaryReader stream)
+ {
+ }
+
+ ///
+ /// Saves the timeline data.
+ ///
+ /// The output stream.
+ protected virtual void SaveTimelineData(BinaryWriter stream)
+ {
+ }
+
+ ///
+ /// Loads the timeline data after reading the timeline tracks.
+ ///
+ /// The version.
+ /// The input stream.
+ protected virtual void LoadTimelineCustomData(int version, BinaryReader stream)
+ {
+ }
+
+ ///
+ /// Saves the timeline data after saving the timeline tracks.
+ ///
+ /// The output stream.
+ protected virtual void SaveTimelineCustomData(BinaryWriter stream)
+ {
+ }
+
+ ///
+ /// Loads the timeline from the specified data.
+ ///
+ /// The data.
+ public virtual void Load(byte[] data)
+ {
+ Profiler.BeginEvent("Clear");
+ Clear();
+ LockChildrenRecursive();
+ Profiler.EndEvent();
+
+ using (var memory = new MemoryStream(data))
+ using (var stream = new BinaryReader(memory))
+ {
+ Profiler.BeginEvent("LoadData");
+ int version = stream.ReadInt32();
+ switch (version)
+ {
+ case 1:
+ {
+ // [Deprecated on 23.07.2019, expires on 27.04.2021]
+
+ // Load properties
+ FramesPerSecond = stream.ReadSingle();
+ DurationFrames = stream.ReadInt32();
+ LoadTimelineData(version, stream);
+
+ // Load tracks
+ int tracksCount = stream.ReadInt32();
+ _tracks.Capacity = Math.Max(_tracks.Capacity, tracksCount);
+ for (int i = 0; i < tracksCount; i++)
+ {
+ var type = stream.ReadByte();
+ var flag = stream.ReadByte();
+ Track track = null;
+ var mute = (flag & 1) == 1;
+ for (int j = 0; j < TrackArchetypes.Count; j++)
+ {
+ if (TrackArchetypes[j].TypeId == type)
+ {
+ var options = new TrackCreateOptions
+ {
+ Archetype = TrackArchetypes[j],
+ Mute = mute,
+ };
+ track = TrackArchetypes[j].Create(options);
+ break;
+ }
+ }
+ if (track == null)
+ throw new Exception("Unknown timeline track type " + type);
+ int parentIndex = stream.ReadInt32();
+ int childrenCount = stream.ReadInt32();
+ track.Name = Utilities.Utils.ReadStr(stream, -13);
+ track.Tag = parentIndex;
+
+ track.Archetype.Load(version, track, stream);
+
+ AddLoadedTrack(track);
+ }
+ break;
+ }
+ case 2:
+ case 3:
+ {
+ // Load properties
+ FramesPerSecond = stream.ReadSingle();
+ DurationFrames = stream.ReadInt32();
+ LoadTimelineData(version, stream);
+
+ // Load tracks
+ int tracksCount = stream.ReadInt32();
+ _tracks.Capacity = Math.Max(_tracks.Capacity, tracksCount);
+ for (int i = 0; i < tracksCount; i++)
+ {
+ var type = stream.ReadByte();
+ var flag = stream.ReadByte();
+ Track track = null;
+ var mute = (flag & 1) == 1;
+ var loop = (flag & 2) == 2;
+ for (int j = 0; j < TrackArchetypes.Count; j++)
+ {
+ if (TrackArchetypes[j].TypeId == type)
+ {
+ var options = new TrackCreateOptions
+ {
+ Archetype = TrackArchetypes[j],
+ Mute = mute,
+ Loop = loop,
+ };
+ track = TrackArchetypes[j].Create(options);
+ break;
+ }
+ }
+ if (track == null)
+ throw new Exception("Unknown timeline track type " + type);
+ int parentIndex = stream.ReadInt32();
+ int childrenCount = stream.ReadInt32();
+ track.Name = Utilities.Utils.ReadStr(stream, -13);
+ track.Tag = parentIndex;
+ track.Color = stream.ReadColor32();
+
+ Profiler.BeginEvent("LoadTack");
+ track.Archetype.Load(version, track, stream);
+ Profiler.EndEvent();
+
+ AddLoadedTrack(track);
+ }
+ break;
+ }
+ default: throw new Exception("Unknown timeline version " + version);
+ }
+ LoadTimelineCustomData(version, stream);
+ Profiler.EndEvent();
+
+ Profiler.BeginEvent("ParentTracks");
+ for (int i = 0; i < _tracks.Count; i++)
+ {
+ var parentIndex = (int)_tracks[i].Tag;
+ _tracks[i].Tag = null;
+ if (parentIndex != -1)
+ _tracks[i].ParentTrack = _tracks[parentIndex];
+ }
+ Profiler.EndEvent();
+ Profiler.BeginEvent("SetupTracks");
+ for (int i = 0; i < _tracks.Count; i++)
+ {
+ _tracks[i].OnLoaded();
+ }
+ Profiler.EndEvent();
+ }
+
+ Profiler.BeginEvent("ArrangeTracks");
+ ArrangeTracks();
+ PerformLayout(true);
+ UnlockChildrenRecursive();
+ PerformLayout(true);
+ Profiler.EndEvent();
+
+ ClearEditedFlag();
+ }
+
+ ///
+ /// Saves the timeline data.
+ ///
+ /// The saved timeline data.
+ public virtual byte[] Save()
+ {
+ // Serialize timeline to stream
+ using (var memory = new MemoryStream(512))
+ using (var stream = new BinaryWriter(memory))
+ {
+ // Save properties
+ stream.Write(FormatVersion);
+ stream.Write(FramesPerSecond);
+ stream.Write(DurationFrames);
+ SaveTimelineData(stream);
+
+ // Save tracks
+ int tracksCount = Tracks.Count;
+ stream.Write(tracksCount);
+ for (int i = 0; i < tracksCount; i++)
+ {
+ var track = Tracks[i];
+
+ stream.Write((byte)track.Archetype.TypeId);
+ byte flag = 0;
+ if (track.Mute)
+ flag |= 1;
+ if (track.Loop)
+ flag |= 2;
+ stream.Write(flag);
+ stream.Write(_tracks.IndexOf(track.ParentTrack));
+ stream.Write(track.SubTracks.Count);
+ Utilities.Utils.WriteStr(stream, track.Name, -13);
+ stream.Write((Color32)track.Color);
+ track.Archetype.Save(track, stream);
+ }
+
+ SaveTimelineCustomData(stream);
+
+ return memory.ToArray();
+ }
+ }
+ }
+}
diff --git a/Source/Editor/GUI/Timeline/Timeline.UI.cs b/Source/Editor/GUI/Timeline/Timeline.UI.cs
new file mode 100644
index 000000000..d124e87cf
--- /dev/null
+++ b/Source/Editor/GUI/Timeline/Timeline.UI.cs
@@ -0,0 +1,301 @@
+// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
+
+using FlaxEditor.CustomEditors;
+using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.GUI.Timeline.Undo;
+using FlaxEngine;
+using FlaxEngine.GUI;
+
+namespace FlaxEditor.GUI.Timeline
+{
+ partial class Timeline
+ {
+ private sealed class TimeIntervalsHeader : ContainerControl
+ {
+ private Timeline _timeline;
+ private bool _isLeftMouseButtonDown;
+
+ public TimeIntervalsHeader(Timeline timeline)
+ {
+ _timeline = timeline;
+ }
+
+ ///
+ public override bool OnMouseDown(Vector2 location, MouseButton button)
+ {
+ if (base.OnMouseDown(location, button))
+ return true;
+
+ if (button == MouseButton.Left)
+ {
+ _isLeftMouseButtonDown = true;
+ _timeline._isMovingPositionHandle = true;
+ StartMouseCapture();
+ Seek(ref location);
+ Focus();
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ public override void OnMouseMove(Vector2 location)
+ {
+ base.OnMouseMove(location);
+
+ if (_isLeftMouseButtonDown)
+ {
+ Seek(ref location);
+ }
+ }
+
+ private void Seek(ref Vector2 location)
+ {
+ if (_timeline.PlaybackState == PlaybackStates.Disabled)
+ return;
+
+ var locationTimeline = PointToParent(_timeline, location);
+ var locationTime = _timeline._backgroundArea.PointFromParent(_timeline, locationTimeline);
+ var frame = (locationTime.X - StartOffset * 2.0f) / _timeline.Zoom / UnitsPerSecond * _timeline.FramesPerSecond;
+ _timeline.OnSeek((int)frame);
+ }
+
+ ///
+ public override bool OnMouseUp(Vector2 location, MouseButton button)
+ {
+ if (base.OnMouseUp(location, button))
+ return true;
+
+ if (button == MouseButton.Left && _isLeftMouseButtonDown)
+ {
+ Seek(ref location);
+ EndMouseCapture();
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ public override void OnEndMouseCapture()
+ {
+ _isLeftMouseButtonDown = false;
+ _timeline._isMovingPositionHandle = false;
+
+ base.OnEndMouseCapture();
+ }
+
+ ///
+ public override void OnDestroy()
+ {
+ _timeline = null;
+
+ base.OnDestroy();
+ }
+ }
+
+ class PropertiesEditPopup : ContextMenuBase
+ {
+ private Timeline _timeline;
+ private bool _isDirty;
+ private byte[] _beforeData;
+ private object _undoContext;
+
+ public PropertiesEditPopup(Timeline timeline, object obj, object undoContext)
+ {
+ const float width = 280.0f;
+ const float height = 160.0f;
+ Size = new Vector2(width, height);
+
+ var panel1 = new Panel(ScrollBars.Vertical)
+ {
+ Bounds = new Rectangle(0, 0.0f, width, height),
+ Parent = this
+ };
+ var editor = new CustomEditorPresenter(null);
+ editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop;
+ editor.Panel.IsScrollable = true;
+ editor.Panel.Parent = panel1;
+ editor.Modified += OnModified;
+
+ editor.Select(obj);
+
+ _timeline = timeline;
+ if (timeline.Undo != null && timeline.Undo.Enabled && undoContext != null)
+ {
+ _undoContext = undoContext;
+ if (undoContext is Track track)
+ _beforeData = EditTrackAction.CaptureData(track);
+ else if (undoContext is Timeline)
+ _beforeData = EditTimelineAction.CaptureData(timeline);
+ }
+ }
+
+ private void OnModified()
+ {
+ _isDirty = true;
+ }
+
+ ///
+ protected override void OnShow()
+ {
+ Focus();
+
+ base.OnShow();
+ }
+
+ ///
+ public override void Hide()
+ {
+ if (!Visible)
+ return;
+
+ Focus(null);
+
+ if (_isDirty)
+ {
+ if (_beforeData != null)
+ {
+ if (_undoContext is Track track)
+ {
+ var after = EditTrackAction.CaptureData(track);
+ if (!Utils.ArraysEqual(_beforeData, after))
+ _timeline.Undo.AddAction(new EditTrackAction(_timeline, track, _beforeData, after));
+ }
+ else if (_undoContext is Timeline)
+ {
+ var after = EditTimelineAction.CaptureData(_timeline);
+ if (!Utils.ArraysEqual(_beforeData, after))
+ _timeline.Undo.AddAction(new EditTimelineAction(_timeline, _beforeData, after));
+ }
+ }
+ _timeline.MarkAsEdited();
+ }
+
+ base.Hide();
+ }
+
+ ///
+ public override bool OnKeyDown(KeyboardKeys key)
+ {
+ if (key == KeyboardKeys.Escape)
+ {
+ Hide();
+ return true;
+ }
+
+ return base.OnKeyDown(key);
+ }
+
+ ///
+ public override void OnDestroy()
+ {
+ _timeline = null;
+ _beforeData = null;
+ _undoContext = null;
+
+ base.OnDestroy();
+ }
+ }
+
+ class TracksPanelArea : Panel
+ {
+ private DragDropEffect _currentDragEffect = DragDropEffect.None;
+ private Timeline _timeline;
+ private bool _needSetup = true;
+
+ public TracksPanelArea(Timeline timeline)
+ : base(ScrollBars.Vertical)
+ {
+ _timeline = timeline;
+ }
+
+ ///
+ public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
+ {
+ var result = base.OnDragEnter(ref location, data);
+ if (result == DragDropEffect.None)
+ {
+ if (_needSetup)
+ {
+ _needSetup = false;
+ _timeline.SetupDragDrop();
+ }
+ for (int i = 0; i < _timeline.DragHandlers.Count; i++)
+ {
+ var dragHelper = _timeline.DragHandlers[i].Helper;
+ if (dragHelper.OnDragEnter(data))
+ {
+ result = dragHelper.Effect;
+ break;
+ }
+ }
+ _currentDragEffect = result;
+ }
+
+ return result;
+ }
+
+ ///
+ public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
+ {
+ var result = base.OnDragEnter(ref location, data);
+ if (result == DragDropEffect.None)
+ {
+ result = _currentDragEffect;
+ }
+
+ return result;
+ }
+
+ ///
+ public override void OnDragLeave()
+ {
+ _currentDragEffect = DragDropEffect.None;
+ _timeline.DragHandlers.ForEach(x => x.Helper.OnDragLeave());
+
+ base.OnDragLeave();
+ }
+
+ ///
+ public override DragDropEffect OnDragDrop(ref Vector2 location, DragData data)
+ {
+ var result = base.OnDragDrop(ref location, data);
+ if (result == DragDropEffect.None && _currentDragEffect != DragDropEffect.None)
+ {
+ for (int i = 0; i < _timeline.DragHandlers.Count; i++)
+ {
+ var e = _timeline.DragHandlers[i];
+ if (e.Helper.HasValidDrag)
+ {
+ e.Action(_timeline, e.Helper);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ public override void Draw()
+ {
+ if (IsDragOver && _currentDragEffect != DragDropEffect.None)
+ {
+ var style = Style.Current;
+ Render2D.FillRectangle(new Rectangle(Vector2.Zero, Size), style.BackgroundSelected * 0.4f);
+ }
+
+ base.Draw();
+ }
+
+ ///
+ public override void OnDestroy()
+ {
+ _timeline = null;
+
+ base.OnDestroy();
+ }
+ }
+ }
+}
diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs
index e87c797b6..98037052a 100644
--- a/Source/Editor/GUI/Timeline/Timeline.cs
+++ b/Source/Editor/GUI/Timeline/Timeline.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using FlaxEditor.CustomEditors;
using FlaxEditor.GUI.ContextMenu;
using FlaxEditor.GUI.Drag;
using FlaxEditor.GUI.Input;
@@ -19,9 +18,8 @@ namespace FlaxEditor.GUI.Timeline
///
/// The timeline control that contains tracks section and headers. Can be used to create time-based media interface for camera tracks editing, audio mixing and events tracking.
///
- ///
[HideInEditor]
- public class Timeline : ContainerControl, IKeyframesEditorContext
+ public partial class Timeline : ContainerControl, IKeyframesEditorContext
{
private static readonly KeyValuePair[] FPSValues =
{
@@ -42,91 +40,6 @@ namespace FlaxEditor.GUI.Timeline
internal const int FormatVersion = 3;
- private sealed class TimeIntervalsHeader : ContainerControl
- {
- private Timeline _timeline;
- private bool _isLeftMouseButtonDown;
-
- public TimeIntervalsHeader(Timeline timeline)
- {
- _timeline = timeline;
- }
-
- ///
- public override bool OnMouseDown(Vector2 location, MouseButton button)
- {
- if (base.OnMouseDown(location, button))
- return true;
-
- if (button == MouseButton.Left)
- {
- _isLeftMouseButtonDown = true;
- _timeline._isMovingPositionHandle = true;
- StartMouseCapture();
- Seek(ref location);
- Focus();
- return true;
- }
-
- return false;
- }
-
- ///
- public override void OnMouseMove(Vector2 location)
- {
- base.OnMouseMove(location);
-
- if (_isLeftMouseButtonDown)
- {
- Seek(ref location);
- }
- }
-
- private void Seek(ref Vector2 location)
- {
- if (_timeline.PlaybackState == PlaybackStates.Disabled)
- return;
-
- var locationTimeline = PointToParent(_timeline, location);
- var locationTime = _timeline._backgroundArea.PointFromParent(_timeline, locationTimeline);
- var frame = (locationTime.X - StartOffset * 2.0f) / _timeline.Zoom / UnitsPerSecond * _timeline.FramesPerSecond;
- _timeline.OnSeek((int)frame);
- }
-
- ///
- public override bool OnMouseUp(Vector2 location, MouseButton button)
- {
- if (base.OnMouseUp(location, button))
- return true;
-
- if (button == MouseButton.Left && _isLeftMouseButtonDown)
- {
- Seek(ref location);
- EndMouseCapture();
- return true;
- }
-
- return false;
- }
-
- ///
- public override void OnEndMouseCapture()
- {
- _isLeftMouseButtonDown = false;
- _timeline._isMovingPositionHandle = false;
-
- base.OnEndMouseCapture();
- }
-
- ///
- public override void OnDestroy()
- {
- _timeline = null;
-
- base.OnDestroy();
- }
- }
-
///
/// The base class for timeline properties proxy objects.
///
@@ -1313,225 +1226,6 @@ namespace FlaxEditor.GUI.Timeline
return archetype.Create(options);
}
- ///
- /// Loads the timeline data.
- ///
- /// The version.
- /// The input stream.
- protected virtual void LoadTimelineData(int version, BinaryReader stream)
- {
- }
-
- ///
- /// Saves the timeline data.
- ///
- /// The output stream.
- protected virtual void SaveTimelineData(BinaryWriter stream)
- {
- }
-
- ///
- /// Loads the timeline data after reading the timeline tracks.
- ///
- /// The version.
- /// The input stream.
- protected virtual void LoadTimelineCustomData(int version, BinaryReader stream)
- {
- }
-
- ///
- /// Saves the timeline data after saving the timeline tracks.
- ///
- /// The output stream.
- protected virtual void SaveTimelineCustomData(BinaryWriter stream)
- {
- }
-
- ///
- /// Loads the timeline from the specified data.
- ///
- /// The data.
- public virtual void Load(byte[] data)
- {
- Profiler.BeginEvent("Clear");
- Clear();
- LockChildrenRecursive();
- Profiler.EndEvent();
-
- using (var memory = new MemoryStream(data))
- using (var stream = new BinaryReader(memory))
- {
- Profiler.BeginEvent("LoadData");
- int version = stream.ReadInt32();
- switch (version)
- {
- case 1:
- {
- // [Deprecated on 23.07.2019, expires on 27.04.2021]
-
- // Load properties
- FramesPerSecond = stream.ReadSingle();
- DurationFrames = stream.ReadInt32();
- LoadTimelineData(version, stream);
-
- // Load tracks
- int tracksCount = stream.ReadInt32();
- _tracks.Capacity = Math.Max(_tracks.Capacity, tracksCount);
- for (int i = 0; i < tracksCount; i++)
- {
- var type = stream.ReadByte();
- var flag = stream.ReadByte();
- Track track = null;
- var mute = (flag & 1) == 1;
- for (int j = 0; j < TrackArchetypes.Count; j++)
- {
- if (TrackArchetypes[j].TypeId == type)
- {
- var options = new TrackCreateOptions
- {
- Archetype = TrackArchetypes[j],
- Mute = mute,
- };
- track = TrackArchetypes[j].Create(options);
- break;
- }
- }
- if (track == null)
- throw new Exception("Unknown timeline track type " + type);
- int parentIndex = stream.ReadInt32();
- int childrenCount = stream.ReadInt32();
- track.Name = Utilities.Utils.ReadStr(stream, -13);
- track.Tag = parentIndex;
-
- track.Archetype.Load(version, track, stream);
-
- AddLoadedTrack(track);
- }
- break;
- }
- case 2:
- case 3:
- {
- // Load properties
- FramesPerSecond = stream.ReadSingle();
- DurationFrames = stream.ReadInt32();
- LoadTimelineData(version, stream);
-
- // Load tracks
- int tracksCount = stream.ReadInt32();
- _tracks.Capacity = Math.Max(_tracks.Capacity, tracksCount);
- for (int i = 0; i < tracksCount; i++)
- {
- var type = stream.ReadByte();
- var flag = stream.ReadByte();
- Track track = null;
- var mute = (flag & 1) == 1;
- var loop = (flag & 2) == 2;
- for (int j = 0; j < TrackArchetypes.Count; j++)
- {
- if (TrackArchetypes[j].TypeId == type)
- {
- var options = new TrackCreateOptions
- {
- Archetype = TrackArchetypes[j],
- Mute = mute,
- Loop = loop,
- };
- track = TrackArchetypes[j].Create(options);
- break;
- }
- }
- if (track == null)
- throw new Exception("Unknown timeline track type " + type);
- int parentIndex = stream.ReadInt32();
- int childrenCount = stream.ReadInt32();
- track.Name = Utilities.Utils.ReadStr(stream, -13);
- track.Tag = parentIndex;
- track.Color = stream.ReadColor32();
-
- Profiler.BeginEvent("LoadTack");
- track.Archetype.Load(version, track, stream);
- Profiler.EndEvent();
-
- AddLoadedTrack(track);
- }
- break;
- }
- default: throw new Exception("Unknown timeline version " + version);
- }
- LoadTimelineCustomData(version, stream);
- Profiler.EndEvent();
-
- Profiler.BeginEvent("ParentTracks");
- for (int i = 0; i < _tracks.Count; i++)
- {
- var parentIndex = (int)_tracks[i].Tag;
- _tracks[i].Tag = null;
- if (parentIndex != -1)
- _tracks[i].ParentTrack = _tracks[parentIndex];
- }
- Profiler.EndEvent();
- Profiler.BeginEvent("SetupTracks");
- for (int i = 0; i < _tracks.Count; i++)
- {
- _tracks[i].OnLoaded();
- }
- Profiler.EndEvent();
- }
-
- Profiler.BeginEvent("ArrangeTracks");
- ArrangeTracks();
- PerformLayout(true);
- UnlockChildrenRecursive();
- PerformLayout(true);
- Profiler.EndEvent();
-
- ClearEditedFlag();
- }
-
- ///
- /// Saves the timeline data.
- ///
- /// The saved timeline data.
- public virtual byte[] Save()
- {
- // Serialize timeline to stream
- using (var memory = new MemoryStream(512))
- using (var stream = new BinaryWriter(memory))
- {
- // Save properties
- stream.Write(FormatVersion);
- stream.Write(FramesPerSecond);
- stream.Write(DurationFrames);
- SaveTimelineData(stream);
-
- // Save tracks
- int tracksCount = Tracks.Count;
- stream.Write(tracksCount);
- for (int i = 0; i < tracksCount; i++)
- {
- var track = Tracks[i];
-
- stream.Write((byte)track.Archetype.TypeId);
- byte flag = 0;
- if (track.Mute)
- flag |= 1;
- if (track.Loop)
- flag |= 2;
- stream.Write(flag);
- stream.Write(_tracks.IndexOf(track.ParentTrack));
- stream.Write(track.SubTracks.Count);
- Utilities.Utils.WriteStr(stream, track.Name, -13);
- stream.Write((Color32)track.Color);
- track.Archetype.Save(track, stream);
- }
-
- SaveTimelineCustomData(stream);
-
- return memory.ToArray();
- }
- }
-
///
/// Finds the track by the name.
///
@@ -2259,209 +1953,6 @@ namespace FlaxEditor.GUI.Timeline
Zoom = viewWidth / timelineWidth;
}
- class PropertiesEditPopup : ContextMenuBase
- {
- private Timeline _timeline;
- private bool _isDirty;
- private byte[] _beforeData;
- private object _undoContext;
-
- public PropertiesEditPopup(Timeline timeline, object obj, object undoContext)
- {
- const float width = 280.0f;
- const float height = 160.0f;
- Size = new Vector2(width, height);
-
- var panel1 = new Panel(ScrollBars.Vertical)
- {
- Bounds = new Rectangle(0, 0.0f, width, height),
- Parent = this
- };
- var editor = new CustomEditorPresenter(null);
- editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop;
- editor.Panel.IsScrollable = true;
- editor.Panel.Parent = panel1;
- editor.Modified += OnModified;
-
- editor.Select(obj);
-
- _timeline = timeline;
- if (timeline.Undo != null && timeline.Undo.Enabled && undoContext != null)
- {
- _undoContext = undoContext;
- if (undoContext is Track track)
- _beforeData = EditTrackAction.CaptureData(track);
- else if (undoContext is Timeline)
- _beforeData = EditTimelineAction.CaptureData(timeline);
- }
- }
-
- private void OnModified()
- {
- _isDirty = true;
- }
-
- ///
- protected override void OnShow()
- {
- Focus();
-
- base.OnShow();
- }
-
- ///
- public override void Hide()
- {
- if (!Visible)
- return;
-
- Focus(null);
-
- if (_isDirty)
- {
- if (_beforeData != null)
- {
- if (_undoContext is Track track)
- {
- var after = EditTrackAction.CaptureData(track);
- if (!Utils.ArraysEqual(_beforeData, after))
- _timeline.Undo.AddAction(new EditTrackAction(_timeline, track, _beforeData, after));
- }
- else if (_undoContext is Timeline)
- {
- var after = EditTimelineAction.CaptureData(_timeline);
- if (!Utils.ArraysEqual(_beforeData, after))
- _timeline.Undo.AddAction(new EditTimelineAction(_timeline, _beforeData, after));
- }
- }
- _timeline.MarkAsEdited();
- }
-
- base.Hide();
- }
-
- ///
- public override bool OnKeyDown(KeyboardKeys key)
- {
- if (key == KeyboardKeys.Escape)
- {
- Hide();
- return true;
- }
-
- return base.OnKeyDown(key);
- }
-
- ///
- public override void OnDestroy()
- {
- _timeline = null;
- _beforeData = null;
- _undoContext = null;
-
- base.OnDestroy();
- }
- }
-
- class TracksPanelArea : Panel
- {
- private DragDropEffect _currentDragEffect = DragDropEffect.None;
- private Timeline _timeline;
- private bool _needSetup = true;
-
- public TracksPanelArea(Timeline timeline)
- : base(ScrollBars.Vertical)
- {
- _timeline = timeline;
- }
-
- ///
- public override DragDropEffect OnDragEnter(ref Vector2 location, DragData data)
- {
- var result = base.OnDragEnter(ref location, data);
- if (result == DragDropEffect.None)
- {
- if (_needSetup)
- {
- _needSetup = false;
- _timeline.SetupDragDrop();
- }
- for (int i = 0; i < _timeline.DragHandlers.Count; i++)
- {
- var dragHelper = _timeline.DragHandlers[i].Helper;
- if (dragHelper.OnDragEnter(data))
- {
- result = dragHelper.Effect;
- break;
- }
- }
- _currentDragEffect = result;
- }
-
- return result;
- }
-
- ///
- public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
- {
- var result = base.OnDragEnter(ref location, data);
- if (result == DragDropEffect.None)
- {
- result = _currentDragEffect;
- }
-
- return result;
- }
-
- ///
- public override void OnDragLeave()
- {
- _currentDragEffect = DragDropEffect.None;
- _timeline.DragHandlers.ForEach(x => x.Helper.OnDragLeave());
-
- base.OnDragLeave();
- }
-
- ///
- public override DragDropEffect OnDragDrop(ref Vector2 location, DragData data)
- {
- var result = base.OnDragDrop(ref location, data);
- if (result == DragDropEffect.None && _currentDragEffect != DragDropEffect.None)
- {
- for (int i = 0; i < _timeline.DragHandlers.Count; i++)
- {
- var e = _timeline.DragHandlers[i];
- if (e.Helper.HasValidDrag)
- {
- e.Action(_timeline, e.Helper);
- }
- }
- }
-
- return result;
- }
-
- ///
- public override void Draw()
- {
- if (IsDragOver && _currentDragEffect != DragDropEffect.None)
- {
- var style = Style.Current;
- Render2D.FillRectangle(new Rectangle(Vector2.Zero, Size), style.BackgroundSelected * 0.4f);
- }
-
- base.Draw();
- }
-
- ///
- public override void OnDestroy()
- {
- _timeline = null;
-
- base.OnDestroy();
- }
- }
-
///
/// Shows the timeline object editing popup.
///