using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; using UnityEngine; using UnityEngine.Rendering; namespace UnityEditor.Rendering { using UnityObject = UnityEngine.Object; public static class CoreEditorUtils { class Styles { static readonly Color k_Normal_AllTheme = new Color32(0, 0, 0, 0); //static readonly Color k_Hover_Dark = new Color32(70, 70, 70, 255); //static readonly Color k_Hover = new Color32(193, 193, 193, 255); static readonly Color k_Active_Dark = new Color32(80, 80, 80, 255); static readonly Color k_Active = new Color32(216, 216, 216, 255); static readonly int s_MoreOptionsHash = "MoreOptions".GetHashCode(); static public GUIContent moreOptionsLabel { get; private set; } static public GUIStyle moreOptionsStyle { get; private set; } static public GUIStyle moreOptionsLabelStyle { get; private set; } static Styles() { moreOptionsLabel = EditorGUIUtility.TrIconContent("MoreOptions", "More Options"); moreOptionsStyle = new GUIStyle(GUI.skin.toggle); Texture2D normalColor = new Texture2D(1, 1); normalColor.SetPixel(1, 1, k_Normal_AllTheme); moreOptionsStyle.normal.background = normalColor; moreOptionsStyle.onActive.background = normalColor; moreOptionsStyle.onFocused.background = normalColor; moreOptionsStyle.onNormal.background = normalColor; moreOptionsStyle.onHover.background = normalColor; moreOptionsStyle.active.background = normalColor; moreOptionsStyle.focused.background = normalColor; moreOptionsStyle.hover.background = normalColor; moreOptionsLabelStyle = new GUIStyle(GUI.skin.label); moreOptionsLabelStyle.padding = new RectOffset(0, 0, 0, -1); } //Note: // - GUIStyle seams to be broken: all states have same state than normal light theme // - Hover with event will not be updated right when we enter the rect //-> Removing hover for now. Keep theme color for refactoring with UIElement later static public bool DrawMoreOptions(Rect rect, bool active) { int id = GUIUtility.GetControlID(s_MoreOptionsHash, FocusType.Passive, rect); var evt = Event.current; switch (evt.type) { case EventType.Repaint: Color background = k_Normal_AllTheme; if (active) background = EditorGUIUtility.isProSkin ? k_Active_Dark : k_Active; EditorGUI.DrawRect(rect, background); GUI.Label(rect, moreOptionsLabel, moreOptionsLabelStyle); break; case EventType.KeyDown: bool anyModifiers = (evt.alt || evt.shift || evt.command || evt.control); if ((evt.keyCode == KeyCode.Space || evt.keyCode == KeyCode.Return || evt.keyCode == KeyCode.KeypadEnter) && !anyModifiers && GUIUtility.keyboardControl == id) { evt.Use(); GUI.changed = true; return !active; } break; case EventType.MouseDown: if (rect.Contains(evt.mousePosition)) { GrabMouseControl(id); evt.Use(); } break; case EventType.MouseUp: if (HasMouseControl(id)) { ReleaseMouseControl(); evt.Use(); if (rect.Contains(evt.mousePosition)) { GUI.changed = true; return !active; } } break; case EventType.MouseDrag: if (HasMouseControl(id)) evt.Use(); break; } return active; } static int s_GrabbedID = -1; static void GrabMouseControl(int id) => s_GrabbedID = id; static void ReleaseMouseControl() => s_GrabbedID = -1; static bool HasMouseControl(int id) => s_GrabbedID == id; } static GraphicsDeviceType[] m_BuildTargets; public static GraphicsDeviceType[] buildTargets => m_BuildTargets ?? (m_BuildTargets = PlayerSettings.GetGraphicsAPIs(EditorUserBuildSettings.activeBuildTarget)); static CoreEditorUtils() { LoadSkinAndIconMethods(); } // Serialization helpers /// /// To use with extreme caution. It not really get the property but try to find a field with similar name /// Hence inheritance override of property is not supported. /// Also variable rename will silently break the search. /// public static string FindProperty(Expression> expr) { // Get the field path as a string MemberExpression me; switch (expr.Body.NodeType) { case ExpressionType.MemberAccess: me = expr.Body as MemberExpression; break; default: throw new InvalidOperationException(); } var members = new List(); while (me != null) { // For field, get the field name // For properties, get the name of the backing field var name = me.Member is FieldInfo ? me.Member.Name : "m_" + me.Member.Name.Substring(0, 1).ToUpper() + me.Member.Name.Substring(1); members.Add(name); me = me.Expression as MemberExpression; } var sb = new StringBuilder(); for (int i = members.Count - 1; i >= 0; i--) { sb.Append(members[i]); if (i > 0) sb.Append('.'); } return sb.ToString(); } // UI Helpers public static void DrawFixMeBox(string text, Action action) { EditorGUILayout.HelpBox(text, MessageType.Warning); GUILayout.Space(-32); using (new EditorGUILayout.HorizontalScope()) { GUILayout.FlexibleSpace(); if (GUILayout.Button("Fix", GUILayout.Width(60))) action(); GUILayout.Space(8); } GUILayout.Space(11); } public static void DrawMultipleFields(string label, SerializedProperty[] ppts, GUIContent[] lbls) => DrawMultipleFields(EditorGUIUtility.TrTextContent(label), ppts, lbls); public static void DrawMultipleFields(GUIContent label, SerializedProperty[] ppts, GUIContent[] lbls) { var labelWidth = EditorGUIUtility.labelWidth; using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.PrefixLabel(label); using (new EditorGUILayout.VerticalScope()) { EditorGUIUtility.labelWidth = 40; EditorGUI.indentLevel--; for (var i = 0; i < ppts.Length; ++i) EditorGUILayout.PropertyField(ppts[i], lbls[i]); EditorGUI.indentLevel++; } } EditorGUIUtility.labelWidth = labelWidth; } public static void DrawSplitter(bool isBoxed = false) { var rect = GUILayoutUtility.GetRect(1f, 1f); // Splitter rect should be full-width rect.xMin = 0f; rect.width += 4f; if (isBoxed) { rect.xMin = EditorGUIUtility.singleLineHeight; rect.width -= 1; } if (Event.current.type != EventType.Repaint) return; EditorGUI.DrawRect(rect, !EditorGUIUtility.isProSkin ? new Color(0.6f, 0.6f, 0.6f, 1.333f) : new Color(0.12f, 0.12f, 0.12f, 1.333f)); } public static void DrawHeader(string title) => DrawHeader(EditorGUIUtility.TrTextContent(title)); public static void DrawHeader(GUIContent title) { var backgroundRect = GUILayoutUtility.GetRect(1f, 17f); var labelRect = backgroundRect; labelRect.xMin += 16f; labelRect.xMax -= 20f; var foldoutRect = backgroundRect; foldoutRect.y += 1f; foldoutRect.width = 13f; foldoutRect.height = 13f; // Background rect should be full-width backgroundRect.xMin = 0f; backgroundRect.width += 4f; // Background float backgroundTint = EditorGUIUtility.isProSkin ? 0.1f : 1f; EditorGUI.DrawRect(backgroundRect, new Color(backgroundTint, backgroundTint, backgroundTint, 0.2f)); // Title EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel); } /// Draw a foldout header /// The title of the header /// The state of the header /// [optional] is the eader contained in a box style ? /// [optional] Delegate used to draw the right state of the advanced button. If null, no button drawn. /// [optional] Callback call when advanced button clicked. Should be used to toggle its state. public static bool DrawHeaderFoldout(string title, bool state, bool isBoxed = false, Func hasMoreOptions = null, Action toggleMoreOption = null) => DrawHeaderFoldout(EditorGUIUtility.TrTextContent(title), state, isBoxed, hasMoreOptions, toggleMoreOption); /// Draw a foldout header /// The title of the header /// The state of the header /// [optional] is the eader contained in a box style ? /// [optional] Delegate used to draw the right state of the advanced button. If null, no button drawn. /// [optional] Callback call when advanced button clicked. Should be used to toggle its state. public static bool DrawHeaderFoldout(GUIContent title, bool state, bool isBoxed = false, Func hasMoreOptions = null, Action toggleMoreOptions = null) { const float height = 17f; var backgroundRect = GUILayoutUtility.GetRect(1f, height); var labelRect = backgroundRect; labelRect.xMin += 16f; labelRect.xMax -= 20f; var foldoutRect = backgroundRect; foldoutRect.y += 1f; foldoutRect.width = 13f; foldoutRect.height = 13f; // More options 1/2 var moreOptionsRect = new Rect(); if (hasMoreOptions != null) { moreOptionsRect = backgroundRect; moreOptionsRect.x += moreOptionsRect.width - 16 - 1; moreOptionsRect.height = 15; moreOptionsRect.width = 16; } // Background rect should be full-width backgroundRect.xMin = 0f; backgroundRect.width += 4f; if (isBoxed) { labelRect.xMin += 5; foldoutRect.xMin += 5; backgroundRect.xMin = EditorGUIUtility.singleLineHeight; backgroundRect.width -= 1; } // Background float backgroundTint = EditorGUIUtility.isProSkin ? 0.1f : 1f; EditorGUI.DrawRect(backgroundRect, new Color(backgroundTint, backgroundTint, backgroundTint, 0.2f)); // More options 2/2 if (hasMoreOptions != null) { EditorGUI.BeginChangeCheck(); Styles.DrawMoreOptions(moreOptionsRect, hasMoreOptions()); if (EditorGUI.EndChangeCheck() && toggleMoreOptions != null) { toggleMoreOptions(); } } // Title EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel); // Active checkbox state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout); var e = Event.current; if (e.type == EventType.MouseDown && backgroundRect.Contains(e.mousePosition) && !moreOptionsRect.Contains(e.mousePosition) && e.button == 0) { state = !state; e.Use(); } return state; } /// Draw a foldout header /// The title of the header /// The state of the header /// [optional] is the eader contained in a box style ? /// [optional] Delegate used to draw the right state of the advanced button. If null, no button drawn. /// [optional] Callback call when advanced button clicked. Should be used to toggle its state. public static bool DrawSubHeaderFoldout(string title, bool state, bool isBoxed = false, Func hasMoreOptions = null, Action toggleMoreOptions = null) => DrawSubHeaderFoldout(EditorGUIUtility.TrTextContent(title), state, isBoxed, hasMoreOptions, toggleMoreOptions); /// Draw a foldout header /// The title of the header /// The state of the header /// [optional] is the eader contained in a box style ? /// [optional] Delegate used to draw the right state of the advanced button. If null, no button drawn. /// [optional] Callback call when advanced button clicked. Should be used to toggle its state. public static bool DrawSubHeaderFoldout(GUIContent title, bool state, bool isBoxed = false, Func hasMoreOptions = null, Action toggleMoreOptions = null) { const float height = 17f; var backgroundRect = GUILayoutUtility.GetRect(1f, height); var labelRect = backgroundRect; labelRect.xMin += 16f; labelRect.xMax -= 20f; var foldoutRect = backgroundRect; foldoutRect.y += 1f; foldoutRect.x += 15 * EditorGUI.indentLevel; //GUI do not handle indent. Handle it here foldoutRect.width = 13f; foldoutRect.height = 13f; // More options var advancedRect = new Rect(); if (hasMoreOptions != null) { advancedRect = backgroundRect; advancedRect.x += advancedRect.width - 16 - 1; advancedRect.height = 16; advancedRect.width = 16; bool moreOptions = hasMoreOptions(); bool newMoreOptions = Styles.DrawMoreOptions(advancedRect, moreOptions); if (moreOptions ^ newMoreOptions) toggleMoreOptions?.Invoke(); } // Background rect should be full-width backgroundRect.xMin = 0f; backgroundRect.width += 4f; if (isBoxed) { labelRect.xMin += 5; foldoutRect.xMin += 5; backgroundRect.xMin = EditorGUIUtility.singleLineHeight; backgroundRect.width -= 3; } // Title EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel); // Active checkbox state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout); var e = Event.current; if (e.type == EventType.MouseDown && backgroundRect.Contains(e.mousePosition) && !advancedRect.Contains(e.mousePosition) && e.button == 0) { state = !state; e.Use(); } return state; } public static bool DrawHeaderToggle(string title, SerializedProperty group, SerializedProperty activeField, Action contextAction = null, Func hasMoreOptions = null, Action toggleMoreOptions = null) => DrawHeaderToggle(EditorGUIUtility.TrTextContent(title), group, activeField, contextAction, hasMoreOptions, toggleMoreOptions); public static bool DrawHeaderToggle(GUIContent title, SerializedProperty group, SerializedProperty activeField, Action contextAction = null, Func hasMoreOptions = null, Action toggleMoreOptions = null) { var backgroundRect = GUILayoutUtility.GetRect(1f, 17f); var labelRect = backgroundRect; labelRect.xMin += 32f; labelRect.xMax -= 20f + 16 + 5; var foldoutRect = backgroundRect; foldoutRect.y += 1f; foldoutRect.width = 13f; foldoutRect.height = 13f; var toggleRect = backgroundRect; toggleRect.x += 16f; toggleRect.y += 2f; toggleRect.width = 13f; toggleRect.height = 13f; // More options 1/2 var moreOptionsRect = new Rect(); if (hasMoreOptions != null) { moreOptionsRect = backgroundRect; moreOptionsRect.x += moreOptionsRect.width - 16 - 1 - 16 - 5; moreOptionsRect.height = 15; moreOptionsRect.width = 16; } // Background rect should be full-width backgroundRect.xMin = 0f; backgroundRect.width += 4f; // Background float backgroundTint = EditorGUIUtility.isProSkin ? 0.1f : 1f; EditorGUI.DrawRect(backgroundRect, new Color(backgroundTint, backgroundTint, backgroundTint, 0.2f)); // Title using (new EditorGUI.DisabledScope(!activeField.boolValue)) EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel); // Foldout group.serializedObject.Update(); group.isExpanded = GUI.Toggle(foldoutRect, group.isExpanded, GUIContent.none, EditorStyles.foldout); group.serializedObject.ApplyModifiedProperties(); // Active checkbox activeField.serializedObject.Update(); activeField.boolValue = GUI.Toggle(toggleRect, activeField.boolValue, GUIContent.none, CoreEditorStyles.smallTickbox); activeField.serializedObject.ApplyModifiedProperties(); // More options 2/2 if (hasMoreOptions != null) { bool moreOptions = hasMoreOptions(); bool newMoreOptions = Styles.DrawMoreOptions(moreOptionsRect, moreOptions); if (moreOptions ^ newMoreOptions) toggleMoreOptions?.Invoke(); } // Context menu var menuIcon = CoreEditorStyles.paneOptionsIcon; var menuRect = new Rect(labelRect.xMax + 3f + 16 + 5 , labelRect.y + 1f, menuIcon.width, menuIcon.height); if (contextAction != null) GUI.DrawTexture(menuRect, menuIcon); // Handle events var e = Event.current; if (e.type == EventType.MouseDown) { if (contextAction != null && menuRect.Contains(e.mousePosition)) { contextAction(new Vector2(menuRect.x, menuRect.yMax)); e.Use(); } else if (labelRect.Contains(e.mousePosition)) { if (e.button == 0) group.isExpanded = !group.isExpanded; else if (contextAction != null) contextAction(e.mousePosition); e.Use(); } } return group.isExpanded; } static readonly GUIContent[] k_DrawVector6_Label = { new GUIContent("X"), new GUIContent("Y"), new GUIContent("Z"), }; const int k_DrawVector6Slider_LabelSize = 60; const int k_DrawVector6Slider_FieldSize = 80; public static void DrawVector6(GUIContent label, SerializedProperty positive, SerializedProperty negative, Vector3 min, Vector3 max, Color[] colors = null, SerializedProperty multiplicator = null) { if (colors != null && (colors.Length != 6)) throw new System.ArgumentException("Colors must be a 6 element array. [+X, +Y, +X, -X, -Y, -Z]"); GUILayout.BeginVertical(); Rect rect = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(0, float.MaxValue, EditorGUIUtility.singleLineHeight, EditorGUIUtility.singleLineHeight)); if (label != GUIContent.none) { var labelRect = rect; labelRect.x -= 15f * EditorGUI.indentLevel; labelRect.width = EditorGUIUtility.labelWidth; EditorGUI.LabelField(labelRect, label); rect.x += EditorGUIUtility.labelWidth - 1f - 15f * EditorGUI.indentLevel; rect.width -= EditorGUIUtility.labelWidth - 1f - 15f * EditorGUI.indentLevel; } DrawVector3(rect, k_DrawVector6_Label, positive, min, max, false, colors == null ? null : new Color[] { colors[0], colors[1], colors[2] }, multiplicator); GUILayout.Space(EditorGUIUtility.standardVerticalSpacing); rect = EditorGUI.IndentedRect(GUILayoutUtility.GetRect(0, float.MaxValue, EditorGUIUtility.singleLineHeight, EditorGUIUtility.singleLineHeight)); rect.x += EditorGUIUtility.labelWidth - 1f - 15f * EditorGUI.indentLevel; rect.width -= EditorGUIUtility.labelWidth - 1f - 15f * EditorGUI.indentLevel; DrawVector3(rect, k_DrawVector6_Label, negative, min, max, true, colors == null ? null : new Color[] { colors[3], colors[4], colors[5] }, multiplicator); GUILayout.EndVertical(); } static void DrawVector3(Rect rect, GUIContent[] labels, SerializedProperty value, Vector3 min, Vector3 max, bool addMinusPrefix, Color[] colors, SerializedProperty multiplicator = null) { float[] multifloat = multiplicator == null ? new float[] { value.vector3Value.x, value.vector3Value.y, value.vector3Value.z } : new float[] { value.vector3Value.x * multiplicator.vector3Value.x, value.vector3Value.y * multiplicator.vector3Value.y, value.vector3Value.z * multiplicator.vector3Value.z }; float fieldWidth = rect.width / 3f; EditorGUI.showMixedValue = value.hasMultipleDifferentValues; EditorGUI.BeginChangeCheck(); EditorGUI.MultiFloatField(rect, labels, multifloat); if(EditorGUI.EndChangeCheck()) { value.vector3Value = multiplicator == null ? new Vector3( Mathf.Clamp(multifloat[0], min.x, max.x), Mathf.Clamp(multifloat[1], min.y, max.y), Mathf.Clamp(multifloat[2], min.z, max.z) ) : new Vector3( Mathf.Clamp((multiplicator.vector3Value.x < -0.00001 || 0.00001 < multiplicator.vector3Value.x) ? multifloat[0] / multiplicator.vector3Value.x : 0f, min.x, max.x), Mathf.Clamp((multiplicator.vector3Value.y < -0.00001 || 0.00001 < multiplicator.vector3Value.y) ? multifloat[1] / multiplicator.vector3Value.y : 0f, min.y, max.y), Mathf.Clamp((multiplicator.vector3Value.z < -0.00001 || 0.00001 < multiplicator.vector3Value.z) ? multifloat[2] / multiplicator.vector3Value.z : 0f, min.z, max.z) ); } EditorGUI.showMixedValue = false; //Suffix is a hack as sublabel only work with 1 character if (addMinusPrefix) { Rect suffixRect = new Rect(rect.x - 4 - 15 * EditorGUI.indentLevel, rect.y, 100, rect.height); for(int i = 0; i < 3; ++i) { EditorGUI.LabelField(suffixRect, "-"); suffixRect.x += fieldWidth + .66f; } } //Color is a hack as nothing is done to handle this at the moment if(colors != null) { if (colors.Length != 3) throw new System.ArgumentException("colors must have 3 elements."); Rect suffixRect = new Rect(rect.x + 7 - 15 * EditorGUI.indentLevel, rect.y, 100, rect.height); GUIStyle colorMark = new GUIStyle(EditorStyles.label); colorMark.normal.textColor = colors[0]; EditorGUI.LabelField(suffixRect, "|", colorMark); suffixRect.x += 1; EditorGUI.LabelField(suffixRect, "|", colorMark); suffixRect.x += fieldWidth - .5f; colorMark.normal.textColor = colors[1]; EditorGUI.LabelField(suffixRect, "|", colorMark); suffixRect.x += 1; EditorGUI.LabelField(suffixRect, "|", colorMark); suffixRect.x += fieldWidth; colorMark.normal.textColor = colors[2]; EditorGUI.LabelField(suffixRect, "|", colorMark); suffixRect.x += 1; EditorGUI.LabelField(suffixRect, "|", colorMark); } } public static void DrawPopup(GUIContent label, SerializedProperty property, string[] options) { var mode = property.intValue; EditorGUI.BeginChangeCheck(); if (mode >= options.Length) Debug.LogError(string.Format("Invalid option while trying to set {0}", label.text)); mode = EditorGUILayout.Popup(label, mode, options); if (EditorGUI.EndChangeCheck()) property.intValue = mode; } /// /// Draw an EnumPopup handling multiEdition /// /// /// /// public static void DrawEnumPopup(SerializedProperty property, System.Type type, GUIContent label = null) { EditorGUI.showMixedValue = property.hasMultipleDifferentValues; EditorGUI.BeginChangeCheck(); var name = System.Enum.GetName(type, property.intValue); var index = System.Array.FindIndex(System.Enum.GetNames(type), n => n == name); var input = (System.Enum)System.Enum.GetValues(type).GetValue(index); var rawResult = EditorGUILayout.EnumPopup(label ?? EditorGUIUtility.TrTextContent(ObjectNames.NicifyVariableName(property.name)), input); var result = ((System.IConvertible)rawResult).ToInt32(System.Globalization.CultureInfo.CurrentCulture); if (EditorGUI.EndChangeCheck()) property.intValue = result; EditorGUI.showMixedValue = false; } public static void RemoveMaterialKeywords(Material material) => material.shaderKeywords = null; public static T[] GetAdditionalData(UnityEngine.Object[] targets, Action initDefault = null) where T : Component { // Handles multi-selection var data = targets.Cast() .Select(t => t.GetComponent()) .ToArray(); for (int i = 0; i < data.Length; i++) { if (data[i] == null) { data[i] = Undo.AddComponent(((Component)targets[i]).gameObject); if (initDefault != null) { initDefault(data[i]); } } } return data; } static public GameObject CreateGameObject(GameObject parent, string name, params Type[] types) => ObjectFactory.CreateGameObject(GameObjectUtility.GetUniqueNameForSibling(parent != null ? parent.transform : null, name), types); static public string GetCurrentProjectVersion() { string[] readText = File.ReadAllLines("ProjectSettings/ProjectVersion.txt"); // format is m_EditorVersion: 2018.2.0b7 string[] versionText = readText[0].Split(' '); return versionText[1]; } static public void CheckOutFile(bool VCSEnabled, UnityObject mat) { if (VCSEnabled) { UnityEditor.VersionControl.Task task = UnityEditor.VersionControl.Provider.Checkout(mat, UnityEditor.VersionControl.CheckoutMode.Both); if (!task.success) { Debug.Log(task.text + " " + task.resultCode); } } } #region IconAndSkin internal enum Skin { Auto, Personnal, Professional, } static Func GetInternalSkinIndex; static Func GetGUIStatePixelsPerPoint; static Func GetTexturePixelPerPoint; static Action SetTexturePixelPerPoint; static void LoadSkinAndIconMethods() { var internalSkinIndexInfo = typeof(EditorGUIUtility).GetProperty("skinIndex", BindingFlags.NonPublic | BindingFlags.Static); var internalSkinIndexLambda = Expression.Lambda>(Expression.Property(null, internalSkinIndexInfo)); GetInternalSkinIndex = internalSkinIndexLambda.Compile(); var guiStatePixelsPerPointInfo = typeof(GUIUtility).GetProperty("pixelsPerPoint", BindingFlags.NonPublic | BindingFlags.Static); var guiStatePixelsPerPointLambda = Expression.Lambda>(Expression.Property(null, guiStatePixelsPerPointInfo)); GetGUIStatePixelsPerPoint = guiStatePixelsPerPointLambda.Compile(); var pixelPerPointParam = Expression.Parameter(typeof(float), "pixelPerPoint"); var texture2DProperty = Expression.Parameter(typeof(Texture2D), "texture2D"); var texture2DPixelsPerPointInfo = typeof(Texture2D).GetProperty("pixelsPerPoint", BindingFlags.NonPublic | BindingFlags.Instance); var texture2DPixelsPerPointProperty = Expression.Property(texture2DProperty, texture2DPixelsPerPointInfo); var texture2DGetPixelsPerPointLambda = Expression.Lambda>(texture2DPixelsPerPointProperty, texture2DProperty); GetTexturePixelPerPoint = texture2DGetPixelsPerPointLambda.Compile(); var texture2DSetPixelsPerPointLambda = Expression.Lambda>(Expression.Assign(texture2DPixelsPerPointProperty, pixelPerPointParam), texture2DProperty, pixelPerPointParam); SetTexturePixelPerPoint = texture2DSetPixelsPerPointLambda.Compile(); } /// Get the skin currently in use static Skin currentSkin => GetInternalSkinIndex() == 0 ? Skin.Personnal : Skin.Professional; // /!\ UIElement do not support well pixel per point at the moment. For this, use the hack forceLowRes /// /// Load an icon regarding skin and editor resolution. /// Icon should be stored as legacy icon resources: /// - "d_" prefix for Professional theme /// - "@2x" suffix for high resolution /// /// Path to seek the icon from Assets/ folder /// Icon name without suffix, prefix or extention /// [Optional] Extention of file (png per default) /// [Optional] Load icon for this skin (Auto per default take current skin) public static Texture2D LoadIcon(string path, string name, string extention = ".png", bool forceLowRes = false) { if (String.IsNullOrEmpty(path) || String.IsNullOrEmpty(name)) return null; string prefix = ""; var skin = currentSkin; if (skin == Skin.Professional) prefix = "d_"; Texture2D icon = null; float pixelsPerPoint = GetGUIStatePixelsPerPoint(); if (pixelsPerPoint > 1.0f && !forceLowRes) { icon = EditorGUIUtility.Load(String.Format("{0}/{1}{2}@2x{3}", path, prefix, name, extention)) as Texture2D; if (icon == null && !string.IsNullOrEmpty(prefix)) icon = EditorGUIUtility.Load(String.Format("{0}/{1}@2x{2}", path, name, extention)) as Texture2D; if (icon != null) SetTexturePixelPerPoint(icon, 2.0f); } if (icon == null) icon = EditorGUIUtility.Load(String.Format("{0}/{1}{2}{3}", path, prefix, name, extention)) as Texture2D; if (icon == null && !string.IsNullOrEmpty(prefix)) icon = EditorGUIUtility.Load(String.Format("{0}/{1}{2}", path, name, extention)) as Texture2D; if (icon != null && !Mathf.Approximately(GetTexturePixelPerPoint(icon), pixelsPerPoint) && //scaling are different !Mathf.Approximately(pixelsPerPoint % 1, 0)) //screen scaling is non-integer { icon.filterMode = FilterMode.Bilinear; } return icon; } #endregion } }