// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // #if UNITY_EDITOR && UNITY_IMGUI using Animancer.Editor; using Animancer.Editor.Previews; using System; using UnityEditor; using UnityEngine; namespace Animancer.Units.Editor { /// [Editor-Only] /// A for fields with a /// which displays them using 3 fields: Normalized, Seconds, and Frames. /// /// /// Documentation: /// /// Time Fields /// /// https://kybernetik.com.au/animancer/api/Animancer.Units.Editor/AnimationTimeAttributeDrawer [CustomPropertyDrawer(typeof(AnimationTimeAttribute), true)] public class AnimationTimeAttributeDrawer : UnitsAttributeDrawer { /************************************************************************************************************************/ /// The default value to be used for the next field drawn by this attribute. public static float NextDefaultValue { get; set; } = float.NaN; /************************************************************************************************************************/ /// protected override int GetLineCount(SerializedProperty property, GUIContent label) => EditorGUIUtility.wideMode || TransitionDrawer.Context.Property == null ? 1 : 2; /************************************************************************************************************************/ /// public override void OnGUI(Rect area, SerializedProperty property, GUIContent label) { EditorGUI.BeginChangeCheck(); var nextDefaultValue = NextDefaultValue; BeginProperty(area, property, ref label, out var value); OnGUI(area, label, ref value); EndProperty(area, property, ref value); if (EditorGUI.EndChangeCheck()) { var index = (int)AnimationTimeAttribute.Units.Normalized; TransitionPreviewWindow.PreviewNormalizedTime = GetDisplayValue(value, nextDefaultValue) * Attribute.Multipliers[index]; } } /************************************************************************************************************************/ /// Draws the GUI for this attribute. public void OnGUI(Rect area, GUIContent label, ref float value) { Initialize(); var context = TransitionDrawer.Context; if (context.Property == null) { value = DoSpecialFloatField(area, label, value, DisplayConverters[Attribute.UnitIndex]); goto Return; } var length = context.MaximumDuration; if (length <= 0) length = float.NaN; AnimancerUtilities.TryGetFrameRate(context.Transition, out var frameRate); var multipliers = CalculateMultipliers(length, frameRate); if (multipliers == null) { EditorGUI.LabelField(area, label.text, $"Invalid {nameof(Validate)}.{nameof(Validate.Value)}"); goto Return; } DoPreviewTimeButton(ref area, ref value, multipliers); Attribute.IsOptional = !float.IsNaN(NextDefaultValue); Attribute.DefaultValue = NextDefaultValue; DoFieldGUI(area, label, ref value); Return: NextDefaultValue = float.NaN; } /************************************************************************************************************************/ private float[] CalculateMultipliers(float length, float frameRate) { switch ((AnimationTimeAttribute.Units)Attribute.UnitIndex) { case AnimationTimeAttribute.Units.Normalized: Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Normalized] = 1; Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Seconds] = length; Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Frames] = length * frameRate; break; case AnimationTimeAttribute.Units.Seconds: Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Normalized] = 1f / length; Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Seconds] = 1; Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Frames] = frameRate; break; case AnimationTimeAttribute.Units.Frames: Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Normalized] = 1f / length / frameRate; Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Seconds] = 1f / frameRate; Attribute.Multipliers[(int)AnimationTimeAttribute.Units.Frames] = 1; break; default: return null; } var settings = AnimancerSettingsGroup.Instance; ApplyVisibilitySetting(settings.showNormalized, AnimationTimeAttribute.Units.Normalized); ApplyVisibilitySetting(settings.showSeconds, AnimationTimeAttribute.Units.Seconds); ApplyVisibilitySetting(settings.showFrames, AnimationTimeAttribute.Units.Frames); void ApplyVisibilitySetting(bool show, AnimationTimeAttribute.Units setting) { if (show) return; var index = (int)setting; if (Attribute.UnitIndex != index) Attribute.Multipliers[index] = float.NaN; } return Attribute.Multipliers; } /************************************************************************************************************************/ private void DoPreviewTimeButton( ref Rect area, ref float value, float[] multipliers) { if (!TransitionPreviewWindow.IsPreviewingCurrentProperty()) return; var previewTime = TransitionPreviewWindow.PreviewNormalizedTime; const string Tooltip = "� Left Click = preview the current value of this field." + "\n� Right Click = set this field to use the current preview time."; var displayValue = GetDisplayValue(value, NextDefaultValue); var multiplier = multipliers[(int)AnimationTimeAttribute.Units.Normalized]; displayValue *= multiplier; var isCurrent = Mathf.Approximately(displayValue, previewTime); var buttonArea = area; if (TransitionDrawer.DoPreviewButtonGUI(ref buttonArea, isCurrent, Tooltip)) { if (Event.current.button != 1) TransitionPreviewWindow.PreviewNormalizedTime = displayValue; else value = previewTime / multiplier; } // Only steal the button area for single line fields. if (area.height <= AnimancerGUI.LineHeight) area = buttonArea; } /************************************************************************************************************************/ } /************************************************************************************************************************/ #region Settings /************************************************************************************************************************/ /// [Editor-Only] Options to determine how displays. /// https://kybernetik.com.au/animancer/api/Animancer.Units.Editor/AnimationTimeAttributeSettings [Serializable, InternalSerializableType] public class AnimationTimeAttributeSettings : AnimancerSettingsGroup { /************************************************************************************************************************/ /// public override string DisplayName => "Time Fields"; /// public override int Index => 5; /************************************************************************************************************************/ /// Should time fields show approximations if the value is too long for the GUI? /// This setting is used by . [Tooltip("Should time fields show approximations if the value is too long for the GUI?" + " For example, '1.111111' could instead show '1.111~'.")] public bool showApproximations = true; /// Should the field be shown? /// This setting is ignored for fields which directly store the normalized value. [Tooltip("Should the " + nameof(AnimationTimeAttribute.Units.Normalized) + " field be shown?")] public bool showNormalized = true; /// Should the field be shown? /// This setting is ignored for fields which directly store the seconds value. [Tooltip("Should the " + nameof(AnimationTimeAttribute.Units.Seconds) + " field be shown?")] public bool showSeconds = true; /// Should the field be shown? /// This setting is ignored for fields which directly store the frame value. [Tooltip("Should the " + nameof(AnimationTimeAttribute.Units.Frames) + " field be shown?")] public bool showFrames = true; /************************************************************************************************************************/ } /************************************************************************************************************************/ #endregion /************************************************************************************************************************/ } #endif