text: // https://github.com/vassvik/imgui_docking_minimal/
  //
  // This is free and unencumbered software released into the public domain.
  //
  // Anyone is free to copy, modify, publish, use, compile, sell, or
  // distribute this software, either in source code form or as a compiled
  // binary, for any purpose, commercial or non-commercial, and by any
  // means.
  //
  // In jurisdictions that recognize copyright laws, the author or authors
  // of this software dedicate any and all copyright interest in the
  // software to the public domain. We make this dedication for the benefit
  // of the public at large and to the detriment of our heirs and
  // successors. We intend this dedication to be an overt act of
  // relinquishment in perpetuity of all present and future rights to this
  // software under copyright law.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  // OTHER DEALINGS IN THE SOFTWARE.
  //
  // For more information, please refer to <http://unlicense.org>
  
  #define IMGUI_DEFINE_MATH_OPERATORS
  #include <new> // placement new
  #include <bgfx/imgui/imgui.h>
  #include <imgui/imgui.h>
  #include <imgui/imgui_internal.h>
  
  namespace ImGui
  {
  	struct DockContext
  	{
  		enum Slot_
  		{
  			Slot_Left,
  			Slot_Right,
  			Slot_Top,
  			Slot_Bottom,
  			Slot_Tab,
  
  			Slot_Float,
  			Slot_None
  		};
  
  		enum EndAction_
  		{
  			EndAction_None,
  			EndAction_Panel,
  			EndAction_End,
  			EndAction_EndChild
  		};
  
  		enum Status_
  		{
  			Status_Docked,
  			Status_Float,
  			Status_Dragged
  		};
  
  		struct Dock
  		{
  			Dock()
  				: id(0)
  				, label(NULL)
  				, next_tab(NULL)
  				, prev_tab(NULL)
  				, parent(NULL)
  				, pos(0, 0)
  				, size(-1, -1)
  				, active(true)
  				, status(Status_Float)
  				, opened(false)
  				, first(true)
  			{
  				location[0] = 0;
  				children[0] = children[1] = NULL;
  			}
  
  			~Dock()
  			{
  				MemFree(label);
  			}
  
  			ImVec2 getMinSize() const
  			{
  				if (!children[0]) return ImVec2(16, 16 + GetTextLineHeightWithSpacing());
  
  				ImVec2 s0 = children[0]->getMinSize();
  				ImVec2 s1 = children[1]->getMinSize();
  				return isHorizontal() ? ImVec2(s0.x + s1.x, ImMax(s0.y, s1.y))
  					: ImVec2(ImMax(s0.x, s1.x), s0.y + s1.y);
  			}
  
  			bool isHorizontal() const
  			{
  				return children[0]->pos.x < children[1]->pos.x;
  			}
  
  			void setParent(Dock* dock)
  			{
  				parent = dock;
  				for (Dock* tmp = prev_tab; tmp; tmp = tmp->prev_tab) tmp->parent = dock;
  				for (Dock* tmp = next_tab; tmp; tmp = tmp->next_tab) tmp->parent = dock;
  			}
  
  			Dock& getSibling()
  			{
  				IM_ASSERT(parent);
  				if (parent->children[0] == &getFirstTab()) return *parent->children[1];
  				return *parent->children[0];
  			}
  
  			Dock& getFirstTab()
  			{
  				Dock* tmp = this;
  				while (tmp->prev_tab) tmp = tmp->prev_tab;
  				return *tmp;
  			}
  
  			void setActive()
  			{
  				active = true;
  				for (Dock* tmp = prev_tab; tmp; tmp = tmp->prev_tab) tmp->active = false;
  				for (Dock* tmp = next_tab; tmp; tmp = tmp->next_tab) tmp->active = false;
  			}
  
  			bool isContainer() const
  			{
  				return children[0] != NULL;
  			}
  
  			void setChildrenPosSize(const ImVec2& _pos, const ImVec2& _size)
  			{
  				ImVec2 s = children[0]->size;
  				if (isHorizontal())
  				{
  					s.y = _size.y;
  					s.x = (float)int(
  							_size.x * children[0]->size.x / (children[0]->size.x + children[1]->size.x));
  					if (s.x < children[0]->getMinSize().x)
  					{
  						s.x = children[0]->getMinSize().x;
  					}
  					else if (_size.x - s.x < children[1]->getMinSize().x)
  					{
  						s.x = _size.x - children[1]->getMinSize().x;
  					}
  					children[0]->setPosSize(_pos, s);
  
  					s.x = _size.x - children[0]->size.x;
  					ImVec2 p = _pos;
  					p.x += children[0]->size.x;
  					children[1]->setPosSize(p, s);
  				}
  				else
  				{
  					s.x = _size.x;
  					s.y = (float)int(
  							_size.y * children[0]->size.y / (children[0]->size.y + children[1]->size.y));
  					if (s.y < children[0]->getMinSize().y)
  					{
  						s.y = children[0]->getMinSize().y;
  					}
  					else if (_size.y - s.y < children[1]->getMinSize().y)
  					{
  						s.y = _size.y - children[1]->getMinSize().y;
  					}
  					children[0]->setPosSize(_pos, s);
  
  					s.y = _size.y - children[0]->size.y;
  					ImVec2 p = _pos;
  					p.y += children[0]->size.y;
  					children[1]->setPosSize(p, s);
  				}
  			}
  
  			void setPosSize(const ImVec2& _pos, const ImVec2& _size)
  			{
  				size = _size;
  				pos = _pos;
  				for (Dock* tmp = prev_tab; tmp; tmp = tmp->prev_tab)
  				{
  					tmp->size = _size;
  					tmp->pos = _pos;
  				}
  				for (Dock* tmp = next_tab; tmp; tmp = tmp->next_tab)
  				{
  					tmp->size = _size;
  					tmp->pos = _pos;
  				}
  
  				if (!isContainer()) return;
  				setChildrenPosSize(_pos, _size);
  			}
  
  			ImU32   id;
  			char*   label;
  			Dock*   next_tab;
  			Dock*   prev_tab;
  			Dock*   parent;
  			ImVec2  pos;
  			ImVec2  size;
  			bool    active;
  			Status_ status;
  			bool    opened;
  
  			Dock* children[2];
  			char  location[16];
  			int   last_frame;
  			int   invalid_frames;
  			bool  first;
  		};
  
  		typedef ImVector<Dock*> DockVector;
  		DockVector m_docks;
  		ImVec2 m_drag_offset;
  		Dock* m_current;
  		int m_last_frame;
  		EndAction_ m_end_action;
  
  		DockContext()
  			: m_current(NULL)
  			, m_last_frame(0)
  		{
  		}
  
  		~DockContext()
  		{
  		}
  
  		Dock& getDock(const char* label, bool opened)
  		{
  			ImU32 id = ImHashStr(label, 0);
  			for (int i = 0; i < m_docks.size(); ++i)
  			{
  				if (m_docks[i]->id == id)
  				{
  					return *m_docks[i];
  				}
  			}
  
  			Dock* new_dock = (Dock*)MemAlloc(sizeof(Dock));
  			IM_PLACEMENT_NEW(new_dock) Dock();
  			m_docks.push_back(new_dock);
  			new_dock->label = ImStrdup(label);
  			IM_ASSERT(new_dock->label);
  			new_dock->id = id;
  			new_dock->setActive();
  			new_dock->status = Status_Float;
  			new_dock->pos = ImVec2(0, 0);
  			new_dock->size = GetIO().DisplaySize;
  			new_dock->opened = opened;
  			new_dock->first = true;
  			new_dock->last_frame = 0;
  			new_dock->invalid_frames = 0;
  			new_dock->location[0] = 0;
  			return *new_dock;
  		}
  
  		void putInBackground()
  		{
  			ImGuiWindow* win = GetCurrentWindow();
  			ImGuiContext& g = *GImGui;
  			if (g.Windows[0] == win) return;
  
  			for (int i = 0; i < g.Windows.Size; i++)
  			{
  				if (g.Windows[i] == win)
  				{
  					for (int j = i - 1; j >= 0; --j)
  					{
  						g.Windows[j + 1] = g.Windows[j];
  					}
  					g.Windows[0] = win;
  					break;
  				}
  			}
  		}
  
  		void splits()
  		{
  			if (GetFrameCount() == m_last_frame)
  			{
  				return;
  			}
  
  			m_last_frame = GetFrameCount();
  
  			putInBackground();
  
  			ImU32 color = GetColorU32(ImGuiCol_Button);
  			ImU32 color_hovered = GetColorU32(ImGuiCol_ButtonHovered);
  			ImDrawList* draw_list = GetWindowDrawList();
  			ImGuiIO& io = GetIO();
  			for (int i = 0; i < m_docks.size(); ++i)
  			{
  				Dock& dock = *m_docks[i];
  				if (!dock.isContainer())
  				{
  					continue;
  				}
  
  				PushID(i);
  				if (!IsMouseDown(0))
  				{
  					dock.status = Status_Docked;
  				}
  
  				ImVec2 dsize(0, 0);
  				SetCursorScreenPos(dock.children[1]->pos);
  				ImVec2 min_size0 = dock.children[0]->getMinSize();
  				ImVec2 min_size1 = dock.children[1]->getMinSize();
  				if (dock.isHorizontal())
  				{
  					InvisibleButton("split", ImVec2(3, dock.size.y));
  					if (dock.status == Status_Dragged) dsize.x = io.MouseDelta.x;
  					dsize.x = -ImMin(-dsize.x, dock.children[0]->size.x - min_size0.x);
  					dsize.x = ImMin(dsize.x, dock.children[1]->size.x - min_size1.x);
  				}
  				else
  				{
  					InvisibleButton("split", ImVec2(dock.size.x, 3));
  					if (dock.status == Status_Dragged) dsize.y = io.MouseDelta.y;
  					dsize.y = -ImMin(-dsize.y, dock.children[0]->size.y - min_size0.y);
  					dsize.y = ImMin(dsize.y, dock.children[1]->size.y - min_size1.y);
  				}
  				ImVec2 new_size0 = dock.children[0]->size + dsize;
  				ImVec2 new_size1 = dock.children[1]->size - dsize;
  				ImVec2 new_pos1 = dock.children[1]->pos + dsize;
  				dock.children[0]->setPosSize(dock.children[0]->pos, new_size0);
  				dock.children[1]->setPosSize(new_pos1, new_size1);
  
  				if (IsItemHovered() && IsMouseClicked(0))
  				{
  					dock.status = Status_Dragged;
  				}
  
  				draw_list->AddRectFilled(
  						GetItemRectMin(), GetItemRectMax(), IsItemHovered() ? color_hovered : color);
  				PopID();
  			}
  		}
  
  		void checkNonexistent()
  		{
  			int frame_limit = ImMax(0, ImGui::GetFrameCount() - 2);
  			for (DockVector::iterator it = m_docks.begin(), itEnd = m_docks.end(); it != itEnd; ++it)
  			{
  				Dock* dock = *it;
  				if (dock->isContainer()) continue;
  				if (dock->status == Status_Float) continue;
  				if (dock->last_frame < frame_limit)
  				{
  					++dock->invalid_frames;
  					if (dock->invalid_frames > 2)
  					{
  						doUndock(*dock);
  						dock->status = Status_Float;
  					}
  					return;
  				}
  				dock->invalid_frames = 0;
  			}
  		}
  
  		void beginPanel()
  		{
  			ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
  				ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse |
  				ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar |
  				ImGuiWindowFlags_NoScrollWithMouse |
  				ImGuiWindowFlags_NoBringToFrontOnFocus;
  			Dock* root = getRootDock();
  			const ImVec2& displaySize = GetIO().DisplaySize;
  			if (root)
  			{
  				const ImVec2 percentage(displaySize.x / root->size.x, displaySize.y / root->size.y );
  				const ImVec2 rescaledPos = root->pos * percentage;
  				const ImVec2 rescaledSize = root->size * percentage;
  				SetNextWindowPos(rescaledPos);
  				SetNextWindowSize(rescaledSize);
  			}
  			else
  			{
  				SetNextWindowPos(ImVec2(0, 0));
  				SetNextWindowSize(displaySize);
  			}
  			ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0);
  			Begin("###DockPanel", NULL, flags);
  			splits();
  
  			checkNonexistent();
  		}
  
  		void endPanel()
  		{
  			End();
  			ImGui::PopStyleVar();
  		}
  
  		// Doesn't use input??
  		Dock* getDockAt(const ImVec2& /*pos*/) const
  		{
  			for (int i = 0; i < m_docks.size(); ++i)
  			{
  				Dock& dock = *m_docks[i];
  				if (dock.isContainer()) continue;
  				if (dock.status != Status_Docked) continue;
  				if (IsMouseHoveringRect(dock.pos, dock.pos + dock.size, false))
  				{
  					return &dock;
  				}
  			}
  
  			return NULL;
  		}
  
  		static ImRect getDockedRect(const ImRect& rect, Slot_ dock_slot)
  		{
  			ImVec2 half_size = rect.GetSize() * 0.5f;
  			switch (dock_slot)
  			{
  				default: return rect;
  				case Slot_Top: return ImRect(rect.Min, rect.Min + ImVec2(rect.Max.x, half_size.y));
  				case Slot_Right: return ImRect(rect.Min + ImVec2(half_size.x, 0), rect.Max);
  				case Slot_Bottom: return ImRect(rect.Min + ImVec2(0, half_size.y), rect.Max);
  				case Slot_Left: return ImRect(rect.Min, rect.Min + ImVec2(half_size.x, rect.Max.y));
  			}
  		}
  
  		static ImRect getSlotRect(ImRect parent_rect, Slot_ dock_slot)
  		{
  			ImVec2 size = parent_rect.Max - parent_rect.Min;
  			ImVec2 center = parent_rect.Min + size * 0.5f;
  			switch (dock_slot)
  			{
  				default: return ImRect(center - ImVec2(20, 20), center + ImVec2(20, 20));
  				case Slot_Top: return ImRect(center + ImVec2(-20, -50), center + ImVec2(20, -30));
  				case Slot_Right: return ImRect(center + ImVec2(30, -20), center + ImVec2(50, 20));
  				case Slot_Bottom: return ImRect(center + ImVec2(-20, +30), center + ImVec2(20, 50));
  				case Slot_Left: return ImRect(center + ImVec2(-50, -20), center + ImVec2(-30, 20));
  			}
  		}
  
  		static ImRect getSlotRectOnBorder(ImRect parent_rect, Slot_ dock_slot)
  		{
  			ImVec2 size = parent_rect.Max - parent_rect.Min;
  			ImVec2 center = parent_rect.Min + size * 0.5f;
  			switch (dock_slot)
  			{
  				case Slot_Top:
  					return ImRect(ImVec2(center.x - 20, parent_rect.Min.y + 10),
  							ImVec2(center.x + 20, parent_rect.Min.y + 30));
  				case Slot_Left:
  					return ImRect(ImVec2(parent_rect.Min.x + 10, center.y - 20),
  							ImVec2(parent_rect.Min.x + 30, center.y + 20));
  				case Slot_Bottom:
  					return ImRect(ImVec2(center.x - 20, parent_rect.Max.y - 30),
  							ImVec2(center.x + 20, parent_rect.Max.y - 10));
  				case Slot_Right:
  					return ImRect(ImVec2(parent_rect.Max.x - 30, center.y - 20),
  							ImVec2(parent_rect.Max.x - 10, center.y + 20));
  				default: IM_ASSERT(false);
  			}
  			IM_ASSERT(false);
  			return ImRect();
  		}
  
  		Dock* getRootDock()
  		{
  			for (int i = 0; i < m_docks.size(); ++i)
  			{
  				if (!m_docks[i]->parent &&
  						(m_docks[i]->status == Status_Docked || m_docks[i]->children[0]))
  				{
  					return m_docks[i];
  				}
  			}
  			return NULL;
  		}
  
  		bool dockSlots(Dock& dock, Dock* dest_dock, const ImRect& rect, bool on_border)
  		{
  			ImDrawList* canvas = GetWindowDrawList();
  			ImU32 color = GetColorU32(ImGuiCol_Button);
  			ImU32 color_hovered = GetColorU32(ImGuiCol_ButtonHovered);
  			ImVec2 mouse_pos = GetIO().MousePos;
  			for (int i = 0; i < (on_border ? 4 : 5); ++i)
  			{
  				ImRect r =
  					on_border ? getSlotRectOnBorder(rect, (Slot_)i) : getSlotRect(rect, (Slot_)i);
  				bool hovered = r.Contains(mouse_pos);
  
  				canvas->AddRectFilled(r.Min, r.Max, hovered ? color_hovered : color);
  				if (!hovered) continue;
  
  				if (!IsMouseDown(0))
  				{
  					doDock(dock, dest_dock ? dest_dock : getRootDock(), (Slot_)i);
  					return true;
  				}
  				ImRect docked_rect = getDockedRect(rect, (Slot_)i);
  				canvas->AddRectFilled(docked_rect.Min, docked_rect.Max, GetColorU32(ImGuiCol_Button));
  			}
  			return false;
  		}
  
  		void handleDrag(Dock& dock)
  		{
  			Dock* dest_dock = getDockAt(GetIO().MousePos);
  
  			Begin("##Overlay",
  					NULL,
  					ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove |
  					ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings |
  					ImGuiWindowFlags_AlwaysAutoResize);
  			ImDrawList* canvas = GetWindowDrawList();
  
  			canvas->PushClipRectFullScreen();
  
  			ImU32 docked_color = GetColorU32(ImGuiCol_FrameBg);
  			docked_color = (docked_color & 0x00ffFFFF) | 0x80000000;
  			dock.pos = GetIO().MousePos - m_drag_offset;
  			if (dest_dock)
  			{
  				if (dockSlots(dock,
  							dest_dock,
  							ImRect(dest_dock->pos, dest_dock->pos + dest_dock->size),
  							false))
  				{
  					canvas->PopClipRect();
  					End();
  					return;
  				}
  			}
  			if (dockSlots(dock, NULL, ImRect(ImVec2(0, 0), GetIO().DisplaySize), true))
  			{
  				canvas->PopClipRect();
  				End();
  				return;
  			}
  			canvas->AddRectFilled(dock.pos, dock.pos + dock.size, docked_color);
  			canvas->PopClipRect();
  
  			if (!IsMouseDown(0))
  			{
  				dock.status = Status_Float;
  				dock.location[0] = 0;
  				dock.setActive();
  			}
  
  			End();
  		}
  
  		void fillLocation(Dock& dock)
  		{
  			if (dock.status == Status_Float) return;
  			char* c = dock.location;
  			Dock* tmp = &dock;
  			while (tmp->parent)
  			{
  				*c = getLocationCode(tmp);
  				tmp = tmp->parent;
  				++c;
  			}
  			*c = 0;
  		}
  
  		void doUndock(Dock& dock)
  		{
  			if (dock.prev_tab)
  				dock.prev_tab->setActive();
  			else if (dock.next_tab)
  				dock.next_tab->setActive();
  			else
  				dock.active = false;
  			Dock* container = dock.parent;
  
  			if (container)
  			{
  				Dock& sibling = dock.getSibling();
  				if (container->children[0] == &dock)
  				{
  					container->children[0] = dock.next_tab;
  				}
  				else if (container->children[1] == &dock)
  				{
  					container->children[1] = dock.next_tab;
  				}
  
  				bool remove_container = !container->children[0] || !container->children[1];
  				if (remove_container)
  				{
  					if (container->parent)
  					{
  						Dock*& child = container->parent->children[0] == container
  							? container->parent->children[0]
  							: container->parent->children[1];
  						child = &sibling;
  						child->setPosSize(container->pos, container->size);
  						child->setParent(container->parent);
  					}
  					else
  					{
  						if (container->children[0])
  						{
  							container->children[0]->setParent(NULL);
  							container->children[0]->setPosSize(container->pos, container->size);
  						}
  						if (container->children[1])
  						{
  							container->children[1]->setParent(NULL);
  							container->children[1]->setPosSize(container->pos, container->size);
  						}
  					}
  					for (int i = 0; i < m_docks.size(); ++i)
  					{
  						if (m_docks[i] == container)
  						{
  							m_docks.erase(m_docks.begin() + i);
  							break;
  						}
  					}
  					container->~Dock();
  					MemFree(container);
  				}
  			}
  			if (dock.prev_tab) dock.prev_tab->next_tab = dock.next_tab;
  			if (dock.next_tab) dock.next_tab->prev_tab = dock.prev_tab;
  			dock.parent = NULL;
  			dock.prev_tab = dock.next_tab = NULL;
  		}
  
  		void drawTabbarListButton(Dock& dock)
  		{
  			if (!dock.next_tab) return;
  
  			ImDrawList* draw_list = GetWindowDrawList();
  			if (InvisibleButton("list", ImVec2(16, 16)))
  			{
  				OpenPopup("tab_list_popup");
  			}
  			if (BeginPopup("tab_list_popup"))
  			{
  				Dock* tmp = &dock;
  				while (tmp)
  				{
  					bool dummy = false;
  					if (Selectable(tmp->label, &dummy))
  					{
  						tmp->setActive();
  					}
  					tmp = tmp->next_tab;
  				}
  				EndPopup();
  			}
  
  			bool hovered = IsItemHovered();
  			ImVec2 min = GetItemRectMin();
  			ImVec2 max = GetItemRectMax();
  			ImVec2 center = (min + max) * 0.5f;
  			ImU32 text_color = GetColorU32(ImGuiCol_Text);
  			ImU32 color_active = GetColorU32(ImGuiCol_FrameBgActive);
  			draw_list->AddRectFilled(ImVec2(center.x - 4, min.y + 3),
  					ImVec2(center.x + 4, min.y + 5),
  					hovered ? color_active : text_color);
  			draw_list->AddTriangleFilled(ImVec2(center.x - 4, min.y + 7),
  					ImVec2(center.x + 4, min.y + 7),
  					ImVec2(center.x, min.y + 12),
  					hovered ? color_active : text_color);
  		}
  
  		bool tabbar(Dock& dock, bool close_button)
  		{
  			float tabbar_height = 2 * GetTextLineHeightWithSpacing();
  			ImVec2 size0(dock.size.x, tabbar_height);
  			bool tab_closed = false;
  
  			SetCursorScreenPos(dock.pos);
  			char tmp[20];
  			ImFormatString(tmp, IM_ARRAYSIZE(tmp), "tabs%d", (int)dock.id);
  			if (BeginChild(tmp, size0, true))
  			{
  				Dock* dock_tab = &dock;
  
  				ImDrawList* draw_list = GetWindowDrawList();
  				ImU32 color = GetColorU32(ImGuiCol_FrameBg);
  				ImU32 color_active = GetColorU32(ImGuiCol_FrameBgActive);
  				ImU32 color_hovered = GetColorU32(ImGuiCol_FrameBgHovered);
  				ImU32 text_color = GetColorU32(ImGuiCol_Text);
  				ImU32 text_color_disabled = GetColorU32(ImGuiCol_TextDisabled);
  				float line_height = GetTextLineHeightWithSpacing();
  				float tab_base = 0.0f;
  
  				drawTabbarListButton(dock);
  
  				while (dock_tab)
  				{
  					SameLine(0, 15);
  
  					const char* text_end = FindRenderedTextEnd(dock_tab->label);
  					ImVec2 size1(CalcTextSize(dock_tab->label, text_end).x, line_height);
  					if (InvisibleButton(dock_tab->label, size1))
  					{
  						dock_tab->setActive();
  					}
  
  					if (IsItemActive() && IsMouseDragging(0))
  					{
  						m_drag_offset = GetMousePos() - dock_tab->pos;
  						doUndock(*dock_tab);
  						dock_tab->status = Status_Dragged;
  					}
  
  					bool hovered = IsItemHovered();
  					ImVec2 pos = GetItemRectMin();
  					size1.x += 20 + GetStyle().ItemSpacing.x;
  
  					tab_base = pos.y;
  
  					draw_list->AddRectFilled(pos+ImVec2(-8.0f, 0.0),
  							pos+size1,
  							hovered ? color_hovered : (dock_tab->active ? color_active : color));
  					draw_list->AddText(pos, text_color, dock_tab->label, text_end);
  
  					if (dock_tab->active && close_button)
  					{
  						SameLine();
  						tab_closed = InvisibleButton("close", ImVec2(16, 16));
  
  						ImVec2 center = ((GetItemRectMin() + GetItemRectMax()) * 0.5f);
  						draw_list->AddLine( center + ImVec2(-3.5f, -3.5f), center + ImVec2(3.5f, 3.5f), text_color);
  						draw_list->AddLine( center + ImVec2(3.5f, -3.5f), center + ImVec2(-3.5f, 3.5f), text_color);
  					} else {
  						if(!dock_tab->active && close_button) {
  							SameLine();
  							InvisibleButton("close", ImVec2(16, 16));
  
  							ImVec2 center = ((GetItemRectMin() + GetItemRectMax()) * 0.5f);
  							draw_list->AddLine( center + ImVec2(-3.5f, -3.5f), center + ImVec2(3.5f, 3.5f), text_color_disabled);
  							draw_list->AddLine( center + ImVec2(3.5f, -3.5f), center + ImVec2(-3.5f, 3.5f), text_color_disabled);
  						}
  					}
  
  					dock_tab = dock_tab->next_tab;
  				}
  				ImVec2 cp(dock.pos.x, tab_base + line_height);
  				draw_list->AddLine(cp, cp + ImVec2(dock.size.x, 0), color);
  			}
  			EndChild();
  			return tab_closed;
  		}
  
  		static void setDockPosSize(Dock& dest, Dock& dock, Slot_ dock_slot, Dock& container)
  		{
  			IM_ASSERT(!dock.prev_tab && !dock.next_tab && !dock.children[0] && !dock.children[1]);
  
  			dest.pos = container.pos;
  			dest.size = container.size;
  			dock.pos = container.pos;
  			dock.size = container.size;
  
  			switch (dock_slot)
  			{
  				case Slot_Bottom:
  					dest.size.y *= 0.5f;
  					dock.size.y *= 0.5f;
  					dock.pos.y += dest.size.y;
  					break;
  				case Slot_Right:
  					dest.size.x *= 0.5f;
  					dock.size.x *= 0.5f;
  					dock.pos.x += dest.size.x;
  					break;
  				case Slot_Left:
  					dest.size.x *= 0.5f;
  					dock.size.x *= 0.5f;
  					dest.pos.x += dock.size.x;
  					break;
  				case Slot_Top:
  					dest.size.y *= 0.5f;
  					dock.size.y *= 0.5f;
  					dest.pos.y += dock.size.y;
  					break;
  				default: IM_ASSERT(false); break;
  			}
  			dest.setPosSize(dest.pos, dest.size);
  
  			if (container.children[1]->pos.x < container.children[0]->pos.x ||
  					container.children[1]->pos.y < container.children[0]->pos.y)
  			{
  				Dock* tmp = container.children[0];
  				container.children[0] = container.children[1];
  				container.children[1] = tmp;
  			}
  		}
  
  		void doDock(Dock& dock, Dock* dest, Slot_ dock_slot)
  		{
  			IM_ASSERT(!dock.parent);
  			if (!dest)
  			{
  				dock.status = Status_Docked;
  				dock.setPosSize(ImVec2(0, 0), GetIO().DisplaySize);
  			}
  			else if (dock_slot == Slot_Tab)
  			{
  				Dock* tmp = dest;
  				while (tmp->next_tab)
  				{
  					tmp = tmp->next_tab;
  				}
  
  				tmp->next_tab = &dock;
  				dock.prev_tab = tmp;
  				dock.size = tmp->size;
  				dock.pos = tmp->pos;
  				dock.parent = dest->parent;
  				dock.status = Status_Docked;
  			}
  			else if (dock_slot == Slot_None)
  			{
  				dock.status = Status_Float;
  			}
  			else
  			{
  				Dock* container = (Dock*)MemAlloc(sizeof(Dock));
  				IM_PLACEMENT_NEW(container) Dock();
  				m_docks.push_back(container);
  				container->children[0] = &dest->getFirstTab();
  				container->children[1] = &dock;
  				container->next_tab = NULL;
  				container->prev_tab = NULL;
  				container->parent = dest->parent;
  				container->size = dest->size;
  				container->pos = dest->pos;
  				container->status = Status_Docked;
  				container->label = ImStrdup("");
  
  				if (!dest->parent)
  				{
  				}
  				else if (&dest->getFirstTab() == dest->parent->children[0])
  				{
  					dest->parent->children[0] = container;
  				}
  				else
  				{
  					dest->parent->children[1] = container;
  				}
  
  				dest->setParent(container);
  				dock.parent = container;
  				dock.status = Status_Docked;
  
  				setDockPosSize(*dest, dock, dock_slot, *container);
  			}
  			dock.setActive();
  		}
  
  		void rootDock(const ImVec2& pos, const ImVec2& size)
  		{
  			Dock* root = getRootDock();
  			if (!root) return;
  
  			ImVec2 min_size = root->getMinSize();
  			ImVec2 requested_size = size;
  			root->setPosSize(pos, ImMax(min_size, requested_size));
  		}
  
  		void setDockActive()
  		{
  			IM_ASSERT(m_current);
  			if (m_current) m_current->setActive();
  		}
  
  		static Slot_ getSlotFromLocationCode(char code)
  		{
  			switch (code)
  			{
  				case '1': return Slot_Left;
  				case '2': return Slot_Top;
  				case '3': return Slot_Bottom;
  				default: return Slot_Right;
  			}
  		}
  
  		static char getLocationCode(Dock* dock)
  		{
  			if (!dock) return '0';
  
  			if (dock->parent->isHorizontal())
  			{
  				if (dock->pos.x < dock->parent->children[0]->pos.x) return '1';
  				if (dock->pos.x < dock->parent->children[1]->pos.x) return '1';
  				return '0';
  			}
  			else
  			{
  				if (dock->pos.y < dock->parent->children[0]->pos.y) return '2';
  				if (dock->pos.y < dock->parent->children[1]->pos.y) return '2';
  				return '3';
  			}
  		}
  
  		void tryDockToStoredLocation(Dock& dock)
  		{
  			if (dock.status == Status_Docked) return;
  			if (dock.location[0] == 0) return;
  
  			Dock* tmp = getRootDock();
  			if (!tmp) return;
  
  			Dock* prev = NULL;
  			char* c = dock.location + strlen(dock.location) - 1;
  			while (c >= dock.location && tmp)
  			{
  				prev = tmp;
  				tmp = *c == getLocationCode(tmp->children[0]) ? tmp->children[0] : tmp->children[1];
  				if(tmp) --c;
  			}
  			if (tmp && tmp->children[0]) tmp = tmp->parent;
  			doDock(dock, tmp ? tmp : prev, tmp && !tmp->children[0] ? Slot_Tab : getSlotFromLocationCode(*c));
  		}
  
  		bool begin(const char* label, bool* opened, ImGuiWindowFlags extra_flags)
  		{
  			Dock& dock = getDock(label, !opened || *opened);
  			if (!dock.opened && (!opened || *opened)) tryDockToStoredLocation(dock);
  			dock.last_frame = ImGui::GetFrameCount();
  			if (strcmp(dock.label, label) != 0)
  			{
  				MemFree(dock.label);
  				dock.label = ImStrdup(label);
  			}
  
  			m_end_action = EndAction_None;
  
  			if (dock.first && opened) *opened = dock.opened;
  			dock.first = false;
  			if (opened && !*opened)
  			{
  				if (dock.status != Status_Float)
  				{
  					fillLocation(dock);
  					doUndock(dock);
  					dock.status = Status_Float;
  				}
  				dock.opened = false;
  				return false;
  			}
  			dock.opened = true;
  
  			m_end_action = EndAction_Panel;
  			beginPanel();
  
  			m_current = &dock;
  			if (dock.status == Status_Dragged) handleDrag(dock);
  
  			bool is_float = dock.status == Status_Float;
  
  			if (is_float)
  			{
  				SetNextWindowPos(dock.pos);
  				SetNextWindowSize(dock.size);
  				bool ret = Begin(label,
  						opened,
  						ImGuiWindowFlags_NoCollapse | extra_flags);
  				m_end_action = EndAction_End;
  				dock.pos = GetWindowPos();
  				dock.size = GetWindowSize();
  
  				ImGuiContext& g = *GImGui;
  
  				if (g.ActiveId == GetCurrentWindow()->MoveId && g.IO.MouseDown[0])
  				{
  					m_drag_offset = GetMousePos() - dock.pos;
  					doUndock(dock);
  					dock.status = Status_Dragged;
  				}
  				return ret;
  			}
  
  			if (!dock.active && dock.status != Status_Dragged) return false;
  
  			m_end_action = EndAction_EndChild;
  
  			PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
  			PushStyleColor(ImGuiCol_BorderShadow, ImVec4(0, 0, 0, 0));
  			float tabbar_height = GetTextLineHeightWithSpacing();
  			if (tabbar(dock.getFirstTab(), opened != NULL))
  			{
  				fillLocation(dock);
  				*opened = false;
  			}
  			ImVec2 pos = dock.pos;
  			ImVec2 size = dock.size;
  			pos.y += tabbar_height + GetStyle().WindowPadding.y;
  			size.y -= tabbar_height + GetStyle().WindowPadding.y;
  
  			SetCursorScreenPos(pos);
  			ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
  				ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse |
  				ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoBringToFrontOnFocus |
  				extra_flags;
  			char tmp[256];
  			strcpy(tmp, label);
  			strcat(tmp, "_docked"); // to avoid https://github.com/ocornut/imgui/issues/713
  			bool ret = BeginChild(tmp, size, true, flags);
  			PopStyleColor();
  			PopStyleColor();
  			return ret;
  		}
  
  		void end()
  		{
  			if (m_end_action == EndAction_End)
  			{
  				End();
  			}
  			else if (m_end_action == EndAction_EndChild)
  			{
  				PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
  				PushStyleColor(ImGuiCol_BorderShadow, ImVec4(0, 0, 0, 0));
  				EndChild();
  				PopStyleColor();
  				PopStyleColor();
  			}
  			m_current = NULL;
  			if (m_end_action > EndAction_None) endPanel();
  		}
  
  		int getDockIndex(Dock* dock)
  		{
  			if (!dock) return -1;
  
  			for (int i = 0; i < m_docks.size(); ++i)
  			{
  				if (dock == m_docks[i]) return i;
  			}
  
  			IM_ASSERT(false);
  			return -1;
  		}
  
  		Dock* getDockByIndex(int idx)
  		{
  			return idx < 0 ? NULL : m_docks[(int)idx];
  		}
  	};
  
  	static DockContext* s_dock = NULL;
  
  	IMGUI_API void InitDockContext()
  	{
  		void* ptr = ImGui::MemAlloc(sizeof(DockContext) );
  		s_dock = new(ptr) DockContext;
  	}
  
  	IMGUI_API void ShutdownDockContext()
  	{
  		s_dock->~DockContext();
  		ImGui::MemFree(s_dock);
  		s_dock = NULL;
  	}
  
  	IMGUI_API void RootDock(const ImVec2& pos, const ImVec2& size)
  	{
  		s_dock->rootDock(pos, size);
  	}
  
  	IMGUI_API bool BeginDock(const char* label, bool* opened, ImGuiWindowFlags extra_flags)
  	{
  		return s_dock->begin(label, opened, extra_flags);
  	}
  
  	IMGUI_API void EndDock()
  	{
  		s_dock->end();
  	}
  
  	IMGUI_API void SetDockActive()
  	{
  		s_dock->setDockActive();
  	}
  
  } // namespace ImGui