// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR && UNITY_IMGUI
using System;
using UnityEditor;
using UnityEngine;
using static Animancer.Editor.AnimancerGUI;
using Object = UnityEngine.Object;
namespace Animancer.Editor.Previews
{
/// [Editor-Only]
/// An interactive preview which displays the internal details of an .
///
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/AnimancerComponentPreview
[CustomPreview(typeof(AnimancerComponent))]
public class AnimancerComponentPreview : ObjectPreview
{
/************************************************************************************************************************/
private static readonly GUIContent
Title = new(nameof(Animancer));
///
public override GUIContent GetPreviewTitle()
=> Title;
/************************************************************************************************************************/
[NonSerialized] private IAnimancerComponent _Animancer;
[NonSerialized] private UnityEditor.Editor _Editor;
/// The drawer for the .
private readonly AnimancerGraphDrawer
GraphDrawer = new();
/************************************************************************************************************************/
///
public override void Initialize(Object[] targets)
{
_Animancer = targets.Length == 1
? targets[0] as IAnimancerComponent
: null;
_Editor = UnityEditor.Editor.CreateEditor(targets);
base.Initialize(targets);
EditorApplication.update += Update;
}
/************************************************************************************************************************/
///
public override void Cleanup()
{
EditorApplication.update -= Update;
Object.DestroyImmediate(_Editor);
base.Cleanup();
}
/************************************************************************************************************************/
///
public override bool HasPreviewGUI()
=> !_Animancer.IsNullOrDestroyed()
&& _Animancer.IsGraphInitialized;
/************************************************************************************************************************/
private static GUIStyle _ToolbarButtonStyle;
///
public override void OnPreviewSettings()
{
base.OnPreviewSettings();
_ToolbarButtonStyle ??= new(EditorStyles.toolbarButton)
{
padding = new(),
};
var graph = _Animancer.Graph;
if (!graph.IsGraphPlaying)
{
var stepArea = GUILayoutUtility.GetRect(LineHeight * 1.5f, LineHeight);
AnimancerGraphControls.DoFrameStepButton(stepArea, graph, _ToolbarButtonStyle);
}
var area = GUILayoutUtility.GetRect(LineHeight * 1.5f, LineHeight);
AnimancerGraphControls.DoPlayPauseToggle(area, graph, _ToolbarButtonStyle);
area = GUILayoutUtility.GetRect(LineHeight * 2f, LineHeight);
AnimancerGraphSpeedSlider.Instance.Graph = graph;
AnimancerGraphSpeedSlider.Instance.DoToggleGUI(area, _ToolbarButtonStyle);
}
/************************************************************************************************************************/
[NonSerialized]
private static GUIStyle _PaddingStyle;
[NonSerialized]
private Rect _Area;
[SerializeField]
private Vector2 _ScrollPosition;
///
public override void OnInteractivePreviewGUI(Rect area, GUIStyle background)
{
_PaddingStyle ??= new()
{
padding = new((int)StandardSpacing, (int)StandardSpacing, (int)StandardSpacing, (int)StandardSpacing),
};
// The area isn't properly set during Layout events so remember it after each Repaint.
if (area.y == 0)
area.y = EditorStyles.toolbar.fixedHeight + 1;
if (Event.current.type == EventType.Repaint)
_Area = area;
// Draw the graph.
var labelWidth = EditorGUIUtility.labelWidth;
EditorGUIUtility.labelWidth += IndentSize;
GUILayout.BeginArea(_Area);
_ScrollPosition = GUILayout.BeginScrollView(_ScrollPosition, _PaddingStyle);
GraphDrawer.DoGUI(_Animancer);
GUILayout.EndScrollView();
GUILayout.EndArea();
EditorGUIUtility.labelWidth = labelWidth;
_LastRepaintTime = EditorApplication.timeSinceStartup;
}
/************************************************************************************************************************/
[NonSerialized]
private double _LastRepaintTime = double.NegativeInfinity;
/// Repaints the preview if necessary.
private void Update()
{
if (!HasPreviewGUI() ||
!UnityEditorInternal.InternalEditorUtility.isApplicationActive)
return;
var targetDeltaTime = 1f / AnimancerComponentPreviewSettings.RepaintRate;
var nextRepaintTime = _LastRepaintTime + targetDeltaTime;
if (EditorApplication.timeSinceStartup > nextRepaintTime)
_Editor.Repaint();
// This seems to be the least hacky way to repaint only the Inspector window.
// Ideally an interactive preview would have a way to repaint itself.
}
/************************************************************************************************************************/
}
/************************************************************************************************************************/
#region Settings
/************************************************************************************************************************/
/// [Editor-Only] Settings for .
/// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/AnimancerComponentPreviewSettings
[Serializable, InternalSerializableType]
public class AnimancerComponentPreviewSettings : AnimancerSettingsGroup
{
/************************************************************************************************************************/
///
public override string DisplayName
=> "Live Inspector";
///
public override int Index
=> 1;
/************************************************************************************************************************/
[SerializeField, Range(1, 100)]
[Tooltip("The target frame rate of repaint commands (FPS)")]
private float _RepaintRate = 30;
/// The target frame rate of repaint commands (FPS).
public static float RepaintRate
=> AnimancerSettingsGroup.Instance._RepaintRate;
/************************************************************************************************************************/
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
}
#endif