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