Refactor Timeline impl to separate files

This commit is contained in:
Wojtek Figat
2021-09-01 14:43:18 +02:00
parent adadde0aa9
commit 8c9a0b2b2f
3 changed files with 532 additions and 510 deletions

View 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();
}
}
}
}

View 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();
}
}
}
}

View File

@@ -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>