修改
This commit is contained in:
parent
3a72214da2
commit
f3251980f5
@ -2,22 +2,72 @@
|
|||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace AlicizaX.UI.Editor {
|
namespace AlicizaX.UI.Editor
|
||||||
public static class UIGenerateEditorTool {
|
{
|
||||||
[MenuItem("GameObject/UI生成绑定/热更工程UI代码", priority = 10)]
|
public class UIGenerateEditorWindow : EditorWindow
|
||||||
public static void GenerateHotfixUIScript() {
|
{
|
||||||
UIScriptGeneratorHelper.GenerateAndAttachScript(
|
private GameObject selectedObject;
|
||||||
Selection.gameObjects.FirstOrDefault(),
|
private Vector2 windowPosition;
|
||||||
UIGenerateConfiguration.Instance.UIScriptGenerateConfig.HotFixProjectUIScriptGenerateData
|
private float windowWidth;
|
||||||
);
|
private float windowHeight;
|
||||||
|
private string[] menuItems;
|
||||||
|
|
||||||
|
[MenuItem("GameObject/UI生成绑定", priority = 10)]
|
||||||
|
public static void ShowWindow()
|
||||||
|
{
|
||||||
|
GameObject selectedObject = Selection.gameObjects.FirstOrDefault();
|
||||||
|
if (selectedObject == null) return;
|
||||||
|
|
||||||
|
var uiScriptConfigs = UIGenerateConfiguration.Instance.UIScriptGenerateConfigs;
|
||||||
|
if (uiScriptConfigs == null || uiScriptConfigs.Count == 0) return;
|
||||||
|
|
||||||
|
var window = GetWindow<UIGenerateEditorWindow>("", true);
|
||||||
|
window.selectedObject = selectedObject;
|
||||||
|
|
||||||
|
window.menuItems = uiScriptConfigs.Select(config => $"{config.ProjectName}").ToArray();
|
||||||
|
|
||||||
|
var windowWidth = 300;
|
||||||
|
var windowHeight = (window.menuItems.Length * 35f);
|
||||||
|
|
||||||
|
Vector3 objectWorldPosition = selectedObject.transform.position;
|
||||||
|
Vector3 screenPosition = HandleUtility.WorldToGUIPoint(objectWorldPosition);
|
||||||
|
var windowPosition = new Vector2(screenPosition.x, screenPosition.y - windowHeight - 5f);
|
||||||
|
|
||||||
|
window.minSize = new Vector2(windowWidth, windowHeight);
|
||||||
|
window.maxSize = new Vector2(windowWidth, windowHeight);
|
||||||
|
|
||||||
|
window.position = new Rect(windowPosition, new Vector2(windowWidth, windowHeight));
|
||||||
|
window.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
[MenuItem("GameObject/UI生成绑定/主工程UI代码", priority = 21)]
|
private void OnGUI()
|
||||||
public static void GenerateMainUIScript() {
|
{
|
||||||
UIScriptGeneratorHelper.GenerateAndAttachScript(
|
GUILayout.Space(5);
|
||||||
Selection.gameObjects.FirstOrDefault(),
|
foreach (var item in menuItems)
|
||||||
UIGenerateConfiguration.Instance.UIScriptGenerateConfig.MainProjectUIScriptGenerateData
|
{
|
||||||
);
|
if (GUILayout.Button(item, EditorStyles.toolbarButton))
|
||||||
|
{
|
||||||
|
GenerateScriptForConfig(selectedObject, item);
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.Space(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateScriptForConfig(GameObject selectedObject, string itemName)
|
||||||
|
{
|
||||||
|
var uiScriptConfigs = UIGenerateConfiguration.Instance.UIScriptGenerateConfigs;
|
||||||
|
var config = uiScriptConfigs.FirstOrDefault(c => $"{c.ProjectName}" == itemName);
|
||||||
|
|
||||||
|
if (config != null)
|
||||||
|
{
|
||||||
|
UIScriptGeneratorHelper.GenerateAndAttachScript(selectedObject, config);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Configuration not found for item: " + itemName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,15 +12,15 @@ using UnityEditor;
|
|||||||
using UnityEditor.Callbacks;
|
using UnityEditor.Callbacks;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
public enum EBindType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Widget,
|
||||||
|
ListCom,
|
||||||
|
}
|
||||||
|
|
||||||
namespace AlicizaX.UI.Editor
|
namespace AlicizaX.UI.Editor
|
||||||
{
|
{
|
||||||
enum EBindType
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Widget,
|
|
||||||
ListCom,
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
class UIBindData
|
class UIBindData
|
||||||
{
|
{
|
||||||
@ -47,8 +47,32 @@ namespace AlicizaX.UI.Editor
|
|||||||
internal static class UIScriptGeneratorHelper
|
internal static class UIScriptGeneratorHelper
|
||||||
{
|
{
|
||||||
private static UIGenerateConfiguration _uiGenerateConfiguration;
|
private static UIGenerateConfiguration _uiGenerateConfiguration;
|
||||||
|
private static IUIGeneratorHelper _nameRuleHelper;
|
||||||
|
|
||||||
static UIGenerateConfiguration UIGenerateConfiguration
|
/// <summary>
|
||||||
|
/// 设置自定义命名规则助手[4](@ref)
|
||||||
|
/// </summary>
|
||||||
|
public static IUIGeneratorHelper UIGeneratorRuleHelper
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_nameRuleHelper == null || (_nameRuleHelper != null && !UIConfiguration.UIScriptGeneratorHelper.Equals(_nameRuleHelper.GetType().FullName)))
|
||||||
|
{
|
||||||
|
Type ruleHelperType = Type.GetType(UIConfiguration.UIScriptGeneratorHelper);
|
||||||
|
if (ruleHelperType == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"UIScriptGeneratorHelper: Could not load UI ScriptGeneratorHelper {UIConfiguration.UIScriptGeneratorHelper}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_nameRuleHelper = Activator.CreateInstance(ruleHelperType) as IUIGeneratorHelper;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _nameRuleHelper;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UIGenerateConfiguration UIConfiguration
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -61,11 +85,11 @@ namespace AlicizaX.UI.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetVerType(string uiName)
|
private static string GetVersionType(string uiName)
|
||||||
{
|
{
|
||||||
foreach (var pair in UIGenerateConfiguration.UIElementRegexConfigs)
|
foreach (var pair in UIConfiguration.UIElementRegexConfigs)
|
||||||
{
|
{
|
||||||
if (uiName.StartsWith(pair.uiElementRegex))
|
if (uiName.StartsWith(pair.uiElementRegex, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
return pair.componentType;
|
return pair.componentType;
|
||||||
}
|
}
|
||||||
@ -74,25 +98,24 @@ namespace AlicizaX.UI.Editor
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string[] SplitComName(string name)
|
private static string[] SplitComponentName(string name)
|
||||||
{
|
{
|
||||||
bool hasCom = name.Contains(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName);
|
bool hasCom = name.Contains(UIConfiguration.UIGenerateCommonData.ComCheckEndName);
|
||||||
if (!hasCom) return null;
|
if (!hasCom) return null;
|
||||||
|
|
||||||
string comStr = name.Substring(0, name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName));
|
string comStr = name.Substring(0,
|
||||||
return comStr.Split(UIGenerateConfiguration.UIGenerateCommonData.ComCheckSplitName);
|
name.IndexOf(UIConfiguration.UIGenerateCommonData.ComCheckEndName, StringComparison.Ordinal));
|
||||||
|
return comStr.Split(UIConfiguration.UIGenerateCommonData.ComCheckSplitName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetKeyName(string key, string componentName, EBindType bindType)
|
||||||
private static string GetKeyName(string key, string componentName)
|
|
||||||
{
|
{
|
||||||
return $"{key}{componentName.Substring(componentName.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName) + 1)}";
|
return UIGeneratorRuleHelper.GetPrivateComponentByNameRule(key, componentName, bindType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<UIBindData> UIBindDatas = new List<UIBindData>();
|
private static List<UIBindData> _uiBindDatas = new List<UIBindData>();
|
||||||
private static string GenerateNameSpace = string.Empty;
|
private static string _generateNameSpace = string.Empty;
|
||||||
|
private static List<string> _arrayComponents = new List<string>();
|
||||||
private static List<string> ArrayComs = new List<string>();
|
|
||||||
|
|
||||||
private static void GetBindData(Transform root)
|
private static void GetBindData(Transform root)
|
||||||
{
|
{
|
||||||
@ -100,34 +123,42 @@ namespace AlicizaX.UI.Editor
|
|||||||
{
|
{
|
||||||
Transform child = root.GetChild(i);
|
Transform child = root.GetChild(i);
|
||||||
|
|
||||||
bool hasWdiget = child.GetComponent<UIHolderObjectBase>() != null;
|
bool hasWidget = child.GetComponent<UIHolderObjectBase>() != null;
|
||||||
|
|
||||||
if (UIGenerateConfiguration.UIGenerateCommonData.ExcludeKeywords.Any(k => child.name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0)) continue;
|
if (UIConfiguration.UIGenerateCommonData.ExcludeKeywords.Any(k =>
|
||||||
|
child.name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0))
|
||||||
|
continue;
|
||||||
|
|
||||||
bool isArrayComs = child.name.StartsWith(UIGenerateConfiguration.UIGenerateCommonData.ArrayComSplitName);
|
bool isArrayComponent = child.name.StartsWith(
|
||||||
if (hasWdiget)
|
UIConfiguration.UIGenerateCommonData.ArrayComSplitName, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
if (hasWidget)
|
||||||
{
|
{
|
||||||
CollectWidget(child);
|
CollectWidget(child);
|
||||||
}
|
}
|
||||||
else if (isArrayComs)
|
else if (isArrayComponent)
|
||||||
{
|
{
|
||||||
string splitCode = UIGenerateConfiguration.UIGenerateCommonData.ArrayComSplitName;
|
string splitCode = UIConfiguration.UIGenerateCommonData.ArrayComSplitName;
|
||||||
int lastIndex = child.name.LastIndexOf(splitCode);
|
int lastIndex = child.name.LastIndexOf(splitCode, StringComparison.Ordinal);
|
||||||
string text = child.name.Substring(child.name.IndexOf(splitCode) + 1, lastIndex - 1);
|
string text = child.name.Substring(
|
||||||
if (ArrayComs.Contains(text)) continue;
|
child.name.IndexOf(splitCode, StringComparison.Ordinal) + 1,
|
||||||
ArrayComs.Add(text);
|
lastIndex - 1);
|
||||||
List<Transform> arrayComs = new List<Transform>();
|
|
||||||
|
if (_arrayComponents.Contains(text)) continue;
|
||||||
|
_arrayComponents.Add(text);
|
||||||
|
|
||||||
|
List<Transform> arrayComponents = new List<Transform>();
|
||||||
for (int j = 0; j < root.childCount; j++)
|
for (int j = 0; j < root.childCount; j++)
|
||||||
{
|
{
|
||||||
if (root.GetChild(j).name.Contains(text))
|
if (root.GetChild(j).name.Contains(text, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
arrayComs.Add(root.GetChild(j));
|
arrayComponents.Add(root.GetChild(j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CollectArrayComponent(arrayComs, text);
|
CollectArrayComponent(arrayComponents, text);
|
||||||
}
|
}
|
||||||
else if (!isArrayComs && !hasWdiget)
|
else if (!isArrayComponent && !hasWidget)
|
||||||
{
|
{
|
||||||
CollectComponent(child);
|
CollectComponent(child);
|
||||||
GetBindData(child);
|
GetBindData(child);
|
||||||
@ -137,25 +168,25 @@ namespace AlicizaX.UI.Editor
|
|||||||
|
|
||||||
private static void CollectComponent(Transform node)
|
private static void CollectComponent(Transform node)
|
||||||
{
|
{
|
||||||
string[] comArray = SplitComName(node.name);
|
string[] componentArray = SplitComponentName(node.name);
|
||||||
if (comArray != null)
|
if (componentArray != null)
|
||||||
{
|
{
|
||||||
foreach (var com in comArray)
|
foreach (var com in componentArray)
|
||||||
{
|
{
|
||||||
string typeName = GetVerType(com);
|
string typeName = GetVersionType(com);
|
||||||
if (string.IsNullOrEmpty(typeName)) continue;
|
if (string.IsNullOrEmpty(typeName)) continue;
|
||||||
|
|
||||||
Component component = node.GetComponent(typeName);
|
Component component = node.GetComponent(typeName);
|
||||||
if (component != null)
|
if (component != null)
|
||||||
{
|
{
|
||||||
string keyName = GetKeyName(com, node.name);
|
string keyName = GetKeyName(com, node.name, EBindType.None);
|
||||||
if (UIBindDatas.Exists(a => a.Name == keyName))
|
if (_uiBindDatas.Exists(a => a.Name == keyName))
|
||||||
{
|
{
|
||||||
Debug.LogError($"Duplicate key found: {keyName}");
|
Debug.LogError($"Duplicate key found: {keyName}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
UIBindDatas.Add(new UIBindData(keyName, component));
|
_uiBindDatas.Add(new UIBindData(keyName, component));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -167,44 +198,50 @@ namespace AlicizaX.UI.Editor
|
|||||||
|
|
||||||
private static void CollectWidget(Transform node)
|
private static void CollectWidget(Transform node)
|
||||||
{
|
{
|
||||||
if (node.name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName) != -1 && node.name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckSplitName) != -1)
|
if (node.name.IndexOf(UIConfiguration.UIGenerateCommonData.ComCheckEndName, StringComparison.Ordinal) != -1 &&
|
||||||
|
node.name.IndexOf(UIConfiguration.UIGenerateCommonData.ComCheckSplitName, StringComparison.Ordinal) != -1)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"{node.name} 子组件不能包含规则定义符号!");
|
Debug.LogWarning($"{node.name} child component cannot contain rule definition symbols!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UIHolderObjectBase component = node.GetComponent<UIHolderObjectBase>();
|
UIHolderObjectBase component = node.GetComponent<UIHolderObjectBase>();
|
||||||
string keyName = node.name;
|
string keyName = GetKeyName(string.Empty, node.name, EBindType.Widget);
|
||||||
if (UIBindDatas.Exists(a => a.Name == keyName))
|
|
||||||
|
if (_uiBindDatas.Exists(a => a.Name == keyName))
|
||||||
{
|
{
|
||||||
Debug.LogError($"Duplicate key found: {keyName}");
|
Debug.LogError($"Duplicate key found: {keyName}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UIBindDatas.Add(new UIBindData(keyName, component, EBindType.Widget));
|
_uiBindDatas.Add(new UIBindData(keyName, component, EBindType.Widget));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CollectArrayComponent(List<Transform> arrayNode, string nodeName)
|
private static void CollectArrayComponent(List<Transform> arrayNode, string nodeName)
|
||||||
{
|
{
|
||||||
string[] comArray = SplitComName(nodeName);
|
string[] componentArray = SplitComponentName(nodeName);
|
||||||
arrayNode = arrayNode.OrderBy(s => int.Parse(s.name.Split('*').Last())).ToList();
|
arrayNode = arrayNode.OrderBy(s => int.Parse(s.name.Split('*').Last(),
|
||||||
List<UIBindData> tempBindDatas = new List<UIBindData>(comArray.Length);
|
System.Globalization.CultureInfo.InvariantCulture)).ToList();
|
||||||
|
|
||||||
if (comArray != null)
|
List<UIBindData> tempBindDatas = new List<UIBindData>(componentArray.Length);
|
||||||
|
|
||||||
|
if (componentArray != null)
|
||||||
{
|
{
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (var com in comArray)
|
foreach (var com in componentArray)
|
||||||
{
|
{
|
||||||
foreach (var node in arrayNode)
|
foreach (var node in arrayNode)
|
||||||
{
|
{
|
||||||
string typeName = GetVerType(com);
|
string typeName = GetVersionType(com);
|
||||||
if (string.IsNullOrEmpty(typeName)) continue;
|
if (string.IsNullOrEmpty(typeName)) continue;
|
||||||
|
|
||||||
Component component = node.GetComponent(typeName);
|
Component component = node.GetComponent(typeName);
|
||||||
if (component != null)
|
if (component != null)
|
||||||
{
|
{
|
||||||
string keyName = GetKeyName(com, nodeName) + "List";
|
string keyName = GetKeyName(com, nodeName, EBindType.ListCom);
|
||||||
if (tempBindDatas.Count - 1 < index) tempBindDatas.Add(new UIBindData(keyName, new List<Component>(), EBindType.ListCom));
|
if (tempBindDatas.Count - 1 < index)
|
||||||
|
tempBindDatas.Add(new UIBindData(keyName, new List<Component>(), EBindType.ListCom));
|
||||||
|
|
||||||
tempBindDatas[index].BindCom.Add(component);
|
tempBindDatas[index].BindCom.Add(component);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -217,84 +254,91 @@ namespace AlicizaX.UI.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIBindDatas.AddRange(tempBindDatas.ToArray());
|
_uiBindDatas.AddRange(tempBindDatas.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string GetReferenceNamespace()
|
||||||
private static string GetRefrenceNameSpace()
|
|
||||||
{
|
{
|
||||||
StringBuilder refrenceNameSpaceBuilder = new StringBuilder();
|
StringBuilder referenceNamespaceBuilder = new StringBuilder();
|
||||||
HashSet<string> nameSpaces = new HashSet<string>();
|
HashSet<string> namespaces = new HashSet<string>();
|
||||||
nameSpaces.Add("UnityEngine");
|
namespaces.Add("UnityEngine");
|
||||||
refrenceNameSpaceBuilder.Append($"using UnityEngine;\n");
|
referenceNamespaceBuilder.Append("using UnityEngine;\n");
|
||||||
foreach (var bindData in UIBindDatas)
|
|
||||||
|
foreach (var bindData in _uiBindDatas)
|
||||||
{
|
{
|
||||||
string nameSpace = bindData.BindCom.FirstOrDefault().GetType().Namespace;
|
string nameSpace = bindData.BindCom.FirstOrDefault()?.GetType().Namespace;
|
||||||
if (bindData.BindType == EBindType.ListCom)
|
if (bindData.BindType == EBindType.ListCom)
|
||||||
{
|
{
|
||||||
if (!nameSpaces.Contains("using System.Collections.Generic;"))
|
if (!namespaces.Contains("System.Collections.Generic"))
|
||||||
{
|
{
|
||||||
refrenceNameSpaceBuilder.Append("using System.Collections.Generic;\n");
|
referenceNamespaceBuilder.Append("using System.Collections.Generic;\n");
|
||||||
|
namespaces.Add("System.Collections.Generic");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nameSpaces.Contains(nameSpace) && !string.IsNullOrEmpty(nameSpace))
|
if (!string.IsNullOrEmpty(nameSpace) && !namespaces.Contains(nameSpace))
|
||||||
{
|
{
|
||||||
nameSpaces.Add(nameSpace);
|
namespaces.Add(nameSpace);
|
||||||
refrenceNameSpaceBuilder.Append($"using {nameSpace};\n");
|
referenceNamespaceBuilder.Append($"using {nameSpace};\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return refrenceNameSpaceBuilder.ToString();
|
return referenceNamespaceBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetVarText(List<UIBindData> uiBindDatas)
|
private static string GetVariableText(List<UIBindData> uiBindDatas)
|
||||||
{
|
{
|
||||||
StringBuilder varTextBuilder = new StringBuilder();
|
StringBuilder variableTextBuilder = new StringBuilder();
|
||||||
foreach (var bindData in uiBindDatas)
|
foreach (var bindData in uiBindDatas)
|
||||||
{
|
{
|
||||||
var varName = bindData.Name;
|
string variableName = bindData.Name;
|
||||||
varTextBuilder.Append("\t\t[SerializeField]\n");
|
string publicName = UIGeneratorRuleHelper.GetPublicComponentByNameRule(variableName);
|
||||||
varTextBuilder.Append("\t\t[ReadOnly]\n");
|
variableTextBuilder.Append("\t\t[SerializeField]\n");
|
||||||
varTextBuilder.Append("\t\t[HideLabel]\n");
|
|
||||||
|
|
||||||
if (bindData.BindType == EBindType.None)
|
if (bindData.BindType == EBindType.None)
|
||||||
{
|
{
|
||||||
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} m{varName};\n");
|
variableTextBuilder.Append(
|
||||||
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} {varName} => m{varName};\n\n");
|
$"\t\tprivate {bindData.BindCom.FirstOrDefault()?.GetType().Name} {variableName};\n");
|
||||||
|
variableTextBuilder.Append(
|
||||||
|
$"\t\tpublic {bindData.BindCom.FirstOrDefault()?.GetType().Name} {publicName} => {variableName};\n\n");
|
||||||
}
|
}
|
||||||
else if (bindData.BindType == EBindType.ListCom)
|
else if (bindData.BindType == EBindType.ListCom)
|
||||||
{
|
{
|
||||||
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} [] m{varName} = new {bindData.BindCom.FirstOrDefault().GetType().Name}[{bindData.BindCom.Count}];\n");
|
variableTextBuilder.Append(
|
||||||
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} [] {varName} => m{varName};\n\n");
|
$"\t\tprivate {bindData.BindCom.FirstOrDefault()?.GetType().Name}[] {variableName} = " +
|
||||||
|
$"new {bindData.BindCom.FirstOrDefault()?.GetType().Name}[{bindData.BindCom.Count}];\n");
|
||||||
|
variableTextBuilder.Append(
|
||||||
|
$"\t\tpublic {bindData.BindCom.FirstOrDefault()?.GetType().Name}[] {publicName} => {variableName};\n\n");
|
||||||
}
|
}
|
||||||
else if (bindData.BindType == EBindType.Widget)
|
else if (bindData.BindType == EBindType.Widget)
|
||||||
{
|
{
|
||||||
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} m{varName};\n");
|
variableTextBuilder.Append(
|
||||||
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} {varName} => m{varName};\n\n");
|
$"\t\tprivate {bindData.BindCom.FirstOrDefault()?.GetType().Name} {variableName};\n");
|
||||||
|
variableTextBuilder.Append(
|
||||||
|
$"\t\tpublic {bindData.BindCom.FirstOrDefault()?.GetType().Name} {publicName} => {variableName};\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return varTextBuilder.ToString();
|
return variableTextBuilder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GenerateScript(string className)
|
private static string GenerateScript(string className)
|
||||||
{
|
{
|
||||||
StringBuilder scriptBuilder = new StringBuilder();
|
StringBuilder scriptBuilder = new StringBuilder();
|
||||||
scriptBuilder.Append(GetRefrenceNameSpace());
|
scriptBuilder.Append(GetReferenceNamespace());
|
||||||
|
|
||||||
scriptBuilder.Append("using Sirenix.OdinInspector;\n");
|
scriptBuilder.Append("using Sirenix.OdinInspector;\n");
|
||||||
scriptBuilder.Append("using AlicizaX.UI.Runtime;\n");
|
scriptBuilder.Append("using AlicizaX.UI.Runtime;\n");
|
||||||
scriptBuilder.Append($"namespace {GenerateNameSpace}\n");
|
scriptBuilder.Append($"namespace {_generateNameSpace}\n");
|
||||||
scriptBuilder.Append("{\n");
|
scriptBuilder.Append("{\n");
|
||||||
scriptBuilder.Append($"\t#Attribute#\n");
|
scriptBuilder.Append("\t#Attribute#\n");
|
||||||
scriptBuilder.Append($"\tpublic class {className} : UIHolderObjectBase\n");
|
scriptBuilder.Append($"\tpublic class {className} : UIHolderObjectBase\n");
|
||||||
scriptBuilder.Append("\t{\n");
|
scriptBuilder.Append("\t{\n");
|
||||||
|
|
||||||
scriptBuilder.Append($"\t\tpublic const string ResTag = #Tag#;\n");
|
scriptBuilder.Append("\t\tpublic const string ResTag = #Tag#;\n");
|
||||||
|
|
||||||
scriptBuilder.Append("\t\t#region Generated by Script Tool\n\n");
|
scriptBuilder.Append("\t\t#region Generated by Script Tool\n\n");
|
||||||
scriptBuilder.Append(GetVarText(UIBindDatas));
|
scriptBuilder.Append(GetVariableText(_uiBindDatas));
|
||||||
scriptBuilder.Append("\n\t\t#endregion\n");
|
scriptBuilder.Append("\n\t\t#endregion\n");
|
||||||
|
|
||||||
scriptBuilder.Append("\t}\n");
|
scriptBuilder.Append("\t}\n");
|
||||||
@ -306,51 +350,50 @@ namespace AlicizaX.UI.Editor
|
|||||||
public static void GenerateAndAttachScript(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
public static void GenerateAndAttachScript(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
|
||||||
{
|
{
|
||||||
EditorPrefs.SetInt("InstanceId", targetObject.GetInstanceID());
|
EditorPrefs.SetInt("InstanceId", targetObject.GetInstanceID());
|
||||||
UIBindDatas.Clear();
|
_uiBindDatas.Clear();
|
||||||
GenerateNameSpace = scriptGenerateData.NameSpace;
|
_generateNameSpace = scriptGenerateData.NameSpace;
|
||||||
ArrayComs.Clear();
|
_arrayComponents.Clear();
|
||||||
|
|
||||||
string className = $"{UIGenerateConfiguration.UIGenerateCommonData.GeneratePrefix}_{targetObject.name}";
|
|
||||||
|
|
||||||
|
string className = $"{UIConfiguration.UIGenerateCommonData.GeneratePrefix}_{targetObject.name}";
|
||||||
string scriptSavePath = Path.Combine(scriptGenerateData.GenerateHolderCodePath, className + ".cs");
|
string scriptSavePath = Path.Combine(scriptGenerateData.GenerateHolderCodePath, className + ".cs");
|
||||||
|
|
||||||
|
|
||||||
GetBindData(targetObject.transform);
|
GetBindData(targetObject.transform);
|
||||||
|
|
||||||
string scriptContent = GenerateScript(className);
|
string scriptContent = GenerateScript(className);
|
||||||
string TagName = $"\"{targetObject.name}\"";
|
string tagName = $"\"{targetObject.name}\"";
|
||||||
|
|
||||||
if (scriptGenerateData.LoadType == EUIResLoadType.Resources)
|
if (scriptGenerateData.LoadType == EUIResLoadType.Resources)
|
||||||
{
|
{
|
||||||
string matchWords = string.Empty;
|
string matchWords = string.Empty;
|
||||||
UIGenerateConfiguration.UIGenerateCommonData.CombineWords.Any(t =>
|
UIConfiguration.UIGenerateCommonData.CombineWords.Any(t =>
|
||||||
{
|
{
|
||||||
if (targetObject.name.IndexOf(t.Key) >= 0)
|
if (targetObject.name.IndexOf(t.Key, StringComparison.Ordinal) >= 0)
|
||||||
{
|
{
|
||||||
matchWords = t.Value;
|
matchWords = t.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
return targetObject.name.IndexOf(t.Key) >= 0;
|
return targetObject.name.IndexOf(t.Key, StringComparison.Ordinal) >= 0;
|
||||||
});
|
});
|
||||||
string finalPath = Path.Combine(scriptGenerateData.UIPrefabRootPath.Replace("Assets/", "").Replace("Resources/", ""), matchWords);
|
|
||||||
string didc = Path.Combine(scriptGenerateData.UIPrefabRootPath, matchWords);
|
string finalPath = Path.Combine(
|
||||||
if (!Directory.Exists(didc))
|
scriptGenerateData.UIPrefabRootPath.Replace("Assets/", "").Replace("Resources/", ""),
|
||||||
|
matchWords);
|
||||||
|
string directory = Path.Combine(scriptGenerateData.UIPrefabRootPath, matchWords);
|
||||||
|
|
||||||
|
if (!Directory.Exists(directory))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(didc);
|
Directory.CreateDirectory(directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
finalPath = Utility.Path.GetRegularPath(finalPath);
|
finalPath = Utility.Path.GetRegularPath(finalPath);
|
||||||
TagName = $"\"{finalPath}/{targetObject.name}\"";
|
tagName = $"\"{finalPath}/{targetObject.name}\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
scriptContent = scriptContent.Replace("#Tag#", TagName);
|
scriptContent = scriptContent.Replace("#Tag#", tagName);
|
||||||
|
|
||||||
|
|
||||||
//#Attribute#
|
|
||||||
string uiAttribute = $"[UIRes({className}.ResTag, EUIResLoadType.{scriptGenerateData.LoadType.ToString()})]";
|
|
||||||
|
|
||||||
|
string uiAttribute = $"[UIRes({className}.ResTag, EUIResLoadType.{scriptGenerateData.LoadType})]";
|
||||||
scriptContent = scriptContent.Replace("#Attribute#", uiAttribute);
|
scriptContent = scriptContent.Replace("#Attribute#", uiAttribute);
|
||||||
|
|
||||||
|
|
||||||
if (File.Exists(scriptSavePath))
|
if (File.Exists(scriptSavePath))
|
||||||
{
|
{
|
||||||
string oldText = File.ReadAllText(scriptSavePath);
|
string oldText = File.ReadAllText(scriptSavePath);
|
||||||
@ -362,34 +405,32 @@ namespace AlicizaX.UI.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File.WriteAllText(scriptSavePath, scriptContent, Encoding.UTF8);
|
||||||
File.WriteAllText(scriptSavePath, scriptContent);
|
|
||||||
EditorPrefs.SetString("Generate", className);
|
EditorPrefs.SetString("Generate", className);
|
||||||
AssetDatabase.Refresh();
|
AssetDatabase.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[DidReloadScripts]
|
[DidReloadScripts]
|
||||||
private static void CheckHasAttach()
|
private static void CheckHasAttach()
|
||||||
{
|
{
|
||||||
bool has = EditorPrefs.HasKey("Generate");
|
bool has = EditorPrefs.HasKey("Generate");
|
||||||
if (has)
|
if (has)
|
||||||
{
|
{
|
||||||
UIBindDatas.Clear();
|
_uiBindDatas.Clear();
|
||||||
ArrayComs.Clear();
|
_arrayComponents.Clear();
|
||||||
var className = EditorPrefs.GetString("Generate");
|
string className = EditorPrefs.GetString("Generate");
|
||||||
// var targetObject = UIGenerateWindow.GetTargetObject();
|
|
||||||
int instanceId = EditorPrefs.GetInt("InstanceId", -1);
|
int instanceId = EditorPrefs.GetInt("InstanceId", -1);
|
||||||
|
|
||||||
if (instanceId == -1)
|
if (instanceId == -1)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var targetObject = (GameObject)EditorUtility.InstanceIDToObject(instanceId);
|
GameObject targetObject = (GameObject)EditorUtility.InstanceIDToObject(instanceId);
|
||||||
|
|
||||||
if (!targetObject)
|
if (!targetObject)
|
||||||
{
|
{
|
||||||
Debug.Log("UI脚本生成附加物体丢失!");
|
Debug.Log("UI script generation attachment object missing!");
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorPrefs.DeleteKey("Generate");
|
EditorPrefs.DeleteKey("Generate");
|
||||||
@ -400,17 +441,16 @@ namespace AlicizaX.UI.Editor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void AttachScriptToGameObject(GameObject targetObject, string scriptClassName)
|
private static void AttachScriptToGameObject(GameObject targetObject, string scriptClassName)
|
||||||
{
|
{
|
||||||
Type scriptType = null;
|
Type scriptType = null;
|
||||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
{
|
{
|
||||||
if (assembly.FullName.Contains("Editor")) continue;
|
if (assembly.FullName.Contains("Editor")) continue;
|
||||||
var types = assembly.GetTypes();
|
Type[] types = assembly.GetTypes();
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
if (type.IsClass && !type.IsAbstract && type.Name.Contains(scriptClassName))
|
if (type.IsClass && !type.IsAbstract && type.Name.Contains(scriptClassName, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
scriptType = type;
|
scriptType = type;
|
||||||
}
|
}
|
||||||
@ -424,28 +464,24 @@ namespace AlicizaX.UI.Editor
|
|||||||
|
|
||||||
foreach (FieldInfo field in fields)
|
foreach (FieldInfo field in fields)
|
||||||
{
|
{
|
||||||
List<Component> componentInObjects = UIBindDatas.Find(data => "m" + data.Name == field.Name)?.BindCom;
|
List<Component> componentInObjects = _uiBindDatas.Find(data => data.Name == field.Name)?.BindCom;
|
||||||
if (componentInObjects != null)
|
if (componentInObjects != null)
|
||||||
{
|
{
|
||||||
if (field.FieldType.IsArray)
|
if (field.FieldType.IsArray)
|
||||||
{
|
{
|
||||||
// 获取数组元素类型
|
|
||||||
Type elementType = field.FieldType.GetElementType();
|
Type elementType = field.FieldType.GetElementType();
|
||||||
// 创建对应类型的数组
|
|
||||||
Array array = Array.CreateInstance(elementType, componentInObjects.Count);
|
Array array = Array.CreateInstance(elementType, componentInObjects.Count);
|
||||||
|
|
||||||
for (int i = 0; i < componentInObjects.Count; i++)
|
for (int i = 0; i < componentInObjects.Count; i++)
|
||||||
{
|
{
|
||||||
Component comp = componentInObjects[i];
|
Component comp = componentInObjects[i];
|
||||||
// 检查元素类型是否匹配
|
|
||||||
if (elementType.IsInstanceOfType(comp))
|
if (elementType.IsInstanceOfType(comp))
|
||||||
{
|
{
|
||||||
array.SetValue(comp, i);
|
array.SetValue(comp, i);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogError($"元素 {i} 类型不匹配,期望 {elementType.Name},实际为 {comp.GetType().Name}");
|
Debug.LogError($"Element {i} type mismatch, expected {elementType.Name}, actual {comp.GetType().Name}");
|
||||||
// 处理错误,如跳过或终止赋值
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -453,24 +489,22 @@ namespace AlicizaX.UI.Editor
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 非数组字段取第一个元素
|
|
||||||
if (componentInObjects.Count > 0)
|
if (componentInObjects.Count > 0)
|
||||||
{
|
{
|
||||||
// 同样检查类型兼容性
|
|
||||||
if (field.FieldType.IsInstanceOfType(componentInObjects[0]))
|
if (field.FieldType.IsInstanceOfType(componentInObjects[0]))
|
||||||
{
|
{
|
||||||
field.SetValue(component, componentInObjects[0]);
|
field.SetValue(component, componentInObjects[0]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogError($"字段 {field.Name} 类型不匹配,无法赋值");
|
Debug.LogError($"Field {field.Name} type mismatch, cannot assign value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogError($"字段 {field.Name} 未找到匹配的组件绑定");
|
Debug.LogError($"Field {field.Name} did not find matching component binding");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,104 +1,648 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using AlicizaX.UI.Runtime;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Sirenix.OdinInspector;
|
|
||||||
using Sirenix.OdinInspector.Editor;
|
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
|
using UnityEditorInternal;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Windows;
|
|
||||||
|
|
||||||
namespace AlicizaX.UI.Editor
|
namespace AlicizaX.UI.Editor
|
||||||
{
|
{
|
||||||
internal class UISettingEditorWindow : OdinEditorWindow
|
public class UISettingEditorWindow_NoOdin_v2 : EditorWindow
|
||||||
{
|
{
|
||||||
[MenuItem("Tools/AlicizaX/UI/Setting Window")]
|
[MenuItem("Tools/AlicizaX/UISetting Window")]
|
||||||
private static void OpenWindow()
|
private static void OpenWindow()
|
||||||
{
|
{
|
||||||
GetWindow<UISettingEditorWindow>().Show();
|
var w = GetWindow<UISettingEditorWindow_NoOdin_v2>("UI Setting");
|
||||||
|
w.minSize = new Vector2(760, 520);
|
||||||
|
w.Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
uiGenerateConfiguration = UIGenerateConfiguration.Instance;
|
|
||||||
UIGenerateCommonData = uiGenerateConfiguration.UIGenerateCommonData;
|
|
||||||
UIScriptGenerateConfig = uiGenerateConfiguration.UIScriptGenerateConfig;
|
|
||||||
UIElementRegexConfigs = uiGenerateConfiguration.UIElementRegexConfigs;
|
|
||||||
RefreshLabel();
|
|
||||||
}
|
|
||||||
|
|
||||||
// [Required] [InlineEditor(InlineEditorObjectFieldModes.CompletelyHidden)] [DisableInPlayMode] [HideLabel]
|
|
||||||
private UIGenerateConfiguration uiGenerateConfiguration;
|
private UIGenerateConfiguration uiGenerateConfiguration;
|
||||||
|
private UIGenerateCommonData UIGenerateCommonData;
|
||||||
|
private List<UIEelementRegexData> UIElementRegexConfigs;
|
||||||
|
private List<UIScriptGenerateData> UIScriptGenerateConfigs;
|
||||||
|
|
||||||
|
private List<string> excludeKeywordsList = new List<string>();
|
||||||
|
|
||||||
|
|
||||||
|
private Vector2 scroll;
|
||||||
|
private int toolbarTab;
|
||||||
|
private readonly string[] toolbarTitles = { "UI基础设置", "UI构建配置", "UI元素映射" };
|
||||||
|
|
||||||
|
|
||||||
|
private ReorderableList combineList;
|
||||||
|
private ReorderableList regexList;
|
||||||
|
private ReorderableList projectList;
|
||||||
|
private ReorderableList excludeList;
|
||||||
|
|
||||||
|
|
||||||
|
private TextAsset importText;
|
||||||
|
|
||||||
[Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI基础设置")] [SerializeField]
|
|
||||||
public UIGenerateCommonData UIGenerateCommonData;
|
|
||||||
|
|
||||||
[TabGroup("UI基础设置")] [LabelText("脚本生成预览")] [ShowInInspector] [ReadOnly] [OnValueChanged("RefreshLabel")]
|
|
||||||
private string previewLabel;
|
private string previewLabel;
|
||||||
|
|
||||||
[TabGroup("UI基础设置")] [LabelText("组件生成预览")] [ShowInInspector] [ReadOnly] [OnValueChanged("RefreshLabel")] [SuffixLabel("(下标0开始)")]
|
|
||||||
private string previewCompLabel;
|
private string previewCompLabel;
|
||||||
|
|
||||||
[Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI构建配置")] [SerializeField]
|
private List<string> m_ScriptGeneratorHelperTypes = new();
|
||||||
public UIScriptGenerateConfig UIScriptGenerateConfig;
|
private int m_ScriptGeneratorHelperSelectIndex;
|
||||||
|
|
||||||
[Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI元素映射")] [SerializeField] [TableList(ShowIndexLabels = false, DrawScrollView = true, AlwaysExpanded = true)]
|
private void OnEnable()
|
||||||
public List<UIEelementRegexData> UIElementRegexConfigs;
|
{
|
||||||
|
uiGenerateConfiguration = UIGenerateConfiguration.Instance;
|
||||||
|
if (uiGenerateConfiguration == null)
|
||||||
|
{
|
||||||
|
uiGenerateConfiguration = ScriptableObject.CreateInstance<UIGenerateConfiguration>();
|
||||||
|
}
|
||||||
|
|
||||||
|
UIGenerateCommonData = uiGenerateConfiguration.UIGenerateCommonData ?? new UIGenerateCommonData();
|
||||||
|
UIElementRegexConfigs = uiGenerateConfiguration.UIElementRegexConfigs ?? new List<UIEelementRegexData>();
|
||||||
|
UIScriptGenerateConfigs = uiGenerateConfiguration.UIScriptGenerateConfigs ?? new List<UIScriptGenerateData>();
|
||||||
|
|
||||||
|
excludeKeywordsList = (UIGenerateCommonData.ExcludeKeywords ?? new string[0]).ToList();
|
||||||
|
|
||||||
|
SetupLists();
|
||||||
|
RefreshLabel();
|
||||||
|
RefreshScriptGeneratorHelperTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupLists()
|
||||||
|
{
|
||||||
|
combineList = new ReorderableList(UIGenerateCommonData.CombineWords, typeof(StringPair), true, true, true, true);
|
||||||
|
combineList.drawHeaderCallback = (r) => EditorGUI.LabelField(r, "路径拼接映射 (Key -> Value)");
|
||||||
|
combineList.drawElementCallback = (rect, index, active, focused) =>
|
||||||
|
{
|
||||||
|
var p = UIGenerateCommonData.CombineWords[index];
|
||||||
|
rect.y += 2;
|
||||||
|
float half = rect.width / 2 - 8;
|
||||||
|
p.Key = EditorGUI.TextField(new Rect(rect.x, rect.y, half, EditorGUIUtility.singleLineHeight), p.Key);
|
||||||
|
p.Value = EditorGUI.TextField(new Rect(rect.x + half + 16, rect.y, half, EditorGUIUtility.singleLineHeight), p.Value);
|
||||||
|
};
|
||||||
|
combineList.onAddCallback = (r) => UIGenerateCommonData.CombineWords.Add(new StringPair("Key", "Value"));
|
||||||
|
combineList.onRemoveCallback = (r) =>
|
||||||
|
{
|
||||||
|
if (r.index >= 0) UIGenerateCommonData.CombineWords.RemoveAt(r.index);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
excludeList = new ReorderableList(excludeKeywordsList, typeof(string), true, true, true, true);
|
||||||
|
excludeList.drawHeaderCallback = (r) => EditorGUI.LabelField(r, "排除关键字(匹配则不生成)");
|
||||||
|
excludeList.drawElementCallback = (rect, index, active, focused) =>
|
||||||
|
{
|
||||||
|
rect.y += 2;
|
||||||
|
excludeKeywordsList[index] = EditorGUI.TextField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), excludeKeywordsList[index]);
|
||||||
|
};
|
||||||
|
excludeList.onAddCallback = (r) => excludeKeywordsList.Add(string.Empty);
|
||||||
|
excludeList.onRemoveCallback = (r) =>
|
||||||
|
{
|
||||||
|
if (r.index >= 0) excludeKeywordsList.RemoveAt(r.index);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
regexList = new ReorderableList(UIElementRegexConfigs, typeof(UIEelementRegexData), true, true, true, true);
|
||||||
|
regexList.drawHeaderCallback = (r) => EditorGUI.LabelField(r, "UI元素映射 (正则 -> 组件)");
|
||||||
|
regexList.elementHeightCallback = (i) => EditorGUIUtility.singleLineHeight * 2 + 8;
|
||||||
|
regexList.drawElementCallback = (rect, index, active, focused) =>
|
||||||
|
{
|
||||||
|
var item = UIElementRegexConfigs[index];
|
||||||
|
rect.y += 2;
|
||||||
|
float lh = EditorGUIUtility.singleLineHeight;
|
||||||
|
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
item.uiElementRegex = EditorGUI.TextField(new Rect(rect.x + 70, rect.y, rect.width - 70, lh), item.uiElementRegex);
|
||||||
|
if (EditorGUI.EndChangeCheck()) RefreshLabel();
|
||||||
|
|
||||||
|
|
||||||
|
EditorGUI.LabelField(new Rect(rect.x, rect.y + lh + 4, 60, lh), "Component");
|
||||||
|
var btnRect = new Rect(rect.x + 70, rect.y + lh + 4, 180, lh);
|
||||||
|
|
||||||
|
string btnLabel = string.IsNullOrEmpty(item.componentType) ? "(选择类型)" : item.componentType;
|
||||||
|
if (GUI.Button(btnRect, btnLabel, EditorStyles.popup))
|
||||||
|
{
|
||||||
|
var opts = CollectComponentTypeNamesFallback();
|
||||||
|
|
||||||
|
|
||||||
|
Rect anchor = new Rect(btnRect.x, btnRect.y + btnRect.height, Math.Min(360f, Mathf.Max(btnRect.width, 200f)), btnRect.height);
|
||||||
|
|
||||||
|
SearchablePopup.Show(anchor, opts, Math.Max(0, opts.IndexOf(item.componentType)), (selIndex) =>
|
||||||
|
{
|
||||||
|
if (selIndex >= 0 && selIndex < opts.Count)
|
||||||
|
{
|
||||||
|
item.componentType = opts[selIndex];
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
item.componentType = EditorGUI.TextField(new Rect(rect.x + 260, rect.y + lh + 4, rect.width - 260, lh), item.componentType);
|
||||||
|
};
|
||||||
|
regexList.onAddCallback = (r) => UIElementRegexConfigs.Add(new UIEelementRegexData { uiElementRegex = "", componentType = "" });
|
||||||
|
regexList.onRemoveCallback = (r) =>
|
||||||
|
{
|
||||||
|
if (r.index >= 0) UIElementRegexConfigs.RemoveAt(r.index);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
projectList = new ReorderableList(UIScriptGenerateConfigs, typeof(UIScriptGenerateData), true, true, true, true);
|
||||||
|
projectList.drawHeaderCallback = (r) => EditorGUI.LabelField(r, "UI脚本生成配置(多个项目)");
|
||||||
|
projectList.elementHeightCallback = (i) => EditorGUIUtility.singleLineHeight * 5 + 10;
|
||||||
|
projectList.drawElementCallback = (rect, index, active, focused) =>
|
||||||
|
{
|
||||||
|
var d = UIScriptGenerateConfigs[index];
|
||||||
|
float lh = EditorGUIUtility.singleLineHeight;
|
||||||
|
float pad = 2;
|
||||||
|
|
||||||
|
d.ProjectName = EditorGUI.TextField(new Rect(rect.x, rect.y, rect.width, lh), "项目名", d.ProjectName);
|
||||||
|
d.NameSpace = EditorGUI.TextField(new Rect(rect.x, rect.y + (lh + pad), rect.width, lh), "命名空间", d.NameSpace);
|
||||||
|
|
||||||
|
d.GenerateHolderCodePath = DrawFolderField("生成脚本路径", d.GenerateHolderCodePath, rect.x, rect.y + 2 * (lh + pad), rect.width, lh);
|
||||||
|
d.UIPrefabRootPath = DrawFolderField("Prefab根目录", d.UIPrefabRootPath, rect.x, rect.y + 3 * (lh + pad), rect.width, lh);
|
||||||
|
d.LoadType = (EUIResLoadType)EditorGUI.EnumPopup(new Rect(rect.x, rect.y + 4 * (lh + pad), rect.width, lh), "加载类型", d.LoadType);
|
||||||
|
};
|
||||||
|
projectList.onAddCallback = (r) => UIScriptGenerateConfigs.Add(new UIScriptGenerateData { ProjectName = "NewProject", NameSpace = "Game.UI", GenerateHolderCodePath = "Assets/Scripts/UI/Generated", UIPrefabRootPath = "Assets/Art/UI/Prefabs", LoadType = EUIResLoadType.Resources });
|
||||||
|
projectList.onRemoveCallback = (r) =>
|
||||||
|
{
|
||||||
|
if (r.index >= 0) UIScriptGenerateConfigs.RemoveAt(r.index);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DrawFolderField(string label, string value, float x, float y, float width, float h)
|
||||||
|
{
|
||||||
|
var txtRect = new Rect(x, y, width - 76, h);
|
||||||
|
var btnRect = new Rect(x + width - 72, y, 68, h);
|
||||||
|
value = EditorGUI.TextField(txtRect, label, value);
|
||||||
|
if (GUI.Button(btnRect, "选择"))
|
||||||
|
{
|
||||||
|
string p = EditorUtility.OpenFolderPanel("选择路径", Application.dataPath, "");
|
||||||
|
if (!string.IsNullOrEmpty(p))
|
||||||
|
{
|
||||||
|
if (p.StartsWith(Application.dataPath))
|
||||||
|
value = "Assets" + p.Substring(Application.dataPath.Length);
|
||||||
|
else
|
||||||
|
EditorUtility.DisplayDialog("提示", "请选择 Assets 下的路径", "确定");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshScriptGeneratorHelperTypes()
|
||||||
|
{
|
||||||
|
m_ScriptGeneratorHelperTypes = new List<string>();
|
||||||
|
|
||||||
|
m_ScriptGeneratorHelperTypes.AddRange(AlicizaX.Utility.Assembly.GetRuntimeTypeNames(typeof(IUIGeneratorHelper)));
|
||||||
|
|
||||||
|
m_ScriptGeneratorHelperSelectIndex = m_ScriptGeneratorHelperTypes.IndexOf(UIGenerateConfiguration.Instance.UIScriptGeneratorHelper);
|
||||||
|
if (m_ScriptGeneratorHelperSelectIndex < 0)
|
||||||
|
{
|
||||||
|
m_ScriptGeneratorHelperSelectIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
GUILayout.Space(6);
|
||||||
|
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||||
|
|
||||||
|
for (int i = 0; i < toolbarTitles.Length; i++)
|
||||||
|
{
|
||||||
|
bool isActive = (toolbarTab == i);
|
||||||
|
bool result = GUILayout.Toggle(isActive, toolbarTitles[i], EditorStyles.toolbarButton, GUILayout.Height(22));
|
||||||
|
if (result && toolbarTab != i)
|
||||||
|
{
|
||||||
|
toolbarTab = i;
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
|
||||||
|
|
||||||
|
var saveIcon = EditorGUIUtility.IconContent("SaveActive");
|
||||||
|
var refreshIcon = EditorGUIUtility.IconContent("Refresh");
|
||||||
|
var reloadIcon = EditorGUIUtility.IconContent("RotateTool");
|
||||||
|
GUIContent saveBtn = new GUIContent(saveIcon.image, "保存 (Save Now)");
|
||||||
|
GUIContent refreshBtn = new GUIContent(refreshIcon.image, "刷新预览");
|
||||||
|
GUIContent reloadBtn = new GUIContent(reloadIcon.image, "重载配置");
|
||||||
|
|
||||||
|
if (GUILayout.Button(saveBtn, EditorStyles.toolbarButton, GUILayout.Width(36)))
|
||||||
|
SaveConfig();
|
||||||
|
if (GUILayout.Button(refreshBtn, EditorStyles.toolbarButton, GUILayout.Width(36)))
|
||||||
|
RefreshLabel();
|
||||||
|
if (GUILayout.Button(reloadBtn, EditorStyles.toolbarButton, GUILayout.Width(36)))
|
||||||
|
{
|
||||||
|
OnEnable();
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
scroll = EditorGUILayout.BeginScrollView(scroll);
|
||||||
|
|
||||||
|
GUILayout.Space(8);
|
||||||
|
switch (toolbarTab)
|
||||||
|
{
|
||||||
|
case 0: DrawCommonPane(); break;
|
||||||
|
case 1: DrawScriptPane(); break;
|
||||||
|
case 2: DrawElementPane(); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
|
||||||
|
GUILayout.Space(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void DrawCommonPane()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical("box");
|
||||||
|
EditorGUILayout.LabelField("通用生成配置", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.Space(4);
|
||||||
|
|
||||||
|
EditorGUILayout.BeginVertical();
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
UIGenerateCommonData.ComCheckSplitName = EditorGUILayout.TextField(new GUIContent("组件检查分隔符", "例如 Button#Close"), UIGenerateCommonData.ComCheckSplitName);
|
||||||
|
UIGenerateCommonData.ComCheckEndName = EditorGUILayout.TextField(new GUIContent("组件结尾分隔符", "例如 @End"), UIGenerateCommonData.ComCheckEndName);
|
||||||
|
UIGenerateCommonData.ArrayComSplitName = EditorGUILayout.TextField(new GUIContent("数组组件分隔符", "例如 *Item"), UIGenerateCommonData.ArrayComSplitName);
|
||||||
|
UIGenerateCommonData.GeneratePrefix = EditorGUILayout.TextField(new GUIContent("生成脚本前缀"), UIGenerateCommonData.GeneratePrefix);
|
||||||
|
m_ScriptGeneratorHelperSelectIndex = EditorGUILayout.Popup("解密服务", m_ScriptGeneratorHelperSelectIndex, m_ScriptGeneratorHelperTypes.ToArray());
|
||||||
|
string selectService = m_ScriptGeneratorHelperTypes[m_ScriptGeneratorHelperSelectIndex];
|
||||||
|
if (uiGenerateConfiguration.UIScriptGeneratorHelper != selectService)
|
||||||
|
{
|
||||||
|
UIGenerateConfiguration.Instance.UIScriptGeneratorHelper = selectService;
|
||||||
|
UIGenerateConfiguration.Save();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
|
||||||
|
GUILayout.Space(8);
|
||||||
|
|
||||||
|
excludeList.DoLayoutList();
|
||||||
|
|
||||||
|
GUILayout.Space(8);
|
||||||
|
|
||||||
|
combineList.DoLayoutList();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
|
||||||
|
EditorGUILayout.LabelField("脚本生成预览", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.HelpBox(previewLabel ?? "", MessageType.None);
|
||||||
|
EditorGUILayout.LabelField("组件生成预览 (下标0开始)", EditorStyles.boldLabel);
|
||||||
|
EditorGUILayout.HelpBox(previewCompLabel ?? "", MessageType.None);
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
GUILayout.Space(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawScriptPane()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical("box");
|
||||||
|
EditorGUILayout.LabelField("UI脚本生成配置(支持多个项目)", EditorStyles.boldLabel);
|
||||||
|
GUILayout.Space(6);
|
||||||
|
projectList.DoLayoutList();
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
GUILayout.Space(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawElementPane()
|
||||||
|
{
|
||||||
|
EditorGUILayout.BeginVertical("box");
|
||||||
|
EditorGUILayout.LabelField("UI元素映射(正则 -> 组件)", EditorStyles.boldLabel);
|
||||||
|
GUILayout.Space(6);
|
||||||
|
|
||||||
|
GUILayout.BeginHorizontal(EditorStyles.toolbar);
|
||||||
|
{
|
||||||
|
if (GUILayout.Button("加载默认", EditorStyles.toolbarButton, GUILayout.Width(90)))
|
||||||
|
LoadDefault();
|
||||||
|
|
||||||
|
if (GUILayout.Button("导出", EditorStyles.toolbarButton, GUILayout.Width(70)))
|
||||||
|
ExportConfig();
|
||||||
|
|
||||||
|
GUILayout.Space(8);
|
||||||
|
|
||||||
|
importText = (TextAsset)EditorGUILayout.ObjectField(importText, typeof(TextAsset), false, GUILayout.Height(18), GUILayout.MinWidth(200));
|
||||||
|
|
||||||
|
GUI.enabled = importText != null;
|
||||||
|
if (GUILayout.Button("执行导入", EditorStyles.toolbarButton, GUILayout.Width(84)))
|
||||||
|
{
|
||||||
|
if (importText != null)
|
||||||
|
{
|
||||||
|
ImportConfig(importText);
|
||||||
|
importText = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.enabled = true;
|
||||||
|
|
||||||
|
GUILayout.FlexibleSpace();
|
||||||
|
}
|
||||||
|
GUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
GUILayout.Space(6);
|
||||||
|
|
||||||
|
|
||||||
|
regexList.elementHeightCallback = (i) => EditorGUIUtility.singleLineHeight + 6;
|
||||||
|
regexList.drawElementCallback = (rect, index, active, focused) =>
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= UIElementRegexConfigs.Count) return;
|
||||||
|
var item = UIElementRegexConfigs[index];
|
||||||
|
|
||||||
|
rect.y += 2;
|
||||||
|
float lh = EditorGUIUtility.singleLineHeight;
|
||||||
|
float padding = 6f;
|
||||||
|
|
||||||
|
|
||||||
|
float leftWidth = rect.width * 0.80f;
|
||||||
|
Rect regexRect = new Rect(rect.x, rect.y, leftWidth - 8, lh);
|
||||||
|
EditorGUI.BeginChangeCheck();
|
||||||
|
string newRegex = EditorGUI.TextField(regexRect, item.uiElementRegex);
|
||||||
|
if (EditorGUI.EndChangeCheck())
|
||||||
|
{
|
||||||
|
item.uiElementRegex = newRegex;
|
||||||
|
RefreshLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float rightX = rect.x + leftWidth + 8;
|
||||||
|
float rightWidth = rect.width - leftWidth - 8;
|
||||||
|
Rect btnRect = new Rect(rightX, rect.y, Math.Min(180, rightWidth), lh);
|
||||||
|
|
||||||
|
string btnLabel = string.IsNullOrEmpty(item.componentType) ? "(选择类型)" : item.componentType;
|
||||||
|
if (GUI.Button(btnRect, btnLabel, EditorStyles.popup))
|
||||||
|
{
|
||||||
|
var opts = CollectComponentTypeNamesFallback();
|
||||||
|
Rect anchor = new Rect(btnRect.x, btnRect.y + btnRect.height, Math.Min(360f, Mathf.Max(btnRect.width, 200f)), btnRect.height);
|
||||||
|
|
||||||
|
SearchablePopup.Show(anchor, opts, Math.Max(0, opts.IndexOf(item.componentType)), (selIndex) =>
|
||||||
|
{
|
||||||
|
if (selIndex >= 0 && selIndex < opts.Count)
|
||||||
|
{
|
||||||
|
item.componentType = opts[selIndex];
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
regexList.DoLayoutList();
|
||||||
|
|
||||||
|
EditorGUILayout.EndVertical();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> cacheFilterType;
|
||||||
|
|
||||||
|
|
||||||
|
private List<string> CollectComponentTypeNamesFallback()
|
||||||
|
{
|
||||||
|
if (cacheFilterType == null)
|
||||||
|
{
|
||||||
|
cacheFilterType = AlicizaX.Utility.Assembly.GetTypes()
|
||||||
|
.Where(m => !m.FullName.Contains("Editor"))
|
||||||
|
.Where(x => !x.IsAbstract || x.IsInterface)
|
||||||
|
.Where(x => !x.IsGenericTypeDefinition)
|
||||||
|
.Where(x => !x.IsSubclassOf(typeof(UIHolderObjectBase)))
|
||||||
|
.Where(x => x.IsSubclassOf(typeof(Component)))
|
||||||
|
.Where(x => !x.FullName.Contains("YooAsset"))
|
||||||
|
.Where(x => !x.FullName.Contains(("Unity.VisualScripting")))
|
||||||
|
.Where(x => !x.FullName.Contains(("Cysharp.Threading")))
|
||||||
|
.Where(x => !x.FullName.Contains(("UnityEngine.Rendering.UI.Debug")))
|
||||||
|
.Where(x => !x.FullName.Contains(("Unity.PerformanceTesting")))
|
||||||
|
.Where(x => !x.FullName.Contains(("UnityEngine.TestTools")))
|
||||||
|
.Select(x => x.Name).ToList();
|
||||||
|
|
||||||
|
cacheFilterType.Add(typeof(GameObject).Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheFilterType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadDefault()
|
||||||
|
{
|
||||||
|
string defaultPath = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var f = typeof(UIGlobalPath).GetField("DefaultComPath", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
|
||||||
|
if (f != null) defaultPath = f.GetValue(null) as string;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(defaultPath)) defaultPath = "Assets/uielementconfig.txt";
|
||||||
|
if (!File.Exists(defaultPath))
|
||||||
|
{
|
||||||
|
EditorUtility.DisplayDialog("加载默认", $"未找到默认文件:{defaultPath}", "OK");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string txt = File.ReadAllText(defaultPath);
|
||||||
|
var list = JsonConvert.DeserializeObject<List<UIEelementRegexData>>(txt);
|
||||||
|
if (list != null)
|
||||||
|
{
|
||||||
|
UIElementRegexConfigs = list;
|
||||||
|
regexList.list = UIElementRegexConfigs;
|
||||||
|
RefreshLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ImportConfig(TextAsset text)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var list = JsonConvert.DeserializeObject<List<UIEelementRegexData>>(text.text);
|
||||||
|
if (list != null)
|
||||||
|
{
|
||||||
|
UIElementRegexConfigs = list;
|
||||||
|
regexList.list = UIElementRegexConfigs;
|
||||||
|
RefreshLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogException(ex);
|
||||||
|
EditorUtility.DisplayDialog("错误", "导入失败,请查看控制台", "OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExportConfig()
|
||||||
|
{
|
||||||
|
string json = JsonConvert.SerializeObject(UIElementRegexConfigs, Formatting.Indented);
|
||||||
|
string path = EditorUtility.SaveFilePanel("导出 UI 元素配置为 JSON", Application.dataPath, "uielementconfig", "txt");
|
||||||
|
if (!string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
File.WriteAllText(path, json);
|
||||||
|
AssetDatabase.Refresh();
|
||||||
|
EditorUtility.DisplayDialog("导出完成", "已导出", "OK");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void RefreshLabel()
|
private void RefreshLabel()
|
||||||
{
|
{
|
||||||
previewLabel = $"{UIGenerateCommonData.GeneratePrefix}_UITestWindow";
|
previewLabel = $"{UIGenerateCommonData.GeneratePrefix}_UITestWindow";
|
||||||
previewCompLabel = $"{UIGenerateCommonData.ArrayComSplitName}Text{UIGenerateCommonData.ComCheckSplitName}Img" +
|
previewCompLabel = $"{UIGenerateCommonData.ArrayComSplitName}Text{UIGenerateCommonData.ComCheckSplitName}Img{UIGenerateCommonData.ComCheckEndName}Test{UIGenerateCommonData.ArrayComSplitName}0";
|
||||||
$"{UIGenerateCommonData.ComCheckEndName}Test{UIGenerateCommonData.ArrayComSplitName}0";
|
Repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
[TabGroup("UI元素映射")]
|
private void SaveConfig()
|
||||||
[Sirenix.OdinInspector.Button("加载默认")]
|
|
||||||
private void LoadDefaultConfig()
|
|
||||||
{
|
{
|
||||||
const string Path = UIGlobalPath.DefaultComPath;
|
UIGenerateCommonData.ExcludeKeywords = excludeKeywordsList.ToArray();
|
||||||
string text = System.IO.File.ReadAllText(Path);
|
|
||||||
UIElementRegexConfigs = JsonConvert.DeserializeObject<List<UIEelementRegexData>>(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TabGroup("UI元素映射")]
|
|
||||||
[Sirenix.OdinInspector.Button("导出")]
|
|
||||||
private void ExportConfig()
|
|
||||||
{
|
|
||||||
var json = JsonConvert.SerializeObject(UIElementRegexConfigs);
|
|
||||||
System.IO.File.WriteAllText("Assets/uielementconfig.txt", json);
|
|
||||||
AssetDatabase.Refresh();
|
|
||||||
Debug.Log("Export UIElements Finished");
|
|
||||||
}
|
|
||||||
|
|
||||||
[TabGroup("UI元素映射")]
|
|
||||||
[Sirenix.OdinInspector.Button("导入")]
|
|
||||||
private void ImportConfig(TextAsset text)
|
|
||||||
{
|
|
||||||
UIElementRegexConfigs = JsonConvert.DeserializeObject<List<UIEelementRegexData>>(text.text);
|
|
||||||
Debug.Log("Import UIElements Finished");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected override void OnDisable()
|
|
||||||
{
|
|
||||||
base.OnDisable();
|
|
||||||
uiGenerateConfiguration.UIGenerateCommonData = UIGenerateCommonData;
|
uiGenerateConfiguration.UIGenerateCommonData = UIGenerateCommonData;
|
||||||
uiGenerateConfiguration.UIScriptGenerateConfig = UIScriptGenerateConfig;
|
|
||||||
uiGenerateConfiguration.UIElementRegexConfigs = UIElementRegexConfigs;
|
uiGenerateConfiguration.UIElementRegexConfigs = UIElementRegexConfigs;
|
||||||
EditorUtility.SetDirty(uiGenerateConfiguration);
|
uiGenerateConfiguration.UIScriptGenerateConfigs = UIScriptGenerateConfigs;
|
||||||
AssetDatabase.SaveAssets();
|
|
||||||
UIGenerateConfiguration.Save();
|
UIGenerateConfiguration.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDestroy()
|
private void OnDisable() => SaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SearchablePopup : PopupWindowContent
|
||||||
|
{
|
||||||
|
private readonly List<string> allItems;
|
||||||
|
private List<string> filtered;
|
||||||
|
private readonly Action<int> onSelect;
|
||||||
|
private int currentIndex;
|
||||||
|
private string search = "";
|
||||||
|
private Vector2 scroll;
|
||||||
|
|
||||||
|
private static GUIStyle searchFieldStyle;
|
||||||
|
private static GUIStyle cancelStyle;
|
||||||
|
private static GUIStyle rowStyle;
|
||||||
|
private static GUIStyle selectedRowStyle;
|
||||||
|
private const float ROW_HEIGHT = 20f;
|
||||||
|
|
||||||
|
private SearchablePopup(List<string> items, int currentIndex, Action<int> onSelect)
|
||||||
{
|
{
|
||||||
base.OnDestroy();
|
this.allItems = items ?? new List<string>();
|
||||||
uiGenerateConfiguration.UIGenerateCommonData = UIGenerateCommonData;
|
this.filtered = new List<string>(this.allItems);
|
||||||
uiGenerateConfiguration.UIScriptGenerateConfig = UIScriptGenerateConfig;
|
this.currentIndex = Mathf.Clamp(currentIndex, -1, this.allItems.Count - 1);
|
||||||
uiGenerateConfiguration.UIElementRegexConfigs = UIElementRegexConfigs;
|
this.onSelect = onSelect;
|
||||||
EditorUtility.SetDirty(uiGenerateConfiguration);
|
}
|
||||||
AssetDatabase.SaveAssets();
|
|
||||||
UIGenerateConfiguration.Save();
|
public static void Show(Rect anchorRect, List<string> items, int currentIndex, Action<int> onSelect)
|
||||||
|
{
|
||||||
|
PopupWindow.Show(anchorRect, new SearchablePopup(items, currentIndex, onSelect));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Vector2 GetWindowSize() => new Vector2(360, 320);
|
||||||
|
|
||||||
|
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));
|
||||||
|
if (GUILayout.Button("", cancelStyle, GUILayout.Width(18)))
|
||||||
|
{
|
||||||
|
search = "";
|
||||||
|
GUI.FocusControl("SearchField");
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
|
||||||
|
FilterList(search);
|
||||||
|
|
||||||
|
|
||||||
|
HandleKeyboard();
|
||||||
|
|
||||||
|
|
||||||
|
scroll = EditorGUILayout.BeginScrollView(scroll);
|
||||||
|
for (int i = 0; i < filtered.Count; i++)
|
||||||
|
{
|
||||||
|
bool selected = (i == currentIndex);
|
||||||
|
var style = selected ? selectedRowStyle : rowStyle;
|
||||||
|
Rect r = GUILayoutUtility.GetRect(new GUIContent(filtered[i]), style, GUILayout.Height(ROW_HEIGHT), GUILayout.ExpandWidth(true));
|
||||||
|
if (Event.current.type == EventType.Repaint)
|
||||||
|
style.Draw(r, filtered[i], false, false, selected, false);
|
||||||
|
|
||||||
|
if (Event.current.type == EventType.MouseDown && r.Contains(Event.current.mousePosition))
|
||||||
|
{
|
||||||
|
Select(filtered[i]);
|
||||||
|
Event.current.Use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleKeyboard()
|
||||||
|
{
|
||||||
|
var e = Event.current;
|
||||||
|
if (e.type != EventType.KeyDown) return;
|
||||||
|
|
||||||
|
if (e.keyCode == KeyCode.DownArrow)
|
||||||
|
{
|
||||||
|
currentIndex = Mathf.Min(currentIndex + 1, filtered.Count - 1);
|
||||||
|
e.Use();
|
||||||
|
editorWindow.Repaint();
|
||||||
|
}
|
||||||
|
else if (e.keyCode == KeyCode.UpArrow)
|
||||||
|
{
|
||||||
|
currentIndex = Mathf.Max(currentIndex - 1, 0);
|
||||||
|
e.Use();
|
||||||
|
editorWindow.Repaint();
|
||||||
|
}
|
||||||
|
else if (e.keyCode == KeyCode.Return || e.keyCode == KeyCode.KeypadEnter)
|
||||||
|
{
|
||||||
|
if (filtered.Count > 0 && currentIndex >= 0 && currentIndex < filtered.Count)
|
||||||
|
Select(filtered[currentIndex]);
|
||||||
|
e.Use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FilterList(string keyword)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(keyword))
|
||||||
|
filtered = new List<string>(allItems);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string lower = keyword.ToLowerInvariant();
|
||||||
|
filtered = allItems.Where(i => i != null && i.ToLowerInvariant().Contains(lower)).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filtered.Count == 0) currentIndex = -1;
|
||||||
|
else currentIndex = Mathf.Clamp(currentIndex, 0, filtered.Count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Select(string item)
|
||||||
|
{
|
||||||
|
int originalIndex = allItems.IndexOf(item);
|
||||||
|
if (originalIndex >= 0)
|
||||||
|
onSelect?.Invoke(originalIndex);
|
||||||
|
|
||||||
|
editorWindow.Close();
|
||||||
|
GUIUtility.ExitGUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitStyles()
|
||||||
|
{
|
||||||
|
if (searchFieldStyle == null)
|
||||||
|
searchFieldStyle = GUI.skin.FindStyle("ToolbarSeachTextField") ?? EditorStyles.toolbarSearchField;
|
||||||
|
if (cancelStyle == null)
|
||||||
|
cancelStyle = GUI.skin.FindStyle("ToolbarSeachCancelButton") ?? EditorStyles.toolbarButton;
|
||||||
|
|
||||||
|
if (rowStyle == null)
|
||||||
|
{
|
||||||
|
rowStyle = new GUIStyle("PR Label") { alignment = TextAnchor.MiddleLeft, padding = new RectOffset(6, 6, 2, 2) };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedRowStyle == null)
|
||||||
|
{
|
||||||
|
selectedRowStyle = new GUIStyle(rowStyle) { normal = { background = Texture2D.grayTexture } };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
Editor/UI/IUIGeneratorHelper.cs
Normal file
31
Editor/UI/IUIGeneratorHelper.cs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace AlicizaX.UI.Editor
|
||||||
|
{
|
||||||
|
public interface IUIGeneratorHelper
|
||||||
|
{
|
||||||
|
string GetPrivateComponentByNameRule(string regexName, string componetName, EBindType bindType);
|
||||||
|
|
||||||
|
string GetPublicComponentByNameRule(string variableName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DefaultUIGeneratorHelper : IUIGeneratorHelper
|
||||||
|
{
|
||||||
|
public string GetPrivateComponentByNameRule(string regexName, string componentName, EBindType bindType)
|
||||||
|
{
|
||||||
|
string endPrefix = bindType == EBindType.ListCom ? "List" : string.Empty;
|
||||||
|
int endNameIndex = componentName.IndexOf(
|
||||||
|
UIGenerateConfiguration.Instance.UIGenerateCommonData.ComCheckEndName,
|
||||||
|
StringComparison.Ordinal);
|
||||||
|
|
||||||
|
string componentSuffix = endNameIndex >= 0 ? componentName.Substring(endNameIndex + 1) : componentName;
|
||||||
|
|
||||||
|
return $"m{regexName}{componentSuffix}{endPrefix}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetPublicComponentByNameRule(string variableName)
|
||||||
|
{
|
||||||
|
return variableName.Substring(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Editor/UI/IUIGeneratorHelper.cs.meta
Normal file
3
Editor/UI/IUIGeneratorHelper.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2df48f035545426a83a384df3411755a
|
||||||
|
timeCreated: 1762516569
|
||||||
80
Editor/UI/Inspector/UIHolderObjectBaseInspector.cs
Normal file
80
Editor/UI/Inspector/UIHolderObjectBaseInspector.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditorInternal;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using AlicizaX.UI.Runtime;
|
||||||
|
|
||||||
|
[CustomEditor(typeof(UIHolderObjectBase), true)]
|
||||||
|
public class UIHolderObjectBaseEditor : Editor
|
||||||
|
{
|
||||||
|
private SerializedProperty[] serializedProperties;
|
||||||
|
private Dictionary<string, ReorderableList> reorderableDic = new Dictionary<string, ReorderableList>();
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
var fields = target.GetType().GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
serializedProperties = new SerializedProperty[fields.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < fields.Length; i++)
|
||||||
|
{
|
||||||
|
SerializedProperty prop = serializedObject.FindProperty(fields[i].Name);
|
||||||
|
if (prop != null)
|
||||||
|
{
|
||||||
|
serializedProperties[i] = prop;
|
||||||
|
|
||||||
|
|
||||||
|
if (prop.isArray)
|
||||||
|
{
|
||||||
|
string arrayElementTypeName = prop.GetArrayElementAtIndex(0).type;
|
||||||
|
|
||||||
|
ReorderableList reorderableList = new ReorderableList(serializedObject, prop, true, true, true, true);
|
||||||
|
reorderableList.drawElementCallback = (rect, index, isActive, isFocused) => DrawElementCallback(rect, index, prop, isActive, isFocused);
|
||||||
|
reorderableList.drawHeaderCallback = (rect) => DrawHeaderCallback(rect, arrayElementTypeName);
|
||||||
|
|
||||||
|
reorderableDic.Add(prop.propertyPath, reorderableList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
serializedObject.Update();
|
||||||
|
EditorGUI.BeginDisabledGroup(true);
|
||||||
|
|
||||||
|
for (int i = 0; i < serializedProperties.Length; i++)
|
||||||
|
{
|
||||||
|
var property = serializedProperties[i];
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
if (property.isArray && reorderableDic.TryGetValue(property.propertyPath, out var reorderableList))
|
||||||
|
{
|
||||||
|
reorderableList.DoLayoutList();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorGUILayout.PropertyField(property, GUIContent.none, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void DrawElementCallback(Rect rect, int index, SerializedProperty arrayProperty, bool isActive, bool isFocused)
|
||||||
|
{
|
||||||
|
var element = arrayProperty.GetArrayElementAtIndex(index);
|
||||||
|
EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, GUIContent.none);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void DrawHeaderCallback(Rect rect, string arrayElementTypeName)
|
||||||
|
{
|
||||||
|
string arrayTypeName = $"{arrayElementTypeName}[]";
|
||||||
|
EditorGUI.LabelField(rect, arrayTypeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Editor/UI/Inspector/UIHolderObjectBaseInspector.cs.meta
Normal file
3
Editor/UI/Inspector/UIHolderObjectBaseInspector.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f03f3b9e96e44b858fa231e9832646ca
|
||||||
|
timeCreated: 1762514196
|
||||||
@ -2,98 +2,84 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AlicizaX.Editor.Setting;
|
|
||||||
using AlicizaX;
|
using AlicizaX;
|
||||||
|
using AlicizaX.Editor.Setting;
|
||||||
using AlicizaX.UI.Runtime;
|
using AlicizaX.UI.Runtime;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Sirenix.OdinInspector;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.Serialization;
|
using UnityEditor;
|
||||||
|
|
||||||
namespace AlicizaX.UI.Editor
|
namespace AlicizaX.UI.Editor
|
||||||
{
|
{
|
||||||
[AlicizaX.Editor.Setting.FilePath("ProjectSettings/UIGenerateConfiguration.asset")]
|
[AlicizaX.Editor.Setting.FilePath("ProjectSettings/UIGenerateConfiguration.asset")]
|
||||||
internal class UIGenerateConfiguration : ScriptableSingleton<UIGenerateConfiguration>
|
internal class UIGenerateConfiguration : AlicizaX.Editor.Setting.ScriptableSingleton<UIGenerateConfiguration>
|
||||||
{
|
{
|
||||||
public UIGenerateCommonData UIGenerateCommonData = new UIGenerateCommonData();
|
[Header("通用生成配置")] public UIGenerateCommonData UIGenerateCommonData = new UIGenerateCommonData();
|
||||||
public UIScriptGenerateConfig UIScriptGenerateConfig = new UIScriptGenerateConfig();
|
|
||||||
public List<UIEelementRegexData> UIElementRegexConfigs = new List<UIEelementRegexData>();
|
[Header("UI生成规则(根据正则匹配)")] public List<UIEelementRegexData> UIElementRegexConfigs = new List<UIEelementRegexData>();
|
||||||
|
|
||||||
|
[Header("UI脚本生成配置(支持多个项目)")] public List<UIScriptGenerateData> UIScriptGenerateConfigs = new List<UIScriptGenerateData>();
|
||||||
|
|
||||||
|
[Header("UI脚本生成辅助类")] public string UIScriptGeneratorHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Serializable]
|
[Serializable]
|
||||||
public class UIGenerateCommonData
|
public class UIGenerateCommonData
|
||||||
{
|
{
|
||||||
[LabelText("组件检查分隔符")] public string ComCheckSplitName = "#";
|
[Header("命名规则")] [Tooltip("组件检查分隔符,例如:Button#Close")]
|
||||||
[LabelText("组件结尾分隔符")] public string ComCheckEndName = "@";
|
public string ComCheckSplitName = "#";
|
||||||
[LabelText("数组组件检查分隔符")] public string ArrayComSplitName = "*";
|
|
||||||
[LabelText("生成脚本前缀")] public string GeneratePrefix = "ui";
|
|
||||||
[LabelText("排除表")] public string[] ExcludeKeywords = { "ViewHolder" };
|
|
||||||
|
|
||||||
[ShowInInspector] [LabelText("生成路径拼接")]
|
[Tooltip("组件结尾分隔符,例如:@End")] public string ComCheckEndName = "@";
|
||||||
public Dictionary<string, string> CombineWords = new Dictionary<string, string>
|
|
||||||
|
[Tooltip("数组组件检查分隔符,例如:*Item")] public string ArrayComSplitName = "*";
|
||||||
|
|
||||||
|
[Tooltip("生成脚本前缀")] public string GeneratePrefix = "ui";
|
||||||
|
|
||||||
|
[Tooltip("排除的关键字(匹配则不生成)")] public string[] ExcludeKeywords = { "ViewHolder" };
|
||||||
|
|
||||||
|
[Tooltip("路径拼接映射,如:Window -> Window 文件夹")]
|
||||||
|
public List<StringPair> CombineWords = new List<StringPair>()
|
||||||
{
|
{
|
||||||
{ "Window", "Window" },
|
new StringPair("Window", "Window"),
|
||||||
{ "ViewHolder", "ViewHolder" },
|
new StringPair("ViewHolder", "ViewHolder"),
|
||||||
{ "Widget", "Widget" },
|
new StringPair("Widget", "Widget"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[System.Serializable]
|
[Serializable]
|
||||||
public class UIEelementRegexData
|
public class UIEelementRegexData
|
||||||
{
|
{
|
||||||
public string uiElementRegex;
|
[Tooltip("匹配UI元素名称的正则表达式")] public string uiElementRegex;
|
||||||
|
|
||||||
|
[Tooltip("匹配到的UI组件类型")] public string componentType;
|
||||||
[ShowInInspector] [ValueDropdown("GetFilteredTypeList", ExpandAllMenuItems = false)]
|
|
||||||
public string componentType;
|
|
||||||
|
|
||||||
|
|
||||||
private static List<string> cacheFilterType;
|
|
||||||
|
|
||||||
public IEnumerable<string> GetFilteredTypeList()
|
|
||||||
{
|
|
||||||
if (cacheFilterType == null)
|
|
||||||
{
|
|
||||||
cacheFilterType = AlicizaX.Utility.Assembly.GetTypes()
|
|
||||||
.Where(m => !m.FullName.Contains("Editor"))
|
|
||||||
.Where(x => !x.IsAbstract || x.IsInterface)
|
|
||||||
.Where(x => !x.IsGenericTypeDefinition)
|
|
||||||
.Where(x => !x.IsSubclassOf(typeof(UIHolderObjectBase)))
|
|
||||||
.Where(x => x.IsSubclassOf(typeof(Component)))
|
|
||||||
.Where(x => !x.FullName.Contains("YooAsset"))
|
|
||||||
.Where(x => !x.FullName.Contains(("Unity.VisualScripting")))
|
|
||||||
.Where(x => !x.FullName.Contains(("Cysharp.Threading")))
|
|
||||||
.Where(x => !x.FullName.Contains(("UnityEngine.Rendering.UI.Debug")))
|
|
||||||
.Where(x => !x.FullName.Contains(("Unity.PerformanceTesting")))
|
|
||||||
.Where(x => !x.FullName.Contains(("UnityEngine.TestTools")))
|
|
||||||
.Select(x => x.Name).ToList();
|
|
||||||
|
|
||||||
cacheFilterType.Add(typeof(GameObject).Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cacheFilterType;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
[System.Serializable]
|
|
||||||
public class UIScriptGenerateConfig
|
|
||||||
{
|
|
||||||
[BoxGroup("主工程")] public UIScriptGenerateData MainProjectUIScriptGenerateData = new UIScriptGenerateData();
|
|
||||||
[BoxGroup("热更工程")] public UIScriptGenerateData HotFixProjectUIScriptGenerateData = new UIScriptGenerateData();
|
|
||||||
}
|
|
||||||
|
|
||||||
[System.Serializable]
|
|
||||||
public class UIScriptGenerateData
|
public class UIScriptGenerateData
|
||||||
{
|
{
|
||||||
public string NameSpace;
|
[Header("项目识别信息")] [Tooltip("该UI工程的名称(例如:MainProject, HotFix, EditorUI)")]
|
||||||
|
public string ProjectName = "MainProject";
|
||||||
|
|
||||||
[Sirenix.OdinInspector.FolderPath(RequireExistingPath = true, AbsolutePath = false)]
|
[Tooltip("该UI工程所属命名空间")] public string NameSpace = "Game.UI";
|
||||||
public string GenerateHolderCodePath;
|
|
||||||
|
|
||||||
[Sirenix.OdinInspector.FolderPath(RequireExistingPath = true, AbsolutePath = false)]
|
[Header("路径设置")] [Tooltip("生成的UI脚本路径(相对Assets)")]
|
||||||
public string UIPrefabRootPath;
|
public string GenerateHolderCodePath = "Assets/Scripts/UI/Generated";
|
||||||
|
|
||||||
public EUIResLoadType LoadType;
|
[Tooltip("UI Prefab根目录")] public string UIPrefabRootPath = "Assets/Art/UI/Prefabs";
|
||||||
|
|
||||||
|
[Header("加载类型")] [Tooltip("UI资源加载方式(本地 / YooAsset / Addressable等)")]
|
||||||
|
public EUIResLoadType LoadType = EUIResLoadType.Resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class StringPair
|
||||||
|
{
|
||||||
|
public string Key;
|
||||||
|
public string Value;
|
||||||
|
|
||||||
|
public StringPair(string key, string value)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,10 @@
|
|||||||
using Sirenix.OdinInspector;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Cysharp.Threading.Tasks;
|
using Cysharp.Threading.Tasks;
|
||||||
using Sirenix.Utilities;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
namespace AlicizaX.UI.Runtime
|
namespace AlicizaX.UI.Runtime
|
||||||
{
|
{
|
||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
[HideMonoScript]
|
|
||||||
public abstract class UIHolderObjectBase : UnityEngine.MonoBehaviour
|
public abstract class UIHolderObjectBase : UnityEngine.MonoBehaviour
|
||||||
{
|
{
|
||||||
public Action OnWindowInitEvent;
|
public Action OnWindowInitEvent;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user