AnimancerComponentPreview.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR && UNITY_IMGUI
  3. using System;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using static Animancer.Editor.AnimancerGUI;
  7. using Object = UnityEngine.Object;
  8. namespace Animancer.Editor.Previews
  9. {
  10. /// <summary>[Editor-Only]
  11. /// An interactive preview which displays the internal details of an <see cref="AnimancerComponent"/>.
  12. /// </summary>
  13. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/AnimancerComponentPreview
  14. [CustomPreview(typeof(AnimancerComponent))]
  15. public class AnimancerComponentPreview : ObjectPreview
  16. {
  17. /************************************************************************************************************************/
  18. private static readonly GUIContent
  19. Title = new(nameof(Animancer));
  20. /// <inheritdoc/>
  21. public override GUIContent GetPreviewTitle()
  22. => Title;
  23. /************************************************************************************************************************/
  24. [NonSerialized] private IAnimancerComponent _Animancer;
  25. [NonSerialized] private UnityEditor.Editor _Editor;
  26. /// <summary>The drawer for the <see cref="IAnimancerComponent.Graph"/>.</summary>
  27. private readonly AnimancerGraphDrawer
  28. GraphDrawer = new();
  29. /************************************************************************************************************************/
  30. /// <inheritdoc/>
  31. public override void Initialize(Object[] targets)
  32. {
  33. _Animancer = targets.Length == 1
  34. ? targets[0] as IAnimancerComponent
  35. : null;
  36. _Editor = UnityEditor.Editor.CreateEditor(targets);
  37. base.Initialize(targets);
  38. EditorApplication.update += Update;
  39. }
  40. /************************************************************************************************************************/
  41. /// <inheritdoc/>
  42. public override void Cleanup()
  43. {
  44. EditorApplication.update -= Update;
  45. Object.DestroyImmediate(_Editor);
  46. base.Cleanup();
  47. }
  48. /************************************************************************************************************************/
  49. /// <inheritdoc/>
  50. public override bool HasPreviewGUI()
  51. => !_Animancer.IsNullOrDestroyed()
  52. && _Animancer.IsGraphInitialized;
  53. /************************************************************************************************************************/
  54. private static GUIStyle _ToolbarButtonStyle;
  55. /// <inheritdoc/>
  56. public override void OnPreviewSettings()
  57. {
  58. base.OnPreviewSettings();
  59. _ToolbarButtonStyle ??= new(EditorStyles.toolbarButton)
  60. {
  61. padding = new(),
  62. };
  63. var graph = _Animancer.Graph;
  64. if (!graph.IsGraphPlaying)
  65. {
  66. var stepArea = GUILayoutUtility.GetRect(LineHeight * 1.5f, LineHeight);
  67. AnimancerGraphControls.DoFrameStepButton(stepArea, graph, _ToolbarButtonStyle);
  68. }
  69. var area = GUILayoutUtility.GetRect(LineHeight * 1.5f, LineHeight);
  70. AnimancerGraphControls.DoPlayPauseToggle(area, graph, _ToolbarButtonStyle);
  71. area = GUILayoutUtility.GetRect(LineHeight * 2f, LineHeight);
  72. AnimancerGraphSpeedSlider.Instance.Graph = graph;
  73. AnimancerGraphSpeedSlider.Instance.DoToggleGUI(area, _ToolbarButtonStyle);
  74. }
  75. /************************************************************************************************************************/
  76. [NonSerialized]
  77. private static GUIStyle _PaddingStyle;
  78. [NonSerialized]
  79. private Rect _Area;
  80. [SerializeField]
  81. private Vector2 _ScrollPosition;
  82. /// <inheritdoc/>
  83. public override void OnInteractivePreviewGUI(Rect area, GUIStyle background)
  84. {
  85. _PaddingStyle ??= new()
  86. {
  87. padding = new((int)StandardSpacing, (int)StandardSpacing, (int)StandardSpacing, (int)StandardSpacing),
  88. };
  89. // The area isn't properly set during Layout events so remember it after each Repaint.
  90. if (area.y == 0)
  91. area.y = EditorStyles.toolbar.fixedHeight + 1;
  92. if (Event.current.type == EventType.Repaint)
  93. _Area = area;
  94. // Draw the graph.
  95. var labelWidth = EditorGUIUtility.labelWidth;
  96. EditorGUIUtility.labelWidth += IndentSize;
  97. GUILayout.BeginArea(_Area);
  98. _ScrollPosition = GUILayout.BeginScrollView(_ScrollPosition, _PaddingStyle);
  99. GraphDrawer.DoGUI(_Animancer);
  100. GUILayout.EndScrollView();
  101. GUILayout.EndArea();
  102. EditorGUIUtility.labelWidth = labelWidth;
  103. _LastRepaintTime = EditorApplication.timeSinceStartup;
  104. }
  105. /************************************************************************************************************************/
  106. [NonSerialized]
  107. private double _LastRepaintTime = double.NegativeInfinity;
  108. /// <summary>Repaints the preview if necessary.</summary>
  109. private void Update()
  110. {
  111. if (!HasPreviewGUI() ||
  112. !UnityEditorInternal.InternalEditorUtility.isApplicationActive)
  113. return;
  114. var targetDeltaTime = 1f / AnimancerComponentPreviewSettings.RepaintRate;
  115. var nextRepaintTime = _LastRepaintTime + targetDeltaTime;
  116. if (EditorApplication.timeSinceStartup > nextRepaintTime)
  117. _Editor.Repaint();
  118. // This seems to be the least hacky way to repaint only the Inspector window.
  119. // Ideally an interactive preview would have a way to repaint itself.
  120. }
  121. /************************************************************************************************************************/
  122. }
  123. /************************************************************************************************************************/
  124. #region Settings
  125. /************************************************************************************************************************/
  126. /// <summary>[Editor-Only] Settings for <see cref="AnimancerComponentPreview"/>.</summary>
  127. /// https://kybernetik.com.au/animancer/api/Animancer.Editor.Previews/AnimancerComponentPreviewSettings
  128. [Serializable, InternalSerializableType]
  129. public class AnimancerComponentPreviewSettings : AnimancerSettingsGroup
  130. {
  131. /************************************************************************************************************************/
  132. /// <inheritdoc/>
  133. public override string DisplayName
  134. => "Live Inspector";
  135. /// <inheritdoc/>
  136. public override int Index
  137. => 1;
  138. /************************************************************************************************************************/
  139. [SerializeField, Range(1, 100)]
  140. [Tooltip("The target frame rate of repaint commands (FPS)")]
  141. private float _RepaintRate = 30;
  142. /// <summary>The target frame rate of repaint commands (FPS).</summary>
  143. public static float RepaintRate
  144. => AnimancerSettingsGroup<AnimancerComponentPreviewSettings>.Instance._RepaintRate;
  145. /************************************************************************************************************************/
  146. }
  147. /************************************************************************************************************************/
  148. #endregion
  149. /************************************************************************************************************************/
  150. }
  151. #endif