BsGUITreeView.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsGUITreeView.h"
  4. #include "BsGUILayout.h"
  5. #include "BsGUITexture.h"
  6. #include "BsGUIButton.h"
  7. #include "BsGUILabel.h"
  8. #include "BsGUISpace.h"
  9. #include "BsCGUIWidget.h"
  10. #include "BsGUIToggle.h"
  11. #include "BsGUITreeViewEditBox.h"
  12. #include "BsGUIMouseEvent.h"
  13. #include "BsGUISkin.h"
  14. #include "BsGUICommandEvent.h"
  15. #include "BsGUIVirtualButtonEvent.h"
  16. #include "BsGUIScrollArea.h"
  17. #include "BsDragAndDropManager.h"
  18. #include "BsTime.h"
  19. using namespace std::placeholders;
  20. namespace BansheeEngine
  21. {
  22. const UINT32 GUITreeView::ELEMENT_EXTRA_SPACING = 3;
  23. const UINT32 GUITreeView::INDENT_SIZE = 10;
  24. const UINT32 GUITreeView::INITIAL_INDENT_OFFSET = 16;
  25. const UINT32 GUITreeView::DRAG_MIN_DISTANCE = 3;
  26. const float GUITreeView::AUTO_EXPAND_DELAY_SEC = 0.5f;
  27. const float GUITreeView::SCROLL_AREA_HEIGHT_PCT = 0.1f;
  28. const UINT32 GUITreeView::SCROLL_SPEED_PX_PER_SEC = 100;
  29. const Color GUITreeView::CUT_COLOR = Color(1.0f, 1.0f, 1.0f, 0.3f);
  30. const Color GUITreeView::DISABLED_COLOR = Color(1.0f, 1.0f, 1.0f, 0.6f);
  31. VirtualButton GUITreeView::mRenameVB = VirtualButton("Rename");
  32. VirtualButton GUITreeView::mDeleteVB = VirtualButton("Delete");
  33. VirtualButton GUITreeView::mDuplicateVB = VirtualButton("Duplicate");
  34. VirtualButton GUITreeView::mCutVB = VirtualButton("Cut");
  35. VirtualButton GUITreeView::mCopyVB = VirtualButton("Copy");
  36. VirtualButton GUITreeView::mPasteVB = VirtualButton("Paste");
  37. GUITreeView::TreeElement::TreeElement()
  38. :mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr), mIsSelected(false),
  39. mIsExpanded(false), mSortedIdx(0), mIsVisible(true), mIsHighlighted(false), mIsCut(false), mIsDisabled(false)
  40. { }
  41. GUITreeView::TreeElement::~TreeElement()
  42. {
  43. for(auto& child : mChildren)
  44. bs_delete(child);
  45. if(mFoldoutBtn != nullptr)
  46. GUIElement::destroy(mFoldoutBtn);
  47. if(mElement != nullptr)
  48. GUIElement::destroy(mElement);
  49. mChildren.clear();
  50. }
  51. bool GUITreeView::TreeElement::isParentRec(TreeElement* element) const
  52. {
  53. TreeElement* curParent = mParent;
  54. while(curParent != nullptr)
  55. {
  56. if(curParent == element)
  57. return true;
  58. curParent = curParent->mParent;
  59. }
  60. return false;
  61. }
  62. GUITreeView::TreeElement* GUITreeView::InteractableElement::getTreeElement() const
  63. {
  64. if(!isTreeElement())
  65. return nullptr;
  66. UINT32 sortedIdx = (index - 1) / 2;
  67. auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
  68. [&](const TreeElement* x) { return x->mSortedIdx == sortedIdx; });
  69. if(findIter != parent->mChildren.end())
  70. return *findIter;
  71. return nullptr;
  72. }
  73. GUITreeView::GUITreeView(const String& backgroundStyle, const String& elementBtnStyle,
  74. const String& foldoutBtnStyle, const String& selectionBackgroundStyle, const String& highlightBackgroundStyle,
  75. const String& editBoxStyle, const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions)
  76. :GUIElementContainer(dimensions), mBackgroundStyle(backgroundStyle),
  77. mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr), mIsElementSelected(false),
  78. mNameEditBox(nullptr), mHighlightBackgroundStyle(highlightBackgroundStyle), mSelectionBackgroundStyle(selectionBackgroundStyle), mDragInProgress(nullptr),
  79. mDragHighlightStyle(dragHighlightStyle), mDragSepHighlightStyle(dragSepHighlightStyle), mDragHighlight(nullptr), mDragSepHighlight(nullptr), mMouseOverDragElement(nullptr),
  80. mMouseOverDragElementTime(0.0f), mScrollState(ScrollState::None), mLastScrollTime(0.0f), mIsElementHighlighted(false)
  81. {
  82. if(mBackgroundStyle == StringUtil::BLANK)
  83. mBackgroundStyle = "TreeViewBackground";
  84. if(mElementBtnStyle == StringUtil::BLANK)
  85. mElementBtnStyle = "TreeViewElementBtn";
  86. if(mFoldoutBtnStyle == StringUtil::BLANK)
  87. mFoldoutBtnStyle = "TreeViewFoldoutBtn";
  88. if(mSelectionBackgroundStyle == StringUtil::BLANK)
  89. mSelectionBackgroundStyle = "TreeViewSelectionBackground";
  90. if (mHighlightBackgroundStyle == StringUtil::BLANK)
  91. mHighlightBackgroundStyle = "TreeViewHighlightBackground";
  92. if(mEditBoxStyle == StringUtil::BLANK)
  93. mEditBoxStyle = "TreeViewEditBox";
  94. if(mDragHighlightStyle == StringUtil::BLANK)
  95. mDragHighlightStyle = "TreeViewElementHighlight";
  96. if(mDragSepHighlightStyle == StringUtil::BLANK)
  97. mDragSepHighlightStyle = "TreeViewElementSepHighlight";
  98. mBackgroundImage = GUITexture::create(mBackgroundStyle);
  99. mNameEditBox = GUITreeViewEditBox::create(mEditBoxStyle);
  100. mNameEditBox->setVisible(false);
  101. mNameEditBox->onInputConfirmed.connect(std::bind(&GUITreeView::onEditAccepted, this));
  102. mNameEditBox->onInputCanceled.connect(std::bind(&GUITreeView::onEditCanceled, this));
  103. mNameEditBox->onFocusLost.connect(std::bind(&GUITreeView::onEditFocusLost, this));
  104. mDragHighlight = GUITexture::create(mDragHighlightStyle);
  105. mDragSepHighlight = GUITexture::create(mDragSepHighlightStyle);
  106. mDragHighlight->setVisible(false);
  107. mDragSepHighlight->setVisible(false);
  108. mDragHighlight->_setElementDepth(2);
  109. mDragSepHighlight->_setElementDepth(2);
  110. _registerChildElement(mBackgroundImage);
  111. _registerChildElement(mNameEditBox);
  112. _registerChildElement(mDragHighlight);
  113. _registerChildElement(mDragSepHighlight);
  114. }
  115. GUITreeView::~GUITreeView()
  116. {
  117. }
  118. void GUITreeView::_update()
  119. {
  120. // Attempt to auto-expand elements we are dragging over
  121. if(acceptDragAndDrop())
  122. {
  123. const GUITreeView::InteractableElement* element = findElementUnderCoord(mDragPosition);
  124. temporarilyExpandElement(element);
  125. }
  126. // NOTE - Instead of iterating through every visible element and comparing it with internal values,
  127. // I might just want to add callbacks to SceneManager that notify me of any changes and then only perform
  128. // update if anything is actually dirty
  129. updateTreeElementHierarchy();
  130. // Attempt to scroll if needed
  131. if(mScrollState != ScrollState::None)
  132. {
  133. GUIScrollArea* scrollArea = findParentScrollArea();
  134. if(scrollArea != nullptr)
  135. {
  136. float curTime = gTime().getTime();
  137. float timeDiff = curTime - mLastScrollTime;
  138. float secondsPerPixel = 1.0f / SCROLL_SPEED_PX_PER_SEC;
  139. switch(mScrollState)
  140. {
  141. case ScrollState::TransitioningUp:
  142. mScrollState = ScrollState::Up;
  143. mLastScrollTime = curTime;
  144. break;
  145. case ScrollState::TransitioningDown:
  146. mScrollState = ScrollState::Down;
  147. mLastScrollTime = curTime;
  148. break;
  149. case ScrollState::Up:
  150. {
  151. UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
  152. mLastScrollTime += scrollAmount * secondsPerPixel;
  153. scrollArea->scrollUpPx(scrollAmount);
  154. }
  155. break;
  156. case ScrollState::Down:
  157. {
  158. UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
  159. mLastScrollTime += scrollAmount * secondsPerPixel;
  160. scrollArea->scrollDownPx(scrollAmount);
  161. }
  162. break;
  163. }
  164. }
  165. }
  166. }
  167. bool GUITreeView::_mouseEvent(const GUIMouseEvent& event)
  168. {
  169. if(event.getType() == GUIMouseEventType::MouseUp)
  170. {
  171. if(DragAndDropManager::instance().isDragInProgress())
  172. return false;
  173. const GUITreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
  174. TreeElement* treeElement = nullptr;
  175. if(element != nullptr && element->isTreeElement())
  176. {
  177. treeElement = element->getTreeElement();
  178. }
  179. if (treeElement != nullptr)
  180. {
  181. bool onFoldout = false;
  182. if (treeElement->mFoldoutBtn != nullptr)
  183. onFoldout = treeElement->mFoldoutBtn->_getClippedBounds().contains(event.getPosition());
  184. bool onEditElement = false;
  185. if (mEditElement != nullptr)
  186. onEditElement = treeElement == mEditElement;
  187. if (!onFoldout && !onEditElement)
  188. {
  189. if (event.getButton() == GUIMouseButton::Left)
  190. {
  191. if (event.isCtrlDown())
  192. {
  193. selectElement(treeElement);
  194. }
  195. else if (event.isShiftDown())
  196. {
  197. if (isSelectionActive())
  198. {
  199. TreeElement* selectionRoot = mSelectedElements[0].element;
  200. unselectAll();
  201. auto iterStartFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  202. [&](const InteractableElement& x) { return x.parent == selectionRoot->mParent; });
  203. bool foundStart = false;
  204. bool foundEnd = false;
  205. for (; iterStartFind != mVisibleElements.end(); ++iterStartFind)
  206. {
  207. if (!iterStartFind->isTreeElement())
  208. continue;
  209. TreeElement* curElem = iterStartFind->getTreeElement();
  210. if (curElem == selectionRoot)
  211. {
  212. foundStart = true;
  213. break;
  214. }
  215. }
  216. auto iterEndFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  217. [&](const InteractableElement& x) { return &x == element; });
  218. if (iterEndFind != mVisibleElements.end())
  219. foundEnd = true;
  220. if (foundStart && foundEnd)
  221. {
  222. if (iterStartFind < iterEndFind)
  223. {
  224. for (; iterStartFind != (iterEndFind + 1); ++iterStartFind)
  225. {
  226. if (iterStartFind->isTreeElement())
  227. selectElement(iterStartFind->getTreeElement());
  228. }
  229. }
  230. else if (iterEndFind < iterStartFind)
  231. {
  232. for (; iterEndFind != (iterStartFind + 1); ++iterEndFind)
  233. {
  234. if (iterEndFind->isTreeElement())
  235. selectElement(iterEndFind->getTreeElement());
  236. }
  237. }
  238. else
  239. selectElement(treeElement);
  240. }
  241. if (!foundStart || !foundEnd)
  242. selectElement(treeElement);
  243. }
  244. else
  245. {
  246. selectElement(treeElement);
  247. }
  248. }
  249. else
  250. {
  251. bool doRename = false;
  252. if (isSelectionActive())
  253. {
  254. for (auto& selectedElem : mSelectedElements)
  255. {
  256. if (selectedElem.element == treeElement)
  257. {
  258. doRename = true;
  259. break;
  260. }
  261. }
  262. }
  263. unselectAll();
  264. selectElement(treeElement);
  265. if (doRename)
  266. renameSelected();
  267. }
  268. _markLayoutAsDirty();
  269. return true;
  270. }
  271. else if (event.getButton() == GUIMouseButton::Right)
  272. {
  273. unselectAll();
  274. selectElement(treeElement);
  275. _markLayoutAsDirty();
  276. return true;
  277. }
  278. }
  279. }
  280. else
  281. {
  282. unselectAll();
  283. return true;
  284. }
  285. }
  286. else if(event.getType() == GUIMouseEventType::MouseDragStart)
  287. {
  288. clearPing();
  289. mDragStartPosition = event.getPosition();
  290. }
  291. else if(event.getType() == GUIMouseEventType::MouseDrag)
  292. {
  293. UINT32 dist = mDragStartPosition.manhattanDist(event.getPosition());
  294. if(!DragAndDropManager::instance().isDragInProgress())
  295. {
  296. if(dist > DRAG_MIN_DISTANCE && mEditElement == nullptr)
  297. {
  298. const GUITreeView::InteractableElement* element = findElementUnderCoord(mDragStartPosition);
  299. TreeElement* treeElement = nullptr;
  300. if(element != nullptr && element->isTreeElement())
  301. {
  302. // If element we are trying to drag isn't selected, select it
  303. TreeElement* treeElement = element->getTreeElement();
  304. auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(),
  305. [&] (const SelectedElement& x) { return x.element == treeElement; });
  306. if(iterFind == mSelectedElements.end())
  307. {
  308. unselectAll();
  309. selectElement(element->getTreeElement());
  310. }
  311. }
  312. dragAndDropStart();
  313. mDragPosition = event.getPosition();
  314. mDragInProgress = true;
  315. mScrollState = ScrollState::None;
  316. _markLayoutAsDirty();
  317. }
  318. }
  319. }
  320. else if(event.getType() == GUIMouseEventType::MouseDragAndDropDragged)
  321. {
  322. if(acceptDragAndDrop())
  323. {
  324. clearPing();
  325. mDragPosition = event.getPosition();
  326. mDragInProgress = true;
  327. _markLayoutAsDirty();
  328. if(mBottomScrollBounds.contains(mDragPosition))
  329. {
  330. if(mScrollState != ScrollState::Down)
  331. mScrollState = ScrollState::TransitioningDown;
  332. }
  333. else if(mTopScrollBounds.contains(mDragPosition))
  334. {
  335. if(mScrollState != ScrollState::Up)
  336. mScrollState = ScrollState::TransitioningUp;
  337. }
  338. else
  339. mScrollState = ScrollState::None;
  340. return true;
  341. }
  342. }
  343. else if(event.getType() == GUIMouseEventType::MouseDragAndDropDropped)
  344. {
  345. if(acceptDragAndDrop())
  346. {
  347. const GUITreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
  348. TreeElement* treeElement = nullptr;
  349. if(element != nullptr)
  350. {
  351. if(element->isTreeElement())
  352. treeElement = element->getTreeElement();
  353. else
  354. treeElement = element->parent;
  355. }
  356. dragAndDropEnded(treeElement);
  357. unselectAll();
  358. return true;
  359. }
  360. }
  361. else if(event.getType() == GUIMouseEventType::MouseOut)
  362. {
  363. mDragInProgress = false;
  364. _markLayoutAsDirty();
  365. }
  366. else if(event.getType() == GUIMouseEventType::MouseDragAndDropLeft)
  367. {
  368. mDragInProgress = false;
  369. _markLayoutAsDirty();
  370. }
  371. return false;
  372. }
  373. bool GUITreeView::_commandEvent(const GUICommandEvent& ev)
  374. {
  375. if(ev.getType() == GUICommandEventType::MoveUp || ev.getType() == GUICommandEventType::SelectUp)
  376. {
  377. TreeElement* topMostElement = getTopMostSelectedElement();
  378. auto topMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  379. [&] (const InteractableElement& x) { return x.getTreeElement() == topMostElement; });
  380. if(topMostIter != mVisibleElements.end() && topMostIter != mVisibleElements.begin())
  381. {
  382. do
  383. {
  384. topMostIter--;
  385. } while (!topMostIter->isTreeElement() && topMostIter != mVisibleElements.begin());
  386. if(topMostIter->isTreeElement())
  387. {
  388. if(ev.getType() == GUICommandEventType::MoveUp)
  389. unselectAll();
  390. TreeElement* treeElement = topMostIter->getTreeElement();
  391. selectElement(treeElement);
  392. scrollToElement(treeElement, false);
  393. }
  394. }
  395. }
  396. else if(ev.getType() == GUICommandEventType::MoveDown || ev.getType() == GUICommandEventType::SelectDown)
  397. {
  398. TreeElement* bottoMostElement = getBottomMostSelectedElement();
  399. auto bottomMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  400. [&] (const InteractableElement& x) { return x.getTreeElement() == bottoMostElement; });
  401. if(bottomMostIter != mVisibleElements.end())
  402. {
  403. do
  404. {
  405. bottomMostIter++;
  406. } while (bottomMostIter != mVisibleElements.end() && !bottomMostIter->isTreeElement());
  407. if(bottomMostIter != mVisibleElements.end() && bottomMostIter->isTreeElement())
  408. {
  409. if(ev.getType() == GUICommandEventType::MoveDown)
  410. unselectAll();
  411. TreeElement* treeElement = bottomMostIter->getTreeElement();
  412. selectElement(treeElement);
  413. scrollToElement(treeElement, false);
  414. }
  415. }
  416. }
  417. return GUIElementContainer::_commandEvent(ev);
  418. }
  419. bool GUITreeView::_virtualButtonEvent(const GUIVirtualButtonEvent& ev)
  420. {
  421. if(ev.getButton() == mRenameVB)
  422. {
  423. renameSelected();
  424. return true;
  425. }
  426. else if(ev.getButton() == mDeleteVB)
  427. {
  428. deleteSelection();
  429. }
  430. else if (ev.getButton() == mDuplicateVB)
  431. {
  432. duplicateSelection();
  433. return true;
  434. }
  435. else if (ev.getButton() == mCutVB)
  436. {
  437. cutSelection();
  438. return true;
  439. }
  440. else if (ev.getButton() == mCopyVB)
  441. {
  442. copySelection();
  443. return true;
  444. }
  445. else if (ev.getButton() == mPasteVB)
  446. {
  447. paste();
  448. return true;
  449. }
  450. return false;
  451. }
  452. bool GUITreeView::isSelectionActive() const
  453. {
  454. return mIsElementSelected && mSelectedElements.size() > 0;
  455. }
  456. void GUITreeView::selectElement(TreeElement* element)
  457. {
  458. clearPing();
  459. auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(),
  460. [&] (const SelectedElement& x) { return x.element == element; });
  461. if(iterFind == mSelectedElements.end())
  462. {
  463. GUITexture* background = GUITexture::create(mSelectionBackgroundStyle);
  464. background->_setElementDepth(3);
  465. _registerChildElement(background);
  466. element->mIsSelected = true;
  467. mSelectedElements.push_back(SelectedElement(element, background));
  468. mIsElementSelected = true;
  469. selectionChanged();
  470. }
  471. }
  472. void GUITreeView::unselectElement(TreeElement* element)
  473. {
  474. clearPing();
  475. auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(),
  476. [&] (const SelectedElement& x) { return x.element == element; });
  477. if(iterFind != mSelectedElements.end())
  478. {
  479. iterFind->element->mIsSelected = false;
  480. GUIElement::destroy(iterFind->background);
  481. mSelectedElements.erase(iterFind);
  482. _markLayoutAsDirty();
  483. selectionChanged();
  484. }
  485. mIsElementSelected = mSelectedElements.size() > 0;
  486. }
  487. void GUITreeView::unselectAll(bool sendEvent)
  488. {
  489. clearPing();
  490. for(auto& selectedElem : mSelectedElements)
  491. {
  492. selectedElem.element->mIsSelected = false;
  493. GUIElement::destroy(selectedElem.background);
  494. }
  495. mSelectedElements.clear();
  496. mIsElementSelected = false;
  497. _markLayoutAsDirty();
  498. if (sendEvent)
  499. selectionChanged();
  500. }
  501. void GUITreeView::renameSelected()
  502. {
  503. if (isSelectionActive() && mEditElement == nullptr)
  504. {
  505. clearPing();
  506. enableEdit(mSelectedElements[0].element);
  507. unselectAll();
  508. }
  509. }
  510. void GUITreeView::deleteSelection()
  511. {
  512. if (isSelectionActive())
  513. {
  514. auto isChildOf = [&](const TreeElement* parent, const TreeElement* child)
  515. {
  516. const TreeElement* elem = child;
  517. while (elem != nullptr && elem != parent)
  518. elem = elem->mParent;
  519. return elem == parent;
  520. };
  521. // Ensure we don't unnecessarily try to delete children if their
  522. // parent is getting deleted anyway
  523. Vector<TreeElement*> elementsToDelete;
  524. for (UINT32 i = 0; i < (UINT32)mSelectedElements.size(); i++)
  525. {
  526. bool hasDeletedParent = false;
  527. for (UINT32 j = 0; j < (UINT32)mSelectedElements.size(); j++)
  528. {
  529. if (i == j)
  530. continue;
  531. if (isChildOf(mSelectedElements[j].element, mSelectedElements[i].element))
  532. {
  533. hasDeletedParent = true;
  534. break;
  535. }
  536. }
  537. if (!hasDeletedParent)
  538. elementsToDelete.push_back(mSelectedElements[i].element);
  539. }
  540. clearPing();
  541. unselectAll();
  542. for (auto& elem : elementsToDelete)
  543. deleteTreeElement(elem);
  544. }
  545. }
  546. void GUITreeView::ping(TreeElement* element)
  547. {
  548. clearPing();
  549. expandToElement(element);
  550. scrollToElement(element, true);
  551. GUITexture* background = GUITexture::create(mHighlightBackgroundStyle);
  552. background->_setElementDepth(2);
  553. _registerChildElement(background);
  554. element->mIsHighlighted = true;
  555. mHighlightedElement.element = element;
  556. mHighlightedElement.background = background;
  557. mIsElementHighlighted = true;
  558. _markLayoutAsDirty();
  559. }
  560. void GUITreeView::clearPing()
  561. {
  562. if (!mIsElementHighlighted)
  563. return;
  564. mHighlightedElement.element->mIsHighlighted = false;
  565. GUIElement::destroy(mHighlightedElement.background);
  566. mHighlightedElement.element = nullptr;
  567. mHighlightedElement.background = nullptr;
  568. mIsElementHighlighted = false;
  569. _markLayoutAsDirty();
  570. }
  571. void GUITreeView::expandToElement(TreeElement* element)
  572. {
  573. if (element->mIsVisible || element->mParent == nullptr)
  574. return;
  575. Stack<TreeElement*> todo;
  576. TreeElement* parent = element->mParent;
  577. while (parent != nullptr)
  578. {
  579. if (!parent->mIsExpanded)
  580. todo.push(parent);
  581. if (parent->mIsVisible)
  582. break;
  583. parent = parent->mParent;
  584. }
  585. while (!todo.empty())
  586. {
  587. TreeElement* curElement = todo.top();
  588. todo.pop();
  589. expandElement(curElement);
  590. }
  591. }
  592. void GUITreeView::expandElement(TreeElement* element)
  593. {
  594. if(element->mIsExpanded)
  595. return;
  596. element->mIsExpanded = true;
  597. if(element->mParent == nullptr || (element->mParent->mIsVisible && element->mParent->mIsExpanded))
  598. {
  599. Stack<TreeElement*> todo;
  600. todo.push(element);
  601. while(!todo.empty())
  602. {
  603. TreeElement* curElem = todo.top();
  604. todo.pop();
  605. curElem->mIsVisible = true;
  606. updateElementGUI(curElem);
  607. if(curElem->mIsExpanded)
  608. {
  609. for(auto& child : curElem->mChildren)
  610. todo.push(child);
  611. }
  612. }
  613. }
  614. }
  615. void GUITreeView::collapseElement(TreeElement* element)
  616. {
  617. if(!element->mIsExpanded)
  618. return;
  619. element->mIsExpanded = false;
  620. updateElementGUI(element);
  621. if(element->mParent == nullptr || (element->mParent->mIsVisible && element->mParent->mIsExpanded))
  622. {
  623. Stack<TreeElement*> todo;
  624. for(auto& child : element->mChildren)
  625. todo.push(child);
  626. while(!todo.empty())
  627. {
  628. TreeElement* curElem = todo.top();
  629. todo.pop();
  630. curElem->mIsVisible = false;
  631. if(curElem->mIsSelected)
  632. unselectElement(curElem);
  633. updateElementGUI(curElem);
  634. if(curElem->mIsExpanded)
  635. {
  636. for(auto& child : curElem->mChildren)
  637. todo.push(child);
  638. }
  639. }
  640. }
  641. }
  642. void GUITreeView::updateElementGUI(TreeElement* element)
  643. {
  644. if(element == &getRootElement())
  645. return;
  646. if(element->mIsVisible)
  647. {
  648. HString name(toWString(element->mName));
  649. if(element->mElement == nullptr)
  650. {
  651. element->mElement = GUILabel::create(name, mElementBtnStyle);
  652. _registerChildElement(element->mElement);
  653. }
  654. if (element->mIsCut)
  655. {
  656. Color cutTint = element->mTint;
  657. cutTint.a = CUT_COLOR.a;
  658. element->mElement->setTint(cutTint);
  659. }
  660. else if(element->mIsDisabled)
  661. {
  662. Color disabledTint = element->mTint;
  663. disabledTint.a = DISABLED_COLOR.a;
  664. element->mElement->setTint(disabledTint);
  665. }
  666. else
  667. element->mElement->setTint(element->mTint);
  668. if(element->mChildren.size() > 0)
  669. {
  670. if(element->mFoldoutBtn == nullptr)
  671. {
  672. element->mFoldoutBtn = GUIToggle::create(GUIContent(HString(L"")), mFoldoutBtnStyle);
  673. _registerChildElement(element->mFoldoutBtn);
  674. element->mFoldoutBtn->onToggled.connect(std::bind(&GUITreeView::elementToggled, this, element, _1));
  675. if(element->mIsExpanded)
  676. element->mFoldoutBtn->toggleOn();
  677. }
  678. }
  679. else
  680. {
  681. if(element->mFoldoutBtn != nullptr)
  682. {
  683. GUIElement::destroy(element->mFoldoutBtn);
  684. element->mFoldoutBtn = nullptr;
  685. }
  686. }
  687. element->mElement->setContent(GUIContent(name));
  688. }
  689. else
  690. {
  691. if(element->mElement != nullptr)
  692. {
  693. GUIElement::destroy(element->mElement);
  694. element->mElement = nullptr;
  695. }
  696. if(element->mFoldoutBtn != nullptr)
  697. {
  698. GUIElement::destroy(element->mFoldoutBtn);
  699. element->mFoldoutBtn = nullptr;
  700. }
  701. if(element->mIsSelected && element->mIsExpanded)
  702. unselectElement(element);
  703. }
  704. _markLayoutAsDirty();
  705. }
  706. void GUITreeView::elementToggled(TreeElement* element, bool toggled)
  707. {
  708. clearPing();
  709. if(toggled)
  710. expandElement(element);
  711. else
  712. collapseElement(element);
  713. }
  714. void GUITreeView::onEditAccepted()
  715. {
  716. TreeElement* elem = mEditElement;
  717. disableEdit(true);
  718. selectElement(elem);
  719. }
  720. void GUITreeView::onEditCanceled()
  721. {
  722. if (mEditElement != nullptr)
  723. {
  724. TreeElement* elem = mEditElement;
  725. disableEdit(false);
  726. selectElement(elem);
  727. }
  728. }
  729. void GUITreeView::onEditFocusLost()
  730. {
  731. if (mEditElement != nullptr)
  732. disableEdit(false);
  733. }
  734. void GUITreeView::enableEdit(TreeElement* element)
  735. {
  736. assert(mEditElement == nullptr);
  737. mEditElement = element;
  738. mNameEditBox->setVisible(true);
  739. mNameEditBox->setText(toWString(element->mName));
  740. mNameEditBox->setFocus(true);
  741. if(element->mElement != nullptr)
  742. element->mElement->setVisible(false);
  743. }
  744. void GUITreeView::disableEdit(bool applyChanges)
  745. {
  746. assert(mEditElement != nullptr);
  747. if(mEditElement->mElement != nullptr)
  748. mEditElement->mElement->setVisible(true);
  749. if(applyChanges)
  750. {
  751. WString newName = mNameEditBox->getText();
  752. renameTreeElement(mEditElement, newName);
  753. }
  754. mNameEditBox->setFocus(false);
  755. mNameEditBox->setVisible(false);
  756. mEditElement = nullptr;
  757. }
  758. Vector2I GUITreeView::_getOptimalSize() const
  759. {
  760. struct UpdateTreeElement
  761. {
  762. UpdateTreeElement(const TreeElement* element, UINT32 indent)
  763. :element(element), indent(indent)
  764. { }
  765. const TreeElement* element;
  766. UINT32 indent;
  767. };
  768. Vector2I optimalSize;
  769. if (_getDimensions().fixedWidth() && _getDimensions().fixedHeight())
  770. {
  771. optimalSize.x = _getDimensions().minWidth;
  772. optimalSize.y = _getDimensions().minHeight;
  773. }
  774. else
  775. {
  776. Stack<UpdateTreeElement> todo;
  777. todo.push(UpdateTreeElement(&getRootElementConst(), 0));
  778. while(!todo.empty())
  779. {
  780. UpdateTreeElement currentUpdateElement = todo.top();
  781. const TreeElement* current = currentUpdateElement.element;
  782. todo.pop();
  783. INT32 yOffset = 0;
  784. if(current->mElement != nullptr)
  785. {
  786. Vector2I curOptimalSize = current->mElement->_getOptimalSize();
  787. optimalSize.x = std::max(optimalSize.x,
  788. (INT32)(INITIAL_INDENT_OFFSET + curOptimalSize.x + currentUpdateElement.indent * INDENT_SIZE));
  789. yOffset = curOptimalSize.y + ELEMENT_EXTRA_SPACING;
  790. }
  791. optimalSize.y += yOffset;
  792. for(auto& child : current->mChildren)
  793. {
  794. if(!child->mIsVisible)
  795. continue;
  796. todo.push(UpdateTreeElement(child, currentUpdateElement.indent + 1));
  797. }
  798. }
  799. if(_getDimensions().fixedWidth())
  800. optimalSize.x = _getDimensions().minWidth;
  801. else
  802. {
  803. if(_getDimensions().minWidth > 0)
  804. optimalSize.x = std::max((INT32)_getDimensions().minWidth, optimalSize.x);
  805. if(_getDimensions().maxWidth > 0)
  806. optimalSize.x = std::min((INT32)_getDimensions().maxWidth, optimalSize.x);
  807. }
  808. if (_getDimensions().fixedHeight())
  809. optimalSize.y = _getDimensions().minHeight;
  810. else
  811. {
  812. if(_getDimensions().minHeight > 0)
  813. optimalSize.y = std::max((INT32)_getDimensions().minHeight, optimalSize.y);
  814. if(_getDimensions().maxHeight > 0)
  815. optimalSize.y = std::min((INT32)_getDimensions().maxHeight, optimalSize.y);
  816. }
  817. }
  818. return optimalSize;
  819. }
  820. void GUITreeView::updateClippedBounds()
  821. {
  822. mClippedBounds = mLayoutData.area;
  823. mClippedBounds.clip(mLayoutData.clipRect);
  824. }
  825. void GUITreeView::_updateLayoutInternal(const GUILayoutData& data)
  826. {
  827. struct UpdateTreeElement
  828. {
  829. UpdateTreeElement(TreeElement* element, UINT32 indent)
  830. :element(element), indent(indent)
  831. { }
  832. TreeElement* element;
  833. UINT32 indent;
  834. };
  835. mVisibleElements.clear();
  836. Stack<UpdateTreeElement> todo;
  837. todo.push(UpdateTreeElement(&getRootElement(), 0));
  838. // NOTE - Instead of iterating through all elements, try to find those within the clip rect
  839. // and only iterate through those. Others should somehow be marked in-active (similar to GUIElement::isDisabled()?)
  840. Vector<TreeElement*> tempOrderedElements;
  841. Vector2I offset(data.area.x, data.area.y);
  842. while(!todo.empty())
  843. {
  844. UpdateTreeElement currentUpdateElement = todo.top();
  845. TreeElement* current = currentUpdateElement.element;
  846. UINT32 indent = currentUpdateElement.indent;
  847. todo.pop();
  848. INT32 btnHeight = 0;
  849. INT32 yOffset = 0;
  850. if(current->mElement != nullptr)
  851. {
  852. Vector2I elementSize = current->mElement->_getOptimalSize();
  853. btnHeight = elementSize.y;
  854. mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 0, Rect2I(data.area.x, offset.y, data.area.width, ELEMENT_EXTRA_SPACING)));
  855. mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 1, Rect2I(data.area.x, offset.y + ELEMENT_EXTRA_SPACING, data.area.width, btnHeight)));
  856. offset.x = data.area.x + INITIAL_INDENT_OFFSET + indent * INDENT_SIZE;
  857. offset.y += ELEMENT_EXTRA_SPACING;
  858. GUILayoutData childData = data;
  859. childData.area.x = offset.x;
  860. childData.area.y = offset.y;
  861. childData.area.width = elementSize.x;
  862. childData.area.height = elementSize.y;
  863. current->mElement->_setLayoutData(childData);
  864. yOffset = btnHeight;
  865. }
  866. if(current->mFoldoutBtn != nullptr)
  867. {
  868. Vector2I elementSize = current->mFoldoutBtn->_getOptimalSize();
  869. offset.x -= std::min((INT32)INITIAL_INDENT_OFFSET, elementSize.x + 2);
  870. Vector2I myOffset = offset;
  871. myOffset.y += 1;
  872. if(elementSize.y > btnHeight)
  873. {
  874. UINT32 diff = elementSize.y - btnHeight;
  875. float half = diff * 0.5f;
  876. myOffset.y -= Math::floorToInt(half);
  877. }
  878. GUILayoutData childData = data;
  879. childData.area.x = myOffset.x;
  880. childData.area.y = myOffset.y;
  881. childData.area.width = elementSize.x;
  882. childData.area.height = elementSize.y;
  883. current->mFoldoutBtn->_setLayoutData(childData);
  884. }
  885. offset.y += yOffset;
  886. tempOrderedElements.resize(current->mChildren.size(), nullptr);
  887. for(auto& child : current->mChildren)
  888. {
  889. tempOrderedElements[child->mSortedIdx] = child;
  890. }
  891. for(auto iter = tempOrderedElements.rbegin(); iter != tempOrderedElements.rend(); ++iter)
  892. {
  893. TreeElement* child = *iter;
  894. if(!child->mIsVisible)
  895. continue;
  896. todo.push(UpdateTreeElement(child, indent + 1));
  897. }
  898. }
  899. UINT32 remainingHeight = (UINT32)std::max(0, (INT32)data.area.height - (offset.y - data.area.y));
  900. if(remainingHeight > 0)
  901. mVisibleElements.push_back(InteractableElement(&getRootElement(), (UINT32)getRootElement().mChildren.size() * 2, Rect2I(data.area.x, offset.y, data.area.width, remainingHeight)));
  902. for(auto selectedElem : mSelectedElements)
  903. {
  904. GUILabel* targetElement = selectedElem.element->mElement;
  905. GUILayoutData childData = data;
  906. childData.area.y = targetElement->_getLayoutData().area.y;
  907. childData.area.height = targetElement->_getLayoutData().area.height;
  908. selectedElem.background->_setLayoutData(childData);
  909. }
  910. if (mIsElementHighlighted)
  911. {
  912. GUILabel* targetElement = mHighlightedElement.element->mElement;
  913. GUILayoutData childData = data;
  914. childData.area.y = targetElement->_getLayoutData().area.y;
  915. childData.area.height = targetElement->_getLayoutData().area.height;
  916. mHighlightedElement.background->_setLayoutData(childData);
  917. }
  918. if(mEditElement != nullptr)
  919. {
  920. GUILabel* targetElement = mEditElement->mElement;
  921. UINT32 remainingWidth = (UINT32)std::max(0, (((INT32)data.area.width) - (offset.x - data.area.x)));
  922. GUILayoutData childData = data;
  923. childData.area = targetElement->_getLayoutData().area;
  924. childData.area.width = remainingWidth;
  925. mNameEditBox->_setLayoutData(childData);
  926. }
  927. if(mDragInProgress)
  928. {
  929. const InteractableElement* interactableElement = findElementUnderCoord(mDragPosition);
  930. if(interactableElement == nullptr)
  931. {
  932. mDragHighlight->setVisible(false);
  933. mDragSepHighlight->setVisible(false);
  934. }
  935. else
  936. {
  937. if(interactableElement->isTreeElement())
  938. {
  939. mDragSepHighlight->setVisible(false);
  940. mDragHighlight->setVisible(true);
  941. GUILayoutData childData = data;
  942. childData.area = interactableElement->bounds;
  943. mDragHighlight->_setLayoutData(childData);
  944. }
  945. else
  946. {
  947. mDragHighlight->setVisible(false);
  948. mDragSepHighlight->setVisible(true);
  949. GUILayoutData childData = data;
  950. childData.area = interactableElement->bounds;
  951. mDragSepHighlight->_setLayoutData(childData);
  952. }
  953. }
  954. }
  955. else
  956. {
  957. mDragHighlight->setVisible(false);
  958. mDragSepHighlight->setVisible(false);
  959. }
  960. // Update scroll bounds
  961. UINT32 scrollHeight = (UINT32)Math::roundToInt(data.clipRect.height * SCROLL_AREA_HEIGHT_PCT);
  962. mTopScrollBounds.x = data.clipRect.x;
  963. mTopScrollBounds.y = data.clipRect.y;
  964. mTopScrollBounds.width = data.clipRect.width;
  965. mTopScrollBounds.height = scrollHeight;
  966. mBottomScrollBounds.x = data.clipRect.x;
  967. mBottomScrollBounds.y = data.clipRect.y + data.clipRect.height - scrollHeight;
  968. mBottomScrollBounds.width = data.clipRect.width;
  969. mBottomScrollBounds.height = scrollHeight;
  970. }
  971. const GUITreeView::InteractableElement* GUITreeView::findElementUnderCoord(const Vector2I& coord) const
  972. {
  973. for(auto& element : mVisibleElements)
  974. {
  975. if(element.bounds.contains(coord))
  976. {
  977. return &element;
  978. }
  979. }
  980. return nullptr;
  981. }
  982. GUITreeView::TreeElement* GUITreeView::getTopMostSelectedElement() const
  983. {
  984. auto topMostElement = mVisibleElements.end();
  985. for(auto& selectedElement : mSelectedElements)
  986. {
  987. auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  988. [&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
  989. if(iterFind != mVisibleElements.end())
  990. {
  991. if(topMostElement == mVisibleElements.end())
  992. topMostElement = iterFind;
  993. else
  994. {
  995. if(iterFind->bounds.y < topMostElement->bounds.y)
  996. topMostElement = iterFind;
  997. }
  998. }
  999. }
  1000. if(topMostElement != mVisibleElements.end())
  1001. return topMostElement->getTreeElement();
  1002. else
  1003. return nullptr;
  1004. }
  1005. GUITreeView::TreeElement* GUITreeView::getBottomMostSelectedElement() const
  1006. {
  1007. auto& botMostElement = mVisibleElements.end();
  1008. for(auto& selectedElement : mSelectedElements)
  1009. {
  1010. auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
  1011. [&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
  1012. if(iterFind != mVisibleElements.end())
  1013. {
  1014. if(botMostElement == mVisibleElements.end())
  1015. botMostElement = iterFind;
  1016. else
  1017. {
  1018. if((iterFind->bounds.y + iterFind->bounds.height) > (botMostElement->bounds.y + botMostElement->bounds.height))
  1019. botMostElement = iterFind;
  1020. }
  1021. }
  1022. }
  1023. if(botMostElement != mVisibleElements.end())
  1024. return botMostElement->getTreeElement();
  1025. else
  1026. return nullptr;
  1027. }
  1028. void GUITreeView::closeTemporarilyExpandedElements()
  1029. {
  1030. temporarilyExpandElement(nullptr);
  1031. }
  1032. void GUITreeView::temporarilyExpandElement(const GUITreeView::InteractableElement* mouseOverElement)
  1033. {
  1034. TreeElement* treeElement = nullptr;
  1035. if(mouseOverElement != nullptr && mouseOverElement->isTreeElement())
  1036. treeElement = mouseOverElement->getTreeElement();
  1037. if(treeElement == nullptr || treeElement != mMouseOverDragElement)
  1038. {
  1039. while(!mAutoExpandedElements.empty())
  1040. {
  1041. TreeElement* autoExpandedElement = mAutoExpandedElements.top();
  1042. bool unexpandElement = false;
  1043. if(mouseOverElement != nullptr && mouseOverElement->parent != nullptr)
  1044. {
  1045. if(mouseOverElement->parent != autoExpandedElement && !mouseOverElement->parent->isParentRec(autoExpandedElement))
  1046. unexpandElement = true;
  1047. else
  1048. break;
  1049. }
  1050. else
  1051. unexpandElement = true;
  1052. if(unexpandElement)
  1053. {
  1054. collapseElement(autoExpandedElement);
  1055. if(autoExpandedElement->mFoldoutBtn != nullptr)
  1056. autoExpandedElement->mFoldoutBtn->toggleOff();
  1057. mAutoExpandedElements.pop();
  1058. }
  1059. }
  1060. mMouseOverDragElement = treeElement;
  1061. mMouseOverDragElementTime = gTime().getTime();
  1062. }
  1063. else
  1064. {
  1065. if(mMouseOverDragElement != nullptr && !mMouseOverDragElement->mIsExpanded)
  1066. {
  1067. float timeDiff = gTime().getTime() - mMouseOverDragElementTime;
  1068. if(timeDiff >= AUTO_EXPAND_DELAY_SEC)
  1069. {
  1070. mAutoExpandedElements.push(mMouseOverDragElement);
  1071. expandElement(mMouseOverDragElement);
  1072. if(mMouseOverDragElement->mFoldoutBtn != nullptr)
  1073. mMouseOverDragElement->mFoldoutBtn->toggleOn();
  1074. }
  1075. }
  1076. }
  1077. }
  1078. void GUITreeView::scrollToElement(TreeElement* element, bool center)
  1079. {
  1080. if(element->mElement == nullptr)
  1081. return;
  1082. GUIScrollArea* scrollArea = findParentScrollArea();
  1083. if(scrollArea == nullptr)
  1084. return;
  1085. if(center)
  1086. {
  1087. Rect2I myBounds = _getClippedBounds();
  1088. INT32 clipVertCenter = myBounds.y + (INT32)Math::roundToInt(myBounds.height * 0.5f);
  1089. INT32 elemVertCenter = element->mElement->_getLayoutData().area.y + (INT32)Math::roundToInt(element->mElement->_getLayoutData().area.height * 0.5f);
  1090. if(elemVertCenter > clipVertCenter)
  1091. scrollArea->scrollDownPx(elemVertCenter - clipVertCenter);
  1092. else
  1093. scrollArea->scrollUpPx(clipVertCenter - elemVertCenter);
  1094. }
  1095. else
  1096. {
  1097. Rect2I myBounds = _getClippedBounds();
  1098. INT32 elemVertTop = element->mElement->_getLayoutData().area.y;
  1099. INT32 elemVertBottom = element->mElement->_getLayoutData().area.y + element->mElement->_getLayoutData().area.height;
  1100. INT32 top = myBounds.y;
  1101. INT32 bottom = myBounds.y + myBounds.height;
  1102. INT32 offset = 0;
  1103. if(elemVertTop < top)
  1104. scrollArea->scrollUpPx(top - elemVertTop);
  1105. else if(elemVertBottom > bottom)
  1106. scrollArea->scrollDownPx(elemVertBottom - bottom);
  1107. }
  1108. }
  1109. GUIScrollArea* GUITreeView::findParentScrollArea() const
  1110. {
  1111. GUIElementBase* parent = _getParent();
  1112. while(parent != nullptr)
  1113. {
  1114. if(parent->_getType() == GUIElementBase::Type::Element)
  1115. {
  1116. GUIElement* parentElement = static_cast<GUIElement*>(parent);
  1117. if(parentElement->_getElementType() == GUIElement::ElementType::ScrollArea)
  1118. {
  1119. GUIScrollArea* scrollArea = static_cast<GUIScrollArea*>(parentElement);
  1120. return scrollArea;
  1121. }
  1122. }
  1123. parent = parent->_getParent();
  1124. }
  1125. return nullptr;
  1126. }
  1127. const String& GUITreeView::getGUITypeName()
  1128. {
  1129. static String typeName = "SceneTreeView";
  1130. return typeName;
  1131. }
  1132. }