马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
1. 简介:
IMGUI是代码驱动gui系统,由 OnGUI 函数驱动:
- void OnGUI() {
- if (GUILayout.Button("Press Me")){
- Debug.Log("Hello!");
- }
- }
复制代码 IMGUI常用于:
创建 in-game debugging displays and tools;
为组件创建自定义inspector;
创建editor windows and tools;
2. IMGUI底子:
OnGUI()函数在脚本激活时每帧调用;
- using UnityEngine;
- [ExecuteAlways]
- public class TestGUI : MonoBehaviour
- {
- private void OnGUI()
- {
- GUI.Box(new Rect(10, 10, 100, 90), "Loader Menu");
- if(GUI.Button(new Rect(20, 40, 80, 20), "Button 1"))
- {
- Debug.Log("Button 1");
- }
- if (GUI.Button(new Rect(20, 70, 80, 20), "Button 2"))
- {
- Debug.Log("Button 2");
- }
- }
- }
复制代码
3. 调用函数来绘制控件:
(GUI/GUILayout).Type (Rect, Content);
定义 Rect 时,坐标系基于屏幕左上角,可使用 Screen.width 和 Screen.height ;
Content范例:
string;
Texture2D;
GUIContent;
- GUI.Box(new Rect(10, 10, 100, 50), icon);
- GUI.Box(new Rect(10, 60, 100, 50), new GUIContent("text", icon));
- GUI.Box(new Rect(10, 110, 100, 50), new GUIContent("text", icon, "This is the tooltip"));
- GUI.Button(new Rect(10, 160, 100, 20), new GUIContent("Click me", "This is the tooltip"));
- GUI.Label(new Rect(10, 180, 100, 20), GUI.tooltip); //显示当前的tooltip内容
复制代码
3. IMGUI控件:
Label:
- GUI.Label(new Rect(100, 100, 100, 30), "show text");
复制代码 Button:
- if(GUI.Button(new Rect(100, 100, 100, 30), "button"))
- {
- Debug.Log("Click button");
- }
复制代码 RepeatButton:
类似 Button,只是点击时一直返回 true;
TextField:
- str = GUI.TextField(new Rect(100, 100, 100, 30), str);
复制代码 单行文本输入;
TextArea:
- str = GUI.TextArea(new Rect(100, 100, 100, 100), str);
复制代码 多行文本输入;
Toggle:
- toggleVal = GUI.Toggle(new Rect(100, 100, 100, 30), toggleVal, "Toggle");
复制代码 Toolbar:
一行单选按钮,toolbarInt 用来记录选择的按钮索引;
- private int toolbarInt = 0;
- private string[] toolbarStrings = { "Toolbar1", "Toolbar2", "Toolbar3" };
- void OnGUI()
- {
- toolbarInt = GUI.Toolbar(new Rect(100, 100, 300, 30), toolbarInt, toolbarStrings);
- }
复制代码 SelectionGrid:
多行Toolbar;
- private int selectionIndex = 0;
- private string[] selectionStrings = { "Toolbar1", "Toolbar2", "Toolbar3", "Toolbar4" };
- void OnGUI()
- {
- selectionIndex = GUI.SelectionGrid(new Rect(100, 100, 300, 60), selectionIndex, selectionStrings, 2);
- }
复制代码 HorizontalSlider:
水平滑动条
- private float sliderValue = 0.0f;
- void OnGUI()
- {
- sliderValue = GUI.HorizontalSlider(new Rect(100, 100, 100, 30), sliderValue, 0.0f, 10.0f);
- }
复制代码 VerticalSlider:
垂直滑动条;
- private float sliderValue = 0.0f;
- void OnGUI()
- {
- sliderValue = GUI.VerticalSlider(new Rect(100, 100, 100, 30), sliderValue, 0.0f, 10.0f);
- }
复制代码 HorizontalScrollbar:
类似 Slider,用于导航 ScrollView 控件,有一个额外参数控制旋钮宽度;
- private float scrollValue = 0.0f;
- void OnGUI()
- {
- scrollValue = GUI.HorizontalScrollbar(new Rect(100, 100, 100, 30), scrollValue, 1, 0.0f, 10.0f);
- }
复制代码 VerticalScrollbar:
类似 HorizontalScrollbar;
ScrollView:
- private Vector2 scrollPosition = Vector2.zero;
- void OnGUI()
- {
- //第一个参数指定可视区域,第三个参数指定在滚动列表内的大小
- scrollPosition = GUI.BeginScrollView(new Rect(100, 100, 100, 100), scrollPosition, new Rect(0, 0, 400, 400));
- GUI.Label(new Rect(0, 0, 400, 400), "text");
- GUI.EndScrollView();
- }
复制代码 Window:
可拖动的容器;
- private Rect windowRect = new Rect(100, 100, 100, 100);
- void OnGUI()
- {
- windowRect = GUI.Window(0, windowRect, WindowFunction, "My Window");
-
- }
- void WindowFunction(int id)
- {
- GUI.Label(new Rect(10, 20, 100, 30), "text");
- GUI.DragWindow(); //让window可拖动
- }
复制代码 GUI.changed:
检测用户是否有操作;
- selectIndex = GUI.Toolbar(new Rect(100, 100, 200, 30), selectIndex, options);
- if(GUI.changed)
- {
- Debug.Log("selectIndex = " + selectIndex);
- }
复制代码 4. 使用GUIStyles样式化控件:
GUIStyles 控制控件的外观显示;
GUISkins 包罗一系列 GUIStyles;
所有控件可设置一个可选的 GUIStyle;
- //指定 GUIStyle 的名字 button
- GUI.Label(new Rect(100, 100, 100, 30), "text", "button");
复制代码
在脚本中声明一个序列化的GUIStyle ,查看属性:
- public GUIStyle style;
- void OnGUI()
- {
- //指定 GUIStyle 的名字 button
- GUI.Label(new Rect(100, 100, 100, 30), "text", style);
- }
复制代码
设置GUISkin:
- public GUISkin skin;
- void OnGUI()
- {
- GUI.skin = skin;
- //绘制
- //...
- //恢复默认skin
- GUI.skin = null; //
- }
复制代码 改变GUIStyle:
- GUIStyle style = GUI.skin.GetStyle("label");
- style.fontSize = 30;
- GUI.Label(new Rect(100, 100, 400, 100), "text1");
- style.fontSize = 50;
- GUI.Label(new Rect(100, 150, 400, 100), "text2");
复制代码 5. IMGUI Layout 模式:
固定结构和自动结构:
要使用自动结构,使用 GUILayout 代替GUI;
可以在 OnGUI() 函数中同时使用两种结构方式,但是两种结构相互独立;
GUILayout 不必要指定 Rect;
- // Fixed Layout
- GUI.Button(new Rect(25, 25, 100, 30), "I am a Fixed Layout Button");
- // Automatic Layout
- GUILayout.Button("I am an Automatic Layout Button");
复制代码 固定结构 Group:
Group内的控件将基于组的左上角定位;
- GUI.BeginGroup(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 50, 100, 100));
- GUI.Box(new Rect(0, 0, 100, 100), "Group is here");
- GUI.Button(new Rect(10, 40, 80, 30), "Click me");
- GUI.EndGroup();
复制代码 可以嵌套使用Group,可获取裁剪效果:
- // background image that is 256 x 32
- public Texture2D bgImage;
- // foreground image that is 256 x 32
- public Texture2D fgImage;
- // a float between 0.0 and 1.0
- public float playerEnergy = 1.0f;
- //使用默认style绘制box
- public GUIStyle style = new GUIStyle();
- void OnGUI()
- {
- if(bgImage == null || fgImage == null)
- {
- return;
- }
- GUI.BeginGroup(new Rect(300, 300, 256, 32));
- // 绘制背景
- GUI.Box(new Rect(0, 0, 256, 32), bgImage, style);
- // 通过 playerEnergy 控制显示大小,达到裁剪效果
- GUI.BeginGroup(new Rect(0, 0, playerEnergy * 256, 32));
- GUI.Box(new Rect(0, 0, 256, 32), fgImage, style);
- GUI.EndGroup();
- GUI.EndGroup();
- }
复制代码
自动结构 Area:
类似固定结构 Group;
定义屏幕的一块区域来包罗 GUILayout 控件,控件基于 Area 的左上角定位;
- GUILayout.Button("I am not inside an Area");
- GUILayout.BeginArea(new Rect(Screen.width / 2, Screen.height / 2, 300, 300));
- GUILayout.Button("I am completely inside an Area");
- GUILayout.EndArea();
复制代码 注意Area里控件的宽度将被拉伸到与Area一样;
自动结构 Horizontal and Vertical Group:
使用自动结构时,控件默认从上往下一个接一个;
为了修改这一行为,提供了Horizontal 和 Vertical Group;
Horizontal Group 里的控件被水平摆放;
GUILayout.BeginHorizontal(), GUILayout.EndHorizontal()
Vertical Group 里的控件被垂直摆放;
GUILayout.BeginVertical(), and GUILayout.EndVertical()
- GUILayout.BeginArea(new Rect(400, 400, 200, 100));
- GUILayout.BeginHorizontal();
- GUILayout.Button("Button\nfirst");
- GUILayout.BeginVertical();
- GUILayout.Button("Button2");
- GUILayout.Label("Label");
- GUILayout.EndVertical();
- GUILayout.EndHorizontal();
- GUILayout.EndArea();
复制代码
GUILayoutOptions :
使用 GUILayoutOptions 覆盖一些自动结构参数;
- GUILayout.BeginArea(new Rect(100, 50, Screen.width - 200, Screen.height - 100));
- GUILayout.Button("I am a regular Automatic Layout Button");
- GUILayout.Button("My width has been overridden", GUILayout.Width(95));
- GUILayout.EndArea();
复制代码
6. 使用 IMGUI 扩展编辑器:
1. 创建自定义 Editor Window:
1. 创建脚本继续 EditorWindow:
- using UnityEditor;
- using UnityEngine;
- public class MyWindow : EditorWindow
- {
- private void OnGUI()
- {
-
- }
- }
复制代码 2. 显示window:
- using UnityEditor;
- using UnityEngine;
- public class MyWindow : EditorWindow
- {
- [MenuItem("Window/My Window")]
- public static void ShowWindow()
- {
- EditorWindow.GetWindow(typeof(MyWindow));
- }
- private void OnGUI()
- {
-
- }
- }
复制代码 3. 实现 gui:
EditorGUI 和 EditorGUILayout 提供了额外的控件;
- using UnityEditor;
- using UnityEngine;
- public class MyWindow : EditorWindow
- {
- string myString = "Hello World";
- bool groupEnabled;
- bool myBool = true;
- float myFloat = 1.23f;
- [MenuItem("Window/My Window")]
- public static void ShowWindow()
- {
- EditorWindow.GetWindow(typeof(MyWindow));
- }
- private void OnGUI()
- {
- GUILayout.Label("Base Settings", EditorStyles.boldLabel);
- myString = EditorGUILayout.TextField("Text Field", myString);
- groupEnabled = EditorGUILayout.BeginToggleGroup("Optional Settings", groupEnabled);
- myBool = EditorGUILayout.Toggle("Toggle", myBool);
- myFloat = EditorGUILayout.Slider("Slider", myFloat, -3, 3);
- EditorGUILayout.EndToggleGroup();
- }
- }
复制代码 2. 自定义 Inspector:
1. 自定义 GUI of Serializable class:
- using System;
- using UnityEngine;
- public enum PersonLevel { A, B, C}
- [Serializable]
- public class Person
- {
- public string name;
- public int age;
- public PersonLevel personLevel;
- }
- public class PersonInfo : MonoBehaviour
- {
- public Person person;
- public Person[] persons;
- }
复制代码 
(默认显示)
使用 Property Drawer 来控制其在 Inspector 上的显示:
- using UnityEditor;
- using UnityEngine;
- [CustomPropertyDrawer(typeof(Person))]
- public class PersonDrawer : PropertyDrawer
- {
- public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
- {
- // Using BeginProperty / EndProperty on the parent property means that
- // prefab override logic works on the entire property.
- EditorGUI.BeginProperty(position, label, property);
- // Draw label
- position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
- // Don't make child fields be indented
- var indent = EditorGUI.indentLevel;
- EditorGUI.indentLevel = 0;
- // Calculate rects
- var nameRect = new Rect(position.x, position.y, 30, position.height);
- var ageRect = new Rect(position.x + 35, position.y, 50, position.height);
- var personLevelRect = new Rect(position.x + 90, position.y, position.width - 90, position.height);
- // Draw fields - pass GUIContent.none to each so they are drawn without labels
- EditorGUI.PropertyField(nameRect, property.FindPropertyRelative("name"), GUIContent.none);
- EditorGUI.PropertyField(ageRect, property.FindPropertyRelative("age"), GUIContent.none);
- EditorGUI.PropertyField(personLevelRect, property.FindPropertyRelative("personLevel"), GUIContent.none);
- // Set indent back to what it was
- EditorGUI.indentLevel = indent;
- EditorGUI.EndProperty();
- }
- }
复制代码 2. 使用 Property Attribute 自定义脚本成员 GUI:
创建 Attribute:
- using UnityEngine;
- public class MyRangeAttribute : PropertyAttribute
- {
- public float min;
- public float max;
- public MyRangeAttribute(float min, float max)
- {
- this.min = min;
- this.max = max;
- }
- }
复制代码 对 Attribute 进行自定义绘制:
- using UnityEditor;
- using UnityEngine;
- //对MyRangeAttribute
- [CustomPropertyDrawer(typeof(MyRangeAttribute))]
- public class RangeDrawer : PropertyDrawer
- {
- public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
- {
- // First get the attribute since it contains the range for the slider
- MyRangeAttribute range = (MyRangeAttribute)attribute;
- // Now draw the property as a Slider or an IntSlider based on whether it's a float or integer.
- if (property.propertyType == SerializedPropertyType.Float)
- EditorGUI.Slider(position, property, range.min, range.max, label);
- else if (property.propertyType == SerializedPropertyType.Integer)
- EditorGUI.IntSlider(position, property, (int)range.min, (int)range.max, label);
- else
- EditorGUI.LabelField(position, label.text, "Use MyRange with float or int.");
- }
- }
复制代码 (注意,为了性能考虑,EditorGUILayout 不可在 PropertyDrawer 中使用)
使用 Attribute:
- using UnityEngine;
- public class TestAttribute : MonoBehaviour
- {
- [MyRange(1.5f, 35f)]
- public float fValue;
- }
复制代码
3. Component 自定义编辑器:
创建 component:
- using UnityEngine;
- [ExecuteInEditMode]
- public class LookAtPoint : MonoBehaviour
- {
- public Vector3 lookAtPoint = Vector3.zero;
- void Update()
- {
- transform.LookAt(lookAtPoint);
- }
- }
复制代码 创建组件自定义编辑器:
- using UnityEditor;
- [CustomEditor(typeof(LookAtPoint))]
- [CanEditMultipleObjects]
- public class LookAtPointEditor : Editor
- {
- LookAtPoint targetComp;
- SerializedProperty lookAtPoint;
- void OnEnable()
- {
- lookAtPoint = serializedObject.FindProperty("lookAtPoint");
- }
- public override void OnInspectorGUI()
- {
- targetComp = target as LookAtPoint;
- serializedObject.Update();
- EditorGUILayout.PropertyField(lookAtPoint);
- serializedObject.ApplyModifiedProperties();
- }
- }
复制代码 通过 OnSceneGUI 往 Scene View 中添加 编辑器代码:
- using UnityEngine;
- using UnityEditor;
- [CustomEditor(typeof(LookAtPoint))]
- [CanEditMultipleObjects]
- public class LookAtPointEditor : Editor
- {
- private void OnSceneGUI()
- {
- LookAtPoint t = (LookAtPoint)target;
- //使用 Handles 类来实现3D场景编辑控制
- EditorGUI.BeginChangeCheck();
- //给定位置,在场景中画中控制轴,拖动的话,返回变化的pos
- Vector3 pos = Handles.PositionHandle(t.lookAtPoint, Quaternion.identity);
- if (EditorGUI.EndChangeCheck())
- {
- Undo.RecordObject(target, "Move point");
- t.lookAtPoint = pos;
- t.Update();
- }
- }
- }
复制代码 如果要在场景中添加 GUI,使用 Handles.BeginGUI() and Handles.EndGUI();
4. TreeView :
是一个 IMGUI 控件,用于显示分层数据,可展开和折叠;
使用 TreeView 创建高度自定义的列表视图和多列表格;
按行渲染,每行代表一个 TreeViewItem, 包罗父节点和子节点的信息;
TreeView 有一个潜伏的根 TreeViewItem,是所有其他 item的根;
重要的类和方法:
类 TreeViewState:
包罗交互的状态信息,可序列化,以便在编辑器编译和进入Play模式时生存数据;
类 TreeViewItem:
包罗每个 TreeView item 的数据,在 TreeView 中必要一个唯一 id;
depth 属性用来视觉缩进;
抽象方法 BuildRoot:
TreeView 类的抽象方法,每次调用 TreeView 的 Reload 方法就会触发;
用于创建 root item;
对于简单树,可创建所有的 TreeViewItem;
对于复杂树,通过覆写 BuildRows 来创建当前必要的行;
虚方法 BuildRows:
默认实现创建整个树;
可覆写只创建展开的行;
初始化 TreeView:
调用 Reload 方法来初始化;
两种方式设置 TreeView:
1. 在 BuildRoot 中创建整个树;
2. 在 BuildRoot 中只创建根,然后在 BuildRows 中只创建展开的 items;
Example 1: A simple TreeView:
- using System.Collections.Generic;
- using UnityEditor.IMGUI.Controls;
- public class SimpleTreeView : TreeView
- {
- public SimpleTreeView(TreeViewState state) : base(state)
- {
- Reload();
- }
- protected override TreeViewItem BuildRoot()
- {
- // BuildRoot is called every time Reload is called to ensure that TreeViewItems
- // are created from data. Here we create a fixed set of items. In a real world example,
- // a data model should be passed into the TreeView and the items created from the model.
- // This section illustrates that IDs should be unique. The root item is required to
- // have a depth of -1, and the rest of the items increment from that.
- var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
- var allItems = new List<TreeViewItem>
- {
- new TreeViewItem {id = 1, depth = 0, displayName = "Animals"},
- new TreeViewItem {id = 2, depth = 1, displayName = "Mammals"},
- new TreeViewItem {id = 3, depth = 2, displayName = "Tiger"},
- new TreeViewItem {id = 4, depth = 2, displayName = "Elephant"},
- new TreeViewItem {id = 5, depth = 2, displayName = "Okapi"},
- new TreeViewItem {id = 6, depth = 2, displayName = "Armadillo"},
- new TreeViewItem {id = 7, depth = 1, displayName = "Reptiles"},
- new TreeViewItem {id = 8, depth = 2, displayName = "Crocodile"},
- new TreeViewItem {id = 9, depth = 2, displayName = "Lizard"},
- };
- // Utility method that initializes the TreeViewItem.children and .parent for all items.
- SetupParentsAndChildrenFromDepths(root, allItems);
- // Return root of the tree
- return root;
- }
- }
复制代码 另一种方式添加 Item:
- protected override TreeViewItem BuildRoot()
- {
- // BuildRoot is called every time Reload is called to ensure that TreeViewItems
- // are created from data. Here we create a fixed set of items. In a real world example,
- // a data model should be passed into the TreeView and the items created from the model.
- // This section illustrates that IDs should be unique. The root item is required to
- // have a depth of -1, and the rest of the items increment from that.
- var root = new TreeViewItem { id = 0, depth = -1, displayName = "Root" };
- //不指定depth,通过 TreeViewItem.AddChild 来添加节点
- var animals = new TreeViewItem { id = 1, displayName = "Animals" };
- root.AddChild(animals);
- var mammals = new TreeViewItem { id = 2, displayName = "Mammals" };
- animals.AddChild(mammals);
- mammals.AddChild(new TreeViewItem { id = 3, displayName = "Tiger" });
- mammals.AddChild(new TreeViewItem { id = 4, displayName = "Elephant" });
- mammals.AddChild(new TreeViewItem { id = 5, displayName = "Okapi" });
- mammals.AddChild(new TreeViewItem { id = 6, displayName = "Armadillo" });
- var reptiles = new TreeViewItem { id = 7, displayName = "Reptiles" };
- animals.AddChild(reptiles);
- reptiles.AddChild(new TreeViewItem { id = 8, displayName = "Crocodile" });
- reptiles.AddChild(new TreeViewItem { id = 9, displayName = "Lizard" });
- SetupDepthsFromParentsAndChildren(root);
- // Return root of the tree
- return root;
- }
复制代码 使用 EditorWindow 显示 TreeView:
- using UnityEditor;
- using UnityEditor.IMGUI.Controls;
- using UnityEngine;
- public class SimpleTreeViewWindow : EditorWindow
- {
- // SerializeField is used to ensure the view state is written to the window
- // layout file. This means that the state survives restarting Unity as long as the window
- // is not closed. If the attribute is omitted then the state is still serialized/deserialized.
- [SerializeField]
- TreeViewState m_TreeViewState;
- //The TreeView is not serializable, so it should be reconstructed from the tree data.
- SimpleTreeView m_SimpleTreeView;
- void OnEnable()
- {
- // Check whether there is already a serialized view state (state
- // that survived assembly reloading)
- if (m_TreeViewState == null)
- m_TreeViewState = new TreeViewState();
-
- //创建 TreeView
- m_SimpleTreeView = new SimpleTreeView(m_TreeViewState);
- }
- void OnGUI()
- {
- //绘制 TreeView
- m_SimpleTreeView.OnGUI(new Rect(0, 0, position.width, position.height));
- }
- // Add menu named "My Window" to the Window menu
- [MenuItem("TreeView Examples/Simple Tree Window")]
- static void ShowWindow()
- {
- // Get existing open window or if none, make a new one:
- var window = GetWindow<SimpleTreeViewWindow>();
- window.titleContent = new GUIContent("SimpleTreeView Window");
- window.Show();
- }
- }
复制代码 Example 2: A multi-column TreeView:
最终效果:


