GUIAnimFieldDisplay.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using BansheeEngine;
  7. namespace BansheeEditor
  8. {
  9. /** @addtogroup AnimationEditor
  10. * @{
  11. */
  12. internal class GUIAnimFieldDisplay
  13. {
  14. private SceneObject root;
  15. private int width;
  16. private int height;
  17. private GUIScrollArea scrollArea;
  18. private List<AnimFieldInfo> fieldInfos = new List<AnimFieldInfo>();
  19. private GUIAnimFieldEntry[] fields;
  20. private GUIAnimFieldLayouts layouts;
  21. public GUIAnimFieldDisplay(GUILayout layout, int width, int height, SceneObject root)
  22. {
  23. this.root = root;
  24. scrollArea = new GUIScrollArea(ScrollBarType.ShowIfDoesntFit, ScrollBarType.NeverShow);
  25. layout.AddElement(scrollArea);
  26. SetSize(width, height);
  27. }
  28. public Action<string> OnEntrySelected;
  29. public void SetSize(int width, int height)
  30. {
  31. this.width = width;
  32. this.height = height;
  33. scrollArea.SetWidth(width);
  34. scrollArea.SetHeight(height);
  35. Rebuild();
  36. }
  37. public void SetFields(AnimFieldInfo[] fields)
  38. {
  39. this.fieldInfos.Clear();
  40. this.fieldInfos.AddRange(fields);
  41. Rebuild();
  42. }
  43. public void AddField(AnimFieldInfo field)
  44. {
  45. bool exists = fieldInfos.Exists(x =>
  46. {
  47. return x.path == field.path;
  48. });
  49. if (!exists)
  50. {
  51. fieldInfos.Add(field);
  52. Rebuild();
  53. }
  54. }
  55. public void SetDisplayValues(GUIAnimFieldPathValue[] values)
  56. {
  57. for(int i = 0; i < values.Length; i++)
  58. Debug.Log(i + ". " + values[i].value);
  59. for (int i = 0; i < fields.Length; i++)
  60. {
  61. string path = fields[i].Path;
  62. for (int j = 0; j < values.Length; j++)
  63. {
  64. if(path == values[j].path)
  65. fields[i].SetValue(values[j].value);
  66. }
  67. }
  68. }
  69. public void SetSelection(string[] paths)
  70. {
  71. Action<GUIAnimFieldEntry> updateSelection = field =>
  72. {
  73. bool foundSelected = false;
  74. for (int j = 0; j < paths.Length; j++)
  75. {
  76. if (field.Path == paths[j])
  77. {
  78. field.SetSelection(true);
  79. foundSelected = true;
  80. break;
  81. }
  82. }
  83. if (!foundSelected)
  84. field.SetSelection(false);
  85. };
  86. for (int i = 0; i < fields.Length; i++)
  87. {
  88. updateSelection(fields[i]);
  89. // Check children (only one level allowed)
  90. GUIAnimFieldEntry[] children = fields[i].GetChildren();
  91. if (children == null)
  92. continue;
  93. for (int j = 0; j < children.Length; j++)
  94. updateSelection(children[j]);
  95. }
  96. }
  97. private void Rebuild()
  98. {
  99. scrollArea.Layout.Clear();
  100. fields = null;
  101. if (fieldInfos == null || root == null)
  102. return;
  103. layouts = new GUIAnimFieldLayouts();
  104. GUIPanel rootPanel = scrollArea.Layout.AddPanel();
  105. GUIPanel mainPanel = rootPanel.AddPanel();
  106. GUIPanel underlayPanel = rootPanel.AddPanel(1);
  107. GUIPanel overlayPanel = rootPanel.AddPanel(-1);
  108. GUIPanel backgroundPanel = rootPanel.AddPanel(2);
  109. layouts.main = mainPanel.AddLayoutY();
  110. layouts.underlay = underlayPanel.AddLayoutY();
  111. layouts.overlay = overlayPanel.AddLayoutY();
  112. layouts.background = backgroundPanel.AddLayoutY();
  113. GUIButton catchAll = new GUIButton("", EditorStyles.Blank);
  114. catchAll.Bounds = new Rect2I(0, 0, width, height);
  115. catchAll.OnClick += () => OnEntrySelected(null);
  116. underlayPanel.AddElement(catchAll);
  117. layouts.main.AddSpace(5);
  118. layouts.underlay.AddSpace(5);
  119. layouts.overlay.AddSpace(5);
  120. layouts.background.AddSpace(3); // Minor hack: Background starts heigher to get it to center better
  121. fields = new GUIAnimFieldEntry[fieldInfos.Count];
  122. for (int i = 0; i < fieldInfos.Count; i++)
  123. {
  124. if (string.IsNullOrEmpty(fieldInfos[i].path))
  125. continue;
  126. bool entryIsMissing;
  127. if (fieldInfos[i].isUserCurve)
  128. {
  129. string pathSuffix;
  130. SerializableProperty property = Animation.FindProperty(root, fieldInfos[i].path, out pathSuffix);
  131. entryIsMissing = property == null;
  132. }
  133. else
  134. entryIsMissing = false;
  135. if (!entryIsMissing)
  136. {
  137. switch (fieldInfos[i].type)
  138. {
  139. case SerializableProperty.FieldType.Vector2:
  140. fields[i] = new GUIAnimVec2Entry(layouts, fieldInfos[i].path);
  141. break;
  142. case SerializableProperty.FieldType.Vector3:
  143. fields[i] = new GUIAnimVec3Entry(layouts, fieldInfos[i].path);
  144. break;
  145. case SerializableProperty.FieldType.Vector4:
  146. fields[i] = new GUIAnimVec4Entry(layouts, fieldInfos[i].path);
  147. break;
  148. case SerializableProperty.FieldType.Color:
  149. fields[i] = new GUIAnimColorEntry(layouts, fieldInfos[i].path);
  150. break;
  151. case SerializableProperty.FieldType.Bool:
  152. case SerializableProperty.FieldType.Int:
  153. case SerializableProperty.FieldType.Float:
  154. fields[i] = new GUIAnimSimpleEntry(layouts, fieldInfos[i].path);
  155. break;
  156. }
  157. }
  158. else
  159. {
  160. fields[i] = new GUIAnimMissingEntry(layouts, fieldInfos[i].path);
  161. }
  162. if (fields[i] != null)
  163. fields[i].OnEntrySelected += OnEntrySelected;
  164. }
  165. layouts.main.AddSpace(5);
  166. layouts.underlay.AddSpace(5);
  167. layouts.overlay.AddSpace(5);
  168. layouts.background.AddSpace(5);
  169. layouts.main.AddFlexibleSpace();
  170. layouts.underlay.AddFlexibleSpace();
  171. layouts.overlay.AddFlexibleSpace();
  172. layouts.background.AddFlexibleSpace();
  173. }
  174. }
  175. internal class GUIAnimFieldLayouts
  176. {
  177. public GUILayout main;
  178. public GUILayout underlay;
  179. public GUILayout overlay;
  180. public GUILayout background;
  181. }
  182. internal struct GUIAnimFieldPathValue
  183. {
  184. public string path;
  185. public object value;
  186. }
  187. internal abstract class GUIAnimFieldEntry
  188. {
  189. private const int MAX_PATH_LENGTH = 30;
  190. protected const int INDENT_AMOUNT = 10;
  191. protected string path;
  192. private GUIButton selectionBtn;
  193. private GUITexture backgroundTexture;
  194. private int entryHeight;
  195. public Action<string> OnEntrySelected;
  196. public string Path { get { return path; } }
  197. public GUIAnimFieldEntry(GUIAnimFieldLayouts layouts, string path, bool child)
  198. {
  199. this.path = path;
  200. GUILayoutX toggleLayout = layouts.main.AddLayoutX();
  201. toggleLayout.AddSpace(child ? 30 : 15);
  202. selectionBtn = new GUIButton(GetDisplayName(path, child), EditorStyles.Label, GUIOption.FlexibleWidth());
  203. selectionBtn.OnClick += () =>
  204. {
  205. OnEntrySelected?.Invoke(path);
  206. };
  207. toggleLayout.AddElement(selectionBtn);
  208. entryHeight = selectionBtn.Bounds.height;
  209. backgroundTexture = new GUITexture(Builtin.WhiteTexture, GUITextureScaleMode.StretchToFit,
  210. GUIOption.FlexibleWidth());
  211. backgroundTexture.SetTint(Color.Transparent);
  212. backgroundTexture.SetHeight(entryHeight);
  213. layouts.background.AddElement(backgroundTexture);
  214. }
  215. public virtual void Toggle(bool on)
  216. {
  217. selectionBtn.Active = on;
  218. backgroundTexture.Active = on;
  219. }
  220. public void SetSelection(bool selected)
  221. {
  222. if(selected)
  223. backgroundTexture.SetTint(Color.DarkCyan);
  224. else
  225. backgroundTexture.SetTint(Color.Transparent);
  226. }
  227. public virtual void SetValue(object value) { }
  228. public virtual GUIAnimFieldEntry[] GetChildren()
  229. {
  230. return null;
  231. }
  232. protected int GetEntryHeight()
  233. {
  234. return entryHeight;
  235. }
  236. protected static string GetDisplayName(string path, bool shortName)
  237. {
  238. if (string.IsNullOrEmpty(path))
  239. return "";
  240. string soName;
  241. string compName;
  242. string propertyPath;
  243. string trimmedPath = path.Trim('/');
  244. GetNames(trimmedPath, shortName, out soName, out compName, out propertyPath);
  245. if (propertyPath == null)
  246. return "";
  247. if (shortName)
  248. return propertyPath;
  249. else
  250. {
  251. string truncatedPropPath;
  252. if (propertyPath.Length > MAX_PATH_LENGTH)
  253. truncatedPropPath = "..." + propertyPath.Substring(propertyPath.Length - MAX_PATH_LENGTH);
  254. else
  255. truncatedPropPath = propertyPath;
  256. if (soName != null)
  257. {
  258. if (compName != null)
  259. return soName + "(" + compName + ") - " + truncatedPropPath;
  260. else
  261. return soName + " - " + truncatedPropPath;
  262. }
  263. else
  264. {
  265. if (compName != null)
  266. return "(" + compName + ") - " + truncatedPropPath;
  267. else
  268. return truncatedPropPath;
  269. }
  270. }
  271. }
  272. protected static void GetNames(string path, bool shortName, out string soName, out string compName, out string propertyPath)
  273. {
  274. string[] entries = path.Split('/');
  275. // Find name of the last scene object in the path
  276. int pathIdx = 0;
  277. soName = null;
  278. for (; pathIdx < entries.Length; pathIdx++)
  279. {
  280. string entry = entries[pathIdx];
  281. if (string.IsNullOrEmpty(entry))
  282. continue;
  283. // Not a scene object, break
  284. if (entry[0] != '!')
  285. {
  286. if (pathIdx > 0)
  287. {
  288. string prevEntry = entries[pathIdx - 1];
  289. soName = prevEntry.Substring(1, prevEntry.Length - 1);
  290. }
  291. break;
  292. }
  293. }
  294. if (pathIdx >= entries.Length)
  295. {
  296. compName = null;
  297. propertyPath = null;
  298. return;
  299. }
  300. // If path is referencing a component, find it
  301. {
  302. string entry = entries[pathIdx];
  303. if (entry[0] == ':')
  304. compName = entry.Substring(1, entry.Length - 1);
  305. else
  306. compName = null;
  307. }
  308. // Look for a field name
  309. if (compName != null)
  310. {
  311. pathIdx++;
  312. if (pathIdx >= entries.Length)
  313. {
  314. propertyPath = null;
  315. return;
  316. }
  317. }
  318. if (shortName)
  319. {
  320. if (pathIdx < entries.Length)
  321. propertyPath = entries[entries.Length - 1];
  322. else
  323. propertyPath = null;
  324. }
  325. else
  326. {
  327. StringBuilder pathBuilder = new StringBuilder();
  328. for (; pathIdx < entries.Length - 1; pathIdx++)
  329. pathBuilder.Append(entries[pathIdx] + "/");
  330. if (pathIdx < entries.Length)
  331. pathBuilder.Append(entries[pathIdx]);
  332. propertyPath = pathBuilder.ToString();
  333. }
  334. }
  335. }
  336. internal class GUIAnimSimpleEntry : GUIAnimFieldEntry
  337. {
  338. private GUILabel valueDisplay;
  339. private GUILayoutX underlayLayout;
  340. private GUILabel overlaySpacing;
  341. public GUIAnimSimpleEntry(GUIAnimFieldLayouts layouts, string path, bool child = false)
  342. : base(layouts, path, child)
  343. {
  344. valueDisplay = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
  345. underlayLayout = layouts.underlay.AddLayoutX();
  346. underlayLayout.AddFlexibleSpace();
  347. underlayLayout.AddElement(valueDisplay);
  348. underlayLayout.AddSpace(50);
  349. overlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
  350. layouts.overlay.AddElement(overlaySpacing);
  351. }
  352. public override void Toggle(bool on)
  353. {
  354. underlayLayout.Active = on;
  355. overlaySpacing.Active = on;
  356. base.Toggle(on);
  357. }
  358. public override void SetValue(object value)
  359. {
  360. if (value == null)
  361. return;
  362. string strValue = value.ToString();
  363. valueDisplay.SetContent(strValue);
  364. }
  365. }
  366. internal class GUIAnimComplexEntry : GUIAnimFieldEntry
  367. {
  368. private GUILayout foldoutLayout;
  369. private GUIToggle foldout;
  370. private GUILabel underlaySpacing;
  371. protected GUIAnimSimpleEntry[] children;
  372. public GUIAnimComplexEntry(GUIAnimFieldLayouts layouts, string path, string[] childEntries)
  373. : base(layouts, path, false)
  374. {
  375. foldout = new GUIToggle("", EditorStyles.Expand);
  376. foldout.OnToggled += Toggle;
  377. GUILabel spacer = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
  378. foldoutLayout = layouts.overlay.AddLayoutX();
  379. foldoutLayout.AddElement(foldout);
  380. foldoutLayout.AddElement(spacer);
  381. foldoutLayout.AddFlexibleSpace();
  382. underlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
  383. layouts.underlay.AddElement(underlaySpacing);
  384. children = new GUIAnimSimpleEntry[childEntries.Length];
  385. for (int i = 0; i < childEntries.Length; i++)
  386. {
  387. children[i] = new GUIAnimSimpleEntry(layouts, path + childEntries[i], true);
  388. children[i].OnEntrySelected += x => { OnEntrySelected?.Invoke(x); };
  389. }
  390. Toggle(false);
  391. }
  392. public override void Toggle(bool on)
  393. {
  394. foreach(var child in children)
  395. child.Toggle(on);
  396. }
  397. public override GUIAnimFieldEntry[] GetChildren()
  398. {
  399. return children;
  400. }
  401. }
  402. internal class GUIAnimVec2Entry : GUIAnimComplexEntry
  403. {
  404. public GUIAnimVec2Entry(GUIAnimFieldLayouts layouts, string path)
  405. : base(layouts, path, new[] { ".x", ".y" })
  406. { }
  407. public override void SetValue(object value)
  408. {
  409. if (value == null)
  410. return;
  411. Vector2 vector = (Vector2)value;
  412. children[0].SetValue(vector.x);
  413. children[1].SetValue(vector.y);
  414. }
  415. }
  416. internal class GUIAnimVec3Entry : GUIAnimComplexEntry
  417. {
  418. public GUIAnimVec3Entry(GUIAnimFieldLayouts layouts, string path)
  419. : base(layouts, path, new[] { ".x", ".y", ".z" })
  420. { }
  421. public override void SetValue(object value)
  422. {
  423. if (value == null)
  424. return;
  425. Vector3 vector = (Vector3)value;
  426. children[0].SetValue(vector.x);
  427. children[1].SetValue(vector.y);
  428. children[2].SetValue(vector.z);
  429. }
  430. }
  431. internal class GUIAnimVec4Entry : GUIAnimComplexEntry
  432. {
  433. public GUIAnimVec4Entry(GUIAnimFieldLayouts layouts, string path)
  434. : base(layouts, path, new[] { ".x", ".y", ".z", ".w" })
  435. { }
  436. public override void SetValue(object value)
  437. {
  438. if (value == null)
  439. return;
  440. Vector4 vector = (Vector4)value;
  441. children[0].SetValue(vector.x);
  442. children[1].SetValue(vector.y);
  443. children[2].SetValue(vector.z);
  444. children[3].SetValue(vector.w);
  445. }
  446. }
  447. internal class GUIAnimColorEntry : GUIAnimComplexEntry
  448. {
  449. public GUIAnimColorEntry(GUIAnimFieldLayouts layouts, string path)
  450. : base(layouts, path, new[] { ".r", ".g", ".b", ".a" })
  451. { }
  452. public override void SetValue(object value)
  453. {
  454. if (value == null)
  455. return;
  456. Color color = (Color)value;
  457. children[0].SetValue(color.r);
  458. children[1].SetValue(color.g);
  459. children[2].SetValue(color.b);
  460. children[3].SetValue(color.a);
  461. }
  462. }
  463. internal class GUIAnimMissingEntry : GUIAnimFieldEntry
  464. {
  465. private GUILabel missingLabel;
  466. private GUILayoutX underlayLayout;
  467. private GUILabel overlaySpacing;
  468. public GUIAnimMissingEntry(GUIAnimFieldLayouts layouts, string path)
  469. : base(layouts, path, false)
  470. {
  471. missingLabel = new GUILabel("Missing property!", GUIOption.FixedHeight(GetEntryHeight()));
  472. underlayLayout = layouts.underlay.AddLayoutX();
  473. underlayLayout.AddFlexibleSpace();
  474. underlayLayout.AddElement(missingLabel);
  475. underlayLayout.AddSpace(50);
  476. overlaySpacing = new GUILabel("", GUIOption.FixedHeight(GetEntryHeight()));
  477. layouts.overlay.AddElement(overlaySpacing);
  478. }
  479. public override void Toggle(bool on)
  480. {
  481. underlayLayout.Active = on;
  482. overlaySpacing.Active = on;
  483. base.Toggle(on);
  484. }
  485. }
  486. internal struct AnimFieldInfo
  487. {
  488. public AnimFieldInfo(string path, SerializableProperty.FieldType type, bool isUserCurve)
  489. {
  490. this.path = path;
  491. this.type = type;
  492. this.isUserCurve = isUserCurve;
  493. }
  494. public string path;
  495. public SerializableProperty.FieldType type;
  496. public bool isUserCurve;
  497. }
  498. /** @} */
  499. }