示例#1
0
        /************************************************************************************************************************/
        #endregion
        /************************************************************************************************************************/
        #region Fields
        /************************************************************************************************************************/

        /// <summary>
        /// Draw a <see cref="EditorGUI.FloatField(Rect, GUIContent, float)"/> with an alternate cached string when it
        /// is not selected (for example, "1" might become "1s" to indicate "seconds").
        /// </summary>
        public static float DoSpecialFloatField(Rect area, GUIContent label, float value, ConversionCache <float, string> toString)
        {
            // Treat most events normally, but when repainting show a text field with the cached string.

            if (label != null)
            {
                if (Event.current.type != EventType.Repaint)
                {
                    return(EditorGUI.FloatField(area, label, value));
                }

                var dragArea = new Rect(area.x, area.y, EditorGUIUtility.labelWidth, area.height);
                EditorGUIUtility.AddCursorRect(dragArea, MouseCursor.SlideArrow);

                EditorGUI.TextField(area, label, toString.Convert(value));
            }
            else
            {
                var indentLevel = EditorGUI.indentLevel;
                EditorGUI.indentLevel = 0;

                if (Event.current.type != EventType.Repaint)
                {
                    value = EditorGUI.FloatField(area, value);
                }
                else
                {
                    EditorGUI.TextField(area, toString.Convert(value));
                }

                EditorGUI.indentLevel = indentLevel;
            }

            return(value);
        }
示例#2
0
        /************************************************************************************************************************/

        /// <summary>Creates a new <see cref="CompactUnitConversionCache"/>.</summary>
        public CompactUnitConversionCache(string suffix)
        {
            Suffix                 = suffix;
            ApproximateSuffix      = "~" + suffix;
            ConvertedZero          = "0" + Suffix;
            ConvertedSmallPositive = "0" + ApproximateSuffix;
            ConvertedSmallNegative = "-0" + ApproximateSuffix;
            SuffixWidth            = WidthCache.Convert(suffix);
        }
示例#3
0
        private void DoLoopCounterGUI(ref Rect area, float length)
        {
            if (_LoopCounterCache == null)
            {
                _LoopCounterCache = new ConversionCache <int, string>((x) => "x" + x);
            }

            string label;
            var    normalizedTime = Target.Time / length;

            if (float.IsNaN(normalizedTime))
            {
                label = "NaN";
            }
            else
            {
                var loops = (int)Math.Abs(Target.Time / length);
                label = _LoopCounterCache.Convert(loops);
            }

            var width = AnimancerGUI.CalculateLabelWidth(label);

            var labelArea = AnimancerGUI.StealFromRight(ref area, width);

            GUI.Label(labelArea, label);
        }
示例#4
0
        /// <summary>[Animancer Extension]
        /// Calls <see cref="Gather(ICollection{AnimationClip}, AnimationClip)"/> for each clip in the `asset`.
        /// </summary>
        public static void GatherFromAsset(this ICollection <AnimationClip> clips, PlayableAsset asset)
        {
            if (asset == null)
            {
                return;
            }

            // We want to get the tracks out of a TimelineAsset without actually referencing that class directly
            // because it comes from an optional package and Animancer does not need to depend on that package.
            if (_TypeToGetRootTracks == null)
            {
                _TypeToGetRootTracks = new Editor.ConversionCache <Type, MethodInfo>((type) =>
                {
                    var method = type.GetMethod("GetRootTracks");
                    if (method != null &&
                        typeof(IEnumerable).IsAssignableFrom(method.ReturnType) &&
                        method.GetParameters().Length == 0)
                    {
                        return(method);
                    }
                    else
                    {
                        return(null);
                    }
                });
            }

            var getRootTracks = _TypeToGetRootTracks.Convert(asset.GetType());

            if (getRootTracks != null)
            {
                var rootTracks = getRootTracks.Invoke(asset, null);
                GatherAnimationClips(rootTracks as IEnumerable, clips);
            }
        }
