com.alicizax.unity.ui.exten.../Editor/RecyclerView/RecyclerViewEditor.cs

842 lines
28 KiB
C#
Raw Normal View History

2025-05-30 10:32:08 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using AlicizaX.UI;
2025-12-26 14:22:46 +08:00
using UnityEditor;
using UnityEngine;
2025-05-30 10:32:08 +08:00
using UnityEngine.UI;
using Object = UnityEngine.Object;
2025-12-26 14:22:46 +08:00
namespace AlicizaX.UI.Editor
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
[CustomEditor(typeof(RecyclerView))]
public class RecyclerViewEditor : UnityEditor.Editor
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
#region Constants
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
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";
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
#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
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
#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()
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
// 先绑定所有 SerializedProperty
InitializeLayoutManagerProperties();
InitializeScrollerProperties();
InitializeBaseProperties();
InitializeTemplateProperties();
// 确保序列化对象是最新的
serializedObject.Update();
// 如果 layoutManager 的 managedReferenceValue 丢失但有记录的 typeName则尝试恢复实例
RestoreLayoutManagerFromTypeNameIfMissing();
// 如果 scroller 组件丢失但有记录的 typeName则尝试恢复组件到目标 GameObject 上
RestoreScrollerFromTypeNameIfMissing();
// 应用修改(若有)
serializedObject.ApplyModifiedProperties();
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
#endregion
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
#region Initialization
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void InitializeLayoutManagerProperties()
{
_layoutManagerTypeName = serializedObject.FindProperty("_layoutManagerTypeName");
_layoutManager = serializedObject.FindProperty("layoutManager");
RefreshLayoutTypes();
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void InitializeScrollerProperties()
{
_scroll = serializedObject.FindProperty("scroll");
_scroller = serializedObject.FindProperty("scroller");
_scrollerTypeName = serializedObject.FindProperty("_scrollerTypeName");
RefreshScrollerTypes();
SyncExistingScroller();
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
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");
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void InitializeTemplateProperties()
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
_templates = serializedObject.FindProperty("templates");
_showScrollBar = serializedObject.FindProperty("showScrollBar");
_scrollbar = serializedObject.FindProperty("scrollbar");
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
#endregion
#region Inspector GUI
public override void OnInspectorGUI()
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
serializedObject.Update();
bool isPlaying = Application.isPlaying;
DrawLayoutManagerSection(isPlaying);
DrawBaseSettingsSection(isPlaying);
DrawScrollerSection(isPlaying);
DrawTemplatesSection();
serializedObject.ApplyModifiedProperties();
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
#endregion
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
#region Layout Manager Section
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void DrawLayoutManagerSection(bool isPlaying)
{
EditorGUILayout.BeginVertical("box");
{
EditorGUILayout.LabelField("Layout Manager", EditorStyles.boldLabel);
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
using (new EditorGUI.DisabledScope(isPlaying))
{
int newIndex = EditorGUILayout.Popup("Layout Type", _selectedLayoutIndex, _layoutTypeNames.ToArray());
if (newIndex != _selectedLayoutIndex)
{
_selectedLayoutIndex = newIndex;
UpdateLayoutManager(newIndex);
serializedObject.ApplyModifiedProperties();
}
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
if (_layoutManager.managedReferenceValue != null)
{
EditorGUILayout.Space(3);
DrawManagedReferenceProperties(_layoutManager);
}
else
{
EditorGUILayout.HelpBox("Please select a Layout Manager", MessageType.Error);
}
}
EditorGUILayout.EndVertical();
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void RefreshLayoutTypes()
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
_layoutTypeNames.Clear();
_layoutTypeNames.Add(NoneOptionName);
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
var types = AlicizaX.Utility.Assembly.GetRuntimeTypes(typeof(ILayoutManager));
foreach (var type in types)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
if (!typeof(MonoBehaviour).IsAssignableFrom(type))
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
_layoutTypeNames.Add(type.FullName);
2025-05-30 10:32:08 +08:00
}
}
2025-12-26 14:22:46 +08:00
_selectedLayoutIndex = Mathf.Clamp(
_layoutTypeNames.IndexOf(_layoutManagerTypeName.stringValue),
0,
_layoutTypeNames.Count - 1
);
}
private void UpdateLayoutManager(int selectedIndex)
{
try
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
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();
}
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
catch (Exception e)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
Debug.LogError($"Layout Manager Error: {e.Message}");
ClearLayoutManager();
2025-05-30 10:32:08 +08:00
}
}
2025-12-26 14:22:46 +08:00
private void ClearLayoutManager()
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
_layoutManager.managedReferenceValue = null;
_layoutManagerTypeName.stringValue = "";
_selectedLayoutIndex = 0;
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private bool IsValidLayoutIndex(int index)
{
return index >= 0 && index < _layoutTypeNames.Count;
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
#endregion
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
#region Base Settings Section
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void DrawBaseSettingsSection(bool isPlaying)
{
EditorGUILayout.BeginVertical("box");
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
EditorGUILayout.LabelField("Base Settings", EditorStyles.boldLabel);
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
using (new EditorGUI.DisabledScope(isPlaying))
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
EditorGUILayout.PropertyField(_direction);
EditorGUILayout.PropertyField(_alignment);
EditorGUILayout.PropertyField(_content);
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Spacing", EditorStyles.boldLabel);
EditorGUILayout.PropertyField(_spacing);
EditorGUILayout.PropertyField(_padding);
EditorGUILayout.Space(5);
EditorGUILayout.LabelField("Scrolling", EditorStyles.boldLabel);
2025-05-30 10:32:08 +08:00
using (new EditorGUI.DisabledScope(isPlaying))
{
2025-12-26 14:22:46 +08:00
bool previousScrollValue = _scroll.boolValue;
EditorGUILayout.PropertyField(_scroll);
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
if (_scroll.boolValue != previousScrollValue)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
HandleScrollToggle();
2025-05-30 10:32:08 +08:00
}
}
2025-12-26 14:22:46 +08:00
if (_scroll.boolValue)
{
DrawScrollBarSettings(isPlaying);
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
EditorGUILayout.PropertyField(_snap);
}
EditorGUILayout.EndVertical();
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
private void DrawScrollBarSettings(bool isPlaying)
2025-05-30 10:32:08 +08:00
{
using (new EditorGUI.DisabledScope(isPlaying))
{
2025-12-26 14:22:46 +08:00
bool previousShowScrollBarValue = _showScrollBar.boolValue;
EditorGUILayout.PropertyField(_showScrollBar);
if (_showScrollBar.boolValue != previousShowScrollBarValue)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
HandleScrollBarToggle();
2025-05-30 10:32:08 +08:00
}
}
2025-12-26 14:22:46 +08:00
}
#endregion
#region Scroller Section
private void DrawScrollerSection(bool isPlaying)
{
if (!_scroll.boolValue) return;
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
EditorGUILayout.BeginVertical("box");
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
EditorGUILayout.LabelField("Scroller Settings", EditorStyles.boldLabel);
using (new EditorGUI.DisabledScope(isPlaying))
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
int newIndex = EditorGUILayout.Popup("Scroller Type", _selectedScrollerIndex, _scrollerTypeNames.ToArray());
if (newIndex != _selectedScrollerIndex)
{
UpdateScroller(newIndex);
serializedObject.ApplyModifiedProperties();
}
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
DrawScrollerComponentProperties();
DrawScrollerSpeedSettings();
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
EditorGUILayout.EndVertical();
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
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);
}
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
private void DrawScrollerSpeedSettings()
{
EditorGUILayout.Space(3);
EditorGUILayout.PropertyField(_scrollSpeed);
EditorGUILayout.PropertyField(_wheelSpeed);
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void RefreshScrollerTypes()
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
_scrollerTypes = TypeCache.GetTypesDerivedFrom<IScroller>()
.Where(t => t.IsSubclassOf(typeof(MonoBehaviour)))
.ToList();
_scrollerTypeNames = _scrollerTypes
.Select(t => t.FullName)
.Prepend(NoneOptionName)
.ToList();
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
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);
}
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void UpdateScroller(int selectedIndex)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
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();
}
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
private void RemoveExistingScroller(RecyclerView recyclerView)
{
var oldScroller = recyclerView.GetComponent<IScroller>();
if (oldScroller != null)
{
Undo.DestroyObjectImmediate(oldScroller as MonoBehaviour);
}
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void AddNewScroller(RecyclerView recyclerView, int selectedIndex)
{
Type selectedType = _scrollerTypes[selectedIndex - 1];
var newScroller = Undo.AddComponent(recyclerView.gameObject, selectedType) as IScroller;
2025-12-26 14:22:46 +08:00
_scroller.objectReferenceValue = newScroller as MonoBehaviour;
_scrollerTypeName.stringValue = selectedType.FullName;
_selectedScrollerIndex = selectedIndex;
2025-12-26 14:22:46 +08:00
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(recyclerView);
}
2025-12-26 14:22:46 +08:00
private void ClearScrollerReferences()
{
_scroller.objectReferenceValue = null;
_scrollerTypeName.stringValue = "";
_selectedScrollerIndex = 0;
}
2025-12-26 14:22:46 +08:00
private void HandleScrollToggle()
{
if (!_scroll.boolValue)
{
RemoveScrollerComponent();
ClearScrollBar();
}
}
2025-12-26 14:22:46 +08:00
private void RemoveScrollerComponent()
{
2025-12-26 14:22:46 +08:00
var recyclerView = target as RecyclerView;
if (recyclerView == null) return;
var scrollerComponent = recyclerView.GetComponent<IScroller>();
if (scrollerComponent != null)
{
Undo.DestroyObjectImmediate(scrollerComponent as MonoBehaviour);
}
ClearScrollerReferences();
}
2025-12-26 14:22:46 +08:00
#endregion
#region Scrollbar Handling
2025-12-26 14:22:46 +08:00
private void HandleScrollBarToggle()
{
if (_showScrollBar.boolValue)
{
CreateScrollBar();
}
else
{
ClearScrollBar();
}
}
2025-12-26 14:22:46 +08:00
private void CreateScrollBar()
{
2025-12-26 14:22:46 +08:00
var recyclerView = target as RecyclerView;
if (recyclerView == null) return;
2025-12-26 14:22:46 +08:00
Direction direction = (Direction)_direction.enumValueIndex;
string prefabPath = GetScrollbarPrefabPath(direction);
if (!string.IsNullOrEmpty(prefabPath))
{
2025-12-26 14:22:46 +08:00
InstantiateScrollBar(prefabPath, recyclerView.transform);
}
}
2025-12-26 14:22:46 +08:00
private string GetScrollbarPrefabPath(Direction direction)
{
2025-12-26 14:22:46 +08:00
return direction switch
{
Direction.Vertical => VerticalScrollbarPath,
Direction.Horizontal => HorizontalScrollbarPath,
_ => null
};
}
2025-12-26 14:22:46 +08:00
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;
}
2025-12-26 14:22:46 +08:00
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab, parent);
PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.UserAction);
2025-12-26 14:22:46 +08:00
_scrollbar.objectReferenceValue = instance.GetComponent<Scrollbar>();
serializedObject.ApplyModifiedProperties();
}
2025-12-26 14:22:46 +08:00
private void ClearScrollBar()
{
2025-12-26 14:22:46 +08:00
_showScrollBar.boolValue = false;
2025-12-26 14:22:46 +08:00
if (_scrollbar.objectReferenceValue != null)
{
Scrollbar scrollbarComponent = _scrollbar.objectReferenceValue as Scrollbar;
_scrollbar.objectReferenceValue = null;
2025-12-26 14:22:46 +08:00
if (scrollbarComponent != null)
{
2025-12-26 14:22:46 +08:00
Object.DestroyImmediate(scrollbarComponent.gameObject);
}
2025-12-26 14:22:46 +08:00
}
2025-12-26 14:22:46 +08:00
serializedObject.ApplyModifiedProperties();
}
2025-12-26 14:22:46 +08:00
#endregion
#region Templates Section
private void DrawTemplatesSection()
{
2025-12-26 14:22:46 +08:00
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++)
{
2025-12-26 14:22:46 +08:00
DrawTemplateItem(i);
}
}
2025-12-26 14:22:46 +08:00
private void DrawTemplateItem(int index)
{
2025-12-26 14:22:46 +08:00
if (index < 0 || index >= _templates.arraySize) return;
SerializedProperty item = _templates.GetArrayElementAtIndex(index);
EditorGUILayout.BeginHorizontal();
{
2025-12-26 14:22:46 +08:00
using (new EditorGUI.DisabledScope(true))
{
EditorGUILayout.PropertyField(item, GUIContent.none, true);
}
if (GUILayout.Button("×", GUILayout.Width(20), GUILayout.Height(EditorGUIUtility.singleLineHeight)))
{
RemoveTemplateItem(index);
}
}
2025-12-26 14:22:46 +08:00
EditorGUILayout.EndHorizontal();
}
2025-12-26 14:22:46 +08:00
private void RemoveTemplateItem(int index)
{
if (_templates == null || index < 0 || index >= _templates.arraySize) return;
2025-12-26 14:22:46 +08:00
_templates.DeleteArrayElementAtIndex(index);
serializedObject.ApplyModifiedProperties();
GUIUtility.ExitGUI();
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void DrawDragAndDropArea()
{
Rect dropArea = GUILayoutUtility.GetRect(0f, 50f, GUILayout.ExpandWidth(true));
GUI.Box(dropArea, "Drag ViewHolder Templates Here", EditorStyles.helpBox);
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
HandleDragAndDrop(dropArea);
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void HandleDragAndDrop(Rect dropArea)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
Event currentEvent = Event.current;
switch (currentEvent.type)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
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;
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void ProcessDraggedTemplates()
{
foreach (Object draggedObject in DragAndDrop.objectReferences)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
if (draggedObject is GameObject gameObject)
{
ProcessDraggedGameObject(gameObject);
}
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
}
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void ProcessDraggedGameObject(GameObject gameObject)
{
ViewHolder viewHolder = gameObject.GetComponent<ViewHolder>();
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
if (viewHolder != null)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
AddTemplate(gameObject);
2025-05-30 10:32:08 +08:00
}
else
{
2025-12-26 14:22:46 +08:00
Debug.LogWarning($"GameObject '{gameObject.name}' must have a ViewHolder component!");
2025-05-30 10:32:08 +08:00
}
}
2025-12-26 14:22:46 +08:00
private void AddTemplate(GameObject templatePrefab)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
if (_templates == null || templatePrefab == null) return;
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
if (IsTemplateDuplicate(templatePrefab))
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
Debug.LogWarning($"Template '{templatePrefab.name}' already exists in the list!");
2025-05-30 10:32:08 +08:00
return;
}
2025-12-26 14:22:46 +08:00
_templates.arraySize++;
SerializedProperty newItem = _templates.GetArrayElementAtIndex(_templates.arraySize - 1);
newItem.objectReferenceValue = templatePrefab;
2025-05-30 10:32:08 +08:00
serializedObject.ApplyModifiedProperties();
}
2025-12-26 14:22:46 +08:00
private bool IsTemplateDuplicate(GameObject templatePrefab)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
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;
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
#endregion
#region Helper Methods
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
private void DrawManagedReferenceProperties(SerializedProperty property)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
SerializedProperty iterator = property.Copy();
bool enterChildren = true;
while (iterator.NextVisible(enterChildren))
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
enterChildren = false;
if (iterator.name == "m_Script") continue;
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
EditorGUILayout.PropertyField(iterator, true);
}
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
private void DrawComponentProperties(MonoBehaviour component, string header = null)
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
if (component == null) return;
EditorGUILayout.Space(3);
if (!string.IsNullOrEmpty(header))
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
EditorGUILayout.LabelField(header, EditorStyles.boldLabel);
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
SerializedObject componentSerializedObject = new SerializedObject(component);
componentSerializedObject.Update();
SerializedProperty property = componentSerializedObject.GetIterator();
bool enterChildren = true;
while (property.NextVisible(enterChildren))
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
enterChildren = false;
if (property.name == "m_Script") continue;
EditorGUILayout.PropertyField(property, true);
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
componentSerializedObject.ApplyModifiedProperties();
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
#endregion
#region Restore Helpers ()
private void RestoreLayoutManagerFromTypeNameIfMissing()
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
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 = "";
}
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
private void RestoreScrollerFromTypeNameIfMissing()
2025-05-30 10:32:08 +08:00
{
2025-12-26 14:22:46 +08:00
try
{
if (_scroller == null || _scrollerTypeName == null) return;
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
// 如果 objectReferenceValue 已经存在就不必恢复
if (_scroller.objectReferenceValue != null) return;
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
string typeName = _scrollerTypeName.stringValue;
if (string.IsNullOrEmpty(typeName)) return;
2025-05-30 10:32:08 +08:00
2025-12-26 14:22:46 +08:00
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 = "";
}
2025-05-30 10:32:08 +08:00
}
2025-12-26 14:22:46 +08:00
#endregion
2025-05-30 10:32:08 +08:00
}
}