Refactor Timeline impl to separate files
This commit is contained in:
230
Source/Editor/GUI/Timeline/Timeline.Data.cs
Normal file
230
Source/Editor/GUI/Timeline/Timeline.Data.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads the timeline data.
|
||||
/// </summary>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="stream">The input stream.</param>
|
||||
protected virtual void LoadTimelineData(int version, BinaryReader stream)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the timeline data.
|
||||
/// </summary>
|
||||
/// <param name="stream">The output stream.</param>
|
||||
protected virtual void SaveTimelineData(BinaryWriter stream)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the timeline data after reading the timeline tracks.
|
||||
/// </summary>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="stream">The input stream.</param>
|
||||
protected virtual void LoadTimelineCustomData(int version, BinaryReader stream)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the timeline data after saving the timeline tracks.
|
||||
/// </summary>
|
||||
/// <param name="stream">The output stream.</param>
|
||||
protected virtual void SaveTimelineCustomData(BinaryWriter stream)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the timeline from the specified data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the timeline data.
|
||||
/// </summary>
|
||||
/// <returns>The saved timeline data.</returns>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
301
Source/Editor/GUI/Timeline/Timeline.UI.cs
Normal file
301
Source/Editor/GUI/Timeline/Timeline.UI.cs
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndMouseCapture()
|
||||
{
|
||||
_isLeftMouseButtonDown = false;
|
||||
_timeline._isMovingPositionHandle = false;
|
||||
|
||||
base.OnEndMouseCapture();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnShow()
|
||||
{
|
||||
Focus();
|
||||
|
||||
base.OnShow();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (key == KeyboardKeys.Escape)
|
||||
{
|
||||
Hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
if (result == DragDropEffect.None)
|
||||
{
|
||||
result = _currentDragEffect;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_currentDragEffect = DragDropEffect.None;
|
||||
_timeline.DragHandlers.ForEach(x => x.Helper.OnDragLeave());
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_timeline = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEngine.GUI.ContainerControl" />
|
||||
[HideInEditor]
|
||||
public class Timeline : ContainerControl, IKeyframesEditorContext
|
||||
public partial class Timeline : ContainerControl, IKeyframesEditorContext
|
||||
{
|
||||
private static readonly KeyValuePair<float, string>[] 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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnEndMouseCapture()
|
||||
{
|
||||
_isLeftMouseButtonDown = false;
|
||||
_timeline._isMovingPositionHandle = false;
|
||||
|
||||
base.OnEndMouseCapture();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_timeline = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The base class for timeline properties proxy objects.
|
||||
/// </summary>
|
||||
@@ -1313,225 +1226,6 @@ namespace FlaxEditor.GUI.Timeline
|
||||
return archetype.Create(options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the timeline data.
|
||||
/// </summary>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="stream">The input stream.</param>
|
||||
protected virtual void LoadTimelineData(int version, BinaryReader stream)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the timeline data.
|
||||
/// </summary>
|
||||
/// <param name="stream">The output stream.</param>
|
||||
protected virtual void SaveTimelineData(BinaryWriter stream)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the timeline data after reading the timeline tracks.
|
||||
/// </summary>
|
||||
/// <param name="version">The version.</param>
|
||||
/// <param name="stream">The input stream.</param>
|
||||
protected virtual void LoadTimelineCustomData(int version, BinaryReader stream)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the timeline data after saving the timeline tracks.
|
||||
/// </summary>
|
||||
/// <param name="stream">The output stream.</param>
|
||||
protected virtual void SaveTimelineCustomData(BinaryWriter stream)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads the timeline from the specified data.
|
||||
/// </summary>
|
||||
/// <param name="data">The data.</param>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the timeline data.
|
||||
/// </summary>
|
||||
/// <returns>The saved timeline data.</returns>
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the track by the name.
|
||||
/// </summary>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnShow()
|
||||
{
|
||||
Focus();
|
||||
|
||||
base.OnShow();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
if (key == KeyboardKeys.Escape)
|
||||
{
|
||||
Hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Vector2 location, DragData data)
|
||||
{
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
if (result == DragDropEffect.None)
|
||||
{
|
||||
result = _currentDragEffect;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_currentDragEffect = DragDropEffect.None;
|
||||
_timeline.DragHandlers.ForEach(x => x.Helper.OnDragLeave());
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
_timeline = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the timeline object editing popup.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user