BsGUISceneTreeView.cpp 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084
  1. #include "BsGUISceneTreeView.h"
  2. #include "BsGUIArea.h"
  3. #include "BsGUILayout.h"
  4. #include "BsGUITexture.h"
  5. #include "BsGUIButton.h"
  6. #include "BsGUILabel.h"
  7. #include "BsGUISpace.h"
  8. #include "BsGUIWidget.h"
  9. #include "BsGUIToggle.h"
  10. #include "BsGUITreeViewEditBox.h"
  11. #include "BsGUIMouseEvent.h"
  12. #include "BsGUISkin.h"
  13. #include "BsGUICommandEvent.h"
  14. #include "CmSceneObject.h"
  15. #include "CmSceneManager.h"
  16. #include "BsCmdEditPlainFieldGO.h"
  17. #include "BsDragAndDropManager.h"
  18. #include "BsCmdReparentSO.h"
  19. using namespace CamelotFramework;
  20. using namespace BansheeEngine;
  21. namespace BansheeEditor
  22. {
  23. const UINT32 GUISceneTreeView::ELEMENT_EXTRA_SPACING = 3;
  24. const UINT32 GUISceneTreeView::INDENT_SIZE = 10;
  25. const UINT32 GUISceneTreeView::INITIAL_INDENT_OFFSET = 16;
  26. const UINT32 GUISceneTreeView::DRAG_MIN_DISTANCE = 3;
  27. GUISceneTreeView::DraggedSceneObjects::DraggedSceneObjects(UINT32 numObjects)
  28. :numObjects(numObjects)
  29. {
  30. objects = cm_newN<HSceneObject>(numObjects);
  31. }
  32. GUISceneTreeView::DraggedSceneObjects::~DraggedSceneObjects()
  33. {
  34. cm_deleteN(objects, numObjects);
  35. objects = nullptr;
  36. }
  37. GUISceneTreeView::TreeElement::TreeElement()
  38. :mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr), mIsSelected(false),
  39. mId(0), mIsExpanded(false), mSortedIdx(0), mIsDirty(false), mIsVisible(true)
  40. { }
  41. GUISceneTreeView::TreeElement::~TreeElement()
  42. {
  43. for(auto& child : mChildren)
  44. cm_delete(child);
  45. if(mFoldoutBtn != nullptr)
  46. GUIElement::destroy(mFoldoutBtn);
  47. if(mElement != nullptr)
  48. GUIElement::destroy(mElement);
  49. mChildren.clear();
  50. }
  51. GUISceneTreeView::GUISceneTreeView(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle,
  52. GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle,
  53. BS::GUIElementStyle* dragHighlightStyle, BS::GUIElementStyle* dragSepHighlightStyle, const GUILayoutOptions& layoutOptions)
  54. :GUIElementContainer(parent, layoutOptions), mBackgroundStyle(backgroundStyle),
  55. mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr), mIsElementSelected(false),
  56. mNameEditBox(nullptr), mSelectionBackgroundStyle(selectionBackgroundStyle), mDragInProgress(nullptr), mDragHighlightStyle(dragHighlightStyle),
  57. mDragSepHighlightStyle(dragSepHighlightStyle), mDragHighlight(nullptr), mDragSepHighlight(nullptr)
  58. {
  59. if(mBackgroundStyle == nullptr)
  60. mBackgroundStyle = parent.getSkin().getStyle("TreeViewBackground");
  61. if(mElementBtnStyle == nullptr)
  62. mElementBtnStyle = parent.getSkin().getStyle("TreeViewElementBtn");
  63. if(mFoldoutBtnStyle == nullptr)
  64. mFoldoutBtnStyle = parent.getSkin().getStyle("TreeViewFoldoutBtn");
  65. if(mSelectionBackgroundStyle == nullptr)
  66. mSelectionBackgroundStyle = parent.getSkin().getStyle("TreeViewSelectionBackground");
  67. if(mEditBoxStyle == nullptr)
  68. mEditBoxStyle = parent.getSkin().getStyle("TreeViewEditBox");
  69. if(mDragHighlightStyle == nullptr)
  70. mDragHighlightStyle = parent.getSkin().getStyle("TreeViewElementHighlight");
  71. if(mDragSepHighlightStyle == nullptr)
  72. mDragSepHighlightStyle = parent.getSkin().getStyle("TreeViewElementSepHighlight");
  73. mBackgroundImage = GUITexture::create(parent, mBackgroundStyle);
  74. mNameEditBox = GUITreeViewEditBox::create(parent, mEditBoxStyle);
  75. mNameEditBox->disableRecursively();
  76. mNameEditBox->onInputConfirmed.connect(boost::bind(&GUISceneTreeView::onEditAccepted, this));
  77. mNameEditBox->onInputCanceled.connect(boost::bind(&GUISceneTreeView::onEditCanceled, this));
  78. mDragHighlight = GUITexture::create(parent, mDragHighlightStyle);
  79. mDragSepHighlight = GUITexture::create(parent, mDragSepHighlightStyle);
  80. mDragHighlight->disableRecursively();
  81. mDragSepHighlight->disableRecursively();
  82. _registerChildElement(mBackgroundImage);
  83. _registerChildElement(mNameEditBox);
  84. _registerChildElement(mDragHighlight);
  85. _registerChildElement(mDragSepHighlight);
  86. }
  87. GUISceneTreeView::~GUISceneTreeView()
  88. {
  89. }
  90. GUISceneTreeView* GUISceneTreeView::create(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle,
  91. GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle, GUIElementStyle* dragHighlightStyle,
  92. GUIElementStyle* dragSepHighlightStyle)
  93. {
  94. return new (cm_alloc<GUISceneTreeView, PoolAlloc>()) GUISceneTreeView(parent, backgroundStyle, elementBtnStyle, foldoutBtnStyle,
  95. selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUILayoutOptions::create(&GUISkin::DefaultStyle));
  96. }
  97. GUISceneTreeView* GUISceneTreeView::create(GUIWidget& parent, const GUIOptions& options, GUIElementStyle* backgroundStyle,
  98. GUIElementStyle* elementBtnStyle, GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle,
  99. GUIElementStyle* editBoxStyle, GUIElementStyle* dragHighlightStyle, GUIElementStyle* dragSepHighlightStyle)
  100. {
  101. return new (cm_alloc<GUISceneTreeView, PoolAlloc>()) GUISceneTreeView(parent, backgroundStyle, elementBtnStyle,
  102. foldoutBtnStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUILayoutOptions::create(options, &GUISkin::DefaultStyle));
  103. }
  104. void GUISceneTreeView::update()
  105. {
  106. // NOTE - Instead of iterating through every visible element and comparing it with internal values,
  107. // I might just want to add callbacks to SceneManager that notify me of any changes and then only perform
  108. // update if anything is actually dirty
  109. struct UpdateTreeElement
  110. {
  111. UpdateTreeElement(TreeElement* element, UINT32 seqIdx, bool visible)
  112. :element(element), seqIdx(seqIdx), visible(visible)
  113. { }
  114. TreeElement* element;
  115. UINT32 seqIdx;
  116. bool visible;
  117. };
  118. HSceneObject root = CM::gSceneManager().getRootNode();
  119. mRootElement.mSceneObject = root;
  120. mRootElement.mId = root->getId();
  121. mRootElement.mSortedIdx = 0;
  122. mRootElement.mIsExpanded = true;
  123. Stack<UpdateTreeElement>::type todo;
  124. todo.push(UpdateTreeElement(&mRootElement, 0, true));
  125. while(!todo.empty())
  126. {
  127. UpdateTreeElement updateElement = todo.top();
  128. TreeElement* current = updateElement.element;
  129. HSceneObject currentSO = current->mSceneObject;
  130. todo.pop();
  131. // Check if SceneObject has changed in any way and update the tree element
  132. if(updateElement.visible)
  133. {
  134. bool completeMatch = (UINT32)current->mChildren.size() == currentSO->getNumChildren();
  135. // Early exit case - Most commonly there will be no changes between active and cached data so
  136. // we first do a quick check in order to avoid expensive comparison later
  137. if(completeMatch)
  138. {
  139. for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
  140. {
  141. UINT32 curId = currentSO->getChild(i)->getId();
  142. if(curId != current->mChildren[i]->mId)
  143. {
  144. completeMatch = false;
  145. break;
  146. }
  147. }
  148. }
  149. // Not a complete match, compare everything and insert/delete elements as needed
  150. if(!completeMatch)
  151. {
  152. Vector<TreeElement*>::type newChildren;
  153. mTempToDelete.resize(current->mChildren.size());
  154. for(UINT32 i = 0; i < (UINT32)current->mChildren.size(); i++)
  155. mTempToDelete[i] = true;
  156. for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
  157. {
  158. HSceneObject currentSOChild = currentSO->getChild(i);
  159. UINT32 curId = currentSOChild->getId();
  160. bool found = false;
  161. for(UINT32 j = 0; j < current->mChildren.size(); j++)
  162. {
  163. TreeElement* currentChild = current->mChildren[j];
  164. if(curId == currentChild->mId)
  165. {
  166. mTempToDelete[j] = false;
  167. currentChild->mIsDirty = true;
  168. currentChild->mSortedIdx = (UINT32)newChildren.size();
  169. newChildren.push_back(currentChild);
  170. found = true;
  171. break;
  172. }
  173. }
  174. if(!found)
  175. {
  176. TreeElement* newChild = cm_new<TreeElement>();
  177. newChild->mParent = current;
  178. newChild->mSceneObject = currentSOChild;
  179. newChild->mId = currentSOChild->getId();
  180. newChild->mName = currentSOChild->getName();
  181. newChild->mSortedIdx = (UINT32)newChildren.size();
  182. newChild->mIsDirty = true;
  183. newChildren.push_back(newChild);
  184. }
  185. }
  186. for(UINT32 i = 0; i < current->mChildren.size(); i++)
  187. {
  188. if(!mTempToDelete[i])
  189. continue;
  190. if(current->mChildren[i]->mIsSelected)
  191. unselectElement(current->mChildren[i]);
  192. cm_delete(current->mChildren[i]);
  193. }
  194. current->mChildren = newChildren;
  195. current->mIsDirty = true;
  196. }
  197. // Check if name needs updating
  198. const String& name = current->mSceneObject->getName();
  199. if(current->mName != name)
  200. {
  201. current->mName = name;
  202. current->mIsDirty = true;
  203. }
  204. // Calculate the sorted index of the element based on its name
  205. TreeElement* parent = current->mParent;
  206. if(current->mIsDirty && parent != nullptr)
  207. {
  208. for(UINT32 i = 0; i < (UINT32)parent->mChildren.size(); i++)
  209. {
  210. INT32 stringCompare = current->mName.compare(parent->mChildren[i]->mName);
  211. if(stringCompare > 0)
  212. {
  213. if(current->mSortedIdx < parent->mChildren[i]->mSortedIdx)
  214. std::swap(current->mSortedIdx, parent->mChildren[i]->mSortedIdx);
  215. }
  216. else if(stringCompare < 0)
  217. {
  218. if(current->mSortedIdx > parent->mChildren[i]->mSortedIdx)
  219. std::swap(current->mSortedIdx, parent->mChildren[i]->mSortedIdx);
  220. }
  221. }
  222. }
  223. }
  224. bool visibilityChanged = false;
  225. if(current->mIsVisible != updateElement.visible)
  226. {
  227. visibilityChanged = true;
  228. current->mIsVisible = updateElement.visible;
  229. current->mIsDirty = true;
  230. }
  231. if(current->mIsDirty && current != &mRootElement)
  232. {
  233. if(updateElement.visible)
  234. {
  235. HString name(toWString(current->mName));
  236. if(current->mElement == nullptr)
  237. {
  238. current->mElement = GUILabel::create(_getParentWidget(), name, mElementBtnStyle);
  239. _registerChildElement(current->mElement);
  240. }
  241. if(current->mChildren.size() > 0)
  242. {
  243. if(current->mFoldoutBtn == nullptr)
  244. {
  245. current->mFoldoutBtn = GUIToggle::create(_getParentWidget(), GUIContent(HString(L"")), mFoldoutBtnStyle);
  246. _registerChildElement(current->mFoldoutBtn);
  247. current->mFoldoutBtn->onToggled.connect(boost::bind(&GUISceneTreeView::elementToggled, this, current, _1));
  248. }
  249. }
  250. else
  251. {
  252. if(current->mFoldoutBtn != nullptr)
  253. {
  254. GUIElement::destroy(current->mFoldoutBtn);
  255. current->mFoldoutBtn = nullptr;
  256. }
  257. }
  258. current->mElement->setContent(GUIContent(name));
  259. }
  260. else
  261. {
  262. if(current->mElement != nullptr)
  263. {
  264. GUIElement::destroy(current->mElement);
  265. current->mElement = nullptr;
  266. }
  267. if(current->mFoldoutBtn != nullptr)
  268. {
  269. GUIElement::destroy(current->mFoldoutBtn);
  270. current->mFoldoutBtn = nullptr;
  271. }
  272. if(visibilityChanged && current->mIsSelected)
  273. unselectElement(current);
  274. }
  275. markContentAsDirty();
  276. current->mIsDirty = false;
  277. }
  278. // Queue children for next iteration
  279. if(visibilityChanged || current->mIsVisible)
  280. {
  281. for(UINT32 i = 0; i < (UINT32)current->mChildren.size(); i++)
  282. {
  283. todo.push(UpdateTreeElement(current->mChildren[i], i, current->mIsVisible && current->mIsExpanded));
  284. }
  285. }
  286. }
  287. }
  288. bool GUISceneTreeView::mouseEvent(const GUIMouseEvent& event)
  289. {
  290. if(event.getType() == GUIMouseEventType::MouseUp)
  291. {
  292. if(DragAndDropManager::instance().isDragInProgress())
  293. return false;
  294. const GUISceneTreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
  295. TreeElement* treeElement = nullptr;
  296. if(element != nullptr && element->isTreeElement())
  297. {
  298. treeElement = element->getTreeElement();
  299. }
  300. if(treeElement != nullptr && event.getPosition().x >= treeElement->mElement->getBounds().x)
  301. {
  302. if(event.isCtrlDown())
  303. {
  304. selectElement(treeElement);
  305. }
  306. else if(event.isShiftDown())
  307. {
  308. if(isSelectionActive())
  309. {
  310. TreeElement* selectionRoot = mSelectedElements[0].element;
  311. unselectAll();
  312. auto iterStartFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  313. [&] (const InteractableElement& x) { return x.parent == selectionRoot->mParent; } );
  314. bool foundStart = false;
  315. bool foundEnd = false;
  316. for(; iterStartFind != mVisibleElements.end(); ++iterStartFind)
  317. {
  318. if(!iterStartFind->isTreeElement())
  319. continue;
  320. TreeElement* curElem = iterStartFind->getTreeElement();
  321. if(curElem == selectionRoot)
  322. {
  323. foundStart = true;
  324. break;
  325. }
  326. }
  327. auto iterEndFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  328. [&] (const InteractableElement& x) { return &x == element; } );
  329. if(iterEndFind != mVisibleElements.end())
  330. foundEnd = true;
  331. if(foundStart && foundEnd)
  332. {
  333. if(iterStartFind < iterEndFind)
  334. {
  335. for(;iterStartFind != (iterEndFind + 1); ++iterStartFind)
  336. {
  337. if(iterStartFind->isTreeElement())
  338. selectElement(iterStartFind->getTreeElement());
  339. }
  340. }
  341. else if(iterEndFind < iterStartFind)
  342. {
  343. for(;iterEndFind != (iterStartFind + 1); ++iterEndFind)
  344. {
  345. if(iterEndFind->isTreeElement())
  346. selectElement(iterEndFind->getTreeElement());
  347. }
  348. }
  349. else
  350. selectElement(treeElement);
  351. }
  352. if(!foundStart || !foundEnd)
  353. selectElement(treeElement);
  354. }
  355. else
  356. {
  357. selectElement(treeElement);
  358. }
  359. }
  360. else
  361. {
  362. unselectAll();
  363. selectElement(treeElement);
  364. }
  365. markContentAsDirty();
  366. return true;
  367. }
  368. }
  369. else if(event.getType() == GUIMouseEventType::MouseDragStart)
  370. {
  371. mDragStartPosition = event.getPosition();
  372. }
  373. else if(event.getType() == GUIMouseEventType::MouseDrag)
  374. {
  375. UINT32 dist = mDragStartPosition.manhattanDist(event.getPosition());
  376. if(!DragAndDropManager::instance().isDragInProgress())
  377. {
  378. if(dist > DRAG_MIN_DISTANCE)
  379. {
  380. const GUISceneTreeView::InteractableElement* element = findElementUnderCoord(mDragStartPosition);
  381. TreeElement* treeElement = nullptr;
  382. if(element != nullptr && element->isTreeElement())
  383. {
  384. // If element we are trying to drag isn't selected, select it
  385. TreeElement* treeElement = element->getTreeElement();
  386. auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(),
  387. [&] (const SelectedElement& x) { return x.element == treeElement; });
  388. if(iterFind == mSelectedElements.end())
  389. {
  390. unselectAll();
  391. selectElement(element->getTreeElement());
  392. }
  393. }
  394. DraggedSceneObjects* draggedSceneObjects = cm_new<DraggedSceneObjects>((UINT32)mSelectedElements.size());
  395. UINT32 cnt = 0;
  396. for(auto& selectedElement : mSelectedElements)
  397. {
  398. draggedSceneObjects->objects[cnt] = selectedElement.element->mSceneObject;
  399. cnt++;
  400. }
  401. DragAndDropManager::instance().startDrag(HTexture(), (UINT32)DragAndDropType::SceneObject, (void*)draggedSceneObjects,
  402. boost::bind(&GUISceneTreeView::dragAndDropEnded, this));
  403. mDragPosition = event.getPosition();
  404. mDragInProgress = true;
  405. markContentAsDirty();
  406. }
  407. }
  408. }
  409. else if(event.getType() == GUIMouseEventType::MouseDragAndDropDragged)
  410. {
  411. if(DragAndDropManager::instance().isDragInProgress() && DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject)
  412. {
  413. mDragPosition = event.getPosition();
  414. mDragInProgress = true;
  415. markContentAsDirty();
  416. return true;
  417. }
  418. }
  419. else if(event.getType() == GUIMouseEventType::MouseDragAndDropDropped)
  420. {
  421. if(DragAndDropManager::instance().isDragInProgress() && DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject)
  422. {
  423. DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
  424. const GUISceneTreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
  425. TreeElement* treeElement = nullptr;
  426. if(element != nullptr)
  427. {
  428. if(element->isTreeElement())
  429. treeElement = element->getTreeElement();
  430. else
  431. treeElement = element->parent;
  432. }
  433. if(treeElement != nullptr)
  434. {
  435. Vector<HSceneObject>::type sceneObjects;
  436. HSceneObject newParent = treeElement->mSceneObject;
  437. for(UINT32 i = 0; i < draggedSceneObjects->numObjects; i++)
  438. {
  439. if(draggedSceneObjects->objects[i] != newParent)
  440. sceneObjects.push_back(draggedSceneObjects->objects[i]);
  441. }
  442. CmdReparentSO::execute(sceneObjects, newParent);
  443. }
  444. unselectAll();
  445. return true;
  446. }
  447. }
  448. else if(event.getType() == GUIMouseEventType::MouseOut)
  449. {
  450. mDragInProgress = false;
  451. markContentAsDirty();
  452. }
  453. return false;
  454. }
  455. bool GUISceneTreeView::commandEvent(const GUICommandEvent& ev)
  456. {
  457. if(ev.getType() == GUICommandEventType::Rename)
  458. {
  459. if(isSelectionActive() && mEditElement == nullptr)
  460. {
  461. unselectAll();
  462. enableEdit(mSelectedElements[0].element);
  463. }
  464. return true;
  465. }
  466. if(ev.getType() == GUICommandEventType::CursorMoveUp || ev.getType() == GUICommandEventType::SelectUp)
  467. {
  468. TreeElement* topMostElement = getTopMostSelectedElement();
  469. auto topMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  470. [&] (const InteractableElement& x) { return x.getTreeElement() == topMostElement; });
  471. if(topMostIter != mVisibleElements.end() && topMostIter != mVisibleElements.begin())
  472. {
  473. do
  474. {
  475. topMostIter--;
  476. } while (!topMostIter->isTreeElement() && topMostIter != mVisibleElements.begin());
  477. if(topMostIter->isTreeElement())
  478. {
  479. if(ev.getType() == GUICommandEventType::CursorMoveUp)
  480. unselectAll();
  481. selectElement(topMostIter->getTreeElement());
  482. }
  483. }
  484. }
  485. else if(ev.getType() == GUICommandEventType::CursorMoveDown || ev.getType() == GUICommandEventType::SelectDown)
  486. {
  487. TreeElement* bottoMostElement = getBottomMostSelectedElement();
  488. auto bottomMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  489. [&] (const InteractableElement& x) { return x.getTreeElement() == bottoMostElement; });
  490. if(bottomMostIter != mVisibleElements.end())
  491. {
  492. do
  493. {
  494. bottomMostIter++;
  495. } while (bottomMostIter != mVisibleElements.end() && !bottomMostIter->isTreeElement());
  496. if(bottomMostIter != mVisibleElements.end() && bottomMostIter->isTreeElement())
  497. {
  498. if(ev.getType() == GUICommandEventType::CursorMoveDown)
  499. unselectAll();
  500. selectElement(bottomMostIter->getTreeElement());
  501. }
  502. }
  503. }
  504. return false;
  505. }
  506. void GUISceneTreeView::dragAndDropEnded()
  507. {
  508. mDragInProgress = false;
  509. markContentAsDirty();
  510. DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
  511. cm_delete(draggedSceneObjects);
  512. }
  513. bool GUISceneTreeView::isSelectionActive() const
  514. {
  515. return mIsElementSelected && mSelectedElements.size() > 0;
  516. }
  517. void GUISceneTreeView::selectElement(TreeElement* element)
  518. {
  519. auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(),
  520. [&] (const SelectedElement& x) { return x.element == element; });
  521. if(iterFind == mSelectedElements.end())
  522. {
  523. GUITexture* background = GUITexture::create(_getParentWidget(), mSelectionBackgroundStyle);
  524. _registerChildElement(background);
  525. element->mIsSelected = true;
  526. mSelectedElements.push_back(SelectedElement(element, background));
  527. mIsElementSelected = true;
  528. }
  529. }
  530. void GUISceneTreeView::unselectElement(TreeElement* element)
  531. {
  532. auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(),
  533. [&] (const SelectedElement& x) { return x.element == element; });
  534. if(iterFind != mSelectedElements.end())
  535. {
  536. iterFind->element->mIsSelected = false;
  537. GUIElement::destroy(iterFind->background);
  538. mSelectedElements.erase(iterFind);
  539. markContentAsDirty();
  540. }
  541. mIsElementSelected = mSelectedElements.size() > 0;
  542. }
  543. void GUISceneTreeView::unselectAll()
  544. {
  545. for(auto& selectedElem : mSelectedElements)
  546. {
  547. selectedElem.element->mIsSelected = false;
  548. GUIElement::destroy(selectedElem.background);
  549. }
  550. mSelectedElements.clear();
  551. mIsElementSelected = false;
  552. markContentAsDirty();
  553. }
  554. void GUISceneTreeView::elementToggled(TreeElement* element, bool toggled)
  555. {
  556. element->mIsExpanded = toggled;
  557. }
  558. void GUISceneTreeView::onEditAccepted()
  559. {
  560. disableEdit(true);
  561. }
  562. void GUISceneTreeView::onEditCanceled()
  563. {
  564. if(mEditElement != nullptr)
  565. disableEdit(false);
  566. }
  567. void GUISceneTreeView::enableEdit(TreeElement* element)
  568. {
  569. assert(mEditElement == nullptr);
  570. mEditElement = element;
  571. mNameEditBox->enableRecursively();
  572. mNameEditBox->setFocus(true);
  573. if(element->mElement != nullptr)
  574. element->mElement->disableRecursively();
  575. }
  576. void GUISceneTreeView::disableEdit(bool applyChanges)
  577. {
  578. assert(mEditElement != nullptr);
  579. if(mEditElement->mElement != nullptr)
  580. mEditElement->mElement->enableRecursively();
  581. if(applyChanges)
  582. {
  583. String newName = toString(mNameEditBox->getText());
  584. CmdEditPlainFieldGO<String>::execute(mEditElement->mSceneObject, "mName", newName);
  585. }
  586. mNameEditBox->disableRecursively();
  587. mEditElement = nullptr;
  588. }
  589. Vector2I GUISceneTreeView::_getOptimalSize() const
  590. {
  591. struct UpdateTreeElement
  592. {
  593. UpdateTreeElement(const TreeElement* element, UINT32 indent)
  594. :element(element), indent(indent)
  595. { }
  596. const TreeElement* element;
  597. UINT32 indent;
  598. };
  599. Vector2I optimalSize;
  600. if(_getLayoutOptions().fixedWidth && _getLayoutOptions().fixedHeight)
  601. {
  602. optimalSize.x = _getLayoutOptions().width;
  603. optimalSize.y = _getLayoutOptions().height;
  604. }
  605. else
  606. {
  607. Stack<UpdateTreeElement>::type todo;
  608. todo.push(UpdateTreeElement(&mRootElement, 0));
  609. while(!todo.empty())
  610. {
  611. UpdateTreeElement currentUpdateElement = todo.top();
  612. const TreeElement* current = currentUpdateElement.element;
  613. todo.pop();
  614. INT32 yOffset = 0;
  615. if(current->mElement != nullptr)
  616. {
  617. Vector2I curOptimalSize = current->mElement->_getOptimalSize();
  618. optimalSize.x = std::max(optimalSize.x,
  619. (INT32)(INITIAL_INDENT_OFFSET + curOptimalSize.x + currentUpdateElement.indent * INDENT_SIZE));
  620. yOffset = curOptimalSize.y + ELEMENT_EXTRA_SPACING;
  621. }
  622. optimalSize.y += yOffset;
  623. for(auto& child : current->mChildren)
  624. {
  625. if(!child->mIsVisible)
  626. continue;
  627. todo.push(UpdateTreeElement(child, currentUpdateElement.indent + 1));
  628. }
  629. }
  630. if(_getLayoutOptions().fixedWidth)
  631. optimalSize.x = _getLayoutOptions().width;
  632. else
  633. {
  634. if(_getLayoutOptions().minWidth > 0)
  635. optimalSize.x = std::max((INT32)_getLayoutOptions().minWidth, optimalSize.x);
  636. if(_getLayoutOptions().maxWidth > 0)
  637. optimalSize.x = std::min((INT32)_getLayoutOptions().maxWidth, optimalSize.x);
  638. }
  639. if(_getLayoutOptions().fixedHeight)
  640. optimalSize.y = _getLayoutOptions().height;
  641. else
  642. {
  643. if(_getLayoutOptions().minHeight > 0)
  644. optimalSize.y = std::max((INT32)_getLayoutOptions().minHeight, optimalSize.y);
  645. if(_getLayoutOptions().maxHeight > 0)
  646. optimalSize.y = std::min((INT32)_getLayoutOptions().maxHeight, optimalSize.y);
  647. }
  648. }
  649. return optimalSize;
  650. }
  651. void GUISceneTreeView::updateClippedBounds()
  652. {
  653. Vector2I offset = _getOffset();
  654. mClippedBounds = RectI(offset.x, offset.y, _getWidth(), _getHeight());
  655. RectI localClipRect(mClipRect.x + mOffset.x, mClipRect.y + mOffset.y, mClipRect.width, mClipRect.height);
  656. mClippedBounds.clip(localClipRect);
  657. }
  658. void GUISceneTreeView::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
  659. RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
  660. {
  661. struct UpdateTreeElement
  662. {
  663. UpdateTreeElement(TreeElement* element, UINT32 indent)
  664. :element(element), indent(indent)
  665. { }
  666. TreeElement* element;
  667. UINT32 indent;
  668. };
  669. mVisibleElements.clear();
  670. Stack<UpdateTreeElement>::type todo;
  671. todo.push(UpdateTreeElement(&mRootElement, 0));
  672. // NOTE - Instead of iterating through all elements, try to find those within the clip rect
  673. // and only iterate through those. Others should somehow be marked in-active (similar to GUIElement::isDisabled()?)
  674. Vector<TreeElement*>::type tempOrderedElements;
  675. Vector2I offset(x, y);
  676. while(!todo.empty())
  677. {
  678. UpdateTreeElement currentUpdateElement = todo.top();
  679. TreeElement* current = currentUpdateElement.element;
  680. UINT32 indent = currentUpdateElement.indent;
  681. todo.pop();
  682. INT32 btnHeight = 0;
  683. INT32 yOffset = 0;
  684. if(current->mElement != nullptr)
  685. {
  686. Vector2I elementSize = current->mElement->_getOptimalSize();
  687. btnHeight = elementSize.y;
  688. mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 0, RectI(x, offset.y, width, ELEMENT_EXTRA_SPACING)));
  689. mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 1, RectI(x, offset.y + ELEMENT_EXTRA_SPACING, width, btnHeight)));
  690. offset.x = x + INITIAL_INDENT_OFFSET + indent * INDENT_SIZE;
  691. offset.y += ELEMENT_EXTRA_SPACING;
  692. current->mElement->_setOffset(offset);
  693. current->mElement->_setWidth(elementSize.x);
  694. current->mElement->_setHeight(elementSize.y);
  695. current->mElement->_setAreaDepth(areaDepth);
  696. current->mElement->_setWidgetDepth(widgetDepth);
  697. RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  698. current->mElement->_setClipRect(elemClipRect);
  699. yOffset = btnHeight;
  700. }
  701. if(current->mFoldoutBtn != nullptr)
  702. {
  703. Vector2I elementSize = current->mFoldoutBtn->_getOptimalSize();
  704. offset.x -= std::min((INT32)INITIAL_INDENT_OFFSET, elementSize.x);
  705. Vector2I myOffset = offset;
  706. myOffset.y -= 2; // TODO: Arbitrary offset, I should adjust it based on font baseline so that the button is nicely centered on text
  707. if(elementSize.y > btnHeight)
  708. {
  709. UINT32 diff = elementSize.y - btnHeight;
  710. float half = diff * 0.5f;
  711. myOffset.y -= Math::floorToInt(half);
  712. }
  713. current->mFoldoutBtn->_setOffset(myOffset);
  714. current->mFoldoutBtn->_setWidth(elementSize.x);
  715. current->mFoldoutBtn->_setHeight(elementSize.y);
  716. current->mFoldoutBtn->_setAreaDepth(areaDepth);
  717. current->mFoldoutBtn->_setWidgetDepth(widgetDepth);
  718. RectI elemClipRect(clipRect.x - myOffset.x, clipRect.y - myOffset.y, clipRect.width, clipRect.height);
  719. current->mFoldoutBtn->_setClipRect(elemClipRect);
  720. }
  721. offset.y += yOffset;
  722. tempOrderedElements.resize(current->mChildren.size(), nullptr);
  723. for(auto& child : current->mChildren)
  724. {
  725. tempOrderedElements[child->mSortedIdx] = child;
  726. }
  727. for(auto iter = tempOrderedElements.rbegin(); iter != tempOrderedElements.rend(); ++iter)
  728. {
  729. TreeElement* child = *iter;
  730. if(!child->mIsVisible)
  731. continue;
  732. todo.push(UpdateTreeElement(child, indent + 1));
  733. }
  734. }
  735. UINT32 remainingHeight = (UINT32)std::max(0, (INT32)height - (offset.y - y));
  736. if(remainingHeight > 0)
  737. mVisibleElements.push_back(InteractableElement(&mRootElement, (UINT32)mRootElement.mChildren.size() * 2, RectI(x, offset.y, width, remainingHeight)));
  738. for(auto selectedElem : mSelectedElements)
  739. {
  740. GUILabel* targetElement = selectedElem.element->mElement;
  741. Vector2I offset = targetElement->_getOffset();
  742. offset.x = x;
  743. selectedElem.background->_setOffset(offset);
  744. selectedElem.background->_setWidth(width);
  745. selectedElem.background->_setHeight(targetElement->_getHeight());
  746. selectedElem.background->_setAreaDepth(areaDepth + 1);
  747. selectedElem.background->_setWidgetDepth(widgetDepth);
  748. RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  749. selectedElem.background->_setClipRect(elemClipRect);
  750. }
  751. if(mEditElement != nullptr)
  752. {
  753. GUILabel* targetElement = mEditElement->mElement;
  754. Vector2I offset = targetElement->_getOffset();
  755. UINT32 remainingWidth = (UINT32)std::max(0, (((INT32)width) - (offset.x - x)));
  756. mNameEditBox->_setOffset(offset);
  757. mNameEditBox->_setWidth(remainingWidth);
  758. mNameEditBox->_setHeight(targetElement->_getHeight());
  759. mNameEditBox->_setAreaDepth(areaDepth);
  760. mNameEditBox->_setWidgetDepth(widgetDepth);
  761. RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  762. mNameEditBox->_setClipRect(elemClipRect);
  763. }
  764. if(mDragInProgress)
  765. {
  766. const InteractableElement* interactableElement = findElementUnderCoord(mDragPosition);
  767. if(interactableElement == nullptr)
  768. {
  769. if(!mDragHighlight->_isDisabled())
  770. mDragHighlight->disableRecursively();
  771. if(!mDragSepHighlight->_isDisabled())
  772. mDragSepHighlight->disableRecursively();
  773. }
  774. else
  775. {
  776. if(interactableElement->isTreeElement())
  777. {
  778. if(!mDragSepHighlight->_isDisabled())
  779. mDragSepHighlight->disableRecursively();
  780. if(mDragHighlight->_isDisabled())
  781. mDragHighlight->enableRecursively();
  782. Vector2I offset(interactableElement->bounds.x, interactableElement->bounds.y);
  783. mDragHighlight->_setOffset(offset);
  784. mDragHighlight->_setWidth(interactableElement->bounds.width);
  785. mDragHighlight->_setHeight(interactableElement->bounds.height);
  786. mDragHighlight->_setAreaDepth(areaDepth + 1);
  787. mDragHighlight->_setWidgetDepth(widgetDepth);
  788. RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  789. mDragHighlight->_setClipRect(elemClipRect);
  790. }
  791. else
  792. {
  793. if(!mDragHighlight->_isDisabled())
  794. mDragHighlight->disableRecursively();
  795. if(mDragSepHighlight->_isDisabled())
  796. mDragSepHighlight->enableRecursively();
  797. Vector2I offset(interactableElement->bounds.x, interactableElement->bounds.y);
  798. mDragSepHighlight->_setOffset(offset);
  799. mDragSepHighlight->_setWidth(interactableElement->bounds.width);
  800. mDragSepHighlight->_setHeight(interactableElement->bounds.height);
  801. mDragSepHighlight->_setAreaDepth(areaDepth + 1);
  802. mDragSepHighlight->_setWidgetDepth(widgetDepth);
  803. RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
  804. mDragSepHighlight->_setClipRect(elemClipRect);
  805. }
  806. }
  807. }
  808. else
  809. {
  810. if(!mDragHighlight->_isDisabled())
  811. mDragHighlight->disableRecursively();
  812. if(!mDragSepHighlight->_isDisabled())
  813. mDragSepHighlight->disableRecursively();
  814. }
  815. }
  816. const GUISceneTreeView::InteractableElement* GUISceneTreeView::findElementUnderCoord(const CM::Vector2I& coord) const
  817. {
  818. for(auto& element : mVisibleElements)
  819. {
  820. if(element.bounds.contains(coord))
  821. {
  822. return &element;
  823. }
  824. }
  825. return nullptr;
  826. }
  827. GUISceneTreeView::TreeElement* GUISceneTreeView::getTopMostSelectedElement() const
  828. {
  829. auto topMostElement = mVisibleElements.end();
  830. for(auto& selectedElement : mSelectedElements)
  831. {
  832. auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  833. [&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
  834. if(iterFind != mVisibleElements.end())
  835. {
  836. if(topMostElement == mVisibleElements.end())
  837. topMostElement = iterFind;
  838. else
  839. {
  840. if(iterFind->bounds.y < topMostElement->bounds.y)
  841. topMostElement = iterFind;
  842. }
  843. }
  844. }
  845. if(topMostElement != mVisibleElements.end())
  846. return topMostElement->getTreeElement();
  847. else
  848. return nullptr;
  849. }
  850. GUISceneTreeView::TreeElement* GUISceneTreeView::getBottomMostSelectedElement() const
  851. {
  852. auto& botMostElement = mVisibleElements.end();
  853. for(auto& selectedElement : mSelectedElements)
  854. {
  855. auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  856. [&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
  857. if(iterFind != mVisibleElements.end())
  858. {
  859. if(botMostElement == mVisibleElements.end())
  860. botMostElement = iterFind;
  861. else
  862. {
  863. if((iterFind->bounds.y + iterFind->bounds.height) > (botMostElement->bounds.y + botMostElement->bounds.height))
  864. botMostElement = iterFind;
  865. }
  866. }
  867. }
  868. if(botMostElement != mVisibleElements.end())
  869. return botMostElement->getTreeElement();
  870. else
  871. return nullptr;
  872. }
  873. const String& GUISceneTreeView::getGUITypeName()
  874. {
  875. static String typeName = "SceneTreeView";
  876. return typeName;
  877. }
  878. GUISceneTreeView::TreeElement* GUISceneTreeView::InteractableElement::getTreeElement() const
  879. {
  880. if(!isTreeElement())
  881. return nullptr;
  882. UINT32 sortedIdx = (index - 1) / 2;
  883. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  884. [&](const TreeElement* x) { return x->mSortedIdx == sortedIdx; });
  885. if(findIter != parent->mChildren.end())
  886. return *findIter;
  887. return nullptr;
  888. }
  889. }