using System.Linq; using UnityEngine; using UnityEditor.AnimatedValues; using UnityEngine.UI; namespace UnityEditor.UI { /// /// Editor class used to edit UI Sprites. /// [CustomEditor(typeof(Image), true)] [CanEditMultipleObjects] /// /// Custom Editor for the Image Component. /// Extend this class to write a custom editor for a component derived from Image. /// public class ImageEditor : GraphicEditor { SerializedProperty m_FillMethod; SerializedProperty m_FillOrigin; SerializedProperty m_FillAmount; SerializedProperty m_FillClockwise; SerializedProperty m_Type; SerializedProperty m_FillCenter; SerializedProperty m_Sprite; SerializedProperty m_PreserveAspect; SerializedProperty m_UseSpriteMesh; SerializedProperty m_PixelsPerUnitMultiplier; GUIContent m_SpriteContent; GUIContent m_SpriteTypeContent; GUIContent m_ClockwiseContent; AnimBool m_ShowSlicedOrTiled; AnimBool m_ShowSliced; AnimBool m_ShowTiled; AnimBool m_ShowFilled; AnimBool m_ShowType; bool m_bIsDriven; private class Styles { public static GUIContent text = EditorGUIUtility.TrTextContent("Fill Origin"); public static GUIContent[] OriginHorizontalStyle = { EditorGUIUtility.TrTextContent("Left"), EditorGUIUtility.TrTextContent("Right") }; public static GUIContent[] OriginVerticalStyle = { EditorGUIUtility.TrTextContent("Bottom"), EditorGUIUtility.TrTextContent("Top") }; public static GUIContent[] Origin90Style = { EditorGUIUtility.TrTextContent("BottomLeft"), EditorGUIUtility.TrTextContent("TopLeft"), EditorGUIUtility.TrTextContent("TopRight"), EditorGUIUtility.TrTextContent("BottomRight") }; public static GUIContent[] Origin180Style = { EditorGUIUtility.TrTextContent("Bottom"), EditorGUIUtility.TrTextContent("Left"), EditorGUIUtility.TrTextContent("Top"), EditorGUIUtility.TrTextContent("Right") }; public static GUIContent[] Origin360Style = { EditorGUIUtility.TrTextContent("Bottom"), EditorGUIUtility.TrTextContent("Right"), EditorGUIUtility.TrTextContent("Top"), EditorGUIUtility.TrTextContent("Left") }; } protected override void OnEnable() { base.OnEnable(); m_SpriteContent = EditorGUIUtility.TrTextContent("Source Image"); m_SpriteTypeContent = EditorGUIUtility.TrTextContent("Image Type"); m_ClockwiseContent = EditorGUIUtility.TrTextContent("Clockwise"); m_Sprite = serializedObject.FindProperty("m_Sprite"); m_Type = serializedObject.FindProperty("m_Type"); m_FillCenter = serializedObject.FindProperty("m_FillCenter"); m_FillMethod = serializedObject.FindProperty("m_FillMethod"); m_FillOrigin = serializedObject.FindProperty("m_FillOrigin"); m_FillClockwise = serializedObject.FindProperty("m_FillClockwise"); m_FillAmount = serializedObject.FindProperty("m_FillAmount"); m_PreserveAspect = serializedObject.FindProperty("m_PreserveAspect"); m_UseSpriteMesh = serializedObject.FindProperty("m_UseSpriteMesh"); m_PixelsPerUnitMultiplier = serializedObject.FindProperty("m_PixelsPerUnitMultiplier"); m_ShowType = new AnimBool(m_Sprite.objectReferenceValue != null); m_ShowType.valueChanged.AddListener(Repaint); var typeEnum = (Image.Type)m_Type.enumValueIndex; m_ShowSlicedOrTiled = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == Image.Type.Sliced); m_ShowSliced = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == Image.Type.Sliced); m_ShowTiled = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == Image.Type.Tiled); m_ShowFilled = new AnimBool(!m_Type.hasMultipleDifferentValues && typeEnum == Image.Type.Filled); m_ShowSlicedOrTiled.valueChanged.AddListener(Repaint); m_ShowSliced.valueChanged.AddListener(Repaint); m_ShowTiled.valueChanged.AddListener(Repaint); m_ShowFilled.valueChanged.AddListener(Repaint); SetShowNativeSize(true); m_bIsDriven = false; } protected override void OnDisable() { base.OnDisable(); m_ShowType.valueChanged.RemoveListener(Repaint); m_ShowSlicedOrTiled.valueChanged.RemoveListener(Repaint); m_ShowSliced.valueChanged.RemoveListener(Repaint); m_ShowTiled.valueChanged.RemoveListener(Repaint); m_ShowFilled.valueChanged.RemoveListener(Repaint); } public override void OnInspectorGUI() { serializedObject.Update(); Image image = target as Image; RectTransform rect = image.GetComponent(); m_bIsDriven = (rect.drivenByObject as Slider)?.fillRect == rect; SpriteGUI(); AppearanceControlsGUI(); RaycastControlsGUI(); MaskableControlsGUI(); m_ShowType.target = m_Sprite.objectReferenceValue != null; if (EditorGUILayout.BeginFadeGroup(m_ShowType.faded)) TypeGUI(); EditorGUILayout.EndFadeGroup(); SetShowNativeSize(false); if (EditorGUILayout.BeginFadeGroup(m_ShowNativeSize.faded)) { EditorGUI.indentLevel++; if ((Image.Type)m_Type.enumValueIndex == Image.Type.Simple) EditorGUILayout.PropertyField(m_UseSpriteMesh); EditorGUILayout.PropertyField(m_PreserveAspect); EditorGUI.indentLevel--; } EditorGUILayout.EndFadeGroup(); NativeSizeButtonGUI(); serializedObject.ApplyModifiedProperties(); } void SetShowNativeSize(bool instant) { Image.Type type = (Image.Type)m_Type.enumValueIndex; bool showNativeSize = (type == Image.Type.Simple || type == Image.Type.Filled) && m_Sprite.objectReferenceValue != null; base.SetShowNativeSize(showNativeSize, instant); } /// /// Draw the atlas and Image selection fields. /// protected void SpriteGUI() { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_Sprite, m_SpriteContent); if (EditorGUI.EndChangeCheck()) { var newSprite = m_Sprite.objectReferenceValue as Sprite; if (newSprite) { Image.Type oldType = (Image.Type)m_Type.enumValueIndex; if (newSprite.border.SqrMagnitude() > 0) { m_Type.enumValueIndex = (int)Image.Type.Sliced; } else if (oldType == Image.Type.Sliced) { m_Type.enumValueIndex = (int)Image.Type.Simple; } } (serializedObject.targetObject as Image).DisableSpriteOptimizations(); } } /// /// Sprites's custom properties based on the type. /// protected void TypeGUI() { EditorGUILayout.PropertyField(m_Type, m_SpriteTypeContent); ++EditorGUI.indentLevel; { Image.Type typeEnum = (Image.Type)m_Type.enumValueIndex; bool showSlicedOrTiled = (!m_Type.hasMultipleDifferentValues && (typeEnum == Image.Type.Sliced || typeEnum == Image.Type.Tiled)); if (showSlicedOrTiled && targets.Length > 1) showSlicedOrTiled = targets.Select(obj => obj as Image).All(img => img.hasBorder); m_ShowSlicedOrTiled.target = showSlicedOrTiled; m_ShowSliced.target = (showSlicedOrTiled && !m_Type.hasMultipleDifferentValues && typeEnum == Image.Type.Sliced); m_ShowTiled.target = (showSlicedOrTiled && !m_Type.hasMultipleDifferentValues && typeEnum == Image.Type.Tiled); m_ShowFilled.target = (!m_Type.hasMultipleDifferentValues && typeEnum == Image.Type.Filled); Image image = target as Image; if (EditorGUILayout.BeginFadeGroup(m_ShowSlicedOrTiled.faded)) { if (image.hasBorder) EditorGUILayout.PropertyField(m_FillCenter); EditorGUILayout.PropertyField(m_PixelsPerUnitMultiplier); } EditorGUILayout.EndFadeGroup(); if (EditorGUILayout.BeginFadeGroup(m_ShowSliced.faded)) { if (image.sprite != null && !image.hasBorder) EditorGUILayout.HelpBox("This Image doesn't have a border.", MessageType.Warning); } EditorGUILayout.EndFadeGroup(); if (EditorGUILayout.BeginFadeGroup(m_ShowTiled.faded)) { if (image.sprite != null && !image.hasBorder && (image.sprite.texture != null && image.sprite.texture.wrapMode != TextureWrapMode.Repeat || image.sprite.packed)) EditorGUILayout.HelpBox("It looks like you want to tile a sprite with no border. It would be more efficient to modify the Sprite properties, clear the Packing tag and set the Wrap mode to Repeat.", MessageType.Warning); } EditorGUILayout.EndFadeGroup(); if (EditorGUILayout.BeginFadeGroup(m_ShowFilled.faded)) { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_FillMethod); if (EditorGUI.EndChangeCheck()) { m_FillOrigin.intValue = 0; } var shapeRect = EditorGUILayout.GetControlRect(true); switch ((Image.FillMethod)m_FillMethod.enumValueIndex) { case Image.FillMethod.Horizontal: EditorGUI.Popup(shapeRect, m_FillOrigin, Styles.OriginHorizontalStyle, Styles.text); break; case Image.FillMethod.Vertical: EditorGUI.Popup(shapeRect, m_FillOrigin, Styles.OriginVerticalStyle, Styles.text); break; case Image.FillMethod.Radial90: EditorGUI.Popup(shapeRect, m_FillOrigin, Styles.Origin90Style, Styles.text); break; case Image.FillMethod.Radial180: EditorGUI.Popup(shapeRect, m_FillOrigin, Styles.Origin180Style, Styles.text); break; case Image.FillMethod.Radial360: EditorGUI.Popup(shapeRect, m_FillOrigin, Styles.Origin360Style, Styles.text); break; } if (m_bIsDriven) EditorGUILayout.HelpBox("The Fill amount property is driven by Slider.", MessageType.None); using (new EditorGUI.DisabledScope(m_bIsDriven)) { EditorGUILayout.PropertyField(m_FillAmount); } if ((Image.FillMethod)m_FillMethod.enumValueIndex > Image.FillMethod.Vertical) { EditorGUILayout.PropertyField(m_FillClockwise, m_ClockwiseContent); } } EditorGUILayout.EndFadeGroup(); } --EditorGUI.indentLevel; } /// /// All graphics have a preview. /// public override bool HasPreviewGUI() { return true; } /// /// Draw the Image preview. /// public override void OnPreviewGUI(Rect rect, GUIStyle background) { Image image = target as Image; if (image == null) return; Sprite sf = image.sprite; if (sf == null) return; SpriteDrawUtility.DrawSprite(sf, rect, image.canvasRenderer.GetColor()); } /// /// A string containing the Image details to be used as a overlay on the component Preview. /// /// /// The Image details. /// public override string GetInfoString() { Image image = target as Image; Sprite sprite = image.sprite; int x = (sprite != null) ? Mathf.RoundToInt(sprite.rect.width) : 0; int y = (sprite != null) ? Mathf.RoundToInt(sprite.rect.height) : 0; return string.Format("Image Size: {0}x{1}", x, y); } } }