BsGUISceneTreeView.cpp 34 KB

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