using System; using System.Collections.Generic; using System.Linq; using System.Reflection; #if ODIN_INSPECTOR|| ODIN_INSPECTOR_3 using Sirenix.OdinInspector.Editor; #endif using TyphoonGUIStyle; using UnityEditor; using UnityEngine; namespace TyphoonUI { [CustomEditor(typeof(UIScrollView), true)] [CanEditMultipleObjects] #if ODIN_INSPECTOR|| ODIN_INSPECTOR_3 public class UIScrollViewEditor : OdinEditor #else public class UIScrollViewEditor : Editor #endif { #region 反射获取方法 private static MethodInfo _method_GeneratePreviewCells = null; private static MethodInfo Method_GeneratePreviewCells { get { if (_method_GeneratePreviewCells == null) { var type = typeof(UIScrollView); _method_GeneratePreviewCells = type.GetMethod("GeneratePreviewCells", BindingFlags.Instance | BindingFlags.NonPublic); } return _method_GeneratePreviewCells; } } private static MethodInfo _method_ClearPreviewCells = null; private static MethodInfo Method_ClearPreviewCells { get { if (_method_ClearPreviewCells == null) { var type = typeof(UIScrollView); _method_ClearPreviewCells = type.GetMethod("ClearPreviewCells", BindingFlags.Instance | BindingFlags.NonPublic); } return _method_ClearPreviewCells; } } private static MethodInfo _method_UpdatePreviewCellsLayoutOnValidate = null; private static MethodInfo Method_UpdatePreviewCellsLayoutOnValidate { get { if (_method_UpdatePreviewCellsLayoutOnValidate == null) { var type = typeof(UIScrollView); _method_UpdatePreviewCellsLayoutOnValidate = type.GetMethod("UpdatePreviewCellsLayoutOnValidate", BindingFlags.Instance | BindingFlags.NonPublic); } return _method_UpdatePreviewCellsLayoutOnValidate; } } #endregion protected UIScrollView Target => target as UIScrollView; protected Type _typeOfTarget = null; protected Type TypeOfTarget { get { if (_typeOfTarget == null) { _typeOfTarget = target.GetType(); } return _typeOfTarget; } } private SerializedProperty _previewPrefab; private SerializedProperty _previewCells; private SerializedProperty _cellPrefabs; private SerializedProperty _cellSize; private SerializedProperty _previewScrollPercent; #if ODIN_INSPECTOR|| ODIN_INSPECTOR_3 protected override void OnEnable() { base.OnEnable(); #else protected virtual void OnEnable() { #endif _cellPrefabs = serializedObject.FindProperty("CellPrefabs"); _cellSize = serializedObject.FindProperty("CellSize"); _previewPrefab = serializedObject.FindProperty("m_PreviewPrefab"); _previewCells = serializedObject.FindProperty("m_PreviewCells"); _previewScrollPercent = serializedObject.FindProperty("m_previewScrollPercent"); } public override void OnInspectorGUI() { DrawCheckerTipsGUI(); base.OnInspectorGUI(); //绘制元素大小 DrawCellSizeGUI(); //绘制预制物资源 DrawCellPrefabsGUI(); //绘制预制物资源 DrawPreviewLayoutGUI(); //更新元素布局 UpdatePreviewCellsLayoutOnValidate(); //绘制重绘按钮 DrawRefreshBtnGUI(); //重绘 Repaint(); } protected Rect CreatRightCenterButtonRect(float width, float height, float offset = 2) { var last = GUILayoutUtility.GetLastRect(); var rect = last; rect.width = width; rect.height = height; rect.center = last.center; rect.x = last.xMax - rect.width; rect.x -= offset; return rect; } protected virtual void DrawCellPrefabsGUI() { serializedObject.Update(); GUILayout.Label("预制物资源", Styles.BoldLabel); EditorGUILayout.PropertyField(_cellPrefabs); serializedObject.ApplyModifiedProperties(); if (Target.CellPrefabs != null && Target.CellPrefabs.Count > 0) { GUILayout.Label(new GUIContent($"默认Prefab:{Target.CellPrefabs.FirstOrDefault()}", Styles.IconInfo), Styles.HelpBox); } } //绘制元素大小 protected virtual void DrawCellSizeGUI() { GUILayout.Space(5); serializedObject.Update(); EditorGUILayout.PropertyField(_cellSize); GUILayout.BeginHorizontal(); GUILayout.Label(""); if (GUILayout.Button("读取大小", Styles.BtnGreen, GUILayout.Width(140))) { var match = new HashSet(); if (Target.CellPrefabs != null) { var valid = Target.CellPrefabs.Where(e => e != null); foreach (var element in valid) { match.Add(element); } } var menu = new GenericMenu(); foreach (var element in match) { menu.AddItem(new GUIContent($"{element.name}"), false, () => { try { var rectTransform = element.GetComponent(); _cellSize.vector2Value = rectTransform.rect.size; serializedObject.ApplyModifiedProperties(); } catch (Exception e) { Debug.LogError($"读取失败:{e}"); } }); } if (_previewPrefab != null && _previewPrefab.objectReferenceValue != null) { var obj = _previewPrefab.objectReferenceValue as GameObject; if (obj != null) { menu.AddItem(new GUIContent($"{obj.name} (Preview Prefab)"), false, () => { try { var rectTransform = obj.GetComponent(); _cellSize.vector2Value = rectTransform.rect.size; serializedObject.ApplyModifiedProperties(); } catch (Exception e) { Debug.LogError($"读取失败:{e}"); } }); } } if (menu.GetItemCount() <= 0) { menu.AddItem(new GUIContent($"暂无匹配项"), false, null); } menu.ShowAsContext(); } GUILayout.EndHorizontal(); serializedObject.ApplyModifiedProperties(); GUILayout.Space(5); } //检查提示GUI protected void DrawCheckerTipsGUI() { if (!Target.ScrollerIsExists) { GUILayout.Label(new GUIContent("未绑定 Scroller 组件!", Styles.IconError), Styles.HelpBox, GUILayout.Height(44)); } if (Target.CellContainer == null) { GUILayout.Label(new GUIContent("Cell Container 不可为空!", Styles.IconWarning), Styles.HelpBox, GUILayout.Height(44)); } if (Target.ScrollerIsExists && Target.Scroller.ViewPort == null) { GUILayout.Label(new GUIContent("Scroller -> ViewPort 为空!", Styles.IconError), Styles.HelpBox, GUILayout.Height(44)); } if (Target.ScrollerIsExists && (TypeOfTarget == typeof(UIScrollRectLoop) || TypeOfTarget == typeof(UIGridViewLoop))) { if (Target.Scroller.Scrollbar != null) { GUILayout.Label(new GUIContent("Scroller -> Scrollbar 需要置空! \nLoop 模式不支持!", Styles.IconError), Styles.HelpBox, GUILayout.Height(44)); } } } //绘制预览布局GUI protected void DrawPreviewLayoutGUI() { GUILayout.Space(10); serializedObject.Update(); GUILayout.Label("预览布局(编辑模式下)", Styles.BoldLabel); EditorGUILayout.PropertyField(_previewPrefab); GUILayout.BeginHorizontal(); GUILayout.Label(""); if (GUILayout.Button("同步大小到 Cell Size", Styles.BtnBlue, GUILayout.Width(140))) { if (_previewPrefab != null) { var obj = _previewPrefab.objectReferenceValue as GameObject; if (obj != null) { var rectTransform = obj.GetComponent(); _cellSize.vector2Value = rectTransform.rect.size; serializedObject.ApplyModifiedProperties(); } } } if (GUILayout.Button("从 Cell Prefabs 选择", Styles.BtnGreen, GUILayout.Width(140))) { var menu = new GenericMenu(); if (Target.CellPrefabs != null && Target.CellPrefabs.Count > 0) { var prefabs = Target.CellPrefabs; foreach (var prefab in prefabs) { if (prefab == null) { continue; } menu.AddItem(new GUIContent($"{prefab.name}"), false, () => { try { _previewPrefab.objectReferenceValue = prefab as GameObject; serializedObject.ApplyModifiedProperties(); } catch (Exception e) { Debug.LogError($"选择失败! error:{e}"); } }); } } if (menu.GetItemCount() <= 0) { menu.AddItem(new GUIContent($"暂无匹配项"), false, null); } menu.ShowAsContext(); } GUILayout.EndHorizontal(); GUILayout.Space(5); EditorGUILayout.PropertyField(_previewScrollPercent); var temEnable = GUI.enabled; GUI.enabled = false; EditorGUILayout.PropertyField(_previewCells); GUI.enabled = temEnable; GUILayout.Label("", GUILayout.Height(24)); var btnPreview = CreatRightCenterButtonRect(100, 22); if (GUI.Button(btnPreview, "预览", Styles.BtnGreen)) { GeneratePreviewCells(); } var btnClear = CreatRightCenterButtonRect(100, 22); btnClear.x = btnPreview.x; btnClear.x -= btnClear.width; btnClear.x -= 2; if (GUI.Button(btnClear, "清理", Styles.BtnRed)) { ClearPreviewCells(); } serializedObject.ApplyModifiedProperties(); } //更新预览布局 protected virtual void UpdatePreviewCellsLayoutOnValidate() { Method_UpdatePreviewCellsLayoutOnValidate?.Invoke(Target, null); } //更新预览实例 protected virtual void ClearPreviewCells() { Method_ClearPreviewCells?.Invoke(Target, null); } //生成预览实例 protected virtual void GeneratePreviewCells() { Method_GeneratePreviewCells?.Invoke(Target, null); } //重新绘制 protected virtual void DrawRefreshBtnGUI() { var temEnable = GUI.enabled; GUI.enabled = false; GUILayout.Space(5); if (Application.isPlaying && Target.DataCount > 0) { if (GUILayout.Button("重绘视窗", Styles.BtnGreen, GUILayout.Height(24))) ; { Target.Refresh(); } } GUI.enabled = temEnable; } } }