using System.Collections.Generic; using System.Text.RegularExpressions; using UnityEditor; using UnityEngine; namespace Amanotes { internal class LogTab { private Core.AmaDevSettings settings; private SerializedObject serializedSettings; private bool needsRefresh = true; private Vector2 scrollPos; private bool tagsExpanded = true; private bool configExpanded; private bool templateExpanded; private readonly Color rowBg = new Color(0.22f, 0.22f, 0.22f, 1f); private readonly Color headerBg = new Color(0.19f, 0.19f, 0.19f, 1f); private readonly Color separatorColor = new Color(0.1f, 0.1f, 0.1f, 1f); private GUIStyle _sRowLabel, _sHeader, _sSmallBtn, _sDimLabel; private GUIStyle sRowLabel => _sRowLabel ??= new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft, padding = new RectOffset(4, 0, 0, 0) }; private GUIStyle sDimLabel => _sDimLabel ??= new GUIStyle(EditorStyles.label) { alignment = TextAnchor.MiddleLeft, padding = new RectOffset(4, 0, 0, 0), normal = { textColor = new Color(0.55f, 0.55f, 0.55f) } }; private GUIStyle sHeader { get { if (_sHeader != null) return _sHeader; _sHeader = new GUIStyle(EditorStyles.foldout) { fontSize = 14 }; _sHeader.normal.textColor = _sHeader.onNormal.textColor = _sHeader.focused.textColor = _sHeader.onFocused.textColor = _sHeader.active.textColor = _sHeader.onActive.textColor = _sHeader.hover.textColor = _sHeader.onHover.textColor = Color.white; return _sHeader; } } private GUIStyle sSmallBtn { get { if (_sSmallBtn != null) return _sSmallBtn; _sSmallBtn = new GUIStyle(GUI.skin.button) { fontSize = 9, alignment = TextAnchor.MiddleCenter, padding = new RectOffset(4, 4, 0, 0), margin = new RectOffset(0, 0, 0, 0), overflow = new RectOffset(0, 0, 0, 0) }; return _sSmallBtn; } } internal void Refresh() => needsRefresh = true; private void EnsureData() { if (!needsRefresh && settings != null) return; needsRefresh = false; settings = Core.AmaDevSettings.Instance; if (settings != null) serializedSettings = new SerializedObject(settings); } internal void OnGUI() { EnsureData(); if (settings == null) { EditorGUILayout.HelpBox("No AmaDevSettings asset found.", MessageType.Info); return; } if (EditorApplication.isCompiling) { EditorGUILayout.HelpBox("Compiling...", MessageType.Info); return; } serializedSettings.Update(); scrollPos = EditorGUILayout.BeginScrollView(scrollPos); DrawTagsList(); GUILayout.Space(4); DrawConfig(); GUILayout.Space(4); DrawTemplate(); EditorGUILayout.EndScrollView(); serializedSettings.ApplyModifiedProperties(); DrawFooter(); } private void DrawTagsList() { var tags = settings.logV2.tags; var headerRect = GUILayoutUtility.GetRect(0, 26f, GUILayout.ExpandWidth(true)); EditorGUI.DrawRect(headerRect, headerBg); EditorGUI.DrawRect(new Rect(headerRect.x, headerRect.y, headerRect.width, 1), separatorColor); const float btnW = 36f; const float btnH = 16f; const float addW = 20f; float bx = headerRect.xMax - 6; bx -= btnW; if (GUI.Button(new Rect(bx, headerRect.y + (26f - btnH) * 0.5f, btnW, btnH), "None", sSmallBtn)) SetAll(false); bx -= btnW + 2; if (GUI.Button(new Rect(bx, headerRect.y + (26f - btnH) * 0.5f, btnW, btnH), "All", sSmallBtn)) SetAll(true); bx -= addW + 2; if (GUI.Button(new Rect(bx, headerRect.y + (26f - btnH) * 0.5f, addW, btnH), "+", sSmallBtn)) AddTagDialog(); var foldW = bx - headerRect.x - 8; tagsExpanded = EditorGUI.Foldout(new Rect(headerRect.x + 4, headerRect.y, foldW, headerRect.height), tagsExpanded, $"Tags ({tags.Count})", true, sHeader); if (!tagsExpanded) return; for (int i = 0; i < tags.Count; i++) DrawTagRow(tags[i], i); } private void DrawTagRow(Core.AmaDevSettings.SymbolInfo tag, int index) { const float rowH = 24f; const float toggleW = 36f; const float tagH = 14f; const float pad = 4f; const float closeW = 14f; var rect = GUILayoutUtility.GetRect(0, rowH, GUILayout.ExpandWidth(true)); if (Event.current.type == EventType.Repaint) { EditorGUI.DrawRect(rect, rowBg); EditorGUI.DrawRect(new Rect(rect.x, rect.yMax - 1, rect.width, 1), new Color(0.18f, 0.18f, 0.18f, 0.5f)); } float x = rect.x + pad; var oldCloseColor = GUI.color; GUI.color = new Color(1f, 1f, 1f, 0.3f); if (GUI.Button(new Rect(x, rect.y, closeW, rowH), "\u00D7", EditorStyles.label)) { settings.logV2.tags.RemoveAt(index); EditorUtility.SetDirty(settings); return; } GUI.color = oldCloseColor; x += closeW + 4; float rx = rect.xMax - pad - toggleW; var btnRect = new Rect(rx, rect.y + (rowH - tagH - 2) * 0.5f, toggleW, tagH + 2); var oldBg = GUI.backgroundColor; GUI.backgroundColor = tag.enabled ? new Color(0.4f, 0.9f, 0.4f, 1f) : new Color(0.5f, 0.5f, 0.5f, 0.5f); if (GUI.Button(btnRect, tag.enabled ? "ON" : "OFF", sSmallBtn)) { tag.enabled = !tag.enabled; EditorUtility.SetDirty(settings); } GUI.backgroundColor = oldBg; float nameW = rx - x - pad; var nameRect = new Rect(x, rect.y, nameW, rowH); if (Event.current.type == EventType.Repaint) (tag.enabled ? sRowLabel : sDimLabel).Draw(nameRect, new GUIContent(tag.name), false, false, false, false); } private void DrawConfig() { var headerRect = GUILayoutUtility.GetRect(0, 26f, GUILayout.ExpandWidth(true)); EditorGUI.DrawRect(headerRect, headerBg); EditorGUI.DrawRect(new Rect(headerRect.x, headerRect.y, headerRect.width, 1), separatorColor); configExpanded = EditorGUI.Foldout(new Rect(headerRect.x + 4, headerRect.y, headerRect.width - 8, headerRect.height), configExpanded, "Config", true, sHeader); if (!configExpanded) return; GUILayout.Space(2); var classNameProp = serializedSettings.FindProperty("logV2.className"); EditorGUILayout.PropertyField(classNameProp, new GUIContent("Class Name")); var folderProp = serializedSettings.FindProperty("logV2.codeGenerateFolder"); EditorGUILayout.PropertyField(folderProp, new GUIContent("Output Folder")); var cleanProp = serializedSettings.FindProperty("logV2.forceCleanUp"); EditorGUILayout.PropertyField(cleanProp, new GUIContent("Force Clean")); } private void DrawTemplate() { var headerRect = GUILayoutUtility.GetRect(0, 26f, GUILayout.ExpandWidth(true)); EditorGUI.DrawRect(headerRect, headerBg); EditorGUI.DrawRect(new Rect(headerRect.x, headerRect.y, headerRect.width, 1), separatorColor); templateExpanded = EditorGUI.Foldout(new Rect(headerRect.x + 4, headerRect.y, headerRect.width - 8, headerRect.height), templateExpanded, "Class Template", true, sHeader); if (!templateExpanded) return; GUILayout.Space(2); var templateProp = serializedSettings.FindProperty("logV2.classTemplate"); EditorGUILayout.PropertyField(templateProp, GUIContent.none, false, GUILayout.Height(16 * 14)); } private void DrawFooter() { var sep = GUILayoutUtility.GetRect(0, 1, GUILayout.ExpandWidth(true)); EditorGUI.DrawRect(sep, new Color(0.3f, 0.3f, 0.3f, 1f)); GUILayout.BeginHorizontal(); if (GUILayout.Button("Refresh", GUILayout.Height(26))) needsRefresh = true; var oldBg = GUI.backgroundColor; GUI.backgroundColor = new Color(0.3f, 0.7f, 1f, 1f); if (GUILayout.Button("Generate", GUILayout.Height(26))) { serializedSettings.ApplyModifiedProperties(); settings.logV2.enabled = true; settings.Apply(); needsRefresh = true; } GUI.backgroundColor = oldBg; GUILayout.EndHorizontal(); GUILayout.Space(2); } private void SetAll(bool enabled) { for (int i = 0; i < settings.logV2.tags.Count; i++) settings.logV2.tags[i].enabled = enabled; EditorUtility.SetDirty(settings); } private void AddTagDialog() { var name = EditorInputDialog.Show("Add Logger Tag", "Enter tag name (e.g. AUDIO, UI, NET):", ""); if (string.IsNullOrEmpty(name)) return; name = name.Trim().ToUpperInvariant(); if (!IsValidTagName(name)) { EditorUtility.DisplayDialog("Invalid Tag", "Tag must be 2+ chars: uppercase letters, digits, underscores. No leading digit.", "OK"); return; } for (int i = 0; i < settings.logV2.tags.Count; i++) { if (settings.logV2.tags[i].name == name) { EditorUtility.DisplayDialog("Duplicate", $"Tag '{name}' already exists.", "OK"); return; } } settings.logV2.tags.Add(new Core.AmaDevSettings.SymbolInfo { name = name, enabled = true }); EditorUtility.SetDirty(settings); } private static bool IsValidTagName(string name) { if (name.Length < 2) return false; return Regex.IsMatch(name, @"^[A-Z_][A-Z0-9_]+$"); } } }