示例#5
0
        /// <summary>
        /// Draws a label showing the `weight` aligned to the right side of the `area` and reduces its
        /// <see cref="Rect.width"/> to remove that label from its area.
        /// </summary>
        public static void DoWeightLabel(ref Rect area, float weight)
        {
            string label;

            if (weight < 0)
            {
                label = "-?";
            }
            else
            {
                if (_F1Cache == null)
                {
                    _F1Cache = new ConversionCache <float, string>((value) => value.ToString("F1"));
                }

                label = _F1Cache.Convert(weight);
            }

            var style = ObjectPool.GetCachedResult(() => new GUIStyle(GUI.skin.label));

            if (_WeightValueWidth < 0)
            {
                _WeightValueWidth = style.CalculateWidth("0.0");
            }

            style.normal.textColor = Color.Lerp(Color.grey, TextColor, weight);
            style.fontStyle        = Mathf.Approximately(weight * 10, (int)(weight * 10)) ?
                                     FontStyle.Normal : FontStyle.Italic;

            var weightArea = StealFromRight(ref area, _WeightValueWidth);

            GUI.Label(weightArea, label, style);
        }
        /************************************************************************************************************************/

        /// <summary>Calculate the index of the cache to use for the given parameters.</summary>
        private int CalculateCacheIndex(float value, float width)
        {
            //if (value > LargeExponentialThreshold ||
            //    value < -LargeExponentialThreshold)
            //    return 0;

            var valueString = value.ToStringCached();

            // It the approximated string wouldn't be shorter than the original, don't approximate.
            if (valueString.Length < 2 + ApproximateSuffix.Length)
            {
                return(0);
            }

            if (_SuffixWidth == 0)
            {
                if (_WidthCache == null)
                {
                    _WidthCache             = AnimancerGUI.CreateWidthCache(EditorStyles.numberField);
                    _FieldPadding           = EditorStyles.numberField.padding.horizontal;
                    _ApproximateSymbolWidth = _WidthCache.Convert("~") - _FieldPadding;
                }

                _SuffixWidth = _WidthCache.Convert(Suffix);
            }

            // If the field is wide enough to fit the full value, don't approximate.
            width -= _FieldPadding + _ApproximateSymbolWidth * 0.75f;
            var valueWidth = _WidthCache.Convert(valueString) + _SuffixWidth;

            if (valueWidth <= width)
            {
                return(0);
            }

            // If the number of allowed characters would include the full value, don't approximate.
            var suffixedLength    = valueString.Length + Suffix.Length;
            var allowedCharacters = (int)(suffixedLength * width / valueWidth);

            if (allowedCharacters + 2 >= suffixedLength)
            {
                return(0);
            }

            return(allowedCharacters);
        }
示例#7
0
        /// <summary>
        /// Calls <see cref="GUIStyle.CalcMinMaxWidth"/> using <see cref="GUISkin.label"/> and returns the max
        /// width. The result is cached for efficient reuse.
        /// <para></para>
        /// This method uses the <see cref="TempContent(string, string, bool)"/>.
        /// </summary>
        public static float CalculateLabelWidth(string text)
        {
            if (_LabelWidthCache == null)
            {
                _LabelWidthCache = CreateWidthCache(GUI.skin.label);
            }

            return(_LabelWidthCache.Convert(text));
        }
示例#8
0
        private static void DrawSpriteFrames(AnimationClip clip)
        {
            var keyframes = GetSpriteReferences(clip);

            if (keyframes == null)
            {
                return;
            }

            for (int i = 0; i < keyframes.Length; i++)
            {
                var keyframe = keyframes[i];
                var sprite   = keyframe.value as Sprite;
                if (sprite != null)
                {
                    if (_FrameCache == null)
                    {
                        _FrameCache = new ConversionCache <int, string>(
                            (value) => $"Frame: {value}");

                        _TimeCache = new ConversionCache <float, string>(
                            (value) => $"Time: {value}s");
                    }

                    var texture = sprite.texture;

                    var area  = GUILayoutUtility.GetRect(0, AnimancerGUI.LineHeight * 4);
                    var width = area.width;

                    var rect = sprite.rect;
                    area.width = area.height * rect.width / rect.height;

                    rect.x      /= texture.width;
                    rect.y      /= texture.height;
                    rect.width  /= texture.width;
                    rect.height /= texture.height;

                    GUI.DrawTextureWithTexCoords(area, texture, rect);

                    var offset = area.width + AnimancerGUI.StandardSpacing;
                    area.x     += offset;
                    area.width  = width - offset;
                    area.height = AnimancerGUI.LineHeight;
                    area.y     += Mathf.Round(area.height * 0.5f);

                    GUI.Label(area, _FrameCache.Convert(i));

                    AnimancerGUI.NextVerticalArea(ref area);
                    GUI.Label(area, _TimeCache.Convert(keyframe.time));

                    AnimancerGUI.NextVerticalArea(ref area);
                    GUI.Label(area, sprite.name);
                }
            }
        }
