Created
January 18, 2016 13:37
-
-
Save r-lyeh-archived/a4ff6019062a8c9b402a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// [src] https://github.com/ocornut/imgui/issues/351 | |
// [src] https://github.com/ocornut/imgui/issues/123 | |
// v1.0 | |
#define IMGUI_DOCK_ENABLE_SERIALIZATION 0 | |
// header | |
// #pragma once | |
namespace Lumix { namespace FS { class OsFile; } } | |
struct lua_State; | |
namespace ImGui | |
{ | |
IMGUI_API void ShutdownDock(); | |
IMGUI_API void RootDock(const ImVec2& pos, const ImVec2& size); | |
IMGUI_API bool BeginDock(const char* label, bool* opened = nullptr, ImGuiWindowFlags extra_flags = 0); | |
IMGUI_API void EndDock(); | |
IMGUI_API void SaveDock(Lumix::FS::OsFile& file); | |
IMGUI_API void LoadDock(lua_State* L); | |
} // namespace ImGui | |
// source following | |
#include "imgui.h" | |
#include "imgui_internal.h" | |
#include <new.h> | |
#if IMGUI_DOCK_ENABLE_SERIALIZATION | |
#include "core/fs/os_file.h" | |
#include <lua.hpp> | |
#endif | |
// Find the optional ## from which we stop displaying text. | |
static const char* FindTextDisplayEnd(const char* text, const char* text_end = NULL) | |
{ | |
const char* text_display_end = text; | |
if (!text_end) | |
text_end = (const char*)-1; | |
ImGuiState& g = *GImGui; | |
if (g.DisableHideTextAfterDoubleHash > 0) | |
{ | |
while (text_display_end < text_end && *text_display_end != '\0') | |
text_display_end++; | |
} | |
else | |
{ | |
while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) | |
text_display_end++; | |
} | |
return text_display_end; | |
} | |
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) | |
, next_tab(nullptr) | |
, prev_tab(nullptr) | |
, parent(nullptr) | |
, pos(0, 0) | |
, size(-1, -1) | |
, active(true) | |
, status(Status_Float) | |
, label(nullptr) | |
{ | |
children[0] = children[1] = nullptr; | |
} | |
~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] != nullptr; } | |
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); | |
} | |
char* label; | |
ImU32 id; | |
Dock* next_tab; | |
Dock* prev_tab; | |
Dock* children[2]; | |
Dock* parent; | |
bool active; | |
ImVec2 pos; | |
ImVec2 size; | |
Status_ status; | |
}; | |
ImVector<Dock*> m_docks; | |
ImVec2 m_drag_offset; | |
Dock* m_current = nullptr; | |
int m_last_frame = 0; | |
EndAction_ m_end_action; | |
~DockContext() | |
{ | |
} | |
Dock& getDock(const char* label, bool opened) | |
{ | |
ImU32 id = ImHash(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)); | |
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; | |
return *new_dock; | |
} | |
void putInBackground() | |
{ | |
ImGuiWindow* win = GetCurrentWindow(); | |
ImGuiState& 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 size = dock.children[0]->size; | |
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.x + dsize.x, dock.children[0]->size.y + dsize.y ); | |
ImVec2 new_size1( dock.children[1]->size.x - dsize.x, dock.children[1]->size.y - dsize.y ); | |
ImVec2 new_pos1( dock.children[1]->pos.x + dsize.x, dock.children[1]->pos.y + dsize.y ); | |
dock.children[0]->setPosSize(dock.children[0]->pos, new_size0); | |
dock.children[1]->setPosSize(new_pos1, new_size1); | |
if (IsItemHoveredRect() && IsMouseClicked(0)) | |
{ | |
dock.status = Status_Dragged; | |
} | |
draw_list->AddRectFilled(GetItemRectMin(), | |
GetItemRectMax(), | |
IsItemHoveredRect() ? color_hovered : color); | |
PopID(); | |
} | |
} | |
void beginPanel() | |
{ | |
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | | |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | | |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoScrollbar | | |
ImGuiWindowFlags_NoScrollWithMouse; | |
Dock* root = getRootDock(); | |
if (root) | |
{ | |
SetNextWindowPos(root->pos); | |
SetNextWindowSize(root->size); | |
} | |
else | |
{ | |
SetNextWindowPos(ImVec2(0, 0)); | |
SetNextWindowSize(GetIO().DisplaySize ); | |
} | |
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0); | |
Begin("###DockPanel", nullptr, flags); | |
splits(); | |
} | |
void endPanel() | |
{ | |
End(); | |
ImGui::PopStyleVar(); | |
} | |
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, ImVec2(dock.pos.x + dock.size.x, dock.pos.y + dock.size.y), false)) | |
{ | |
return &dock; | |
} | |
} | |
return nullptr; | |
} | |
static ImRect getDockedRect(const ImRect& rect, Slot_ dock_slot) | |
{ | |
ImVec2 size = rect.GetSize(); | |
ImVec2 half_size( size.x * 0.5f, size.y * 0.5f ); | |
switch (dock_slot) | |
{ | |
default: return rect; | |
case Slot_Top: return ImRect(rect.Min, ImVec2(rect.Min.x + rect.Max.x, rect.Min.y + half_size.y)); | |
case Slot_Right: | |
return ImRect( ImVec2(rect.Min.x+half_size.x, rect.Min.y + 0), rect.Max); | |
case Slot_Bottom: | |
return ImRect( ImVec2(rect.Min.x+0, rect.Min.y + half_size.y), rect.Max); | |
case Slot_Left: return ImRect(rect.Min, ImVec2(rect.Min.x + half_size.x, rect.Min.y + rect.Max.y)); | |
} | |
} | |
static ImRect getSlotRect(ImRect parent_rect, Slot_ dock_slot) | |
{ | |
ImVec2 size( parent_rect.Max.x - parent_rect.Min.x, parent_rect.Max.y - parent_rect.Min.y ); | |
ImVec2 center( parent_rect.Min.x + size.x * 0.5f, parent_rect.Min.y + size.y * 0.5f ); | |
switch (dock_slot) | |
{ | |
default: return ImRect(ImVec2(center.x - 20, center.y - 20), ImVec2(center.x + 20, center.y + 20)); | |
case Slot_Top: return ImRect(ImVec2(center.x + -20, center.y + -50), ImVec2(center.x + 20, center.y + -30)); | |
case Slot_Right: return ImRect(ImVec2(center.x + 30, center.y + -20), ImVec2(center.x + 50, center.y + 20)); | |
case Slot_Bottom: return ImRect(ImVec2(center.x + -20, center.y + +30), ImVec2(center.x + 20, center.y + 50)); | |
case Slot_Left: return ImRect(ImVec2(center.x + -50, center.y + -20), ImVec2(center.x + -30, center.y + 20)); | |
} | |
} | |
static ImRect getSlotRectOnBorder(ImRect parent_rect, Slot_ dock_slot) | |
{ | |
ImVec2 size(parent_rect.Max.x - parent_rect.Min.x, parent_rect.Max.y - parent_rect.Min.y); | |
ImVec2 center(parent_rect.Min.x + size.x * 0.5f, parent_rect.Min.y + size.y * 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)); | |
} | |
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 nullptr; | |
} | |
bool dockSlots(Dock& dock, Dock* dest_dock, const ImRect& rect, bool on_border) | |
{ | |
ImDrawList* canvas = GetWindowDrawList(); | |
ImU32 text_color = GetColorU32(ImGuiCol_Text); | |
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_TitleBg)); | |
} | |
return false; | |
} | |
void handleDrag(Dock& dock) | |
{ | |
Dock* dest_dock = getDockAt(GetIO().MousePos); | |
Begin("##Overlay", | |
NULL, | |
ImVec2(0, 0), | |
0.f, | |
ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | | |
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | | |
ImGuiWindowFlags_AlwaysAutoResize); | |
ImDrawList* canvas = GetWindowDrawList(); | |
canvas->PushClipRectFullScreen(); | |
ImU32 docked_color = GetColorU32(ImGuiCol_FrameBg); | |
ImVec2 mp = GetIO().MousePos; | |
dock.pos = ImVec2( mp.x - m_drag_offset.x, mp.y - m_drag_offset.y ); | |
if (dest_dock) | |
{ | |
if (dockSlots(dock, dest_dock, ImRect(dest_dock->pos, ImVec2(dest_dock->pos.x + dest_dock->size.x, dest_dock->pos.y + dest_dock->size.y)), false)) | |
{ | |
canvas->PopClipRect(); | |
End(); | |
return; | |
} | |
} | |
if(dockSlots(dock, nullptr, ImRect(ImVec2(0, 0), GetIO().DisplaySize), true)) | |
{ | |
canvas->PopClipRect(); | |
End(); | |
return; | |
} | |
canvas->AddRectFilled(dock.pos, ImVec2(dock.pos.x + dock.size.x, dock.pos.y + dock.size.y), docked_color); | |
canvas->PopClipRect(); | |
if (!IsMouseDown(0)) | |
{ | |
dock.status = Status_Float; | |
dock.setActive(); | |
} | |
End(); | |
} | |
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(nullptr); | |
container->children[0]->setPosSize(container->pos, container->size); | |
} | |
if (container->children[1]) | |
{ | |
container->children[1]->setParent(nullptr); | |
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 = nullptr; | |
dock.prev_tab = dock.next_tab = nullptr; | |
} | |
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.x + max.x) * 0.5f, (min.y + max.y) * 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 size(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, size, 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); | |
float line_height = GetTextLineHeightWithSpacing(); | |
float tab_base; | |
drawTabbarListButton(dock); | |
while (dock_tab) | |
{ | |
SameLine(0, 15); | |
const char* text_end = FindTextDisplayEnd(dock_tab->label); | |
ImVec2 size(CalcTextSize(dock_tab->label, text_end).x, line_height); | |
if (InvisibleButton(dock_tab->label, size)) | |
{ | |
dock_tab->setActive(); | |
} | |
if (IsItemActive() && IsMouseDragging()) | |
{ | |
ImVec2 mp = GetMousePos(); | |
m_drag_offset = ImVec2( mp.x - dock_tab->pos.x, mp.y - dock_tab->pos.y ); | |
doUndock(*dock_tab); | |
dock_tab->status = Status_Dragged; | |
} | |
bool hovered = IsItemHovered(); | |
ImVec2 pos = GetItemRectMin(); | |
if (dock_tab->active && close_button) | |
{ | |
size.x += 16 + GetStyle().ItemSpacing.x; | |
SameLine(); | |
tab_closed = InvisibleButton("close", ImVec2(16, 16)); | |
ImVec2 rmin = GetItemRectMin(); | |
ImVec2 rmax = GetItemRectMax(); | |
ImVec2 center( (rmin.x + rmax.x) * 0.5f, (rmin.y + rmax.y) * 0.5f ); | |
draw_list->AddLine(ImVec2(center.x - 3.5f, center.y - 3.5f), ImVec2(center.x + 3.5f, center.y + 3.5f), text_color); | |
draw_list->AddLine(ImVec2(center.x + 3.5f, center.y - 3.5f), ImVec2(center.x - 3.5f, center.y + 3.5f), text_color); | |
} | |
tab_base = pos.y; | |
draw_list->PathClear(); | |
draw_list->PathLineTo(ImVec2(pos.x+-15, pos.y+size.y)); | |
draw_list->PathBezierCurveTo( | |
ImVec2(pos.x-10, pos.y+size.y), ImVec2(pos.x-5, pos.y+0), ImVec2(pos.x+0, pos.y+0), 10); | |
draw_list->PathLineTo(ImVec2(pos.x+size.x, pos.y+0)); | |
draw_list->PathBezierCurveTo(ImVec2(pos.x+size.x + 5, pos.y+0), | |
ImVec2(pos.x+size.x + 10, pos.y+size.y), | |
ImVec2(pos.x+size.x + 15, pos.y+size.y), | |
10); | |
draw_list->PathFill( | |
hovered ? color_hovered : (dock_tab->active ? color_active : color)); | |
draw_list->AddText(pos, text_color, dock_tab->label, text_end); | |
dock_tab = dock_tab->next_tab; | |
} | |
ImVec2 cp(dock.pos.x, tab_base + line_height); | |
draw_list->AddLine(cp, ImVec2(cp.x+dock.size.x, cp.y+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)); | |
new (container)Dock(); | |
m_docks.push_back(container); | |
container->children[0] = &dest->getFirstTab(); | |
container->children[1] = &dock; | |
container->next_tab = nullptr; | |
container->prev_tab = nullptr; | |
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)); | |
} | |
bool begin(const char* label, bool* opened, ImGuiWindowFlags extra_flags) | |
{ | |
Dock& dock = getDock(label, !opened || *opened); | |
m_end_action = EndAction_None; | |
if (opened && !*opened) | |
{ | |
if (dock.status != Status_Float) | |
{ | |
doUndock(dock); | |
dock.status = Status_Float; | |
} | |
return false; | |
} | |
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, dock.size, -1.0f, ImGuiWindowFlags_NoCollapse | extra_flags); | |
m_end_action = EndAction_End; | |
dock.pos = GetWindowPos(); | |
dock.size = GetWindowSize(); | |
ImGuiState& g = *GImGui; | |
if (g.ActiveId == GetCurrentWindow()->MoveID && g.IO.MouseDown[0]) | |
{ | |
ImVec2 mp = GetMousePos(); | |
m_drag_offset = ImVec2( mp.x - dock.pos.x, mp.y - dock.pos.y ); | |
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)); | |
float tabbar_height = GetTextLineHeightWithSpacing(); | |
if (tabbar(dock.getFirstTab(), opened != nullptr)) | |
{ | |
*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 | extra_flags; | |
bool ret = BeginChild(label, size, true, flags); | |
ImDrawList* draw_list = GetWindowDrawList(); | |
return ret; | |
} | |
void end() | |
{ | |
if (m_end_action == EndAction_End) | |
{ | |
End(); | |
} | |
else if (m_end_action == EndAction_EndChild) | |
{ | |
EndChild(); | |
PopStyleColor(); | |
} | |
m_current = nullptr; | |
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; | |
} | |
#if IMGUI_DOCK_ENABLE_SERIALIZATION | |
void save(Lumix::FS::OsFile& file) | |
{ | |
file << "docks = {\n"; | |
for (int i = 0; i < m_docks.size(); ++i) | |
{ | |
Dock& dock = *m_docks[i]; | |
file << "dock" << (Lumix::uint64)&dock << " = {\n"; | |
file << "index = " << i << ",\n"; | |
file << "label = \"" << dock.label << "\",\n"; | |
file << "x = " << (int)dock.pos.x << ",\n"; | |
file << "y = " << (int)dock.pos.y << ",\n"; | |
file << "size_x = " << (int)dock.size.x << ",\n"; | |
file << "size_y = " << (int)dock.size.y << ",\n"; | |
file << "status = " << (int)dock.status << ",\n"; | |
file << "active = " << (int)dock.active << ",\n"; | |
file << "prev = " << (int)getDockIndex(dock.prev_tab) << ",\n"; | |
file << "next = " << (int)getDockIndex(dock.next_tab) << ",\n"; | |
file << "child0 = " << (int)getDockIndex(dock.children[0]) << ",\n"; | |
file << "child1 = " << (int)getDockIndex(dock.children[1]) << ",\n"; | |
file << "parent = " << (int)getDockIndex(dock.parent) << "\n"; | |
if (i < m_docks.size() - 1) | |
file << "},\n"; | |
else | |
file << "}\n"; | |
} | |
file << "}\n"; | |
} | |
Dock* getDockByIndex(lua_Integer idx) | |
{ | |
return idx < 0 ? nullptr : m_docks[(int)idx]; | |
} | |
void load(lua_State* L) | |
{ | |
for (int i = 0; i < m_docks.size(); ++i) | |
{ | |
m_docks[i]->~Dock(); | |
MemFree(m_docks[i]); | |
} | |
m_docks.clear(); | |
if (lua_getglobal(L, "docks") == LUA_TTABLE) | |
{ | |
lua_pushnil(L); | |
while (lua_next(L, -2) != 0) | |
{ | |
Dock* new_dock = (Dock*)MemAlloc(sizeof(Dock)); | |
m_docks.push_back(new (new_dock) Dock()); | |
lua_pop(L, 1); | |
} | |
} | |
lua_pop(L, 1); | |
int i = 0; | |
if (lua_getglobal(L, "docks") == LUA_TTABLE) | |
{ | |
lua_pushnil(L); | |
while (lua_next(L, -2) != 0) | |
{ | |
if (lua_istable(L, -1)) | |
{ | |
int idx; | |
if (lua_getfield(L, -1, "index") == LUA_TNUMBER) | |
idx = (int)lua_tointeger(L, -1); | |
Dock& dock = *m_docks[idx]; | |
lua_pop(L, 1); | |
if (lua_getfield(L, -1, "label") == LUA_TSTRING) | |
{ | |
dock.label = ImStrdup(lua_tostring(L, -1)); | |
dock.id = ImHash(dock.label, 0); | |
} | |
lua_pop(L, 1); | |
if (lua_getfield(L, -1, "x") == LUA_TNUMBER) | |
dock.pos.x = (float)lua_tonumber(L, -1); | |
if (lua_getfield(L, -2, "y") == LUA_TNUMBER) | |
dock.pos.y = (float)lua_tonumber(L, -1); | |
if (lua_getfield(L, -3, "size_x") == LUA_TNUMBER) | |
dock.size.x = (float)lua_tonumber(L, -1); | |
if (lua_getfield(L, -4, "size_y") == LUA_TNUMBER) | |
dock.size.y = (float)lua_tonumber(L, -1); | |
if (lua_getfield(L, -5, "active") == LUA_TNUMBER) | |
dock.active = lua_tointeger(L, -1) != 0; | |
if (lua_getfield(L, -6, "status") == LUA_TNUMBER) | |
{ | |
dock.status = (Status_)lua_tointeger(L, -1); | |
} | |
lua_pop(L, 6); | |
if (lua_getfield(L, -1, "prev") == LUA_TNUMBER) | |
{ | |
dock.prev_tab = getDockByIndex(lua_tointeger(L, -1)); | |
} | |
if (lua_getfield(L, -2, "next") == LUA_TNUMBER) | |
{ | |
dock.next_tab = getDockByIndex(lua_tointeger(L, -1)); | |
} | |
if (lua_getfield(L, -3, "child0") == LUA_TNUMBER) | |
{ | |
dock.children[0] = getDockByIndex(lua_tointeger(L, -1)); | |
} | |
if (lua_getfield(L, -4, "child1") == LUA_TNUMBER) | |
{ | |
dock.children[1] = getDockByIndex(lua_tointeger(L, -1)); | |
} | |
if (lua_getfield(L, -5, "parent") == LUA_TNUMBER) | |
{ | |
dock.parent = getDockByIndex(lua_tointeger(L, -1)); | |
} | |
lua_pop(L, 5); | |
} | |
lua_pop(L, 1); | |
++i; | |
} | |
} | |
lua_pop(L, 1); | |
} | |
#endif | |
}; | |
static DockContext g_dock; | |
void ShutdownDock() | |
{ | |
for (int i = 0; i < g_dock.m_docks.size(); ++i) | |
{ | |
g_dock.m_docks[i]->~Dock(); | |
MemFree(g_dock.m_docks[i]); | |
} | |
} | |
void RootDock(const ImVec2 & pos, const ImVec2 & size) | |
{ | |
g_dock.rootDock(pos, size); | |
} | |
bool BeginDock(const char* label, bool* opened, ImGuiWindowFlags extra_flags) | |
{ | |
return g_dock.begin(label, opened, extra_flags); | |
} | |
void EndDock() | |
{ | |
g_dock.end(); | |
} | |
#if IMGUI_DOCK_ENABLE_SERIALIZATION | |
void SaveDock(Lumix::FS::OsFile& file) | |
{ | |
g_dock.save(file); | |
} | |
void LoadDock(lua_State* L) | |
{ | |
g_dock.load(L); | |
} | |
#endif | |
} // namespace ImGui |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment