NodeInspector.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. //
  2. // Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
  3. // LICENSE: Atomic Game Engine Editor and Tools EULA
  4. // Please see LICENSE_ATOMIC_EDITOR_AND_TOOLS.md in repository root for
  5. // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
  6. //
  7. import ScriptWidget = require("ui/ScriptWidget");
  8. import ComponentInspector = require("./ComponentInspector");
  9. import DataBinding = require("./DataBinding");
  10. import CreateComponentButton = require("./CreateComponentButton");
  11. import EditorEvents = require("editor/EditorEvents");
  12. interface ComponentState {
  13. expanded: boolean;
  14. }
  15. interface NodeState {
  16. expanded: boolean;
  17. componentStates: { [id: number]: ComponentState };
  18. }
  19. class NodeInspector extends ScriptWidget {
  20. constructor() {
  21. super();
  22. this.subscribeToEvent(this, "WidgetEvent", (ev) => this.handleWidgetEvent(ev));
  23. this.subscribeToEvent("GizmoMoved", (ev) => this.handleGizmoModed(ev));
  24. this.subscribeToEvent("Update", (ev) => this.handleUpdate(ev));
  25. }
  26. handleUpdate(ev) {
  27. // to keep from spamming UI update we have a little delta on the gizmo updates
  28. if (!this.node) {
  29. this.gizmoMoved = false;
  30. return;
  31. }
  32. if (this.gizmoMoved) {
  33. if (this.updateDelta > 1.0) {
  34. this.updateDelta = 0.0;
  35. }
  36. else {
  37. this.updateDelta -= ev.timeStep;
  38. }
  39. if (this.updateDelta <= 0) {
  40. for (var i in this.bindings) {
  41. this.bindings[i].setWidgetValueFromObject();
  42. }
  43. this.gizmoMoved = false;
  44. this.updateDelta = 0;
  45. }
  46. }
  47. }
  48. handleGizmoModed(ev) {
  49. if (!this.node) return;
  50. this.gizmoMoved = true;
  51. this.updateDelta += .3;
  52. }
  53. handleWidgetEvent(ev: Atomic.UIWidgetEvent) {
  54. var handled = false;
  55. for (var i = 0; i < this.bindings.length; i++) {
  56. if (this.bindings[i].handleWidgetEvent(ev)) {
  57. handled = true;
  58. }
  59. }
  60. // return handled
  61. return handled;
  62. }
  63. getPrefabComponent(node: Atomic.Node): Atomic.PrefabComponent {
  64. if (node.getComponent("PrefabComponent"))
  65. return <Atomic.PrefabComponent>node.getComponent("PrefabComponent");
  66. if (node.parent)
  67. return this.getPrefabComponent(node.parent);
  68. return null;
  69. }
  70. detectPrefab(node: Atomic.Node): boolean {
  71. if (node.getComponent("PrefabComponent"))
  72. return true;
  73. if (node.parent)
  74. return this.detectPrefab(node.parent);
  75. return false;
  76. }
  77. inspect(node: Atomic.Node) {
  78. this.bindings = new Array();
  79. this.node = node;
  80. return;
  81. /*
  82. this.subscribeToEvent(node, "SceneEditStateChange", (data) => this.handleSceneEditStateChangeEvent(data));
  83. this.isPrefab = this.detectPrefab(node);
  84. var fd = new Atomic.UIFontDescription();
  85. fd.id = "Vera";
  86. fd.size = 11;
  87. var nlp = new Atomic.UILayoutParams();
  88. nlp.width = 304;
  89. var nodeLayout = this.nodeLayout = new Atomic.UILayout();
  90. nodeLayout.spacing = 4;
  91. nodeLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
  92. nodeLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
  93. nodeLayout.layoutParams = nlp;
  94. nodeLayout.axis = Atomic.UI_AXIS_Y;
  95. // node attr layout
  96. var nodeSection = new Atomic.UISection();
  97. nodeSection.id = "node_section";
  98. nodeSection.text = "Node";
  99. nodeSection.value = 1;
  100. nodeLayout.addChild(nodeSection);
  101. var attrsVerticalLayout = new Atomic.UILayout(Atomic.UI_AXIS_Y);
  102. attrsVerticalLayout.spacing = 3;
  103. attrsVerticalLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
  104. attrsVerticalLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_AVAILABLE;
  105. nodeSection.contentRoot.addChild(attrsVerticalLayout);
  106. var attrs = node.getAttributes();
  107. for (var i in attrs) {
  108. var attr = <Atomic.AttributeInfo>attrs[i];
  109. if (attr.mode & Atomic.AM_NOEDIT)
  110. continue;
  111. var binding = DataBinding.createBinding(node, attr);
  112. if (!binding)
  113. continue;
  114. var attrLayout = new Atomic.UILayout();
  115. attrLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
  116. var name = new Atomic.UITextField();
  117. name.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
  118. name.skinBg = "InspectorTextAttrName";
  119. if (attr.type == Atomic.VAR_VECTOR3 || attr.type == Atomic.VAR_COLOR ||
  120. attr.type == Atomic.VAR_QUATERNION) {
  121. attrLayout.axis = Atomic.UI_AXIS_Y;
  122. attrLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
  123. attrLayout.skinBg = "InspectorVectorAttrLayout";
  124. }
  125. var bname = attr.name;
  126. if (bname == "Is Enabled")
  127. bname = "Enabled";
  128. name.text = bname;
  129. name.fontDescription = fd;
  130. attrLayout.addChild(name);
  131. attrLayout.addChild(binding.widget);
  132. attrsVerticalLayout.addChild(attrLayout);
  133. this.bindings.push(binding);
  134. }
  135. // PREFAB
  136. if (this.isPrefab) {
  137. var name = new Atomic.UITextField();
  138. name.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
  139. name.skinBg = "InspectorTextAttrName";
  140. name.text = "Prefab"
  141. name.fontDescription = fd;
  142. var prefabLayout = new Atomic.UILayout();
  143. prefabLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
  144. var saveButton = new Atomic.UIButton();
  145. saveButton.text = "Save";
  146. saveButton.fontDescription = fd;
  147. saveButton.onClick = () => {
  148. var prefabComponent = this.getPrefabComponent(this.node);
  149. if (prefabComponent) {
  150. prefabComponent.savePrefab();
  151. return true;
  152. }
  153. }
  154. var undoButton = new Atomic.UIButton();
  155. undoButton.text = "Undo";
  156. undoButton.fontDescription = fd;
  157. undoButton.onClick = () => {
  158. var prefabComponent = this.getPrefabComponent(this.node);
  159. if (prefabComponent) {
  160. prefabComponent.undoPrefab();
  161. // our components are now recreated
  162. // need to handle this
  163. return true;
  164. }
  165. }
  166. var breakButton = new Atomic.UIButton();
  167. breakButton.text = "Break";
  168. breakButton.fontDescription = fd;
  169. breakButton.onClick = () => {
  170. var prefabComponent = this.getPrefabComponent(this.node);
  171. if (prefabComponent) {
  172. prefabComponent.breakPrefab();
  173. prefabLayout.visibility = Atomic.UI_WIDGET_VISIBILITY_GONE;
  174. return true;
  175. }
  176. }
  177. prefabLayout.addChild(name);
  178. prefabLayout.addChild(saveButton);
  179. prefabLayout.addChild(undoButton);
  180. prefabLayout.addChild(breakButton);
  181. attrsVerticalLayout.addChild(prefabLayout);
  182. }
  183. // COMPONENTS
  184. var components = node.getComponents();
  185. for (var i in components) {
  186. var component = components[i];
  187. //if (component.isTemporary())
  188. // continue;
  189. var ci = new ComponentInspector();
  190. this.componentInspectors.push(ci);
  191. ci.id = "component_section_" + component.id;
  192. ci.inspect(component);
  193. nodeLayout.addChild(ci);
  194. }
  195. this.addChild(nodeLayout);
  196. var button = new CreateComponentButton(node);
  197. nodeLayout.addChild(button);
  198. for (var i in this.bindings) {
  199. this.bindings[i].setWidgetValueFromObject();
  200. this.bindings[i].objectLocked = false;
  201. }
  202. this.loadState();
  203. */
  204. }
  205. updateWidgetValues(components: boolean = false) {
  206. for (var i in this.bindings) {
  207. this.bindings[i].objectLocked = true;
  208. this.bindings[i].setWidgetValueFromObject();
  209. this.bindings[i].objectLocked = false;
  210. }
  211. if (components) {
  212. for (var i in this.componentInspectors) {
  213. this.componentInspectors[i].updateWidgetValues();
  214. }
  215. }
  216. }
  217. handleSceneEditStateChangeEvent(ev) {
  218. this.updateWidgetValues();
  219. }
  220. saveState() {
  221. var node = this.node;
  222. if (!node.scene)
  223. return;
  224. var nodeStates = NodeInspector.nodeStates[node.scene.id];
  225. if (!nodeStates)
  226. return;
  227. var state = nodeStates[node.id];
  228. if (!state) {
  229. state = nodeStates[node.id] = { expanded: true, componentStates: {} };
  230. }
  231. var section: Atomic.UISection = <Atomic.UISection>this.nodeLayout.getWidget("node_section");
  232. state.expanded = section.value ? true : false;
  233. var components = node.getComponents();
  234. for (var i in components) {
  235. var component = components[i];
  236. var cstate = state.componentStates[component.id];
  237. if (!cstate) {
  238. cstate = state.componentStates[component.id] = { expanded: false };
  239. }
  240. section = <Atomic.UISection>this.nodeLayout.getWidget("component_section_" + component.id);
  241. if (section)
  242. cstate.expanded = section.value ? true : false;
  243. }
  244. }
  245. loadState() {
  246. var node = this.node;
  247. // lookup in node states via scene id
  248. var nodeStates = NodeInspector.nodeStates[node.scene.id];
  249. if (!nodeStates) {
  250. nodeStates = NodeInspector.nodeStates[node.scene.id] = {};
  251. }
  252. // lookup by node id
  253. var state = nodeStates[node.id];
  254. if (!state) {
  255. // we don't have a state, so save default state
  256. this.saveState();
  257. } else {
  258. var section: Atomic.UISection = <Atomic.UISection>this.nodeLayout.getWidget("node_section");
  259. section.value = state.expanded ? 1 : 0;
  260. var components = node.getComponents();
  261. for (var i in components) {
  262. var component = components[i];
  263. var cstate = state.componentStates[component.id];
  264. section = <Atomic.UISection>this.nodeLayout.getWidget("component_section_" + component.id);
  265. if (cstate && section) {
  266. section.value = cstate.expanded ? 1 : 0;
  267. }
  268. }
  269. }
  270. }
  271. isPrefab: boolean;
  272. node: Atomic.Node;
  273. nodeLayout: Atomic.UILayout;
  274. bindings: Array<DataBinding>;
  275. gizmoMoved = false;
  276. updateDelta = 0;
  277. componentInspectors: Array<ComponentInspector> = [];
  278. static nodeStates: { [sceneID: number]: { [nodeId: number]: NodeState } } = {};
  279. }
  280. export = NodeInspector;