Add timeline tracks duplicating option

#519
This commit is contained in:
Wojtek Figat
2021-08-26 12:13:14 +02:00
parent ba09d9111e
commit 7198fdd74a
5 changed files with 154 additions and 3 deletions

View File

@@ -1732,7 +1732,7 @@ namespace FlaxEditor.GUI.Timeline
GetTracks(SelectedTracks[i], tracks);
}
SelectedTracks.Clear();
if (withUndo & Undo != null)
if (withUndo && Undo != null && Undo.Enabled)
{
if (tracks.Count == 1)
{
@@ -1769,7 +1769,7 @@ namespace FlaxEditor.GUI.Timeline
// Delete tracks
var tracks = new List<Track>(4);
GetTracks(track, tracks);
if (withUndo & Undo != null)
if (withUndo && Undo != null && Undo.Enabled)
{
if (tracks.Count == 1)
{
@@ -1803,6 +1803,93 @@ namespace FlaxEditor.GUI.Timeline
track.OnDeleted();
}
/// <summary>
/// Duplicates the selected tracks/media events.
/// </summary>
/// <param name="withUndo">True if use undo/redo action for track duplication.</param>
public void DuplicateSelection(bool withUndo = true)
{
if (SelectedMedia.Count > 0)
{
throw new NotImplementedException("TODO: duplicating selected media events");
}
if (SelectedTracks.Count > 0)
{
// Duplicate selected tracks
var tracks = new List<Track>(SelectedTracks.Count);
for (int i = 0; i < SelectedTracks.Count; i++)
{
GetTracks(SelectedTracks[i], tracks);
}
var clones = new Track[tracks.Count];
for (int i = 0; i < tracks.Count; i++)
{
var track = tracks[i];
var options = new TrackCreateOptions
{
Archetype = track.Archetype,
Loop = track.Loop,
Mute = track.Mute,
};
var clone = options.Archetype.Create(options);
clone.Name = track.CanRename ? GetValidTrackName(track.Name) : track.Name;
clone.Color = track.Color;
clone.IsExpanded = track.IsExpanded;
byte[] data;
using (var memory = new MemoryStream(512))
using (var stream = new BinaryWriter(memory))
{
// TODO: reuse memory stream to improve tracks duplication performance
options.Archetype.Save(track, stream);
data = memory.ToArray();
}
using (var memory = new MemoryStream(data))
using (var stream = new BinaryReader(memory))
{
track.Archetype.Load(Timeline.FormatVersion, clone, stream);
}
var trackParent = track.ParentTrack;
var trackIndex = track.TrackIndex + 1;
if (trackParent != null && tracks.Contains(trackParent))
{
for (int j = 0; j < i; j++)
{
if (tracks[j] == trackParent)
{
trackParent = clones[j];
break;
}
}
trackIndex--;
}
clone.ParentTrack = trackParent;
clone.TrackIndex = trackIndex;
track.OnDuplicated(clone);
AddTrack(clone, false);
clones[i] = clone;
}
OnTracksOrderChanged();
if (withUndo && Undo != null && Undo.Enabled)
{
if (clones.Length == 1)
{
Undo.AddAction(new AddRemoveTrackAction(this, clones[0], true));
}
else
{
var actions = new List<IUndoAction>();
for (int i = 0; i < clones.Length; i++)
actions.Add(new AddRemoveTrackAction(this, clones[i], true));
Undo.AddAction(new MultiUndoAction(actions, "Remove tracks"));
}
}
OnTracksChanged();
MarkAsEdited();
SelectedTracks[0].Focus();
}
}
/// <summary>
/// Called once to setup the drag drop handling for the timeline (lazy init on first drag action).
/// </summary>
@@ -1945,6 +2032,20 @@ namespace FlaxEditor.GUI.Timeline
return !string.IsNullOrEmpty(name) && _tracks.All(x => x.Name != name);
}
/// <summary>
/// Gets the name of the track that is valid to use for a timeline.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>The track name.</returns>
public string GetValidTrackName(string name)
{
string newName = name;
int count = 0;
while (!IsTrackNameValid(newName))
newName = string.Format("{0} {1}", name, count++);
return newName;
}
/// <summary>
/// Arranges the tracks.
/// </summary>
@@ -2185,7 +2286,7 @@ namespace FlaxEditor.GUI.Timeline
editor.Select(obj);
_timeline = timeline;
if (timeline.Undo != null && undoContext != null)
if (timeline.Undo != null && timeline.Undo.Enabled && undoContext != null)
{
_undoContext = undoContext;
if (undoContext is Track track)

View File

@@ -380,6 +380,14 @@ namespace FlaxEditor.GUI.Timeline
Dispose();
}
/// <summary>
/// Called when track gets duplicated.
/// </summary>
/// <param name="clone">The cloned track instance.</param>
public virtual void OnDuplicated(Track clone)
{
}
/// <summary>
/// Arranges the track and all its media. Called when timeline performs layout for the contents.
/// </summary>
@@ -715,6 +723,11 @@ namespace FlaxEditor.GUI.Timeline
/// </summary>
public virtual bool CanRename => true;
/// <summary>
/// Gets a value indicating whether user can copy and paste this track.
/// </summary>
public virtual bool CanCopyPaste => true;
/// <summary>
/// Gets a value indicating whether user can expand the track contents of the inner hierarchy.
/// </summary>
@@ -1248,6 +1261,13 @@ namespace FlaxEditor.GUI.Timeline
case KeyboardKeys.Delete:
_timeline.DeleteSelection();
return true;
case KeyboardKeys.D:
if (Root.GetKey(KeyboardKeys.Control) && CanCopyPaste)
{
_timeline.DuplicateSelection();
return true;
}
break;
case KeyboardKeys.ArrowLeft:
if (CanExpand && IsExpanded)
{

View File

@@ -533,6 +533,9 @@ namespace FlaxEditor.GUI.Timeline.Tracks
/// <inheritdoc />
public override bool CanCopyPaste => false;
/// <inheritdoc />
public override bool CanExpand => true;
/// <inheritdoc />
public override void OnParentTrackChanged(Track parent)
{
@@ -631,6 +634,14 @@ namespace FlaxEditor.GUI.Timeline.Tracks
UpdatePreviewValue();
}
/// <inheritdoc />
public override void OnDuplicated(Track clone)
{
base.OnDuplicated(clone);
clone.Name = Guid.NewGuid().ToString("N");
}
/// <inheritdoc />
public override void OnDestroy()
{

View File

@@ -288,6 +288,9 @@ namespace FlaxEditor.GUI.Timeline.Tracks
/// <inheritdoc />
public override bool CanRename => false;
/// <inheritdoc />
public override bool CanCopyPaste => false;
/// <summary>
/// Called when member gets changed.
/// </summary>
@@ -314,6 +317,14 @@ namespace FlaxEditor.GUI.Timeline.Tracks
}
}
/// <inheritdoc />
public override void OnDuplicated(Track clone)
{
base.OnDuplicated(clone);
clone.Name = Guid.NewGuid().ToString("N");
}
private void OnTimelineShowPreviewValuesChanged()
{
_previewValue.Visible = Timeline.ShowPreviewValues;

View File

@@ -44,7 +44,10 @@ namespace FlaxEditor.GUI.Timeline.Undo
{
var track = _timeline.FindTrack(_name);
if (track != null)
{
Editor.LogWarning($"Cannot add track {_name}. It already exists.");
return;
}
track = _options.Archetype.Create(_options);
track.Name = _name;
track.Color = _color;
@@ -64,6 +67,11 @@ namespace FlaxEditor.GUI.Timeline.Undo
private void Remove()
{
var track = _timeline.FindTrack(_name);
if (track == null)
{
Editor.LogWarning($"Cannot remove track {_name}. It doesn't already exists.");
return;
}
_timeline.Delete(track, false);
}