示例#9
0
        private void DoRulerLabelGUI(ref Rect previousArea, float time)
        {
            if (_RulerLabelStyle == null)
            {
                _RulerLabelStyle = new GUIStyle(GUI.skin.label)
                {
                    padding       = new RectOffset(),
                    contentOffset = new Vector2(0, -2),
                    alignment     = TextAnchor.UpperLeft,
                    fontSize      = Mathf.CeilToInt(AnimancerGUI.LineHeight * 0.6f),
                }
            }
            ;

            var text = G2Cache.Convert(time);

            if (_TimeLabelWidthCache == null)
            {
                _TimeLabelWidthCache = AnimancerGUI.CreateWidthCache(_RulerLabelStyle);
            }

            var area = new Rect(
                SecondsToPixels(time),
                _Area.y,
                _TimeLabelWidthCache.Convert(text),
                _Area.height);

            if (area.x > _Area.x)
            {
                var tickY = _Area.yMax - TickHeight;
                EditorGUI.DrawRect(new Rect(area.x, tickY, 1, TickHeight), AnimancerGUI.TextColor);
            }

            if (area.xMax > _Area.xMax)
            {
                area.x = _Area.xMax - area.width;
            }
            if (area.x < 0)
            {
                area.x = 0;
            }

            if (area.x > previousArea.xMax + 2)
            {
                GUI.Label(area, text, _RulerLabelStyle);

                previousArea = area;
            }
        }

        /************************************************************************************************************************/
        #endregion
        /************************************************************************************************************************/
    }
示例#10
0
        /// <summary>
        /// Returns the `text` without any spaces if <see cref="EditorGUIUtility.wideMode"/> is false.
        /// Otherwise simply returns the `text` without any changes.
        /// </summary>
        public static string GetNarrowText(string text)
        {
            if (EditorGUIUtility.wideMode ||
                string.IsNullOrEmpty(text))
            {
                return(text);
            }

            if (_NarrowTextCache == null)
            {
                _NarrowTextCache = new ConversionCache <string, string>((str) => str.Replace(" ", ""));
            }

            return(_NarrowTextCache.Convert(text));
        }
示例#11
0
        /// <summary>Draws the GUI for the `animancerEvent`.</summary>
        public static void Draw(ref Rect area, string name, AnimancerEvent animancerEvent)
        {
            area.height = AnimancerGUI.LineHeight;

            if (_EventTimeCache == null)
            {
                _EventTimeCache = new ConversionCache <float, string>((time)
                                                                      => float.IsNaN(time) ? "Time = Auto" : $"Time = {time.ToStringCached()}x");
            }

            EditorGUI.LabelField(area, name, _EventTimeCache.Convert(animancerEvent.normalizedTime));

            AnimancerGUI.NextVerticalArea(ref area);

            EditorGUI.indentLevel++;
            DrawInvocationList(ref area, animancerEvent.callback);
            EditorGUI.indentLevel--;
        }
        /// <summary>
        /// Draws a label showing the `weight` aligned to the right side of the `area` and reduces its
        /// <see cref="Rect.width"/> to remove that label from its area.
        /// </summary>
        public static void DoWeightLabel(ref Rect area, float weight)
        {
            if (_F1Cache == null)
            {
                _F1Cache          = new ConversionCache <float, string>((value) => value.ToString("F1"));
                _WeightLabelStyle = new GUIStyle(GUI.skin.label);
                _WeightValueWidth = _WeightLabelStyle.CalculateWidth("0.0");
            }

            var weightArea = StealFromRight(ref area, _WeightValueWidth);

            var label = _F1Cache.Convert(weight);

            _WeightLabelStyle.normal.textColor = Color.Lerp(Color.grey, TextColor, weight);
            _WeightLabelStyle.fontStyle        = Mathf.Approximately(weight * 10, (int)(weight * 10)) ?
                                                 FontStyle.Normal : FontStyle.Italic;

            GUI.Label(weightArea, label, _WeightLabelStyle);
        }
