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;
|
2026-03-23 14:45:55 +08:00
|
|
|
|
private SerializedProperty identifierFormatterTypeProperty;
|
|
|
|
|
|
private SerializedProperty resourcePathResolverTypeProperty;
|
|
|
|
|
|
private SerializedProperty scriptCodeEmitterTypeProperty;
|
|
|
|
|
|
private SerializedProperty scriptFileWriterTypeProperty;
|
2026-03-16 18:33:06 +08:00
|
|
|
|
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-23 14:45:55 +08:00
|
|
|
|
private List<string> identifierFormatterTypes = new();
|
|
|
|
|
|
private List<string> resourcePathResolverTypes = new();
|
|
|
|
|
|
private List<string> scriptCodeEmitterTypes = new();
|
|
|
|
|
|
private List<string> scriptFileWriterTypes = new();
|
|
|
|
|
|
private int identifierFormatterSelectIndex;
|
|
|
|
|
|
private int resourcePathResolverSelectIndex;
|
|
|
|
|
|
private int scriptCodeEmitterSelectIndex;
|
|
|
|
|
|
private int scriptFileWriterSelectIndex;
|
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();
|
2026-03-23 14:45:55 +08:00
|
|
|
|
RefreshGeneratorServiceTypes();
|
2026-03-16 18:33:06 +08:00
|
|
|
|
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));
|
2026-03-23 14:45:55 +08:00
|
|
|
|
identifierFormatterTypeProperty = serializedConfig.FindProperty(nameof(UIGenerateConfiguration.UIIdentifierFormatterTypeName));
|
|
|
|
|
|
resourcePathResolverTypeProperty = serializedConfig.FindProperty(nameof(UIGenerateConfiguration.UIResourcePathResolverTypeName));
|
|
|
|
|
|
scriptCodeEmitterTypeProperty = serializedConfig.FindProperty(nameof(UIGenerateConfiguration.UIScriptCodeEmitterTypeName));
|
|
|
|
|
|
scriptFileWriterTypeProperty = serializedConfig.FindProperty(nameof(UIGenerateConfiguration.UIScriptFileWriterTypeName));
|
2026-03-16 18:33:06 +08:00
|
|
|
|
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();
|
2026-03-23 14:45:55 +08:00
|
|
|
|
RefreshGeneratorServiceTypes();
|
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
|
|
|
|
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.PropertyField(
|
|
|
|
|
|
commonDataProperty.FindPropertyRelative(nameof(UIGenerateCommonData.ComCheckSplitName)),
|
2026-03-23 14:45:55 +08:00
|
|
|
|
new GUIContent("组件分割符", "例如: Button#Close"));
|
2026-03-16 18:33:06 +08:00
|
|
|
|
EditorGUILayout.PropertyField(
|
|
|
|
|
|
commonDataProperty.FindPropertyRelative(nameof(UIGenerateCommonData.ComCheckEndName)),
|
2026-03-23 14:45:55 +08:00
|
|
|
|
new GUIContent("组件结尾符", "例如: @End"));
|
2026-03-16 18:33:06 +08:00
|
|
|
|
EditorGUILayout.PropertyField(
|
|
|
|
|
|
commonDataProperty.FindPropertyRelative(nameof(UIGenerateCommonData.ArrayComSplitName)),
|
2026-03-23 14:45:55 +08:00
|
|
|
|
new GUIContent("数组分割", "例如: *Item"));
|
2026-03-16 18:33:06 +08:00
|
|
|
|
EditorGUILayout.PropertyField(
|
|
|
|
|
|
commonDataProperty.FindPropertyRelative(nameof(UIGenerateCommonData.GeneratePrefix)),
|
2026-03-23 14:45:55 +08:00
|
|
|
|
new GUIContent("生成脚本前缀"));
|
|
|
|
|
|
|
|
|
|
|
|
DrawTypePopup(
|
|
|
|
|
|
"Identifier Formatter",
|
|
|
|
|
|
identifierFormatterTypeProperty,
|
|
|
|
|
|
identifierFormatterTypes,
|
|
|
|
|
|
ref identifierFormatterSelectIndex,
|
|
|
|
|
|
typeof(DefaultUIIdentifierFormatter).FullName);
|
|
|
|
|
|
DrawTypePopup(
|
|
|
|
|
|
"Resource Path Resolver",
|
|
|
|
|
|
resourcePathResolverTypeProperty,
|
|
|
|
|
|
resourcePathResolverTypes,
|
|
|
|
|
|
ref resourcePathResolverSelectIndex,
|
|
|
|
|
|
typeof(DefaultUIResourcePathResolver).FullName);
|
|
|
|
|
|
DrawTypePopup(
|
|
|
|
|
|
"Script Code Emitter",
|
|
|
|
|
|
scriptCodeEmitterTypeProperty,
|
|
|
|
|
|
scriptCodeEmitterTypes,
|
|
|
|
|
|
ref scriptCodeEmitterSelectIndex,
|
|
|
|
|
|
typeof(DefaultUIScriptCodeEmitter).FullName);
|
|
|
|
|
|
DrawTypePopup(
|
|
|
|
|
|
"Script File Writer",
|
|
|
|
|
|
scriptFileWriterTypeProperty,
|
|
|
|
|
|
scriptFileWriterTypes,
|
|
|
|
|
|
ref scriptFileWriterSelectIndex,
|
|
|
|
|
|
typeof(DefaultUIScriptFileWriter).FullName);
|
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-23 14:45:55 +08:00
|
|
|
|
private void RefreshGeneratorServiceTypes()
|
|
|
|
|
|
{
|
|
|
|
|
|
RefreshTypeOptions(
|
|
|
|
|
|
typeof(IUIIdentifierFormatter),
|
|
|
|
|
|
identifierFormatterTypeProperty,
|
|
|
|
|
|
typeof(DefaultUIIdentifierFormatter).FullName,
|
|
|
|
|
|
ref identifierFormatterTypes,
|
|
|
|
|
|
ref identifierFormatterSelectIndex);
|
|
|
|
|
|
RefreshTypeOptions(
|
|
|
|
|
|
typeof(IUIResourcePathResolver),
|
|
|
|
|
|
resourcePathResolverTypeProperty,
|
|
|
|
|
|
typeof(DefaultUIResourcePathResolver).FullName,
|
|
|
|
|
|
ref resourcePathResolverTypes,
|
|
|
|
|
|
ref resourcePathResolverSelectIndex);
|
|
|
|
|
|
RefreshTypeOptions(
|
|
|
|
|
|
typeof(IUIScriptCodeEmitter),
|
|
|
|
|
|
scriptCodeEmitterTypeProperty,
|
|
|
|
|
|
typeof(DefaultUIScriptCodeEmitter).FullName,
|
|
|
|
|
|
ref scriptCodeEmitterTypes,
|
|
|
|
|
|
ref scriptCodeEmitterSelectIndex);
|
|
|
|
|
|
RefreshTypeOptions(
|
|
|
|
|
|
typeof(IUIScriptFileWriter),
|
|
|
|
|
|
scriptFileWriterTypeProperty,
|
|
|
|
|
|
typeof(DefaultUIScriptFileWriter).FullName,
|
|
|
|
|
|
ref scriptFileWriterTypes,
|
|
|
|
|
|
ref scriptFileWriterSelectIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void RefreshTypeOptions(
|
|
|
|
|
|
Type interfaceType,
|
|
|
|
|
|
SerializedProperty property,
|
|
|
|
|
|
string defaultTypeName,
|
|
|
|
|
|
ref List<string> options,
|
|
|
|
|
|
ref int selectedIndex)
|
2026-03-16 18:33:06 +08:00
|
|
|
|
{
|
2026-03-23 14:45:55 +08:00
|
|
|
|
options = AlicizaX.Utility.Assembly
|
|
|
|
|
|
.GetRuntimeTypeNames(interfaceType)
|
2026-03-16 18:33:06 +08:00
|
|
|
|
.Distinct(StringComparer.Ordinal)
|
|
|
|
|
|
.OrderBy(typeName => typeName, StringComparer.Ordinal)
|
|
|
|
|
|
.ToList();
|
2025-11-07 20:47:57 +08:00
|
|
|
|
|
2026-03-23 14:45:55 +08:00
|
|
|
|
if (!options.Contains(defaultTypeName))
|
|
|
|
|
|
{
|
|
|
|
|
|
options.Insert(0, defaultTypeName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string currentType = string.IsNullOrWhiteSpace(property?.stringValue) ? defaultTypeName : property.stringValue;
|
|
|
|
|
|
if (!string.IsNullOrEmpty(currentType) && !options.Contains(currentType))
|
|
|
|
|
|
{
|
|
|
|
|
|
options.Insert(0, currentType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
selectedIndex = Mathf.Max(0, options.IndexOf(currentType));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static void DrawTypePopup(
|
|
|
|
|
|
string label,
|
|
|
|
|
|
SerializedProperty property,
|
|
|
|
|
|
List<string> options,
|
|
|
|
|
|
ref int selectedIndex,
|
|
|
|
|
|
string defaultTypeName)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (property == null || options == null || options.Count == 0)
|
2026-03-16 18:33:06 +08:00
|
|
|
|
{
|
2026-03-23 14:45:55 +08:00
|
|
|
|
return;
|
2026-03-16 18:33:06 +08:00
|
|
|
|
}
|
2025-11-07 20:47:57 +08:00
|
|
|
|
|
2026-03-23 14:45:55 +08:00
|
|
|
|
int currentIndex = selectedIndex;
|
|
|
|
|
|
if (currentIndex < 0 || currentIndex >= options.Count)
|
2026-03-16 18:33:06 +08:00
|
|
|
|
{
|
2026-03-23 14:45:55 +08:00
|
|
|
|
string currentType = string.IsNullOrWhiteSpace(property.stringValue) ? defaultTypeName : property.stringValue;
|
|
|
|
|
|
currentIndex = Mathf.Max(0, options.IndexOf(currentType));
|
|
|
|
|
|
selectedIndex = currentIndex;
|
2026-03-16 18:33:06 +08:00
|
|
|
|
}
|
2025-11-07 20:47:57 +08:00
|
|
|
|
|
2026-03-23 14:45:55 +08:00
|
|
|
|
int nextIndex = EditorGUILayout.Popup(label, currentIndex, options.ToArray());
|
|
|
|
|
|
if (nextIndex >= 0 && nextIndex < options.Count && nextIndex != currentIndex)
|
|
|
|
|
|
{
|
|
|
|
|
|
selectedIndex = nextIndex;
|
|
|
|
|
|
property.stringValue = options[nextIndex];
|
|
|
|
|
|
}
|
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();
|
2026-03-23 14:45:55 +08:00
|
|
|
|
RefreshGeneratorServiceTypes();
|
2026-03-16 18:33:06 +08:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|