BsDockManager.cpp 30 KB

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