EditorSceneWindow.as 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. // Urho3D editor hierarchy window handling
  2. const int ITEM_NONE = 0;
  3. const int ITEM_NODE = 1;
  4. const int ITEM_COMPONENT = 2;
  5. const uint NO_ITEM = M_MAX_UNSIGNED;
  6. Window@ sceneWindow;
  7. ListView@ hierarchyList;
  8. bool suppressSceneChanges = false;
  9. void CreateSceneWindow()
  10. {
  11. if (sceneWindow !is null)
  12. return;
  13. sceneWindow = ui.LoadLayout(cache.GetResource("XMLFile", "UI/EditorSceneWindow.xml"));
  14. hierarchyList = sceneWindow.GetChild("NodeList");
  15. ui.root.AddChild(sceneWindow);
  16. int height = Min(ui.root.height - 60, 500);
  17. sceneWindow.SetSize(300, height);
  18. sceneWindow.SetPosition(20, 40);
  19. sceneWindow.opacity = uiMaxOpacity;
  20. sceneWindow.BringToFront();
  21. UpdateSceneWindow();
  22. DropDownList@ newNodeList = sceneWindow.GetChild("NewNodeList", true);
  23. Array<String> newNodeChoices = {"Replicated", "Local"};
  24. for (uint i = 0; i < newNodeChoices.length; ++i)
  25. {
  26. Text@ choice = Text();
  27. choice.SetStyle(uiStyle, "FileSelectorFilterText");
  28. choice.text = newNodeChoices[i];
  29. newNodeList.AddItem(choice);
  30. }
  31. DropDownList@ newComponentList = sceneWindow.GetChild("NewComponentList", true);
  32. Array<String> componentTypes = GetAvailableComponents();
  33. for (uint i = 0; i < componentTypes.length; ++i)
  34. {
  35. Text@ choice = Text();
  36. choice.SetStyle(uiStyle, "FileSelectorFilterText");
  37. choice.text = componentTypes[i];
  38. newComponentList.AddItem(choice);
  39. }
  40. // Set drag & drop target mode on the node list background, which is used to parent nodes back to the root node
  41. hierarchyList.contentElement.dragDropMode = DD_TARGET;
  42. hierarchyList.scrollPanel.dragDropMode = DD_TARGET;
  43. SubscribeToEvent(sceneWindow.GetChild("CloseButton", true), "Released", "HideSceneWindow");
  44. SubscribeToEvent(sceneWindow.GetChild("ExpandButton", true), "Released", "ExpandCollapseHierarchy");
  45. SubscribeToEvent(sceneWindow.GetChild("CollapseButton", true), "Released", "ExpandCollapseHierarchy");
  46. SubscribeToEvent(hierarchyList, "SelectionChanged", "HandleSceneWindowSelectionChange");
  47. SubscribeToEvent(hierarchyList, "ItemDoubleClicked", "HandleSceneWindowItemDoubleClick");
  48. SubscribeToEvent(hierarchyList, "UnhandledKey", "HandleSceneWindowKey");
  49. SubscribeToEvent(newNodeList, "ItemSelected", "HandleCreateNode");
  50. SubscribeToEvent(newComponentList, "ItemSelected", "HandleCreateComponent");
  51. SubscribeToEvent("DragDropTest", "HandleDragDropTest");
  52. SubscribeToEvent("DragDropFinish", "HandleDragDropFinish");
  53. SubscribeToEvent(editorScene, "NodeAdded", "HandleNodeAdded");
  54. SubscribeToEvent(editorScene, "NodeRemoved", "HandleNodeRemoved");
  55. SubscribeToEvent(editorScene, "ComponentAdded", "HandleComponentAdded");
  56. SubscribeToEvent(editorScene, "ComponentRemoved", "HandleComponentRemoved");
  57. SubscribeToEvent(editorScene, "NodeNameChanged", "HandleNodeNameChanged");
  58. }
  59. void ShowSceneWindow()
  60. {
  61. sceneWindow.visible = true;
  62. sceneWindow.BringToFront();
  63. }
  64. void HideSceneWindow()
  65. {
  66. sceneWindow.visible = false;
  67. }
  68. void ExpandCollapseHierarchy(StringHash eventType, VariantMap& eventData)
  69. {
  70. Button@ button = eventData["Element"].GetUIElement();
  71. bool enable = button.name == "ExpandButton";
  72. bool all = cast<CheckBox>(sceneWindow.GetChild("AllCheckBox", true)).checked;
  73. Array<uint> selections = hierarchyList.selections;
  74. for (uint i = 0; i < selections.length; ++i)
  75. hierarchyList.Expand(selections[i], enable, all);
  76. }
  77. void EnableExpandCollapseButtons(bool enable)
  78. {
  79. String[] buttons = { "ExpandButton", "CollapseButton", "AllCheckBox" };
  80. for (uint i = 0; i < buttons.length; ++i)
  81. {
  82. UIElement@ element = sceneWindow.GetChild(buttons[i], true);
  83. element.active = enable;
  84. element.children[0].color = enable ? normalTextColor : nonEditableTextColor;
  85. }
  86. }
  87. void ClearSceneWindow()
  88. {
  89. if (sceneWindow is null)
  90. return;
  91. hierarchyList.RemoveAllItems();
  92. }
  93. void UpdateSceneWindow()
  94. {
  95. ClearSceneWindow();
  96. UpdateSceneWindowNode(0, editorScene, null);
  97. // Re-enable layout update
  98. hierarchyList.EnableLayoutUpdate();
  99. // Clear copybuffer when whole window refreshed
  100. copyBuffer.Clear();
  101. }
  102. uint UpdateSceneWindowNode(uint itemIndex, Node@ node, UIElement@ parentItem)
  103. {
  104. // Whenever we're updating, disable layout update to optimize speed
  105. hierarchyList.contentElement.DisableLayoutUpdate();
  106. // Remove old item if exists
  107. if (itemIndex < hierarchyList.numItems && (node is null || (hierarchyList.items[itemIndex].vars["Type"].GetInt() == ITEM_NODE &&
  108. hierarchyList.items[itemIndex].vars["NodeID"].GetUInt() == node.id)))
  109. hierarchyList.RemoveItem(itemIndex);
  110. if (node is null)
  111. {
  112. hierarchyList.contentElement.EnableLayoutUpdate();
  113. hierarchyList.contentElement.UpdateLayout();
  114. return itemIndex;
  115. }
  116. Text@ text = Text();
  117. text.SetStyle(uiStyle, "FileSelectorListText");
  118. text.vars["Type"] = ITEM_NODE;
  119. text.vars["NodeID"] = node.id;
  120. text.text = GetNodeTitle(node);
  121. BorderImage@ icon = BorderImage();
  122. icon.texture = cache.GetResource("Texture2D", "Textures/UrhoDecal.dds");
  123. icon.blendMode = BLEND_ADD;
  124. text.AddChild(icon);
  125. // Nodes can be moved by drag and drop. The root node (scene) can not.
  126. if (node.typeName == "Node")
  127. {
  128. text.dragDropMode = DD_SOURCE_AND_TARGET;
  129. icon.color = Color(1, 1, 0);
  130. }
  131. else
  132. {
  133. text.dragDropMode = DD_TARGET;
  134. icon.color = Color(1, 0, 0);
  135. }
  136. hierarchyList.InsertItem(itemIndex, text, parentItem);
  137. // Advance the index for the child components and/or nodes
  138. if (itemIndex == M_MAX_UNSIGNED)
  139. itemIndex = hierarchyList.numItems;
  140. else
  141. ++itemIndex;
  142. // Get the indent level after the item is inserted
  143. icon.indent = text.indent - 1;
  144. icon.SetFixedSize(text.indentWidth, 16);
  145. // Update components first
  146. for (uint i = 0; i < node.numComponents; ++i)
  147. {
  148. Component@ component = node.components[i];
  149. AddComponentToSceneWindow(component, itemIndex++, text);
  150. }
  151. // Then update child nodes recursively
  152. for (uint i = 0; i < node.numChildren; ++i)
  153. {
  154. Node@ childNode = node.children[i];
  155. itemIndex = UpdateSceneWindowNode(itemIndex, childNode, text);
  156. }
  157. // Re-enable layout update (and do manual layout) now
  158. hierarchyList.contentElement.EnableLayoutUpdate();
  159. hierarchyList.contentElement.UpdateLayout();
  160. return itemIndex;
  161. }
  162. void UpdateSceneWindowNodeOnly(uint itemIndex, Node@ node)
  163. {
  164. Text@ text = hierarchyList.items[itemIndex];
  165. if (text is null)
  166. return;
  167. text.text = GetNodeTitle(node);
  168. }
  169. void UpdateSceneWindowNode(Node@ node)
  170. {
  171. // In case of node's parent is not found in the hierarchy list then the node will inserted at the root level, but it should not happen
  172. UpdateSceneWindowNode(GetNodeListIndex(node), node, hierarchyList.items[GetNodeListIndex(node.parent)]);
  173. }
  174. void UpdateSceneWindowNodeOnly(Node@ node)
  175. {
  176. uint index = GetNodeListIndex(node);
  177. UpdateSceneWindowNodeOnly(index, node);
  178. }
  179. void AddComponentToSceneWindow(Component@ component, uint compItemIndex, UIElement@ parentItem)
  180. {
  181. Text@ text = Text();
  182. text.SetStyle(uiStyle, "FileSelectorListText");
  183. text.vars["Type"] = ITEM_COMPONENT;
  184. text.vars["NodeID"] = component.node.id;
  185. text.vars["ComponentID"] = component.id;
  186. text.text = GetComponentTitle(component);
  187. BorderImage@ icon = BorderImage();
  188. icon.texture = cache.GetResource("Texture2D", "Textures/Mushroom.dds");
  189. text.AddChild(icon);
  190. hierarchyList.InsertItem(compItemIndex, text, parentItem);
  191. icon.indent = text.indent - 1;
  192. icon.SetFixedSize(text.indentWidth - 4, 14);
  193. }
  194. uint GetNodeListIndex(Node@ node)
  195. {
  196. if (node is null)
  197. return NO_ITEM;
  198. uint numItems = hierarchyList.numItems;
  199. uint nodeID = node.id;
  200. for (uint i = 0; i < numItems; ++i)
  201. {
  202. UIElement@ item = hierarchyList.items[i];
  203. if (item.vars["Type"].GetInt() == ITEM_NODE && item.vars["NodeID"].GetUInt() == nodeID)
  204. return i;
  205. }
  206. return NO_ITEM;
  207. }
  208. uint GetNodeListIndex(Node@ node, uint startPos)
  209. {
  210. if (node is null)
  211. return NO_ITEM;
  212. uint numItems = hierarchyList.numItems;
  213. uint nodeID = node.id;
  214. for (uint i = startPos; i < numItems; --i)
  215. {
  216. UIElement@ item = hierarchyList.items[i];
  217. if (item.vars["Type"].GetInt() == ITEM_NODE && item.vars["NodeID"].GetInt() == int(nodeID))
  218. return i;
  219. }
  220. return NO_ITEM;
  221. }
  222. Node@ GetListNode(uint index)
  223. {
  224. UIElement@ item = hierarchyList.items[index];
  225. if (item is null)
  226. return null;
  227. return editorScene.GetNode(item.vars["NodeID"].GetUInt());
  228. }
  229. Component@ GetListComponent(uint index)
  230. {
  231. UIElement@ item = hierarchyList.items[index];
  232. return GetListComponent(item);
  233. }
  234. Component@ GetListComponent(UIElement@ item)
  235. {
  236. if (item is null)
  237. return null;
  238. if (item.vars["Type"].GetInt() != ITEM_COMPONENT)
  239. return null;
  240. return editorScene.GetComponent(item.vars["ComponentID"].GetUInt());
  241. }
  242. uint GetComponentListIndex(Component@ component)
  243. {
  244. if (component is null)
  245. return NO_ITEM;
  246. uint numItems = hierarchyList.numItems;
  247. for (uint i = 0; i < numItems; ++i)
  248. {
  249. UIElement@ item = hierarchyList.items[i];
  250. if (item.vars["Type"].GetInt() == ITEM_COMPONENT && item.vars["ComponentID"].GetUInt() == component.id)
  251. return i;
  252. }
  253. return NO_ITEM;
  254. }
  255. int GetNodeIndent(Node@ node)
  256. {
  257. int indent = 0;
  258. for (;;)
  259. {
  260. if (node.parent is null)
  261. break;
  262. ++indent;
  263. node = node.parent;
  264. }
  265. return indent;
  266. }
  267. String GetNodeTitle(Node@ node)
  268. {
  269. String idStr;
  270. if (node.id >= FIRST_LOCAL_ID)
  271. idStr = "Local " + String(node.id - FIRST_LOCAL_ID);
  272. else
  273. idStr = String(node.id);
  274. if (node.name.empty)
  275. return node.typeName + " (" + idStr + ")";
  276. else
  277. return node.name + " (" + idStr + ")";
  278. }
  279. String GetComponentTitle(Component@ component)
  280. {
  281. String localStr;
  282. if (component.id >= FIRST_LOCAL_ID)
  283. localStr = " (Local)";
  284. return component.typeName + localStr;
  285. }
  286. void SelectNode(Node@ node, bool multiselect)
  287. {
  288. if (node is null && !multiselect)
  289. {
  290. hierarchyList.ClearSelection();
  291. return;
  292. }
  293. uint nodeItem = GetNodeListIndex(node);
  294. // Go in the parent chain up to make sure the chain is expanded
  295. for (;;)
  296. {
  297. Node@ parent = node.parent;
  298. if (node is editorScene || parent is null)
  299. break;
  300. node = parent;
  301. }
  302. uint numItems = hierarchyList.numItems;
  303. uint parentItem = GetNodeListIndex(node);
  304. if (nodeItem < numItems)
  305. {
  306. // Expand the node chain now
  307. if (!multiselect || !hierarchyList.IsSelected(nodeItem))
  308. {
  309. if (parentItem < numItems)
  310. hierarchyList.Expand(parentItem, true);
  311. hierarchyList.Expand(nodeItem, true);
  312. }
  313. // This causes an event to be sent, in response we set the node/component selections, and refresh editors
  314. if (!multiselect)
  315. hierarchyList.selection = nodeItem;
  316. else
  317. hierarchyList.ToggleSelection(nodeItem);
  318. }
  319. else
  320. {
  321. if (!multiselect)
  322. hierarchyList.ClearSelection();
  323. }
  324. }
  325. void SelectComponent(Component@ component, bool multiselect)
  326. {
  327. if (component is null && !multiselect)
  328. {
  329. hierarchyList.ClearSelection();
  330. return;
  331. }
  332. Node@ node = component.node;
  333. if (node is null && !multiselect)
  334. {
  335. hierarchyList.ClearSelection();
  336. return;
  337. }
  338. uint nodeItem = GetNodeListIndex(node);
  339. uint componentItem = GetComponentListIndex(component);
  340. // Go in the parent chain up to make sure the chain is expanded
  341. for (;;)
  342. {
  343. Node@ parent = node.parent;
  344. if (node is editorScene || parent is null)
  345. break;
  346. node = parent;
  347. }
  348. uint numItems = hierarchyList.numItems;
  349. uint parentItem = GetNodeListIndex(node);
  350. if (parentItem >= hierarchyList.numItems && !multiselect)
  351. {
  352. hierarchyList.ClearSelection();
  353. return;
  354. }
  355. if (nodeItem < numItems && componentItem < numItems)
  356. {
  357. // Expand the node chain now
  358. if (!multiselect || !hierarchyList.IsSelected(componentItem))
  359. {
  360. if (parentItem < numItems)
  361. hierarchyList.Expand(parentItem, true);
  362. hierarchyList.Expand(nodeItem, true);
  363. }
  364. // This causes an event to be sent, in response we set the node/component selections, and refresh editors
  365. if (!multiselect)
  366. hierarchyList.selection = componentItem;
  367. else
  368. hierarchyList.ToggleSelection(componentItem);
  369. }
  370. else
  371. {
  372. if (!multiselect)
  373. hierarchyList.ClearSelection();
  374. }
  375. }
  376. void HandleSceneWindowSelectionChange()
  377. {
  378. if (inSelectionModify)
  379. return;
  380. ClearSelection();
  381. Array<uint> indices = hierarchyList.selections;
  382. // Enable Expand/Collapse button when there is selection
  383. EnableExpandCollapseButtons(indices.length > 0);
  384. for (uint i = 0; i < indices.length; ++i)
  385. {
  386. uint index = indices[i];
  387. UIElement@ item = hierarchyList.items[index];
  388. int type = item.vars["Type"].GetInt();
  389. if (type == ITEM_COMPONENT)
  390. {
  391. Component@ comp = GetListComponent(index);
  392. if (comp !is null)
  393. selectedComponents.Push(comp);
  394. }
  395. else if (type == ITEM_NODE)
  396. {
  397. Node@ node = GetListNode(index);
  398. if (node !is null)
  399. selectedNodes.Push(node);
  400. }
  401. }
  402. // If only one node selected, use it for editing
  403. if (selectedNodes.length == 1)
  404. editNode = selectedNodes[0];
  405. // If selection contains only components, and they have a common node, use it for editing
  406. if (selectedNodes.empty && !selectedComponents.empty)
  407. {
  408. Node@ commonNode;
  409. for (uint i = 0; i < selectedComponents.length; ++i)
  410. {
  411. if (i == 0)
  412. commonNode = selectedComponents[i].node;
  413. else
  414. {
  415. if (selectedComponents[i].node !is commonNode)
  416. commonNode = null;
  417. }
  418. }
  419. editNode = commonNode;
  420. }
  421. // Now check if the component(s) can be edited. If many selected, must have same type or have same edit node
  422. if (!selectedComponents.empty)
  423. {
  424. if (editNode is null)
  425. {
  426. ShortStringHash compType = selectedComponents[0].type;
  427. bool sameType = true;
  428. for (uint i = 1; i < selectedComponents.length; ++i)
  429. {
  430. if (selectedComponents[i].type != compType)
  431. {
  432. sameType = false;
  433. break;
  434. }
  435. }
  436. if (sameType)
  437. editComponents = selectedComponents;
  438. }
  439. else
  440. {
  441. editComponents = selectedComponents;
  442. numEditableComponentsPerNode = selectedComponents.length;
  443. }
  444. }
  445. // If just nodes selected, and no components, show as many matching components for editing as possible
  446. if (!selectedNodes.empty && selectedComponents.empty && selectedNodes[0].numComponents > 0)
  447. {
  448. uint count = 0;
  449. for (uint j = 0; j < selectedNodes[0].numComponents; ++j)
  450. {
  451. ShortStringHash compType = selectedNodes[0].components[j].type;
  452. bool sameType = true;
  453. for (uint i = 1; i < selectedNodes.length; ++i)
  454. {
  455. if (selectedNodes[i].numComponents <= j || selectedNodes[i].components[j].type != compType)
  456. {
  457. sameType = false;
  458. break;
  459. }
  460. }
  461. if (sameType)
  462. {
  463. ++count;
  464. for (uint i = 0; i < selectedNodes.length; ++i)
  465. editComponents.Push(selectedNodes[i].components[j]);
  466. }
  467. }
  468. if (count > 1)
  469. numEditableComponentsPerNode = count;
  470. }
  471. if (selectedNodes.empty && editNode !is null)
  472. editNodes.Push(editNode);
  473. else
  474. {
  475. editNodes = selectedNodes;
  476. // Ensure the first one in array is not the scene node because the first node is used as template for attribute editing
  477. if (editNodes.length > 1 && editNodes[0] is editorScene)
  478. {
  479. editNodes.Erase(0);
  480. editNodes.Push(editorScene);
  481. }
  482. }
  483. PositionGizmo();
  484. UpdateNodeWindow();
  485. }
  486. void HandleSceneWindowItemDoubleClick(StringHash eventType, VariantMap& eventData)
  487. {
  488. uint index = eventData["Selection"].GetUInt();
  489. hierarchyList.ToggleExpand(index);
  490. }
  491. void HandleSceneWindowKey(StringHash eventType, VariantMap& eventData)
  492. {
  493. int key = eventData["Key"].GetInt();
  494. }
  495. void HandleDragDropTest(StringHash eventType, VariantMap& eventData)
  496. {
  497. UIElement@ source = eventData["Source"].GetUIElement();
  498. UIElement@ target = eventData["Target"].GetUIElement();
  499. eventData["Accept"] = TestSceneWindowElements(source, target);
  500. }
  501. void HandleDragDropFinish(StringHash eventType, VariantMap& eventData)
  502. {
  503. UIElement@ source = eventData["Source"].GetUIElement();
  504. UIElement@ target = eventData["Target"].GetUIElement();
  505. bool accept = TestSceneWindowElements(source, target);
  506. eventData["Accept"] = accept;
  507. if (!accept)
  508. return;
  509. Node@ sourceNode = editorScene.GetNode(source.vars["NodeID"].GetUInt());
  510. Node@ targetNode = editorScene.GetNode(target.vars["NodeID"].GetUInt());
  511. // If target is null, parent to scene
  512. if (targetNode is null)
  513. targetNode = editorScene;
  514. // Perform the reparenting
  515. if (!SceneChangeParent(sourceNode, targetNode))
  516. return;
  517. // Focus the node at its new position in the list which in turn should trigger a refresh in attribute inspector
  518. FocusNode(sourceNode);
  519. }
  520. bool TestSceneWindowElements(UIElement@ source, UIElement@ target)
  521. {
  522. // Test for validity of reparenting by drag and drop
  523. Node@ sourceNode;
  524. Node@ targetNode;
  525. if (source.vars.Contains("NodeID"))
  526. sourceNode = editorScene.GetNode(source.vars["NodeID"].GetUInt());
  527. if (target.vars.Contains("NodeID"))
  528. editorScene.GetNode(target.vars["NodeID"].GetUInt());
  529. if (sourceNode is null)
  530. return false;
  531. if (sourceNode is editorScene)
  532. return false;
  533. if (targetNode !is null)
  534. {
  535. if (sourceNode.parent is targetNode)
  536. return false;
  537. if (targetNode.parent is sourceNode)
  538. return false;
  539. }
  540. return true;
  541. }
  542. void FocusNode(Node@ node)
  543. {
  544. uint index = GetNodeListIndex(node);
  545. hierarchyList.selection = index;
  546. }
  547. void FocusComponent(Component@ component)
  548. {
  549. uint index = GetComponentListIndex(component);
  550. hierarchyList.selection = index;
  551. }
  552. void HandleCreateNode(StringHash eventType, VariantMap& eventData)
  553. {
  554. DropDownList@ list = eventData["Element"].GetUIElement();
  555. uint mode = list.selection;
  556. if (mode >= list.numItems)
  557. return;
  558. Node@ newNode = editorScene.CreateChild("", mode == 0 ? REPLICATED : LOCAL);
  559. // Set the new node a certain distance from the camera
  560. newNode.position = GetNewNodePosition();
  561. FocusNode(newNode);
  562. }
  563. void HandleCreateComponent(StringHash eventType, VariantMap& eventData)
  564. {
  565. if (editNode is null)
  566. return;
  567. DropDownList@ list = eventData["Element"].GetUIElement();
  568. Text@ text = list.selectedItem;
  569. if (text is null)
  570. return;
  571. // If this is the root node, do not allow to create duplicate scene-global components
  572. if (editNode is editorScene && CheckForExistingGlobalComponent(editNode, text.text))
  573. return;
  574. // For now, make a local node's all components local
  575. /// \todo Allow to specify the createmode
  576. Component@ newComponent = editNode.CreateComponent(text.text, editNode.id < FIRST_LOCAL_ID ? REPLICATED : LOCAL);
  577. FocusComponent(newComponent);
  578. }
  579. void CreateBuiltinObject(const String& name)
  580. {
  581. Node@ newNode = editorScene.CreateChild(name, REPLICATED);
  582. // Set the new node a certain distance from the camera
  583. newNode.position = GetNewNodePosition();
  584. StaticModel@ object = newNode.CreateComponent("StaticModel");
  585. object.model = cache.GetResource("Model", "Models/" + name + ".mdl");
  586. FocusNode(newNode);
  587. }
  588. bool CheckSceneWindowFocus()
  589. {
  590. // When we do scene operations based on key shortcuts, make sure either the 3D scene or the node list is focused,
  591. // not for example a file selector
  592. if (ui.focusElement is hierarchyList || ui.focusElement is null)
  593. return true;
  594. else
  595. return false;
  596. }
  597. bool CheckForExistingGlobalComponent(Node@ node, const String&in typeName)
  598. {
  599. if (typeName != "Octree" && typeName != "PhysicsWorld" && typeName != "DebugRenderer")
  600. return false;
  601. else
  602. return node.HasComponent(typeName);
  603. }
  604. void HandleNodeAdded(StringHash eventType, VariantMap& eventData)
  605. {
  606. if (suppressSceneChanges)
  607. return;
  608. Node@ node = eventData["Node"].GetNode();
  609. UpdateSceneWindowNode(node);
  610. }
  611. void HandleNodeRemoved(StringHash eventType, VariantMap& eventData)
  612. {
  613. if (suppressSceneChanges)
  614. return;
  615. Node@ node = eventData["Node"].GetNode();
  616. uint index = GetNodeListIndex(node);
  617. UpdateSceneWindowNode(index, null, null);
  618. }
  619. void HandleComponentAdded(StringHash eventType, VariantMap& eventData)
  620. {
  621. if (suppressSceneChanges)
  622. return;
  623. Node@ node = eventData["Node"].GetNode();
  624. UpdateSceneWindowNode(node);
  625. }
  626. void HandleComponentRemoved(StringHash eventType, VariantMap& eventData)
  627. {
  628. if (suppressSceneChanges)
  629. return;
  630. Component@ component = eventData["Component"].GetComponent();
  631. uint index = GetComponentListIndex(component);
  632. if (index != NO_ITEM)
  633. {
  634. ListView@ list = sceneWindow.GetChild("NodeList", true);
  635. list.RemoveItem(index);
  636. }
  637. }
  638. void HandleNodeNameChanged(StringHash eventType, VariantMap& eventData)
  639. {
  640. if (suppressSceneChanges)
  641. return;
  642. Node@ node = eventData["Node"].GetNode();
  643. UpdateSceneWindowNodeOnly(node);
  644. }