namespace jeanf.validationTools { #if UNITY_EDITOR using System.Collections.Generic; using UnityEditor; using UnityEngine; /// Shared colors/styles so every validation surface speaks the same orange. public static class ValidationUi { /// The attention color used everywhere (hierarchy, inspector, fields). public static readonly Color Orange = new Color(1f, 0.6f, 0.1f); /// Translucent wash used behind fields/banners that need attention. public static readonly Color OrangeWash = new Color(1f, 0.6f, 0.1f, 0.14f); private static GUIStyle _orangeLabel; public static GUIStyle OrangeLabel { get { if (_orangeLabel == null) { _orangeLabel = new GUIStyle(GUI.skin.label); _orangeLabel.normal.textColor = Orange; _orangeLabel.hover.textColor = Orange; } return _orangeLabel; } } private static GUIStyle _orangeBoldLabel; public static GUIStyle OrangeBoldLabel { get { if (_orangeBoldLabel == null) { _orangeBoldLabel = new GUIStyle(EditorStyles.boldLabel); _orangeBoldLabel.normal.textColor = Orange; _orangeBoldLabel.hover.textColor = Orange; } return _orangeBoldLabel; } } private static readonly List Issues = new List(); /// /// Layout version of the ValidationDrawer treatment, for custom editors whose fields are /// only *conditionally* required (a [Validation] attribute would flag them unconditionally). /// When the field is unset: warning box just above it, orange wash behind the block and an /// orange label. When set, it draws like a normal field. /// public static void DrawRequiredField(SerializedProperty property, string message) { if (!ValidationDrawer.IsUnset(property)) { EditorGUILayout.PropertyField(property, true); return; } var block = EditorGUILayout.BeginVertical(); EditorGUI.DrawRect(block, OrangeWash); EditorGUILayout.HelpBox(message, MessageType.Warning); EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(new GUIContent(property.displayName, message), OrangeLabel, GUILayout.Width(EditorGUIUtility.labelWidth - 2f)); var previousColor = GUI.backgroundColor; GUI.backgroundColor = Orange; EditorGUILayout.PropertyField(property, GUIContent.none, true); GUI.backgroundColor = previousColor; EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); } /// /// Draws the orange "needs setup" banner for a component — title bar stripe plus the /// issue list — or nothing at all when the component is clean. ValidationInspectorBanner /// calls this for components using the default inspector; components with their own /// [CustomEditor] call it themselves so they keep the same banner instead of losing it. /// public static void DrawIssuesBanner(Component component) { if (component == null) return; Issues.Clear(); ValidationScanner.GetIssues(component, Issues); if (Issues.Count == 0) return; var title = $"⚠ {component.GetType().Name} needs setup"; var lineHeight = EditorGUIUtility.singleLineHeight; var height = lineHeight + 6f + Issues.Count * lineHeight; var rect = GUILayoutUtility.GetRect(0f, height, GUILayout.ExpandWidth(true)); EditorGUI.DrawRect(rect, OrangeWash); // Solid orange edge on the left, like Unity's own message stripes. EditorGUI.DrawRect(new Rect(rect.x, rect.y, 3f, rect.height), Orange); var line = new Rect(rect.x + 8f, rect.y + 3f, rect.width - 12f, lineHeight); EditorGUI.LabelField(line, title, OrangeBoldLabel); foreach (var issue in Issues) { line.y += lineHeight; var text = string.IsNullOrEmpty(issue.FieldName) ? issue.Message : $"• {issue.FieldName}: {issue.Message}"; EditorGUI.LabelField(line, text, OrangeLabel); } EditorGUILayout.Space(2f); } } #endif }