| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416 | 
							- // Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
 
- #if UNITY_EDITOR && UNITY_IMGUI
 
- using System;
 
- using UnityEditor;
 
- using UnityEngine;
 
- using UnityEngine.Playables;
 
- using static Animancer.Editor.AnimancerGUI;
 
- using Object = UnityEngine.Object;
 
- namespace Animancer.Editor
 
- {
 
-     /// <summary>[Editor-Only] Draws the Inspector GUI for an <see cref="AnimancerNode"/>.</summary>
 
-     /// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerNodeDrawer_1
 
-     /// 
 
-     public abstract class AnimancerNodeDrawer<T> : CustomGUI<T>
 
-         where T : AnimancerNode
 
-     {
 
-         /************************************************************************************************************************/
 
-         /// <summary>Extra padding for the left side of the labels.</summary>
 
-         public const float ExtraLeftPadding = 3;
 
-         /************************************************************************************************************************/
 
-         /// <summary>Should the target node's details be expanded in the Inspector?</summary>
 
-         public ref bool IsExpanded
 
-             => ref Value._IsInspectorExpanded;
 
-         /************************************************************************************************************************/
 
-         /// <inheritdoc/>
 
-         public override void DoGUI()
 
-         {
 
-             if (!Value.IsValid())
 
-                 return;
 
-             GUILayout.BeginVertical();
 
-             {
 
-                 DoHeaderGUI();
 
-                 DoDetailsGUI();
 
-             }
 
-             GUILayout.EndVertical();
 
-             if (TryUseClickEvent(GUILayoutUtility.GetLastRect(), 1))
 
-                 OpenContextMenu();
 
-         }
 
-         /************************************************************************************************************************/
 
-         /// <summary>Draws the name and other details of the <see cref="CustomGUI{T}.Value"/> in the GUI.</summary>
 
-         protected virtual void DoHeaderGUI()
 
-         {
 
-             var area = LayoutSingleLineRect(SpacingMode.Before);
 
-             DoLabelGUI(area);
 
-             DoFoldoutGUI(area);
 
-         }
 
-         /************************************************************************************************************************/
 
-         /// <summary>
 
-         /// Draws a field for the <see cref="AnimancerState.MainObject"/> if it has one, otherwise just a simple text
 
-         /// label.
 
-         /// </summary>
 
-         protected abstract void DoLabelGUI(Rect area);
 
-         /// <summary>Draws a foldout arrow to expand/collapse the node details.</summary>
 
-         protected abstract void DoFoldoutGUI(Rect area);
 
-         /************************************************************************************************************************/
 
-         private FastObjectField _DebugNameField;
 
-         /// <summary>Draws the details of the <see cref="CustomGUI{T}.Value"/>.</summary>
 
-         protected virtual void DoDetailsGUI()
 
-         {
 
-             if (!IsExpanded)
 
-                 return;
 
-             var debugName = Value.DebugName;
 
-             if (debugName == null)
 
-                 return;
 
-             var area = LayoutSingleLineRect(SpacingMode.Before);
 
-             area = EditorGUI.IndentedRect(area);
 
-             _DebugNameField.Draw(area, "Debug Name", debugName);
 
-         }
 
-         /************************************************************************************************************************/
 
-         private static readonly int FloatFieldHash = "EditorTextField".GetHashCode();
 
-         /// <summary>
 
-         /// Draws controls for <see cref="AnimancerState.IsPlaying"/>, <see cref="AnimancerNodeBase.Speed"/>, and
 
-         /// <see cref="AnimancerNode.Weight"/>.
 
-         /// </summary>
 
-         protected void DoNodeDetailsGUI()
 
-         {
 
-             var area = LayoutSingleLineRect(SpacingMode.Before);
 
-             area.xMin += EditorGUI.indentLevel * IndentSize + ExtraLeftPadding;
 
-             var xMin = area.xMin;
 
-             var labelWidth = EditorGUIUtility.labelWidth;
 
-             var indentLevel = EditorGUI.indentLevel;
 
-             EditorGUI.indentLevel = 0;
 
-             // Is Playing.
 
-             if (Value is AnimancerState state)
 
-             {
 
-                 var buttonArea = StealFromLeft(ref area, LineHeight, StandardSpacing);
 
-                 state.IsPlaying = DoPlayPauseToggle(buttonArea, state.IsPlaying);
 
-             }
 
-             SplitHorizontally(area, "Speed", "Weight",
 
-                 out var speedWidth,
 
-                 out var weightWidth,
 
-                 out var speedRect,
 
-                 out var weightRect);
 
-             // Speed.
 
-             EditorGUIUtility.labelWidth = speedWidth;
 
-             EditorGUI.BeginChangeCheck();
 
-             var speed = EditorGUI.FloatField(speedRect, "Speed", Value.Speed);
 
-             if (EditorGUI.EndChangeCheck())
 
-                 Value.Speed = speed;
 
-             if (TryUseClickEvent(speedRect, 2))
 
-                 Value.Speed = Value.Speed != 1 ? 1 : 0;
 
-             // Weight.
 
-             EditorGUIUtility.labelWidth = weightWidth;
 
-             EditorGUI.BeginChangeCheck();
 
-             var weight = EditorGUI.FloatField(weightRect, "Weight", Value.Weight);
 
-             if (EditorGUI.EndChangeCheck())
 
-                 SetWeight(Mathf.Max(weight, 0));
 
-             if (TryUseClickEvent(weightRect, 2))
 
-                 SetWeight(Value.Weight != 1 ? 1 : 0);
 
-             // Real Speed.
 
-             // Mixer Synchronization changes the internal Playable Speed without setting the State Speed.
 
-             speed = (float)Value._Playable.GetSpeed();
 
-             if (Value.Speed != speed)
 
-             {
 
-                 using (new EditorGUI.DisabledScope(true))
 
-                 {
 
-                     area = LayoutSingleLineRect(SpacingMode.Before);
 
-                     area.xMin = xMin;
 
-                     var label = BeginTightLabel("Real Speed");
 
-                     EditorGUIUtility.labelWidth = CalculateLabelWidth(label);
 
-                     EditorGUI.FloatField(area, label, speed);
 
-                     EndTightLabel();
 
-                 }
 
-             }
 
-             else// Add a dummy ID so that subsequent IDs don't change when the Real Speed appears or disappears.
 
-             {
 
-                 GUIUtility.GetControlID(FloatFieldHash, FocusType.Keyboard);
 
-             }
 
-             EditorGUI.indentLevel = indentLevel;
 
-             EditorGUIUtility.labelWidth = labelWidth;
 
-             DoFadeDetailsGUI();
 
-         }
 
-         /************************************************************************************************************************/
 
-         /// <summary>Indicates whether changing the <see cref="AnimancerNode.Weight"/> should normalize its siblings.</summary>
 
-         protected virtual bool AutoNormalizeSiblingWeights
 
-             => false;
 
-         private void SetWeight(float weight)
 
-         {
 
-             if (weight < 0 ||
 
-                 weight > 1 ||
 
-                 Mathf.Approximately(Value.Weight, 1) ||
 
-                 !AutoNormalizeSiblingWeights)
 
-                 goto JustSetWeight;
 
-             var parent = Value.Parent;
 
-             if (parent == null)
 
-                 goto JustSetWeight;
 
-             var totalWeight = 0f;
 
-             var siblingCount = parent.ChildCount;
 
-             for (int i = 0; i < siblingCount; i++)
 
-             {
 
-                 var sibling = parent.GetChildNode(i);
 
-                 if (sibling.IsValid())
 
-                     totalWeight += sibling.Weight;
 
-             }
 
-             // If the weights weren't previously normalized, don't normalize them now.
 
-             if (!Mathf.Approximately(totalWeight, 1))
 
-                 goto JustSetWeight;
 
-             var siblingWeightMultiplier = (totalWeight - weight) / (totalWeight - Value.Weight);
 
-             for (int i = 0; i < siblingCount; i++)
 
-             {
 
-                 var sibling = parent.GetChildNode(i);
 
-                 if (sibling != Value && sibling.IsValid())
 
-                     sibling.Weight *= siblingWeightMultiplier;
 
-             }
 
-             JustSetWeight:
 
-             Value.Weight = weight;
 
-         }
 
-         /************************************************************************************************************************/
 
-         private float
 
-             _FadeDuration = float.NaN,
 
-             _TargetWeight = float.NaN;
 
-         /// <summary>
 
-         /// Draws the <see cref="AnimancerNode.FadeSpeed"/>
 
-         /// and <see cref="AnimancerNode.TargetWeight"/>.
 
-         /// </summary>
 
-         private void DoFadeDetailsGUI()
 
-         {
 
-             var area = LayoutSingleLineRect(SpacingMode.Before);
 
-             area = EditorGUI.IndentedRect(area);
 
-             area.xMin += ExtraLeftPadding;
 
-             var durationLabel = "Fade Duration";
 
-             var targetLabel = "Target Weight";
 
-             SplitHorizontally(
 
-                 area,
 
-                 durationLabel,
 
-                 targetLabel,
 
-                 out var durationWidth,
 
-                 out var weightWidth,
 
-                 out var durationRect,
 
-                 out var weightRect);
 
-             var labelWidth = EditorGUIUtility.labelWidth;
 
-             var indentLevel = EditorGUI.indentLevel;
 
-             EditorGUI.indentLevel = 0;
 
-             EditorGUI.BeginChangeCheck();
 
-             var fade = Value.FadeGroup;
 
-             var fadeDuration = DoFadeDurationGUI(durationWidth, durationRect, durationLabel, fade);
 
-             var targetWeight = DoTargetWeightGUI(weightWidth, weightRect, targetLabel, fade);
 
-             if (EditorGUI.EndChangeCheck())
 
-                 SetFade(targetWeight, fadeDuration);
 
-             EditorGUI.indentLevel = indentLevel;
 
-             EditorGUIUtility.labelWidth = labelWidth;
 
-         }
 
-         /************************************************************************************************************************/
 
-         private float DoFadeDurationGUI(
 
-             float labelWidth,
 
-             Rect area,
 
-             string label,
 
-             FadeGroup fade)
 
-         {
 
-             EditorGUIUtility.labelWidth = labelWidth;
 
-             var fadeDuration = fade != null ? fade.FadeDuration : _FadeDuration;
 
-             fadeDuration = EditorGUI.DelayedFloatField(area, label, fadeDuration);
 
-             if (fadeDuration > 0)
 
-             {
 
-             }
 
-             else// NaN or Negative.
 
-             {
 
-                 fadeDuration = _FadeDuration = float.NaN;
 
-             }
 
-             if (TryUseClickEvent(area, 2))
 
-             {
 
-                 var defaultFadeDuration = AnimancerGraph.DefaultFadeDuration;
 
-                 if (fadeDuration != 0 || defaultFadeDuration == 0)
 
-                 {
 
-                     fadeDuration = 0;
 
-                 }
 
-                 else
 
-                 {
 
-                     var fadeDistance = Math.Abs(Value.Weight - Value.TargetWeight);
 
-                     if (fadeDistance != 0)
 
-                     {
 
-                         fadeDuration = fadeDistance / defaultFadeDuration;
 
-                     }
 
-                     else
 
-                     {
 
-                         fadeDuration = defaultFadeDuration;
 
-                     }
 
-                 }
 
-             }
 
-             return fadeDuration;
 
-         }
 
-         /************************************************************************************************************************/
 
-         private float DoTargetWeightGUI(
 
-             float labelWidth,
 
-             Rect area,
 
-             string label,
 
-             FadeGroup fade)
 
-         {
 
-             EditorGUIUtility.labelWidth = labelWidth;
 
-             var targetWeight = fade != null
 
-                 ? fade.TargetWeight
 
-                 : _TargetWeight.IsFinite()
 
-                 ? _TargetWeight
 
-                 : Value.Weight;
 
-             targetWeight = EditorGUI.DelayedFloatField(area, label, targetWeight);
 
-             if (targetWeight >= 0)
 
-             {
 
-             }
 
-             else// NaN or Negative.
 
-             {
 
-                 targetWeight = _TargetWeight = float.NaN;
 
-             }
 
-             if (TryUseClickEvent(area, 2))
 
-             {
 
-                 if (targetWeight != Value.Weight)
 
-                     targetWeight = Value.Weight;
 
-                 else if (targetWeight != 1)
 
-                     targetWeight = 1;
 
-                 else
 
-                     targetWeight = 0;
 
-             }
 
-             return targetWeight;
 
-         }
 
-         /************************************************************************************************************************/
 
-         /// <summary>Starts a fade or changes the details of an existing one.</summary>
 
-         private void SetFade(float targetWeight, float fadeDuration)
 
-         {
 
-             _TargetWeight = targetWeight;
 
-             _FadeDuration = fadeDuration;
 
-             if (!targetWeight.IsFinite() ||
 
-                 !fadeDuration.IsFinite() ||
 
-                 targetWeight == Value.Weight ||
 
-                 fadeDuration <= 0)
 
-                 return;
 
-             // If it's a state attached to a layer, start a proper cross fade.
 
-             if (Value is AnimancerState state &&
 
-                 state.Parent is AnimancerLayer layer)
 
-             {
 
-                 layer.Play(state, fadeDuration, FadeMode.FixedDuration);
 
-                 // That might not have started a fade if the state was already playing,
 
-                 // So just continue to verify its details.
 
-             }
 
-             var fade = Value.FadeGroup;
 
-             if (fade != null && fade.FadeIn.Node == Value)
 
-             {
 
-                 fade.TargetWeight = targetWeight;
 
-                 fade.FadeDuration = fadeDuration;
 
-                 return;
 
-             }
 
-             Value.StartFade(targetWeight, fadeDuration);
 
-         }
 
-         /************************************************************************************************************************/
 
-         #region Context Menu
 
-         /************************************************************************************************************************/
 
-         /// <summary>
 
-         /// The menu label prefix used for details about the <see cref="CustomGUI{T}.Value"/>.
 
-         /// </summary>
 
-         protected const string DetailsPrefix = "Details/";
 
-         /// <summary>
 
-         /// Checks if the current event is a context menu click within the `clickArea` and opens a context menu with various
 
-         /// functions for the <see cref="CustomGUI{T}.Value"/>.
 
-         /// </summary>
 
-         protected void OpenContextMenu()
 
-         {
 
-             var menu = new GenericMenu();
 
-             menu.AddDisabledItem(new(Value.ToString()));
 
-             PopulateContextMenu(menu);
 
-             menu.AddItem(new(DetailsPrefix + "Log Details"), false,
 
-                 () => Debug.Log(Value.GetDescription(), Value.Graph?.Component as Object));
 
-             menu.AddItem(new(DetailsPrefix + "Log Details Of Everything"), false,
 
-                 () => Debug.Log(Value.Graph.GetDescription(), Value.Graph?.Component as Object));
 
-             AnimancerGraphDrawer.AddPlayableGraphVisualizerFunction(menu, DetailsPrefix, Value.Graph._PlayableGraph);
 
-             menu.ShowAsContext();
 
-         }
 
-         /// <summary>Adds functions relevant to the <see cref="CustomGUI{T}.Value"/>.</summary>
 
-         protected abstract void PopulateContextMenu(GenericMenu menu);
 
-         /************************************************************************************************************************/
 
-         #endregion
 
-         /************************************************************************************************************************/
 
-     }
 
- }
 
- #endif
 
 
  |