com.alicizax.unity.framework/Editor/UI/GenerateWindow/UISettingEditorWindow.cs

821 lines
32 KiB
C#
Raw Normal View History

2026-03-16 18:33:06 +08:00
using System;
2025-09-05 19:46:30 +08:00
using System.Collections.Generic;
2025-11-07 20:47:57 +08:00
using System.IO;
using System.Linq;
using AlicizaX.UI.Runtime;
2025-09-05 19:46:30 +08:00
using Newtonsoft.Json;
using UnityEditor;
2025-11-07 20:47:57 +08:00
using UnityEditorInternal;
2025-09-05 19:46:30 +08:00
using UnityEngine;
namespace AlicizaX.UI.Editor
{
2026-03-16 18:33:06 +08:00
public class UISettingEditorWindow : EditorWindow
2025-09-05 19:46:30 +08:00
{
2026-03-16 18:33:06 +08:00
private const float ToolbarButtonWidth = 36f;
private static List<string> cacheFilterType;
2025-11-07 20:47:57 +08:00
[MenuItem("Tools/AlicizaX/UISetting Window")]
2025-09-05 19:46:30 +08:00
private static void OpenWindow()
{
2026-03-16 18:33:06 +08:00
var window = GetWindow<UISettingEditorWindow>("UI Setting");
window.minSize = new Vector2(760, 520);
window.Show();
2025-09-05 19:46:30 +08:00
}
2026-03-16 18:33:06 +08:00
private readonly string[] toolbarTitles = { "General", "Script Generation", "Element Mapping" };
2025-09-05 19:46:30 +08:00
2025-11-07 20:47:57 +08:00
private UIGenerateConfiguration uiGenerateConfiguration;
2026-03-16 18:33:06 +08:00
private SerializedObject serializedConfig;
private SerializedProperty commonDataProperty;
private SerializedProperty regexConfigsProperty;
private SerializedProperty scriptGenerateConfigsProperty;
private SerializedProperty generatorRuleHelperProperty;
private SerializedProperty excludeKeywordsProperty;
2025-11-07 20:47:57 +08:00
private ReorderableList regexList;
private ReorderableList projectList;
private ReorderableList excludeList;
2026-03-16 18:33:06 +08:00
private Vector2 scroll;
private int toolbarTab;
2025-11-07 20:47:57 +08:00
private TextAsset importText;
private string previewLabel;
private string previewCompLabel;
2026-03-16 18:33:06 +08:00
private List<string> scriptGeneratorHelperTypes = new();
private int scriptGeneratorHelperSelectIndex;
2025-11-07 20:47:57 +08:00
private void OnEnable()
2025-09-05 19:46:30 +08:00
{
2026-03-16 18:33:06 +08:00
BindConfiguration();
SetupLists();
RefreshScriptGeneratorHelperTypes();
RefreshPreview();
}
private void OnDisable()
{
SaveConfig(false);
}
private void BindConfiguration()
{
uiGenerateConfiguration = UIGenerateConfiguration.LoadOrCreate();
2025-11-07 20:47:57 +08:00
if (uiGenerateConfiguration == null)
{
2026-03-16 18:33:06 +08:00
uiGenerateConfiguration = CreateInstance<UIGenerateConfiguration>();
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
uiGenerateConfiguration.UIGenerateCommonData ??= new UIGenerateCommonData();
uiGenerateConfiguration.UIElementRegexConfigs ??= new List<UIEelementRegexData>();
uiGenerateConfiguration.UIScriptGenerateConfigs ??= new List<UIScriptGenerateData>();
uiGenerateConfiguration.UIGenerateCommonData.ExcludeKeywords ??= Array.Empty<string>();
serializedConfig = new SerializedObject(uiGenerateConfiguration);
commonDataProperty = serializedConfig.FindProperty(nameof(UIGenerateConfiguration.UIGenerateCommonData));
regexConfigsProperty = serializedConfig.FindProperty(nameof(UIGenerateConfiguration.UIElementRegexConfigs));
scriptGenerateConfigsProperty = serializedConfig.FindProperty(nameof(UIGenerateConfiguration.UIScriptGenerateConfigs));
generatorRuleHelperProperty = serializedConfig.FindProperty(nameof(UIGenerateConfiguration.UIScriptGeneratorRuleHelper));
excludeKeywordsProperty = commonDataProperty?.FindPropertyRelative(nameof(UIGenerateCommonData.ExcludeKeywords));
2025-09-05 19:46:30 +08:00
}
2025-11-07 20:47:57 +08:00
private void SetupLists()
{
2026-03-16 18:33:06 +08:00
SetupExcludeList();
SetupRegexList();
SetupProjectList();
}
2025-09-05 19:46:30 +08:00
2026-03-16 18:33:06 +08:00
private void SetupExcludeList()
{
excludeList = new ReorderableList(serializedConfig, excludeKeywordsProperty, true, true, true, true);
excludeList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "Exclude Keywords");
2025-11-07 20:47:57 +08:00
excludeList.drawElementCallback = (rect, index, active, focused) =>
{
2026-03-16 18:33:06 +08:00
if (!IsValidArrayIndex(excludeKeywordsProperty, index))
{
return;
}
rect.y += 2f;
var itemProperty = excludeKeywordsProperty.GetArrayElementAtIndex(index);
itemProperty.stringValue = EditorGUI.TextField(
new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight),
itemProperty.stringValue);
2025-11-07 20:47:57 +08:00
};
2026-03-16 18:33:06 +08:00
excludeList.onAddCallback = _ =>
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
int index = excludeKeywordsProperty.arraySize;
excludeKeywordsProperty.InsertArrayElementAtIndex(index);
excludeKeywordsProperty.GetArrayElementAtIndex(index).stringValue = string.Empty;
ApplyConfigChanges();
2025-11-07 20:47:57 +08:00
};
2026-03-16 18:33:06 +08:00
excludeList.onRemoveCallback = list =>
{
ReorderableList.defaultBehaviours.DoRemoveButton(list);
ApplyConfigChanges();
};
}
2025-09-05 19:46:30 +08:00
2026-03-16 18:33:06 +08:00
private void SetupRegexList()
{
regexList = new ReorderableList(serializedConfig, regexConfigsProperty, true, true, true, true);
regexList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "UI Element Mapping (Prefix -> Component)");
regexList.elementHeightCallback = _ => EditorGUIUtility.singleLineHeight + 6f;
2025-11-07 20:47:57 +08:00
regexList.drawElementCallback = (rect, index, active, focused) =>
{
2026-03-16 18:33:06 +08:00
if (!IsValidArrayIndex(regexConfigsProperty, index))
{
return;
}
2025-09-05 19:46:30 +08:00
2026-03-16 18:33:06 +08:00
var itemProperty = regexConfigsProperty.GetArrayElementAtIndex(index);
var regexProperty = itemProperty.FindPropertyRelative(nameof(UIEelementRegexData.uiElementRegex));
var componentTypeProperty = itemProperty.FindPropertyRelative(nameof(UIEelementRegexData.componentType));
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
rect.y += 2f;
float lineHeight = EditorGUIUtility.singleLineHeight;
float leftWidth = rect.width * 0.8f;
Rect textRect = new Rect(rect.x, rect.y, leftWidth - 8f, lineHeight);
Rect buttonRect = new Rect(rect.x + leftWidth + 8f, rect.y, Mathf.Min(180f, rect.width - leftWidth - 8f), lineHeight);
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
regexProperty.stringValue = EditorGUI.TextField(textRect, regexProperty.stringValue);
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
string buttonLabel = string.IsNullOrEmpty(componentTypeProperty.stringValue)
? "(Select Type)"
: componentTypeProperty.stringValue;
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
if (GUI.Button(buttonRect, buttonLabel, EditorStyles.popup))
{
var options = CollectComponentTypeNamesFallback();
Rect anchor = new Rect(
buttonRect.x,
buttonRect.y + buttonRect.height,
Mathf.Min(360f, Mathf.Max(buttonRect.width, 200f)),
buttonRect.height);
SearchablePopup.Show(
anchor,
options,
Mathf.Max(0, options.IndexOf(componentTypeProperty.stringValue)),
selectedIndex => UpdateRegexComponentType(index, options, selectedIndex));
2025-11-07 20:47:57 +08:00
}
};
2026-03-16 18:33:06 +08:00
regexList.onAddCallback = _ =>
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
int index = regexConfigsProperty.arraySize;
regexConfigsProperty.InsertArrayElementAtIndex(index);
var itemProperty = regexConfigsProperty.GetArrayElementAtIndex(index);
itemProperty.FindPropertyRelative(nameof(UIEelementRegexData.uiElementRegex)).stringValue = string.Empty;
itemProperty.FindPropertyRelative(nameof(UIEelementRegexData.componentType)).stringValue = string.Empty;
ApplyConfigChanges();
2025-11-07 20:47:57 +08:00
};
2026-03-16 18:33:06 +08:00
regexList.onRemoveCallback = list =>
{
ReorderableList.defaultBehaviours.DoRemoveButton(list);
ApplyConfigChanges();
};
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
private void SetupProjectList()
{
projectList = new ReorderableList(serializedConfig, scriptGenerateConfigsProperty, true, true, true, true);
projectList.drawHeaderCallback = rect => EditorGUI.LabelField(rect, "UI Script Generation Config");
projectList.elementHeightCallback = _ => EditorGUIUtility.singleLineHeight * 5f + 10f;
2025-11-07 20:47:57 +08:00
projectList.drawElementCallback = (rect, index, active, focused) =>
{
2026-03-16 18:33:06 +08:00
if (!IsValidArrayIndex(scriptGenerateConfigsProperty, index))
{
return;
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
var itemProperty = scriptGenerateConfigsProperty.GetArrayElementAtIndex(index);
var projectNameProperty = itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.ProjectName));
var namespaceProperty = itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.NameSpace));
var generatePathProperty = itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.GenerateHolderCodePath));
var prefabRootProperty = itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.UIPrefabRootPath));
var loadTypeProperty = itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.LoadType));
float lineHeight = EditorGUIUtility.singleLineHeight;
float padding = 2f;
projectNameProperty.stringValue = EditorGUI.TextField(
new Rect(rect.x, rect.y, rect.width, lineHeight),
"Project Name",
projectNameProperty.stringValue);
namespaceProperty.stringValue = EditorGUI.TextField(
new Rect(rect.x, rect.y + (lineHeight + padding), rect.width, lineHeight),
"Namespace",
namespaceProperty.stringValue);
DrawFolderField(
"Holder Code Path",
generatePathProperty,
rect.x,
rect.y + 2f * (lineHeight + padding),
rect.width,
lineHeight);
DrawFolderField(
"Prefab Root Path",
prefabRootProperty,
rect.x,
rect.y + 3f * (lineHeight + padding),
rect.width,
lineHeight);
loadTypeProperty.enumValueIndex = (int)(EUIResLoadType)EditorGUI.EnumPopup(
new Rect(rect.x, rect.y + 4f * (lineHeight + padding), rect.width, lineHeight),
"Load Type",
(EUIResLoadType)loadTypeProperty.enumValueIndex);
};
projectList.onAddCallback = _ =>
{
int index = scriptGenerateConfigsProperty.arraySize;
scriptGenerateConfigsProperty.InsertArrayElementAtIndex(index);
var itemProperty = scriptGenerateConfigsProperty.GetArrayElementAtIndex(index);
itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.ProjectName)).stringValue = "NewProject";
itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.NameSpace)).stringValue = "Game.UI";
itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.GenerateHolderCodePath)).stringValue = "Assets/Scripts/UI/Generated";
itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.UIPrefabRootPath)).stringValue = "Assets/Resources/UI";
itemProperty.FindPropertyRelative(nameof(UIScriptGenerateData.LoadType)).enumValueIndex = (int)EUIResLoadType.Resources;
ApplyConfigChanges();
2025-11-07 20:47:57 +08:00
};
2026-03-16 18:33:06 +08:00
projectList.onRemoveCallback = list =>
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
ReorderableList.defaultBehaviours.DoRemoveButton(list);
ApplyConfigChanges();
2025-11-07 20:47:57 +08:00
};
}
2026-03-16 18:33:06 +08:00
private void OnGUI()
2025-09-05 19:46:30 +08:00
{
2026-03-16 18:33:06 +08:00
if (serializedConfig == null)
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
BindConfiguration();
SetupLists();
RefreshScriptGeneratorHelperTypes();
RefreshPreview();
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
serializedConfig.Update();
2025-09-05 19:46:30 +08:00
2026-03-16 18:33:06 +08:00
GUILayout.Space(6f);
DrawToolbar();
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
scroll = EditorGUILayout.BeginScrollView(scroll);
GUILayout.Space(8f);
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
switch (toolbarTab)
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
case 0:
DrawCommonPane();
break;
case 1:
DrawScriptPane();
break;
case 2:
DrawElementPane();
break;
2025-11-07 20:47:57 +08:00
}
2025-09-05 19:46:30 +08:00
2026-03-16 18:33:06 +08:00
EditorGUILayout.EndScrollView();
GUILayout.Space(8f);
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
if (serializedConfig.ApplyModifiedProperties())
{
EditorUtility.SetDirty(uiGenerateConfiguration);
RefreshPreview();
}
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
private void DrawToolbar()
{
2025-11-07 20:47:57 +08:00
GUILayout.BeginHorizontal(EditorStyles.toolbar);
for (int i = 0; i < toolbarTitles.Length; i++)
{
2026-03-16 18:33:06 +08:00
bool isActive = toolbarTab == i;
bool toggled = GUILayout.Toggle(isActive, toolbarTitles[i], EditorStyles.toolbarButton, GUILayout.Height(22f));
if (toggled && toolbarTab != i)
2025-11-07 20:47:57 +08:00
{
toolbarTab = i;
Repaint();
}
}
GUILayout.FlexibleSpace();
var saveIcon = EditorGUIUtility.IconContent("SaveActive");
var refreshIcon = EditorGUIUtility.IconContent("Refresh");
var reloadIcon = EditorGUIUtility.IconContent("RotateTool");
2026-03-16 18:33:06 +08:00
if (GUILayout.Button(new GUIContent(saveIcon.image, "Save configuration"), EditorStyles.toolbarButton, GUILayout.Width(ToolbarButtonWidth)))
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
SaveConfig(true);
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
if (GUILayout.Button(new GUIContent(refreshIcon.image, "Refresh preview"), EditorStyles.toolbarButton, GUILayout.Width(ToolbarButtonWidth)))
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
RefreshPreview();
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
if (GUILayout.Button(new GUIContent(reloadIcon.image, "Reload configuration"), EditorStyles.toolbarButton, GUILayout.Width(ToolbarButtonWidth)))
{
ReloadConfiguration();
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
GUILayout.EndHorizontal();
2025-11-07 20:47:57 +08:00
}
private void DrawCommonPane()
{
EditorGUILayout.BeginVertical("box");
2026-03-16 18:33:06 +08:00
EditorGUILayout.LabelField("General Generation Settings", EditorStyles.boldLabel);
EditorGUILayout.Space(4f);
EditorGUILayout.PropertyField(
commonDataProperty.FindPropertyRelative(nameof(UIGenerateCommonData.ComCheckSplitName)),
new GUIContent("Component Split", "Example: Button#Close"));
EditorGUILayout.PropertyField(
commonDataProperty.FindPropertyRelative(nameof(UIGenerateCommonData.ComCheckEndName)),
new GUIContent("Component End", "Example: @End"));
EditorGUILayout.PropertyField(
commonDataProperty.FindPropertyRelative(nameof(UIGenerateCommonData.ArrayComSplitName)),
new GUIContent("Array Split", "Example: *Item"));
EditorGUILayout.PropertyField(
commonDataProperty.FindPropertyRelative(nameof(UIGenerateCommonData.GeneratePrefix)),
new GUIContent("Generate Prefix"));
int nextIndex = EditorGUILayout.Popup(
"Generator Rule Helper",
scriptGeneratorHelperSelectIndex,
scriptGeneratorHelperTypes.ToArray());
if (nextIndex != scriptGeneratorHelperSelectIndex &&
nextIndex >= 0 &&
nextIndex < scriptGeneratorHelperTypes.Count)
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
scriptGeneratorHelperSelectIndex = nextIndex;
generatorRuleHelperProperty.stringValue = scriptGeneratorHelperTypes[nextIndex];
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
GUILayout.Space(8f);
2025-11-07 20:47:57 +08:00
excludeList.DoLayoutList();
2026-03-16 18:33:06 +08:00
EditorGUILayout.Space(8f);
EditorGUILayout.LabelField("Script Preview", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(previewLabel ?? string.Empty, MessageType.None);
EditorGUILayout.LabelField("Component Preview", EditorStyles.boldLabel);
EditorGUILayout.HelpBox(previewCompLabel ?? string.Empty, MessageType.None);
2025-11-07 20:47:57 +08:00
EditorGUILayout.EndVertical();
2026-03-16 18:33:06 +08:00
GUILayout.Space(8f);
2025-11-07 20:47:57 +08:00
}
private void DrawScriptPane()
{
EditorGUILayout.BeginVertical("box");
2026-03-16 18:33:06 +08:00
EditorGUILayout.LabelField("UI Script Generation Config", EditorStyles.boldLabel);
GUILayout.Space(6f);
2025-11-07 20:47:57 +08:00
projectList.DoLayoutList();
EditorGUILayout.EndVertical();
2026-03-16 18:33:06 +08:00
GUILayout.Space(8f);
2025-11-07 20:47:57 +08:00
}
private void DrawElementPane()
{
EditorGUILayout.BeginVertical("box");
2026-03-16 18:33:06 +08:00
EditorGUILayout.LabelField("UI Element Mapping", EditorStyles.boldLabel);
GUILayout.Space(6f);
2025-11-07 20:47:57 +08:00
GUILayout.BeginHorizontal(EditorStyles.toolbar);
2026-03-16 18:33:06 +08:00
if (GUILayout.Button("Load Default", EditorStyles.toolbarButton, GUILayout.Width(90f)))
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
LoadDefault();
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
if (GUILayout.Button("Export", EditorStyles.toolbarButton, GUILayout.Width(70f)))
{
ExportConfig();
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
GUILayout.Space(8f);
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
importText = (TextAsset)EditorGUILayout.ObjectField(
importText,
typeof(TextAsset),
false,
GUILayout.Height(18f),
GUILayout.MinWidth(200f));
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
GUI.enabled = importText != null;
if (GUILayout.Button("Import", EditorStyles.toolbarButton, GUILayout.Width(84f)) && importText != null)
{
ImportConfig(importText);
importText = null;
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
GUI.enabled = true;
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
GUILayout.Space(6f);
regexList.DoLayoutList();
EditorGUILayout.EndVertical();
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
private void DrawFolderField(string label, SerializedProperty property, float x, float y, float width, float height)
{
if (property == null)
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
return;
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
Rect textRect = new Rect(x, y, width - 76f, height);
Rect buttonRect = new Rect(x + width - 72f, y, 68f, height);
property.stringValue = EditorGUI.TextField(textRect, label, property.stringValue);
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
if (!GUI.Button(buttonRect, "Select"))
{
return;
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
string selectedPath = EditorUtility.OpenFolderPanel("Select Folder", Application.dataPath, string.Empty);
if (string.IsNullOrEmpty(selectedPath))
{
return;
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
if (!selectedPath.StartsWith(Application.dataPath, StringComparison.OrdinalIgnoreCase))
{
EditorUtility.DisplayDialog("Invalid Folder", "Please select a folder under Assets.", "OK");
return;
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
Undo.RecordObject(uiGenerateConfiguration, "Change UI Generation Folder");
property.stringValue = "Assets" + selectedPath.Substring(Application.dataPath.Length);
ApplyConfigChanges();
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
private void RefreshScriptGeneratorHelperTypes()
{
scriptGeneratorHelperTypes = AlicizaX.Utility.Assembly
.GetRuntimeTypeNames(typeof(IUIGeneratorRuleHelper))
.Distinct(StringComparer.Ordinal)
.OrderBy(typeName => typeName, StringComparer.Ordinal)
.ToList();
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
if (scriptGeneratorHelperTypes.Count == 0)
{
scriptGeneratorHelperTypes.Add(typeof(DefaultUIGeneratorRuleHelper).FullName);
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
string currentType = generatorRuleHelperProperty?.stringValue;
if (!string.IsNullOrEmpty(currentType) && !scriptGeneratorHelperTypes.Contains(currentType))
{
scriptGeneratorHelperTypes.Insert(0, currentType);
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
scriptGeneratorHelperSelectIndex = Mathf.Max(0, scriptGeneratorHelperTypes.IndexOf(currentType));
2025-11-07 20:47:57 +08:00
}
private List<string> CollectComponentTypeNamesFallback()
{
if (cacheFilterType == null)
{
cacheFilterType = AlicizaX.Utility.Assembly.GetTypes()
2026-03-16 18:33:06 +08:00
.Where(type => !string.IsNullOrEmpty(type.FullName) && !type.FullName.Contains("Editor"))
.Where(type => !type.IsAbstract && !type.IsInterface)
.Where(type => !type.IsGenericTypeDefinition)
.Where(type => !type.IsSubclassOf(typeof(UIHolderObjectBase)))
.Where(type => type.IsSubclassOf(typeof(Component)))
.Where(type => !type.FullName.Contains("YooAsset"))
.Where(type => !type.FullName.Contains("Unity.VisualScripting"))
.Where(type => !type.FullName.Contains("Cysharp.Threading"))
.Where(type => !type.FullName.Contains("UnityEngine.Rendering.UI.Debug"))
.Where(type => !type.FullName.Contains("Unity.PerformanceTesting"))
.Where(type => !type.FullName.Contains("UnityEngine.TestTools"))
.Select(type => type.FullName)
.Distinct(StringComparer.Ordinal)
.OrderBy(typeName => typeName, StringComparer.Ordinal)
.ToList();
2025-11-07 20:47:57 +08:00
cacheFilterType.Add(typeof(GameObject).Name);
}
return cacheFilterType;
}
2026-03-16 18:33:06 +08:00
private void UpdateRegexComponentType(int index, List<string> options, int selectedIndex)
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
if (!IsValidArrayIndex(regexConfigsProperty, index) ||
selectedIndex < 0 ||
selectedIndex >= options.Count)
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
return;
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
Undo.RecordObject(uiGenerateConfiguration, "Change UI Element Component Type");
serializedConfig.Update();
regexConfigsProperty
.GetArrayElementAtIndex(index)
.FindPropertyRelative(nameof(UIEelementRegexData.componentType))
.stringValue = options[selectedIndex];
ApplyConfigChanges();
Repaint();
}
private void LoadDefault()
{
string defaultPath = UIGlobalPath.DefaultComPath;
2025-11-07 20:47:57 +08:00
if (!File.Exists(defaultPath))
{
2026-03-16 18:33:06 +08:00
EditorUtility.DisplayDialog("Load Default", $"Default file not found: {defaultPath}", "OK");
2025-11-07 20:47:57 +08:00
return;
}
2026-03-16 18:33:06 +08:00
string text = File.ReadAllText(defaultPath);
var list = JsonConvert.DeserializeObject<List<UIEelementRegexData>>(text);
ReplaceRegexConfigs(list, "Load Default UI Element Config");
2025-09-05 19:46:30 +08:00
}
private void ImportConfig(TextAsset text)
{
2025-11-07 20:47:57 +08:00
try
{
var list = JsonConvert.DeserializeObject<List<UIEelementRegexData>>(text.text);
2026-03-16 18:33:06 +08:00
ReplaceRegexConfigs(list, "Import UI Element Config");
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
catch (Exception exception)
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
Debug.LogException(exception);
EditorUtility.DisplayDialog("Import Failed", "Import failed. Check the console for details.", "OK");
2025-11-07 20:47:57 +08:00
}
2025-09-05 19:46:30 +08:00
}
2026-03-16 18:33:06 +08:00
private void ReplaceRegexConfigs(IReadOnlyList<UIEelementRegexData> list, string undoName)
{
if (list == null)
{
return;
}
Undo.RecordObject(uiGenerateConfiguration, undoName);
serializedConfig.Update();
regexConfigsProperty.arraySize = list.Count;
for (int i = 0; i < list.Count; i++)
{
var itemProperty = regexConfigsProperty.GetArrayElementAtIndex(i);
itemProperty.FindPropertyRelative(nameof(UIEelementRegexData.uiElementRegex)).stringValue = list[i]?.uiElementRegex ?? string.Empty;
itemProperty.FindPropertyRelative(nameof(UIEelementRegexData.componentType)).stringValue = list[i]?.componentType ?? string.Empty;
}
ApplyConfigChanges();
}
2025-11-07 20:47:57 +08:00
private void ExportConfig()
{
2026-03-16 18:33:06 +08:00
serializedConfig.ApplyModifiedProperties();
string json = JsonConvert.SerializeObject(uiGenerateConfiguration.UIElementRegexConfigs, Formatting.Indented);
string path = EditorUtility.SaveFilePanel("Export UI Element Config", Application.dataPath, "uielementconfig", "txt");
if (string.IsNullOrEmpty(path))
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
return;
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
File.WriteAllText(path, json);
AssetDatabase.Refresh();
EditorUtility.DisplayDialog("Export Complete", "Config exported.", "OK");
}
private void ReloadConfiguration()
{
BindConfiguration();
SetupLists();
RefreshScriptGeneratorHelperTypes();
RefreshPreview();
Repaint();
2025-11-07 20:47:57 +08:00
}
2025-09-05 19:46:30 +08:00
2026-03-16 18:33:06 +08:00
private void RefreshPreview()
2025-09-05 19:46:30 +08:00
{
2026-03-16 18:33:06 +08:00
string prefix = commonDataProperty?.FindPropertyRelative(nameof(UIGenerateCommonData.GeneratePrefix))?.stringValue ?? "ui";
string arraySplit = commonDataProperty?.FindPropertyRelative(nameof(UIGenerateCommonData.ArrayComSplitName))?.stringValue ?? "*";
string componentSplit = commonDataProperty?.FindPropertyRelative(nameof(UIGenerateCommonData.ComCheckSplitName))?.stringValue ?? "#";
string componentEnd = commonDataProperty?.FindPropertyRelative(nameof(UIGenerateCommonData.ComCheckEndName))?.stringValue ?? "@";
previewLabel = $"{prefix}_UITestWindow";
previewCompLabel = $"{arraySplit}Text{componentSplit}Img{componentEnd}Test{arraySplit}0";
2025-11-07 20:47:57 +08:00
Repaint();
2025-09-05 19:46:30 +08:00
}
2026-03-16 18:33:06 +08:00
private void ApplyConfigChanges()
{
serializedConfig.ApplyModifiedProperties();
EditorUtility.SetDirty(uiGenerateConfiguration);
RefreshPreview();
}
private void SaveConfig(bool logSave)
2025-09-05 19:46:30 +08:00
{
2026-03-16 18:33:06 +08:00
if (serializedConfig == null || uiGenerateConfiguration == null)
{
return;
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
serializedConfig.ApplyModifiedProperties();
EditorUtility.SetDirty(uiGenerateConfiguration);
2025-09-05 19:46:30 +08:00
UIGenerateConfiguration.Save();
2026-03-16 18:33:06 +08:00
if (logSave)
{
Debug.Log("UIGenerateConfiguration saved.");
}
2025-09-05 19:46:30 +08:00
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
private static bool IsValidArrayIndex(SerializedProperty property, int index)
{
return property != null && index >= 0 && index < property.arraySize;
}
2025-11-07 20:47:57 +08:00
}
internal class SearchablePopup : PopupWindowContent
{
2026-03-16 18:33:06 +08:00
private const float RowHeight = 20f;
2025-11-07 20:47:57 +08:00
private static GUIStyle searchFieldStyle;
private static GUIStyle cancelStyle;
private static GUIStyle rowStyle;
private static GUIStyle selectedRowStyle;
2026-03-16 18:33:06 +08:00
private readonly List<string> allItems;
private readonly Action<int> onSelect;
private List<string> filtered;
private int currentIndex;
private string search = string.Empty;
private Vector2 scroll;
2025-11-07 20:47:57 +08:00
private SearchablePopup(List<string> items, int currentIndex, Action<int> onSelect)
{
2026-03-16 18:33:06 +08:00
allItems = items ?? new List<string>();
filtered = new List<string>(allItems);
this.currentIndex = Mathf.Clamp(currentIndex, -1, allItems.Count - 1);
2025-11-07 20:47:57 +08:00
this.onSelect = onSelect;
}
public static void Show(Rect anchorRect, List<string> items, int currentIndex, Action<int> onSelect)
{
PopupWindow.Show(anchorRect, new SearchablePopup(items, currentIndex, onSelect));
}
2026-03-16 18:33:06 +08:00
public override Vector2 GetWindowSize()
{
return new Vector2(360f, 320f);
}
2025-11-07 20:47:57 +08:00
public override void OnOpen()
{
EditorApplication.delayCall += () => EditorGUI.FocusTextInControl("SearchField");
}
public override void OnGUI(Rect rect)
{
InitStyles();
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUI.SetNextControlName("SearchField");
search = EditorGUILayout.TextField(search, searchFieldStyle, GUILayout.ExpandWidth(true));
2026-03-16 18:33:06 +08:00
if (GUILayout.Button(string.Empty, cancelStyle, GUILayout.Width(18f)))
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
search = string.Empty;
2025-11-07 20:47:57 +08:00
GUI.FocusControl("SearchField");
}
EditorGUILayout.EndHorizontal();
FilterList(search);
HandleKeyboard();
scroll = EditorGUILayout.BeginScrollView(scroll);
for (int i = 0; i < filtered.Count; i++)
{
2026-03-16 18:33:06 +08:00
bool selected = i == currentIndex;
2025-11-07 20:47:57 +08:00
var style = selected ? selectedRowStyle : rowStyle;
2026-03-16 18:33:06 +08:00
Rect rowRect = GUILayoutUtility.GetRect(
new GUIContent(filtered[i]),
style,
GUILayout.Height(RowHeight),
GUILayout.ExpandWidth(true));
2025-11-07 20:47:57 +08:00
if (Event.current.type == EventType.Repaint)
2026-03-16 18:33:06 +08:00
{
style.Draw(rowRect, filtered[i], false, false, selected, false);
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
if (Event.current.type == EventType.MouseDown && rowRect.Contains(Event.current.mousePosition))
2025-11-07 20:47:57 +08:00
{
Select(filtered[i]);
Event.current.Use();
}
}
EditorGUILayout.EndScrollView();
}
private void HandleKeyboard()
{
2026-03-16 18:33:06 +08:00
Event currentEvent = Event.current;
if (currentEvent.type != EventType.KeyDown)
{
return;
}
2025-11-07 20:47:57 +08:00
2026-03-16 18:33:06 +08:00
if (currentEvent.keyCode == KeyCode.DownArrow)
2025-11-07 20:47:57 +08:00
{
currentIndex = Mathf.Min(currentIndex + 1, filtered.Count - 1);
2026-03-16 18:33:06 +08:00
currentEvent.Use();
2025-11-07 20:47:57 +08:00
editorWindow.Repaint();
2026-03-16 18:33:06 +08:00
return;
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
if (currentEvent.keyCode == KeyCode.UpArrow)
2025-11-07 20:47:57 +08:00
{
currentIndex = Mathf.Max(currentIndex - 1, 0);
2026-03-16 18:33:06 +08:00
currentEvent.Use();
2025-11-07 20:47:57 +08:00
editorWindow.Repaint();
2026-03-16 18:33:06 +08:00
return;
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
if ((currentEvent.keyCode == KeyCode.Return || currentEvent.keyCode == KeyCode.KeypadEnter) &&
filtered.Count > 0 &&
currentIndex >= 0 &&
currentIndex < filtered.Count)
2025-11-07 20:47:57 +08:00
{
2026-03-16 18:33:06 +08:00
Select(filtered[currentIndex]);
currentEvent.Use();
2025-11-07 20:47:57 +08:00
}
}
private void FilterList(string keyword)
{
if (string.IsNullOrEmpty(keyword))
2026-03-16 18:33:06 +08:00
{
2025-11-07 20:47:57 +08:00
filtered = new List<string>(allItems);
2026-03-16 18:33:06 +08:00
}
2025-11-07 20:47:57 +08:00
else
{
2026-03-16 18:33:06 +08:00
string lowerKeyword = keyword.ToLowerInvariant();
filtered = allItems
.Where(item => item != null && item.ToLowerInvariant().Contains(lowerKeyword))
.ToList();
2025-11-07 20:47:57 +08:00
}
2026-03-16 18:33:06 +08:00
currentIndex = filtered.Count == 0 ? -1 : Mathf.Clamp(currentIndex, 0, filtered.Count - 1);
2025-11-07 20:47:57 +08:00
}
private void Select(string item)
{
int originalIndex = allItems.IndexOf(item);
if (originalIndex >= 0)
2026-03-16 18:33:06 +08:00
{
2025-11-07 20:47:57 +08:00
onSelect?.Invoke(originalIndex);
2026-03-16 18:33:06 +08:00
}
2025-11-07 20:47:57 +08:00
editorWindow.Close();
GUIUtility.ExitGUI();
}
private void InitStyles()
{
if (searchFieldStyle == null)
2026-03-16 18:33:06 +08:00
{
2025-11-07 20:47:57 +08:00
searchFieldStyle = GUI.skin.FindStyle("ToolbarSeachTextField") ?? EditorStyles.toolbarSearchField;
2026-03-16 18:33:06 +08:00
}
2025-11-07 20:47:57 +08:00
if (cancelStyle == null)
2026-03-16 18:33:06 +08:00
{
2025-11-07 20:47:57 +08:00
cancelStyle = GUI.skin.FindStyle("ToolbarSeachCancelButton") ?? EditorStyles.toolbarButton;
2026-03-16 18:33:06 +08:00
}
2025-11-07 20:47:57 +08:00
if (rowStyle == null)
{
2026-03-16 18:33:06 +08:00
rowStyle = new GUIStyle("PR Label")
{
alignment = TextAnchor.MiddleLeft,
padding = new RectOffset(6, 6, 2, 2),
};
2025-11-07 20:47:57 +08:00
}
if (selectedRowStyle == null)
{
2026-03-16 18:33:06 +08:00
selectedRowStyle = new GUIStyle(rowStyle);
selectedRowStyle.normal.background = Texture2D.grayTexture;
2025-11-07 20:47:57 +08:00
}
}
2025-09-05 19:46:30 +08:00
}
}