BsDockManager.cpp 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073
  1. #include "BsDockManager.h"
  2. #include "BsEditorWidgetContainer.h"
  3. #include "BsEditorWidget.h"
  4. #include "BsEditorWidgetManager.h"
  5. #include "BsMath.h"
  6. #include "BsException.h"
  7. #include "BsMesh.h"
  8. #include "BsMaterial.h"
  9. #include "BsVector2.h"
  10. #include "BsDrawList.h"
  11. #include "BsCoreApplication.h"
  12. #include "BsRendererManager.h"
  13. #include "BsCoreRenderer.h"
  14. #include "BsSceneObject.h"
  15. #include "BsGUIManager.h"
  16. #include "BsBuiltinEditorResources.h"
  17. #include "BsGUIWidget.h"
  18. #include "BsCamera.h"
  19. #include "BsDragAndDropManager.h"
  20. #include "BsGUIDockSlider.h"
  21. #include "BsVertexDataDesc.h"
  22. #include "BsGUISkin.h"
  23. #include "BsBuiltinResources.h"
  24. #include "BsDockManagerLayout.h"
  25. #include "BsEditorWindow.h"
  26. #include "BsGUIPanel.h"
  27. #include "BsGUISkin.h"
  28. #include "BsGUIButton.h"
  29. using namespace std::placeholders;
  30. namespace BansheeEngine
  31. {
  32. const UINT32 DockManager::DockContainer::SLIDER_SIZE = 4;
  33. const UINT32 DockManager::DockContainer::MIN_CHILD_SIZE = 20;
  34. const Color DockManager::TINT_COLOR = Color(0.44f, 0.44f, 0.44f, 0.22f);
  35. const Color DockManager::HIGHLIGHT_COLOR = Color(0.44f, 0.44f, 0.44f, 0.42f);
  36. DockManager::DockContainer::DockContainer(DockManager* manager)
  37. :mIsLeaf(true), mWidgets(nullptr), mSplitPosition(0.5f),
  38. mIsHorizontal(false), mParent(nullptr), mSlider(nullptr), mManager(manager)
  39. {
  40. mChildren[0] = nullptr;
  41. mChildren[1] = nullptr;
  42. }
  43. DockManager::DockContainer::DockContainer(DockManager* manager, DockContainer* parent)
  44. :mIsLeaf(false), mWidgets(nullptr), mSplitPosition(0.5f),
  45. mIsHorizontal(false), mParent(parent), mSlider(nullptr), mManager(manager)
  46. {
  47. mChildren[0] = nullptr;
  48. mChildren[1] = nullptr;
  49. }
  50. DockManager::DockContainer::~DockContainer()
  51. {
  52. if (mIsLeaf)
  53. {
  54. if (mWidgets != nullptr)
  55. bs_delete(mWidgets);
  56. if (mGUIWidgetSO != nullptr)
  57. mGUIWidgetSO->destroy();
  58. }
  59. if(!mIsLeaf)
  60. {
  61. if(mChildren[0] != nullptr)
  62. bs_delete(mChildren[0]);
  63. if(mChildren[1] != nullptr)
  64. bs_delete(mChildren[1]);
  65. }
  66. if(mSlider != nullptr)
  67. {
  68. GUIElement::destroy(mSlider);
  69. mSlider = nullptr;
  70. }
  71. }
  72. void DockManager::DockContainer::setArea(INT32 x, INT32 y, UINT32 width, UINT32 height)
  73. {
  74. if(mIsLeaf)
  75. {
  76. if(mWidgets != nullptr)
  77. {
  78. mWidgets->setPosition(x, y);
  79. mWidgets->setSize(width, height);
  80. }
  81. }
  82. mArea.x = x;
  83. mArea.y = y;
  84. mArea.width = width;
  85. mArea.height = height;
  86. updateChildAreas();
  87. }
  88. void DockManager::DockContainer::updateChildAreas()
  89. {
  90. if(!mIsLeaf && mChildren[0] != nullptr && mChildren[1] != nullptr)
  91. {
  92. if(mIsHorizontal)
  93. {
  94. UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.height - (INT32)SLIDER_SIZE);
  95. UINT32 sizeTop = Math::floorToInt(remainingSize * mSplitPosition);
  96. UINT32 sizeBottom = remainingSize - sizeTop;
  97. mChildren[0]->setArea(mArea.x, mArea.y, mArea.width, sizeTop);
  98. mChildren[1]->setArea(mArea.x, mArea.y + sizeTop + SLIDER_SIZE, mArea.width, sizeBottom);
  99. mSlider->setWidth(mArea.width);
  100. mSlider->setHeight(SLIDER_SIZE);
  101. mSlider->setPosition(mArea.x, mArea.y + sizeTop);
  102. }
  103. else
  104. {
  105. UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.width - (INT32)SLIDER_SIZE);
  106. UINT32 sizeLeft = Math::floorToInt(remainingSize * mSplitPosition);
  107. UINT32 sizeRight = remainingSize - sizeLeft;
  108. mChildren[0]->setArea(mArea.x, mArea.y, sizeLeft, mArea.height);
  109. mChildren[1]->setArea(mArea.x + sizeLeft + SLIDER_SIZE, mArea.y, sizeRight, mArea.height);
  110. mSlider->setWidth(SLIDER_SIZE);
  111. mSlider->setHeight(mArea.height);
  112. mSlider->setPosition(mArea.x + sizeLeft, mArea.y);
  113. }
  114. }
  115. }
  116. void DockManager::DockContainer::makeLeaf(EditorWindowBase* parentWindow)
  117. {
  118. mGUIWidgetSO = SceneObject::create("DockContainer", SOF_Internal | SOF_Persistent | SOF_DontSave);
  119. HGUIWidget guiWidget = mGUIWidgetSO->addComponent<GUIWidget>(parentWindow->getGUICamera()->getViewport().get());
  120. guiWidget->setDepth(128);
  121. guiWidget->setSkin(BuiltinEditorResources::instance().getSkin());
  122. mIsLeaf = true;
  123. mWidgets = bs_new<EditorWidgetContainer>(guiWidget.get(), parentWindow);
  124. mWidgets->onWidgetClosed.connect(std::bind(&DockManager::DockContainer::widgetRemoved, this));
  125. if(mSlider != nullptr)
  126. {
  127. GUIElement::destroy(mSlider);
  128. mSlider = nullptr;
  129. }
  130. mWidgets->setPosition(mArea.x, mArea.y);
  131. mWidgets->setSize(mArea.width, mArea.height);
  132. }
  133. void DockManager::DockContainer::makeLeaf(const HSceneObject& guiWidgetSO, EditorWidgetContainer* existingContainer)
  134. {
  135. mIsLeaf = true;
  136. mWidgets = existingContainer;
  137. mGUIWidgetSO = guiWidgetSO;
  138. mWidgets->onWidgetClosed.connect(std::bind(&DockManager::DockContainer::widgetRemoved, this));
  139. if(mSlider != nullptr)
  140. {
  141. GUIElement::destroy(mSlider);
  142. mSlider = nullptr;
  143. }
  144. mWidgets->setPosition(mArea.x, mArea.y);
  145. mWidgets->setSize(mArea.width, mArea.height);
  146. }
  147. void DockManager::DockContainer::addLeft(EditorWidgetBase* widget)
  148. {
  149. if(mIsLeaf)
  150. splitContainer(false, true);
  151. mChildren[0]->addWidget(widget);
  152. }
  153. void DockManager::DockContainer::addRight(EditorWidgetBase* widget)
  154. {
  155. if(mIsLeaf)
  156. splitContainer(false, false);
  157. mChildren[1]->addWidget(widget);
  158. }
  159. void DockManager::DockContainer::addTop(EditorWidgetBase* widget)
  160. {
  161. if(mIsLeaf)
  162. splitContainer(true, true);
  163. mChildren[0]->addWidget(widget);
  164. }
  165. void DockManager::DockContainer::addBottom(EditorWidgetBase* widget)
  166. {
  167. if(mIsLeaf)
  168. splitContainer(true, false);
  169. mChildren[1]->addWidget(widget);
  170. }
  171. void DockManager::DockContainer::splitContainer(bool horizontal, bool newChildIsFirst, float splitPosition)
  172. {
  173. DockContainer* children[2];
  174. UINT32 idxA = newChildIsFirst ? 0 : 1;
  175. UINT32 idxB = (idxA + 1) % 2;
  176. children[idxA] = bs_new<DockContainer>(mManager, this);
  177. children[idxB] = bs_new<DockContainer>(mManager, this);
  178. mWidgets->onWidgetClosed.clear();
  179. children[idxA]->makeLeaf(mManager->mParentWindow);
  180. children[idxB]->makeLeaf(mGUIWidgetSO, mWidgets);
  181. mWidgets = nullptr;
  182. mGUIWidgetSO = nullptr;
  183. makeSplit(children[0], children[1], horizontal, splitPosition);
  184. }
  185. void DockManager::DockContainer::makeSplit(DockManager::DockContainer* first, DockManager::DockContainer* second, bool horizontal, float splitPosition)
  186. {
  187. mChildren[0] = first;
  188. mChildren[1] = second;
  189. mIsLeaf = false;
  190. mIsHorizontal = horizontal;
  191. mSplitPosition = splitPosition;
  192. if (mWidgets != nullptr)
  193. {
  194. bs_delete(mWidgets);
  195. mWidgets = nullptr;
  196. }
  197. if (mGUIWidgetSO != nullptr)
  198. {
  199. mGUIWidgetSO->destroy();
  200. mGUIWidgetSO = nullptr;
  201. }
  202. if (mSlider != nullptr)
  203. {
  204. GUIElement::destroy(mSlider);
  205. mSlider = nullptr;
  206. }
  207. mSlider = GUIDockSlider::create(horizontal, "DockSliderBtn");
  208. mManager->_getParentWidget()->getPanel()->addElement(mSlider);
  209. mSlider->onDragged.connect(std::bind(&DockManager::DockContainer::sliderDragged, this, _1));
  210. setArea(mArea.x, mArea.y, mArea.width, mArea.height);
  211. }
  212. void DockManager::DockContainer::addWidget(EditorWidgetBase* widget)
  213. {
  214. if(!mIsLeaf)
  215. return;
  216. mWidgets->add(*widget);
  217. }
  218. void DockManager::DockContainer::addWidget(const String& name)
  219. {
  220. if(!mIsLeaf)
  221. return;
  222. EditorWidgetManager::instance().create(name, *mWidgets);
  223. }
  224. void DockManager::DockContainer::sliderDragged(const Vector2I& delta)
  225. {
  226. if(mIsHorizontal && delta.y != 0)
  227. {
  228. UINT32 maxSize = (UINT32)std::max(MIN_CHILD_SIZE, (INT32)mArea.height - (INT32)SLIDER_SIZE - MIN_CHILD_SIZE);
  229. UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.height - (INT32)SLIDER_SIZE);
  230. mSplitPosition = Math::clamp((UINT32)Math::floorToInt(remainingSize * mSplitPosition) + delta.y, MIN_CHILD_SIZE, maxSize) / (float)remainingSize;
  231. updateChildAreas();
  232. }
  233. else if(!mIsHorizontal && delta.x != 0)
  234. {
  235. UINT32 maxSize = (UINT32)std::max(MIN_CHILD_SIZE, (INT32)mArea.width - (INT32)SLIDER_SIZE - MIN_CHILD_SIZE);
  236. UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.width - (INT32)SLIDER_SIZE);
  237. mSplitPosition = Math::clamp((UINT32)Math::floorToInt(remainingSize * mSplitPosition) + delta.x, MIN_CHILD_SIZE, maxSize) / (float)remainingSize;
  238. updateChildAreas();
  239. }
  240. }
  241. void DockManager::DockContainer::widgetRemoved()
  242. {
  243. assert(mIsLeaf);
  244. if(mWidgets->getNumWidgets() == 0)
  245. {
  246. if(mParent == nullptr) // We're root so we just reset ourselves, can't delete root
  247. {
  248. bs_delete(mWidgets);
  249. mWidgets = nullptr;
  250. mGUIWidgetSO->destroy();
  251. mGUIWidgetSO = nullptr;
  252. mIsLeaf = true;
  253. mSplitPosition = 0.5f;
  254. mIsHorizontal = false;
  255. }
  256. else
  257. {
  258. // Replace our parent with our sibling
  259. DockContainer* sibling = nullptr;
  260. if(mParent->mChildren[0] == this)
  261. sibling = mParent->mChildren[1];
  262. else
  263. sibling = mParent->mChildren[0];
  264. if (sibling->mIsLeaf)
  265. {
  266. sibling->mWidgets->onWidgetClosed.clear();
  267. mParent->makeLeaf(sibling->mGUIWidgetSO, sibling->mWidgets);
  268. sibling->mWidgets = nullptr;
  269. sibling->mGUIWidgetSO = nullptr;
  270. }
  271. else
  272. {
  273. mParent->makeSplit(sibling->mChildren[0], sibling->mChildren[1], sibling->mIsHorizontal, sibling->mSplitPosition);
  274. sibling->mChildren[0]->mParent = mParent;
  275. sibling->mChildren[1]->mParent = mParent;
  276. sibling->mChildren[0] = nullptr;
  277. sibling->mChildren[1] = nullptr;
  278. }
  279. bs_delete(sibling);
  280. bs_delete(this);
  281. }
  282. }
  283. }
  284. DockManager::DockContainer* DockManager::DockContainer::find(EditorWidgetContainer* widgetContainer)
  285. {
  286. if(mIsLeaf)
  287. {
  288. if(mWidgets == widgetContainer)
  289. return this;
  290. else
  291. return nullptr;
  292. }
  293. else
  294. {
  295. if(mChildren[0] != nullptr)
  296. {
  297. DockContainer* foundContainer = mChildren[0]->find(widgetContainer);
  298. if (foundContainer != nullptr)
  299. return foundContainer;
  300. }
  301. if(mChildren[1] != nullptr && mChildren[1]->find(widgetContainer) != nullptr)
  302. {
  303. DockContainer* foundContainer = mChildren[1]->find(widgetContainer);
  304. if (foundContainer != nullptr)
  305. return foundContainer;
  306. }
  307. }
  308. return nullptr;
  309. }
  310. DockManager::DockContainer* DockManager::DockContainer::findAtPos(const Vector2I& pos)
  311. {
  312. if(mIsLeaf)
  313. {
  314. if(mArea.contains(pos))
  315. {
  316. return this;
  317. }
  318. }
  319. else
  320. {
  321. if (mChildren[0] != nullptr)
  322. {
  323. DockContainer* foundContainer = mChildren[0]->findAtPos(pos);
  324. if (foundContainer != nullptr)
  325. return foundContainer;
  326. }
  327. if(mChildren[1] != nullptr)
  328. {
  329. DockContainer* foundContainer = mChildren[1]->findAtPos(pos);
  330. if (foundContainer != nullptr)
  331. return foundContainer;
  332. }
  333. }
  334. return nullptr;
  335. }
  336. Rect2I DockManager::DockContainer::getContentBounds() const
  337. {
  338. if(!mIsLeaf || mWidgets == nullptr)
  339. return mArea;
  340. return mWidgets->getContentBounds();
  341. }
  342. void DockManager::DockContainer::update()
  343. {
  344. if (mIsLeaf)
  345. {
  346. if (mWidgets != nullptr)
  347. mWidgets->update();
  348. }
  349. else
  350. {
  351. if (mChildren[0] != nullptr)
  352. mChildren[0]->update();
  353. if (mChildren[1] != nullptr)
  354. mChildren[1]->update();
  355. }
  356. }
  357. DockManager::DockManager(EditorWindowBase* parentWindow, const GUIDimensions& dimensions)
  358. :GUIElementContainer(dimensions), mMouseOverContainer(nullptr), mHighlightedDropLoc(DockLocation::None),
  359. mShowOverlay(false), mRootContainer(this), mParentWindow(parentWindow)
  360. {
  361. mTopDropPolygon = bs_newN<Vector2>(4);
  362. mBotDropPolygon = bs_newN<Vector2>(4);
  363. mLeftDropPolygon = bs_newN<Vector2>(4);
  364. mRightDropPolygon = bs_newN<Vector2>(4);
  365. mDropOverlayMat = BuiltinEditorResources::instance().createDockDropOverlayMaterial();
  366. mRenderCallback = RendererManager::instance().getActive()->onRenderViewport.connect(std::bind(&DockManager::render, this, _1, _2));
  367. }
  368. DockManager::~DockManager()
  369. {
  370. mRenderCallback.disconnect();
  371. bs_deleteN(mTopDropPolygon, 4);
  372. bs_deleteN(mBotDropPolygon, 4);
  373. bs_deleteN(mLeftDropPolygon, 4);
  374. bs_deleteN(mRightDropPolygon, 4);
  375. }
  376. DockManager* DockManager::create(EditorWindowBase* parentWindow)
  377. {
  378. return new (bs_alloc<DockManager>()) DockManager(parentWindow, GUIDimensions::create());
  379. }
  380. void DockManager::update()
  381. {
  382. if(!DragAndDropManager::instance().isDragInProgress())
  383. {
  384. mHighlightedDropLoc = DockLocation::None;
  385. mShowOverlay = false;
  386. }
  387. mRootContainer.update();
  388. }
  389. void DockManager::render(const Viewport* viewport, DrawList& drawList)
  390. {
  391. if (_getParentWidget() == nullptr || _getParentWidget()->getTarget() != viewport)
  392. return;
  393. if(!mShowOverlay)
  394. return;
  395. float invViewportWidth = 1.0f / (viewport->getWidth() * 0.5f);
  396. float invViewportHeight = 1.0f / (viewport->getHeight() * 0.5f);
  397. if(!mDropOverlayMesh.isLoaded())
  398. return;
  399. if(!mDropOverlayMat.isLoaded())
  400. return;
  401. mDropOverlayMat->setFloat("invViewportWidth", invViewportWidth);
  402. mDropOverlayMat->setFloat("invViewportHeight", invViewportHeight);
  403. mDropOverlayMat->setColor("tintColor", TINT_COLOR);
  404. mDropOverlayMat->setColor("highlightColor", HIGHLIGHT_COLOR);
  405. Color highlightColor;
  406. switch(mHighlightedDropLoc)
  407. {
  408. case DockLocation::Top:
  409. highlightColor = Color(1.0f, 0.0f, 0.0f, 0.0f);
  410. break;
  411. case DockLocation::Bottom:
  412. highlightColor = Color(0.0f, 1.0f, 0.0f, 0.0f);
  413. break;
  414. case DockLocation::Left:
  415. highlightColor = Color(0.0f, 0.0f, 1.0f, 0.0f);
  416. break;
  417. case DockLocation::Right:
  418. highlightColor = Color(0.0f, 0.0f, 0.0f, 1.0f);
  419. break;
  420. case DockLocation::None:
  421. highlightColor = Color(0.0f, 0.0f, 0.0f, 0.0f);
  422. break;
  423. }
  424. mDropOverlayMat->setColor("highlightActive", highlightColor);
  425. drawList.add(mDropOverlayMat.getInternalPtr(), mDropOverlayMesh.getInternalPtr(), 0, Vector3::ZERO);
  426. }
  427. void DockManager::insert(EditorWidgetContainer* relativeTo, EditorWidgetBase* widgetToInsert, DockLocation location)
  428. {
  429. if(relativeTo != nullptr)
  430. {
  431. DockContainer* container = mRootContainer.find(relativeTo);
  432. if(container == nullptr)
  433. BS_EXCEPT(InternalErrorException, "Cannot find the wanted widget container relative to which the widget should be inserted.");
  434. switch(location)
  435. {
  436. case DockLocation::Left:
  437. container->addLeft(widgetToInsert);
  438. break;
  439. case DockLocation::Right:
  440. container->addRight(widgetToInsert);
  441. break;
  442. case DockLocation::Top:
  443. container->addTop(widgetToInsert);
  444. break;
  445. case DockLocation::Bottom:
  446. container->addBottom(widgetToInsert);
  447. break;
  448. }
  449. }
  450. else
  451. {
  452. if(mRootContainer.mWidgets != nullptr)
  453. BS_EXCEPT(InternalErrorException, "Trying to insert a widget into dock manager root container but one already exists.");
  454. mRootContainer.makeLeaf(mParentWindow);
  455. mRootContainer.addWidget(widgetToInsert);
  456. }
  457. }
  458. void DockManager::setArea(INT32 x, INT32 y, UINT32 width, UINT32 height)
  459. {
  460. mRootContainer.setArea(x, y, width, height);
  461. mArea = Rect2I(x, y, width, height);
  462. updateDropOverlay(x, y, width, height);
  463. }
  464. void DockManager::closeAll()
  465. {
  466. mRootContainer = DockContainer(this);
  467. mMouseOverContainer = nullptr;
  468. }
  469. DockManagerLayoutPtr DockManager::getLayout() const
  470. {
  471. struct StackElem
  472. {
  473. StackElem(DockManagerLayout::Entry* layoutEntry, const DockContainer* container)
  474. :layoutEntry(layoutEntry), container(container)
  475. { }
  476. DockManagerLayout::Entry* layoutEntry;
  477. const DockContainer* container;
  478. };
  479. auto GetWidgetNamesInContainer = [&] (const DockContainer* container)
  480. {
  481. Vector<String> widgetNames;
  482. if(container->mWidgets != nullptr)
  483. {
  484. UINT32 numWidgets = container->mWidgets->getNumWidgets();
  485. for(UINT32 i = 0; i < numWidgets; i++)
  486. {
  487. EditorWidgetBase* widget = container->mWidgets->getWidget(i);
  488. widgetNames.push_back(widget->getName());
  489. }
  490. }
  491. return widgetNames;
  492. };
  493. DockManagerLayoutPtr layout = bs_shared_ptr_new<DockManagerLayout>();
  494. DockManagerLayout::Entry* rootEntry = &layout->getRootEntry();
  495. if(mRootContainer.mIsLeaf)
  496. {
  497. rootEntry->isLeaf = true;
  498. rootEntry->widgetNames = GetWidgetNamesInContainer(&mRootContainer);
  499. }
  500. else
  501. {
  502. rootEntry->isLeaf = false;
  503. rootEntry->horizontalSplit = mRootContainer.mIsHorizontal;
  504. rootEntry->splitPosition = mRootContainer.mSplitPosition;
  505. rootEntry->parent = nullptr;
  506. }
  507. Stack<StackElem> todo;
  508. todo.push(StackElem(rootEntry, &mRootContainer));
  509. while(!todo.empty())
  510. {
  511. StackElem currentElem = todo.top();
  512. todo.pop();
  513. if(!currentElem.container->mIsLeaf)
  514. {
  515. for(UINT32 i = 0; i < 2; i++)
  516. {
  517. if(currentElem.container->mChildren[i] == nullptr)
  518. continue;
  519. if(currentElem.container->mChildren[i]->mIsLeaf)
  520. {
  521. Vector<String> widgetNames = GetWidgetNamesInContainer(currentElem.container->mChildren[i]);
  522. currentElem.layoutEntry->children[i] =
  523. DockManagerLayout::Entry::createLeaf(currentElem.layoutEntry, i, widgetNames);
  524. }
  525. else
  526. {
  527. currentElem.layoutEntry->children[i] =
  528. DockManagerLayout::Entry::createContainer(currentElem.layoutEntry, i,
  529. currentElem.container->mChildren[i]->mSplitPosition,
  530. currentElem.container->mChildren[i]->mIsHorizontal);
  531. todo.push(StackElem(currentElem.layoutEntry->children[i], currentElem.container->mChildren[i]));
  532. }
  533. }
  534. }
  535. }
  536. return layout;
  537. }
  538. void DockManager::setLayout(const DockManagerLayoutPtr& layout)
  539. {
  540. // Undock all currently docked widgets
  541. Vector<EditorWidgetBase*> undockedWidgets;
  542. Stack<DockContainer*> todo;
  543. todo.push(&mRootContainer);
  544. while(!todo.empty())
  545. {
  546. DockContainer* current = todo.top();
  547. todo.pop();
  548. if(current->mIsLeaf)
  549. {
  550. if(current->mWidgets != nullptr)
  551. {
  552. while(current->mWidgets->getNumWidgets() > 0)
  553. {
  554. EditorWidgetBase* curWidget = current->mWidgets->getWidget(0);
  555. current->mWidgets->remove(*curWidget);
  556. undockedWidgets.push_back(curWidget);
  557. }
  558. }
  559. }
  560. else
  561. {
  562. todo.push(current->mChildren[0]);
  563. todo.push(current->mChildren[1]);
  564. }
  565. }
  566. mRootContainer = DockContainer(this);
  567. // Load layout
  568. struct StackEntry
  569. {
  570. StackEntry(const DockManagerLayout::Entry* layoutEntry, DockContainer* container)
  571. :layoutEntry(layoutEntry), container(container)
  572. { }
  573. const DockManagerLayout::Entry* layoutEntry;
  574. DockContainer* container;
  575. };
  576. auto GetLeafEntry = [] (const DockManagerLayout::Entry* parentEntry, UINT32 childIdx) -> const DockManagerLayout::Entry*
  577. {
  578. while(true)
  579. {
  580. if(parentEntry->isLeaf)
  581. return parentEntry;
  582. parentEntry = parentEntry->children[childIdx];
  583. }
  584. return nullptr;
  585. };
  586. auto OpenWidgets = [&] (DockContainer* parent, const Vector<String>& widgetNames)
  587. {
  588. for(auto& widgetName : widgetNames)
  589. {
  590. parent->addWidget(widgetName);
  591. }
  592. };
  593. // Prune layout by removing invalid leafs (ones with no widgets, or widgets that no longer exist)
  594. layout->pruneInvalidLeaves();
  595. // Dock elements
  596. const DockManagerLayout::Entry* rootEntry = &layout->getRootEntry();
  597. const DockManagerLayout::Entry* leafEntry = GetLeafEntry(rootEntry, 0);
  598. if(leafEntry->widgetNames.size() > 0) // If zero, entire layout is empty
  599. {
  600. mRootContainer.makeLeaf(mParentWindow);
  601. OpenWidgets(&mRootContainer, leafEntry->widgetNames);
  602. if(!rootEntry->isLeaf)
  603. {
  604. Stack<StackEntry> layoutTodo;
  605. layoutTodo.push(StackEntry(rootEntry, &mRootContainer));
  606. while(!layoutTodo.empty())
  607. {
  608. StackEntry curEntry = layoutTodo.top();
  609. layoutTodo.pop();
  610. leafEntry = GetLeafEntry(curEntry.layoutEntry->children[1], 0);
  611. curEntry.container->splitContainer(curEntry.layoutEntry->horizontalSplit, false, curEntry.layoutEntry->splitPosition);
  612. DockContainer* otherChild = curEntry.container->mChildren[1];
  613. OpenWidgets(otherChild, leafEntry->widgetNames);
  614. if(!curEntry.layoutEntry->children[0]->isLeaf)
  615. layoutTodo.push(StackEntry(curEntry.layoutEntry->children[0], curEntry.container->mChildren[0]));
  616. if(!curEntry.layoutEntry->children[1]->isLeaf)
  617. layoutTodo.push(StackEntry(curEntry.layoutEntry->children[1], curEntry.container->mChildren[1]));
  618. }
  619. }
  620. }
  621. // Set container sizes
  622. {
  623. Stack<StackEntry> layoutTodo;
  624. layoutTodo.push(StackEntry(rootEntry, &mRootContainer));
  625. while(!layoutTodo.empty())
  626. {
  627. StackEntry curEntry = layoutTodo.top();
  628. layoutTodo.pop();
  629. if(!curEntry.layoutEntry->isLeaf)
  630. {
  631. layoutTodo.push(StackEntry(curEntry.layoutEntry->children[0], curEntry.container->mChildren[0]));
  632. layoutTodo.push(StackEntry(curEntry.layoutEntry->children[1], curEntry.container->mChildren[1]));
  633. }
  634. }
  635. }
  636. // Destroy any widgets that are no longer docked anywhere
  637. for(auto& widget : undockedWidgets)
  638. {
  639. if(widget->_getParent() == nullptr)
  640. widget->close();
  641. }
  642. setArea(mArea.x, mArea.y, mArea.width, mArea.height);
  643. }
  644. void DockManager::updateClippedBounds()
  645. {
  646. // TODO - Clipping not actually accounted for but shouldn't matter as right now DockManager is only used in one specific situation
  647. mClippedBounds = mRootContainer.mArea;
  648. }
  649. void DockManager::updateDropOverlay(INT32 x, INT32 y, UINT32 width, UINT32 height)
  650. {
  651. const static int spacing = 10;
  652. const static float innerScale = 0.75f;
  653. UINT32 outWidth = std::max(0, (INT32)width - spacing * 2);
  654. UINT32 outHeight = std::max(0, (INT32)height - spacing * 2);
  655. UINT32 inWidth = (UINT32)Math::floorToInt(innerScale * outWidth);
  656. UINT32 inHeight = (UINT32)Math::floorToInt(innerScale * outHeight);
  657. INT32 inXOffset = Math::floorToInt((outWidth - inWidth) * 0.5f);
  658. INT32 inYOffset = Math::floorToInt((outHeight - inHeight) * 0.5f);
  659. Vector2 outTopLeft((float)x, (float)y);
  660. Vector2 outTopRight((float)(x + outWidth), (float)y);
  661. Vector2 outBotLeft((float)x, (float)(y + outHeight));
  662. Vector2 outBotRight((float)(x + outWidth), (float)(y + outHeight));
  663. Vector2 inTopLeft((float)(x + inXOffset), (float)(y + inYOffset));
  664. Vector2 inTopRight((float)(x + inXOffset + inWidth), (float)(y + inYOffset));
  665. Vector2 inBotLeft((float)(x + inXOffset), (float)(y + inYOffset + inHeight));
  666. Vector2 inBotRight((float)(x + inXOffset + inWidth), (float)(y + inYOffset + inHeight));
  667. VertexDataDescPtr vertexDesc = bs_shared_ptr_new<VertexDataDesc>();
  668. vertexDesc->addVertElem(VET_FLOAT2, VES_POSITION);
  669. vertexDesc->addVertElem(VET_COLOR, VES_COLOR);
  670. MeshDataPtr meshData = bs_shared_ptr_new<MeshData>(16, 24, vertexDesc);
  671. auto vertIter = meshData->getVec2DataIter(VES_POSITION);
  672. auto colIter = meshData->getDWORDDataIter(VES_COLOR);
  673. // Top
  674. Vector2 topOffset((float)spacing, 0.0f);
  675. mTopDropPolygon[0] = outTopLeft + topOffset;
  676. mTopDropPolygon[1] = outTopRight + topOffset;
  677. mTopDropPolygon[2] = inTopRight + topOffset;
  678. mTopDropPolygon[3] = inTopLeft + topOffset;
  679. vertIter.addValue(mTopDropPolygon[0]);
  680. vertIter.addValue(mTopDropPolygon[1]);
  681. vertIter.addValue(mTopDropPolygon[2]);
  682. vertIter.addValue(mTopDropPolygon[3]);
  683. Color color(1.0f, 0.0f, 0.0f, 0.0f);
  684. UINT32 color32 = color.getAsRGBA();
  685. colIter.addValue(color32);
  686. colIter.addValue(color32);
  687. colIter.addValue(color32);
  688. colIter.addValue(color32);
  689. // Bottom
  690. Vector2 botOffset((float)spacing, (float)spacing * 2.0f);
  691. mBotDropPolygon[0] = inBotLeft + botOffset;
  692. mBotDropPolygon[1] = inBotRight + botOffset;
  693. mBotDropPolygon[2] = outBotRight + botOffset;
  694. mBotDropPolygon[3] = outBotLeft + botOffset;
  695. vertIter.addValue(mBotDropPolygon[0]);
  696. vertIter.addValue(mBotDropPolygon[1]);
  697. vertIter.addValue(mBotDropPolygon[2]);
  698. vertIter.addValue(mBotDropPolygon[3]);
  699. color = Color(0.0f, 1.0f, 0.0f, 0.0f);
  700. color32 = color.getAsRGBA();
  701. colIter.addValue(color32);
  702. colIter.addValue(color32);
  703. colIter.addValue(color32);
  704. colIter.addValue(color32);
  705. // Left
  706. Vector2 leftOffset(0.0f, (float)spacing);
  707. mLeftDropPolygon[0] = outTopLeft + leftOffset;
  708. mLeftDropPolygon[1] = inTopLeft + leftOffset;
  709. mLeftDropPolygon[2] = inBotLeft + leftOffset;
  710. mLeftDropPolygon[3] = outBotLeft + leftOffset;
  711. vertIter.addValue(mLeftDropPolygon[0]);
  712. vertIter.addValue(mLeftDropPolygon[1]);
  713. vertIter.addValue(mLeftDropPolygon[2]);
  714. vertIter.addValue(mLeftDropPolygon[3]);
  715. color = Color(0.0f, 0.0f, 1.0f, 0.0f);
  716. color32 = color.getAsRGBA();
  717. colIter.addValue(color32);
  718. colIter.addValue(color32);
  719. colIter.addValue(color32);
  720. colIter.addValue(color32);
  721. // Right
  722. Vector2 rightOffset((float)spacing * 2.0f, (float)spacing);
  723. mRightDropPolygon[0] = inTopRight + rightOffset;
  724. mRightDropPolygon[1] = outTopRight + rightOffset;
  725. mRightDropPolygon[2] = outBotRight + rightOffset;
  726. mRightDropPolygon[3] = inBotRight + rightOffset;
  727. vertIter.addValue(mRightDropPolygon[0]);
  728. vertIter.addValue(mRightDropPolygon[1]);
  729. vertIter.addValue(mRightDropPolygon[2]);
  730. vertIter.addValue(mRightDropPolygon[3]);
  731. color = Color(0.0f, 0.0f, 0.0f, 1.0f);
  732. color32 = color.getAsRGBA();
  733. colIter.addValue(color32);
  734. colIter.addValue(color32);
  735. colIter.addValue(color32);
  736. colIter.addValue(color32);
  737. UINT32* indexData = meshData->getIndices32();
  738. // Top
  739. indexData[0] = 0;
  740. indexData[1] = 1;
  741. indexData[2] = 2;
  742. indexData[3] = 0;
  743. indexData[4] = 2;
  744. indexData[5] = 3;
  745. // Bottom
  746. indexData[6] = 4;
  747. indexData[7] = 5;
  748. indexData[8] = 6;
  749. indexData[9] = 4;
  750. indexData[10] = 6;
  751. indexData[11] = 7;
  752. // Left
  753. indexData[12] = 8;
  754. indexData[13] = 9;
  755. indexData[14] = 10;
  756. indexData[15] = 8;
  757. indexData[16] = 10;
  758. indexData[17] = 11;
  759. // Right
  760. indexData[18] = 12;
  761. indexData[19] = 13;
  762. indexData[20] = 14;
  763. indexData[21] = 12;
  764. indexData[22] = 14;
  765. indexData[23] = 15;
  766. mDropOverlayMesh = Mesh::create(meshData);
  767. }
  768. bool DockManager::_mouseEvent(const GUIMouseEvent& event)
  769. {
  770. if(event.getType() == GUIMouseEventType::MouseDragAndDropDragged)
  771. {
  772. if(DragAndDropManager::instance().getDragTypeId() != (UINT32)DragAndDropType::EditorWidget)
  773. return false;
  774. const Vector2I& widgetRelPos = event.getPosition();
  775. const Matrix4& worldTfrm = _getParentWidget()->SO()->getWorldTfrm();
  776. Vector4 tfrmdPos = worldTfrm.multiplyAffine(Vector4((float)widgetRelPos.x, (float)widgetRelPos.y, 0.0f, 1.0f));
  777. Vector2 windowPosVec(tfrmdPos.x, tfrmdPos.y);
  778. Vector2I windowPos(Math::roundToInt(windowPosVec.x), Math::roundToInt(windowPosVec.y));
  779. mMouseOverContainer = mRootContainer.findAtPos(windowPos);
  780. if(mMouseOverContainer == nullptr)
  781. mMouseOverContainer = &mRootContainer;
  782. Rect2I overlayBounds;
  783. if(mMouseOverContainer != nullptr)
  784. overlayBounds = mMouseOverContainer->getContentBounds();
  785. // Update mesh if needed
  786. if(mLastOverlayBounds != overlayBounds)
  787. {
  788. if(overlayBounds.width <= 0 || overlayBounds.height <= 0)
  789. mDropOverlayMesh = HMesh();
  790. else
  791. updateDropOverlay(overlayBounds.x, overlayBounds.y, overlayBounds.width, overlayBounds.height);
  792. mLastOverlayBounds = overlayBounds;
  793. }
  794. // Check if we need to highlight any drop locations
  795. if(mMouseOverContainer != nullptr)
  796. {
  797. if(insidePolygon(mTopDropPolygon, 4, windowPosVec))
  798. mHighlightedDropLoc = DockLocation::Top;
  799. else if(insidePolygon(mBotDropPolygon, 4, windowPosVec))
  800. mHighlightedDropLoc = DockLocation::Bottom;
  801. else if(insidePolygon(mLeftDropPolygon, 4, windowPosVec))
  802. mHighlightedDropLoc = DockLocation::Left;
  803. else if(insidePolygon(mRightDropPolygon, 4, windowPosVec))
  804. mHighlightedDropLoc = DockLocation::Right;
  805. else
  806. mHighlightedDropLoc = DockLocation::None;
  807. if(overlayBounds.contains(windowPos))
  808. mShowOverlay = true;
  809. else
  810. mShowOverlay = false;
  811. }
  812. else
  813. mShowOverlay = false;
  814. return false;
  815. }
  816. else if(event.getType() == GUIMouseEventType::MouseDragAndDropDropped)
  817. {
  818. if(DragAndDropManager::instance().getDragTypeId() != (UINT32)DragAndDropType::EditorWidget)
  819. return false;
  820. EditorWidgetBase* draggedWidget = reinterpret_cast<EditorWidgetBase*>(DragAndDropManager::instance().getDragData());
  821. const Vector2I& widgetRelPos = event.getPosition();
  822. const Matrix4& worldTfrm = _getParentWidget()->SO()->getWorldTfrm();
  823. Vector4 tfrmdPos = worldTfrm.multiplyAffine(Vector4((float)widgetRelPos.x, (float)widgetRelPos.y, 0.0f, 1.0f));
  824. Vector2 windowPosVec(tfrmdPos.x, tfrmdPos.y);
  825. Vector2I windowPos(Math::roundToInt(windowPosVec.x), Math::roundToInt(windowPosVec.y));
  826. DockContainer* mouseOverContainer = mRootContainer.findAtPos(windowPos);
  827. if(mouseOverContainer == nullptr)
  828. {
  829. Rect2I overlayBounds = mRootContainer.getContentBounds();
  830. if(overlayBounds.contains(windowPos))
  831. {
  832. insert(nullptr, draggedWidget, DockLocation::None);
  833. return true;
  834. }
  835. }
  836. else
  837. {
  838. if (insidePolygon(mTopDropPolygon, 4, windowPosVec))
  839. {
  840. insert(mouseOverContainer->mWidgets, draggedWidget, DockLocation::Top);
  841. return true;
  842. }
  843. else if (insidePolygon(mBotDropPolygon, 4, windowPosVec))
  844. {
  845. insert(mouseOverContainer->mWidgets, draggedWidget, DockLocation::Bottom);
  846. return true;
  847. }
  848. else if (insidePolygon(mLeftDropPolygon, 4, windowPosVec))
  849. {
  850. insert(mouseOverContainer->mWidgets, draggedWidget, DockLocation::Left);
  851. return true;
  852. }
  853. else if (insidePolygon(mRightDropPolygon, 4, windowPosVec))
  854. {
  855. insert(mouseOverContainer->mWidgets, draggedWidget, DockLocation::Right);
  856. return true;
  857. }
  858. }
  859. }
  860. return false;
  861. }
  862. // TODO - Move to a separate Polygon class?
  863. bool DockManager::insidePolygon(Vector2* polyPoints, UINT32 numPoints, Vector2 point) const
  864. {
  865. bool isInside = false;
  866. for (UINT32 i = 0, j = numPoints - 1; i < numPoints; j = i++)
  867. {
  868. float lineVal = (polyPoints[j].x - polyPoints[i].x) * (point.y - polyPoints[i].y) / (polyPoints[j].y - polyPoints[i].y) + polyPoints[i].x;
  869. if (((polyPoints[i].y > point.y) != (polyPoints[j].y > point.y)) && (point.x < lineVal))
  870. isInside = !isInside;
  871. }
  872. return isInside;
  873. }
  874. }