// 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