// shared by compute shader inspector too
internal static void ShaderErrorListUI(Object shader, ShaderMessage[] messages, ref Vector2 scrollPosition)
{
int n = messages.Length;
GUILayout.Space(kSpace);
GUILayout.Label(string.Format("Errors ({0}):", n), EditorStyles.boldLabel);
int errorListID = GUIUtility.GetControlID(kErrorViewHash, FocusType.Passive);
float height = Mathf.Min(n * 20f + 40f, 150f);
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUISkin.current.box, GUILayout.MinHeight(height));
EditorGUIUtility.SetIconSize(new Vector2(16.0f, 16.0f));
float lineHeight = Styles.messageStyle.CalcHeight(EditorGUIUtility.TempContent(Styles.errorIcon), 100);
Event e = Event.current;
for (int i = 0; i < n; ++i)
{
Rect r = EditorGUILayout.GetControlRect(false, lineHeight);
string err = messages[i].message;
string plat = messages[i].platform.ToString();
bool warn = messages[i].severity != ShaderCompilerMessageSeverity.Error;
string fileName = FileUtil.GetLastPathNameComponent(messages[i].file);
int line = messages[i].line;
// Double click opens shader file at error line
if (e.type == EventType.MouseDown && e.button == 0 && r.Contains(e.mousePosition))
{
GUIUtility.keyboardControl = errorListID;
if (e.clickCount == 2)
{
string filePath = messages[i].file;
Object asset = string.IsNullOrEmpty(filePath) ? null : AssetDatabase.LoadMainAssetAtPath(filePath);
// if we don't have an asset and the filePath is an absolute path, it's an error in a system
// cginc - open that instead
if (asset == null && System.IO.Path.IsPathRooted(filePath))
{
ShaderUtil.OpenSystemShaderIncludeError(filePath, line);
}
else
{
AssetDatabase.OpenAsset(asset ?? shader, line);
}
GUIUtility.ExitGUI();
}
e.Use();
}
// Context menu, "Copy"
if (e.type == EventType.ContextClick && r.Contains(e.mousePosition))
{
e.Use();
var menu = new GenericMenu();
// need to copy current value to be used in delegate
// (C# closures close over variables, not their values)
var errorIndex = i;
menu.AddItem(EditorGUIUtility.TrTextContent("Copy error text"), false, delegate {
string errMsg = messages[errorIndex].message;
if (!string.IsNullOrEmpty(messages[errorIndex].messageDetails))
{
errMsg += '\n';
errMsg += messages[errorIndex].messageDetails;
}
EditorGUIUtility.systemCopyBuffer = errMsg;
});
menu.ShowAsContext();
}
// background
if (e.type == EventType.Repaint)
{
if ((i & 1) == 0)
{
GUIStyle st = Styles.evenBackground;
st.Draw(r, false, false, false, false);
}
}
// error location on the right side
Rect locRect = r;
locRect.xMin = locRect.xMax;
if (line > 0)
{
GUIContent gc;
if (string.IsNullOrEmpty(fileName))
{
gc = EditorGUIUtility.TempContent(line.ToString(CultureInfo.InvariantCulture));
}
else
{
gc = EditorGUIUtility.TempContent(fileName + ":" + line.ToString(CultureInfo.InvariantCulture));
}
// calculate size so we can right-align it
Vector2 size = EditorStyles.miniLabel.CalcSize(gc);
locRect.xMin -= size.x;
GUI.Label(locRect, gc, EditorStyles.miniLabel);
locRect.xMin -= 2;
// ensure some minimum width so that platform field next will line up
if (locRect.width < 30)
{
locRect.xMin = locRect.xMax - 30;
}
}
// platform to the left of it
Rect platRect = locRect;
platRect.width = 0;
if (plat.Length > 0)
{
GUIContent gc = EditorGUIUtility.TempContent(plat);
// calculate size so we can right-align it
Vector2 size = EditorStyles.miniLabel.CalcSize(gc);
platRect.xMin -= size.x;
// draw platform in dimmer color; it's often not very important information
Color oldColor = GUI.contentColor;
GUI.contentColor = new Color(1, 1, 1, 0.5f);
GUI.Label(platRect, gc, EditorStyles.miniLabel);
GUI.contentColor = oldColor;
platRect.xMin -= 2;
}
// error message
Rect msgRect = r;
msgRect.xMax = platRect.xMin;
GUI.Label(msgRect, EditorGUIUtility.TempContent(err, warn ? Styles.warningIcon : Styles.errorIcon), Styles.messageStyle);
}
EditorGUIUtility.SetIconSize(Vector2.zero);
GUILayout.EndScrollView();
}