2025-05-30 10:32:08 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
2025-11-20 15:40:38 +08:00
|
|
|
|
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-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
_scroller.objectReferenceValue = newScroller as MonoBehaviour;
|
|
|
|
|
|
_scrollerTypeName.stringValue = selectedType.FullName;
|
|
|
|
|
|
_selectedScrollerIndex = selectedIndex;
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
EditorUtility.SetDirty(recyclerView);
|
|
|
|
|
|
}
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private void ClearScrollerReferences()
|
|
|
|
|
|
{
|
|
|
|
|
|
_scroller.objectReferenceValue = null;
|
|
|
|
|
|
_scrollerTypeName.stringValue = "";
|
|
|
|
|
|
_selectedScrollerIndex = 0;
|
|
|
|
|
|
}
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private void HandleScrollToggle()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_scroll.boolValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoveScrollerComponent();
|
|
|
|
|
|
ClearScrollBar();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private void RemoveScrollerComponent()
|
2025-11-20 15:40:38 +08:00
|
|
|
|
{
|
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-11-20 15:40:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Scrollbar Handling
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private void HandleScrollBarToggle()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_showScrollBar.boolValue)
|
|
|
|
|
|
{
|
|
|
|
|
|
CreateScrollBar();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
ClearScrollBar();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private void CreateScrollBar()
|
2025-11-20 15:40:38 +08:00
|
|
|
|
{
|
2025-12-26 14:22:46 +08:00
|
|
|
|
var recyclerView = target as RecyclerView;
|
|
|
|
|
|
if (recyclerView == null) return;
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
Direction direction = (Direction)_direction.enumValueIndex;
|
|
|
|
|
|
string prefabPath = GetScrollbarPrefabPath(direction);
|
|
|
|
|
|
if (!string.IsNullOrEmpty(prefabPath))
|
2025-11-20 15:40:38 +08:00
|
|
|
|
{
|
2025-12-26 14:22:46 +08:00
|
|
|
|
InstantiateScrollBar(prefabPath, recyclerView.transform);
|
2025-11-20 15:40:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private string GetScrollbarPrefabPath(Direction direction)
|
|
|
|
|
|
{
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
return direction switch
|
|
|
|
|
|
{
|
|
|
|
|
|
Direction.Vertical => VerticalScrollbarPath,
|
|
|
|
|
|
Direction.Horizontal => HorizontalScrollbarPath,
|
|
|
|
|
|
_ => null
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
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-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab, parent);
|
|
|
|
|
|
PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.UserAction);
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
_scrollbar.objectReferenceValue = instance.GetComponent<Scrollbar>();
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
}
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private void ClearScrollBar()
|
2025-11-20 15:40:38 +08:00
|
|
|
|
{
|
2025-12-26 14:22:46 +08:00
|
|
|
|
_showScrollBar.boolValue = false;
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
if (_scrollbar.objectReferenceValue != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Scrollbar scrollbarComponent = _scrollbar.objectReferenceValue as Scrollbar;
|
|
|
|
|
|
_scrollbar.objectReferenceValue = null;
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
if (scrollbarComponent != null)
|
2025-11-20 15:40:38 +08:00
|
|
|
|
{
|
2025-12-26 14:22:46 +08:00
|
|
|
|
Object.DestroyImmediate(scrollbarComponent.gameObject);
|
2025-11-20 15:40:38 +08:00
|
|
|
|
}
|
2025-12-26 14:22:46 +08:00
|
|
|
|
}
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
2025-11-20 15:40:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Templates Section
|
|
|
|
|
|
|
|
|
|
|
|
private void DrawTemplatesSection()
|
2025-11-20 15:40:38 +08:00
|
|
|
|
{
|
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-11-20 15:40:38 +08:00
|
|
|
|
{
|
2025-12-26 14:22:46 +08:00
|
|
|
|
DrawTemplateItem(i);
|
2025-11-20 15:40:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private void DrawTemplateItem(int index)
|
2025-11-20 15:40:38 +08:00
|
|
|
|
{
|
2025-12-26 14:22:46 +08:00
|
|
|
|
if (index < 0 || index >= _templates.arraySize) return;
|
|
|
|
|
|
|
|
|
|
|
|
SerializedProperty item = _templates.GetArrayElementAtIndex(index);
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.BeginHorizontal();
|
2025-11-20 15:40:38 +08:00
|
|
|
|
{
|
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-11-20 15:40:38 +08:00
|
|
|
|
}
|
2025-12-26 14:22:46 +08:00
|
|
|
|
EditorGUILayout.EndHorizontal();
|
2025-11-20 15:40:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 14:22:46 +08:00
|
|
|
|
private void RemoveTemplateItem(int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_templates == null || index < 0 || index >= _templates.arraySize) return;
|
2025-11-20 15:40:38 +08:00
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
}
|