GUIAnimFieldDisplay.cs 18 KB

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