Docking: Added Docking system. (Part 2) (#351)

This commit is contained in:
omar 2018-09-06 21:23:15 +02:00
parent bd5b38e232
commit 9ac1e93aa1

449
imgui.cpp
View File

@ -1067,6 +1067,7 @@ ImGuiIO::ImGuiIO()
DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f);
// Miscellaneous configuration options
ConfigDockingWithKeyMod = true;
#ifdef __APPLE__
ConfigMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag
#else
@ -2345,7 +2346,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
AutoFitChildAxises = 0x00;
AutoPosLastDirection = ImGuiDir_None;
HiddenFramesRegular = HiddenFramesForResize = 0;
SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing;
SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX);
LastFrameActive = -1;
@ -2367,6 +2368,12 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
FocusIdxAllCounter = FocusIdxTabCounter = -1;
FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX;
FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX;
DockNode = DockNodeAsHost = NULL;
DockId = 0;
DockTabItemStatusFlags = 0;
DockOrder = -1;
DockIsActive = DockTabIsVisible = DockTabWantClose = false;
}
ImGuiWindow::~ImGuiWindow()
@ -2656,8 +2663,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
if ((window->DC.ItemFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
return false;
// Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case.
if (window->DC.LastItemId == window->MoveId && window->WriteAccessed)
// Special handling for the dummy item after Begin() which represent the title bar or tab.
// When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
if ((window->DC.LastItemId == window->ID || window->DC.LastItemId == window->MoveId) && window->WriteAccessed)
return false;
return true;
}
@ -3042,16 +3050,17 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
// - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame.
// - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms.
FindHoveredWindow();
//IM_ASSERT(g.HoveredWindow == NULL || g.HoveredWindow == g.MovingWindow || g.HoveredWindow->Viewport == g.MouseViewport);
// Modal windows prevents cursor from hovering behind them.
ImGuiWindow* modal_window = GetFrontMostPopupModal();
if (modal_window)
if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window))
g.HoveredRootWindow = g.HoveredWindow = NULL;
g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
// Disabled mouse?
if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse)
g.HoveredWindow = g.HoveredRootWindow = NULL;
g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
// We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward.
int mouse_earliest_button_down = -1;
@ -3071,7 +3080,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()
// FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02)
const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0;
if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload)
g.HoveredWindow = g.HoveredRootWindow = NULL;
g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
// Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app)
if (g.WantCaptureMouseNextFrame != -1)
@ -3210,9 +3219,16 @@ void ImGui::NewFrame()
g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame);
g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX;
// Undocking
// (needs to be before UpdateMovingWindow so the window is already offset and following the mouse on the detaching frame)
DockContextUpdateUndocking(g.DockContext);
// Find hovered window
// (needs to be before UpdateMovingWindow so we fill HoveredWindowUnderMovingWindow on the mouse release frame)
UpdateHoveredWindowAndCaptureFlags();
// Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering)
UpdateMouseMovingWindow();
UpdateHoveredWindowAndCaptureFlags();
// Background darkening/whitening
if (GetFrontMostPopupModal() != NULL || (g.NavWindowingTarget != NULL && g.NavWindowingHighlightAlpha > 0.0f))
@ -3257,6 +3273,9 @@ void ImGui::NewFrame()
g.CurrentPopupStack.resize(0);
ClosePopupsOverWindow(g.NavWindow);
// Docking
DockContextUpdateDocking(g.DockContext);
// Create implicit window - we will only render it if the user has added something to it.
// We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags.
SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver);
@ -3277,6 +3296,10 @@ void ImGui::Initialize(ImGuiContext* context)
ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
g.SettingsHandlers.push_back(ini_handler);
// Docking Extension
IM_ASSERT(g.DockContext == NULL);
DockContextInitialize(&g);
g.Initialized = true;
}
@ -3297,6 +3320,10 @@ void ImGui::Shutdown(ImGuiContext* context)
if (g.SettingsLoaded && g.IO.IniFilename != NULL)
SaveIniSettingsToDisk(g.IO.IniFilename);
// Shutdown Docking extensions
IM_ASSERT(g.DockContext != NULL);
DockContextShutdown(&g);
// Clear everything else
for (int i = 0; i < g.Windows.Size; i++)
IM_DELETE(g.Windows[i]);
@ -3306,8 +3333,7 @@ void ImGui::Shutdown(ImGuiContext* context)
g.CurrentWindowStack.clear();
g.WindowsById.Clear();
g.NavWindow = NULL;
g.HoveredWindow = NULL;
g.HoveredRootWindow = NULL;
g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL;
g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL;
g.MovingWindow = NULL;
g.ColorModifiers.clear();
@ -3470,6 +3496,14 @@ void ImGui::PopClipRect()
window->ClipRect = window->DrawList->_ClipRectStack.back();
}
static ImGuiWindow* FindFromMostVisibleChildWindow(ImGuiWindow* window)
{
for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--)
if (IsWindowActiveAndVisible(window->DC.ChildWindows[n]))
return FindFromMostVisibleChildWindow(window->DC.ChildWindows[n]);
return window;
}
// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal.
void ImGui::EndFrame()
{
@ -3492,6 +3526,33 @@ void ImGui::EndFrame()
g.CurrentWindow->Active = false;
End();
// CTRL-TAB
const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL);
if (dim_bg_for_window_list)
{
// Choose a draw list that will be front-most across all our children
ImGuiWindow* window = g.NavWindowingTargetAnim;
ImDrawList* draw_list = FindFromMostVisibleChildWindow(window->RootWindow)->DrawList;
draw_list->PushClipRectFullScreen();
// Docking: draw modal whitening background on other nodes of a same dock tree
if (window->RootWindowDockStop->DockIsActive)
if (window->RootWindow != window->RootWindowDockStop)
RenderRectFilledWithHole(draw_list, window->RootWindow->Rect(), window->RootWindowDockStop->Rect(), GetColorU32(ImGuiCol_NavWindowingDimBg, g.DimBgRatio), g.Style.WindowRounding);
// Draw navigation selection/windowing rectangle border
float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding);
ImRect bb = window->Rect();
bb.Expand(g.FontSize);
if (bb.Contains(GetViewportRect())) // If a window fits the entire viewport, adjust its highlight inward
{
bb.Expand(-g.FontSize - 1.0f);
rounding = window->WindowRounding;
}
draw_list->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f);
draw_list->PopClipRect();
}
// Show CTRL+TAB list
if (g.NavWindowingTarget)
NavUpdateWindowingList();
@ -3701,10 +3762,11 @@ static void FindHoveredWindow()
ImGuiContext& g = *GImGui;
ImGuiWindow* hovered_window = NULL;
ImGuiWindow* hovered_window_ignoring_moving_window = NULL;
if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs))
hovered_window = g.MovingWindow;
for (int i = g.Windows.Size - 1; i >= 0 && hovered_window == NULL; i--)
for (int i = g.Windows.Size - 1; i >= 0; i--)
{
ImGuiWindow* window = g.Windows[i];
if (!window->Active || window->Hidden)
@ -3718,14 +3780,16 @@ static void FindHoveredWindow()
{
if (hovered_window == NULL)
hovered_window = window;
if (hovered_window)
if (hovered_window_ignoring_moving_window == NULL && (!g.MovingWindow || window->RootWindow != g.MovingWindow->RootWindow))
hovered_window_ignoring_moving_window = window;
if (hovered_window && hovered_window_ignoring_moving_window)
break;
}
}
g.HoveredWindow = hovered_window;
g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL;
g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window;
}
// Test if mouse cursor is hovering given rectangle
@ -3951,7 +4015,17 @@ bool ImGui::IsItemDeactivatedAfterEdit()
bool ImGui::IsItemFocused()
{
ImGuiContext& g = *GImGui;
return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId;
ImGuiWindow* window = g.CurrentWindow;
if (g.NavId == 0 || g.NavDisableHighlight || g.NavId != window->DC.LastItemId)
return false;
// Special handling for the dummy item after Begin() which represent the title bar or tab.
// When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect the case.
if (window->DC.LastItemId == window->ID && window->WriteAccessed)
return false;
return true;
}
bool ImGui::IsItemClicked(int mouse_button)
@ -4030,7 +4104,7 @@ static bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size
ImGuiContext& g = *GImGui;
ImGuiWindow* parent_window = g.CurrentWindow;
flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow;
flags |= ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_NoDocking;
flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag
// Size
@ -4163,6 +4237,13 @@ static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, b
window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags);
window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags);
window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags);
window->SetWindowDockAllowFlags = enabled ? (window->SetWindowDockAllowFlags | flags) : (window->SetWindowDockAllowFlags & ~flags);
}
ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
{
ImGuiContext& g = *GImGui;
return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id);
}
ImGuiWindow* ImGui::FindWindowByName(const char* name)
@ -4195,6 +4276,8 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
window->Collapsed = settings->Collapsed;
if (ImLengthSqr(settings->Size) > 0.00001f)
size = ImFloor(settings->Size);
window->DockId = settings->DockId;
window->DockOrder = settings->DockOrder;
}
window->Size = window->SizeFull = window->SizeFullAtLastBegin = size;
window->DC.CursorMaxPos = window->Pos; // So first call to CalcSizeContents() doesn't return crazy values
@ -4220,6 +4303,11 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFl
return window;
}
static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window)
{
return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window;
}
static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
{
ImGuiContext& g = *GImGui;
@ -4244,8 +4332,9 @@ static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size)
// Minimum size
if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize)))
{
ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window);
new_size = ImMax(new_size, g.Style.WindowMinSize);
new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
new_size.y = ImMax(new_size.y, window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows
}
return new_size;
}
@ -4476,7 +4565,7 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au
}
// Apply back modified position/size to window
if (size_target.x != FLT_MAX)
if (size_target.x != FLT_MAX && (size_target.x != window->SizeFull.x || size_target.y != window->SizeFull.y))
{
window->SizeFull = size_target;
MarkIniSettingsDirty(window);
@ -4493,9 +4582,11 @@ static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_au
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
{
window->ParentWindow = parent_window;
window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
window->RootWindow = window->RootWindowDockStop = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
window->RootWindow = parent_window->RootWindow;
if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip) && !window->DockIsActive)
window->RootWindowDockStop = parent_window->RootWindowDockStop;
if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
@ -4526,6 +4617,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window = CreateNewWindow(name, size_on_first_use, flags);
}
// Update stored window name when it changes (which can only happen with the "###" operator).
// Only if it is meant to be displayed to the end user in a different place than the title bar (which already always display the 'name' parameter)
bool window_title_visible_elsewhere = (window->DockIsActive);
if (!window_just_created && window_title_visible_elsewhere && strcmp(name, window->Name) != 0)
{
IM_DELETE(window->Name);
window->Name = ImStrdup(name);
}
// Automatically disable manual moving/resizing when NoInputs is set
if (flags & ImGuiWindowFlags_NoInputs)
flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize;
@ -4546,12 +4646,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
flags = window->Flags;
}
// Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
window->HasCloseButton = (p_open != NULL);
// Update the Appearing flag
bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on
const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFramesForResize > 0);
@ -4565,6 +4659,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
if (window->Appearing)
SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true);
// Docking
// (NB: during the frame dock nodes are created, it is possible that (window->DockIsActive == false) even though (window->DockNode->Windows.Size > 1)
IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both
if (g.NextWindowData.DockCond)
SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond);
if (first_begin_of_the_frame && (window->DockId != 0 || window->DockNode != NULL))
{
BeginDocked(window, p_open);
flags = window->Flags;
}
// Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack
ImGuiWindow* parent_window_in_stack = window->DockIsActive ? window->DockNode->HostWindow : g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back();
ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow;
IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow));
// Add to stack
g.CurrentWindowStack.push_back(window);
SetCurrentWindow(window);
@ -4631,6 +4741,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
UpdateWindowParentAndRootLinks(window, flags, parent_window);
window->Active = true;
window->HasCloseButton = (p_open != NULL);
window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX);
window->LastFrameActive = current_frame;
window->IDStack.resize(1);
@ -4666,16 +4777,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
SetCurrentWindow(window);
// Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies)
window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
window->WindowPadding = style.WindowPadding;
if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
if (window->DockIsActive)
window->WindowBorderSize = 0.0f;
else if (flags & ImGuiWindowFlags_ChildWindow)
window->WindowBorderSize = style.ChildBorderSize;
else
window->WindowBorderSize = ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize;
if (!window->DockIsActive && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f)
window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f);
else
window->WindowPadding = style.WindowPadding;
window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x);
window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y;
// Collapse window by double-clicking on title bar
// At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing
if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse))
if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse) && !window->DockIsActive)
{
// We don't use a regular button+id to test for double-click on title bar (mostly due to legacy reason, could be fixed), so verify that we don't have items over the title bar.
ImRect title_bar_rect = window->TitleBarRect();
@ -4719,6 +4836,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
MarkIniSettingsDirty(window);
}
//if (window->DockNode && window->DockIsActive)
// size_full_modified = window->SizeFull;
// Apply minimum/maximum window size constraints and final size
window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull);
window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull;
@ -4768,14 +4888,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->Pos = FindBestWindowPosForPopup(window);
// Clamp position so it stays visible
if (!(flags & ImGuiWindowFlags_ChildWindow))
// Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
{
if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing.
{
ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size;
window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding);
}
ImVec2 clamp_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding);
window->Pos = ImMax(window->Pos + window->Size, clamp_padding) - window->Size;
window->Pos = ImMin(window->Pos, g.IO.DisplaySize - clamp_padding);
}
window->Pos = ImFloor(window->Pos);
@ -4795,8 +4913,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Apply window focus (new and reactivated windows are moved to front)
bool want_focus = false;
if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing))
if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup))
{
if (flags & ImGuiWindowFlags_Popup)
want_focus = true;
else if ((window->DockIsActive || !(flags & ImGuiWindowFlags_ChildWindow)) && !(flags & ImGuiWindowFlags_Tooltip))
want_focus = true;
}
// Handle manual resize: Resize Grips, Borders, Gamepad
int border_held = -1;
@ -4846,7 +4968,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
const float window_rounding = window->WindowRounding;
const float window_border_size = window->WindowBorderSize;
const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow;
const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight);
const bool title_bar_is_highlight = want_focus || (window_to_highlight && (window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight || (window->DockNode && window->DockNode == window_to_highlight->DockNode)));
const ImRect title_bar_rect = window->TitleBarRect();
if (window->Collapsed)
{
@ -4862,16 +4984,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Window background
ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags));
if (g.NextWindowData.BgAlphaCond != 0)
{
bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT);
g.NextWindowData.BgAlphaCond = 0;
}
window->DrawList->AddRectFilled(window->Pos + ImVec2(0, window->TitleBarHeight()), window->Pos + window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot);
// Title bar
// (when docked, DockNode are drawing their own title bar. Individual windows however do NOT set the _NoTitleBar flag,
// in order for their pos/size to be matching their undocking state.)
if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
{
ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg);
if (!(flags & ImGuiWindowFlags_NoTitleBar))
window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top);
}
// Menu bar
if (flags & ImGuiWindowFlags_MenuBar)
@ -4911,7 +5034,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
ImRect border = GetResizeBorderRect(window, border_held, grip_draw_size, 0.0f);
window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size));
}
if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar))
if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize, -1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize);
}
@ -4991,7 +5114,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
}
// Title bar
if (!(flags & ImGuiWindowFlags_NoTitleBar))
if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive)
{
// Close & collapse button are on layer 1 (same as menus) and don't default focus
const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags;
@ -5007,9 +5130,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Close button
if (p_open != NULL)
{
const float pad = style.FramePadding.y;
const float rad = g.FontSize * 0.5f;
if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1))
if (CloseButton(window->GetID("#CLOSE"), ImVec2(window->Pos.x + window->Size.x - style.FramePadding.x - rad, window->Pos.y + style.FramePadding.y + rad), rad + 1))
*p_open = false;
}
@ -5042,6 +5164,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
// Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
window->OuterRectClipped = window->Rect();
if (window->DockIsActive)
window->OuterRectClipped.Min.y += window->TitleBarHeight();
window->OuterRectClipped.ClipWith(window->ClipRect);
// Pressing CTRL+C while holding on a window copy its content to the clipboard
@ -5069,12 +5193,38 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize)));
window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y);
// After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.).
if (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)
{
// Docking: Dragging a dockable window (or any of its child) turns it into a drag and drop source.
// We need to do this _before_ we overwrite window->DC.LastItemId below because BeginAsDockableDragDropSource() also overwrites it.
if ((g.ActiveId == window->MoveId) && ((g.IO.ConfigDockingWithKeyMod && g.IO.KeyShift) || (!g.IO.ConfigDockingWithKeyMod)))
if ((window->RootWindow->Flags & (ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking)) == 0)
BeginAsDockableDragDropSource(window);
// Docking: Any dockable window can act as a target. For dock node hosts we call BeginAsDockableDragDropTarget() in DockNodeUpdate() instead.
if (g.DragDropActive && !(flags & ImGuiWindowFlags_NoDocking))
if (g.MovingWindow == NULL || g.MovingWindow->RootWindow != window)
if ((window == window->RootWindow) && !(window->Flags & ImGuiWindowFlags_DockNodeHost))
BeginAsDockableDragDropTarget(window);
}
// We fill last item data based on Title Bar or Tab, in order for IsItemHovered() and IsItemActive() to be usable after Begin().
// This is useful to allow creating context menus on title bar only, etc.
if (window->DockIsActive)
{
window->DC.LastItemId = window->ID;
window->DC.LastItemStatusFlags = window->DockTabItemStatusFlags;
window->DC.LastItemRect = window->DockTabItemRect;
}
else
{
window->DC.LastItemId = window->MoveId;
window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0;
window->DC.LastItemRect = title_bar_rect;
}
}
if (!(flags & ImGuiWindowFlags_DockNodeHost))
PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true);
// Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused)
@ -5084,12 +5234,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->BeginCount++;
g.NextWindowData.Clear();
if (window->DockIsActive && !window->DockTabIsVisible)
window->HiddenFramesRegular = 1;
if (flags & ImGuiWindowFlags_ChildWindow)
{
// Child window can be out of sight and have "negative" clip windows.
// Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar).
IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0);
IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0 || (window->DockIsActive));
if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0)
if (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y)
window->HiddenFramesRegular = 1;
@ -5104,7 +5256,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
window->HiddenFramesRegular = 1;
// Update the Hidden flag
window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize);
window->Hidden = (window->HiddenFramesRegular > 0) || (window->HiddenFramesForResize > 0);
// Return false if we don't intend to display anything to allow user to perform an early out optimization
window->SkipItems = (window->Collapsed || !window->Active || window->Hidden) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && window->HiddenFramesForResize <= 0;
@ -5135,12 +5287,18 @@ void ImGui::End()
if (window->DC.ColumnsSet != NULL)
EndColumns();
PopClipRect(); // Inner window clip rectangle
if (!(window->Flags & ImGuiWindowFlags_DockNodeHost)) // Pop inner window clip rectangle
PopClipRect();
// Stop logging
if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging
LogFinish();
// Docking: report contents sizes to parent to allow for auto-resize
if (window->DockNode && window->DockTabIsVisible)
if (ImGuiWindow* host_window = window->DockNode->HostWindow) // FIXME-DOCKSPACE
host_window->DC.CursorMaxPos = window->DC.CursorMaxPos + window->WindowPadding - host_window->WindowPadding;
// Pop from window stack
g.CurrentWindowStack.pop_back();
if (window->Flags & ImGuiWindowFlags_Popup)
@ -5192,7 +5350,7 @@ void ImGui::FocusWindow(ImGuiWindow* window)
g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId
g.NavIdIsAlive = false;
g.NavLayer = 0;
//printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL);
//IMGUI_DEBUG_LOG("FocusWindow(\"%s\")\n", window ? window->Name : NULL);
}
// Passing NULL allow to disable keyboard focus
@ -5511,6 +5669,8 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
case ImGuiCol_TabActive: return "TabActive";
case ImGuiCol_TabUnfocused: return "TabUnfocused";
case ImGuiCol_TabUnfocusedActive: return "TabUnfocusedActive";
case ImGuiCol_DockingPreview: return "DockingPreview";
case ImGuiCol_DockingBg: return "DockingBg";
case ImGuiCol_PlotLines: return "PlotLines";
case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered";
case ImGuiCol_PlotHistogram: return "PlotHistogram";
@ -5554,11 +5714,11 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows))
{
case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows:
if (g.HoveredRootWindow != g.CurrentWindow->RootWindow)
if (g.HoveredWindow == NULL || g.HoveredWindow->RootWindowDockStop != g.CurrentWindow->RootWindowDockStop)
return false;
break;
case ImGuiHoveredFlags_RootWindow:
if (g.HoveredWindow != g.CurrentWindow->RootWindow)
if (g.HoveredWindow != g.CurrentWindow->RootWindowDockStop)
return false;
break;
case ImGuiHoveredFlags_ChildWindows:
@ -5591,9 +5751,9 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows))
{
case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows:
return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
return g.NavWindow && g.NavWindow->RootWindowDockStop == g.CurrentWindow->RootWindowDockStop;
case ImGuiFocusedFlags_RootWindow:
return g.NavWindow == g.CurrentWindow->RootWindow;
return g.NavWindow == g.CurrentWindow->RootWindowDockStop;
case ImGuiFocusedFlags_ChildWindows:
return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow);
default:
@ -5601,10 +5761,16 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
}
}
bool ImGui::IsWindowDocked()
{
ImGuiContext& g = *GImGui;
return (g.CurrentWindow->DockIsActive);
}
// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext)
bool ImGui::IsWindowNavFocusable(ImGuiWindow* window)
{
return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
return window->Active && window == window->RootWindowDockStop && !(window->Flags & ImGuiWindowFlags_NoNavFocus);
}
float ImGui::GetWindowWidth()
@ -5777,6 +5943,7 @@ void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pi
g.NextWindowData.PosVal = pos;
g.NextWindowData.PosPivotVal = pivot;
g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always;
g.NextWindowData.PosUndock = true;
}
void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond)
@ -5824,6 +5991,13 @@ void ImGui::SetNextWindowBgAlpha(float alpha)
g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op)
}
void ImGui::SetNextWindowDock(ImGuiID id, ImGuiCond cond)
{
ImGuiContext& g = *GImGui;
g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always;
g.NextWindowData.DockId = id;
}
// In window space (not screen space!)
ImVec2 ImGui::GetContentRegionMax()
{
@ -6280,7 +6454,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_
window->HiddenFramesRegular = 1;
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount);
}
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav;
ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav|ImGuiWindowFlags_NoDocking;
Begin(window_name, NULL, flags | extra_flags);
}
@ -6494,7 +6668,7 @@ bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags)
g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values
return false;
}
return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings);
return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoDocking);
}
bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags)
@ -6992,8 +7166,8 @@ static void NavRestoreLayer(int layer)
g.NavLayer = layer;
if (layer == 0)
g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow);
if (layer == 0 && g.NavWindow->NavLastIds[0] != 0)
ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]);
if (g.NavWindow->NavLastIds[layer] != 0)
ImGui::SetNavIDWithRectRel(g.NavWindow->NavLastIds[layer], layer, g.NavWindow->NavRectRel[layer]);
else
ImGui::NavInitWindow(g.NavWindow, true);
}
@ -7034,7 +7208,10 @@ static ImVec2 ImGui::NavCalcPreferredRefPos()
{
ImGuiContext& g = *GImGui;
if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow)
{
IM_ASSERT(ImGui::IsMousePosValid()); // This will probably trigger at some point, please share your repro!
return ImFloor(g.IO.MousePos);
}
// When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item
const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer];
@ -7215,7 +7392,7 @@ static void ImGui::NavUpdate()
{
ClearActiveID();
}
else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow)
else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow && g.NavWindow != g.NavWindow->RootWindowDockStop)
{
// Exit child window
ImGuiWindow* child_window = g.NavWindow;
@ -7596,7 +7773,7 @@ static void ImGui::NavUpdateWindowing()
}
// Apply final focus
if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow))
if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowDockStop))
{
g.NavDisableHighlight = false;
g.NavDisableMouseHover = true;
@ -7618,7 +7795,10 @@ static void ImGui::NavUpdateWindowing()
{
// Move to parent menu if necessary
ImGuiWindow* new_nav_window = g.NavWindow;
while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
while (new_nav_window->ParentWindow
&& (new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0
&& (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0
&& (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0)
new_nav_window = new_nav_window->ParentWindow;
if (new_nav_window != g.NavWindow)
{
@ -7628,7 +7808,13 @@ static void ImGui::NavUpdateWindowing()
}
g.NavDisableHighlight = false;
g.NavDisableMouseHover = true;
NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0);
// When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. It however persist on docking tab tabs.
const int new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0;
const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL);
if (new_nav_layer == 1 && !preserve_layer_1_nav_id)
g.NavWindow->NavLastIds[1] = 0;
NavRestoreLayer(new_nav_layer);
}
}
@ -7639,6 +7825,8 @@ static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window)
return "(Popup)";
if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0)
return "(Main menu bar)";
if (window->DockNodeAsHost)
return "(Dock node)";
return "(Untitled)";
}
@ -8178,7 +8366,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id)
return false;
ImGuiWindow* window = g.CurrentWindow;
if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow)
return false;
IM_ASSERT(id != 0);
if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId))
@ -8206,7 +8394,7 @@ bool ImGui::BeginDragDropTarget()
ImGuiWindow* window = g.CurrentWindow;
if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect))
return false;
if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow)
if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow)
return false;
const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect;
@ -8663,6 +8851,7 @@ void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size)
}
ImGui::MemFree(buf);
g.SettingsLoaded = true;
DockContextOnLoadSettings();
}
void ImGui::SaveIniSettingsToDisk(const char* ini_filename)
@ -8711,9 +8900,12 @@ static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*,
ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry;
float x, y;
int i;
if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y);
else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize);
else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0);
ImU32 u1;
if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) { settings->Pos = ImVec2(x, y); }
else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) { settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); }
else if (sscanf(line, "Collapsed=%d", &i) == 1) { settings->Collapsed = (i != 0); }
else if (sscanf(line, "DockId=0x%X,%d", &u1, &i) == 2) { settings->DockId = u1; settings->DockOrder = (short)i; }
else if (sscanf(line, "DockId=0x%X", &u1) == 1) { settings->DockId = u1; settings->DockOrder = -1; }
}
static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf)
@ -8721,6 +8913,8 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting
// Gather data from windows that were active during this session
// (if a window wasn't opened in this session we preserve its settings)
ImGuiContext& g = *imgui_ctx;
if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
return;
for (int i = 0; i != g.Windows.Size; i++)
{
ImGuiWindow* window = g.Windows[i];
@ -8736,6 +8930,9 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting
IM_ASSERT(settings->ID == window->ID);
settings->Pos = window->Pos;
settings->Size = window->SizeFull;
IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId);
settings->DockId = window->DockId;
settings->DockOrder = window->DockOrder;
settings->Collapsed = window->Collapsed;
}
@ -8744,8 +8941,6 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting
for (int i = 0; i != g.SettingsWindows.Size; i++)
{
const ImGuiWindowSettings* settings = &g.SettingsWindows[i];
if (settings->Pos.x == FLT_MAX)
continue;
const char* name = settings->Name;
if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID()
name = p;
@ -8753,6 +8948,14 @@ static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSetting
buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y);
buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y);
buf->appendf("Collapsed=%d\n", settings->Collapsed);
if (settings->DockId != 0)
{
// Write DockId as 4 digits if possible. Automatic DockId are small numbers, but full explicit DockSpace() are full ImGuiID range.
if (settings->DockOrder == -1)
buf->appendf("DockId=0x%08X\n", settings->DockId);
else
buf->appendf("DockId=0x%08X,%d\n", settings->DockId, settings->DockOrder);
}
buf->appendf("\n");
}
}
@ -8899,6 +9102,32 @@ void ImGui::ShowMetricsWindow(bool* p_open)
struct Funcs
{
static void NodeDockNode(ImGuiDockNode* node, const char* label)
{
ImGui::SetNextTreeNodeOpen(true, ImGuiCond_Once);
bool open;
if (node->Windows.Size > 0)
open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: %d windows (vis: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", node->Windows.Size, node->VisibleWindow ? node->VisibleWindow->Name : "NULL");
else
open = ImGui::TreeNode((void*)(intptr_t)node->ID, "%s 0x%04X%s: split %s (act: '%s')", label, node->ID, node->IsVisible ? "" : " (hidden)", (node->SplitAxis == ImGuiAxis_X) ? "horizontal" : (node->SplitAxis == ImGuiAxis_Y) ? "vertical" : "n/a", node->VisibleWindow ? node->VisibleWindow->Name : "NULL");
if (open)
{
IM_ASSERT(node->ChildNodes[0] == NULL || node->ChildNodes[0]->ParentNode == node);
IM_ASSERT(node->ChildNodes[1] == NULL || node->ChildNodes[1]->ParentNode == node);
ImGui::BulletText("Pos (%.0f,%.0f), Size (%.0f, %.0f), LastExplicit (%.0f, %.0f)%s%s",
node->Pos.x, node->Pos.y, node->Size.x, node->Size.y,
node->LastExplicitSize.x, node->LastExplicitSize.y,
node->IsExplicitRoot ? ", IsExplicitRoot " : "", node->IsDocumentRoot ? ", IsDocumentRoot " : "");
if (node->ChildNodes[0])
NodeDockNode(node->ChildNodes[0], "Child[0]");
if (node->ChildNodes[1])
NodeDockNode(node->ChildNodes[1], "Child[1]");
if (node->TabBar)
NodeTabBar(node->TabBar);
ImGui::TreePop();
}
}
static void NodeTabBar(ImGuiTabBar* tab_bar)
{
// Previous window list
@ -9027,7 +9256,9 @@ void ImGui::ShowMetricsWindow(bool* p_open)
ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y);
else
ImGui::BulletText("NavRectRel[0]: <None>");
ImGui::BulletText("DockId: 0x%04X, DockOrder: %d, %s: 0x%p", window->DockId, window->DockOrder, window->DockNodeAsHost ? "DockNodeAsHost" : "DockNode", window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode);
if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow");
if (window->RootWindowDockStop != window->RootWindow) NodeWindow(window->RootWindowDockStop, "RootWindowDockStop");
if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow");
if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows");
if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size))
@ -9059,6 +9290,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList");
ImGui::TreePop();
}
if (ImGui::TreeNode("Popups", "Popups (%d)", g.OpenPopupStack.Size))
{
for (int i = 0; i < g.OpenPopupStack.Size; i++)
@ -9068,17 +9300,93 @@ void ImGui::ShowMetricsWindow(bool* p_open)
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Docking"))
{
ImGuiDockContext* ctx = g.DockContext;
static bool show_window_dock_info = false;
ImGui::Checkbox("Ctrl shows window dock info", &show_window_dock_info);
if (ImGui::TreeNode("Dock nodes"))
{
if (ImGui::SmallButton("Clear settings")) { DockContextClearNodes(ctx, true); }
ImGui::SameLine();
if (ImGui::SmallButton("Rebuild all")) { ctx->WantFullRebuild = true; }
for (int n = 0; n < ctx->Nodes.Data.Size; n++)
if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p)
if (node->IsRootNode())
Funcs::NodeDockNode(node, "Node");
ImGui::TreePop();
}
if (ImGui::TreeNode("TabBars", "Tab Bars (%d)", g.TabBars.Data.Size))
{
for (int n = 0; n < g.TabBars.Data.Size; n++)
Funcs::NodeTabBar(g.TabBars.GetByIndex(n));
ImGui::TreePop();
}
if (ImGui::TreeNode("Settings"))
{
if (ImGui::SmallButton("Refresh"))
SaveIniSettingsToMemory();
ImGui::SameLine();
if (ImGui::SmallButton("Save to disk"))
SaveIniSettingsToDisk(g.IO.IniFilename);
ImGui::Separator();
ImGui::Text("Docked Windows:");
for (int n = 0; n < g.SettingsWindows.Size; n++)
if (g.SettingsWindows[n].DockId != 0)
ImGui::BulletText("Window '%s' -> DockId %08X", g.SettingsWindows[n].Name, g.SettingsWindows[n].DockId);
ImGui::Separator();
ImGui::Text("Dock Nodes:");
for (int n = 0; n < ctx->SettingsNodes.Size; n++)
{
ImGuiDockNodeSettings* settings = &ctx->SettingsNodes[n];
const char* selected_tab_name = NULL;
if (settings->SelectedTabID)
{
if (ImGuiWindow* window = FindWindowByID(settings->SelectedTabID))
selected_tab_name = window->Name;
else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedTabID))
selected_tab_name = window_settings->Name;
}
ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentID, settings->SelectedTabID, selected_tab_name ? selected_tab_name : settings->SelectedTabID ? "N/A" : "");
}
ImGui::TreePop();
}
if (g.IO.KeyCtrl && show_window_dock_info)
{
for (int n = 0; n < ctx->Nodes.Data.Size; n++)
if (ImGuiDockNode* node = (ImGuiDockNode*)ctx->Nodes.Data[n].val_p)
{
ImGuiDockNode* root_node = DockNodeGetRootNode(node);
if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(root_node, g.IO.MousePos))
if (hovered_node != node)
continue;
char buf[64] = "";
char* p = buf;
ImDrawList* overlay_draw_list = GetOverlayDrawList();
p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsDocumentRoot ? " *DocRoot*" : "");
p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Size: (%.0f, %.0f)\n", node->Size.x, node->Size.y);
p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "Explicit: (%.0f, %.0f)\n", node->LastExplicitSize.x, node->LastExplicitSize.y);
int depth = DockNodeGetDepth(node);
overlay_draw_list->AddRect(node->Pos + ImVec2(3,3) * (float)depth, node->Pos + node->Size - ImVec2(3,3) * (float)depth, IM_COL32(200, 100, 100, 255));
ImVec2 pos = node->Pos + ImVec2(3,3) * (float)depth;
overlay_draw_list->AddRectFilled(pos - ImVec2(1, 1), pos + CalcTextSize(buf) + ImVec2(1, 1), IM_COL32(200, 100, 100, 255));
overlay_draw_list->AddText(NULL, 0.0f, pos, IM_COL32(255, 255, 255, 255), buf);
}
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Internal state"))
{
const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT);
ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL");
ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL");
ImGui::Text("HoveredWindowUnderMovingWindow: '%s'", g.HoveredWindowUnderMovingWindow ? g.HoveredWindowUnderMovingWindow->Name : "NULL");
ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not
ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]);
ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
@ -9094,7 +9402,6 @@ void ImGui::ShowMetricsWindow(bool* p_open)
ImGui::TreePop();
}
if (g.IO.KeyCtrl && show_window_begin_order)
{
for (int n = 0; n < g.Windows.Size; n++)