BindingTreeViewDataSource.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEditor;
  5. using UnityEditor.IMGUI.Controls;
  6. using UnityEditorInternal;
  7. using UnityEngine;
  8. using UnityEngine.Timeline;
  9. namespace UnityEditor.Timeline
  10. {
  11. class BindingTreeViewDataSource : TreeViewDataSource
  12. {
  13. struct BindingGroup : IEquatable<BindingGroup>, IComparable<BindingGroup>
  14. {
  15. public readonly string GroupName;
  16. public readonly string Path;
  17. public readonly Type Type;
  18. public BindingGroup(string path, string groupName, Type type)
  19. {
  20. Path = path;
  21. GroupName = groupName;
  22. Type = type;
  23. }
  24. public string groupDisplayName => string.IsNullOrEmpty(Path) ? GroupName : string.Format($"{Path} : {GroupName}");
  25. public bool Equals(BindingGroup other) => GroupName == other.GroupName && Type == other.Type && Path == other.Path;
  26. public int CompareTo(BindingGroup other) => GetHashCode() - other.GetHashCode();
  27. public override bool Equals(object obj)=> obj is BindingGroup other && Equals(other);
  28. public override int GetHashCode()
  29. {
  30. return HashUtility.CombineHash(GroupName != null ? GroupName.GetHashCode() : 0, Type != null ? Type.GetHashCode() : 0, Path != null ? Path.GetHashCode() : 0);
  31. }
  32. }
  33. static readonly string s_DefaultValue = L10n.Tr("{0} (Default Value)");
  34. public const int RootID = int.MinValue;
  35. public const int GroupID = -1;
  36. private readonly AnimationClip m_Clip;
  37. private readonly CurveDataSource m_CurveDataSource;
  38. public BindingTreeViewDataSource(
  39. TreeViewController treeView, AnimationClip clip, CurveDataSource curveDataSource)
  40. : base(treeView)
  41. {
  42. m_Clip = clip;
  43. showRootItem = false;
  44. m_CurveDataSource = curveDataSource;
  45. }
  46. void SetupRootNodeSettings()
  47. {
  48. showRootItem = false;
  49. SetExpanded(RootID, true);
  50. SetExpanded(GroupID, true);
  51. }
  52. public override void FetchData()
  53. {
  54. if (m_Clip == null)
  55. return;
  56. var bindings = AnimationUtility.GetCurveBindings(m_Clip)
  57. .Union(AnimationUtility.GetObjectReferenceCurveBindings(m_Clip))
  58. .ToArray();
  59. // a sorted linear list of nodes
  60. var results = bindings.GroupBy(GetBindingGroup, p => p, CreateTuple)
  61. .OrderBy(t => t.Item1.Path)
  62. .ThenBy(NamePrioritySort)
  63. // this makes component ordering match the animation window
  64. .ThenBy(t => t.Item1.Type.ToString())
  65. .ThenBy(t => t.Item1.GroupName).ToArray();
  66. m_RootItem = new CurveTreeViewNode(RootID, null, "root", null)
  67. {
  68. children = new List<TreeViewItem>(1)
  69. };
  70. if (results.Any())
  71. {
  72. var groupingNode = new CurveTreeViewNode(GroupID, m_RootItem, m_CurveDataSource.groupingName, bindings)
  73. {
  74. children = new List<TreeViewItem>()
  75. };
  76. m_RootItem.children.Add(groupingNode);
  77. foreach (var r in results)
  78. {
  79. var key = r.Item1;
  80. var nodeBindings = r.Item2;
  81. FillMissingTransformCurves(nodeBindings);
  82. if (nodeBindings.Count == 1)
  83. groupingNode.children.Add(CreateLeafNode(nodeBindings[0], groupingNode, PropertyName(nodeBindings[0], true)));
  84. else if (nodeBindings.Count > 1)
  85. {
  86. var childBindings = nodeBindings.OrderBy(BindingSort).ToArray();
  87. var parent = new CurveTreeViewNode(key.GetHashCode(), groupingNode, key.groupDisplayName, childBindings) {children = new List<TreeViewItem>()};
  88. groupingNode.children.Add(parent);
  89. foreach (var b in childBindings)
  90. parent.children.Add(CreateLeafNode(b, parent, PropertyName(b, false)));
  91. }
  92. }
  93. SetupRootNodeSettings();
  94. }
  95. m_NeedRefreshRows = true;
  96. }
  97. public void UpdateData()
  98. {
  99. m_TreeView.ReloadData();
  100. }
  101. string GroupName(EditorCurveBinding binding)
  102. {
  103. var propertyName = m_CurveDataSource.ModifyPropertyDisplayName(binding.path, binding.propertyName);
  104. return CleanUpArrayBinding(AnimationWindowUtility.NicifyPropertyGroupName(binding.type, propertyName), true);
  105. }
  106. static string CleanUpArrayBinding(string propertyName, bool isGroup)
  107. {
  108. const string arrayIndicator = ".Array.data[";
  109. const string arrayDisplay = ".data[";
  110. var arrayIndex = propertyName.LastIndexOf(arrayIndicator, StringComparison.Ordinal);
  111. if (arrayIndex == -1)
  112. return propertyName;
  113. if (isGroup)
  114. propertyName = propertyName.Substring(0, arrayIndex);
  115. return propertyName.Replace(arrayIndicator, arrayDisplay );
  116. }
  117. string PropertyName(EditorCurveBinding binding, bool prependPathName)
  118. {
  119. var propertyName = m_CurveDataSource.ModifyPropertyDisplayName(binding.path, binding.propertyName);
  120. propertyName = CleanUpArrayBinding(AnimationWindowUtility.GetPropertyDisplayName(propertyName), false);
  121. if (binding.isPhantom)
  122. propertyName = string.Format(s_DefaultValue, propertyName);
  123. if (prependPathName && !string.IsNullOrEmpty(binding.path))
  124. propertyName = $"{binding.path} : {propertyName}";
  125. return propertyName;
  126. }
  127. BindingGroup GetBindingGroup(EditorCurveBinding binding)
  128. {
  129. return new BindingGroup(binding.path ?? string.Empty, GroupName(binding), binding.type);
  130. }
  131. static CurveTreeViewNode CreateLeafNode(EditorCurveBinding binding, TreeViewItem parent, string displayName)
  132. {
  133. return new CurveTreeViewNode(binding.GetHashCode(), parent, displayName, new[] { binding }, AnimationWindowUtility.ForceGrouping(binding));
  134. }
  135. static void FillMissingTransformCurves(List<EditorCurveBinding> bindings)
  136. {
  137. if (!AnimationWindowUtility.IsActualTransformCurve(bindings[0]) || bindings.Count >= 3)
  138. return;
  139. var binding = bindings[0];
  140. var prefixPropertyName = binding.propertyName.Split('.').First();
  141. binding.isPhantom = true;
  142. if (!bindings.Any(p => p.propertyName.EndsWith(".x")))
  143. {
  144. binding.propertyName = prefixPropertyName + ".x";
  145. bindings.Insert(0, binding);
  146. }
  147. if (!bindings.Any(p => p.propertyName.EndsWith(".y")))
  148. {
  149. binding.propertyName = prefixPropertyName + ".y";
  150. bindings.Insert(1, binding);
  151. }
  152. if (!bindings.Any(p => p.propertyName.EndsWith(".z")))
  153. {
  154. binding.propertyName = prefixPropertyName + ".z";
  155. bindings.Insert(2, binding);
  156. }
  157. }
  158. // make sure vectors and colors are sorted correctly in their subgroups
  159. static int BindingSort(EditorCurveBinding b)
  160. {
  161. return AnimationWindowUtility.GetComponentIndex(b.propertyName);
  162. }
  163. static int NamePrioritySort(ValueTuple<BindingGroup, List<EditorCurveBinding>> group)
  164. {
  165. if (group.Item1.Type != typeof(Transform))
  166. return 0;
  167. switch (group.Item1.GroupName)
  168. {
  169. case "Position" : return Int32.MinValue;
  170. case "Rotation" : return Int32.MinValue+1;
  171. case "Scale" : return Int32.MinValue+2;
  172. default : return 0;
  173. }
  174. }
  175. static ValueTuple<BindingGroup, List<EditorCurveBinding>> CreateTuple(BindingGroup key, IEnumerable<EditorCurveBinding> items)
  176. {
  177. return new ValueTuple<BindingGroup, List<EditorCurveBinding>>()
  178. {
  179. Item1 = key,
  180. Item2 = items.ToList()
  181. };
  182. }
  183. }
  184. }