BsGUISceneTreeView.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. #include "BsGUISceneTreeView.h"
  2. #include "BsSceneObject.h"
  3. #include "BsSceneManager.h"
  4. #include "BsGUISkin.h"
  5. #include "BsCmdRecordSO.h"
  6. #include "BsCmdReparentSO.h"
  7. #include "BsCmdDeleteSO.h"
  8. #include "BsCmdCloneSO.h"
  9. #include "BsCmdCreateSO.h"
  10. #include "BsCmdInstantiateSO.h"
  11. #include "BsDragAndDropManager.h"
  12. #include "BsSelection.h"
  13. #include "BsGUIResourceTreeView.h"
  14. #include "BsProjectLibrary.h"
  15. #include "BsProjectResourceMeta.h"
  16. #include "BsPrefab.h"
  17. #include "BsResources.h"
  18. #include "BsGUIContextMenu.h"
  19. namespace BansheeEngine
  20. {
  21. const MessageId GUISceneTreeView::SELECTION_CHANGED_MSG = MessageId("SceneTreeView_SelectionChanged");
  22. const Color GUISceneTreeView::PREFAB_TINT = Color(1.0f, (168.0f / 255.0f), 0.0f, 1.0f);
  23. DraggedSceneObjects::DraggedSceneObjects(UINT32 numObjects)
  24. :numObjects(numObjects)
  25. {
  26. objects = bs_newN<HSceneObject>(numObjects);
  27. }
  28. DraggedSceneObjects::~DraggedSceneObjects()
  29. {
  30. bs_deleteN(objects, numObjects);
  31. objects = nullptr;
  32. }
  33. GUISceneTreeView::GUISceneTreeView(const String& backgroundStyle, const String& elementBtnStyle,
  34. const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle,
  35. const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions)
  36. :GUITreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle, highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle,
  37. dragSepHighlightStyle, dimensions), mCutFlag(false)
  38. {
  39. SceneTreeViewLocator::_provide(this);
  40. GUIContextMenuPtr contextMenu = bs_shared_ptr_new<GUIContextMenu>();
  41. contextMenu->addMenuItem(L"New", std::bind(&GUISceneTreeView::createNewSO, this), 50);
  42. contextMenu->addMenuItem(L"Rename", std::bind(&GUISceneTreeView::renameSelected, this), 49, ShortcutKey(ButtonModifier::None, BC_F2));
  43. contextMenu->addMenuItem(L"Delete", std::bind(&GUISceneTreeView::deleteSelection, this), 48, ShortcutKey(ButtonModifier::None, BC_DELETE));
  44. contextMenu->addSeparator(L"", 40);
  45. contextMenu->addMenuItem(L"Duplicate", std::bind(&GUISceneTreeView::duplicateSelection, this), 39, ShortcutKey(ButtonModifier::Ctrl, BC_D));
  46. contextMenu->addMenuItem(L"Copy", std::bind(&GUISceneTreeView::copySelection, this), 38, ShortcutKey(ButtonModifier::Ctrl, BC_C));
  47. contextMenu->addMenuItem(L"Cut", std::bind(&GUISceneTreeView::cutSelection, this), 37, ShortcutKey(ButtonModifier::Ctrl, BC_X));
  48. contextMenu->addMenuItem(L"Paste", std::bind(&GUISceneTreeView::paste, this), 36, ShortcutKey(ButtonModifier::Ctrl, BC_V));
  49. setContextMenu(contextMenu);
  50. }
  51. GUISceneTreeView::~GUISceneTreeView()
  52. {
  53. SceneTreeViewLocator::_remove(this);
  54. }
  55. GUISceneTreeView* GUISceneTreeView::create(const String& backgroundStyle, const String& elementBtnStyle, const String& foldoutBtnStyle,
  56. const String& highlightBackgroundStyle, const String& selectionBackgroundStyle, const String& editBoxStyle, const String& dragHighlightStyle,
  57. const String& dragSepHighlightStyle)
  58. {
  59. return new (bs_alloc<GUISceneTreeView>()) GUISceneTreeView(backgroundStyle, elementBtnStyle, foldoutBtnStyle,
  60. highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUIDimensions::create());
  61. }
  62. GUISceneTreeView* GUISceneTreeView::create(const GUIOptions& options, const String& backgroundStyle, const String& elementBtnStyle,
  63. const String& foldoutBtnStyle, const String& highlightBackgroundStyle, const String& selectionBackgroundStyle,
  64. const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle)
  65. {
  66. return new (bs_alloc<GUISceneTreeView>()) GUISceneTreeView(backgroundStyle, elementBtnStyle,
  67. foldoutBtnStyle, highlightBackgroundStyle, selectionBackgroundStyle, editBoxStyle,
  68. dragHighlightStyle, dragSepHighlightStyle, GUIDimensions::create(options));
  69. }
  70. void GUISceneTreeView::updateTreeElement(SceneTreeElement* element)
  71. {
  72. HSceneObject currentSO = element->mSceneObject;
  73. // Check if SceneObject has changed in any way and update the tree element
  74. // Early exit case - Most commonly there will be no changes between active and cached data so
  75. // we first do a quick check in order to avoid expensive comparison later
  76. bool completeMatch = true;
  77. UINT32 visibleChildCount = 0;
  78. for (UINT32 i = 0; i < currentSO->getNumChildren(); i++)
  79. {
  80. if (i >= element->mChildren.size())
  81. {
  82. completeMatch = false;
  83. break;
  84. }
  85. HSceneObject currentSOChild = currentSO->getChild(i);
  86. #if BS_DEBUG_MODE == 0
  87. if (currentSOChild->hasFlag(SOF_Internal))
  88. continue;
  89. #endif
  90. SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[visibleChildCount]);
  91. visibleChildCount++;
  92. UINT64 curId = currentSOChild->getInstanceId();
  93. if (curId != currentChild->mId)
  94. {
  95. completeMatch = false;
  96. break;
  97. }
  98. }
  99. completeMatch &= visibleChildCount == element->mChildren.size();
  100. // Not a complete match, compare everything and insert/delete elements as needed
  101. bool needsUpdate = false;
  102. if(!completeMatch)
  103. {
  104. Vector<TreeElement*> newChildren;
  105. bool* tempToDelete = (bool*)bs_stack_alloc(sizeof(bool) * (UINT32)element->mChildren.size());
  106. for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
  107. tempToDelete[i] = true;
  108. for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
  109. {
  110. HSceneObject currentSOChild = currentSO->getChild(i);
  111. bool isInternal = currentSOChild->hasFlag(SOF_Internal);
  112. bool isPrefabInstance = !currentSOChild->getPrefabLink().empty();
  113. #if BS_DEBUG_MODE == 0
  114. if (isInternal)
  115. continue;
  116. #endif
  117. UINT64 curId = currentSOChild->getInstanceId();
  118. bool found = false;
  119. for(UINT32 j = 0; j < element->mChildren.size(); j++)
  120. {
  121. SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[j]);
  122. if(curId == currentChild->mId)
  123. {
  124. tempToDelete[j] = false;
  125. currentChild->mSortedIdx = (UINT32)newChildren.size();
  126. newChildren.push_back(currentChild);
  127. found = true;
  128. break;
  129. }
  130. }
  131. if(!found)
  132. {
  133. SceneTreeElement* newChild = bs_new<SceneTreeElement>();
  134. newChild->mParent = element;
  135. newChild->mSceneObject = currentSOChild;
  136. newChild->mId = currentSOChild->getInstanceId();
  137. newChild->mName = currentSOChild->getName();
  138. newChild->mSortedIdx = (UINT32)newChildren.size();
  139. newChild->mIsVisible = element->mIsVisible && element->mIsExpanded;
  140. newChild->mTint = isInternal ? Color::Red : (isPrefabInstance ? PREFAB_TINT : Color::White);
  141. newChild->mIsPrefabInstance = isPrefabInstance;
  142. newChildren.push_back(newChild);
  143. updateElementGUI(newChild);
  144. }
  145. }
  146. for(UINT32 i = 0; i < element->mChildren.size(); i++)
  147. {
  148. if(!tempToDelete[i])
  149. continue;
  150. deleteTreeElementInternal(element->mChildren[i]);
  151. }
  152. bs_stack_free(tempToDelete);
  153. element->mChildren = newChildren;
  154. needsUpdate = true;
  155. }
  156. // Check if name needs updating
  157. const String& name = element->mSceneObject->getName();
  158. if(element->mName != name)
  159. {
  160. element->mName = name;
  161. needsUpdate = true;
  162. }
  163. // Check if active state needs updating
  164. bool isDisabled = !element->mSceneObject->getActive();
  165. if(element->mIsDisabled != isDisabled)
  166. {
  167. element->mIsDisabled = isDisabled;
  168. needsUpdate = true;
  169. }
  170. // Check if prefab instance state needs updating
  171. bool isPrefabInstance = !element->mSceneObject->getPrefabLink().empty();
  172. if (element->mIsPrefabInstance != isPrefabInstance)
  173. {
  174. element->mIsPrefabInstance = isPrefabInstance;
  175. bool isInternal = element->mSceneObject->hasFlag(SOF_Internal);
  176. element->mTint = isInternal ? Color::Red : (isPrefabInstance ? PREFAB_TINT : Color::White);
  177. needsUpdate = true;
  178. }
  179. if(needsUpdate)
  180. updateElementGUI(element);
  181. for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
  182. {
  183. SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(element->mChildren[i]);
  184. updateTreeElement(sceneElement);
  185. }
  186. // Calculate the sorted index of the elements based on their name
  187. bs_frame_mark();
  188. FrameVector<SceneTreeElement*> sortVector;
  189. for (auto& child : element->mChildren)
  190. sortVector.push_back(static_cast<SceneTreeElement*>(child));
  191. std::sort(sortVector.begin(), sortVector.end(),
  192. [&](const SceneTreeElement* lhs, const SceneTreeElement* rhs)
  193. {
  194. return StringUtil::compare(lhs->mName, rhs->mName, false) < 0;
  195. });
  196. UINT32 idx = 0;
  197. for (auto& child : sortVector)
  198. {
  199. child->mSortedIdx = idx;
  200. idx++;
  201. }
  202. bs_frame_clear();
  203. }
  204. void GUISceneTreeView::updateTreeElementHierarchy()
  205. {
  206. HSceneObject root = gCoreSceneManager().getRootNode();
  207. mRootElement.mSceneObject = root;
  208. mRootElement.mId = root->getInstanceId();
  209. mRootElement.mSortedIdx = 0;
  210. mRootElement.mIsExpanded = true;
  211. updateTreeElement(&mRootElement);
  212. }
  213. void GUISceneTreeView::renameTreeElement(GUITreeView::TreeElement* element, const WString& name)
  214. {
  215. SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(element);
  216. HSceneObject so = sceneTreeElement->mSceneObject;
  217. CmdRecordSO::execute(so, false, L"Renamed \"" + toWString(so->getName()) + L"\"");
  218. so->setName(toString(name));
  219. onModified();
  220. }
  221. void GUISceneTreeView::deleteTreeElement(TreeElement* element)
  222. {
  223. SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(element);
  224. HSceneObject so = sceneTreeElement->mSceneObject;
  225. CmdDeleteSO::execute(so, L"Deleted \"" + toWString(so->getName()) + L"\"");
  226. onModified();
  227. }
  228. void GUISceneTreeView::deleteTreeElementInternal(GUITreeView::TreeElement* element)
  229. {
  230. closeTemporarilyExpandedElements(); // In case this element is one of them
  231. if (element->mIsHighlighted)
  232. clearPing();
  233. if(element->mIsSelected)
  234. unselectElement(element);
  235. bs_delete(element);
  236. }
  237. bool GUISceneTreeView::acceptDragAndDrop() const
  238. {
  239. return DragAndDropManager::instance().isDragInProgress() &&
  240. (DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject ||
  241. DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::Resources);
  242. }
  243. void GUISceneTreeView::dragAndDropStart()
  244. {
  245. DraggedSceneObjects* draggedSceneObjects = bs_new<DraggedSceneObjects>((UINT32)mSelectedElements.size());
  246. UINT32 cnt = 0;
  247. for(auto& selectedElement : mSelectedElements)
  248. {
  249. SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(selectedElement.element);
  250. draggedSceneObjects->objects[cnt] = sceneTreeElement->mSceneObject;
  251. cnt++;
  252. }
  253. DragAndDropManager::instance().startDrag((UINT32)DragAndDropType::SceneObject, (void*)draggedSceneObjects,
  254. std::bind(&GUISceneTreeView::dragAndDropFinalize, this), false);
  255. }
  256. void GUISceneTreeView::dragAndDropEnded(TreeElement* overTreeElement)
  257. {
  258. UINT32 dragTypeId = DragAndDropManager::instance().getDragTypeId();
  259. if (dragTypeId == (UINT32)DragAndDropType::SceneObject)
  260. {
  261. if (overTreeElement != nullptr)
  262. {
  263. DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
  264. Vector<HSceneObject> sceneObjects;
  265. SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(overTreeElement);
  266. HSceneObject newParent = sceneTreeElement->mSceneObject;
  267. for (UINT32 i = 0; i < draggedSceneObjects->numObjects; i++)
  268. {
  269. if (draggedSceneObjects->objects[i] != newParent)
  270. sceneObjects.push_back(draggedSceneObjects->objects[i]);
  271. }
  272. CmdReparentSO::execute(sceneObjects, newParent);
  273. onModified();
  274. }
  275. }
  276. else if (dragTypeId == (UINT32)DragAndDropType::Resources)
  277. {
  278. DraggedResources* draggedResources = reinterpret_cast<DraggedResources*>(DragAndDropManager::instance().getDragData());
  279. HSceneObject newParent;
  280. if (overTreeElement != nullptr)
  281. {
  282. SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(overTreeElement);
  283. newParent = sceneTreeElement->mSceneObject;
  284. }
  285. onResourceDropped(newParent, draggedResources->resourcePaths);
  286. }
  287. }
  288. void GUISceneTreeView::dragAndDropFinalize()
  289. {
  290. mDragInProgress = false;
  291. _markLayoutAsDirty();
  292. if (DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject)
  293. {
  294. DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
  295. bs_delete(draggedSceneObjects);
  296. }
  297. }
  298. bool GUISceneTreeView::_acceptDragAndDrop(const Vector2I position, UINT32 typeId) const
  299. {
  300. return (typeId == (UINT32)DragAndDropType::SceneObject || typeId == (UINT32)DragAndDropType::Resources) && !_isDisabled();
  301. }
  302. void GUISceneTreeView::selectionChanged()
  303. {
  304. onSelectionChanged();
  305. sendMessage(SELECTION_CHANGED_MSG);
  306. }
  307. Vector<HSceneObject> GUISceneTreeView::getSelection() const
  308. {
  309. Vector<HSceneObject> selectedSOs;
  310. for (auto& selectedElem : mSelectedElements)
  311. {
  312. SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(selectedElem.element);
  313. selectedSOs.push_back(sceneTreeElement->mSceneObject);
  314. }
  315. return selectedSOs;
  316. }
  317. void GUISceneTreeView::setSelection(const Vector<HSceneObject>& objects)
  318. {
  319. unselectAll(false);
  320. // Note: I could queue the selection update until after the next frame in order to avoid the hierarchy update here
  321. // for better performance.
  322. updateTreeElementHierarchy();
  323. SceneTreeElement& root = mRootElement;
  324. Stack<SceneTreeElement*> todo;
  325. todo.push(&mRootElement);
  326. while (!todo.empty())
  327. {
  328. SceneTreeElement* currentElem = todo.top();
  329. todo.pop();
  330. auto iterFind = std::find(objects.begin(), objects.end(), currentElem->mSceneObject);
  331. if (iterFind != objects.end())
  332. {
  333. expandToElement(currentElem);
  334. selectElement(currentElem);
  335. }
  336. for (auto& child : currentElem->mChildren)
  337. {
  338. SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
  339. todo.push(sceneChild);
  340. }
  341. }
  342. }
  343. void GUISceneTreeView::ping(const HSceneObject& object)
  344. {
  345. SceneTreeElement& root = mRootElement;
  346. Stack<SceneTreeElement*> todo;
  347. todo.push(&mRootElement);
  348. while (!todo.empty())
  349. {
  350. SceneTreeElement* currentElem = todo.top();
  351. todo.pop();
  352. if (currentElem->mSceneObject == object)
  353. {
  354. GUITreeView::ping(currentElem);
  355. break;
  356. }
  357. for (auto& child : currentElem->mChildren)
  358. {
  359. SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
  360. todo.push(sceneChild);
  361. }
  362. }
  363. }
  364. GUISceneTreeView::SceneTreeElement* GUISceneTreeView::findTreeElement(const HSceneObject& so)
  365. {
  366. SceneTreeElement& root = mRootElement;
  367. Stack<SceneTreeElement*> todo;
  368. todo.push(&mRootElement);
  369. while (!todo.empty())
  370. {
  371. SceneTreeElement* currentElem = todo.top();
  372. todo.pop();
  373. if (so == currentElem->mSceneObject)
  374. return currentElem;
  375. for (auto& child : currentElem->mChildren)
  376. {
  377. SceneTreeElement* sceneChild = static_cast<SceneTreeElement*>(child);
  378. todo.push(sceneChild);
  379. }
  380. }
  381. return nullptr;
  382. }
  383. void GUISceneTreeView::duplicateSelection()
  384. {
  385. Vector<HSceneObject> duplicateList;
  386. for (auto& selectedElem : mSelectedElements)
  387. {
  388. SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
  389. duplicateList.push_back(sceneElement->mSceneObject);
  390. }
  391. cleanDuplicates(duplicateList);
  392. if (duplicateList.size() == 0)
  393. return;
  394. WString message;
  395. if (duplicateList.size() == 1)
  396. message = L"Duplicated " + toWString(duplicateList[0]->getName());
  397. else
  398. message = L"Duplicated " + toWString(duplicateList.size()) + L" elements";
  399. CmdCloneSO::execute(duplicateList, message);
  400. onModified();
  401. }
  402. void GUISceneTreeView::copySelection()
  403. {
  404. clearCopyList();
  405. for (auto& selectedElem : mSelectedElements)
  406. {
  407. SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
  408. mCopyList.push_back(sceneElement->mSceneObject);
  409. }
  410. mCutFlag = false;
  411. }
  412. void GUISceneTreeView::cutSelection()
  413. {
  414. clearCopyList();
  415. for (auto& selectedElem : mSelectedElements)
  416. {
  417. SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(selectedElem.element);
  418. mCopyList.push_back(sceneElement->mSceneObject);
  419. sceneElement->mIsCut = true;
  420. updateElementGUI(sceneElement);
  421. }
  422. mCutFlag = true;
  423. _markLayoutAsDirty();
  424. }
  425. void GUISceneTreeView::paste()
  426. {
  427. cleanDuplicates(mCopyList);
  428. if (mCopyList.size() == 0)
  429. return;
  430. HSceneObject parent = mRootElement.mSceneObject;
  431. if (mSelectedElements.size() > 0)
  432. {
  433. SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(mSelectedElements[0].element);
  434. parent = sceneElement->mSceneObject;
  435. }
  436. if (mCutFlag)
  437. {
  438. WString message;
  439. if (mCopyList.size() == 1)
  440. message = L"Moved " + toWString(mCopyList[0]->getName());
  441. else
  442. message = L"Moved " + toWString(mCopyList.size()) + L" elements";
  443. CmdReparentSO::execute(mCopyList, parent, message);
  444. clearCopyList();
  445. }
  446. else
  447. {
  448. WString message;
  449. if (mCopyList.size() == 1)
  450. message = L"Copied " + toWString(mCopyList[0]->getName());
  451. else
  452. message = L"Copied " + toWString(mCopyList.size()) + L" elements";
  453. Vector<HSceneObject> clones = CmdCloneSO::execute(mCopyList, message);
  454. for (auto& clone : clones)
  455. clone->setParent(parent);
  456. }
  457. onModified();
  458. }
  459. void GUISceneTreeView::clearCopyList()
  460. {
  461. for (auto& so : mCopyList)
  462. {
  463. if (so.isDestroyed())
  464. continue;
  465. TreeElement* treeElem = findTreeElement(so);
  466. if (treeElem != nullptr)
  467. {
  468. treeElem->mIsCut = false;
  469. updateElementGUI(treeElem);
  470. }
  471. }
  472. mCopyList.clear();
  473. _markLayoutAsDirty();
  474. }
  475. void GUISceneTreeView::createNewSO()
  476. {
  477. HSceneObject newSO = CmdCreateSO::execute("New", 0, L"Created a new SceneObject");
  478. if (mSelectedElements.size() > 0)
  479. {
  480. SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(mSelectedElements[0].element);
  481. newSO->setParent(sceneElement->mSceneObject);
  482. }
  483. updateTreeElementHierarchy();
  484. TreeElement* newTreeElement = findTreeElement(newSO);
  485. expandToElement(newTreeElement);
  486. setSelection({ newSO });
  487. renameSelected();
  488. onModified();
  489. }
  490. void GUISceneTreeView::cleanDuplicates(Vector<HSceneObject>& objects)
  491. {
  492. auto isChildOf = [&](const HSceneObject& parent, const HSceneObject& child)
  493. {
  494. HSceneObject elem = child;
  495. while (elem != nullptr && elem != parent)
  496. elem = elem->getParent();
  497. return elem == parent;
  498. };
  499. Vector<HSceneObject> cleanList;
  500. for (UINT32 i = 0; i < (UINT32)objects.size(); i++)
  501. {
  502. bool foundParent = false;
  503. for (UINT32 j = 0; j < (UINT32)objects.size(); j++)
  504. {
  505. if (i != j && isChildOf(objects[j], objects[i]))
  506. {
  507. foundParent = true;
  508. break;
  509. }
  510. }
  511. if (!foundParent)
  512. cleanList.push_back(objects[i]);
  513. }
  514. objects = cleanList;
  515. }
  516. const String& GUISceneTreeView::getGUITypeName()
  517. {
  518. static String typeName = "SceneTreeView";
  519. return typeName;
  520. }
  521. }