@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user