using UnityEngine; using UnityEditor; using System; using System.Collections.Generic; using System.IO; namespace Cinemachine.Editor { /// /// Collection of tools and helpers for drawing inspectors /// public class InspectorUtility { /// Put multiple properties on a single inspector line, with /// optional label overrides. Passing null as a label (or sublabel) override will /// cause the property's displayName to be used as a label. For no label at all, /// pass GUIContent.none. /// Rect in which to draw /// Main label /// Properties to place on the line /// Sublabels for the properties public static void MultiPropertyOnLine( Rect rect, GUIContent label, SerializedProperty[] props, GUIContent[] subLabels) { if (props == null || props.Length == 0) return; const int hSpace = 2; int indentLevel = EditorGUI.indentLevel; float labelWidth = EditorGUIUtility.labelWidth; float totalSubLabelWidth = 0; int numBoolColumns = 0; List actualLabels = new List(); for (int i = 0; i < props.Length; ++i) { GUIContent sublabel = new GUIContent(props[i].displayName, props[i].tooltip); if (subLabels != null && subLabels.Length > i && subLabels[i] != null) sublabel = subLabels[i]; actualLabels.Add(sublabel); totalSubLabelWidth += GUI.skin.label.CalcSize(sublabel).x; if (i > 0) totalSubLabelWidth += hSpace; // Special handling for toggles, or it looks stupid if (props[i].propertyType == SerializedPropertyType.Boolean) { totalSubLabelWidth += rect.height; ++numBoolColumns; } } float subFieldWidth = rect.width - labelWidth - totalSubLabelWidth; float numCols = props.Length - numBoolColumns; float colWidth = numCols == 0 ? 0 : subFieldWidth / numCols; // Main label. If no first sublabel, then main label must take on that // role, for mouse dragging value-scrolling support int subfieldStartIndex = 0; if (label == null) label = new GUIContent(props[0].displayName, props[0].tooltip); if (actualLabels[0] != GUIContent.none) rect = EditorGUI.PrefixLabel(rect, label); else { rect.width = labelWidth + colWidth; EditorGUI.PropertyField(rect, props[0], label); rect.x += rect.width + hSpace; subfieldStartIndex = 1; } for (int i = subfieldStartIndex; i < props.Length; ++i) { EditorGUI.indentLevel = 0; EditorGUIUtility.labelWidth = GUI.skin.label.CalcSize(actualLabels[i]).x; if (props[i].propertyType == SerializedPropertyType.Boolean) { rect.width = EditorGUIUtility.labelWidth + rect.height; props[i].boolValue = EditorGUI.ToggleLeft(rect, actualLabels[i], props[i].boolValue); } else { rect.width = EditorGUIUtility.labelWidth + colWidth; EditorGUI.PropertyField(rect, props[i], actualLabels[i]); } rect.x += rect.width + hSpace; } EditorGUIUtility.labelWidth = labelWidth; EditorGUI.indentLevel = indentLevel; } /// /// Normalize a curve so that each of X and Y axes ranges from 0 to 1 /// /// Curve to normalize /// The normalized curve public static AnimationCurve NormalizeCurve(AnimationCurve curve) { return RuntimeUtility.NormalizeCurve(curve, true, true); } /// /// Remove the "Cinemachine" prefix, then call the standard Unity Nicify. /// /// The name to nicify /// The nicified name public static string NicifyClassName(string name) { if (name.StartsWith("Cinemachine")) name = name.Substring(11); // Trim the prefix return ObjectNames.NicifyVariableName(name); } /// /// Add to a list all assets of a given type found in a given location /// /// The asset type to look for /// The list to add found assets to /// The location in which to look. Path is relative to package root. public static void AddAssetsFromPackageSubDirectory( Type type, List assets, string path) { try { path = "/" + path; var info = new DirectoryInfo(ScriptableObjectUtility.CinemachineInstallPath + path); path = ScriptableObjectUtility.kPackageRoot + path + "/"; var fileInfo = info.GetFiles(); foreach (var file in fileInfo) { if (file.Extension != ".asset") continue; string name = path + file.Name; ScriptableObject a = AssetDatabase.LoadAssetAtPath(name, type) as ScriptableObject; if (a != null) assets.Add(a); } } catch { } } // Temporarily here /// /// Create a game object. Uses ObjectFactory if the Unity version is sufficient. /// /// Name to give the object /// Optional components to add /// public static GameObject CreateGameObject(string name, params Type[] types) { return ObjectFactory.CreateGameObject(name, types); } private static int m_lastRepaintFrame; /// /// Force a repaint of the Game View /// /// Like it says public static void RepaintGameView(UnityEngine.Object unused = null) { if (m_lastRepaintFrame == Time.frameCount) return; m_lastRepaintFrame = Time.frameCount; EditorApplication.QueuePlayerLoopUpdate(); UnityEditorInternal.InternalEditorUtility.RepaintAllViews(); } /// /// Try to get the name of the owning virtual camera oibject. If none then use /// the object's name /// /// /// internal static string GetVirtualCameraObjectName(SerializedProperty property) { // A little hacky here, as we favour virtual cameras... var obj = property.serializedObject.targetObject; GameObject go = obj as GameObject; if (go == null) { var component = obj as Component; if (component != null) go = component.gameObject; } if (go != null) { var vcam = go.GetComponentInParent(); if (vcam != null) return vcam.Name; return go.name; } return obj.name; } } }