// Copyright (c) Jason Ma using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using NUnit.Framework; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; namespace LWGUI { /// /// Misc Function /// public class Helper { #region Engine Misc public static void ObsoleteWarning(string obsoleteStr, string newStr) { Debug.LogWarning("'" + obsoleteStr + "' is Obsolete! Please use '" + newStr + "'!"); } public static bool PropertyValueEquals(MaterialProperty prop1, MaterialProperty prop2) { if (prop1.textureValue == prop2.textureValue && prop1.vectorValue == prop2.vectorValue && prop1.colorValue == prop2.colorValue && prop1.floatValue == prop2.floatValue #if UNITY_2021_1_OR_NEWER && prop1.intValue == prop2.intValue #endif ) return true; else return false; } public static bool IsPropertyHideInInspector(MaterialProperty prop) { return (prop.flags & MaterialProperty.PropFlags.HideInInspector) != 0; } public static string GetKeyWord(string keyWord, string propName) { string k; if (string.IsNullOrEmpty(keyWord) || keyWord == "__") { k = propName.ToUpperInvariant() + "_ON"; } else { k = keyWord.ToUpperInvariant(); } return k; } public static void SetShaderKeyWord(Object[] materials, string keyWord, bool isEnable) { if (string.IsNullOrEmpty(keyWord) || string.IsNullOrEmpty(keyWord)) return; foreach (Material m in materials) { // delete "_" keywords if (keyWord == "_") { if (m.IsKeywordEnabled(keyWord)) { m.DisableKeyword(keyWord); } continue; } if (m.IsKeywordEnabled(keyWord)) { if (!isEnable) m.DisableKeyword(keyWord); } else { if (isEnable) m.EnableKeyword(keyWord); } } } public static void SetShaderKeyWord(Object[] materials, string[] keyWords, int index) { Debug.Assert(keyWords.Length >= 1 && index < keyWords.Length && index >= 0, "KeyWords Length: " + keyWords.Length + " or Index: " + index + " Error! "); for (int i = 0; i < keyWords.Length; i++) { SetShaderKeyWord(materials, keyWords[i], index == i); } } public static void SetShaderPassEnabled(Object[] materials, string[] lightModeNames, bool enabled) { if (lightModeNames.Length == 0) return; foreach (Material material in materials) { for (int i = 0; i < lightModeNames.Length; i++) { material.SetShaderPassEnabled(lightModeNames[i], enabled); } } } /// /// make Drawer can get all current Material props by customShaderGUI /// Unity 2019.2+ /// public static LWGUI GetLWGUI(MaterialEditor editor) { var customShaderGUI = ReflectionHelper.GetCustomShaderGUI(editor); if (customShaderGUI != null && customShaderGUI is LWGUI) { LWGUI gui = customShaderGUI as LWGUI; return gui; } else { Debug.LogWarning("Please add \"CustomEditor \"LWGUI.LWGUI\"\" to the end of your shader!"); return null; } } public static void AdaptiveFieldWidth(GUIStyle style, GUIContent content, float extraWidth = 0) { var extraTextWidth = Mathf.Max(0, style.CalcSize(content).x + extraWidth - EditorGUIUtility.fieldWidth); EditorGUIUtility.labelWidth -= extraTextWidth; EditorGUIUtility.fieldWidth += extraTextWidth; } public static void BeginProperty(Rect rect, MaterialProperty property, LWGUI lwgui) { #if UNITY_2022_1_OR_NEWER MaterialEditor.BeginProperty(rect, property); foreach (var extraPropName in lwgui.perShaderData.propertyDatas[property.name].extraPropNames) MaterialEditor.BeginProperty(rect, lwgui.perFrameData.propertyDatas[extraPropName].property); #endif } public static void EndProperty(LWGUI lwgui, MaterialProperty property) { #if UNITY_2022_1_OR_NEWER MaterialEditor.EndProperty(); foreach (var extraPropName in lwgui.perShaderData.propertyDatas[property.name].extraPropNames) MaterialEditor.EndProperty(); #endif } public static bool EndChangeCheck(LWGUI lwgui, MaterialProperty property) { return lwgui.perFrameData.EndChangeCheck(property.name); } #endregion #region Math public static float PowPreserveSign(float f, float p) { float num = Mathf.Pow(Mathf.Abs(f), p); if ((double)f < 0.0) return -num; return num; } #endregion #region GUI Styles // Tips: Use properties to fix null reference errors private static GUIStyle _guiStyles_IconButton; public static GUIStyle guiStyles_IconButton { get { if (_guiStyles_IconButton == null) { _guiStyles_IconButton = new GUIStyle( #if UNITY_2021_2_OR_NEWER EditorStyles.iconButton #else "iconButton" #endif ) { fixedHeight = 0, fixedWidth = 0 }; } return _guiStyles_IconButton; } } private static GUIStyle _guiStyle_Foldout; public static GUIStyle guiStyle_Foldout { get { if (_guiStyle_Foldout == null) { _guiStyle_Foldout = new GUIStyle(EditorStyles.miniButton) { contentOffset = new Vector2(22, 0), fixedHeight = 27, alignment = TextAnchor.MiddleLeft, font = EditorStyles.boldLabel.font, fontSize = EditorStyles.boldLabel.fontSize #if UNITY_2019_4_OR_NEWER + 1, #endif }; } return _guiStyle_Foldout; } } private static GUIStyle _guiStyle_Helpbox; public static GUIStyle guiStyle_Helpbox { get { if (_guiStyle_Helpbox == null) { _guiStyle_Helpbox = new GUIStyle(EditorStyles.helpBox) { fontSize = 12 }; } return _guiStyle_Helpbox; } } private static GUIStyle _guiStyles_ToolbarSearchTextFieldPopup; public static GUIStyle guiStyles_ToolbarSearchTextFieldPopup { get { if (_guiStyles_ToolbarSearchTextFieldPopup == null) { string toolbarSeachTextFieldPopupStr = "ToolbarSeachTextFieldPopup"; { // ToolbarSeachTextFieldPopup has renamed at Unity 2021.3.28+ #if !UNITY_2022_3_OR_NEWER string[] versionParts = Application.unityVersion.Split('.'); int majorVersion = int.Parse(versionParts[0]); int minorVersion = int.Parse(versionParts[1]); Match patchVersionMatch = Regex.Match(versionParts[2], @"\d+"); int patchVersion = int.Parse(patchVersionMatch.Value); if (majorVersion >= 2021 && minorVersion >= 3 && patchVersion >= 28) #endif { toolbarSeachTextFieldPopupStr = "ToolbarSearchTextFieldPopup"; } } _guiStyles_ToolbarSearchTextFieldPopup = new GUIStyle(toolbarSeachTextFieldPopupStr); } return _guiStyles_ToolbarSearchTextFieldPopup; } } #endregion #region Draw GUI for Drawer // TODO: use Reflection // copy and edit of https://github.com/GucioDevs/SimpleMinMaxSlider/blob/master/Assets/SimpleMinMaxSlider/Scripts/Editor/MinMaxSliderDrawer.cs public static Rect[] SplitRect(Rect rectToSplit, int n) { Rect[] rects = new Rect[n]; for (int i = 0; i < n; i++) { rects[i] = new Rect(rectToSplit.position.x + (i * rectToSplit.width / n), rectToSplit.position.y, rectToSplit.width / n, rectToSplit.height); } int padding = (int)rects[0].width - 50; // use 50, enough to show 0.xx (2 digits) int space = 5; rects[0].width -= padding + space; rects[2].width -= padding + space; rects[1].x -= padding; rects[1].width += padding * 2; rects[2].x += padding + space; return rects; } public static bool DrawFoldout(Rect rect, ref bool isFolding, bool toggleValue, bool hasToggle, GUIContent label) { var toggleRect = new Rect(rect.x + 8f, rect.y + 7f, 13f, 13f); // Toggle Event if (hasToggle) { if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && toggleRect.Contains(Event.current.mousePosition)) { toggleValue = !toggleValue; Event.current.Use(); GUI.changed = true; } } // Button { // Cancel Right Click if (Event.current.type == EventType.MouseDown && Event.current.button == 1 && rect.Contains(Event.current.mousePosition)) Event.current.Use(); var enabled = GUI.enabled; GUI.enabled = true; var guiColor = GUI.backgroundColor; GUI.backgroundColor = isFolding ? Color.white : new Color(0.85f, 0.85f, 0.85f); if (GUI.Button(rect, label, guiStyle_Foldout)) { isFolding = !isFolding; GUI.changed = false; } GUI.backgroundColor = guiColor; GUI.enabled = enabled; } // Toggle Icon if (hasToggle) { EditorGUI.Toggle(toggleRect, string.Empty, toggleValue); } return toggleValue; } #endregion #region Draw GUI for Material public static void DrawSplitLine() { var rect = EditorGUILayout.GetControlRect(true, 1); rect.x = 0; rect.width = EditorGUIUtility.currentViewWidth; EditorGUI.DrawRect(rect, new Color(0, 0, 0, 0.45f)); } private static readonly Texture2D _helpboxIcon = EditorGUIUtility.IconContent("console.infoicon").image as Texture2D; public static void DrawHelpbox(PropertyStaticData propertyStaticData, PropertyDynamicData propertyDynamicData) { var helpboxStr = propertyStaticData.helpboxMessages; if (!string.IsNullOrEmpty(helpboxStr)) { var content = new GUIContent(helpboxStr, _helpboxIcon); var helpboxRect = EditorGUI.IndentedRect(EditorGUILayout.GetControlRect(true, guiStyle_Helpbox.CalcHeight(content, EditorGUIUtility.currentViewWidth))); helpboxRect.xMax -= RevertableHelper.revertButtonWidth; GUI.Label(helpboxRect, content, guiStyle_Helpbox); // EditorGUI.HelpBox(helpboxRect, helpboxStr, MessageType.Info); } } private static Texture _logo = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath("26b9d845eb7b1a747bf04dc84e5bcc2c")); private static GUIContent _logoGuiContent = new GUIContent(string.Empty, _logo, "LWGUI (Light Weight Shader GUI)\n\n" + "A Lightweight, Flexible, Powerful Unity Shader GUI system.\n\n" + "Copyright (c) Jason Ma"); public static void DrawLogo() { var logoRect = EditorGUILayout.GetControlRect(false, _logo.height); var w = logoRect.width; logoRect.xMin += w * 0.5f - _logo.width * 0.5f; logoRect.xMax -= w * 0.5f - _logo.width * 0.5f; if (EditorGUIUtility.currentViewWidth >= logoRect.width && GUI.Button(logoRect, _logoGuiContent, guiStyles_IconButton)) { Application.OpenURL("https://github.com/JasonMa0012/LWGUI"); } } #endregion #region Toolbar Buttons private static Material _copiedMaterial; private static List _copiedProps = new List(); private static Texture _iconCopy = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath("9cdef444d18d2ce4abb6bbc4fed4d109")); private static Texture _iconPaste = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath("8e7a78d02e4c3574998524a0842a8ccb")); private static Texture _iconSelect = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath("6f44e40b24300974eb607293e4224ecc")); private static Texture _iconCheckout = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath("72488141525eaa8499e65e52755cb6d0")); private static Texture _iconExpand = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath("2382450e7f4ddb94c9180d6634c41378")); private static Texture _iconCollapse = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath("929b6e5dfacc42b429d715a3e1ca2b57")); private static Texture _iconVisibility = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath("9576e23a695b35d49a9fc55c9a948b4f")); private static GUIContent _guiContentCopy = new GUIContent("", _iconCopy, "Copy Material Properties"); private static GUIContent _guiContentPaste = new GUIContent("", _iconPaste, "Paste Material Properties\n\nRight-click to paste values by type."); private static GUIContent _guiContentSelect = new GUIContent("", _iconSelect, "Select the Material Asset\n\nUsed to jump from a Runtime Material Instance to a Material Asset."); private static GUIContent _guiContentChechout = new GUIContent("", _iconCheckout, "Checkout selected Material Assets"); private static GUIContent _guiContentExpand = new GUIContent("", _iconExpand, "Expand All Groups"); private static GUIContent _guiContentCollapse = new GUIContent("", _iconCollapse, "Collapse All Groups"); private static GUIContent _guiContentVisibility = new GUIContent("", _iconVisibility, "Display Mode"); private static string[] _materialInstanceNameEnd = new[] { "_Instantiated", " (Instance)" }; private enum CopyMaterialValueMask { Float = 1 << 0, Vector = 1 << 1, Texture = 1 << 2, Keyword = 1 << 3, RenderQueue = 1 << 4, Number = Float | Vector, All = (1 << 5) - 1, } private static GUIContent[] _pasteMaterialMenus = new[] { new GUIContent("Paste Number Values"), new GUIContent("Paste Texture Values"), new GUIContent("Paste Keywords"), new GUIContent("Paste RenderQueue"), }; private static uint[] _pasteMaterialMenuValueMasks = new[] { (uint)CopyMaterialValueMask.Number, (uint)CopyMaterialValueMask.Texture, (uint)CopyMaterialValueMask.Keyword, (uint)CopyMaterialValueMask.RenderQueue, }; private static void DoPasteMaterialProperties(LWGUI lwgui , uint valueMask) { if (!_copiedMaterial) { Debug.LogError("Please copy Material Properties first!"); return; } foreach (Material material in lwgui.materialEditor.targets) { if (!VersionControlHelper.Checkout(material)) { Debug.LogError("Material: '" + lwgui.material.name + "' unable to write!"); return; } Undo.RecordObject(material, "Paste Material Properties"); for (int i = 0; i < ShaderUtil.GetPropertyCount(_copiedMaterial.shader); i++) { var name = ShaderUtil.GetPropertyName(_copiedMaterial.shader, i); var type = ShaderUtil.GetPropertyType(_copiedMaterial.shader, i); PastePropertyValueToMaterial(material, name, name, type, valueMask); } if ((valueMask & (uint)CopyMaterialValueMask.Keyword) != 0) material.shaderKeywords = _copiedMaterial.shaderKeywords; if ((valueMask & (uint)CopyMaterialValueMask.RenderQueue) != 0) material.renderQueue = _copiedMaterial.renderQueue; } } private static void PastePropertyValueToMaterial(Material material, string srcName, string dstName) { for (int i = 0; i < ShaderUtil.GetPropertyCount(_copiedMaterial.shader); i++) { var name = ShaderUtil.GetPropertyName(_copiedMaterial.shader, i); if (name == srcName) { var type = ShaderUtil.GetPropertyType(_copiedMaterial.shader, i); PastePropertyValueToMaterial(material, srcName, dstName, type); return; } } } private static void PastePropertyValueToMaterial(Material material, string srcName, string dstName, ShaderUtil.ShaderPropertyType type, uint valueMask = (uint)CopyMaterialValueMask.All) { switch (type) { case ShaderUtil.ShaderPropertyType.Color: if ((valueMask & (uint)CopyMaterialValueMask.Vector) != 0) material.SetColor(dstName, _copiedMaterial.GetColor(srcName)); break; case ShaderUtil.ShaderPropertyType.Vector: if ((valueMask & (uint)CopyMaterialValueMask.Vector) != 0) material.SetVector(dstName, _copiedMaterial.GetVector(srcName)); break; case ShaderUtil.ShaderPropertyType.TexEnv: if ((valueMask & (uint)CopyMaterialValueMask.Texture) != 0) material.SetTexture(dstName, _copiedMaterial.GetTexture(srcName)); break; // Float default: if ((valueMask & (uint)CopyMaterialValueMask.Float) != 0) material.SetFloat(dstName, _copiedMaterial.GetFloat(srcName)); break; } } public static void DrawToolbarButtons(ref Rect toolBarRect, LWGUI lwgui) { // Copy var buttonRectOffset = toolBarRect.height + 2; var buttonRect = new Rect(toolBarRect.x, toolBarRect.y, toolBarRect.height, toolBarRect.height); toolBarRect.xMin += buttonRectOffset; if (GUI.Button(buttonRect, _guiContentCopy, Helper.guiStyles_IconButton)) { _copiedMaterial = UnityEngine.Object.Instantiate(lwgui.material); } // Paste buttonRect.x += buttonRectOffset; toolBarRect.xMin += buttonRectOffset; // Right Click if (Event.current.type == EventType.MouseDown && Event.current.button == 1 && buttonRect.Contains(Event.current.mousePosition)) { EditorUtility.DisplayCustomMenu(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0, 0), _pasteMaterialMenus, -1, (data, options, selected) => { DoPasteMaterialProperties(lwgui, _pasteMaterialMenuValueMasks[selected]); }, null); Event.current.Use(); } // Left Click if (GUI.Button(buttonRect, _guiContentPaste, Helper.guiStyles_IconButton)) { DoPasteMaterialProperties(lwgui, (uint)CopyMaterialValueMask.All); } // Select Material Asset, jump from a Runtime Material Instance to a Material Asset buttonRect.x += buttonRectOffset; toolBarRect.xMin += buttonRectOffset; if (GUI.Button(buttonRect, _guiContentSelect, Helper.guiStyles_IconButton)) { if (AssetDatabase.Contains(lwgui.material)) { Selection.activeObject = lwgui.material; } else { // Get Material Asset name var name = lwgui.material.name; foreach (var nameEnd in _materialInstanceNameEnd) { if (name.EndsWith(nameEnd)) { name = name.Substring(0, name.Length - nameEnd.Length); break; } } // Get path var guids = AssetDatabase.FindAssets("t:Material " + name); var paths = guids.Select(((guid, i) => { var filePath = AssetDatabase.GUIDToAssetPath(guid); var fileName = System.IO.Path.GetFileNameWithoutExtension(filePath); return (fileName == name && filePath.EndsWith(".mat")) ? filePath : null; })).Where((s => !string.IsNullOrEmpty(s))).ToArray(); // Select Asset if (paths.Length == 0) { Debug.LogError("Can not find Material Assets with name: " + name); } else if (paths.Length > 1) { var str = string.Empty; foreach (string path in paths) { str += "\n" + path; } Debug.LogWarning("Multiple Material Assets with the same name have been found, select only the first one:" + str); Selection.activeObject = AssetDatabase.LoadAssetAtPath(paths[0]); } else { Selection.activeObject = AssetDatabase.LoadAssetAtPath(paths[0]); } } } // Checkout buttonRect.x += buttonRectOffset; toolBarRect.xMin += buttonRectOffset; if (GUI.Button(buttonRect, _guiContentChechout, Helper.guiStyles_IconButton)) { foreach (var material in lwgui.materialEditor.targets) { VersionControlHelper.Checkout(material); } } // Expand buttonRect.x += buttonRectOffset; toolBarRect.xMin += buttonRectOffset; if (GUI.Button(buttonRect, _guiContentExpand, Helper.guiStyles_IconButton)) { foreach (var propertyStaticDataPair in lwgui.perShaderData.propertyDatas) { if (propertyStaticDataPair.Value.isMain || propertyStaticDataPair.Value.isAdvancedHeader) propertyStaticDataPair.Value.isExpanding = true; } } // Collapse buttonRect.x += buttonRectOffset; toolBarRect.xMin += buttonRectOffset; if (GUI.Button(buttonRect, _guiContentCollapse, Helper.guiStyles_IconButton)) { foreach (var propertyStaticDataPair in lwgui.perShaderData.propertyDatas) { if (propertyStaticDataPair.Value.isMain || propertyStaticDataPair.Value.isAdvancedHeader) propertyStaticDataPair.Value.isExpanding = false; } } // Display Mode buttonRect.x += buttonRectOffset; toolBarRect.xMin += buttonRectOffset; var color = GUI.color; if (!lwgui.perShaderData.displayModeData.IsDefaultDisplayMode()) GUI.color = Color.yellow; if (GUI.Button(buttonRect, _guiContentVisibility, Helper.guiStyles_IconButton)) { string[] displayModeMenus = new[] { "Show All Advanced (" + lwgui.perShaderData.displayModeData.advancedCount + " of " + lwgui.perShaderData.propertyDatas.Count + ")", "Show All Hidden (" + lwgui.perShaderData.displayModeData.hiddenCount + " of " + lwgui.perShaderData.propertyDatas.Count + ")", "Show Only Modified (" + lwgui.perFrameData.modifiedCount + " of " + lwgui.perShaderData.propertyDatas.Count + ")", }; bool[] enabled = new[] { true, true, true }; bool[] separator = new bool[3]; int[] selected = new[] { lwgui.perShaderData.displayModeData.showAllAdvancedProperties ? 0 : -1, lwgui.perShaderData.displayModeData.showAllHiddenProperties ? 1 : -1, lwgui.perShaderData.displayModeData.showOnlyModifiedProperties ? 2 : -1, }; ReflectionHelper.DisplayCustomMenuWithSeparators(new Rect(Event.current.mousePosition.x, Event.current.mousePosition.y, 0, 0), displayModeMenus, enabled, separator, selected, (data, options, selectedIndex) => { switch (selectedIndex) { case 0: lwgui.perShaderData.displayModeData.showAllAdvancedProperties = !lwgui.perShaderData.displayModeData.showAllAdvancedProperties; lwgui.perShaderData.ToggleShowAllAdvancedProperties(); break; case 1: lwgui.perShaderData.displayModeData.showAllHiddenProperties = !lwgui.perShaderData.displayModeData.showAllHiddenProperties; break; case 2: lwgui.perShaderData.displayModeData.showOnlyModifiedProperties = !lwgui.perShaderData.displayModeData.showOnlyModifiedProperties; break; } }); } GUI.color = color; toolBarRect.xMin += 2; } #endregion #region Search Field private static readonly int s_TextFieldHash = "EditorTextField".GetHashCode(); private static readonly GUIContent[] _searchModeMenus = (new GUIContent[(int)SearchMode.Num]).Select(((guiContent, i) => { if (i == (int)SearchMode.Num) return null; return new GUIContent(((SearchMode)i).ToString()); })).ToArray(); /// is has changed? public static bool DrawSearchField(Rect rect, LWGUI lwgui) { bool hasChanged = false; EditorGUI.BeginChangeCheck(); var revertButtonRect = RevertableHelper.SplitRevertButtonRect(ref rect); // Get internal TextField ControlID int controlId = GUIUtility.GetControlID(s_TextFieldHash, FocusType.Keyboard, rect) + 1; // searching mode Rect modeRect = new Rect(rect); modeRect.width = 20f; if (Event.current.type == UnityEngine.EventType.MouseDown && modeRect.Contains(Event.current.mousePosition)) { EditorUtility.DisplayCustomMenu(rect, _searchModeMenus, (int)lwgui.perShaderData.searchMode, (data, options, selected) => { lwgui.perShaderData.searchMode = (SearchMode)selected; hasChanged = true; }, null); Event.current.Use(); } lwgui.perShaderData.searchString = EditorGUI.TextField(rect, String.Empty, lwgui.perShaderData.searchString, guiStyles_ToolbarSearchTextFieldPopup); if (EditorGUI.EndChangeCheck()) hasChanged = true; // revert button if (!string.IsNullOrEmpty(lwgui.perShaderData.searchString) && RevertableHelper.DrawRevertButton(revertButtonRect)) { lwgui.perShaderData.searchString = string.Empty; hasChanged = true; GUIUtility.keyboardControl = 0; } // display search mode if (GUIUtility.keyboardControl != controlId && string.IsNullOrEmpty(lwgui.perShaderData.searchString) && Event.current.type == UnityEngine.EventType.Repaint) { using (new EditorGUI.DisabledScope(true)) { #if UNITY_2019_2_OR_NEWER var disableTextRect = new Rect(rect.x, rect.y, rect.width, guiStyles_ToolbarSearchTextFieldPopup.fixedHeight > 0.0 ? guiStyles_ToolbarSearchTextFieldPopup.fixedHeight : rect.height); #else var disableTextRect = rect; disableTextRect.yMin -= 3f; #endif disableTextRect = guiStyles_ToolbarSearchTextFieldPopup.padding.Remove(disableTextRect); int fontSize = EditorStyles.label.fontSize; EditorStyles.label.fontSize = guiStyles_ToolbarSearchTextFieldPopup.fontSize; EditorStyles.label.Draw(disableTextRect, new GUIContent(lwgui.perShaderData.searchMode.ToString()), false, false, false, false); EditorStyles.label.fontSize = fontSize; } } if (hasChanged) lwgui.perShaderData.UpdateSearchFilter(); return hasChanged; } #endregion #region Context Menu private static void EditPresetEvent(string mode, ShaderPropertyPreset presetAsset, ShaderPropertyPreset.Preset activePreset, MaterialProperty prop, LWGUI lwgui) { if (!VersionControlHelper.Checkout(presetAsset)) { Debug.LogError("Can not edit the preset: " + presetAsset); return; } switch (mode) { case "Add": case "Update": activePreset.AddOrUpdateIncludeExtraProperties(lwgui, prop); break; case "Remove": activePreset.RemoveIncludeExtraProperties(lwgui, prop.name); break; } EditorUtility.SetDirty(presetAsset); } public static void DoPropertyContextMenus(Rect rect, MaterialProperty prop, LWGUI lwgui) { if (Event.current.type != EventType.ContextClick || !rect.Contains(Event.current.mousePosition)) return; Event.current.Use(); var propStaticData = lwgui.perShaderData.propertyDatas[prop.name]; var menus = new GenericMenu(); // 2022+ Material Varant Menus #if UNITY_2022_1_OR_NEWER ReflectionHelper.HandleApplyRevert(menus, prop); #endif // Copy menus.AddItem(new GUIContent("Copy"), false, () => { _copiedMaterial = UnityEngine.Object.Instantiate(lwgui.material); _copiedProps.Clear(); _copiedProps.Add(prop.name); foreach (var extraPropName in propStaticData.extraPropNames) { _copiedProps.Add(extraPropName); } // Copy Children foreach (var childPropStaticData in propStaticData.children) { _copiedProps.Add(childPropStaticData.name); foreach (var extraPropName in childPropStaticData.extraPropNames) { _copiedProps.Add(extraPropName); } foreach (var childChildPropStaticData in childPropStaticData.children) { _copiedProps.Add(childChildPropStaticData.name); foreach (var extraPropName in childChildPropStaticData.extraPropNames) { _copiedProps.Add(extraPropName); } } } }); // Paste GenericMenu.MenuFunction pasteAction = () => { foreach (Material material in prop.targets) { if (!VersionControlHelper.Checkout(material)) { Debug.LogError("Material: '" + lwgui.material.name + "' unable to write!"); return; } Undo.RecordObject(material, "Paste Material Properties"); var index = 0; PastePropertyValueToMaterial(material, _copiedProps[index++], prop.name); foreach (var extraPropName in propStaticData.extraPropNames) { if (index == _copiedProps.Count) break; PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName); } // Paste Children foreach (var childPropStaticData in propStaticData.children) { if (index == _copiedProps.Count) break; PastePropertyValueToMaterial(material, _copiedProps[index++], childPropStaticData.name); foreach (var extraPropName in childPropStaticData.extraPropNames) { if (index == _copiedProps.Count) break; PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName); } foreach (var childChildPropStaticData in childPropStaticData.children) { if (index == _copiedProps.Count) break; PastePropertyValueToMaterial(material, _copiedProps[index++], childChildPropStaticData.name); foreach (var extraPropName in childChildPropStaticData.extraPropNames) { if (index == _copiedProps.Count) break; PastePropertyValueToMaterial(material, _copiedProps[index++], extraPropName); } } } } }; if (_copiedMaterial != null && _copiedProps.Count > 0 && GUI.enabled) menus.AddItem(new GUIContent("Paste"), false, pasteAction); else menus.AddDisabledItem(new GUIContent("Paste")); menus.AddSeparator(""); // Copy Display Name menus.AddItem(new GUIContent("Copy Display Name"), false, () => { EditorGUIUtility.systemCopyBuffer = propStaticData.displayName; }); // Copy Property Names menus.AddItem(new GUIContent("Copy Property Names"), false, () => { EditorGUIUtility.systemCopyBuffer = prop.name; foreach (var extraPropName in propStaticData.extraPropNames) { EditorGUIUtility.systemCopyBuffer += ", " + extraPropName; } }); // menus.AddSeparator(""); // // // Add to Favorites // menus.AddItem(new GUIContent("Add to Favorites"), false, () => // { // }); // // // Remove from Favorites // menus.AddItem(new GUIContent("Remove from Favorites"), false, () => // { // }); // Preset if (GUI.enabled) { menus.AddSeparator(""); foreach (var activePresetData in lwgui.perFrameData.activePresets) { if (activePresetData.property == prop) continue; var activePreset = activePresetData.preset; var presetAsset = lwgui.perShaderData.propertyDatas[activePresetData.property.name].propertyPresetAsset; var presetPropDisplayName = lwgui.perShaderData.propertyDatas[activePresetData.property.name].displayName; if (activePreset.GetPropertyValue(prop.name) != null) { menus.AddItem(new GUIContent("Update to Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Update", presetAsset, activePreset, prop, lwgui)); menus.AddItem(new GUIContent("Remove from Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Remove", presetAsset, activePreset, prop, lwgui)); } else { menus.AddItem(new GUIContent("Add to Preset/" + presetPropDisplayName + "/" + activePreset.presetName), false, () => EditPresetEvent("Add", presetAsset, activePreset, prop, lwgui)); } } } menus.ShowAsContext(); } #endregion } }