Add keyframes editor to proxy keyframes from subtracks on object track
#519
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -296,5 +297,11 @@ namespace FlaxEditor.GUI
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public abstract void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index);
|
public abstract void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract void OnKeyframesGet(string trackName, Action<string, float, object> get);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public abstract void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -615,9 +615,9 @@ namespace FlaxEditor.GUI
|
|||||||
{
|
{
|
||||||
GetKeyframe(selectedIndices[i], out var time, out var value, out var tangentIn, out var tangentOut);
|
GetKeyframe(selectedIndices[i], out var time, out var value, out var tangentIn, out var tangentOut);
|
||||||
data.AppendLine((time + offset).ToString(CultureInfo.InvariantCulture));
|
data.AppendLine((time + offset).ToString(CultureInfo.InvariantCulture));
|
||||||
data.AppendLine(JsonSerializer.Serialize(value).Replace(Environment.NewLine, ""));
|
data.AppendLine(JsonSerializer.Serialize(value).RemoveNewLine());
|
||||||
data.AppendLine(JsonSerializer.Serialize(tangentIn).Replace(Environment.NewLine, ""));
|
data.AppendLine(JsonSerializer.Serialize(tangentIn).RemoveNewLine());
|
||||||
data.AppendLine(JsonSerializer.Serialize(tangentOut).Replace(Environment.NewLine, ""));
|
data.AppendLine(JsonSerializer.Serialize(tangentOut).RemoveNewLine());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1326,6 +1326,34 @@ namespace FlaxEditor.GUI
|
|||||||
return new Vector2(k.Time, Accessor.GetCurveValue(ref k.Value, component));
|
return new Vector2(k.Time, Accessor.GetCurveValue(ref k.Value, component));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnKeyframesGet(string trackName, Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _keyframes.Count; i++)
|
||||||
|
{
|
||||||
|
var k = _keyframes[i];
|
||||||
|
get(trackName, k.Time, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
OnEditingStart();
|
||||||
|
_keyframes.Clear();
|
||||||
|
if (keyframes != null)
|
||||||
|
{
|
||||||
|
foreach (var e in keyframes)
|
||||||
|
{
|
||||||
|
var k = (LinearCurve<T>.Keyframe)e.Value;
|
||||||
|
_keyframes.Add(new LinearCurve<T>.Keyframe(e.Key, k.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OnKeyframesChanged();
|
||||||
|
OnEdited();
|
||||||
|
OnEditingEnd();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override int KeyframesCount => _keyframes.Count;
|
public override int KeyframesCount => _keyframes.Count;
|
||||||
|
|
||||||
@@ -2019,6 +2047,34 @@ namespace FlaxEditor.GUI
|
|||||||
return new Vector2(k.Time, Accessor.GetCurveValue(ref k.Value, component));
|
return new Vector2(k.Time, Accessor.GetCurveValue(ref k.Value, component));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnKeyframesGet(string trackName, Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _keyframes.Count; i++)
|
||||||
|
{
|
||||||
|
var k = _keyframes[i];
|
||||||
|
get(trackName, k.Time, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
OnEditingStart();
|
||||||
|
_keyframes.Clear();
|
||||||
|
if (keyframes != null)
|
||||||
|
{
|
||||||
|
foreach (var e in keyframes)
|
||||||
|
{
|
||||||
|
var k = (BezierCurve<T>.Keyframe)e.Value;
|
||||||
|
_keyframes.Add(new BezierCurve<T>.Keyframe(e.Key, k.Value, k.TangentIn, k.TangentOut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OnKeyframesChanged();
|
||||||
|
OnEdited();
|
||||||
|
OnEditingEnd();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override int KeyframesCount => _keyframes.Count;
|
public override int KeyframesCount => _keyframes.Count;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -67,5 +69,18 @@ namespace FlaxEditor.GUI
|
|||||||
/// <param name="datas">The pasted data text.</param>
|
/// <param name="datas">The pasted data text.</param>
|
||||||
/// <param name="index">The counter for the current data index. Set to -1 until the calling editor starts paste operation.</param>
|
/// <param name="index">The counter for the current data index. Set to -1 until the calling editor starts paste operation.</param>
|
||||||
void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index);
|
void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when collecting keyframes from the context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="trackName">The name of the track.</param>
|
||||||
|
/// <param name="get">The getter function to call for all keyframes. Args are: track name, keyframe time, keyframe object.</param>
|
||||||
|
void OnKeyframesGet(string trackName, Action<string, float, object> get);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when setting keyframes data (opposite to <see cref="OnKeyframesGet"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyframes">The list of keyframes, null for empty list. Keyframe data is: time and keyframe object.</param>
|
||||||
|
void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -62,5 +64,17 @@ namespace FlaxEditor.GUI
|
|||||||
/// <param name="datas">The pasted data text.</param>
|
/// <param name="datas">The pasted data text.</param>
|
||||||
/// <param name="index">The counter for the current data index. Set to -1 until the calling editor starts paste operation.</param>
|
/// <param name="index">The counter for the current data index. Set to -1 until the calling editor starts paste operation.</param>
|
||||||
void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index);
|
void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when collecting keyframes from the context.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="get">The getter function to call for all keyframes. Args are: track name, keyframe time, keyframe object.</param>
|
||||||
|
void OnKeyframesGet(Action<string, float, object> get);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when setting keyframes data (opposite to <see cref="OnKeyframesGet"/>).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyframes">The list of keyframes, null for empty list. Keyframe data is: time and keyframe object.</param>
|
||||||
|
void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,12 @@ namespace FlaxEditor.GUI
|
|||||||
{
|
{
|
||||||
return Time > other.Time ? 1 : 0;
|
return Time > other.Time ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Value?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -409,8 +415,8 @@ namespace FlaxEditor.GUI
|
|||||||
_cmShowPos = PointToKeyframes(location, ref viewRect);
|
_cmShowPos = PointToKeyframes(location, ref viewRect);
|
||||||
|
|
||||||
var cm = new ContextMenu.ContextMenu();
|
var cm = new ContextMenu.ContextMenu();
|
||||||
cm.AddButton("Add keyframe", () => _editor.AddKeyframe(_cmShowPos)).Enabled = _editor.Keyframes.Count < _editor.MaxKeyframes;
|
cm.AddButton("Add keyframe", () => _editor.AddKeyframe(_cmShowPos)).Enabled = _editor.Keyframes.Count < _editor.MaxKeyframes && _editor.DefaultValue != null;
|
||||||
if (selectionCount > 0)
|
if (selectionCount > 0 && _editor.EnableKeyframesValueEdit)
|
||||||
{
|
{
|
||||||
cm.AddButton(selectionCount == 1 ? "Edit keyframe" : "Edit keyframes", () => _editor.EditKeyframes(this, location));
|
cm.AddButton(selectionCount == 1 ? "Edit keyframe" : "Edit keyframes", () => _editor.EditKeyframes(this, location));
|
||||||
}
|
}
|
||||||
@@ -421,13 +427,14 @@ namespace FlaxEditor.GUI
|
|||||||
cm.AddButton(totalSelectionCount == 1 ? "Copy keyframe" : "Copy keyframes", () => _editor.CopyKeyframes(point));
|
cm.AddButton(totalSelectionCount == 1 ? "Copy keyframe" : "Copy keyframes", () => _editor.CopyKeyframes(point));
|
||||||
}
|
}
|
||||||
cm.AddButton("Paste keyframes", () => KeyframesEditorUtils.Paste(_editor, point?.Time ?? _cmShowPos.X)).Enabled = KeyframesEditorUtils.CanPaste();
|
cm.AddButton("Paste keyframes", () => KeyframesEditorUtils.Paste(_editor, point?.Time ?? _cmShowPos.X)).Enabled = KeyframesEditorUtils.CanPaste();
|
||||||
cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location));
|
if (_editor.EnableKeyframesValueEdit)
|
||||||
cm.AddButton("Select all keyframes", _editor.SelectAll);
|
cm.AddButton("Edit all keyframes", () => _editor.EditAllKeyframes(this, location));
|
||||||
|
cm.AddButton("Select all keyframes", _editor.SelectAll).Enabled = _editor._points.Count > 0;
|
||||||
cm.AddButton("Copy all keyframes", () =>
|
cm.AddButton("Copy all keyframes", () =>
|
||||||
{
|
{
|
||||||
_editor.SelectAll();
|
_editor.SelectAll();
|
||||||
_editor.CopyKeyframes(point);
|
_editor.CopyKeyframes(point);
|
||||||
});
|
}).Enabled = _editor.DefaultValue != null;
|
||||||
if (_editor.EnableZoom && _editor.EnablePanning)
|
if (_editor.EnableZoom && _editor.EnablePanning)
|
||||||
{
|
{
|
||||||
cm.AddSeparator();
|
cm.AddSeparator();
|
||||||
@@ -645,6 +652,16 @@ namespace FlaxEditor.GUI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnablePanning = true;
|
public bool EnablePanning = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if enable keyframes values editing (via popup).
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableKeyframesValueEdit = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// True if enable view panning. Otherwise user won't be able to move the view area.
|
||||||
|
/// </summary>
|
||||||
|
public bool Enable = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether user is editing the keyframes.
|
/// Gets a value indicating whether user is editing the keyframes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1353,7 +1370,7 @@ namespace FlaxEditor.GUI
|
|||||||
continue;
|
continue;
|
||||||
var k = _keyframes[i];
|
var k = _keyframes[i];
|
||||||
data.AppendLine((k.Time + offset).ToString(CultureInfo.InvariantCulture));
|
data.AppendLine((k.Time + offset).ToString(CultureInfo.InvariantCulture));
|
||||||
data.AppendLine(JsonSerializer.Serialize(k.Value).Replace(Environment.NewLine, ""));
|
data.AppendLine(JsonSerializer.Serialize(k.Value).RemoveNewLine());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1428,5 +1445,33 @@ namespace FlaxEditor.GUI
|
|||||||
Editor.LogWarning(ex);
|
Editor.LogWarning(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesGet(string trackName, Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _keyframes.Count; i++)
|
||||||
|
{
|
||||||
|
var k = _keyframes[i];
|
||||||
|
get(trackName, k.Time, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
OnEditingStart();
|
||||||
|
_keyframes.Clear();
|
||||||
|
if (keyframes != null)
|
||||||
|
{
|
||||||
|
foreach (var e in keyframes)
|
||||||
|
{
|
||||||
|
var k = (Keyframe)e.Value;
|
||||||
|
_keyframes.Add(new Keyframe(e.Key, k.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OnKeyframesChanged();
|
||||||
|
OnEdited();
|
||||||
|
OnEditingEnd();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2137,5 +2137,20 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
trackContext.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
trackContext.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesGet(Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _tracks.Count; i++)
|
||||||
|
{
|
||||||
|
if (_tracks[i] is IKeyframesEditorContext trackContext)
|
||||||
|
trackContext.OnKeyframesGet(get);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,9 +65,13 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
set => ActorID = value?.ID ?? Guid.Empty;
|
set => ActorID = value?.ID ?? Guid.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
public ActorTrack(ref TrackCreateOptions options)
|
/// Initializes a new instance of the <see cref="ActorTrack"/> class.
|
||||||
: base(ref options)
|
/// </summary>
|
||||||
|
/// <param name="options">The track initial options.</param>
|
||||||
|
/// <param name="useProxyKeyframes">True if show sub-tracks keyframes as a proxy on this track, otherwise false.</param>
|
||||||
|
public ActorTrack(ref TrackCreateOptions options, bool useProxyKeyframes = true)
|
||||||
|
: base(ref options, useProxyKeyframes)
|
||||||
{
|
{
|
||||||
// Select Actor button
|
// Select Actor button
|
||||||
const float buttonSize = 18;
|
const float buttonSize = 18;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using FlaxEditor.GUI.Timeline.Undo;
|
using FlaxEditor.GUI.Timeline.Undo;
|
||||||
using FlaxEditor.Viewport.Previews;
|
using FlaxEditor.Viewport.Previews;
|
||||||
@@ -476,10 +477,12 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
{
|
{
|
||||||
if (_audioMedia == null || Curve == null || Timeline == null)
|
if (_audioMedia == null || Curve == null || Timeline == null)
|
||||||
return;
|
return;
|
||||||
|
bool wasVisible = Curve.Visible;
|
||||||
Curve.Visible = Visible;
|
Curve.Visible = Visible;
|
||||||
if (!Visible)
|
if (!Visible)
|
||||||
{
|
{
|
||||||
Curve.ClearSelection();
|
if (wasVisible)
|
||||||
|
Curve.ClearSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Curve.KeyframesEditorContext = Timeline;
|
Curve.KeyframesEditorContext = Timeline;
|
||||||
@@ -702,5 +705,17 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
if (Curve != null && Curve.Visible)
|
if (Curve != null && Curve.Visible)
|
||||||
Curve.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
Curve.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesGet(Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
Curve?.OnKeyframesGet(Name, get);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
Curve?.OnKeyframesSet(keyframes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -728,7 +728,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public CameraCutTrack(ref TrackCreateOptions options)
|
public CameraCutTrack(ref TrackCreateOptions options)
|
||||||
: base(ref options)
|
: base(ref options, false)
|
||||||
{
|
{
|
||||||
Height = CameraCutThumbnailRenderer.Height + 8;
|
Height = CameraCutThumbnailRenderer.Height + 8;
|
||||||
|
|
||||||
|
|||||||
356
Source/Editor/GUI/Timeline/Tracks/ConductorTrack.cs
Normal file
356
Source/Editor/GUI/Timeline/Tracks/ConductorTrack.cs
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
|
namespace FlaxEditor.GUI.Timeline.Tracks
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The timeline track that can proxy the sub-tracks keyframes for easier batched editing.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
||||||
|
public abstract class ConductorTrack : Track, IKeyframesEditorContext
|
||||||
|
{
|
||||||
|
private sealed class ConductionUndoAction : IUndoAction
|
||||||
|
{
|
||||||
|
public bool IsDo;
|
||||||
|
public Timeline Timeline;
|
||||||
|
public string Track;
|
||||||
|
|
||||||
|
private void Refresh()
|
||||||
|
{
|
||||||
|
Refresh(Timeline.FindTrack(Track));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Refresh(Track track)
|
||||||
|
{
|
||||||
|
if (track is ConductorTrack conductor && conductor.Proxy != null)
|
||||||
|
{
|
||||||
|
conductor.LoadProxy();
|
||||||
|
foreach (var subTrack in conductor.SubTracks)
|
||||||
|
Refresh(subTrack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ActionString => "Edit track";
|
||||||
|
|
||||||
|
public void Do()
|
||||||
|
{
|
||||||
|
if (IsDo)
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Undo()
|
||||||
|
{
|
||||||
|
if (!IsDo)
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Timeline = null;
|
||||||
|
Track = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashSet<string> _proxyTracks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The batched keyframes editor for all sub-tracks.
|
||||||
|
/// </summary>
|
||||||
|
protected KeyframesEditor Proxy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ConductorTrack"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="options">The track initial options.</param>
|
||||||
|
/// <param name="useProxyKeyframes">True if show sub-tracks keyframes as a proxy on this track, otherwise false.</param>
|
||||||
|
protected ConductorTrack(ref TrackCreateOptions options, bool useProxyKeyframes = true)
|
||||||
|
: base(ref options)
|
||||||
|
{
|
||||||
|
if (useProxyKeyframes)
|
||||||
|
{
|
||||||
|
// Proxy keyframes editor
|
||||||
|
Proxy = new KeyframesEditor
|
||||||
|
{
|
||||||
|
EnableZoom = false,
|
||||||
|
EnablePanning = false,
|
||||||
|
EnableKeyframesValueEdit = false,
|
||||||
|
DefaultValue = new ProxyKey(),
|
||||||
|
ScrollBars = ScrollBars.None,
|
||||||
|
};
|
||||||
|
Proxy.Edited += OnProxyEdited;
|
||||||
|
Proxy.EditingEnd += OnProxyEditingEnd;
|
||||||
|
Proxy.UnlockChildrenRecursive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateProxyKeyframes()
|
||||||
|
{
|
||||||
|
if (Proxy == null || Timeline == null)
|
||||||
|
return;
|
||||||
|
bool wasVisible = Proxy.Visible;
|
||||||
|
Proxy.Visible = Visible && IsCollapsed && SubTracks.Any(x => x is IKeyframesEditorContext);
|
||||||
|
if (!Visible)
|
||||||
|
{
|
||||||
|
if (wasVisible)
|
||||||
|
Proxy.ClearSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Proxy.KeyframesEditorContext = Timeline;
|
||||||
|
Proxy.CustomViewPanning = Timeline.OnKeyframesViewPanning;
|
||||||
|
Proxy.Bounds = new Rectangle(Timeline.StartOffset, Y + 1.0f, Timeline.Duration * Timeline.UnitsPerSecond * Timeline.Zoom, Height - 2.0f);
|
||||||
|
Proxy.ViewScale = new Vector2(Timeline.Zoom, 1.0f);
|
||||||
|
if (!wasVisible)
|
||||||
|
LoadProxy();
|
||||||
|
else
|
||||||
|
Proxy.UpdateKeyframes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnProxyEdited()
|
||||||
|
{
|
||||||
|
Timeline.MarkAsEdited();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnProxyEditingEnd()
|
||||||
|
{
|
||||||
|
SaveProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The proxy key data.
|
||||||
|
/// </summary>
|
||||||
|
public struct ProxyKey
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The keyframes.
|
||||||
|
/// </summary>
|
||||||
|
[EditorDisplay("Keyframes", EditorDisplayAttribute.InlineStyle), ExpandGroups]
|
||||||
|
[Collection(CanReorderItems = false, ReadOnly = true)]
|
||||||
|
public List<KeyValuePair<string, object>> Keyframes;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (Keyframes == null || Keyframes.Count == 0)
|
||||||
|
return string.Empty;
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < Keyframes.Count; i++)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
sb.Append(", ");
|
||||||
|
var k = Keyframes[i];
|
||||||
|
if (k.Value != null)
|
||||||
|
sb.Append(k.Value);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadProxy()
|
||||||
|
{
|
||||||
|
if (_proxyTracks == null)
|
||||||
|
_proxyTracks = new HashSet<string>();
|
||||||
|
else
|
||||||
|
_proxyTracks.Clear();
|
||||||
|
|
||||||
|
// Build keyframes list that contains keys from all tracks to proxy
|
||||||
|
var keyframes = new List<KeyframesEditor.Keyframe>();
|
||||||
|
var eps = Proxy.FPS.HasValue ? 0.5f / Proxy.FPS.Value : Mathf.Epsilon;
|
||||||
|
Action<string, float, object> getter = (trackName, time, value) =>
|
||||||
|
{
|
||||||
|
_proxyTracks.Add(trackName);
|
||||||
|
int idx = keyframes.FindIndex(x => Mathf.Abs(x.Time - time) <= eps);
|
||||||
|
if (idx == -1)
|
||||||
|
{
|
||||||
|
idx = keyframes.Count;
|
||||||
|
keyframes.Add(new KeyframesEditor.Keyframe(time, new ProxyKey { Keyframes = new List<KeyValuePair<string, object>>() }));
|
||||||
|
}
|
||||||
|
((ProxyKey)keyframes[idx].Value).Keyframes.Add(new KeyValuePair<string, object>(trackName, value));
|
||||||
|
};
|
||||||
|
foreach (var subTrack in SubTracks)
|
||||||
|
{
|
||||||
|
var trackContext = subTrack as IKeyframesEditorContext;
|
||||||
|
trackContext?.OnKeyframesGet(getter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Proxy.SetKeyframes(keyframes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveProxy()
|
||||||
|
{
|
||||||
|
// Collect keyframes list per track
|
||||||
|
var perTrackKeyframes = new Dictionary<string, List<KeyValuePair<float, object>>>();
|
||||||
|
var keyframes = Proxy.Keyframes;
|
||||||
|
foreach (var keyframe in keyframes)
|
||||||
|
{
|
||||||
|
var proxy = (ProxyKey)keyframe.Value;
|
||||||
|
if (proxy.Keyframes == null)
|
||||||
|
continue;
|
||||||
|
foreach (var e in proxy.Keyframes)
|
||||||
|
{
|
||||||
|
if (!perTrackKeyframes.TryGetValue(e.Key, out var trackKeyframes))
|
||||||
|
{
|
||||||
|
trackKeyframes = new List<KeyValuePair<float, object>>();
|
||||||
|
perTrackKeyframes.Add(e.Key, trackKeyframes);
|
||||||
|
}
|
||||||
|
trackKeyframes.Add(new KeyValuePair<float, object>(keyframe.Time, e.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the new keyframes to all tracks in this proxy
|
||||||
|
Timeline.AddBatchedUndoAction(new ConductionUndoAction { IsDo = false, Timeline = Timeline, Track = Name });
|
||||||
|
foreach (var trackName in _proxyTracks)
|
||||||
|
{
|
||||||
|
var track = Timeline.FindTrack(trackName);
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
Editor.LogWarning(string.Format("Failed to find track {0} for keyframes proxy on track {1}.", trackName, Title ?? Name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var trackContext = track as IKeyframesEditorContext;
|
||||||
|
if (trackContext == null)
|
||||||
|
{
|
||||||
|
Editor.LogWarning(string.Format("Track {0} used keyframes proxy on track {1} is invalid.", track.Title ?? track.Name, Title ?? Name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
perTrackKeyframes.TryGetValue(trackName, out var trackKeyframes);
|
||||||
|
trackContext.OnKeyframesSet(trackKeyframes);
|
||||||
|
}
|
||||||
|
Timeline.AddBatchedUndoAction(new ConductionUndoAction { IsDo = true, Timeline = Timeline, Track = Name });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnVisibleChanged()
|
||||||
|
{
|
||||||
|
base.OnVisibleChanged();
|
||||||
|
|
||||||
|
UpdateProxyKeyframes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnExpandedChanged()
|
||||||
|
{
|
||||||
|
base.OnExpandedChanged();
|
||||||
|
|
||||||
|
UpdateProxyKeyframes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnTimelineChanged(Timeline timeline)
|
||||||
|
{
|
||||||
|
base.OnTimelineChanged(timeline);
|
||||||
|
|
||||||
|
if (Proxy != null)
|
||||||
|
{
|
||||||
|
Proxy.Parent = timeline?.MediaPanel;
|
||||||
|
Proxy.FPS = timeline?.FramesPerSecond;
|
||||||
|
UpdateProxyKeyframes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnTimelineZoomChanged()
|
||||||
|
{
|
||||||
|
base.OnTimelineZoomChanged();
|
||||||
|
|
||||||
|
UpdateProxyKeyframes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnTimelineArrange()
|
||||||
|
{
|
||||||
|
base.OnTimelineArrange();
|
||||||
|
|
||||||
|
UpdateProxyKeyframes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnTimelineFpsChanged(float before, float after)
|
||||||
|
{
|
||||||
|
base.OnTimelineFpsChanged(before, after);
|
||||||
|
|
||||||
|
Proxy.FPS = after;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnDestroy()
|
||||||
|
{
|
||||||
|
if (Proxy != null)
|
||||||
|
{
|
||||||
|
Proxy.Dispose();
|
||||||
|
Proxy = null;
|
||||||
|
_proxyTracks = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesDeselect(IKeyframesEditor editor)
|
||||||
|
{
|
||||||
|
if (Proxy != null && Proxy.Visible)
|
||||||
|
Proxy.OnKeyframesDeselect(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection)
|
||||||
|
{
|
||||||
|
if (Proxy != null && Proxy.Visible)
|
||||||
|
Proxy.OnKeyframesSelection(editor, control, selection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int OnKeyframesSelectionCount()
|
||||||
|
{
|
||||||
|
return Proxy != null && Proxy.Visible ? Proxy.OnKeyframesSelectionCount() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesDelete(IKeyframesEditor editor)
|
||||||
|
{
|
||||||
|
if (Proxy != null && Proxy.Visible)
|
||||||
|
Proxy.OnKeyframesDelete(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesMove(IKeyframesEditor editor, ContainerControl control, Vector2 location, bool start, bool end)
|
||||||
|
{
|
||||||
|
if (Proxy != null && Proxy.Visible)
|
||||||
|
Proxy.OnKeyframesMove(editor, control, location, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesCopy(IKeyframesEditor editor, float? timeOffset, StringBuilder data)
|
||||||
|
{
|
||||||
|
if (Proxy != null && Proxy.Visible)
|
||||||
|
Proxy.OnKeyframesCopy(editor, timeOffset, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index)
|
||||||
|
{
|
||||||
|
if (Proxy != null && Proxy.Visible)
|
||||||
|
Proxy.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesGet(Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
foreach (var subTrack in SubTracks)
|
||||||
|
{
|
||||||
|
if (subTrack is IKeyframesEditorContext trackContext)
|
||||||
|
trackContext.OnKeyframesGet(get);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -224,10 +225,12 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
{
|
{
|
||||||
if (Curve == null || Timeline == null)
|
if (Curve == null || Timeline == null)
|
||||||
return;
|
return;
|
||||||
|
bool wasVisible = Curve.Visible;
|
||||||
Curve.Visible = Visible;
|
Curve.Visible = Visible;
|
||||||
if (!Visible)
|
if (!Visible)
|
||||||
{
|
{
|
||||||
Curve.ClearSelection();
|
if (wasVisible)
|
||||||
|
Curve.ClearSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var expanded = IsExpanded;
|
var expanded = IsExpanded;
|
||||||
@@ -432,52 +435,64 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesDeselect(IKeyframesEditor editor)
|
public new void OnKeyframesDeselect(IKeyframesEditor editor)
|
||||||
{
|
{
|
||||||
if (Curve != null && Curve.Visible)
|
if (Curve != null && Curve.Visible)
|
||||||
Curve.OnKeyframesDeselect(editor);
|
Curve.OnKeyframesDeselect(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection)
|
public new void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection)
|
||||||
{
|
{
|
||||||
if (Curve != null && Curve.Visible)
|
if (Curve != null && Curve.Visible)
|
||||||
Curve.OnKeyframesSelection(editor, control, selection);
|
Curve.OnKeyframesSelection(editor, control, selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int OnKeyframesSelectionCount()
|
public new int OnKeyframesSelectionCount()
|
||||||
{
|
{
|
||||||
return Curve != null && Curve.Visible ? Curve.OnKeyframesSelectionCount() : 0;
|
return Curve != null && Curve.Visible ? Curve.OnKeyframesSelectionCount() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesDelete(IKeyframesEditor editor)
|
public new void OnKeyframesDelete(IKeyframesEditor editor)
|
||||||
{
|
{
|
||||||
if (Curve != null && Curve.Visible)
|
if (Curve != null && Curve.Visible)
|
||||||
Curve.OnKeyframesDelete(editor);
|
Curve.OnKeyframesDelete(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesMove(IKeyframesEditor editor, ContainerControl control, Vector2 location, bool start, bool end)
|
public new void OnKeyframesMove(IKeyframesEditor editor, ContainerControl control, Vector2 location, bool start, bool end)
|
||||||
{
|
{
|
||||||
if (Curve != null && Curve.Visible)
|
if (Curve != null && Curve.Visible)
|
||||||
Curve.OnKeyframesMove(editor, control, location, start, end);
|
Curve.OnKeyframesMove(editor, control, location, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesCopy(IKeyframesEditor editor, float? timeOffset, StringBuilder data)
|
public new void OnKeyframesCopy(IKeyframesEditor editor, float? timeOffset, StringBuilder data)
|
||||||
{
|
{
|
||||||
if (Curve != null && Curve.Visible)
|
if (Curve != null && Curve.Visible)
|
||||||
Curve.OnKeyframesCopy(editor, timeOffset, data);
|
Curve.OnKeyframesCopy(editor, timeOffset, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index)
|
public new void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index)
|
||||||
{
|
{
|
||||||
if (Curve != null && Curve.Visible)
|
if (Curve != null && Curve.Visible)
|
||||||
Curve.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
Curve.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public new void OnKeyframesGet(Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
Curve?.OnKeyframesGet(Name, get);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public new void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
Curve?.OnKeyframesSet(keyframes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
@@ -50,7 +51,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
{
|
{
|
||||||
e.EventParamsSizes[i] = stream.ReadInt32();
|
e.EventParamsSizes[i] = stream.ReadInt32();
|
||||||
var paramTypeName = LoadName(stream);
|
var paramTypeName = LoadName(stream);
|
||||||
e.EventParamsTypes[i] = Scripting.TypeUtils.GetType(paramTypeName).Type;
|
e.EventParamsTypes[i] = Scripting.TypeUtils.GetManagedType(paramTypeName);
|
||||||
if (e.EventParamsTypes[i] == null)
|
if (e.EventParamsTypes[i] == null)
|
||||||
isInvalid = true;
|
isInvalid = true;
|
||||||
}
|
}
|
||||||
@@ -283,9 +284,11 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
{
|
{
|
||||||
if (Events == null || Timeline == null)
|
if (Events == null || Timeline == null)
|
||||||
return;
|
return;
|
||||||
|
bool wasVisible = Events.Visible;
|
||||||
Events.Visible = Visible;
|
Events.Visible = Visible;
|
||||||
if (!Visible)
|
if (!Visible)
|
||||||
{
|
{
|
||||||
|
if(wasVisible)
|
||||||
Events.ClearSelection();
|
Events.ClearSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -413,51 +416,63 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesDeselect(IKeyframesEditor editor)
|
public new void OnKeyframesDeselect(IKeyframesEditor editor)
|
||||||
{
|
{
|
||||||
if (Events != null && Events.Visible)
|
if (Events != null && Events.Visible)
|
||||||
Events.OnKeyframesDeselect(editor);
|
Events.OnKeyframesDeselect(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection)
|
public new void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection)
|
||||||
{
|
{
|
||||||
if (Events != null && Events.Visible)
|
if (Events != null && Events.Visible)
|
||||||
Events.OnKeyframesSelection(editor, control, selection);
|
Events.OnKeyframesSelection(editor, control, selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int OnKeyframesSelectionCount()
|
public new int OnKeyframesSelectionCount()
|
||||||
{
|
{
|
||||||
return Events != null && Events.Visible ? Events.OnKeyframesSelectionCount() : 0;
|
return Events != null && Events.Visible ? Events.OnKeyframesSelectionCount() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesDelete(IKeyframesEditor editor)
|
public new void OnKeyframesDelete(IKeyframesEditor editor)
|
||||||
{
|
{
|
||||||
if (Events != null && Events.Visible)
|
if (Events != null && Events.Visible)
|
||||||
Events.OnKeyframesDelete(editor);
|
Events.OnKeyframesDelete(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesMove(IKeyframesEditor editor, ContainerControl control, Vector2 location, bool start, bool end)
|
public new void OnKeyframesMove(IKeyframesEditor editor, ContainerControl control, Vector2 location, bool start, bool end)
|
||||||
{
|
{
|
||||||
if (Events != null && Events.Visible)
|
if (Events != null && Events.Visible)
|
||||||
Events.OnKeyframesMove(editor, control, location, start, end);
|
Events.OnKeyframesMove(editor, control, location, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesCopy(IKeyframesEditor editor, float? timeOffset, StringBuilder data)
|
public new void OnKeyframesCopy(IKeyframesEditor editor, float? timeOffset, StringBuilder data)
|
||||||
{
|
{
|
||||||
if (Events != null && Events.Visible)
|
if (Events != null && Events.Visible)
|
||||||
Events.OnKeyframesCopy(editor, timeOffset, data);
|
Events.OnKeyframesCopy(editor, timeOffset, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index)
|
public new void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index)
|
||||||
{
|
{
|
||||||
if (Events != null && Events.Visible)
|
if (Events != null && Events.Visible)
|
||||||
Events.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
Events.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public new void OnKeyframesGet(Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
Events?.OnKeyframesGet(Name, get);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public new void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
Events?.OnKeyframesSet(keyframes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// The timeline track that represents a folder used to group and organize tracks.
|
/// The timeline track that represents a folder used to group and organize tracks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
||||||
public class FolderTrack : Track
|
public class FolderTrack : ConductorTrack
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the archetype.
|
/// Gets the archetype.
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@@ -55,7 +56,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
|
|
||||||
var keyframes = new KeyframesEditor.Keyframe[keyframesCount];
|
var keyframes = new KeyframesEditor.Keyframe[keyframesCount];
|
||||||
var dataBuffer = new byte[e.ValueSize];
|
var dataBuffer = new byte[e.ValueSize];
|
||||||
var propertyType = Scripting.TypeUtils.GetType(e.MemberTypeName).Type;
|
var propertyType = Scripting.TypeUtils.GetManagedType(e.MemberTypeName);
|
||||||
if (propertyType == null)
|
if (propertyType == null)
|
||||||
{
|
{
|
||||||
e.Keyframes.ResetKeyframes();
|
e.Keyframes.ResetKeyframes();
|
||||||
@@ -250,10 +251,12 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
{
|
{
|
||||||
if (Keyframes == null || Timeline == null)
|
if (Keyframes == null || Timeline == null)
|
||||||
return;
|
return;
|
||||||
|
bool wasVisible = Keyframes.Visible;
|
||||||
Keyframes.Visible = Visible;
|
Keyframes.Visible = Visible;
|
||||||
if (!Visible)
|
if (!Visible)
|
||||||
{
|
{
|
||||||
Keyframes.ClearSelection();
|
if (wasVisible)
|
||||||
|
Keyframes.ClearSelection();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Keyframes.KeyframesEditorContext = Timeline;
|
Keyframes.KeyframesEditorContext = Timeline;
|
||||||
@@ -393,51 +396,63 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesDeselect(IKeyframesEditor editor)
|
public new void OnKeyframesDeselect(IKeyframesEditor editor)
|
||||||
{
|
{
|
||||||
if (Keyframes != null && Keyframes.Visible)
|
if (Keyframes != null && Keyframes.Visible)
|
||||||
Keyframes.OnKeyframesDeselect(editor);
|
Keyframes.OnKeyframesDeselect(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection)
|
public new void OnKeyframesSelection(IKeyframesEditor editor, ContainerControl control, Rectangle selection)
|
||||||
{
|
{
|
||||||
if (Keyframes != null && Keyframes.Visible)
|
if (Keyframes != null && Keyframes.Visible)
|
||||||
Keyframes.OnKeyframesSelection(editor, control, selection);
|
Keyframes.OnKeyframesSelection(editor, control, selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int OnKeyframesSelectionCount()
|
public new int OnKeyframesSelectionCount()
|
||||||
{
|
{
|
||||||
return Keyframes != null && Keyframes.Visible ? Keyframes.OnKeyframesSelectionCount() : 0;
|
return Keyframes != null && Keyframes.Visible ? Keyframes.OnKeyframesSelectionCount() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesDelete(IKeyframesEditor editor)
|
public new void OnKeyframesDelete(IKeyframesEditor editor)
|
||||||
{
|
{
|
||||||
if (Keyframes != null && Keyframes.Visible)
|
if (Keyframes != null && Keyframes.Visible)
|
||||||
Keyframes.OnKeyframesDelete(editor);
|
Keyframes.OnKeyframesDelete(editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesMove(IKeyframesEditor editor, ContainerControl control, Vector2 location, bool start, bool end)
|
public new void OnKeyframesMove(IKeyframesEditor editor, ContainerControl control, Vector2 location, bool start, bool end)
|
||||||
{
|
{
|
||||||
if (Keyframes != null && Keyframes.Visible)
|
if (Keyframes != null && Keyframes.Visible)
|
||||||
Keyframes.OnKeyframesMove(editor, control, location, start, end);
|
Keyframes.OnKeyframesMove(editor, control, location, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesCopy(IKeyframesEditor editor, float? timeOffset, StringBuilder data)
|
public new void OnKeyframesCopy(IKeyframesEditor editor, float? timeOffset, StringBuilder data)
|
||||||
{
|
{
|
||||||
if (Keyframes != null && Keyframes.Visible)
|
if (Keyframes != null && Keyframes.Visible)
|
||||||
Keyframes.OnKeyframesCopy(editor, timeOffset, data);
|
Keyframes.OnKeyframesCopy(editor, timeOffset, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index)
|
public new void OnKeyframesPaste(IKeyframesEditor editor, float? timeOffset, string[] datas, ref int index)
|
||||||
{
|
{
|
||||||
if (Keyframes != null && Keyframes.Visible)
|
if (Keyframes != null && Keyframes.Visible)
|
||||||
Keyframes.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
Keyframes.OnKeyframesPaste(editor, timeOffset, datas, ref index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public new void OnKeyframesGet(Action<string, float, object> get)
|
||||||
|
{
|
||||||
|
Keyframes?.OnKeyframesGet(Name, get);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public new void OnKeyframesSet(List<KeyValuePair<float, object>> keyframes)
|
||||||
|
{
|
||||||
|
Keyframes?.OnKeyframesSet(keyframes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// The timeline track for animating object member (managed object).
|
/// The timeline track for animating object member (managed object).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
||||||
public abstract class MemberTrack : Track
|
public abstract class MemberTrack : ConductorTrack
|
||||||
{
|
{
|
||||||
private float _previewValueLeft;
|
private float _previewValueLeft;
|
||||||
|
|
||||||
@@ -112,8 +112,9 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// <param name="options">The track initial options.</param>
|
/// <param name="options">The track initial options.</param>
|
||||||
/// <param name="useNavigationButtons">True if show keyframe navigation buttons, otherwise false.</param>
|
/// <param name="useNavigationButtons">True if show keyframe navigation buttons, otherwise false.</param>
|
||||||
/// <param name="useValuePreview">True if show current value preview, otherwise false.</param>
|
/// <param name="useValuePreview">True if show current value preview, otherwise false.</param>
|
||||||
protected MemberTrack(ref TrackCreateOptions options, bool useNavigationButtons = true, bool useValuePreview = true)
|
/// <param name="useProxyKeyframes">True if show sub-tracks keyframes as a proxy on this track, otherwise false.</param>
|
||||||
: base(ref options)
|
protected MemberTrack(ref TrackCreateOptions options, bool useNavigationButtons = true, bool useValuePreview = true, bool useProxyKeyframes = false)
|
||||||
|
: base(ref options, useProxyKeyframes)
|
||||||
{
|
{
|
||||||
var uiLeft = _muteCheckbox.Offsets.Left;
|
var uiLeft = _muteCheckbox.Offsets.Left;
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ObjectPropertyTrack(ref TrackCreateOptions options)
|
public ObjectPropertyTrack(ref TrackCreateOptions options)
|
||||||
: base(ref options, false, false)
|
: base(ref options, false, false, true)
|
||||||
{
|
{
|
||||||
// Add track button
|
// Add track button
|
||||||
const float buttonSize = 14;
|
const float buttonSize = 14;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// The timeline track for animating managed objects.
|
/// The timeline track for animating managed objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
/// <seealso cref="FlaxEditor.GUI.Timeline.Track" />
|
||||||
public abstract class ObjectTrack : Track, IObjectTrack
|
public abstract class ObjectTrack : ConductorTrack, IObjectTrack
|
||||||
{
|
{
|
||||||
private bool _hasObject;
|
private bool _hasObject;
|
||||||
|
|
||||||
@@ -42,9 +42,13 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract object Object { get; }
|
public abstract object Object { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
protected ObjectTrack(ref TrackCreateOptions options)
|
/// Initializes a new instance of the <see cref="ObjectTrack"/> class.
|
||||||
: base(ref options)
|
/// </summary>
|
||||||
|
/// <param name="options">The track initial options.</param>
|
||||||
|
/// <param name="useProxyKeyframes">True if show sub-tracks keyframes as a proxy on this track, otherwise false.</param>
|
||||||
|
protected ObjectTrack(ref TrackCreateOptions options, bool useProxyKeyframes = true)
|
||||||
|
: base(ref options, useProxyKeyframes)
|
||||||
{
|
{
|
||||||
// Add track button
|
// Add track button
|
||||||
const float buttonSize = 14;
|
const float buttonSize = 14;
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public StructPropertyTrack(ref TrackCreateOptions options)
|
public StructPropertyTrack(ref TrackCreateOptions options)
|
||||||
: base(ref options, false, false)
|
: base(ref options, false, false, true)
|
||||||
{
|
{
|
||||||
// Add track button
|
// Add track button
|
||||||
const float buttonSize = 14;
|
const float buttonSize = 14;
|
||||||
|
|||||||
@@ -378,6 +378,12 @@ namespace FlaxEngine
|
|||||||
{
|
{
|
||||||
return Time > other.Time ? 1 : 0;
|
return Time > other.Time ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Value?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -655,6 +661,12 @@ namespace FlaxEngine
|
|||||||
{
|
{
|
||||||
return Time > other.Time ? 1 : 0;
|
return Time > other.Time ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Value?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -254,6 +254,16 @@ namespace FlaxEngine
|
|||||||
return string.Concat(graphemes);
|
return string.Concat(graphemes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes any new line characters (\r or \n) from the string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="s">The string to process.</param>
|
||||||
|
/// <returns>The single-line string.</returns>
|
||||||
|
public static string RemoveNewLine(this string s)
|
||||||
|
{
|
||||||
|
return s.Replace("\n", "").Replace("\r", "");
|
||||||
|
}
|
||||||
|
|
||||||
private static readonly Regex IncNameRegex1 = new Regex("(\\d+)$");
|
private static readonly Regex IncNameRegex1 = new Regex("(\\d+)$");
|
||||||
private static readonly Regex IncNameRegex2 = new Regex("\\((\\d+)\\)$");
|
private static readonly Regex IncNameRegex2 = new Regex("\\((\\d+)\\)$");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user