BsDockManager.cpp 29 KB

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