示例#13
0
        /// <summary>Gathers all the animations in the `tracks`.</summary>
        private static void GatherAnimationClips(IEnumerable tracks, ICollection <AnimationClip> clips)
        {
            if (tracks == null)
            {
                return;
            }

            if (_TrackAssetToGetClips == null)
            {
                _TrackAssetToGetClips = new Editor.ConversionCache <Type, MethodInfo>((type) =>
                {
                    var method = type.GetMethod("GetClips");
                    if (method != null &&
                        typeof(IEnumerable).IsAssignableFrom(method.ReturnType) &&
                        method.GetParameters().Length == 0)
                    {
                        return(method);
                    }
                    else
                    {
                        return(null);
                    }
                });

                _TimelineClipToAnimationClip = new Editor.ConversionCache <Type, MethodInfo>((type) =>
                {
                    var property = type.GetProperty("animationClip");
                    if (property != null &&
                        property.PropertyType == typeof(AnimationClip))
                    {
                        return(property.GetGetMethod());
                    }
                    else
                    {
                        return(null);
                    }
                });

                _TrackAssetToGetChildTracks = new Editor.ConversionCache <Type, MethodInfo>((type) =>
                {
                    var method = type.GetMethod("GetChildTracks");
                    if (method != null &&
                        typeof(IEnumerable).IsAssignableFrom(method.ReturnType) &&
                        method.GetParameters().Length == 0)
                    {
                        return(method);
                    }
                    else
                    {
                        return(null);
                    }
                });
            }

            foreach (var track in tracks)
            {
                if (track == null)
                {
                    continue;
                }

                var trackType = track.GetType();

                var getClips = _TrackAssetToGetClips.Convert(trackType);
                if (getClips != null)
                {
                    var trackClips = getClips.Invoke(track, null) as IEnumerable;
                    if (trackClips != null)
                    {
                        foreach (var clip in trackClips)
                        {
                            var getClip = _TimelineClipToAnimationClip.Convert(clip.GetType());
                            if (getClip != null)
                            {
                                clips.Gather(getClip.Invoke(clip, null) as AnimationClip);
                            }
                        }
                    }
                }

                var getChildTracks = _TrackAssetToGetChildTracks.Convert(trackType);
                if (getChildTracks != null)
                {
                    var childTracks = getChildTracks.Invoke(track, null);
                    GatherAnimationClips(childTracks as IEnumerable, clips);
                }
            }
        }
示例#14
0
        /************************************************************************************************************************/

        /// <summary>Draws the GUI for the `events`.</summary>
        public void Draw(ref Rect area, Sequence events, GUIContent label)
        {
            if (events == null)
            {
                return;
            }

            area.height = AnimancerGUI.LineHeight;

            var headerArea = area;

            const string LogLabel = "Log";

            if (float.IsNaN(_LogButtonWidth))
            {
                _LogButtonWidth = EditorStyles.miniButton.CalculateWidth(LogLabel);
            }
            var logArea = AnimancerGUI.StealFromRight(ref headerArea, _LogButtonWidth);

            if (GUI.Button(logArea, LogLabel, EditorStyles.miniButton))
            {
                Debug.Log(events.DeepToString());
            }

            _IsExpanded = EditorGUI.Foldout(headerArea, _IsExpanded, GUIContent.none, true);
            using (ObjectPool.Disposable.AcquireContent(out var summary, GetSummary(events)))
                EditorGUI.LabelField(headerArea, label, summary);

            AnimancerGUI.NextVerticalArea(ref area);

            if (!_IsExpanded)
            {
                return;
            }

            var enabled = GUI.enabled;

            GUI.enabled = false;

            EditorGUI.indentLevel++;

            for (int i = 0; i < events.Count; i++)
            {
                var name = events.GetName(i);
                if (string.IsNullOrEmpty(name))
                {
                    if (_EventNumberCache == null)
                    {
                        _EventNumberCache = new ConversionCache <int, string>((index) => $"Event {index}");
                    }

                    name = _EventNumberCache.Convert(i);
                }

                Draw(ref area, name, events[i]);
            }

            Draw(ref area, "End Event", events.endEvent);

            EditorGUI.indentLevel--;

            GUI.enabled = enabled;
        }
示例#15
0
        /// <summary>Returns a string which approximates the `weight` into no more than 3 digits.</summary>
        private static string WeightToShortString(float weight, out bool isExact)
        {
            isExact = true;

            if (weight == 0)
            {
                return("0.0");
            }
            if (weight == 1)
            {
                return("1.0");
            }

            isExact = false;

            if (weight >= -0.5f && weight < 0.05f)
            {
                return("~0.");
            }
            if (weight >= 0.95f && weight < 1.05f)
            {
                return("~1.");
            }

            if (weight <= -99.5f)
            {
                return("-??");
            }
            if (weight >= 999.5f)
            {
                return("???");
            }

            if (_ShortWeightCache == null)
            {
                _ShortWeightCache = new ConversionCache <float, string>((value) =>
                {
                    if (value < -9.5f)
                    {
                        return($"{value:F0}");
                    }
                    if (value < -0.5f)
                    {
                        return($"{value:F0}.");
                    }
                    if (value < 9.5f)
                    {
                        return($"{value:F1}");
                    }
                    if (value < 99.5f)
                    {
                        return($"{value:F0}.");
                    }
                    return($"{value:F0}");
                });
            }

            var rounded = weight > 0 ? Mathf.Floor(weight * 10) : Mathf.Ceil(weight * 10);

            isExact = Mathf.Approximately(weight * 10, rounded);

            return(_ShortWeightCache.Convert(weight));
        }
示例#16
0
 /// <summary>[Animancer Extension]
 /// Calls <see cref="float.ToString(string)"/> using <c>"g"</c> as the format and caches the result.
 /// </summary>
 public static string ToStringCached(this float value) => FloatToString.Convert(value);