(列Header鼠标右键菜单)
EditorWindow:
- using System;
- using System.Collections.Generic;
- using UnityEditor.Callbacks;
- using UnityEditor.IMGUI.Controls;
- using UnityEngine;
- namespace UnityEditor.TreeViewExamples
- {
- class MultiColumnWindow : EditorWindow
- {
- [NonSerialized] bool m_Initialized;
- // Serialized in the window layout file so it survives assembly reloading
- [SerializeField] TreeViewState m_TreeViewState;
- [SerializeField] MultiColumnHeaderState m_MultiColumnHeaderState;
- SearchField m_SearchField;
- MultiColumnTreeView m_TreeView;
- MyTreeAsset m_MyTreeAsset;
- [MenuItem("TreeView Examples/Multi Columns")]
- public static MultiColumnWindow GetWindow ()
- {
- var window = GetWindow<MultiColumnWindow>();
- window.titleContent = new GUIContent("Multi Columns");
- window.Focus();
- window.Repaint();
- return window;
- }
- //双击资源时触发
- [OnOpenAsset]
- public static bool OnOpenAsset (int instanceID, int line)
- {
- var myTreeAsset = EditorUtility.InstanceIDToObject (instanceID) as MyTreeAsset;
- if (myTreeAsset != null)
- {
- var window = GetWindow ();
- window.SetTreeAsset(myTreeAsset);
- return true;
- }
- return false; // we did not handle the open
- }
- void SetTreeAsset (MyTreeAsset myTreeAsset)
- {
- m_MyTreeAsset = myTreeAsset;
- m_Initialized = false;
- }
- // TreeView 显示区域
- Rect multiColumnTreeViewRect
- {
- get { return new Rect(20, 30, position.width-40, position.height-60); }
- }
- // 搜索显示区域
- Rect toolbarRect
- {
- get { return new Rect (20f, 10f, position.width-40f, 20f); }
- }
- //底部工具栏区域
- Rect bottomToolbarRect
- {
- get { return new Rect(20f, position.height - 18f, position.width - 40f, 16f); }
- }
- public MultiColumnTreeView treeView
- {
- get { return m_TreeView; }
- }
- void InitIfNeeded ()
- {
- if (!m_Initialized)
- {
- // Check if it already exists (deserialized from window layout file or scriptable object)
- if (m_TreeViewState == null)
- m_TreeViewState = new TreeViewState();
- //创建 HeaderState,包含各列头部ui相关显示
- bool firstInit = m_MultiColumnHeaderState == null;
- var headerState = MultiColumnTreeView.CreateDefaultMultiColumnHeaderState(multiColumnTreeViewRect.width);
- //应用本地数据
- if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_MultiColumnHeaderState, headerState))
- MultiColumnHeaderState.OverwriteSerializedFields(m_MultiColumnHeaderState, headerState);
- m_MultiColumnHeaderState = headerState;
- //根据 HeaderState 创建 MultiColumnHeader
- var multiColumnHeader = new MyMultiColumnHeader(headerState);
- if (firstInit)
- multiColumnHeader.ResizeToFit ();
- var treeModel = new TreeModel<MyTreeElement>(GetData());
- //根据 TreeViewState,MultiColumnHeader, TreeModel 创建 TreeView
- m_TreeView = new MultiColumnTreeView(m_TreeViewState, multiColumnHeader, treeModel);
- //搜索输入框
- m_SearchField = new SearchField();
- m_SearchField.downOrUpArrowKeyPressed += m_TreeView.SetFocusAndEnsureSelectedItem;
- m_Initialized = true;
- }
- }
-
- // 获取列表数据
- IList<MyTreeElement> GetData ()
- {
- if (m_MyTreeAsset != null && m_MyTreeAsset.treeElements != null && m_MyTreeAsset.treeElements.Count > 0)
- return m_MyTreeAsset.treeElements;
- // generate some test data
- return MyTreeElementGenerator.GenerateRandomTree(130);
- }
- // 选择某个资源时触发
- void OnSelectionChange ()
- {
- if (!m_Initialized)
- return;
- var myTreeAsset = Selection.activeObject as MyTreeAsset;
- if (myTreeAsset != null && myTreeAsset != m_MyTreeAsset)
- {
- m_MyTreeAsset = myTreeAsset;
- m_TreeView.treeModel.SetData (GetData ());
- m_TreeView.Reload ();
- }
- }
- void OnGUI ()
- {
- //初始化
- InitIfNeeded();
- SearchBar (toolbarRect);
- DoTreeView (multiColumnTreeViewRect);
- BottomToolBar (bottomToolbarRect);
- }
- void SearchBar (Rect rect)
- {
- //绘制搜索输入框
- treeView.searchString = m_SearchField.OnGUI (rect, treeView.searchString);
- }
- void DoTreeView (Rect rect)
- {
- //绘制 TreeView
- m_TreeView.OnGUI(rect);
- }
- //底部工具栏
- void BottomToolBar (Rect rect)
- {
- GUILayout.BeginArea (rect);
- using (new EditorGUILayout.HorizontalScope ())
- {
- var style = "miniButton";
- if (GUILayout.Button("Expand All", style))
- {
- treeView.ExpandAll ();
- }
- if (GUILayout.Button("Collapse All", style))
- {
- treeView.CollapseAll ();
- }
- GUILayout.FlexibleSpace();
- GUILayout.Label (m_MyTreeAsset != null ? AssetDatabase.GetAssetPath (m_MyTreeAsset) : string.Empty);
- GUILayout.FlexibleSpace ();
- if (GUILayout.Button("Set sorting", style))
- {
- var myColumnHeader = (MyMultiColumnHeader)treeView.multiColumnHeader;
- myColumnHeader.SetSortingColumns (new int[] {4, 3, 2}, new[] {true, false, true});
- myColumnHeader.mode = MyMultiColumnHeader.Mode.LargeHeader;
- }
- GUILayout.Label ("Header: ", "minilabel");
- if (GUILayout.Button("Large", style))
- {
- var myColumnHeader = (MyMultiColumnHeader) treeView.multiColumnHeader;
- myColumnHeader.mode = MyMultiColumnHeader.Mode.LargeHeader;
- }
- if (GUILayout.Button("Default", style))
- {
- var myColumnHeader = (MyMultiColumnHeader)treeView.multiColumnHeader;
- myColumnHeader.mode = MyMultiColumnHeader.Mode.DefaultHeader;
- }
- if (GUILayout.Button("No sort", style))
- {
- var myColumnHeader = (MyMultiColumnHeader)treeView.multiColumnHeader;
- myColumnHeader.mode = MyMultiColumnHeader.Mode.MinimumHeaderWithoutSorting;
- }
- GUILayout.Space (10);
-
- if (GUILayout.Button("values <-> controls", style))
- {
- treeView.showControls = !treeView.showControls;
- }
- }
- GUILayout.EndArea();
- }
- }
- internal class MyMultiColumnHeader : MultiColumnHeader
- {
- Mode m_Mode;
- public enum Mode
- {
- LargeHeader,
- DefaultHeader,
- MinimumHeaderWithoutSorting
- }
- public MyMultiColumnHeader(MultiColumnHeaderState state)
- : base(state)
- {
- mode = Mode.DefaultHeader;
- }
- public Mode mode
- {
- get
- {
- return m_Mode;
- }
- set
- {
- m_Mode = value;
- switch (m_Mode)
- {
- case Mode.LargeHeader:
- canSort = true;
- height = 37f;
- break;
- case Mode.DefaultHeader:
- canSort = true;
- height = DefaultGUI.defaultHeight;
- break;
- case Mode.MinimumHeaderWithoutSorting:
- canSort = false;
- height = DefaultGUI.minimumHeight;
- break;
- }
- }
- }
- // 自定义 Head GUI 显示
- protected override void ColumnHeaderGUI (MultiColumnHeaderState.Column column, Rect headerRect, int columnIndex)
- {
- // Default column header gui
- base.ColumnHeaderGUI(column, headerRect, columnIndex);
- // Add additional info for large header
- if (mode == Mode.LargeHeader)
- {
- // Show example overlay stuff on some of the columns
- if (columnIndex > 2)
- {
- headerRect.xMax -= 3f;
- var oldAlignment = EditorStyles.largeLabel.alignment;
- EditorStyles.largeLabel.alignment = TextAnchor.UpperRight;
- GUI.Label(headerRect, 36 + columnIndex + "%", EditorStyles.largeLabel);
- EditorStyles.largeLabel.alignment = oldAlignment;
- }
- }
- }
- }
- }
复制代码 MultiColumnTreeView:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEditor.IMGUI.Controls;
- using UnityEngine;
- using UnityEngine.Assertions;
- namespace UnityEditor.TreeViewExamples
- {
- internal class MultiColumnTreeView : TreeViewWithTreeModel<MyTreeElement>
- {
- const float kRowHeights = 20f;
- const float kToggleWidth = 18f;
- public bool showControls = true;
- //编辑器图标资源
- static GUIContent[] s_TestIcons =
- {
- EditorGUIUtility.IconContent ("Folder Icon"),
- EditorGUIUtility.IconContent ("AudioSource Icon"),
- EditorGUIUtility.IconContent ("Camera Icon"),
- EditorGUIUtility.IconContent ("Windzone Icon"),
- EditorGUIUtility.IconContent ("GameObject Icon")
- };
- // All columns
- enum MyColumns
- {
- Icon1,
- Icon2,
- Name,
- Value1,
- Value2,
- Value3,
- }
- public enum SortOption
- {
- Name,
- Value1,
- Value2,
- Value3,
- }
- // Sort options per column
- SortOption[] m_SortOptions =
- {
- SortOption.Value1,
- SortOption.Value3,
- SortOption.Name,
- SortOption.Value1,
- SortOption.Value2,
- SortOption.Value3
- };
- //展开Tree,扁平处理,不包含Root
- public static void TreeToList (TreeViewItem root, IList<TreeViewItem> result)
- {
- if (root == null)
- throw new NullReferenceException("root");
- if (result == null)
- throw new NullReferenceException("result");
- result.Clear();
-
- if (root.children == null)
- return;
- //通过一个堆来展开
- Stack<TreeViewItem> stack = new Stack<TreeViewItem>();
- //后面的元素先入栈,后弹出
- for (int i = root.children.Count - 1; i >= 0; i--)
- stack.Push(root.children[i]);
- while (stack.Count > 0)
- {
- TreeViewItem current = stack.Pop();
- result.Add(current);
- if (current.hasChildren && current.children[0] != null)
- {
- for (int i = current.children.Count - 1; i >= 0; i--)
- {
- stack.Push(current.children[i]);
- }
- }
- }
- }
- public MultiColumnTreeView (TreeViewState state, MultiColumnHeader multicolumnHeader, TreeModel<MyTreeElement> model) : base (state, multicolumnHeader, model)
- {
- Assert.AreEqual(m_SortOptions.Length , Enum.GetValues(typeof(MyColumns)).Length, "Ensure number of sort options are in sync with number of MyColumns enum values");
- // Custom setup
- rowHeight = kRowHeights; //行高
- columnIndexForTreeFoldouts = 2; //折叠图标在哪列显示
- showAlternatingRowBackgrounds = true; //背景交替变化
- showBorder = true; //显示边框
- customFoldoutYOffset = (kRowHeights - EditorGUIUtility.singleLineHeight) * 0.5f; // center foldout in the row since we also center content. See RowGUI
- extraSpaceBeforeIconAndLabel = kToggleWidth;
- multicolumnHeader.sortingChanged += OnSortingChanged; //排序发生变化
-
- Reload();
- }
- //构建显示的行 TreeViewItem 数据
- // Note we We only build the visible rows, only the backend has the full tree information.
- // The treeview only creates info for the row list.
- protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
- {
- var rows = base.BuildRows (root);
- SortIfNeeded (root, rows);
- return rows;
- }
- void OnSortingChanged (MultiColumnHeader multiColumnHeader)
- {
- SortIfNeeded (rootItem, GetRows());
- }
- void SortIfNeeded (TreeViewItem root, IList<TreeViewItem> rows)
- {
- if (rows.Count <= 1)
- return;
-
- if (multiColumnHeader.sortedColumnIndex == -1)
- {
- return; // No column to sort for (just use the order the data are in)
- }
-
- // Sort the roots of the existing tree items
- SortByMultipleColumns ();
- TreeToList(root, rows);
- Repaint();
- }
- void SortByMultipleColumns ()
- {
- //设置排序的列
- var sortedColumns = multiColumnHeader.state.sortedColumns;
- if (sortedColumns.Length == 0)
- return;
- //只排序根节点直属的子节点
- var myTypes = rootItem.children.Cast<TreeViewItem<MyTreeElement> >();
- //首选列优先排序
- var orderedQuery = InitialOrder (myTypes, sortedColumns);
- //其他列排序
- for (int i=1; i<sortedColumns.Length; i++)
- {
- SortOption sortOption = m_SortOptions[sortedColumns[i]];
- bool ascending = multiColumnHeader.IsSortedAscending(sortedColumns[i]);
- switch (sortOption)
- {
- case SortOption.Name:
- // ThenBy 在现有排序的基础上,对于相等的元素,再深入排序
- orderedQuery = orderedQuery.ThenBy(l => l.data.name, ascending);
- break;
- case SortOption.Value1:
- orderedQuery = orderedQuery.ThenBy(l => l.data.floatValue1, ascending);
- break;
- case SortOption.Value2:
- orderedQuery = orderedQuery.ThenBy(l => l.data.floatValue2, ascending);
- break;
- case SortOption.Value3:
- orderedQuery = orderedQuery.ThenBy(l => l.data.floatValue3, ascending);
- break;
- }
- }
- rootItem.children = orderedQuery.Cast<TreeViewItem> ().ToList ();
- }
- IOrderedEnumerable<TreeViewItem<MyTreeElement>> InitialOrder(IEnumerable<TreeViewItem<MyTreeElement>> myTypes, int[] history)
- {
- SortOption sortOption = m_SortOptions[history[0]];
- //是否升序
- bool ascending = multiColumnHeader.IsSortedAscending(history[0]);
- switch (sortOption)
- {
- case SortOption.Name:
- return myTypes.Order(l => l.data.name, ascending);
- case SortOption.Value1:
- return myTypes.Order(l => l.data.floatValue1, ascending);
- case SortOption.Value2:
- return myTypes.Order(l => l.data.floatValue2, ascending);
- case SortOption.Value3:
- return myTypes.Order(l => l.data.floatValue3, ascending);
- default:
- Assert.IsTrue(false, "Unhandled enum");
- break;
- }
- // default
- return myTypes.Order(l => l.data.name, ascending);
- }
- int GetIcon1Index(TreeViewItem<MyTreeElement> item)
- {
- return (int)(Mathf.Min(0.99f, item.data.floatValue1) * s_TestIcons.Length);
- }
- int GetIcon2Index (TreeViewItem<MyTreeElement> item)
- {
- return Mathf.Min(item.data.text.Length, s_TestIcons.Length-1);
- }
- //绘制一行的 GUI
- protected override void RowGUI (RowGUIArgs args)
- {
- var item = (TreeViewItem<MyTreeElement>) args.item;
- //列可通过Header菜单隐藏,故需要 args.GetNumVisibleColumns
- for (int i = 0; i < args.GetNumVisibleColumns (); ++i)
- {
- //绘制每列
- CellGUI(args.GetCellRect(i), item, (MyColumns)args.GetColumn(i), ref args);
- }
- }
- void CellGUI (Rect cellRect, TreeViewItem<MyTreeElement> item, MyColumns column, ref RowGUIArgs args)
- {
- // Center cell rect vertically (makes it easier to place controls, icons etc in the cells)
- CenterRectUsingSingleLineHeight(ref cellRect);
- //根据类型绘制 GUI
- switch (column)
- {
- case MyColumns.Icon1:
- {
- GUI.DrawTexture(cellRect, s_TestIcons[GetIcon1Index(item)].image, ScaleMode.ScaleToFit);
- }
- break;
- case MyColumns.Icon2:
- {
- GUI.DrawTexture(cellRect, s_TestIcons[GetIcon2Index(item)].image, ScaleMode.ScaleToFit);
- }
- break;
- case MyColumns.Name:
- {
- // Do toggle
- Rect toggleRect = cellRect;
- toggleRect.x += GetContentIndent(item);
- toggleRect.width = kToggleWidth;
- if (toggleRect.xMax < cellRect.xMax)
- item.data.enabled = EditorGUI.Toggle(toggleRect, item.data.enabled); // hide when outside cell rect
- // Default icon and label
- args.rowRect = cellRect;
- base.RowGUI(args);
- }
- break;
- case MyColumns.Value1:
- case MyColumns.Value2:
- case MyColumns.Value3:
- {
- if (showControls)
- {
- cellRect.xMin += 5f; // When showing controls make some extra spacing
- if (column == MyColumns.Value1)
- item.data.floatValue1 = EditorGUI.Slider(cellRect, GUIContent.none, item.data.floatValue1, 0f, 1f);
- if (column == MyColumns.Value2)
- item.data.material = (Material)EditorGUI.ObjectField(cellRect, GUIContent.none, item.data.material, typeof(Material), false);
- if (column == MyColumns.Value3)
- item.data.text = GUI.TextField(cellRect, item.data.text);
- }
- else
- {
- string value = "Missing";
- if (column == MyColumns.Value1)
- value = item.data.floatValue1.ToString("f5");
- if (column == MyColumns.Value2)
- value = item.data.floatValue2.ToString("f5");
- if (column == MyColumns.Value3)
- value = item.data.floatValue3.ToString("f5");
- DefaultGUI.LabelRightAligned(cellRect, value, args.selected, args.focused);
- }
- }
- break;
- }
- }
- // Rename
- //--------
- protected override bool CanRename(TreeViewItem item)
- {
- // Only allow rename if we can show the rename overlay with a certain width (label might be clipped by other columns)
- Rect renameRect = GetRenameRect (treeViewRect, 0, item);
- return renameRect.width > 30;
- }
- protected override void RenameEnded(RenameEndedArgs args)
- {
- // Set the backend name and reload the tree to reflect the new model
- if (args.acceptedRename)
- {
- var element = treeModel.Find(args.itemID);
- element.name = args.newName;
- Reload();
- }
- }
- protected override Rect GetRenameRect (Rect rowRect, int row, TreeViewItem item)
- {
- Rect cellRect = GetCellRectForTreeFoldouts (rowRect);
- CenterRectUsingSingleLineHeight(ref cellRect);
- return base.GetRenameRect (cellRect, row, item);
- }
- // Misc
- //--------
- protected override bool CanMultiSelect (TreeViewItem item)
- {
- return true;
- }
- //创建 HeaderState
- public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState(float treeViewWidth)
- {
- var columns = new[]
- {
- new MultiColumnHeaderState.Column
- {
- headerContent = new GUIContent(EditorGUIUtility.FindTexture("FilterByLabel"), "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "),
- contextMenuText = "Asset", //右键点击出现的菜单,可配合 allowToggleVisibility 来控制该列是否显示
- headerTextAlignment = TextAlignment.Center,
- sortedAscending = true,
- sortingArrowAlignment = TextAlignment.Right,
- width = 30,
- minWidth = 30,
- maxWidth = 60,
- autoResize = false,
- allowToggleVisibility = true
- },
- new MultiColumnHeaderState.Column
- {
- headerContent = new GUIContent(EditorGUIUtility.FindTexture("FilterByType"), "Sed hendrerit mi enim, eu iaculis leo tincidunt at."),
- contextMenuText = "Type",
- headerTextAlignment = TextAlignment.Center,
- sortedAscending = true,
- sortingArrowAlignment = TextAlignment.Right,
- width = 30,
- minWidth = 30,
- maxWidth = 60,
- autoResize = false,
- allowToggleVisibility = true
- },
- new MultiColumnHeaderState.Column
- {
- headerContent = new GUIContent("Name"),
- headerTextAlignment = TextAlignment.Left,
- sortedAscending = true,
- sortingArrowAlignment = TextAlignment.Center,
- width = 150,
- minWidth = 60,
- autoResize = false,
- allowToggleVisibility = false
- },
- new MultiColumnHeaderState.Column
- {
- headerContent = new GUIContent("Multiplier", "In sed porta ante. Nunc et nulla mi."),
- headerTextAlignment = TextAlignment.Right,
- sortedAscending = true,
- sortingArrowAlignment = TextAlignment.Left,
- width = 110,
- minWidth = 60,
- autoResize = true
- },
- new MultiColumnHeaderState.Column
- {
- headerContent = new GUIContent("Material", "Maecenas congue non tortor eget vulputate."),
- headerTextAlignment = TextAlignment.Right,
- sortedAscending = true,
- sortingArrowAlignment = TextAlignment.Left,
- width = 95,
- minWidth = 60,
- autoResize = true,
- allowToggleVisibility = true
- },
- new MultiColumnHeaderState.Column
- {
- headerContent = new GUIContent("Note", "Nam at tellus ultricies ligula vehicula ornare sit amet quis metus."),
- headerTextAlignment = TextAlignment.Right,
- sortedAscending = true,
- sortingArrowAlignment = TextAlignment.Left,
- width = 70,
- minWidth = 60,
- autoResize = true
- }
- };
- Assert.AreEqual(columns.Length, Enum.GetValues(typeof(MyColumns)).Length, "Number of columns should match number of enum values: You probably forgot to update one of them.");
- var state = new MultiColumnHeaderState(columns);
- return state;
- }
- }
- static class MyExtensionMethods
- {
- public static IOrderedEnumerable<T> Order<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector, bool ascending)
- {
- if (ascending)
- {
- return source.OrderBy(selector);
- }
- else
- {
- return source.OrderByDescending(selector);
- }
- }
- public static IOrderedEnumerable<T> ThenBy<T, TKey>(this IOrderedEnumerable<T> source, Func<T, TKey> selector, bool ascending)
- {
- if (ascending)
- {
- return source.ThenBy(selector);
- }
- else
- {
- return source.ThenByDescending(selector);
- }
- }
- }
- }
复制代码 基类 TreeViewWithTreeModel:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEditor.IMGUI.Controls;
- using UnityEngine;
- namespace UnityEditor.TreeViewExamples
- {
- internal class TreeViewItem<T> : TreeViewItem where T : TreeElement
- {
- public T data { get; set; }
- public TreeViewItem (int id, int depth, string displayName, T data) : base (id, depth, displayName)
- {
- this.data = data;
- }
- }
- internal class TreeViewWithTreeModel<T> : TreeView where T : TreeElement
- {
- TreeModel<T> m_TreeModel;
- readonly List<TreeViewItem> m_Rows = new List<TreeViewItem>(100);
- public event Action treeChanged;
- public TreeModel<T> treeModel { get { return m_TreeModel; } }
- public event Action<IList<TreeViewItem>> beforeDroppingDraggedItems;
- public TreeViewWithTreeModel (TreeViewState state, TreeModel<T> model) : base (state)
- {
- Init (model);
- }
- public TreeViewWithTreeModel (TreeViewState state, MultiColumnHeader multiColumnHeader, TreeModel<T> model)
- : base(state, multiColumnHeader)
- {
- Init (model);
- }
- void Init (TreeModel<T> model)
- {
- m_TreeModel = model;
- m_TreeModel.modelChanged += ModelChanged;
- }
- void ModelChanged ()
- {
- if (treeChanged != null)
- treeChanged ();
- Reload ();
- }
- //构建根节点
- protected override TreeViewItem BuildRoot()
- {
- int depthForHiddenRoot = -1;
- return new TreeViewItem<T>(m_TreeModel.root.id, depthForHiddenRoot, m_TreeModel.root.name, m_TreeModel.root);
- }
- //构建显示的行 TreeViewItem
- protected override IList<TreeViewItem> BuildRows (TreeViewItem root)
- {
- if (m_TreeModel.root == null)
- {
- Debug.LogError ("tree model root is null. did you call SetData()?");
- }
- m_Rows.Clear ();
- if (!string.IsNullOrEmpty(searchString))
- {
- Search (m_TreeModel.root, searchString, m_Rows);
- }
- else
- {
- if (m_TreeModel.root.hasChildren)
- AddChildrenRecursive(m_TreeModel.root, 0, m_Rows);
- }
- // We still need to setup the child parent information for the rows since this
- // information is used by the TreeView internal logic (navigation, dragging etc)
- SetupParentsAndChildrenFromDepths (root, m_Rows);
- return m_Rows;
- }
- void AddChildrenRecursive (T parent, int depth, IList<TreeViewItem> newRows)
- {
- foreach (T child in parent.children)
- {
- var item = new TreeViewItem<T>(child.id, depth, child.name, child);
- newRows.Add(item);
- if (child.hasChildren)
- {
- if (IsExpanded(child.id)) //展开显示的行
- {
- AddChildrenRecursive (child, depth + 1, newRows);
- }
- else
- {
- item.children = CreateChildListForCollapsedParent();
- }
- }
- }
- }
- void Search(T searchFromThis, string search, List<TreeViewItem> result)
- {
- if (string.IsNullOrEmpty(search))
- throw new ArgumentException("Invalid search: cannot be null or empty", "search");
- const int kItemDepth = 0; // tree is flattened when searching
- Stack<T> stack = new Stack<T>();
- foreach (var element in searchFromThis.children)
- stack.Push((T)element);
- while (stack.Count > 0)
- {
- T current = stack.Pop();
- // 名字匹配的行
- if (current.name.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0)
- {
- result.Add(new TreeViewItem<T>(current.id, kItemDepth, current.name, current));
- }
- if (current.children != null && current.children.Count > 0)
- {
- foreach (var element in current.children)
- {
- stack.Push((T)element);
- }
- }
- }
- SortSearchResult(result);
- }
- protected virtual void SortSearchResult (List<TreeViewItem> rows)
- {
- rows.Sort ((x,y) => EditorUtility.NaturalCompare (x.displayName, y.displayName)); // sort by displayName by default, can be overriden for multicolumn solutions
- }
-
- protected override IList<int> GetAncestors (int id)
- {
- return m_TreeModel.GetAncestors(id);
- }
- protected override IList<int> GetDescendantsThatHaveChildren (int id)
- {
- return m_TreeModel.GetDescendantsThatHaveChildren(id);
- }
- // Dragging
- //-----------
-
- const string k_GenericDragID = "GenericDragColumnDragging";
- protected override bool CanStartDrag (CanStartDragArgs args)
- {
- return true;
- }
- protected override void SetupDragAndDrop(SetupDragAndDropArgs args)
- {
- if (hasSearch)
- return;
- DragAndDrop.PrepareStartDrag();
- var draggedRows = GetRows().Where(item => args.draggedItemIDs.Contains(item.id)).ToList();
- DragAndDrop.SetGenericData(k_GenericDragID, draggedRows);
- DragAndDrop.objectReferences = new UnityEngine.Object[] { }; // this IS required for dragging to work
- string title = draggedRows.Count == 1 ? draggedRows[0].displayName : "< Multiple >";
- DragAndDrop.StartDrag (title);
- }
- protected override DragAndDropVisualMode HandleDragAndDrop (DragAndDropArgs args)
- {
- // Check if we can handle the current drag data (could be dragged in from other areas/windows in the editor)
- var draggedRows = DragAndDrop.GetGenericData(k_GenericDragID) as List<TreeViewItem>;
- if (draggedRows == null)
- return DragAndDropVisualMode.None;
- // Parent item is null when dragging outside any tree view items.
- switch (args.dragAndDropPosition)
- {
- case DragAndDropPosition.UponItem:
- case DragAndDropPosition.BetweenItems:
- {
- bool validDrag = ValidDrag(args.parentItem, draggedRows);
- if (args.performDrop && validDrag)
- {
- T parentData = ((TreeViewItem<T>)args.parentItem).data;
- OnDropDraggedElementsAtIndex(draggedRows, parentData, args.insertAtIndex == -1 ? 0 : args.insertAtIndex);
- }
- return validDrag ? DragAndDropVisualMode.Move : DragAndDropVisualMode.None;
- }
- case DragAndDropPosition.OutsideItems:
- {
- if (args.performDrop)
- OnDropDraggedElementsAtIndex(draggedRows, m_TreeModel.root, m_TreeModel.root.children.Count);
- return DragAndDropVisualMode.Move;
- }
- default:
- Debug.LogError("Unhandled enum " + args.dragAndDropPosition);
- return DragAndDropVisualMode.None;
- }
- }
- public virtual void OnDropDraggedElementsAtIndex (List<TreeViewItem> draggedRows, T parent, int insertIndex)
- {
- if (beforeDroppingDraggedItems != null)
- beforeDroppingDraggedItems (draggedRows);
- var draggedElements = new List<TreeElement> ();
- foreach (var x in draggedRows)
- draggedElements.Add (((TreeViewItem<T>) x).data);
-
- var selectedIDs = draggedElements.Select (x => x.id).ToArray();
- m_TreeModel.MoveElements (parent, insertIndex, draggedElements);
- SetSelection(selectedIDs, TreeViewSelectionOptions.RevealAndFrame);
- }
- bool ValidDrag(TreeViewItem parent, List<TreeViewItem> draggedItems)
- {
- TreeViewItem currentParent = parent;
- while (currentParent != null)
- {
- if (draggedItems.Contains(currentParent))
- return false;
- currentParent = currentParent.parent;
- }
- return true;
- }
-
- }
- }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |