842 lines
28 KiB
C#
842 lines
28 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using AlicizaX.UI;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using Object = UnityEngine.Object;
|
||
|
||
namespace AlicizaX.UI.Editor
|
||
{
|
||
[CustomEditor(typeof(RecyclerView))]
|
||
public class RecyclerViewEditor : UnityEditor.Editor
|
||
{
|
||
#region Constants
|
||
|
||
private const string NoneOptionName = "None";
|
||
private const string VerticalScrollbarPath = "Packages/com.alicizax.unity.ui.extension/Editor/RecyclerView/Res/vertical.prefab";
|
||
private const string HorizontalScrollbarPath = "Packages/com.alicizax.unity.ui.extension/Editor/RecyclerView/Res/horizontal.prefab";
|
||
|
||
#endregion
|
||
|
||
#region Serialized Properties - Layout Manager
|
||
|
||
private SerializedProperty _layoutManagerTypeName;
|
||
private SerializedProperty _layoutManager;
|
||
private List<string> _layoutTypeNames = new List<string>();
|
||
private int _selectedLayoutIndex;
|
||
|
||
#endregion
|
||
|
||
#region Serialized Properties - Scroller
|
||
|
||
private SerializedProperty _scroll;
|
||
private SerializedProperty _scroller;
|
||
private SerializedProperty _scrollerTypeName;
|
||
private List<Type> _scrollerTypes = new List<Type>();
|
||
private List<string> _scrollerTypeNames = new List<string>();
|
||
private int _selectedScrollerIndex;
|
||
|
||
#endregion
|
||
|
||
#region Serialized Properties - Base Settings
|
||
|
||
private SerializedProperty _direction;
|
||
private SerializedProperty _alignment;
|
||
private SerializedProperty _content;
|
||
private SerializedProperty _spacing;
|
||
private SerializedProperty _padding;
|
||
private SerializedProperty _snap;
|
||
private SerializedProperty _scrollSpeed;
|
||
private SerializedProperty _wheelSpeed;
|
||
|
||
#endregion
|
||
|
||
#region Serialized Properties - Templates & Scrollbar
|
||
|
||
private SerializedProperty _templates;
|
||
private SerializedProperty _showScrollBar;
|
||
private SerializedProperty _scrollbar;
|
||
|
||
#endregion
|
||
|
||
#region Unity Lifecycle
|
||
|
||
private void OnEnable()
|
||
{
|
||
// 先绑定所有 SerializedProperty
|
||
InitializeLayoutManagerProperties();
|
||
InitializeScrollerProperties();
|
||
InitializeBaseProperties();
|
||
InitializeTemplateProperties();
|
||
|
||
// 确保序列化对象是最新的
|
||
serializedObject.Update();
|
||
|
||
// 如果 layoutManager 的 managedReferenceValue 丢失但有记录的 typeName,则尝试恢复实例
|
||
RestoreLayoutManagerFromTypeNameIfMissing();
|
||
|
||
// 如果 scroller 组件丢失但有记录的 typeName,则尝试恢复组件到目标 GameObject 上
|
||
RestoreScrollerFromTypeNameIfMissing();
|
||
|
||
// 应用修改(若有)
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Initialization
|
||
|
||
private void InitializeLayoutManagerProperties()
|
||
{
|
||
_layoutManagerTypeName = serializedObject.FindProperty("_layoutManagerTypeName");
|
||
_layoutManager = serializedObject.FindProperty("layoutManager");
|
||
RefreshLayoutTypes();
|
||
}
|
||
|
||
private void InitializeScrollerProperties()
|
||
{
|
||
_scroll = serializedObject.FindProperty("scroll");
|
||
_scroller = serializedObject.FindProperty("scroller");
|
||
_scrollerTypeName = serializedObject.FindProperty("_scrollerTypeName");
|
||
RefreshScrollerTypes();
|
||
SyncExistingScroller();
|
||
}
|
||
|
||
private void InitializeBaseProperties()
|
||
{
|
||
_direction = serializedObject.FindProperty("direction");
|
||
_alignment = serializedObject.FindProperty("alignment");
|
||
_content = serializedObject.FindProperty("content");
|
||
_spacing = serializedObject.FindProperty("spacing");
|
||
_padding = serializedObject.FindProperty("padding");
|
||
_snap = serializedObject.FindProperty("snap");
|
||
_scrollSpeed = serializedObject.FindProperty("scrollSpeed");
|
||
_wheelSpeed = serializedObject.FindProperty("wheelSpeed");
|
||
}
|
||
|
||
private void InitializeTemplateProperties()
|
||
{
|
||
_templates = serializedObject.FindProperty("templates");
|
||
_showScrollBar = serializedObject.FindProperty("showScrollBar");
|
||
_scrollbar = serializedObject.FindProperty("scrollbar");
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Inspector GUI
|
||
|
||
public override void OnInspectorGUI()
|
||
{
|
||
serializedObject.Update();
|
||
bool isPlaying = Application.isPlaying;
|
||
|
||
DrawLayoutManagerSection(isPlaying);
|
||
DrawBaseSettingsSection(isPlaying);
|
||
DrawScrollerSection(isPlaying);
|
||
DrawTemplatesSection();
|
||
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Layout Manager Section
|
||
|
||
private void DrawLayoutManagerSection(bool isPlaying)
|
||
{
|
||
EditorGUILayout.BeginVertical("box");
|
||
{
|
||
EditorGUILayout.LabelField("Layout Manager", EditorStyles.boldLabel);
|
||
|
||
using (new EditorGUI.DisabledScope(isPlaying))
|
||
{
|
||
int newIndex = EditorGUILayout.Popup("Layout Type", _selectedLayoutIndex, _layoutTypeNames.ToArray());
|
||
if (newIndex != _selectedLayoutIndex)
|
||
{
|
||
_selectedLayoutIndex = newIndex;
|
||
UpdateLayoutManager(newIndex);
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
}
|
||
|
||
if (_layoutManager.managedReferenceValue != null)
|
||
{
|
||
EditorGUILayout.Space(3);
|
||
DrawManagedReferenceProperties(_layoutManager);
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.HelpBox("Please select a Layout Manager", MessageType.Error);
|
||
}
|
||
}
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
private void RefreshLayoutTypes()
|
||
{
|
||
_layoutTypeNames.Clear();
|
||
_layoutTypeNames.Add(NoneOptionName);
|
||
|
||
var types = AlicizaX.Utility.Assembly.GetRuntimeTypes(typeof(ILayoutManager));
|
||
foreach (var type in types)
|
||
{
|
||
if (!typeof(MonoBehaviour).IsAssignableFrom(type))
|
||
{
|
||
_layoutTypeNames.Add(type.FullName);
|
||
}
|
||
}
|
||
|
||
_selectedLayoutIndex = Mathf.Clamp(
|
||
_layoutTypeNames.IndexOf(_layoutManagerTypeName.stringValue),
|
||
0,
|
||
_layoutTypeNames.Count - 1
|
||
);
|
||
}
|
||
|
||
private void UpdateLayoutManager(int selectedIndex)
|
||
{
|
||
try
|
||
{
|
||
if (selectedIndex == 0)
|
||
{
|
||
ClearLayoutManager();
|
||
return;
|
||
}
|
||
|
||
if (!IsValidLayoutIndex(selectedIndex))
|
||
{
|
||
Debug.LogError($"Invalid layout index: {selectedIndex}");
|
||
ClearLayoutManager();
|
||
return;
|
||
}
|
||
|
||
string typeName = _layoutTypeNames[selectedIndex];
|
||
Type type = AlicizaX.Utility.Assembly.GetType(typeName);
|
||
|
||
if (type != null && typeof(ILayoutManager).IsAssignableFrom(type))
|
||
{
|
||
_layoutManager.managedReferenceValue = Activator.CreateInstance(type);
|
||
_layoutManagerTypeName.stringValue = typeName;
|
||
_selectedLayoutIndex = selectedIndex;
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError($"Invalid layout type: {typeName}");
|
||
ClearLayoutManager();
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"Layout Manager Error: {e.Message}");
|
||
ClearLayoutManager();
|
||
}
|
||
}
|
||
|
||
private void ClearLayoutManager()
|
||
{
|
||
_layoutManager.managedReferenceValue = null;
|
||
_layoutManagerTypeName.stringValue = "";
|
||
_selectedLayoutIndex = 0;
|
||
}
|
||
|
||
private bool IsValidLayoutIndex(int index)
|
||
{
|
||
return index >= 0 && index < _layoutTypeNames.Count;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Base Settings Section
|
||
|
||
private void DrawBaseSettingsSection(bool isPlaying)
|
||
{
|
||
EditorGUILayout.BeginVertical("box");
|
||
{
|
||
EditorGUILayout.LabelField("Base Settings", EditorStyles.boldLabel);
|
||
|
||
using (new EditorGUI.DisabledScope(isPlaying))
|
||
{
|
||
EditorGUILayout.PropertyField(_direction);
|
||
EditorGUILayout.PropertyField(_alignment);
|
||
EditorGUILayout.PropertyField(_content);
|
||
}
|
||
|
||
EditorGUILayout.Space(5);
|
||
EditorGUILayout.LabelField("Spacing", EditorStyles.boldLabel);
|
||
EditorGUILayout.PropertyField(_spacing);
|
||
EditorGUILayout.PropertyField(_padding);
|
||
|
||
EditorGUILayout.Space(5);
|
||
EditorGUILayout.LabelField("Scrolling", EditorStyles.boldLabel);
|
||
|
||
using (new EditorGUI.DisabledScope(isPlaying))
|
||
{
|
||
bool previousScrollValue = _scroll.boolValue;
|
||
EditorGUILayout.PropertyField(_scroll);
|
||
|
||
if (_scroll.boolValue != previousScrollValue)
|
||
{
|
||
HandleScrollToggle();
|
||
}
|
||
}
|
||
|
||
if (_scroll.boolValue)
|
||
{
|
||
DrawScrollBarSettings(isPlaying);
|
||
}
|
||
|
||
EditorGUILayout.PropertyField(_snap);
|
||
}
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
private void DrawScrollBarSettings(bool isPlaying)
|
||
{
|
||
using (new EditorGUI.DisabledScope(isPlaying))
|
||
{
|
||
bool previousShowScrollBarValue = _showScrollBar.boolValue;
|
||
EditorGUILayout.PropertyField(_showScrollBar);
|
||
|
||
if (_showScrollBar.boolValue != previousShowScrollBarValue)
|
||
{
|
||
HandleScrollBarToggle();
|
||
}
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Scroller Section
|
||
|
||
private void DrawScrollerSection(bool isPlaying)
|
||
{
|
||
if (!_scroll.boolValue) return;
|
||
|
||
EditorGUILayout.BeginVertical("box");
|
||
{
|
||
EditorGUILayout.LabelField("Scroller Settings", EditorStyles.boldLabel);
|
||
|
||
using (new EditorGUI.DisabledScope(isPlaying))
|
||
{
|
||
int newIndex = EditorGUILayout.Popup("Scroller Type", _selectedScrollerIndex, _scrollerTypeNames.ToArray());
|
||
if (newIndex != _selectedScrollerIndex)
|
||
{
|
||
UpdateScroller(newIndex);
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
}
|
||
|
||
DrawScrollerComponentProperties();
|
||
DrawScrollerSpeedSettings();
|
||
}
|
||
EditorGUILayout.EndVertical();
|
||
}
|
||
|
||
private void DrawScrollerComponentProperties()
|
||
{
|
||
var recyclerView = target as RecyclerView;
|
||
if (recyclerView == null) return;
|
||
|
||
var scrollerComponent = recyclerView.GetComponent<IScroller>();
|
||
if (scrollerComponent != null)
|
||
{
|
||
DrawComponentProperties(scrollerComponent as MonoBehaviour, "Scroller Properties");
|
||
}
|
||
else
|
||
{
|
||
EditorGUILayout.HelpBox("Please select a Scroller type", MessageType.Error);
|
||
}
|
||
}
|
||
|
||
private void DrawScrollerSpeedSettings()
|
||
{
|
||
EditorGUILayout.Space(3);
|
||
EditorGUILayout.PropertyField(_scrollSpeed);
|
||
EditorGUILayout.PropertyField(_wheelSpeed);
|
||
}
|
||
|
||
private void RefreshScrollerTypes()
|
||
{
|
||
_scrollerTypes = TypeCache.GetTypesDerivedFrom<IScroller>()
|
||
.Where(t => t.IsSubclassOf(typeof(MonoBehaviour)))
|
||
.ToList();
|
||
|
||
_scrollerTypeNames = _scrollerTypes
|
||
.Select(t => t.FullName)
|
||
.Prepend(NoneOptionName)
|
||
.ToList();
|
||
}
|
||
|
||
private void SyncExistingScroller()
|
||
{
|
||
var recyclerView = target as RecyclerView;
|
||
if (recyclerView == null) return;
|
||
|
||
var existingScroller = recyclerView.GetComponent<IScroller>();
|
||
if (existingScroller != null)
|
||
{
|
||
_scroller.objectReferenceValue = existingScroller as MonoBehaviour;
|
||
_scrollerTypeName.stringValue = existingScroller.GetType().FullName;
|
||
_selectedScrollerIndex = _scrollerTypeNames.IndexOf(_scrollerTypeName.stringValue);
|
||
}
|
||
else
|
||
{
|
||
// 如果组件不存在,但属性里存了类型名,这里不清理 typeName(恢复逻辑会处理)
|
||
_selectedScrollerIndex = Mathf.Clamp(_scrollerTypeNames.IndexOf(_scrollerTypeName.stringValue), 0, _scrollerTypeNames.Count - 1);
|
||
}
|
||
}
|
||
|
||
private void UpdateScroller(int selectedIndex)
|
||
{
|
||
try
|
||
{
|
||
var recyclerView = target as RecyclerView;
|
||
if (recyclerView == null) return;
|
||
|
||
Undo.RecordObjects(new Object[] { recyclerView, this }, "Update Scroller");
|
||
|
||
RemoveExistingScroller(recyclerView);
|
||
|
||
if (selectedIndex == 0)
|
||
{
|
||
ClearScrollerReferences();
|
||
return;
|
||
}
|
||
|
||
AddNewScroller(recyclerView, selectedIndex);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"Scroller Error: {e}");
|
||
ClearScrollerReferences();
|
||
}
|
||
}
|
||
|
||
private void RemoveExistingScroller(RecyclerView recyclerView)
|
||
{
|
||
var oldScroller = recyclerView.GetComponent<IScroller>();
|
||
if (oldScroller != null)
|
||
{
|
||
Undo.DestroyObjectImmediate(oldScroller as MonoBehaviour);
|
||
}
|
||
}
|
||
|
||
private void AddNewScroller(RecyclerView recyclerView, int selectedIndex)
|
||
{
|
||
Type selectedType = _scrollerTypes[selectedIndex - 1];
|
||
var newScroller = Undo.AddComponent(recyclerView.gameObject, selectedType) as IScroller;
|
||
|
||
_scroller.objectReferenceValue = newScroller as MonoBehaviour;
|
||
_scrollerTypeName.stringValue = selectedType.FullName;
|
||
_selectedScrollerIndex = selectedIndex;
|
||
|
||
serializedObject.ApplyModifiedProperties();
|
||
EditorUtility.SetDirty(recyclerView);
|
||
}
|
||
|
||
private void ClearScrollerReferences()
|
||
{
|
||
_scroller.objectReferenceValue = null;
|
||
_scrollerTypeName.stringValue = "";
|
||
_selectedScrollerIndex = 0;
|
||
}
|
||
|
||
private void HandleScrollToggle()
|
||
{
|
||
if (!_scroll.boolValue)
|
||
{
|
||
RemoveScrollerComponent();
|
||
ClearScrollBar();
|
||
}
|
||
}
|
||
|
||
private void RemoveScrollerComponent()
|
||
{
|
||
var recyclerView = target as RecyclerView;
|
||
if (recyclerView == null) return;
|
||
|
||
var scrollerComponent = recyclerView.GetComponent<IScroller>();
|
||
if (scrollerComponent != null)
|
||
{
|
||
Undo.DestroyObjectImmediate(scrollerComponent as MonoBehaviour);
|
||
}
|
||
|
||
ClearScrollerReferences();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Scrollbar Handling
|
||
|
||
private void HandleScrollBarToggle()
|
||
{
|
||
if (_showScrollBar.boolValue)
|
||
{
|
||
CreateScrollBar();
|
||
}
|
||
else
|
||
{
|
||
ClearScrollBar();
|
||
}
|
||
}
|
||
|
||
private void CreateScrollBar()
|
||
{
|
||
var recyclerView = target as RecyclerView;
|
||
if (recyclerView == null) return;
|
||
|
||
Direction direction = (Direction)_direction.enumValueIndex;
|
||
string prefabPath = GetScrollbarPrefabPath(direction);
|
||
if (!string.IsNullOrEmpty(prefabPath))
|
||
{
|
||
InstantiateScrollBar(prefabPath, recyclerView.transform);
|
||
}
|
||
}
|
||
|
||
private string GetScrollbarPrefabPath(Direction direction)
|
||
{
|
||
|
||
return direction switch
|
||
{
|
||
Direction.Vertical => VerticalScrollbarPath,
|
||
Direction.Horizontal => HorizontalScrollbarPath,
|
||
_ => null
|
||
};
|
||
}
|
||
|
||
private void InstantiateScrollBar(string path, Transform parent)
|
||
{
|
||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||
if (prefab == null)
|
||
{
|
||
Debug.LogError($"Scrollbar prefab not found at path: {path}");
|
||
return;
|
||
}
|
||
|
||
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab, parent);
|
||
PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.UserAction);
|
||
|
||
_scrollbar.objectReferenceValue = instance.GetComponent<Scrollbar>();
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
|
||
private void ClearScrollBar()
|
||
{
|
||
_showScrollBar.boolValue = false;
|
||
|
||
if (_scrollbar.objectReferenceValue != null)
|
||
{
|
||
Scrollbar scrollbarComponent = _scrollbar.objectReferenceValue as Scrollbar;
|
||
_scrollbar.objectReferenceValue = null;
|
||
|
||
if (scrollbarComponent != null)
|
||
{
|
||
Object.DestroyImmediate(scrollbarComponent.gameObject);
|
||
}
|
||
}
|
||
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Templates Section
|
||
|
||
private void DrawTemplatesSection()
|
||
{
|
||
EditorGUILayout.Space(5);
|
||
EditorGUILayout.LabelField("Item Templates", EditorStyles.boldLabel);
|
||
|
||
DrawTemplatesList();
|
||
DrawDragAndDropArea();
|
||
}
|
||
|
||
private void DrawTemplatesList()
|
||
{
|
||
if (_templates == null || !_templates.isArray) return;
|
||
|
||
for (int i = 0; i < _templates.arraySize; i++)
|
||
{
|
||
DrawTemplateItem(i);
|
||
}
|
||
}
|
||
|
||
private void DrawTemplateItem(int index)
|
||
{
|
||
if (index < 0 || index >= _templates.arraySize) return;
|
||
|
||
SerializedProperty item = _templates.GetArrayElementAtIndex(index);
|
||
|
||
EditorGUILayout.BeginHorizontal();
|
||
{
|
||
using (new EditorGUI.DisabledScope(true))
|
||
{
|
||
EditorGUILayout.PropertyField(item, GUIContent.none, true);
|
||
}
|
||
|
||
if (GUILayout.Button("×", GUILayout.Width(20), GUILayout.Height(EditorGUIUtility.singleLineHeight)))
|
||
{
|
||
RemoveTemplateItem(index);
|
||
}
|
||
}
|
||
EditorGUILayout.EndHorizontal();
|
||
}
|
||
|
||
private void RemoveTemplateItem(int index)
|
||
{
|
||
if (_templates == null || index < 0 || index >= _templates.arraySize) return;
|
||
|
||
_templates.DeleteArrayElementAtIndex(index);
|
||
serializedObject.ApplyModifiedProperties();
|
||
GUIUtility.ExitGUI();
|
||
}
|
||
|
||
private void DrawDragAndDropArea()
|
||
{
|
||
Rect dropArea = GUILayoutUtility.GetRect(0f, 50f, GUILayout.ExpandWidth(true));
|
||
GUI.Box(dropArea, "Drag ViewHolder Templates Here", EditorStyles.helpBox);
|
||
|
||
HandleDragAndDrop(dropArea);
|
||
}
|
||
|
||
private void HandleDragAndDrop(Rect dropArea)
|
||
{
|
||
Event currentEvent = Event.current;
|
||
|
||
switch (currentEvent.type)
|
||
{
|
||
case EventType.DragUpdated:
|
||
case EventType.DragPerform:
|
||
if (!dropArea.Contains(currentEvent.mousePosition))
|
||
return;
|
||
|
||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||
|
||
if (currentEvent.type == EventType.DragPerform)
|
||
{
|
||
DragAndDrop.AcceptDrag();
|
||
ProcessDraggedTemplates();
|
||
currentEvent.Use();
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void ProcessDraggedTemplates()
|
||
{
|
||
foreach (Object draggedObject in DragAndDrop.objectReferences)
|
||
{
|
||
if (draggedObject is GameObject gameObject)
|
||
{
|
||
ProcessDraggedGameObject(gameObject);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void ProcessDraggedGameObject(GameObject gameObject)
|
||
{
|
||
ViewHolder viewHolder = gameObject.GetComponent<ViewHolder>();
|
||
|
||
if (viewHolder != null)
|
||
{
|
||
AddTemplate(gameObject);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogWarning($"GameObject '{gameObject.name}' must have a ViewHolder component!");
|
||
}
|
||
}
|
||
|
||
private void AddTemplate(GameObject templatePrefab)
|
||
{
|
||
if (_templates == null || templatePrefab == null) return;
|
||
|
||
if (IsTemplateDuplicate(templatePrefab))
|
||
{
|
||
Debug.LogWarning($"Template '{templatePrefab.name}' already exists in the list!");
|
||
return;
|
||
}
|
||
|
||
_templates.arraySize++;
|
||
SerializedProperty newItem = _templates.GetArrayElementAtIndex(_templates.arraySize - 1);
|
||
newItem.objectReferenceValue = templatePrefab;
|
||
|
||
serializedObject.ApplyModifiedProperties();
|
||
}
|
||
|
||
private bool IsTemplateDuplicate(GameObject templatePrefab)
|
||
{
|
||
Type templateType = templatePrefab.GetComponent<ViewHolder>().GetType();
|
||
|
||
for (int i = 0; i < _templates.arraySize; i++)
|
||
{
|
||
SerializedProperty existingItem = _templates.GetArrayElementAtIndex(i);
|
||
var existingViewHolder = existingItem.objectReferenceValue as GameObject;
|
||
|
||
if (existingViewHolder != null)
|
||
{
|
||
var existingType = existingViewHolder.GetComponent<ViewHolder>().GetType();
|
||
if (existingType == templateType)
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Helper Methods
|
||
|
||
private void DrawManagedReferenceProperties(SerializedProperty property)
|
||
{
|
||
SerializedProperty iterator = property.Copy();
|
||
bool enterChildren = true;
|
||
|
||
while (iterator.NextVisible(enterChildren))
|
||
{
|
||
enterChildren = false;
|
||
if (iterator.name == "m_Script") continue;
|
||
|
||
EditorGUILayout.PropertyField(iterator, true);
|
||
}
|
||
}
|
||
|
||
private void DrawComponentProperties(MonoBehaviour component, string header = null)
|
||
{
|
||
if (component == null) return;
|
||
|
||
EditorGUILayout.Space(3);
|
||
|
||
if (!string.IsNullOrEmpty(header))
|
||
{
|
||
EditorGUILayout.LabelField(header, EditorStyles.boldLabel);
|
||
}
|
||
|
||
SerializedObject componentSerializedObject = new SerializedObject(component);
|
||
componentSerializedObject.Update();
|
||
|
||
SerializedProperty property = componentSerializedObject.GetIterator();
|
||
bool enterChildren = true;
|
||
|
||
while (property.NextVisible(enterChildren))
|
||
{
|
||
enterChildren = false;
|
||
if (property.name == "m_Script") continue;
|
||
|
||
EditorGUILayout.PropertyField(property, true);
|
||
}
|
||
|
||
componentSerializedObject.ApplyModifiedProperties();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Restore Helpers (新增)
|
||
|
||
private void RestoreLayoutManagerFromTypeNameIfMissing()
|
||
{
|
||
try
|
||
{
|
||
if (_layoutManager == null || _layoutManagerTypeName == null) return;
|
||
|
||
// 如果 managedReferenceValue 已经存在就不必恢复
|
||
if (_layoutManager.managedReferenceValue != null) return;
|
||
|
||
string typeName = _layoutManagerTypeName.stringValue;
|
||
if (string.IsNullOrEmpty(typeName)) return;
|
||
|
||
Type type = AlicizaX.Utility.Assembly.GetType(typeName);
|
||
if (type == null)
|
||
{
|
||
Debug.LogWarning($"LayoutManager type '{typeName}' not found. Cannot restore layout manager.");
|
||
return;
|
||
}
|
||
|
||
if (!typeof(ILayoutManager).IsAssignableFrom(type))
|
||
{
|
||
Debug.LogWarning($"Type '{typeName}' does not implement ILayoutManager. Cannot restore layout manager.");
|
||
_layoutManagerTypeName.stringValue = "";
|
||
return;
|
||
}
|
||
|
||
// 实例化并赋值
|
||
var instance = Activator.CreateInstance(type);
|
||
_layoutManager.managedReferenceValue = instance;
|
||
// 尝试刷新下拉列表并更新选择索引
|
||
RefreshLayoutTypes();
|
||
_selectedLayoutIndex = Mathf.Clamp(_layoutTypeNames.IndexOf(typeName), 0, _layoutTypeNames.Count - 1);
|
||
|
||
Debug.Log($"LayoutManager restored from type name '{typeName}'.");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"Error restoring LayoutManager: {e}");
|
||
_layoutManager.managedReferenceValue = null;
|
||
_layoutManagerTypeName.stringValue = "";
|
||
}
|
||
}
|
||
|
||
private void RestoreScrollerFromTypeNameIfMissing()
|
||
{
|
||
try
|
||
{
|
||
if (_scroller == null || _scrollerTypeName == null) return;
|
||
|
||
// 如果 objectReferenceValue 已经存在就不必恢复
|
||
if (_scroller.objectReferenceValue != null) return;
|
||
|
||
string typeName = _scrollerTypeName.stringValue;
|
||
if (string.IsNullOrEmpty(typeName)) return;
|
||
|
||
Type type = AlicizaX.Utility.Assembly.GetType(typeName) ?? Type.GetType(typeName);
|
||
if (type == null)
|
||
{
|
||
Debug.LogWarning($"Scroller type '{typeName}' not found. Cannot restore scroller component.");
|
||
_scrollerTypeName.stringValue = "";
|
||
return;
|
||
}
|
||
|
||
if (!typeof(MonoBehaviour).IsAssignableFrom(type) || !typeof(IScroller).IsAssignableFrom(type))
|
||
{
|
||
Debug.LogWarning($"Type '{typeName}' is not a MonoBehaviour implementing IScroller. Cannot restore scroller.");
|
||
_scrollerTypeName.stringValue = "";
|
||
return;
|
||
}
|
||
|
||
var recyclerView = target as RecyclerView;
|
||
if (recyclerView == null) return;
|
||
|
||
// 给目标 GameObject 添加组件(使用 Undo 以支持撤销)
|
||
var newComp = Undo.AddComponent(recyclerView.gameObject, type) as MonoBehaviour;
|
||
if (newComp == null)
|
||
{
|
||
Debug.LogError($"Failed to add scroller component of type '{typeName}' to GameObject '{recyclerView.gameObject.name}'.");
|
||
_scrollerTypeName.stringValue = "";
|
||
return;
|
||
}
|
||
|
||
_scroller.objectReferenceValue = newComp;
|
||
// 刷新 scroller 类型列表并更新索引(如果存在)
|
||
RefreshScrollerTypes();
|
||
_selectedScrollerIndex = Mathf.Clamp(_scrollerTypeNames.IndexOf(typeName), 0, _scrollerTypeNames.Count - 1);
|
||
|
||
EditorUtility.SetDirty(recyclerView);
|
||
Debug.Log($"Scroller component of type '{typeName}' restored and attached to GameObject '{recyclerView.gameObject.name}'.");
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"Error restoring Scroller: {e}");
|
||
_scroller.objectReferenceValue = null;
|
||
_scrollerTypeName.stringValue = "";
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
}
|