// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
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;
using FlaxEditor.GUI.Timeline.GUI;
using FlaxEditor.GUI.Timeline.Tracks;
using FlaxEditor.GUI.Timeline.Undo;
using FlaxEngine;
using FlaxEngine.GUI;
namespace FlaxEditor.GUI.Timeline
{
///
/// 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.
///
///
public class Timeline : ContainerControl
{
private static readonly KeyValuePair[] FPSValues =
{
new KeyValuePair(12f, "12 fps"),
new KeyValuePair(15f, "15 fps"),
new KeyValuePair(23.976f, "23.97 (NTSC)"),
new KeyValuePair(24f, "24 fps"),
new KeyValuePair(25f, "25 (PAL)"),
new KeyValuePair(30f, "30 fps"),
new KeyValuePair(48f, "48 fps"),
new KeyValuePair(50f, "50 (PAL)"),
new KeyValuePair(60f, "60 fps"),
new KeyValuePair(100f, "100 fps"),
new KeyValuePair(120f, "120 fps"),
new KeyValuePair(240f, "240 fps"),
new KeyValuePair(0, "Custom"),
};
internal const int FormatVersion = 3;
private sealed class TimeIntervalsHeader : ContainerControl
{
private Timeline _timeline;
private bool _isLeftMouseButtonDown;
public TimeIntervalsHeader(Timeline timeline)
{
_timeline = timeline;
}
///
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;
}
///
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);
}
///
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;
}
///
public override void OnEndMouseCapture()
{
_isLeftMouseButtonDown = false;
_timeline._isMovingPositionHandle = false;
base.OnEndMouseCapture();
}
///
public override void OnDestroy()
{
_timeline = null;
base.OnDestroy();
}
}
///
/// The base class for timeline properties proxy objects.
///
/// The type of the timeline.
public abstract class ProxyBase
where TTimeline : Timeline
{
///
/// The timeline reference.
///
[HideInEditor, NoSerialize]
public TTimeline Timeline;
///
/// Gets or sets the total duration of the timeline in the frames amount.
///
[EditorDisplay("General"), EditorOrder(10), Limit(1), Tooltip("Total duration of the timeline event the frames amount.")]
public int DurationFrames
{
get => Timeline.DurationFrames;
set => Timeline.DurationFrames = value;
}
///
/// Initializes a new instance of the class.
///
/// The timeline.
protected ProxyBase(TTimeline timeline)
{
Timeline = timeline ?? throw new ArgumentNullException(nameof(timeline));
}
}
///
/// The time axis value formatting modes.
///
public enum TimeShowModes
{
///
/// The frame numbers.
///
Frames,
///
/// The seconds amount.
///
Seconds,
///
/// The time.
///
Time,
}
///
/// The timeline animation playback states.
///
public enum PlaybackStates
{
///
/// The timeline animation feature is disabled.
///
Disabled,
///
/// The timeline animation feature is disabled except for current frame seeking.
///
Seeking,
///
/// The timeline animation is stopped.
///
Stopped,
///
/// The timeline animation is playing.
///
Playing,
///
/// The timeline animation is paused.
///
Paused,
}
///
/// The timeline playback buttons types.
///
[Flags]
public enum PlaybackButtons
{
///
/// No buttons.
///
None = 0,
///
/// The play/pause button.
///
Play = 1,
///
/// The stop button.
///
Stop = 2,
///
/// The current frame navigation buttons (left/right frame, seep begin/end).
///
Navigation = 4,
}
///
/// The header top area height (in pixels).
///
public static readonly float HeaderTopAreaHeight = 22.0f;
///
/// The timeline units per second (on time axis).
///
public static readonly float UnitsPerSecond = 100.0f;
///
/// The start offset for the timeline (on time axis).
///
public static readonly float StartOffset = 50.0f;
private bool _isChangingFps;
private float _framesPerSecond = 30.0f;
private int _durationFrames = 30 * 5;
private int _currentFrame;
private TimeShowModes _timeShowMode = TimeShowModes.Frames;
private bool _showPreviewValues = true;
private PlaybackStates _state = PlaybackStates.Disabled;
///
/// Flag used to mark modified timeline data.
///
protected bool _isModified;
///
/// The tracks collection.
///
protected readonly List