@@ -1732,7 +1732,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
GetTracks(SelectedTracks[i], tracks);
|
GetTracks(SelectedTracks[i], tracks);
|
||||||
}
|
}
|
||||||
SelectedTracks.Clear();
|
SelectedTracks.Clear();
|
||||||
if (withUndo & Undo != null)
|
if (withUndo && Undo != null && Undo.Enabled)
|
||||||
{
|
{
|
||||||
if (tracks.Count == 1)
|
if (tracks.Count == 1)
|
||||||
{
|
{
|
||||||
@@ -1769,7 +1769,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
// Delete tracks
|
// Delete tracks
|
||||||
var tracks = new List<Track>(4);
|
var tracks = new List<Track>(4);
|
||||||
GetTracks(track, tracks);
|
GetTracks(track, tracks);
|
||||||
if (withUndo & Undo != null)
|
if (withUndo && Undo != null && Undo.Enabled)
|
||||||
{
|
{
|
||||||
if (tracks.Count == 1)
|
if (tracks.Count == 1)
|
||||||
{
|
{
|
||||||
@@ -1803,6 +1803,93 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
track.OnDeleted();
|
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>
|
/// <summary>
|
||||||
/// Called once to setup the drag drop handling for the timeline (lazy init on first drag action).
|
/// Called once to setup the drag drop handling for the timeline (lazy init on first drag action).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1945,6 +2032,20 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
return !string.IsNullOrEmpty(name) && _tracks.All(x => x.Name != name);
|
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>
|
/// <summary>
|
||||||
/// Arranges the tracks.
|
/// Arranges the tracks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2185,7 +2286,7 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
editor.Select(obj);
|
editor.Select(obj);
|
||||||
|
|
||||||
_timeline = timeline;
|
_timeline = timeline;
|
||||||
if (timeline.Undo != null && undoContext != null)
|
if (timeline.Undo != null && timeline.Undo.Enabled && undoContext != null)
|
||||||
{
|
{
|
||||||
_undoContext = undoContext;
|
_undoContext = undoContext;
|
||||||
if (undoContext is Track track)
|
if (undoContext is Track track)
|
||||||
|
|||||||
@@ -380,6 +380,14 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when track gets duplicated.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clone">The cloned track instance.</param>
|
||||||
|
public virtual void OnDuplicated(Track clone)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Arranges the track and all its media. Called when timeline performs layout for the contents.
|
/// Arranges the track and all its media. Called when timeline performs layout for the contents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -715,6 +723,11 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool CanRename => true;
|
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>
|
/// <summary>
|
||||||
/// Gets a value indicating whether user can expand the track contents of the inner hierarchy.
|
/// Gets a value indicating whether user can expand the track contents of the inner hierarchy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1248,6 +1261,13 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
case KeyboardKeys.Delete:
|
case KeyboardKeys.Delete:
|
||||||
_timeline.DeleteSelection();
|
_timeline.DeleteSelection();
|
||||||
return true;
|
return true;
|
||||||
|
case KeyboardKeys.D:
|
||||||
|
if (Root.GetKey(KeyboardKeys.Control) && CanCopyPaste)
|
||||||
|
{
|
||||||
|
_timeline.DuplicateSelection();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case KeyboardKeys.ArrowLeft:
|
case KeyboardKeys.ArrowLeft:
|
||||||
if (CanExpand && IsExpanded)
|
if (CanExpand && IsExpanded)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -533,6 +533,9 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanCopyPaste => false;
|
public override bool CanCopyPaste => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanExpand => true;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnParentTrackChanged(Track parent)
|
public override void OnParentTrackChanged(Track parent)
|
||||||
{
|
{
|
||||||
@@ -631,6 +634,14 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
UpdatePreviewValue();
|
UpdatePreviewValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDuplicated(Track clone)
|
||||||
|
{
|
||||||
|
base.OnDuplicated(clone);
|
||||||
|
|
||||||
|
clone.Name = Guid.NewGuid().ToString("N");
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -288,6 +288,9 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanRename => false;
|
public override bool CanRename => false;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanCopyPaste => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when member gets changed.
|
/// Called when member gets changed.
|
||||||
/// </summary>
|
/// </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()
|
private void OnTimelineShowPreviewValuesChanged()
|
||||||
{
|
{
|
||||||
_previewValue.Visible = Timeline.ShowPreviewValues;
|
_previewValue.Visible = Timeline.ShowPreviewValues;
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
|||||||
{
|
{
|
||||||
var track = _timeline.FindTrack(_name);
|
var track = _timeline.FindTrack(_name);
|
||||||
if (track != null)
|
if (track != null)
|
||||||
|
{
|
||||||
|
Editor.LogWarning($"Cannot add track {_name}. It already exists.");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
track = _options.Archetype.Create(_options);
|
track = _options.Archetype.Create(_options);
|
||||||
track.Name = _name;
|
track.Name = _name;
|
||||||
track.Color = _color;
|
track.Color = _color;
|
||||||
@@ -64,6 +67,11 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
|||||||
private void Remove()
|
private void Remove()
|
||||||
{
|
{
|
||||||
var track = _timeline.FindTrack(_name);
|
var track = _timeline.FindTrack(_name);
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
Editor.LogWarning($"Cannot remove track {_name}. It doesn't already exists.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
_timeline.Delete(track, false);
|
_timeline.Delete(track, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user