dock.inl 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088
  1. // https://github.com/vassvik/imgui_docking_minimal/
  2. //
  3. // This is free and unencumbered software released into the public domain.
  4. //
  5. // Anyone is free to copy, modify, publish, use, compile, sell, or
  6. // distribute this software, either in source code form or as a compiled
  7. // binary, for any purpose, commercial or non-commercial, and by any
  8. // means.
  9. //
  10. // In jurisdictions that recognize copyright laws, the author or authors
  11. // of this software dedicate any and all copyright interest in the
  12. // software to the public domain. We make this dedication for the benefit
  13. // of the public at large and to the detriment of our heirs and
  14. // successors. We intend this dedication to be an overt act of
  15. // relinquishment in perpetuity of all present and future rights to this
  16. // software under copyright law.
  17. //
  18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  20. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  21. // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  22. // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  23. // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  24. // OTHER DEALINGS IN THE SOFTWARE.
  25. //
  26. // For more information, please refer to <http://unlicense.org>
  27. #include <new> // placement new
  28. namespace ImGui
  29. {
  30. struct DockContext
  31. {
  32. enum Slot_
  33. {
  34. Slot_Left,
  35. Slot_Right,
  36. Slot_Top,
  37. Slot_Bottom,
  38. Slot_Tab,
  39. Slot_Float,
  40. Slot_None
  41. };
  42. enum EndAction_
  43. {
  44. EndAction_None,
  45. EndAction_Panel,
  46. EndAction_End,
  47. EndAction_EndChild
  48. };
  49. enum Status_
  50. {
  51. Status_Docked,
  52. Status_Float,
  53. Status_Dragged
  54. };
  55. struct Dock
  56. {
  57. Dock()
  58. : id(0)
  59. , label(NULL)
  60. , next_tab(NULL)
  61. , prev_tab(NULL)
  62. , parent(NULL)
  63. , pos(0, 0)
  64. , size(-1, -1)
  65. , active(true)
  66. , status(Status_Float)
  67. , opened(false)
  68. , first(true)
  69. {
  70. location[0] = 0;
  71. children[0] = children[1] = NULL;
  72. }
  73. ~Dock()
  74. {
  75. MemFree(label);
  76. }
  77. ImVec2 getMinSize() const
  78. {
  79. if (!children[0]) return ImVec2(16, 16 + GetTextLineHeightWithSpacing());
  80. ImVec2 s0 = children[0]->getMinSize();
  81. ImVec2 s1 = children[1]->getMinSize();
  82. return isHorizontal() ? ImVec2(s0.x + s1.x, ImMax(s0.y, s1.y))
  83. : ImVec2(ImMax(s0.x, s1.x), s0.y + s1.y);
  84. }
  85. bool isHorizontal() const
  86. {
  87. return children[0]->pos.x < children[1]->pos.x;
  88. }
  89. void setParent(Dock* dock)
  90. {
  91. parent = dock;
  92. for (Dock* tmp = prev_tab; tmp; tmp = tmp->prev_tab) tmp->parent = dock;
  93. for (Dock* tmp = next_tab; tmp; tmp = tmp->next_tab) tmp->parent = dock;
  94. }
  95. Dock& getSibling()
  96. {
  97. IM_ASSERT(parent);
  98. if (parent->children[0] == &getFirstTab()) return *parent->children[1];
  99. return *parent->children[0];
  100. }
  101. Dock& getFirstTab()
  102. {
  103. Dock* tmp = this;
  104. while (tmp->prev_tab) tmp = tmp->prev_tab;
  105. return *tmp;
  106. }
  107. void setActive()
  108. {
  109. active = true;
  110. for (Dock* tmp = prev_tab; tmp; tmp = tmp->prev_tab) tmp->active = false;
  111. for (Dock* tmp = next_tab; tmp; tmp = tmp->next_tab) tmp->active = false;
  112. }
  113. bool isContainer() const
  114. {
  115. return children[0] != NULL;
  116. }
  117. void setChildrenPosSize(const ImVec2& _pos, const ImVec2& _size)
  118. {
  119. ImVec2 s = children[0]->size;
  120. if (isHorizontal())
  121. {
  122. s.y = _size.y;
  123. s.x = (float)int(
  124. _size.x * children[0]->size.x / (children[0]->size.x + children[1]->size.x));
  125. if (s.x < children[0]->getMinSize().x)
  126. {
  127. s.x = children[0]->getMinSize().x;
  128. }
  129. else if (_size.x - s.x < children[1]->getMinSize().x)
  130. {
  131. s.x = _size.x - children[1]->getMinSize().x;
  132. }
  133. children[0]->setPosSize(_pos, s);
  134. s.x = _size.x - children[0]->size.x;
  135. ImVec2 p = _pos;
  136. p.x += children[0]->size.x;
  137. children[1]->setPosSize(p, s);
  138. }
  139. else
  140. {
  141. s.x = _size.x;
  142. s.y = (float)int(
  143. _size.y * children[0]->size.y / (children[0]->size.y + children[1]->size.y));
  144. if (s.y < children[0]->getMinSize().y)
  145. {
  146. s.y = children[0]->getMinSize().y;
  147. }
  148. else if (_size.y - s.y < children[1]->getMinSize().y)
  149. {
  150. s.y = _size.y - children[1]->getMinSize().y;
  151. }
  152. children[0]->setPosSize(_pos, s);
  153. s.y = _size.y - children[0]->size.y;
  154. ImVec2 p = _pos;
  155. p.y += children[0]->size.y;
  156. children[1]->setPosSize(p, s);
  157. }
  158. }
  159. void setPosSize(const ImVec2& _pos, const ImVec2& _size)
  160. {
  161. size = _size;
  162. pos = _pos;
  163. for (Dock* tmp = prev_tab; tmp; tmp = tmp->prev_tab)
  164. {
  165. tmp->size = _size;
  166. tmp->pos = _pos;
  167. }
  168. for (Dock* tmp = next_tab; tmp; tmp = tmp->next_tab)
  169. {
  170. tmp->size = _size;
  171. tmp->pos = _pos;
  172. }
  173. if (!isContainer()) return;
  174. setChildrenPosSize(_pos, _size);
  175. }
  176. ImU32 id;
  177. char* label;
  178. Dock* next_tab;
  179. Dock* prev_tab;
  180. Dock* parent;
  181. ImVec2 pos;
  182. ImVec2 size;
  183. bool active;
  184. Status_ status;
  185. bool opened;
  186. Dock* children[2];
  187. char location[16];
  188. int last_frame;
  189. int invalid_frames;
  190. bool first;
  191. };
  192. typedef ImVector<Dock*> DockVector;
  193. DockVector m_docks;
  194. ImVec2 m_drag_offset;
  195. Dock* m_current;
  196. int m_last_frame;
  197. EndAction_ m_end_action;
  198. DockContext()
  199. : m_current(NULL)
  200. , m_last_frame(0)
  201. {
  202. }
  203. ~DockContext()
  204. {
  205. }
  206. Dock& getDock(const char* label, bool opened)
  207. {
  208. ImU32 id = ImHashStr(label, 0);
  209. for (int i = 0; i < m_docks.size(); ++i)
  210. {
  211. if (m_docks[i]->id == id)
  212. {
  213. return *m_docks[i];
  214. }
  215. }
  216. Dock* new_dock = (Dock*)MemAlloc(sizeof(Dock));
  217. IM_PLACEMENT_NEW(new_dock) Dock();
  218. m_docks.push_back(new_dock);
  219. new_dock->label = ImStrdup(label);
  220. IM_ASSERT(new_dock->label);
  221. new_dock->id = id;
  222. new_dock->setActive();
  223. new_dock->status = Status_Float;
  224. new_dock->pos = ImVec2(0, 0);
  225. new_dock->size = GetIO().DisplaySize;
  226. new_dock->opened = opened;
  227. new_dock->first = true;
  228. new_dock->last_frame = 0;
  229. new_dock->invalid_frames = 0;
  230. new_dock->location[0] = 0;
  231. return *new_dock;
  232. }
  233. void putInBackground()
  234. {
  235. ImGuiWindow* win = GetCurrentWindow();
  236. ImGuiContext& g = *GImGui;
  237. if (g.Windows[0] == win) return;
  238. for (int i = 0; i < g.Windows.Size; i++)
  239. {
  240. if (g.Windows[i] == win)
  241. {
  242. for (int j = i - 1; j >= 0; --j)
  243. {
  244. g.Windows[j + 1] = g.Windows[j];
  245. }
  246. g.Windows[0] = win;
  247. break;
  248. }
  249. }
  250. }
  251. void splits()
  252. {
  253. if (GetFrameCount() == m_last_frame)
  254. {
  255. return;
  256. }
  257. m_last_frame = GetFrameCount();
  258. putInBackground();
  259. ImU32 color = GetColorU32(ImGuiCol_Button);
  260. ImU32 color_hovered = GetColorU32(ImGuiCol_ButtonHovered);
  261. ImDrawList* draw_list = GetWindowDrawList();
  262. ImGuiIO& io = GetIO();
  263. for (int i = 0; i < m_docks.size(); ++i)
  264. {
  265. Dock& dock = *m_docks[i];
  266. if (!dock.isContainer())
  267. {
  268. continue;
  269. }
  270. PushID(i);
  271. if (!IsMouseDown(0))
  272. {
  273. dock.status = Status_Docked;
  274. }
  275. ImVec2 dsize(0, 0);
  276. SetCursorScreenPos(dock.children[1]->pos);
  277. ImVec2 min_size0 = dock.children[0]->getMinSize();
  278. ImVec2 min_size1 = dock.children[1]->getMinSize();
  279. if (dock.isHorizontal())
  280. {
  281. InvisibleButton("split", ImVec2(3, dock.size.y));
  282. if (dock.status == Status_Dragged) dsize.x = io.MouseDelta.x;
  283. dsize.x = -ImMin(-dsize.x, dock.children[0]->size.x - min_size0.x);
  284. dsize.x = ImMin(dsize.x, dock.children[1]->size.x - min_size1.x);
  285. }
  286. else
  287. {
  288. InvisibleButton("split", ImVec2(dock.size.x, 3));
  289. if (dock.status == Status_Dragged) dsize.y = io.MouseDelta.y;
  290. dsize.y = -ImMin(-dsize.y, dock.children[0]->size.y - min_size0.y);
  291. dsize.y = ImMin(dsize.y, dock.children[1]->size.y - min_size1.y);
  292. }
  293. ImVec2 new_size0 = dock.children[0]->size + dsize;
  294. ImVec2 new_size1 = dock.children[1]->size - dsize;
  295. ImVec2 new_pos1 = dock.children[1]->pos + dsize;
  296. dock.children[0]->setPosSize(dock.children[0]->pos, new_size0);
  297. dock.children[1]->setPosSize(new_pos1, new_size1);
  298. if (IsItemHovered() && IsMouseClicked(0))
  299. {
  300. dock.status = Status_Dragged;
  301. }
  302. draw_list->AddRectFilled(
  303. GetItemRectMin(), GetItemRectMax(), IsItemHovered() ? color_hovered : color);
  304. PopID();
  305. }
  306. }
  307. void checkNonexistent()
  308. {
  309. int frame_limit = ImMax(0, ImGui::GetFrameCount() - 2);
  310. for (DockVector::iterator it = m_docks.begin(), itEnd = m_docks.end(); it != itEnd; ++it)
  311. {
  312. Dock* dock = *it;
  313. if (dock->isContainer()) continue;
  314. if (dock->status == Status_Float) continue;
  315. if (dock->last_frame < frame_limit)
  316. {
  317. ++dock->invalid_frames;
  318. if (dock->invalid_frames > 2)
  319. {
  320. doUndock(*dock);
  321. dock->status = Status_Float;
  322. }
  323. return;
  324. }
  325. dock->invalid_frames = 0;
  326. }
  327. }
  328. void beginPanel()
  329. {
  330. ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
  331. ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse |
  332. ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
  333. ImGuiWindowFlags_NoScrollWithMouse |
  334. ImGuiWindowFlags_NoBringToFrontOnFocus;
  335. Dock* root = getRootDock();
  336. const ImVec2& displaySize = GetIO().DisplaySize;
  337. if (root)
  338. {
  339. const ImVec2 percentage(displaySize.x / root->size.x, displaySize.y / root->size.y );
  340. const ImVec2 rescaledPos = root->pos * percentage;
  341. const ImVec2 rescaledSize = root->size * percentage;
  342. SetNextWindowPos(rescaledPos);
  343. SetNextWindowSize(rescaledSize);
  344. }
  345. else
  346. {
  347. SetNextWindowPos(ImVec2(0, 0));
  348. SetNextWindowSize(displaySize);
  349. }
  350. ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
  351. Begin("###DockPanel", NULL, flags);
  352. splits();
  353. checkNonexistent();
  354. }
  355. void endPanel()
  356. {
  357. End();
  358. ImGui::PopStyleVar();
  359. }
  360. // Doesn't use input??
  361. Dock* getDockAt(const ImVec2& /*pos*/) const
  362. {
  363. for (int i = 0; i < m_docks.size(); ++i)
  364. {
  365. Dock& dock = *m_docks[i];
  366. if (dock.isContainer()) continue;
  367. if (dock.status != Status_Docked) continue;
  368. if (IsMouseHoveringRect(dock.pos, dock.pos + dock.size, false))
  369. {
  370. return &dock;
  371. }
  372. }
  373. return NULL;
  374. }
  375. static ImRect getDockedRect(const ImRect& rect, Slot_ dock_slot)
  376. {
  377. ImVec2 half_size = rect.GetSize() * 0.5f;
  378. switch (dock_slot)
  379. {
  380. default: return rect;
  381. case Slot_Top: return ImRect(rect.Min, rect.Min + ImVec2(rect.Max.x, half_size.y));
  382. case Slot_Right: return ImRect(rect.Min + ImVec2(half_size.x, 0), rect.Max);
  383. case Slot_Bottom: return ImRect(rect.Min + ImVec2(0, half_size.y), rect.Max);
  384. case Slot_Left: return ImRect(rect.Min, rect.Min + ImVec2(half_size.x, rect.Max.y));
  385. }
  386. }
  387. static ImRect getSlotRect(ImRect parent_rect, Slot_ dock_slot)
  388. {
  389. ImVec2 size = parent_rect.Max - parent_rect.Min;
  390. ImVec2 center = parent_rect.Min + size * 0.5f;
  391. switch (dock_slot)
  392. {
  393. default: return ImRect(center - ImVec2(20, 20), center + ImVec2(20, 20));
  394. case Slot_Top: return ImRect(center + ImVec2(-20, -50), center + ImVec2(20, -30));
  395. case Slot_Right: return ImRect(center + ImVec2(30, -20), center + ImVec2(50, 20));
  396. case Slot_Bottom: return ImRect(center + ImVec2(-20, +30), center + ImVec2(20, 50));
  397. case Slot_Left: return ImRect(center + ImVec2(-50, -20), center + ImVec2(-30, 20));
  398. }
  399. }
  400. static ImRect getSlotRectOnBorder(ImRect parent_rect, Slot_ dock_slot)
  401. {
  402. ImVec2 size = parent_rect.Max - parent_rect.Min;
  403. ImVec2 center = parent_rect.Min + size * 0.5f;
  404. switch (dock_slot)
  405. {
  406. case Slot_Top:
  407. return ImRect(ImVec2(center.x - 20, parent_rect.Min.y + 10),
  408. ImVec2(center.x + 20, parent_rect.Min.y + 30));
  409. case Slot_Left:
  410. return ImRect(ImVec2(parent_rect.Min.x + 10, center.y - 20),
  411. ImVec2(parent_rect.Min.x + 30, center.y + 20));
  412. case Slot_Bottom:
  413. return ImRect(ImVec2(center.x - 20, parent_rect.Max.y - 30),
  414. ImVec2(center.x + 20, parent_rect.Max.y - 10));
  415. case Slot_Right:
  416. return ImRect(ImVec2(parent_rect.Max.x - 30, center.y - 20),
  417. ImVec2(parent_rect.Max.x - 10, center.y + 20));
  418. default: IM_ASSERT(false);
  419. }
  420. IM_ASSERT(false);
  421. return ImRect();
  422. }
  423. Dock* getRootDock()
  424. {
  425. for (int i = 0; i < m_docks.size(); ++i)
  426. {
  427. if (!m_docks[i]->parent &&
  428. (m_docks[i]->status == Status_Docked || m_docks[i]->children[0]))
  429. {
  430. return m_docks[i];
  431. }
  432. }
  433. return NULL;
  434. }
  435. bool dockSlots(Dock& dock, Dock* dest_dock, const ImRect& rect, bool on_border)
  436. {
  437. ImDrawList* canvas = GetWindowDrawList();
  438. ImU32 color = GetColorU32(ImGuiCol_Button);
  439. ImU32 color_hovered = GetColorU32(ImGuiCol_ButtonHovered);
  440. ImVec2 mouse_pos = GetIO().MousePos;
  441. for (int i = 0; i < (on_border ? 4 : 5); ++i)
  442. {
  443. ImRect r =
  444. on_border ? getSlotRectOnBorder(rect, (Slot_)i) : getSlotRect(rect, (Slot_)i);
  445. bool hovered = r.Contains(mouse_pos);
  446. canvas->AddRectFilled(r.Min, r.Max, hovered ? color_hovered : color);
  447. if (!hovered) continue;
  448. if (!IsMouseDown(0))
  449. {
  450. doDock(dock, dest_dock ? dest_dock : getRootDock(), (Slot_)i);
  451. return true;
  452. }
  453. ImRect docked_rect = getDockedRect(rect, (Slot_)i);
  454. canvas->AddRectFilled(docked_rect.Min, docked_rect.Max, GetColorU32(ImGuiCol_Button));
  455. }
  456. return false;
  457. }
  458. void handleDrag(Dock& dock)
  459. {
  460. Dock* dest_dock = getDockAt(GetIO().MousePos);
  461. Begin("##Overlay",
  462. NULL,
  463. ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove |
  464. ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings |
  465. ImGuiWindowFlags_AlwaysAutoResize);
  466. ImDrawList* canvas = GetWindowDrawList();
  467. canvas->PushClipRectFullScreen();
  468. ImU32 docked_color = GetColorU32(ImGuiCol_FrameBg);
  469. docked_color = (docked_color & 0x00ffFFFF) | 0x80000000;
  470. dock.pos = GetIO().MousePos - m_drag_offset;
  471. if (dest_dock)
  472. {
  473. if (dockSlots(dock,
  474. dest_dock,
  475. ImRect(dest_dock->pos, dest_dock->pos + dest_dock->size),
  476. false))
  477. {
  478. canvas->PopClipRect();
  479. End();
  480. return;
  481. }
  482. }
  483. if (dockSlots(dock, NULL, ImRect(ImVec2(0, 0), GetIO().DisplaySize), true))
  484. {
  485. canvas->PopClipRect();
  486. End();
  487. return;
  488. }
  489. canvas->AddRectFilled(dock.pos, dock.pos + dock.size, docked_color);
  490. canvas->PopClipRect();
  491. if (!IsMouseDown(0))
  492. {
  493. dock.status = Status_Float;
  494. dock.location[0] = 0;
  495. dock.setActive();
  496. }
  497. End();
  498. }
  499. void fillLocation(Dock& dock)
  500. {
  501. if (dock.status == Status_Float) return;
  502. char* c = dock.location;
  503. Dock* tmp = &dock;
  504. while (tmp->parent)
  505. {
  506. *c = getLocationCode(tmp);
  507. tmp = tmp->parent;
  508. ++c;
  509. }
  510. *c = 0;
  511. }
  512. void doUndock(Dock& dock)
  513. {
  514. if (dock.prev_tab)
  515. dock.prev_tab->setActive();
  516. else if (dock.next_tab)
  517. dock.next_tab->setActive();
  518. else
  519. dock.active = false;
  520. Dock* container = dock.parent;
  521. if (container)
  522. {
  523. Dock& sibling = dock.getSibling();
  524. if (container->children[0] == &dock)
  525. {
  526. container->children[0] = dock.next_tab;
  527. }
  528. else if (container->children[1] == &dock)
  529. {
  530. container->children[1] = dock.next_tab;
  531. }
  532. bool remove_container = !container->children[0] || !container->children[1];
  533. if (remove_container)
  534. {
  535. if (container->parent)
  536. {
  537. Dock*& child = container->parent->children[0] == container
  538. ? container->parent->children[0]
  539. : container->parent->children[1];
  540. child = &sibling;
  541. child->setPosSize(container->pos, container->size);
  542. child->setParent(container->parent);
  543. }
  544. else
  545. {
  546. if (container->children[0])
  547. {
  548. container->children[0]->setParent(NULL);
  549. container->children[0]->setPosSize(container->pos, container->size);
  550. }
  551. if (container->children[1])
  552. {
  553. container->children[1]->setParent(NULL);
  554. container->children[1]->setPosSize(container->pos, container->size);
  555. }
  556. }
  557. for (int i = 0; i < m_docks.size(); ++i)
  558. {
  559. if (m_docks[i] == container)
  560. {
  561. m_docks.erase(m_docks.begin() + i);
  562. break;
  563. }
  564. }
  565. container->~Dock();
  566. MemFree(container);
  567. }
  568. }
  569. if (dock.prev_tab) dock.prev_tab->next_tab = dock.next_tab;
  570. if (dock.next_tab) dock.next_tab->prev_tab = dock.prev_tab;
  571. dock.parent = NULL;
  572. dock.prev_tab = dock.next_tab = NULL;
  573. }
  574. void drawTabbarListButton(Dock& dock)
  575. {
  576. if (!dock.next_tab) return;
  577. ImDrawList* draw_list = GetWindowDrawList();
  578. if (InvisibleButton("list", ImVec2(16, 16)))
  579. {
  580. OpenPopup("tab_list_popup");
  581. }
  582. if (BeginPopup("tab_list_popup"))
  583. {
  584. Dock* tmp = &dock;
  585. while (tmp)
  586. {
  587. bool dummy = false;
  588. if (Selectable(tmp->label, &dummy))
  589. {
  590. tmp->setActive();
  591. }
  592. tmp = tmp->next_tab;
  593. }
  594. EndPopup();
  595. }
  596. bool hovered = IsItemHovered();
  597. ImVec2 min = GetItemRectMin();
  598. ImVec2 max = GetItemRectMax();
  599. ImVec2 center = (min + max) * 0.5f;
  600. ImU32 text_color = GetColorU32(ImGuiCol_Text);
  601. ImU32 color_active = GetColorU32(ImGuiCol_FrameBgActive);
  602. draw_list->AddRectFilled(ImVec2(center.x - 4, min.y + 3),
  603. ImVec2(center.x + 4, min.y + 5),
  604. hovered ? color_active : text_color);
  605. draw_list->AddTriangleFilled(ImVec2(center.x - 4, min.y + 7),
  606. ImVec2(center.x + 4, min.y + 7),
  607. ImVec2(center.x, min.y + 12),
  608. hovered ? color_active : text_color);
  609. }
  610. bool tabbar(Dock& dock, bool close_button)
  611. {
  612. float tabbar_height = 2 * GetTextLineHeightWithSpacing();
  613. ImVec2 size0(dock.size.x, tabbar_height);
  614. bool tab_closed = false;
  615. SetCursorScreenPos(dock.pos);
  616. char tmp[20];
  617. ImFormatString(tmp, IM_ARRAYSIZE(tmp), "tabs%d", (int)dock.id);
  618. if (BeginChild(tmp, size0, true))
  619. {
  620. Dock* dock_tab = &dock;
  621. ImDrawList* draw_list = GetWindowDrawList();
  622. ImU32 color = GetColorU32(ImGuiCol_FrameBg);
  623. ImU32 color_active = GetColorU32(ImGuiCol_FrameBgActive);
  624. ImU32 color_hovered = GetColorU32(ImGuiCol_FrameBgHovered);
  625. ImU32 text_color = GetColorU32(ImGuiCol_Text);
  626. ImU32 text_color_disabled = GetColorU32(ImGuiCol_TextDisabled);
  627. float line_height = GetTextLineHeightWithSpacing();
  628. float tab_base = 0.0f;
  629. drawTabbarListButton(dock);
  630. while (dock_tab)
  631. {
  632. SameLine(0, 15);
  633. const char* text_end = FindRenderedTextEnd(dock_tab->label);
  634. ImVec2 size1(CalcTextSize(dock_tab->label, text_end).x, line_height);
  635. if (InvisibleButton(dock_tab->label, size1))
  636. {
  637. dock_tab->setActive();
  638. }
  639. if (IsItemActive() && IsMouseDragging(0))
  640. {
  641. m_drag_offset = GetMousePos() - dock_tab->pos;
  642. doUndock(*dock_tab);
  643. dock_tab->status = Status_Dragged;
  644. }
  645. bool hovered = IsItemHovered();
  646. ImVec2 pos = GetItemRectMin();
  647. size1.x += 20 + GetStyle().ItemSpacing.x;
  648. tab_base = pos.y;
  649. draw_list->AddRectFilled(pos+ImVec2(-8.0f, 0.0),
  650. pos+size1,
  651. hovered ? color_hovered : (dock_tab->active ? color_active : color));
  652. draw_list->AddText(pos, text_color, dock_tab->label, text_end);
  653. if (dock_tab->active && close_button)
  654. {
  655. SameLine();
  656. tab_closed = InvisibleButton("close", ImVec2(16, 16));
  657. ImVec2 center = ((GetItemRectMin() + GetItemRectMax()) * 0.5f);
  658. draw_list->AddLine( center + ImVec2(-3.5f, -3.5f), center + ImVec2(3.5f, 3.5f), text_color);
  659. draw_list->AddLine( center + ImVec2(3.5f, -3.5f), center + ImVec2(-3.5f, 3.5f), text_color);
  660. } else {
  661. if(!dock_tab->active && close_button) {
  662. SameLine();
  663. InvisibleButton("close", ImVec2(16, 16));
  664. ImVec2 center = ((GetItemRectMin() + GetItemRectMax()) * 0.5f);
  665. draw_list->AddLine( center + ImVec2(-3.5f, -3.5f), center + ImVec2(3.5f, 3.5f), text_color_disabled);
  666. draw_list->AddLine( center + ImVec2(3.5f, -3.5f), center + ImVec2(-3.5f, 3.5f), text_color_disabled);
  667. }
  668. }
  669. dock_tab = dock_tab->next_tab;
  670. }
  671. ImVec2 cp(dock.pos.x, tab_base + line_height);
  672. draw_list->AddLine(cp, cp + ImVec2(dock.size.x, 0), color);
  673. }
  674. EndChild();
  675. return tab_closed;
  676. }
  677. static void setDockPosSize(Dock& dest, Dock& dock, Slot_ dock_slot, Dock& container)
  678. {
  679. IM_ASSERT(!dock.prev_tab && !dock.next_tab && !dock.children[0] && !dock.children[1]);
  680. dest.pos = container.pos;
  681. dest.size = container.size;
  682. dock.pos = container.pos;
  683. dock.size = container.size;
  684. switch (dock_slot)
  685. {
  686. case Slot_Bottom:
  687. dest.size.y *= 0.5f;
  688. dock.size.y *= 0.5f;
  689. dock.pos.y += dest.size.y;
  690. break;
  691. case Slot_Right:
  692. dest.size.x *= 0.5f;
  693. dock.size.x *= 0.5f;
  694. dock.pos.x += dest.size.x;
  695. break;
  696. case Slot_Left:
  697. dest.size.x *= 0.5f;
  698. dock.size.x *= 0.5f;
  699. dest.pos.x += dock.size.x;
  700. break;
  701. case Slot_Top:
  702. dest.size.y *= 0.5f;
  703. dock.size.y *= 0.5f;
  704. dest.pos.y += dock.size.y;
  705. break;
  706. default: IM_ASSERT(false); break;
  707. }
  708. dest.setPosSize(dest.pos, dest.size);
  709. if (container.children[1]->pos.x < container.children[0]->pos.x ||
  710. container.children[1]->pos.y < container.children[0]->pos.y)
  711. {
  712. Dock* tmp = container.children[0];
  713. container.children[0] = container.children[1];
  714. container.children[1] = tmp;
  715. }
  716. }
  717. void doDock(Dock& dock, Dock* dest, Slot_ dock_slot)
  718. {
  719. IM_ASSERT(!dock.parent);
  720. if (!dest)
  721. {
  722. dock.status = Status_Docked;
  723. dock.setPosSize(ImVec2(0, 0), GetIO().DisplaySize);
  724. }
  725. else if (dock_slot == Slot_Tab)
  726. {
  727. Dock* tmp = dest;
  728. while (tmp->next_tab)
  729. {
  730. tmp = tmp->next_tab;
  731. }
  732. tmp->next_tab = &dock;
  733. dock.prev_tab = tmp;
  734. dock.size = tmp->size;
  735. dock.pos = tmp->pos;
  736. dock.parent = dest->parent;
  737. dock.status = Status_Docked;
  738. }
  739. else if (dock_slot == Slot_None)
  740. {
  741. dock.status = Status_Float;
  742. }
  743. else
  744. {
  745. Dock* container = (Dock*)MemAlloc(sizeof(Dock));
  746. IM_PLACEMENT_NEW(container) Dock();
  747. m_docks.push_back(container);
  748. container->children[0] = &dest->getFirstTab();
  749. container->children[1] = &dock;
  750. container->next_tab = NULL;
  751. container->prev_tab = NULL;
  752. container->parent = dest->parent;
  753. container->size = dest->size;
  754. container->pos = dest->pos;
  755. container->status = Status_Docked;
  756. container->label = ImStrdup("");
  757. if (!dest->parent)
  758. {
  759. }
  760. else if (&dest->getFirstTab() == dest->parent->children[0])
  761. {
  762. dest->parent->children[0] = container;
  763. }
  764. else
  765. {
  766. dest->parent->children[1] = container;
  767. }
  768. dest->setParent(container);
  769. dock.parent = container;
  770. dock.status = Status_Docked;
  771. setDockPosSize(*dest, dock, dock_slot, *container);
  772. }
  773. dock.setActive();
  774. }
  775. void rootDock(const ImVec2& pos, const ImVec2& size)
  776. {
  777. Dock* root = getRootDock();
  778. if (!root) return;
  779. ImVec2 min_size = root->getMinSize();
  780. ImVec2 requested_size = size;
  781. root->setPosSize(pos, ImMax(min_size, requested_size));
  782. }
  783. void setDockActive()
  784. {
  785. IM_ASSERT(m_current);
  786. if (m_current) m_current->setActive();
  787. }
  788. static Slot_ getSlotFromLocationCode(char code)
  789. {
  790. switch (code)
  791. {
  792. case '1': return Slot_Left;
  793. case '2': return Slot_Top;
  794. case '3': return Slot_Bottom;
  795. default: return Slot_Right;
  796. }
  797. }
  798. static char getLocationCode(Dock* dock)
  799. {
  800. if (!dock) return '0';
  801. if (dock->parent->isHorizontal())
  802. {
  803. if (dock->pos.x < dock->parent->children[0]->pos.x) return '1';
  804. if (dock->pos.x < dock->parent->children[1]->pos.x) return '1';
  805. return '0';
  806. }
  807. else
  808. {
  809. if (dock->pos.y < dock->parent->children[0]->pos.y) return '2';
  810. if (dock->pos.y < dock->parent->children[1]->pos.y) return '2';
  811. return '3';
  812. }
  813. }
  814. void tryDockToStoredLocation(Dock& dock)
  815. {
  816. if (dock.status == Status_Docked) return;
  817. if (dock.location[0] == 0) return;
  818. Dock* tmp = getRootDock();
  819. if (!tmp) return;
  820. Dock* prev = NULL;
  821. char* c = dock.location + strlen(dock.location) - 1;
  822. while (c >= dock.location && tmp)
  823. {
  824. prev = tmp;
  825. tmp = *c == getLocationCode(tmp->children[0]) ? tmp->children[0] : tmp->children[1];
  826. if(tmp) --c;
  827. }
  828. if (tmp && tmp->children[0]) tmp = tmp->parent;
  829. doDock(dock, tmp ? tmp : prev, tmp && !tmp->children[0] ? Slot_Tab : getSlotFromLocationCode(*c));
  830. }
  831. bool begin(const char* label, bool* opened, ImGuiWindowFlags extra_flags)
  832. {
  833. Dock& dock = getDock(label, !opened || *opened);
  834. if (!dock.opened && (!opened || *opened)) tryDockToStoredLocation(dock);
  835. dock.last_frame = ImGui::GetFrameCount();
  836. if (strcmp(dock.label, label) != 0)
  837. {
  838. MemFree(dock.label);
  839. dock.label = ImStrdup(label);
  840. }
  841. m_end_action = EndAction_None;
  842. if (dock.first && opened) *opened = dock.opened;
  843. dock.first = false;
  844. if (opened && !*opened)
  845. {
  846. if (dock.status != Status_Float)
  847. {
  848. fillLocation(dock);
  849. doUndock(dock);
  850. dock.status = Status_Float;
  851. }
  852. dock.opened = false;
  853. return false;
  854. }
  855. dock.opened = true;
  856. m_end_action = EndAction_Panel;
  857. beginPanel();
  858. m_current = &dock;
  859. if (dock.status == Status_Dragged) handleDrag(dock);
  860. bool is_float = dock.status == Status_Float;
  861. if (is_float)
  862. {
  863. SetNextWindowPos(dock.pos);
  864. SetNextWindowSize(dock.size);
  865. bool ret = Begin(label,
  866. opened,
  867. ImGuiWindowFlags_NoCollapse | extra_flags);
  868. m_end_action = EndAction_End;
  869. dock.pos = GetWindowPos();
  870. dock.size = GetWindowSize();
  871. ImGuiContext& g = *GImGui;
  872. if (g.ActiveId == GetCurrentWindow()->MoveId && g.IO.MouseDown[0])
  873. {
  874. m_drag_offset = GetMousePos() - dock.pos;
  875. doUndock(dock);
  876. dock.status = Status_Dragged;
  877. }
  878. return ret;
  879. }
  880. if (!dock.active && dock.status != Status_Dragged) return false;
  881. m_end_action = EndAction_EndChild;
  882. PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
  883. PushStyleColor(ImGuiCol_BorderShadow, ImVec4(0, 0, 0, 0));
  884. float tabbar_height = GetTextLineHeightWithSpacing();
  885. if (tabbar(dock.getFirstTab(), opened != NULL))
  886. {
  887. fillLocation(dock);
  888. *opened = false;
  889. }
  890. ImVec2 pos = dock.pos;
  891. ImVec2 size = dock.size;
  892. pos.y += tabbar_height + GetStyle().WindowPadding.y;
  893. size.y -= tabbar_height + GetStyle().WindowPadding.y;
  894. SetCursorScreenPos(pos);
  895. ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
  896. ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse |
  897. ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoBringToFrontOnFocus |
  898. extra_flags;
  899. char tmp[256];
  900. strcpy(tmp, label);
  901. strcat(tmp, "_docked"); // to avoid https://github.com/ocornut/imgui/issues/713
  902. bool ret = BeginChild(tmp, size, true, flags);
  903. PopStyleColor();
  904. PopStyleColor();
  905. return ret;
  906. }
  907. void end()
  908. {
  909. if (m_end_action == EndAction_End)
  910. {
  911. End();
  912. }
  913. else if (m_end_action == EndAction_EndChild)
  914. {
  915. PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
  916. PushStyleColor(ImGuiCol_BorderShadow, ImVec4(0, 0, 0, 0));
  917. EndChild();
  918. PopStyleColor();
  919. PopStyleColor();
  920. }
  921. m_current = NULL;
  922. if (m_end_action > EndAction_None) endPanel();
  923. }
  924. int getDockIndex(Dock* dock)
  925. {
  926. if (!dock) return -1;
  927. for (int i = 0; i < m_docks.size(); ++i)
  928. {
  929. if (dock == m_docks[i]) return i;
  930. }
  931. IM_ASSERT(false);
  932. return -1;
  933. }
  934. Dock* getDockByIndex(int idx)
  935. {
  936. return idx < 0 ? NULL : m_docks[(int)idx];
  937. }
  938. };
  939. static DockContext* s_dock = NULL;
  940. IMGUI_API void InitDockContext()
  941. {
  942. void* ptr = ImGui::MemAlloc(sizeof(DockContext) );
  943. s_dock = new(ptr) DockContext;
  944. }
  945. IMGUI_API void ShutdownDockContext()
  946. {
  947. s_dock->~DockContext();
  948. ImGui::MemFree(s_dock);
  949. s_dock = NULL;
  950. }
  951. IMGUI_API void RootDock(const ImVec2& pos, const ImVec2& size)
  952. {
  953. s_dock->rootDock(pos, size);
  954. }
  955. IMGUI_API bool BeginDock(const char* label, bool* opened, ImGuiWindowFlags extra_flags)
  956. {
  957. return s_dock->begin(label, opened, extra_flags);
  958. }
  959. IMGUI_API void EndDock()
  960. {
  961. s_dock->end();
  962. }
  963. IMGUI_API void SetDockActive()
  964. {
  965. s_dock->setDockActive();
  966. }
  967. } // namespace ImGui