From 5eac518b4ffe8570b9d4a6965668a348879cba27 Mon Sep 17 00:00:00 2001 From: omar Date: Wed, 29 Jul 2020 14:44:58 +0200 Subject: [PATCH] Internals: backport window HitTestHole code from docking branch + RenderRectFilledWithHole() helper. (#1512, #3368) --- imgui.cpp | 6022 +++------------------------------------------- imgui_draw.cpp | 18 - imgui_internal.h | 319 +-- 3 files changed, 374 insertions(+), 5985 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e5b61a48b996..59845af72ef7 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -75,8 +75,6 @@ CODE // [SECTION] DRAG AND DROP // [SECTION] LOGGING/CAPTURING // [SECTION] SETTINGS -// [SECTION] VIEWPORTS, PLATFORM WINDOWS -// [SECTION] DOCKING // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUG WINDOW @@ -374,15 +372,6 @@ CODE When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. - (Docking/Viewport Branch) - - 2020/XX/XX (1.XX) - when multi-viewports are enabled, all positions will be in your natural OS coordinates space. It means that: - - reference to hard-coded positions such as in SetNextWindowPos(ImVec2(0,0)) are probably not what you want anymore. - you may use GetMainViewport()->Pos to offset hard-coded positions, e.g. SetNextWindowPos(GetMainViewport()->Pos) - - likewise io.MousePos and GetMousePos() will use OS coordinates. - If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos. - - 2020/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - - - 2020/06/23 (1.77) - removed BeginPopupContextWindow(const char*, int mouse_button, bool also_over_items) in favor of BeginPopupContextWindow(const char*, ImGuiPopupFlags flags) with ImGuiPopupFlags_NoOverItems. - 2020/06/15 (1.77) - renamed OpenPopupOnItemClick() to OpenPopupContextItem(). Kept inline redirection function (will obsolete). - 2020/06/15 (1.77) - removed CalcItemRectClosestPoint() entry point which was made obsolete and asserting in December 2017. @@ -809,10 +798,6 @@ static const float WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS = 4.0f; // Exten static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. -// Docking -static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport. -static const float DOCKING_SPLITTER_SIZE = 2.0f; - //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- @@ -825,6 +810,8 @@ static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* wind static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); static void AddWindowToSortBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); +static ImRect GetViewportRect(); + // Settings static void WindowSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); static void* WindowSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); @@ -835,6 +822,7 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext*, ImGuiSetti // Platform Dependents default implementation for IO functions static const char* GetClipboardTextFn_DefaultImpl(void* user_data); static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); namespace ImGui { @@ -866,23 +854,8 @@ static void UpdateTabFocus(); static void UpdateDebugToolItemPicker(); static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); -static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); +static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); -static void EndFrameDrawDimmedBackgrounds(); - -// Viewports -const ImGuiID IMGUI_VIEWPORT_DEFAULT_ID = 0x11111111; // Using an arbitrary constant instead of e.g. ImHashStr("ViewportDefault", 0); so it's easier to spot in the debugger. The exact value doesn't matter. -static ImGuiViewportP* AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& platform_pos, const ImVec2& size, ImGuiViewportFlags flags); -static void UpdateViewportsNewFrame(); -static void UpdateViewportsEndFrame(); -static void UpdateSelectWindowViewport(ImGuiWindow* window); -static bool UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* host_viewport); -static bool UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window); -static void SetCurrentViewport(ImGuiWindow* window, ImGuiViewportP* viewport); -static bool GetWindowAlwaysWantOwnViewport(ImGuiWindow* window); -static int FindPlatformMonitorForPos(const ImVec2& pos); -static int FindPlatformMonitorForRect(const ImRect& r); -static void UpdateViewportPlatformMonitor(ImGuiViewportP* viewport); } @@ -1027,18 +1000,6 @@ ImGuiIO::ImGuiIO() FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - // Docking options (when ImGuiConfigFlags_DockingEnable is set) - ConfigDockingNoSplit = false; - ConfigDockingWithShift = false; - ConfigDockingAlwaysTabBar = false; - ConfigDockingTransparentPayload = false; - - // Viewport options (when ImGuiConfigFlags_ViewportsEnable is set) - ConfigViewportsNoAutoMerge = false; - ConfigViewportsNoTaskBarIcon = false; - ConfigViewportsNoDecoration = true; - ConfigViewportsNoDefaultParent = false; - // Miscellaneous options MouseDrawCursor = false; #ifdef __APPLE__ @@ -1057,6 +1018,8 @@ ImGuiIO::ImGuiIO() GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; ClipboardUserData = NULL; + ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; + ImeWindowHandle = NULL; #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS RenderDrawListsFn = NULL; @@ -2489,8 +2452,6 @@ 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_DockingEmptyBg: return "DockingEmptyBg"; case ImGuiCol_PlotLines: return "PlotLines"; case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; case ImGuiCol_PlotHistogram: return "PlotHistogram"; @@ -2506,7 +2467,6 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx) return "Unknown"; } - //----------------------------------------------------------------------------- // [SECTION] RENDER HELPERS // Some of those (internal) functions are currently quite a legacy mess - their signature and behavior will change, @@ -2764,11 +2724,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) Name = ImStrdup(name); ID = ImHashStr(name); IDStack.push_back(ID); - Flags = FlagsPreviousFrame = ImGuiWindowFlags_None; - Viewport = NULL; - ViewportId = 0; - ViewportAllowPlatformMonitorExtend = -1; - ViewportPos = ImVec2(FLT_MAX, FLT_MAX); + Flags = ImGuiWindowFlags_None; Pos = ImVec2(0.0f, 0.0f); Size = SizeFull = ImVec2(0.0f, 0.0f); ContentSize = ContentSizeExplicit = ImVec2(0.0f, 0.0f); @@ -2783,7 +2739,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); ScrollbarSizes = ImVec2(0.0f, 0.0f); ScrollbarX = ScrollbarY = false; - ViewportOwned = false; Active = WasActive = false; WriteAccessed = false; Collapsed = false; @@ -2803,23 +2758,21 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) AutoFitOnlyGrows = false; AutoPosLastDirection = ImGuiDir_None; HiddenFramesCanSkipItems = HiddenFramesCannotSkipItems = 0; - SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = SetWindowDockAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; + SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); InnerRect = ImRect(0.0f, 0.0f, 0.0f, 0.0f); // Clear so the InnerRect.GetSize() code in Begin() doesn't lead to overflow even if the result isn't used. LastFrameActive = -1; - LastFrameJustFocused = -1; LastTimeActive = -1.0f; ItemWidthDefault = 0.0f; - FontWindowScale = FontDpiScale = 1.0f; + FontWindowScale = 1.0f; SettingsOffset = -1; DrawList = &DrawListInst; DrawList->_OwnerName = Name; ParentWindow = NULL; RootWindow = NULL; - RootWindowDockStop = NULL; RootWindowForTitleBarHighlight = NULL; RootWindowForNav = NULL; @@ -2829,12 +2782,6 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) MemoryCompacted = false; MemoryDrawListIdxCapacity = MemoryDrawListVtxCapacity = 0; - - DockNode = DockNodeAsHost = NULL; - DockId = 0; - DockTabItemStatusFlags = ImGuiItemStatusFlags_None; - DockOrder = -1; - DockIsActive = DockTabIsVisible = DockTabWantClose = false; } ImGuiWindow::~ImGuiWindow() @@ -3052,12 +2999,6 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) return false; } - - // Filter by viewport - if (window->Viewport != g.MouseViewport) - if (g.MovingWindow == NULL || window->RootWindow != g.MovingWindow->RootWindow) - return false; - return true; } @@ -3100,7 +3041,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // 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) + if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) return false; return true; } @@ -3306,17 +3247,11 @@ ImGuiIO& ImGui::GetIO() return GImGui->IO; } -ImGuiPlatformIO& ImGui::GetPlatformIO() -{ - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); - return GImGui->PlatformIO; -} - // Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame() ImDrawData* ImGui::GetDrawData() { ImGuiContext& g = *GImGui; - return g.Viewports[0]->DrawDataP.Valid ? &g.Viewports[0]->DrawDataP : NULL; + return g.DrawData.Valid ? &g.DrawData : NULL; } double ImGui::GetTime() @@ -3329,50 +3264,14 @@ int ImGui::GetFrameCount() return GImGui->FrameCount; } -static ImDrawList* GetViewportDrawList(ImGuiViewportP* viewport, size_t drawlist_no, const char* drawlist_name) -{ - // Create the draw list on demand, because they are not frequently used for all viewports - ImGuiContext& g = *GImGui; - IM_ASSERT(drawlist_no < IM_ARRAYSIZE(viewport->DrawLists)); - ImDrawList* draw_list = viewport->DrawLists[drawlist_no]; - if (draw_list == NULL) - { - draw_list = IM_NEW(ImDrawList)(&g.DrawListSharedData); - draw_list->_OwnerName = drawlist_name; - viewport->DrawLists[drawlist_no] = draw_list; - } - - // Our ImDrawList system requires that there is always a command - if (viewport->LastFrameDrawLists[drawlist_no] != g.FrameCount) - { - draw_list->_ResetForNewFrame(); - draw_list->PushTextureID(g.IO.Fonts->TexID); - draw_list->PushClipRect(viewport->Pos, viewport->Pos + viewport->Size, false); - viewport->LastFrameDrawLists[drawlist_no] = g.FrameCount; - } - return draw_list; -} - -ImDrawList* ImGui::GetBackgroundDrawList(ImGuiViewport* viewport) -{ - return GetViewportDrawList((ImGuiViewportP*)viewport, 0, "##Background"); -} - ImDrawList* ImGui::GetBackgroundDrawList() { - ImGuiWindow* window = GImGui->CurrentWindow; - return GetBackgroundDrawList(window->Viewport); -} - -ImDrawList* ImGui::GetForegroundDrawList(ImGuiViewport* viewport) -{ - return GetViewportDrawList((ImGuiViewportP*)viewport, 1, "##Foreground"); + return &GImGui->BackgroundDrawList; } ImDrawList* ImGui::GetForegroundDrawList() { - ImGuiWindow* window = GImGui->CurrentWindow; - return GetForegroundDrawList(window->Viewport); + return &GImGui->ForegroundDrawList; } ImDrawListSharedData* ImGui::GetDrawListSharedData() @@ -3394,45 +3293,10 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) bool can_move_window = true; if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) can_move_window = false; - if (ImGuiDockNode* node = window->DockNodeAsHost) - if (node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove)) - can_move_window = false; if (can_move_window) g.MovingWindow = window; } -// We use 'undock_floating_node == false' when dragging from title bar to allow moving groups of floating nodes without undocking them. -// - undock_floating_node == true: when dragging from a floating node within a hierarchy, always undock the node. -// - undock_floating_node == false: when dragging from a floating node within a hierarchy, move root window. -void ImGui::StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node) -{ - ImGuiContext& g = *GImGui; - bool can_undock_node = false; - if (node != NULL && node->VisibleWindow && (node->VisibleWindow->Flags & ImGuiWindowFlags_NoMove) == 0) - { - // Can undock if: - // - part of a floating node hierarchy with more than one visible node (if only one is visible, we'll just move the whole hierarchy) - // - part of a dockspace node hierarchy (trivia: undocking from a fixed/central node will create a new node and copy windows) - ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (root_node->OnlyNodeWithWindows != node || root_node->CentralNode != NULL) // -V1051 PVS-Studio thinks node should be root_node and is wrong about that. - if (undock_floating_node || root_node->IsDockSpace()) - can_undock_node = true; - } - - const bool clicked = IsMouseClicked(0); - const bool dragging = IsMouseDragging(0, g.IO.MouseDragThreshold * 1.70f); - if (can_undock_node && dragging) - { - DockContextQueueUndockNode(&g, node); - g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - node->Pos; - } - else if (!can_undock_node && (clicked || dragging) && g.MovingWindow != window) - { - StartMouseMovingWindow(window); - g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; - } -} - // Handle mouse moving window // Note: moving window with the navigation keys (Square + d-pad / CTRL+TAB + Arrows) are processed in NavUpdateWindowing() // FIXME: We don't have strong guarantee that g.MovingWindow stay synched with g.ActiveId == g.MovingWindow->MoveId. @@ -3455,25 +3319,11 @@ void ImGui::UpdateMouseMovingWindowNewFrame() { MarkIniSettingsDirty(moving_window); SetWindowPos(moving_window, pos, ImGuiCond_Always); - if (moving_window->ViewportOwned) // Synchronize viewport immediately because some overlays may relies on clipping rectangle before we Begin() into the window. - moving_window->Viewport->Pos = pos; } FocusWindow(g.MovingWindow); } else { - // Try to merge the window back into the main viewport. - // This works because MouseViewport should be != MovingWindow->Viewport on release (as per code in UpdateViewports) - if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) - UpdateTryMergeWindowIntoHostViewport(moving_window, g.MouseViewport); - - // Restore the mouse viewport so that we don't hover the viewport _under_ the moved window during the frame we released the mouse button. - if (!IsDragDropPayloadBeingAccepted()) - g.MouseViewport = moving_window->Viewport; - - // Clear the NoInput window flag set by the Viewport system - moving_window->Viewport->Flags &= ~ImGuiViewportFlags_NoInputs; - ClearActiveID(); g.MovingWindow = NULL; } @@ -3502,22 +3352,20 @@ void ImGui::UpdateMouseMovingWindowEndFrame() if (g.NavWindow && g.NavWindow->Appearing) return; - // Click on void to focus window and start moving - // (after we're done with all our widgets, so e.g. clicking on docking tab-bar which have set HoveredId already and not get us here!) + // Click on empty space to focus window and start moving (after we're done with all our widgets) if (g.IO.MouseClicked[0]) { // Handle the edge case of a popup being closed while clicking in its empty space. // If we try to focus it, FocusWindow() > ClosePopupsOverWindow() will accidentally close any parent popups because they are not linked together any more. - ImGuiWindow* root_window = g.HoveredWindow ? g.HoveredWindow->RootWindowDockStop : NULL; + ImGuiWindow* root_window = g.HoveredRootWindow; const bool is_closed_popup = root_window && (root_window->Flags & ImGuiWindowFlags_Popup) && !IsPopupOpen(root_window->PopupId, ImGuiPopupFlags_AnyPopupLevel); if (root_window != NULL && !is_closed_popup) { StartMouseMovingWindow(g.HoveredWindow); - if (g.IO.ConfigWindowsMoveFromTitleBarOnly) - if (!(root_window->Flags & ImGuiWindowFlags_NoTitleBar) || root_window->DockIsActive) - if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) - g.MovingWindow = NULL; + if (g.IO.ConfigWindowsMoveFromTitleBarOnly && !(root_window->Flags & ImGuiWindowFlags_NoTitleBar)) + if (!root_window->TitleBarRect().Contains(g.IO.MouseClickedPos[0])) + g.MovingWindow = NULL; } else if (root_window == NULL && g.NavWindow != NULL && GetTopMostPopupModal() == NULL) { @@ -3549,28 +3397,6 @@ void ImGui::UpdateMouseMovingWindowEndFrame() } } -static void TranslateWindow(ImGuiWindow* window, const ImVec2& delta) -{ - window->Pos += delta; - window->ClipRect.Translate(delta); - window->OuterRectClipped.Translate(delta); - window->InnerRect.Translate(delta); - window->DC.CursorPos += delta; - window->DC.CursorStartPos += delta; - window->DC.CursorMaxPos += delta; - window->DC.LastItemRect.Translate(delta); - window->DC.LastItemDisplayRect.Translate(delta); -} - -static void ScaleWindow(ImGuiWindow* window, float scale) -{ - ImVec2 origin = window->Viewport->Pos; - window->Pos = ImFloor((window->Pos - origin) * scale + origin); - window->Size = ImFloor(window->Size * scale); - window->SizeFull = ImFloor(window->SizeFull * scale); - window->ContentSize = ImFloor(window->ContentSize * scale); -} - static bool IsWindowActiveAndVisible(ImGuiWindow* window) { return (window->Active) && (!window->Hidden); @@ -3765,17 +3591,16 @@ 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 = GetTopMostPopupModal(); if (modal_window) if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) - g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; + g.HoveredRootWindow = g.HoveredWindow = NULL; // Disabled mouse? if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) - g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = 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; @@ -3795,7 +3620,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 = g.HoveredWindowUnderMovingWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = NULL; // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to Dear ImGui + app) if (g.WantCaptureMouseNextFrame != -1) @@ -3836,9 +3661,7 @@ void ImGui::NewFrame() #endif // Check and assert for various common IO and Configuration mistakes - g.ConfigFlagsLastFrame = g.ConfigFlagsCurrFrame; ErrorCheckNewFrameSanityChecks(); - g.ConfigFlagsCurrFrame = g.IO.ConfigFlags; // Load settings on first frame, save settings when modified (after a delay) UpdateSettings(); @@ -3856,17 +3679,11 @@ 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; - UpdateViewportsNewFrame(); - // Setup current font and draw list shared data - // FIXME-VIEWPORT: the concept of a single ClipRectFullscreen is not ideal! g.IO.Fonts->Locked = true; SetCurrentFont(GetDefaultFont()); IM_ASSERT(g.Font->IsLoaded()); - ImRect virtual_space(FLT_MAX, FLT_MAX, -FLT_MAX, -FLT_MAX); - for (int n = 0; n < g.Viewports.Size; n++) - virtual_space.Add(g.Viewports[n]->GetMainRect()); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(virtual_space.Min.x, virtual_space.Min.y, virtual_space.Max.x, virtual_space.Max.y); + g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; g.DrawListSharedData.SetCircleSegmentMaxError(g.Style.CircleSegmentMaxError); g.DrawListSharedData.InitialFlags = ImDrawListFlags_None; @@ -3879,13 +3696,16 @@ void ImGui::NewFrame() if (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasVtxOffset) g.DrawListSharedData.InitialFlags |= ImDrawListFlags_AllowVtxOffset; + g.BackgroundDrawList._ResetForNewFrame(); + g.BackgroundDrawList.PushTextureID(g.IO.Fonts->TexID); + g.BackgroundDrawList.PushClipRectFullScreen(); + + g.ForegroundDrawList._ResetForNewFrame(); + g.ForegroundDrawList.PushTextureID(g.IO.Fonts->TexID); + g.ForegroundDrawList.PushClipRectFullScreen(); + // Mark rendering data as invalid to prevent user who may have a handle on it to use it. - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawData = NULL; - viewport->DrawDataP.Clear(); - } + g.DrawData.Clear(); // Drag and drop keep the source ID alive so even if the source disappear our state is consistent if (g.DragDropActive && g.DragDropPayload.SourceId == g.ActiveId) @@ -3947,10 +3767,6 @@ void ImGui::NewFrame() // Update mouse input state UpdateMouseInputs(); - // Undocking - // (needs to be before UpdateMouseMovingWindowNewFrame so the window is already offset and following the mouse on the detaching frame) - DockContextUpdateUndocking(&g); - // Find hovered window // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) UpdateHoveredWindowAndCaptureFlags(); @@ -3967,7 +3783,6 @@ void ImGui::NewFrame() g.MouseCursor = ImGuiMouseCursor_Arrow; g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default - g.PlatformImePosViewport = NULL; // Mouse wheel scrolling, scale UpdateMouseWheel(); @@ -4001,9 +3816,6 @@ void ImGui::NewFrame() g.BeginPopupStack.resize(0); ClosePopupsOverWindow(g.NavWindow, false); - // Docking - DockContextUpdateDocking(&g); - // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. UpdateDebugToolItemPicker(); @@ -4077,17 +3889,6 @@ void ImGui::Initialize(ImGuiContext* context) #endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK - // Create default viewport - ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); - viewport->ID = IMGUI_VIEWPORT_DEFAULT_ID; - viewport->Idx = 0; - viewport->PlatformWindowCreated = true; - g.Viewports.push_back(viewport); - g.PlatformIO.MainViewport = g.Viewports[0]; // Make it accessible in public-facing GetPlatformIO() immediately (before the first call to EndFrame) - g.PlatformIO.Viewports.push_back(g.Viewports[0]); - - // Extensions - DockContextInitialize(&g); #endif // #ifdef IMGUI_HAS_DOCK g.Initialized = true; @@ -4118,15 +3919,6 @@ void ImGui::Shutdown(ImGuiContext* context) SetCurrentContext(backup_context); } - // Destroy platform windows - ImGuiContext* backup_context = ImGui::GetCurrentContext(); - SetCurrentContext(context); - DestroyPlatformWindows(); - SetCurrentContext(backup_context); - - // Shutdown extensions - DockContextShutdown(&g); - // Notify hooked test engine, if any #ifdef IMGUI_ENABLE_TEST_ENGINE ImGuiTestEngineHook_Shutdown(context); @@ -4142,7 +3934,7 @@ void ImGui::Shutdown(ImGuiContext* context) g.CurrentWindowStack.clear(); g.WindowsById.Clear(); g.NavWindow = NULL; - g.HoveredWindow = g.HoveredRootWindow = g.HoveredWindowUnderMovingWindow = NULL; + g.HoveredWindow = g.HoveredRootWindow = NULL; g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; g.MovingWindow = NULL; g.ColorModifiers.clear(); @@ -4150,11 +3942,9 @@ void ImGui::Shutdown(ImGuiContext* context) g.FontStack.clear(); g.OpenPopupStack.clear(); g.BeginPopupStack.clear(); - - g.CurrentViewport = g.MouseViewport = g.MouseLastHoveredViewport = NULL; - for (int i = 0; i < g.Viewports.Size; i++) - IM_DELETE(g.Viewports[i]); - g.Viewports.clear(); + g.DrawDataBuilder.ClearFreeMemory(); + g.BackgroundDrawList._ClearFreeMemory(); + g.ForegroundDrawList._ClearFreeMemory(); g.TabBars.Clear(); g.CurrentTabBarStack.clear(); @@ -4245,24 +4035,25 @@ static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* d out_list->push_back(draw_list); } -static void AddWindowToDrawData(ImGuiWindow* window, int layer) +static void AddWindowToDrawData(ImVector* out_render_list, ImGuiWindow* window) { ImGuiContext& g = *GImGui; g.IO.MetricsRenderWindows++; - AddDrawListToDrawData(&window->Viewport->DrawDataBuilder.Layers[layer], window->DrawList); + AddDrawListToDrawData(out_render_list, window->DrawList); for (int i = 0; i < window->DC.ChildWindows.Size; i++) { ImGuiWindow* child = window->DC.ChildWindows[i]; - if (IsWindowActiveAndVisible(child)) // Clipped children may have been marked not active - AddWindowToDrawData(child, layer); + if (IsWindowActiveAndVisible(child)) // clipped children may have been marked not active + AddWindowToDrawData(out_render_list, child); } } // Layer is locked for the root window, however child windows may use a different viewport (e.g. extruding menu) static void AddRootWindowToDrawData(ImGuiWindow* window) { + ImGuiContext& g = *GImGui; int layer = (window->Flags & ImGuiWindowFlags_Tooltip) ? 1 : 0; - AddWindowToDrawData(window, layer); + AddWindowToDrawData(&g.DrawDataBuilder.Layers[layer], window); } void ImDrawDataBuilder::FlattenIntoSingleLayer() @@ -4283,25 +4074,16 @@ void ImDrawDataBuilder::FlattenIntoSingleLayer() } } -static void SetupViewportDrawData(ImGuiViewportP* viewport, ImVector* draw_lists) +static void SetupDrawData(ImVector* draw_lists, ImDrawData* draw_data) { - // When minimized, we report draw_data->DisplaySize as zero to be consistent with non-viewport mode, - // and to allow applications/back-ends to easily skip rendering. - // FIXME: Note that we however do NOT attempt to report "zero drawlist / vertices" into the ImDrawData structure. - // This is because the work has been done already, and its wasted! We should fix that and add optimizations for - // it earlier in the pipeline, rather than pretend to hide the data at the end of the pipeline. - const bool is_minimized = (viewport->Flags & ImGuiViewportFlags_Minimized) != 0; - - ImDrawData* draw_data = &viewport->DrawDataP; - viewport->DrawData = draw_data; // Make publicly accessible + ImGuiIO& io = ImGui::GetIO(); draw_data->Valid = true; draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; draw_data->CmdListsCount = draw_lists->Size; draw_data->TotalVtxCount = draw_data->TotalIdxCount = 0; - draw_data->DisplayPos = viewport->Pos; - draw_data->DisplaySize = is_minimized ? ImVec2(0.0f, 0.0f) : viewport->Size; - draw_data->FramebufferScale = ImGui::GetIO().DisplayFramebufferScale; // FIXME-VIEWPORT: This may vary on a per-monitor/viewport basis? - draw_data->OwnerViewport = viewport; + draw_data->DisplayPos = ImVec2(0.0f, 0.0f); + draw_data->DisplaySize = io.DisplaySize; + draw_data->FramebufferScale = io.DisplayFramebufferScale; for (int n = 0; n < draw_lists->Size; n++) { draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; @@ -4329,64 +4111,6 @@ void ImGui::PopClipRect() window->ClipRect = window->DrawList->_ClipRectStack.back(); } -static ImGuiWindow* FindFrontMostVisibleChildWindow(ImGuiWindow* window) -{ - for (int n = window->DC.ChildWindows.Size - 1; n >= 0; n--) - if (IsWindowActiveAndVisible(window->DC.ChildWindows[n])) - return FindFrontMostVisibleChildWindow(window->DC.ChildWindows[n]); - return window; -} - -static void ImGui::EndFrameDrawDimmedBackgrounds() -{ - ImGuiContext& g = *GImGui; - - // Draw modal whitening background on _other_ viewports than the one the modal is one - ImGuiWindow* modal_window = GetTopMostPopupModal(); - const bool dim_bg_for_modal = (modal_window != NULL); - const bool dim_bg_for_window_list = (g.NavWindowingTargetAnim != NULL); - if (dim_bg_for_modal || dim_bg_for_window_list) - for (int viewport_n = 0; viewport_n < g.Viewports.Size; viewport_n++) - { - ImGuiViewportP* viewport = g.Viewports[viewport_n]; - if (modal_window && viewport == modal_window->Viewport) - continue; - if (g.NavWindowingListWindow && viewport == g.NavWindowingListWindow->Viewport) - continue; - if (g.NavWindowingTargetAnim && viewport == g.NavWindowingTargetAnim->Viewport) - continue; - ImDrawList* draw_list = GetForegroundDrawList(viewport); - const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); - draw_list->AddRectFilled(viewport->Pos, viewport->Pos + viewport->Size, dim_bg_col); - } - - // Draw modal whitening background between CTRL-TAB list - 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 = FindFrontMostVisibleChildWindow(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(window->Viewport->GetMainRect())) // 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(); - } -} - // 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() { @@ -4401,13 +4125,11 @@ void ImGui::EndFrame() ErrorCheckEndFrameSanityChecks(); // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if (g.PlatformIO.Platform_SetImeInputPos && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImePos - g.PlatformImeLastPos) > 0.0001f)) - if (g.PlatformImePosViewport && g.PlatformImePosViewport->PlatformWindowCreated) - { - g.PlatformIO.Platform_SetImeInputPos(g.PlatformImePosViewport, g.PlatformImePos); - g.PlatformImeLastPos = g.PlatformImePos; - g.PlatformImePosViewport = NULL; - } + if (g.IO.ImeSetInputScreenPosFn && (g.PlatformImeLastPos.x == FLT_MAX || ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f)) + { + g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); + g.PlatformImeLastPos = g.PlatformImePos; + } // Hide implicit/fallback "Debug" window if it hasn't been used g.WithinFrameScopeWithImplicitWindow = false; @@ -4415,14 +4137,9 @@ void ImGui::EndFrame() g.CurrentWindow->Active = false; End(); - // Draw modal whitening background on _other_ viewports than the one the modal is one - EndFrameDrawDimmedBackgrounds(); - // Update navigation: CTRL+Tab, wrap-around requests NavEndFrame(); - SetCurrentViewport(NULL, NULL); - // Drag and Drop: Elapse payload (if delivered, or if source stops being submitted) if (g.DragDropActive) { @@ -4447,9 +4164,6 @@ void ImGui::EndFrame() // Initiate moving window + handle left-click and right-click focus UpdateMouseMovingWindowEndFrame(); - // Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) - UpdateViewportsEndFrame(); - // Sort the window list so that all child windows are after their parent // We cannot do that on FocusWindow() because children may not exist yet g.WindowsTempSortBuffer.resize(0); @@ -4485,15 +4199,11 @@ void ImGui::Render() EndFrame(); g.FrameCountRendered = g.FrameCount; g.IO.MetricsRenderWindows = 0; + g.DrawDataBuilder.Clear(); - // Add background ImDrawList (for each active viewport) - for (int n = 0; n != g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.Clear(); - if (viewport->DrawLists[0] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetBackgroundDrawList(viewport)); - } + // Add background ImDrawList + if (!g.BackgroundDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.BackgroundDrawList); // Add ImDrawList to render ImGuiWindow* windows_to_render_top_most[2]; @@ -4508,40 +4218,25 @@ void ImGui::Render() for (int n = 0; n < IM_ARRAYSIZE(windows_to_render_top_most); n++) if (windows_to_render_top_most[n] && IsWindowActiveAndVisible(windows_to_render_top_most[n])) // NavWindowingTarget is always temporarily displayed as the top-most window AddRootWindowToDrawData(windows_to_render_top_most[n]); + g.DrawDataBuilder.FlattenIntoSingleLayer(); - ImVec2 mouse_cursor_offset, mouse_cursor_size, mouse_cursor_uv[4]; - if (g.IO.MouseDrawCursor && g.MouseCursor != ImGuiMouseCursor_None) - g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &mouse_cursor_offset, &mouse_cursor_size, &mouse_cursor_uv[0], &mouse_cursor_uv[2]); - - // Setup ImDrawData structures for end-user - g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = 0; - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->DrawDataBuilder.FlattenIntoSingleLayer(); - - // Draw software mouse cursor if requested by io.MouseDrawCursor flag - // (note we scale cursor by current viewport/monitor, however Windows 10 for its own hardware cursor seems to be using a different scale factor) - if (mouse_cursor_size.x > 0.0f && mouse_cursor_size.y > 0.0f) - { - float scale = g.Style.MouseCursorScale * viewport->DpiScale; - if (viewport->GetMainRect().Overlaps(ImRect(g.IO.MousePos, g.IO.MousePos + ImVec2(mouse_cursor_size.x + 2, mouse_cursor_size.y + 2) * scale))) - RenderMouseCursor(GetForegroundDrawList(viewport), g.IO.MousePos, scale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); - } + // Draw software mouse cursor if requested + if (g.IO.MouseDrawCursor) + RenderMouseCursor(&g.ForegroundDrawList, g.IO.MousePos, g.Style.MouseCursorScale, g.MouseCursor, IM_COL32_WHITE, IM_COL32_BLACK, IM_COL32(0, 0, 0, 48)); - // Add foreground ImDrawList (for each active viewport) - if (viewport->DrawLists[1] != NULL) - AddDrawListToDrawData(&viewport->DrawDataBuilder.Layers[0], GetForegroundDrawList(viewport)); + // Add foreground ImDrawList + if (!g.ForegroundDrawList.VtxBuffer.empty()) + AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.ForegroundDrawList); - SetupViewportDrawData(viewport, &viewport->DrawDataBuilder.Layers[0]); - g.IO.MetricsRenderVertices += viewport->DrawData->TotalVtxCount; - g.IO.MetricsRenderIndices += viewport->DrawData->TotalIdxCount; - } + // Setup ImDrawData structure for end-user + SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); + g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; + g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; // (Legacy) Call the Render callback function. The current prefer way is to let the user retrieve GetDrawData() and call the render function themselves. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (g.Viewports[0]->DrawData->CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) - g.IO.RenderDrawListsFn(g.Viewports[0]->DrawData); + if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) + g.IO.RenderDrawListsFn(&g.DrawData); #endif } @@ -4577,13 +4272,7 @@ static void FindHoveredWindow() { ImGuiContext& g = *GImGui; - // Special handling for the window being moved: Ignore the mouse viewport check (because it may reset/lose its viewport during the undocking frame) - ImGuiViewportP* moving_window_viewport = g.MovingWindow ? g.MovingWindow->Viewport : NULL; - if (g.MovingWindow) - g.MovingWindow->Viewport = g.MouseViewport; - ImGuiWindow* hovered_window = NULL; - ImGuiWindow* hovered_window_ignoring_moving_window = NULL; if (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoMouseInputs)) hovered_window = g.MovingWindow; @@ -4596,9 +4285,6 @@ static void FindHoveredWindow() continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) continue; - IM_ASSERT(window->Viewport); - if (window->Viewport != g.MouseViewport) - continue; // Using the clipped AABB, a child window will typically be clipped by its parent (not always) ImRect bb(window->OuterRectClipped); @@ -4609,29 +4295,26 @@ static void FindHoveredWindow() if (!bb.Contains(g.IO.MousePos)) continue; + // Support for one rectangular hole in any given window + // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512) if (window->HitTestHoleSize.x != 0) { - // FIXME: Consider generalizing hit-testing override (with more generic data, callback, etc.) (#1512) - ImRect hole_bb((float)(window->HitTestHoleOffset.x), (float)(window->HitTestHoleOffset.y), - (float)(window->HitTestHoleOffset.x + window->HitTestHoleSize.x), (float)(window->HitTestHoleOffset.y + window->HitTestHoleSize.y)); - if (hole_bb.Contains(g.IO.MousePos - window->Pos)) + ImVec2 hole_pos(window->Pos.x + (float)window->HitTestHoleOffset.x, window->Pos.y + (float)window->HitTestHoleOffset.y); + ImVec2 hole_size((float)window->HitTestHoleSize.x, (float)window->HitTestHoleSize.y); + if (ImRect(hole_pos, hole_pos + hole_size).Contains(g.IO.MousePos)) continue; } + // Those seemingly unnecessary extra tests are because the code here is a little different in viewport/docking branches. if (hovered_window == NULL) hovered_window = 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) + if (hovered_window) break; } g.HoveredWindow = hovered_window; g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - g.HoveredWindowUnderMovingWindow = hovered_window_ignoring_moving_window; - if (g.MovingWindow) - g.MovingWindow->Viewport = moving_window_viewport; } // Test if mouse cursor is hovering given rectangle @@ -4650,8 +4333,6 @@ bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool c const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); if (!rect_for_touch.Contains(g.IO.MousePos)) return false; - if (!g.MouseViewport->GetMainRect().Overlaps(rect_clipped)) - return false; return true; } @@ -4905,12 +4586,6 @@ bool ImGui::IsItemFocused() 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; } @@ -4989,12 +4664,18 @@ ImVec2 ImGui::GetItemRectSize() return window->DC.LastItemRect.GetSize(); } +static ImRect GetViewportRect() +{ + ImGuiContext& g = *GImGui; + return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); +} + bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* parent_window = g.CurrentWindow; - flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoDocking; + flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_ChildWindow; flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag // Size @@ -5120,7 +4801,6 @@ 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) @@ -5137,19 +4817,10 @@ ImGuiWindow* ImGui::FindWindowByName(const char* name) static void ApplyWindowSettings(ImGuiWindow* window, ImGuiWindowSettings* settings) { - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - window->ViewportPos = main_viewport->Pos; - if (settings->ViewportId) - { - window->ViewportId = settings->ViewportId; - window->ViewportPos = ImVec2(settings->ViewportPos.x, settings->ViewportPos.y); - } - window->Pos = ImFloor(ImVec2(settings->Pos.x + window->ViewportPos.x, settings->Pos.y + window->ViewportPos.y)); + window->Pos = ImFloor(ImVec2(settings->Pos.x, settings->Pos.y)); if (settings->Size.x > 0 && settings->Size.y > 0) window->Size = window->SizeFull = ImFloor(ImVec2(settings->Size.x, settings->Size.y)); window->Collapsed = settings->Collapsed; - window->DockId = settings->DockId; - window->DockOrder = settings->DockOrder; } static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) @@ -5163,9 +4834,7 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) g.WindowsById.SetVoidPtr(window->ID, window); // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - window->Pos = main_viewport->Pos + ImVec2(60, 60); - window->ViewportPos = main_viewport->Pos; + window->Pos = ImVec2(60, 60); // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. if (!(flags & ImGuiWindowFlags_NoSavedSettings)) @@ -5200,16 +4869,6 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) return window; } -static ImGuiWindow* GetWindowForTitleDisplay(ImGuiWindow* window) -{ - return window->DockNodeAsHost ? window->DockNodeAsHost->VisibleWindow : window; -} - -static ImGuiWindow* GetWindowForTitleAndMenuHeight(ImGuiWindow* window) -{ - return (window->DockNodeAsHost && window->DockNodeAsHost->VisibleWindow) ? window->DockNodeAsHost->VisibleWindow : window; -} - static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) { ImGuiContext& g = *GImGui; @@ -5236,7 +4895,7 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size // Minimum size if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) { - ImGuiWindow* window_for_height = GetWindowForTitleAndMenuHeight(window); + ImGuiWindow* window_for_height = window; new_size = ImMax(new_size, g.Style.WindowMinSize); 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 } @@ -5277,15 +4936,7 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont ImVec2 size_min = style.WindowMinSize; if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); - - // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows? - ImVec2 avail_size = window->Viewport->Size; - if (window->ViewportOwned) - avail_size = ImVec2(FLT_MAX, FLT_MAX); - const int monitor_idx = window->ViewportAllowPlatformMonitorExtend; - if (monitor_idx >= 0 && monitor_idx < g.PlatformIO.Monitors.Size) - avail_size = g.PlatformIO.Monitors[monitor_idx].WorkSize; - ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - g.Style.DisplaySafeAreaPadding * 2.0f)); + ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, g.IO.DisplaySize - style.DisplaySafeAreaPadding * 2.0f)); // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. @@ -5378,7 +5029,7 @@ static ImRect GetResizeBorderRect(ImGuiWindow* window, int border_n, float perp_ ImGuiID ImGui::GetWindowResizeID(ImGuiWindow* window, int n) { IM_ASSERT(n >= 0 && n <= 7); - ImGuiID id = window->DockIsActive ? window->DockNode->HostWindow->ID : window->ID; + ImGuiID id = window->ID; id = ImHashStr("#RESIZE", 0, id); id = ImHashData(&n, sizeof(int), id); return id; @@ -5405,16 +5056,6 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s ImVec2 pos_target(FLT_MAX, FLT_MAX); ImVec2 size_target(FLT_MAX, FLT_MAX); - // Clip mouse interaction rectangles within the viewport rectangle (in practice the narrowing is going to happen most of the time). - // - Not narrowing would mostly benefit the situation where OS windows _without_ decoration have a threshold for hovering when outside their limits. - // This is however not the case with current back-ends under Win32, but a custom borderless window implementation would benefit from it. - // - When decoration are enabled we typically benefit from that distance, but then our resize elements would be conflicting with OS resize elements, so we also narrow. - // - Note that we are unable to tell if the platform setup allows hovering with a distance threshold (on Win32, decorated window have such threshold). - // We only clip interaction so we overwrite window->ClipRect, cannot call PushClipRect() yet as DrawList is not yet setup. - const bool clip_with_viewport_rect = !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) || (g.IO.MouseHoveredViewport != window->ViewportId) || !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration); - if (clip_with_viewport_rect) - window->ClipRect = window->Viewport->GetMainRect(); - // Resize grips and borders are on layer 1 window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); @@ -5551,7 +5192,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) window->DrawList->PathArcTo(ImLerp(border_r.Min, border_r.Max, def.CornerPosN2) + ImVec2(0.5f, 0.5f) + def.InnerDir * rounding, rounding, def.OuterAngle, def.OuterAngle + IM_PI * 0.25f); window->DrawList->PathStroke(GetColorU32(ImGuiCol_SeparatorActive), false, ImMax(2.0f, border_size)); // Thicker than usual } - if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) + if (g.Style.FrameBorderSize > 0 && !(window->Flags & ImGuiWindowFlags_NoTitleBar)) { float y = window->Pos.y + window->TitleBarHeight() - 1; window->DrawList->AddLine(ImVec2(window->Pos.x + border_size, y), ImVec2(window->Pos.x + window->Size.x - border_size, y), GetColorU32(ImGuiCol_Border), g.Style.FrameBorderSize); @@ -5560,7 +5201,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) // Draw background and borders // Draw and handle scrollbars -void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) +void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; @@ -5587,44 +5228,21 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar // Window background if (!(flags & ImGuiWindowFlags_NoBackground)) { - bool is_docking_transparent_payload = false; - if (g.DragDropActive && (g.FrameCount - g.DragDropAcceptFrameCount) <= 1 && g.IO.ConfigDockingTransparentPayload) - if (g.DragDropPayload.IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && *(ImGuiWindow**)g.DragDropPayload.Data == window) - is_docking_transparent_payload = true; - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); - if (window->ViewportOwned) - { - // No alpha - bg_col = (bg_col | IM_COL32_A_MASK); - if (is_docking_transparent_payload) - window->Viewport->Alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; - } - else + bool override_alpha = false; + float alpha = 1.0f; + if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) { - // Adjust alpha. For docking - bool override_alpha = false; - float alpha = 1.0f; - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasBgAlpha) - { - alpha = g.NextWindowData.BgAlphaVal; - override_alpha = true; - } - if (is_docking_transparent_payload) - { - alpha *= DOCKING_TRANSPARENT_PAYLOAD_ALPHA; // FIXME-DOCK: Should that be an override? - override_alpha = true; - } - if (override_alpha) - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); + alpha = g.NextWindowData.BgAlphaVal; + override_alpha = true; } + if (override_alpha) + bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(alpha) << IM_COL32_A_SHIFT); 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) + if (!(flags & ImGuiWindowFlags_NoTitleBar)) { ImU32 title_bar_col = GetColorU32(title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); @@ -5640,25 +5258,6 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); } - // Docking: Unhide tab bar (small triangle in the corner), drag from small triangle to quickly undock - ImGuiDockNode* node = window->DockNode; - if (window->DockIsActive && node->IsHiddenTabBar() && !node->IsNoTabBar()) - { - float unhide_sz_draw = ImFloor(g.FontSize * 0.70f); - float unhide_sz_hit = ImFloor(g.FontSize * 0.55f); - ImVec2 p = node->Pos; - ImRect r(p, p + ImVec2(unhide_sz_hit, unhide_sz_hit)); - bool hovered, held; - if (ButtonBehavior(r, window->GetID("#UNHIDE"), &hovered, &held, ImGuiButtonFlags_FlattenChildren)) - node->WantHiddenTabBarToggle = true; - else if (held && IsMouseDragging(0)) - StartMouseMovingWindowOrNode(window, node, true); - - // FIXME-DOCK: Ideally we'd use ImGuiCol_TitleBgActive/ImGuiCol_TitleBg here, but neither is guaranteed to be visible enough at this sort of size.. - ImU32 col = GetColorU32(((held && hovered) || (node->IsFocused && !hovered)) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - window->DrawList->AddTriangleFilled(p, p + ImVec2(unhide_sz_draw, 0.0f), p + ImVec2(0.0f, unhide_sz_draw), col); - } - // Scrollbars if (window->ScrollbarX) Scrollbar(ImGuiAxis_X); @@ -5666,7 +5265,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar Scrollbar(ImGuiAxis_Y); // Render resize grips (after their input handling so we don't have a frame of latency) - if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize)) + if (!(flags & ImGuiWindowFlags_NoResize)) { for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { @@ -5679,14 +5278,12 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } } - // Borders (for dock node host they will be rendered over after the tab bar) - if (handle_borders_and_resize_grips && !window->DockNodeAsHost) - RenderWindowOuterBorders(window); + // Borders + RenderWindowOuterBorders(window); } } // Render title text, collapse button, close button -// When inside a dock node, this is handled in DockNodeUpdateTabBar() instead. void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open) { ImGuiContext& g = *GImGui; @@ -5727,7 +5324,7 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl // Collapse button (submitting first so it gets priority when choosing a navigation init fallback) if (has_collapse_button) - if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos, NULL)) + if (CollapseButton(window->GetID("#COLLAPSE"), collapse_button_pos)) window->WantCollapseToggle = true; // Defer actual collapsing to next frame as we are too far in the Begin() function // Close button @@ -5774,13 +5371,9 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window) { window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowDockStop = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; + window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window; if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) - { window->RootWindow = parent_window->RootWindow; - if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost)) - 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) @@ -5838,7 +5431,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update Flags, LastFrameActive, BeginOrderXXX fields if (first_begin_of_the_frame) { - window->FlagsPreviousFrame = window->Flags; window->Flags = (ImGuiWindowFlags)flags; window->LastFrameActive = current_frame; window->LastTimeActive = (float)g.Time; @@ -5850,29 +5442,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) flags = window->Flags; } - // 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.Flags & ImGuiNextWindowDataFlags_HasDock) - SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond); - if (first_begin_of_the_frame) - { - bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL); - bool new_auto_dock_node = !has_dock_node && GetWindowAlwaysWantOwnTabBar(window); - if (has_dock_node || new_auto_dock_node) - { - BeginDocked(window, p_open); - flags = window->Flags; - if (window->DockIsActive) - IM_ASSERT(window->DockNode != NULL); - - // Docking currently override constraints - g.NextWindowData.Flags &= ~ImGuiNextWindowDataFlags_HasSizeConstraint; - } - } - // 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_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)); @@ -5943,8 +5514,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ContentSizeExplicit = g.NextWindowData.ContentSizeVal; else if (first_begin_of_the_frame) window->ContentSizeExplicit = ImVec2(0.0f, 0.0f); - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasWindowClass) - window->WindowClass = g.NextWindowData.WindowClass; if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasCollapsed) SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasFocus) @@ -5970,9 +5539,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update stored window name when it changes (which can _only_ happen with the "###" operator, so the ID would stay unchanged). // The title bar always display the 'name' parameter, so we only update the string storage if it needs to be visible to the end-user elsewhere. bool window_title_visible_elsewhere = false; - if ((window->Viewport && window->Viewport->Window == window) || (window->DockIsActive)) - window_title_visible_elsewhere = true; - else if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB + if (g.NavWindowingListWindow != NULL && (window->Flags & ImGuiWindowFlags_NoNavFocus) == 0) // Window titles visible when using CTRL+TAB window_title_visible_elsewhere = true; if (window_title_visible_elsewhere && !window_just_created && strcmp(name, window->Name) != 0) { @@ -6010,33 +5577,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) } // SELECT VIEWPORT - // We need to do this before using any style/font sizes, as viewport with a different DPI may affect font sizes. - - UpdateSelectWindowViewport(window); - SetCurrentViewport(window, window->Viewport); - window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; + // FIXME-VIEWPORT: In the docking/viewport branch, this is the point where we select the current viewport (which may affect the style) SetCurrentWindow(window); - flags = window->Flags; // LOCK BORDER SIZE AND PADDING FOR THE FRAME (so that altering them doesn't cause inconsistencies) - // We read Style data after the call to UpdateSelectWindowViewport() which might be swapping the style. 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 = style.WindowPadding; + if ((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; - - // Lock menu offset so size calculation can use it as menu-bar windows need a minimum size. - 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) && !window->DockIsActive) + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) { // 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(); @@ -6130,131 +5686,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) window->Pos = FindBestWindowPosForPopup(window); - // Late create viewport if we don't fit within our current host viewport. - if (window->ViewportAllowPlatformMonitorExtend >= 0 && !window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_Minimized)) - if (!window->Viewport->GetMainRect().Contains(window->Rect())) - { - // This is based on the assumption that the DPI will be known ahead (same as the DPI of the selection done in UpdateSelectWindowViewport) - //ImGuiViewport* old_viewport = window->Viewport; - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); - - // FIXME-DPI - //IM_ASSERT(old_viewport->DpiScale == window->Viewport->DpiScale); // FIXME-DPI: Something went wrong - SetCurrentViewport(window, window->Viewport); - window->FontDpiScale = (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleFonts) ? window->Viewport->DpiScale : 1.0f; - SetCurrentWindow(window); - } - - bool viewport_rect_changed = false; - if (window->ViewportOwned) - { - // Synchronize window --> viewport in most situations - // Synchronize viewport -> window in case the platform window has been moved or resized from the OS/WM - if (window->Viewport->PlatformRequestMove) - { - window->Pos = window->Viewport->Pos; - MarkIniSettingsDirty(window); - } - else if (memcmp(&window->Viewport->Pos, &window->Pos, sizeof(window->Pos)) != 0) - { - viewport_rect_changed = true; - window->Viewport->Pos = window->Pos; - } - - if (window->Viewport->PlatformRequestResize) - { - window->Size = window->SizeFull = window->Viewport->Size; - MarkIniSettingsDirty(window); - } - else if (memcmp(&window->Viewport->Size, &window->Size, sizeof(window->Size)) != 0) - { - viewport_rect_changed = true; - window->Viewport->Size = window->Size; - } - - // The viewport may have changed monitor since the global update in UpdateViewportsNewFrame() - // Either a SetNextWindowPos() call in the current frame or a SetWindowPos() call in the previous frame may have this effect. - if (viewport_rect_changed) - UpdateViewportPlatformMonitor(window->Viewport); - - // Update common viewport flags - const ImGuiViewportFlags viewport_flags_to_clear = ImGuiViewportFlags_TopMost | ImGuiViewportFlags_NoTaskBarIcon | ImGuiViewportFlags_NoDecoration | ImGuiViewportFlags_NoRendererClear; - ImGuiViewportFlags viewport_flags = window->Viewport->Flags & ~viewport_flags_to_clear; - const bool is_short_lived_floating_window = (flags & (ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) != 0; - if (flags & ImGuiWindowFlags_Tooltip) - viewport_flags |= ImGuiViewportFlags_TopMost; - if (g.IO.ConfigViewportsNoTaskBarIcon || is_short_lived_floating_window) - viewport_flags |= ImGuiViewportFlags_NoTaskBarIcon; - if (g.IO.ConfigViewportsNoDecoration || is_short_lived_floating_window) - viewport_flags |= ImGuiViewportFlags_NoDecoration; - - // For popups and menus that may be protruding out of their parent viewport, we enable _NoFocusOnClick so that clicking on them - // won't steal the OS focus away from their parent window (which may be reflected in OS the title bar decoration). - // Setting _NoFocusOnClick would technically prevent us from bringing back to front in case they are being covered by an OS window from a different app, - // but it shouldn't be much of a problem considering those are already popups that are closed when clicking elsewhere. - if (is_short_lived_floating_window && (flags & ImGuiWindowFlags_Modal) == 0) - viewport_flags |= ImGuiViewportFlags_NoFocusOnAppearing | ImGuiViewportFlags_NoFocusOnClick; - - // We can overwrite viewport flags using ImGuiWindowClass (advanced users) - // We don't default to the main viewport because. - if (window->WindowClass.ParentViewportId) - window->Viewport->ParentViewportId = window->WindowClass.ParentViewportId; - else if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && parent_window_in_stack) - window->Viewport->ParentViewportId = parent_window_in_stack->Viewport->ID; - else - window->Viewport->ParentViewportId = g.IO.ConfigViewportsNoDefaultParent ? 0 : IMGUI_VIEWPORT_DEFAULT_ID; - if (window->WindowClass.ViewportFlagsOverrideSet) - viewport_flags |= window->WindowClass.ViewportFlagsOverrideSet; - if (window->WindowClass.ViewportFlagsOverrideClear) - viewport_flags &= ~window->WindowClass.ViewportFlagsOverrideClear; - - // We also tell the back-end that clearing the platform window won't be necessary, as our window is filling the viewport and we have disabled BgAlpha - if (!(flags & ImGuiWindowFlags_NoBackground)) - viewport_flags &= ~ImGuiViewportFlags_NoRendererClear; - - window->Viewport->Flags = viewport_flags; - } - // Calculate the range of allowed position for that window (to be movable and visible past safe area padding) // When clamping to stay visible, we will enforce that window->Pos stays inside of visibility_rect. - ImRect viewport_rect(window->Viewport->GetMainRect()); - ImRect viewport_work_rect(window->Viewport->GetWorkRect()); + ImRect viewport_rect(GetViewportRect()); ImVec2 visibility_padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - ImRect visibility_rect(viewport_work_rect.Min + visibility_padding, viewport_work_rect.Max - visibility_padding); + ImRect visibility_rect(viewport_rect.Min + visibility_padding, viewport_rect.Max - visibility_padding); // Clamp position/size so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. - // FIXME: Similar to code in GetWindowAllowedExtentRect() if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - { - if (!window->ViewportOwned && viewport_rect.GetWidth() > 0 && viewport_rect.GetHeight() > 0.0f) - { + if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f) ClampWindowRect(window, visibility_rect); - } - else if (window->ViewportOwned && g.PlatformIO.Monitors.Size > 0) - { - if (window->Viewport->PlatformMonitor == -1) - { - // Fallback for "lost" window (e.g. a monitor disconnected): we move the window back over the main viewport - SetWindowPos(window, g.Viewports[0]->Pos + style.DisplayWindowPadding, ImGuiCond_Always); - } - else - { - ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->Viewport->PlatformMonitor]; - visibility_rect.Min = monitor.WorkPos + visibility_padding; - visibility_rect.Max = monitor.WorkPos + monitor.WorkSize - visibility_padding; - ClampWindowRect(window, visibility_rect); - } - } - } window->Pos = ImFloor(window->Pos); // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) // Large values tend to lead to variety of artifacts and are not recommended. - if (window->ViewportOwned || window->DockIsActive) - window->WindowRounding = 0.0f; - else - window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; + window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; // For windows with title bar or menu bar, we clamp to FrameHeight(FontSize + FramePadding.y * 2.0f) to completely hide artifacts. //if ((window->Flags & ImGuiWindowFlags_MenuBar) || !(window->Flags & ImGuiWindowFlags_NoTitleBar)) @@ -6266,36 +5713,20 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) { if (flags & ImGuiWindowFlags_Popup) want_focus = true; - else if ((window->DockIsActive || (flags & ImGuiWindowFlags_ChildWindow) == 0) && !(flags & ImGuiWindowFlags_Tooltip)) + else if ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) == 0) want_focus = true; } - // Decide if we are going to handle borders and resize grips - const bool handle_borders_and_resize_grips = (window->DockNodeAsHost || !window->DockIsActive); - // Handle manual resize: Resize Grips, Borders, Gamepad int border_held = -1; ImU32 resize_grip_col[4] = {}; const int resize_grip_count = g.IO.ConfigWindowsResizeFromEdges ? 2 : 1; // Allow resize from lower-left if we have the mouse cursor feedback for it. const float resize_grip_draw_size = IM_FLOOR(ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f)); - if (handle_borders_and_resize_grips && !window->Collapsed) + if (!window->Collapsed) if (UpdateWindowManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0], visibility_rect)) use_current_size_for_scrollbar_x = use_current_size_for_scrollbar_y = true; window->ResizeBorderHeld = (signed char)border_held; - // Synchronize window --> viewport again and one last time (clamping and manual resize may have affected either) - if (window->ViewportOwned) - { - if (!window->Viewport->PlatformRequestMove) - window->Viewport->Pos = window->Pos; - if (!window->Viewport->PlatformRequestResize) - window->Viewport->Size = window->Size; - viewport_rect = window->Viewport->GetMainRect(); - } - - // Save last known viewport position within the window itself (so it can be saved in .ini file and restored) - window->ViewportPos = window->Viewport->Pos; - // SCROLLBAR VISIBILITY // Update scrollbar visibility (based on the Size that was effective during last frame or the auto-resized Size). @@ -6329,8 +5760,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const ImRect outer_rect = window->Rect(); const ImRect title_bar_rect = window->TitleBarRect(); window->OuterRectClipped = outer_rect; - if (window->DockIsActive) - window->OuterRectClipped.Min.y += window->TitleBarHeight(); window->OuterRectClipped.ClipWith(host_rect); // Inner rectangle @@ -6383,10 +5812,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); PushClipRect(host_rect.Min, host_rect.Max, false); - // Draw modal or window list full viewport dimming background (for other viewports we'll render them in EndFrame) - ImGuiWindow* window_window_list = g.NavWindowingListWindow; + // Draw modal window background (darkens what is behind them, all viewports) const bool dim_bg_for_modal = (flags & ImGuiWindowFlags_Modal) && window == GetTopMostPopupModal() && window->HiddenFramesCannotSkipItems <= 0; - const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && ((window == g.NavWindowingTargetAnim->RootWindow) || (window == window_window_list && window_window_list->Viewport != g.NavWindowingTargetAnim->Viewport)); + const bool dim_bg_for_window_list = g.NavWindowingTargetAnim && (window == g.NavWindowingTargetAnim->RootWindow); if (dim_bg_for_modal || dim_bg_for_window_list) { const ImU32 dim_bg_col = GetColorU32(dim_bg_for_modal ? ImGuiCol_ModalWindowDimBg : ImGuiCol_NavWindowingDimBg, g.DimBgRatio); @@ -6407,8 +5835,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // We disable this when the parent window has zero vertices, which is a common pattern leading to laying out multiple overlapping child. // We also disabled this when we have dimming overlay behind this specific one child. // FIXME: More code may rely on explicit sorting of overlapping child window and would need to disable this somehow. Please get in contact if you are affected. - const bool is_undocked_or_docked_visible = !window->DockIsActive || window->DockTabIsVisible; - if (is_undocked_or_docked_visible) { bool render_decorations_in_parent = false; if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) @@ -6419,8 +5845,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Handle title bar, scrollbar, resize grips and resize borders 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 || (window->DockNode && window->DockNode == window_to_highlight->DockNode))); - RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size); + const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); + RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size); if (render_decorations_in_parent) window->DrawList = &window->DrawListInst; @@ -6486,6 +5912,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.NavHasScroll = (window->ScrollMax.y > 0.0f); window->DC.MenuBarAppending = false; + window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); + window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; window->DC.MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); window->DC.TreeDepth = 0; window->DC.TreeJumpToParentOnPopMask = 0x00; @@ -6518,20 +5946,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) NavInitWindow(window, false); } - // Close requested by platform window - if (p_open != NULL && window->Viewport->PlatformRequestClose && window->Viewport != GetMainViewport()) - { - if (!window->DockIsActive || window->DockTabIsVisible) - { - window->Viewport->PlatformRequestClose = false; - g.NavWindowingToggleLayer = false; // Assume user mapped PlatformRequestClose on ALT-F4 so we disable ALT for menu toggle. False positive not an issue. - IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' PlatformRequestClose\n", window->Name); - *p_open = false; - } - } - // Title bar - if (!(flags & ImGuiWindowFlags_NoTitleBar) && !window->DockIsActive) + if (!(flags & ImGuiWindowFlags_NoTitleBar)) RenderWindowTitleBarContents(window, title_bar_rect, name, p_open); // Clear hit test shape every frame @@ -6546,36 +5962,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) LogToClipboard(); */ - 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 BeginDockableDragDropSource() also overwrites it. - if ((g.MovingWindow == window) && (g.IO.ConfigDockingWithShift == g.IO.KeyShift)) - if ((window->RootWindow->Flags & ImGuiWindowFlags_NoDocking) == 0) - BeginDockableDragDropSource(window); - - // Docking: Any dockable window can act as a target. For dock node hosts we call BeginDockableDragDropTarget() 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)) - BeginDockableDragDropTarget(window); - } - // We fill last item data based on Title Bar/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; - } - + 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; #ifdef IMGUI_ENABLE_TEST_ENGINE if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) IMGUI_TEST_ENGINE_ITEM_ADD(window->DC.LastItemRect, window->DC.LastItemId); @@ -6584,12 +5975,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) else { // Append - SetCurrentViewport(window, window->Viewport); SetCurrentWindow(window); } - if (!(flags & ImGuiWindowFlags_DockNodeHost)) - PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); + 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) if (first_begin_of_the_frame) @@ -6598,23 +5987,11 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->BeginCount++; g.NextWindowData.ClearFlags(); - // When we are about to select this tab (which will only be visible on the _next frame_), flag it with a non-zero HiddenFramesCannotSkipItems. - // This will have the important effect of actually returning true in Begin() and not setting SkipItems, allowing an earlier submission of the window contents. - // This is analogous to regular windows being hidden from one frame. - // It is especially important as e.g. nested TabBars would otherwise generate flicker in the form of one empty frame, or focus requests won't be processed. - if (window->DockIsActive && !window->DockTabIsVisible) - { - if (window->LastFrameJustFocused == g.FrameCount) - window->HiddenFramesCannotSkipItems = 1; - else - window->HiddenFramesCanSkipItems = 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 || (window->DockIsActive)); + IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); 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->HiddenFramesCanSkipItems = 1; @@ -6657,32 +6034,24 @@ void ImGui::End() IM_ASSERT(g.CurrentWindowStack.Size > 0); // Error checking: verify that user doesn't directly call End() on a child window. - if ((window->Flags & ImGuiWindowFlags_ChildWindow) && !window->DockIsActive) + if (window->Flags & ImGuiWindowFlags_ChildWindow) IM_ASSERT_USER_ERROR(g.WithinEndChild, "Must call EndChild() and not End()!"); // Close anything that is open if (window->DC.CurrentColumns) EndColumns(); - if (!(window->Flags & ImGuiWindowFlags_DockNodeHost)) // Pop inner window clip rectangle - PopClipRect(); + PopClipRect(); // Inner window clip rectangle // 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-DOCK - 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) g.BeginPopupStack.pop_back(); ErrorCheckBeginEndCompareStacksSize(window, false); SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); - if (g.CurrentWindow) - SetCurrentViewport(g.CurrentWindow, g.CurrentWindow->Viewport); } void ImGui::BringWindowToFocusFront(ImGuiWindow* window) @@ -6751,30 +6120,20 @@ void ImGui::FocusWindow(ImGuiWindow* window) // Move the root window to the top of the pile IM_ASSERT(window == NULL || window->RootWindow != NULL); - ImGuiWindow* focus_front_window = window ? window->RootWindowDockStop : NULL; + ImGuiWindow* focus_front_window = window ? window->RootWindow : NULL; // NB: In docking branch this is window->RootWindowDockStop ImGuiWindow* display_front_window = window ? window->RootWindow : NULL; - ImGuiDockNode* dock_node = window ? window->DockNode : NULL; - bool active_id_window_is_dock_node_host = (g.ActiveIdWindow && dock_node && dock_node->HostWindow == g.ActiveIdWindow); - - // Steal active widgets. Some of the cases it triggers includes: - // - Focus a window while an InputText in another window is active, if focus happens before the old InputText can run. - // - When using Nav to activate menu items (due to timing of activating on press->new window appears->losing ActiveId) - // - Using dock host items (tab, collapse button) can trigger this before we redirect the ActiveIdWindow toward the child window. - if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindowDockStop != focus_front_window && !active_id_window_is_dock_node_host) + + // Steal focus on active widgets + if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != focus_front_window) ClearActiveID(); // Passing NULL allow to disable keyboard focus if (!window) return; - window->LastFrameJustFocused = g.FrameCount; - - // Select in dock node - if (dock_node && dock_node->TabBar) - dock_node->TabBar->SelectedTabId = dock_node->TabBar->NextSelectedTabId = window->ID; // Bring to front BringWindowToFocusFront(focus_front_window); - if (((window->Flags | focus_front_window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) + if (((window->Flags | display_front_window->Flags) & ImGuiWindowFlags_NoBringToFrontOnFocus) == 0) BringWindowToDisplayFront(display_front_window); } @@ -6793,13 +6152,9 @@ void ImGui::FocusTopMostWindowUnderOne(ImGuiWindow* under_this_window, ImGuiWind { // We may later decide to test for different NoXXXInputs based on the active navigation input (mouse vs nav) but that may feel more confusing to the user. ImGuiWindow* window = g.WindowsFocusOrder[i]; - if (window != ignore_window && window->WasActive && window->RootWindowDockStop == window) + if (window != ignore_window && window->WasActive && !(window->Flags & ImGuiWindowFlags_ChildWindow)) if ((window->Flags & (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) != (ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoNavInputs)) { - // FIXME-DOCK: This is failing (lagging by one frame) for docked windows. - // If A and B are docked into window and B disappear, at the NewFrame() call site window->NavLastChildNavWindow will still point to B. - // We might leverage the tab order implicitly stored in window->DockNodeAsHost->TabBar (essentially the 'most_recently_selected_tab' code in tab bar will do that but on next update) - // to tell which is the "previous" window. Or we may leverage 'LastFrameFocused/LastFrameJustFocused' and have this function handle child window itself? ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(window); FocusWindow(focus_window); return; @@ -6922,11 +6277,11 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) { case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow == NULL || g.HoveredWindow->RootWindowDockStop != g.CurrentWindow->RootWindowDockStop) + if (g.HoveredRootWindow != g.CurrentWindow->RootWindow) return false; break; case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != g.CurrentWindow->RootWindowDockStop) + if (g.HoveredWindow != g.CurrentWindow->RootWindow) return false; break; case ImGuiHoveredFlags_ChildWindows: @@ -6959,9 +6314,9 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) { case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindowDockStop == g.CurrentWindow->RootWindowDockStop; + return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindowDockStop; + return g.NavWindow == g.CurrentWindow->RootWindow; case ImGuiFocusedFlags_ChildWindows: return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); default: @@ -6969,24 +6324,12 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) } } -ImGuiID ImGui::GetWindowDockID() -{ - ImGuiContext& g = *GImGui; - return g.CurrentWindow->DockId; -} - -bool ImGui::IsWindowDocked() -{ - ImGuiContext& g = *GImGui; - return g.CurrentWindow->DockIsActive; -} - // Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) // Note that NoNavFocus makes the window not reachable with CTRL+TAB but it can still be focused with mouse or programmatically. // If you want a window to never be focused, you may use the e.g. NoInputs flag. bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) { - return window->Active && window == window->RootWindowDockStop && !(window->Flags & ImGuiWindowFlags_NoNavFocus); + return window->Active && window == window->RootWindow && !(window->Flags & ImGuiWindowFlags_NoNavFocus); } float ImGui::GetWindowWidth() @@ -7155,7 +6498,6 @@ 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) @@ -7214,48 +6556,12 @@ void ImGui::SetNextWindowBgAlpha(float alpha) g.NextWindowData.BgAlphaVal = alpha; } -void ImGui::SetNextWindowViewport(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasViewport; - g.NextWindowData.ViewportId = id; -} - -void ImGui::SetNextWindowDockID(ImGuiID id, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasDock; - g.NextWindowData.DockCond = cond ? cond : ImGuiCond_Always; - g.NextWindowData.DockId = id; -} - -void ImGui::SetNextWindowClass(const ImGuiWindowClass* window_class) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT((window_class->ViewportFlagsOverrideSet & window_class->ViewportFlagsOverrideClear) == 0); // Cannot set both set and clear for the same bit - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasWindowClass; - g.NextWindowData.WindowClass = *window_class; -} - ImDrawList* ImGui::GetWindowDrawList() { ImGuiWindow* window = GetCurrentWindow(); return window->DrawList; } -float ImGui::GetWindowDpiScale() -{ - ImGuiContext& g = *GImGui; - return g.CurrentDpiScale; -} - -ImGuiViewport* ImGui::GetWindowViewport() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.CurrentViewport != NULL && g.CurrentViewport == g.CurrentWindow->Viewport); - return g.CurrentViewport; -} - ImFont* ImGui::GetFont() { return GImGui->Font; @@ -7476,49 +6782,7 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() // Perform simple check: the beta io.ConfigWindowsResizeFromEdges option requires back-end to honor mouse cursor changes and set the ImGuiBackendFlags_HasMouseCursors flag accordingly. if (g.IO.ConfigWindowsResizeFromEdges && !(g.IO.BackendFlags & ImGuiBackendFlags_HasMouseCursors)) g.IO.ConfigWindowsResizeFromEdges = false; - - // Perform simple check: error if Docking or Viewport are enabled _exactly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. - if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_DockingEnable) == 0) - IM_ASSERT(0 && "Please set DockingEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); - if (g.FrameCount == 1 && (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable) == 0) - IM_ASSERT(0 && "Please set ViewportsEnable before the first call to NewFrame()! Otherwise you will lose your .ini settings!"); - - // Perform simple checks: multi-viewport and platform windows support - if (g.IO.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) - { - if ((g.IO.BackendFlags & ImGuiBackendFlags_PlatformHasViewports) && (g.IO.BackendFlags & ImGuiBackendFlags_RendererHasViewports)) - { - IM_ASSERT((g.FrameCount == 0 || g.FrameCount == g.FrameCountPlatformEnded) && "Forgot to call UpdatePlatformWindows() in main loop after EndFrame()? Check examples/ applications for reference."); - IM_ASSERT(g.PlatformIO.Platform_CreateWindow != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_DestroyWindow != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_GetWindowPos != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_SetWindowPos != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_GetWindowSize != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Platform_SetWindowSize != NULL && "Platform init didn't install handlers?"); - IM_ASSERT(g.PlatformIO.Monitors.Size > 0 && "Platform init didn't setup Monitors list?"); - IM_ASSERT((g.Viewports[0]->PlatformUserData != NULL || g.Viewports[0]->PlatformHandle != NULL) && "Platform init didn't setup main viewport."); - if (g.IO.ConfigDockingTransparentPayload && (g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) - IM_ASSERT(g.PlatformIO.Platform_SetWindowAlpha != NULL && "Platform_SetWindowAlpha handler is required to use io.ConfigDockingTransparent!"); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - IM_ASSERT(g.IO.RenderDrawListsFn == NULL); // Call ImGui::Render() then pass ImGui::GetDrawData() yourself to your render function! -#endif - } - else - { - // Disable feature, our back-ends do not support it - g.IO.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; - } - - // Perform simple checks on platform monitor data + compute a total bounding box for quick early outs - for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) - { - ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[monitor_n]; - IM_ASSERT(mon.MainSize.x > 0.0f && mon.MainSize.y > 0.0f && "Monitor main bounds not setup properly."); - IM_ASSERT(ImRect(mon.MainPos, mon.MainPos + mon.MainSize).Contains(ImRect(mon.WorkPos, mon.WorkPos + mon.WorkSize)) && "Monitor work bounds not setup properly. If you don't have work area information, just copy MainPos/MainSize into them."); - IM_ASSERT(mon.DpiScale != 0.0f); - } - } -} +} static void ImGui::ErrorCheckEndFrameSanityChecks() { @@ -8251,7 +7515,7 @@ void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, ImGuiTooltipFlags toolt window->HiddenFramesCanSkipItems = 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_NoDocking; + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize; Begin(window_name, NULL, flags | extra_flags); } @@ -8360,7 +7624,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); popup_ref.OpenMousePos = IsMousePosValid(&g.IO.MousePos) ? g.IO.MousePos : popup_ref.OpenPopupPos; - IMGUI_DEBUG_LOG_POPUP("OpenPopupEx(0x%08X)\n", id); + //IMGUI_DEBUG_LOG("OpenPopupEx(0x%08X)\n", g.FrameCount, id); if (g.OpenPopupStack.Size < current_stack_size + 1) { g.OpenPopupStack.push_back(popup_ref); @@ -8421,7 +7685,7 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to } if (popup_count_to_keep < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the statement below { - IMGUI_DEBUG_LOG_POPUP("ClosePopupsOverWindow(\"%s\") -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); + //IMGUI_DEBUG_LOG("ClosePopupsOverWindow(%s) -> ClosePopupToLevel(%d)\n", ref_window->Name, popup_count_to_keep); ClosePopupToLevel(popup_count_to_keep, restore_focus_to_window_under_popup); } } @@ -8429,7 +7693,6 @@ void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window, bool restore_focus_to void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_popup) { ImGuiContext& g = *GImGui; - IMGUI_DEBUG_LOG_POPUP("ClosePopupToLevel(%d), restore_focus_to_window_under_popup=%d\n", remaining, restore_focus_to_window_under_popup); IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); // Trim open popup stack @@ -8474,7 +7737,7 @@ void ImGui::CloseCurrentPopup() break; popup_idx--; } - IMGUI_DEBUG_LOG_POPUP("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); + //IMGUI_DEBUG_LOG("CloseCurrentPopup %d -> %d\n", g.BeginPopupStack.Size - 1, popup_idx); ClosePopupToLevel(popup_idx, true); // A common pattern is to close a popup when selecting a menu item/selectable that will open another window. @@ -8500,7 +7763,7 @@ bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags flags) else ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame - flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoDocking; + flags |= ImGuiWindowFlags_Popup; bool is_open = Begin(name, NULL, flags); if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) EndPopup(); @@ -8537,12 +7800,9 @@ bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags fla // (this won't really last as settings will kick in, and is mostly for backward compatibility. user may do the same themselves) // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) == 0) - { - ImGuiViewportP* viewport = window->WasActive ? window->Viewport : (ImGuiViewportP*)GetMainViewport(); // FIXME-VIEWPORT: What may be our reference viewport? - SetNextWindowPos(viewport->GetMainRect().GetCenter(), ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); - } + SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f)); - flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking; + flags |= ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse; const bool is_open = Begin(name, p_open, flags); if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) { @@ -8633,9 +7893,6 @@ bool ImGui::BeginPopupContextVoid(const char* str_id, ImGuiPopupFlags popup_flag // r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) // r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. -// (r_outer is usually equivalent to the viewport rectangle minus padding, but when multi-viewports are enabled and monitor -// information are available, it may represent the entire platform monitor from the frame of reference of the current viewport. -// this allows us to have tooltips/popups displayed out of the parent viewport.) ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy) { ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); @@ -8689,25 +7946,11 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s return pos; } -// Note that this is used for popups, which can overlap the non work-area of individual viewports. ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) { - ImGuiContext& g = *GImGui; - ImRect r_screen; - if (window->ViewportAllowPlatformMonitorExtend >= 0) - { - // Extent with be in the frame of reference of the given viewport (so Min is likely to be negative here) - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[window->ViewportAllowPlatformMonitorExtend]; - r_screen.Min = monitor.WorkPos; - r_screen.Max = monitor.WorkPos + monitor.WorkSize; - } - else - { - // Use the full viewport area (not work area) for popups - r_screen.Min = window->Viewport->Pos; - r_screen.Max = window->Viewport->Pos + window->Viewport->Size; - } - ImVec2 padding = g.Style.DisplaySafeAreaPadding; + IM_UNUSED(window); + ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; + ImRect r_screen = GetViewportRect(); r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); return r_screen; } @@ -8715,13 +7958,15 @@ ImRect ImGui::GetWindowAllowedExtentRect(ImGuiWindow* window) ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) { ImGuiContext& g = *GImGui; + + ImRect r_outer = GetWindowAllowedExtentRect(window); if (window->Flags & ImGuiWindowFlags_ChildMenu) { // Child menus typically request _any_ position within the parent menu item, and then we move the new menu outside the parent bounds. // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. - ImGuiWindow* parent_window = window->ParentWindow; + IM_ASSERT(g.CurrentWindow == window); + ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; float horizontal_overlap = g.Style.ItemInnerSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). - ImRect r_outer = GetWindowAllowedExtentRect(window); ImRect r_avoid; if (parent_window->DC.MenuBarAppending) r_avoid = ImRect(-FLT_MAX, parent_window->ClipRect.Min.y, FLT_MAX, parent_window->ClipRect.Max.y); // Avoid parent menu-bar. If we wanted multi-line menu-bar, we may instead want to have the calling window setup e.g. a NextWindowData.PosConstraintAvoidRect field @@ -8731,7 +7976,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } if (window->Flags & ImGuiWindowFlags_Popup) { - ImRect r_outer = GetWindowAllowedExtentRect(window); ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); } @@ -8740,7 +7984,6 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) // Position tooltip (always follows mouse) float sc = g.Style.MouseCursorScale; ImVec2 ref_pos = NavCalcPreferredRefPos(); - ImRect r_outer = GetWindowAllowedExtentRect(window); ImRect r_avoid; if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); @@ -9079,23 +8322,18 @@ void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags mov // This way we could find the last focused window among our children. It would be much less confusing this way? static void ImGui::NavSaveLastChildNavWindowIntoParent(ImGuiWindow* nav_window) { - ImGuiWindow* parent = nav_window; - while (parent && parent->RootWindowDockStop != parent && (parent->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - parent = parent->ParentWindow; - if (parent && parent != nav_window) - parent->NavLastChildNavWindow = nav_window; + ImGuiWindow* parent_window = nav_window; + while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) + parent_window = parent_window->ParentWindow; + if (parent_window && parent_window != nav_window) + parent_window->NavLastChildNavWindow = nav_window; } // Restore the last focused child. // Call when we are expected to land on the Main Layer (0) after FocusWindow() static ImGuiWindow* ImGui::NavRestoreLastChildNavWindow(ImGuiWindow* window) { - if (window->NavLastChildNavWindow && window->NavLastChildNavWindow->WasActive) - return window->NavLastChildNavWindow; - if (window->DockNodeAsHost && window->DockNodeAsHost->TabBar) - if (ImGuiTabItem* tab = TabBarFindMostRecentlySelectedTabForActiveWindow(window->DockNodeAsHost->TabBar)) - return tab->Window; - return window; + return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; } static void NavRestoreLayer(ImGuiNavLayer layer) @@ -9105,8 +8343,8 @@ static void NavRestoreLayer(ImGuiNavLayer layer) if (layer == 0) g.NavWindow = ImGui::NavRestoreLastChildNavWindow(g.NavWindow); ImGuiWindow* window = g.NavWindow; - if (window->NavLastIds[layer] != 0) - ImGui::SetNavIDWithRectRel(window->NavLastIds[layer], layer, 0, g.NavWindow->NavRectRel[layer]); + if (layer == 0 && window->NavLastIds[0] != 0) + ImGui::SetNavIDWithRectRel(window->NavLastIds[0], layer, 0, window->NavRectRel[0]); else ImGui::NavInitWindow(window, true); } @@ -9160,7 +8398,7 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() // 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]; ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x * 4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = g.NavWindow->Viewport->GetMainRect(); + ImRect visible_rect = GetViewportRect(); return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. } } @@ -9313,7 +8551,7 @@ static void ImGui::NavUpdate() if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) ClearActiveID(); } - else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow && g.NavWindow != g.NavWindow->RootWindowDockStop) + else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) { // Exit child window ImGuiWindow* child_window = g.NavWindow; @@ -9470,7 +8708,7 @@ static void ImGui::NavUpdate() // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0, 0, 0, 0); - g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : ImRect(0, 0, 0, 0); + g.NavScoringRect = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); g.NavScoringRect.TranslateY(nav_scoring_rect_offset_y); g.NavScoringRect.Min.x = ImMin(g.NavScoringRect.Min.x + 1.0f, g.NavScoringRect.Max.x); g.NavScoringRect.Max.x = g.NavScoringRect.Min.x; @@ -9748,7 +8986,7 @@ static void ImGui::NavUpdateWindowing() if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { - g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindowDockStop; + g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; // FIXME-DOCK: Will need to use RootWindowDockStop g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; @@ -9819,9 +9057,8 @@ static void ImGui::NavUpdateWindowing() } // Apply final focus - if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowDockStop)) + if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindow)) { - ImGuiViewport* previous_viewport = g.NavWindow ? g.NavWindow->Viewport : NULL; ClearActiveID(); g.NavDisableHighlight = false; g.NavDisableMouseHover = true; @@ -9834,10 +9071,6 @@ static void ImGui::NavUpdateWindowing() // If the window only has a menu layer, select it directly if (apply_focus_window->DC.NavLayerActiveMask == (1 << ImGuiNavLayer_Menu)) g.NavLayer = ImGuiNavLayer_Menu; - - // Request OS level focus - if (apply_focus_window->Viewport != previous_viewport && g.PlatformIO.Platform_SetWindowFocus) - g.PlatformIO.Platform_SetWindowFocus(apply_focus_window->Viewport); } if (apply_focus_window) g.NavWindowingTarget = NULL; @@ -9861,11 +9094,8 @@ static void ImGui::NavUpdateWindowing() g.NavDisableHighlight = false; g.NavDisableMouseHover = true; - // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. It however persist on docking tab tabs. + // When entering a regular menu bar with the Alt key, we always reinitialize the navigation ID. const ImGuiNavLayer new_nav_layer = (g.NavWindow->DC.NavLayerActiveMask & (1 << ImGuiNavLayer_Menu)) ? (ImGuiNavLayer)((int)g.NavLayer ^ 1) : ImGuiNavLayer_Main; - const bool preserve_layer_1_nav_id = (new_nav_window->DockNodeAsHost != NULL); - if (new_nav_layer == ImGuiNavLayer_Menu && !preserve_layer_1_nav_id) - g.NavWindow->NavLastIds[ImGuiNavLayer_Menu] = 0; NavRestoreLayer(new_nav_layer); } } @@ -9877,8 +9107,6 @@ 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)"; } @@ -9893,9 +9121,8 @@ void ImGui::NavUpdateWindowingOverlay() if (g.NavWindowingListWindow == NULL) g.NavWindowingListWindow = FindWindowByName("###NavWindowingList"); - ImGuiViewportP* viewport = /*g.NavWindow ? g.NavWindow->Viewport :*/ (ImGuiViewportP*)GetMainViewport(); - SetNextWindowSizeConstraints(ImVec2(viewport->Size.x * 0.20f, viewport->Size.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); - SetNextWindowPos(viewport->Pos + viewport->Size * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); + SetNextWindowSizeConstraints(ImVec2(g.IO.DisplaySize.x * 0.20f, g.IO.DisplaySize.y * 0.20f), ImVec2(FLT_MAX, FLT_MAX)); + SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); PushStyleVar(ImGuiStyleVar_WindowPadding, g.Style.WindowPadding * 2.0f); Begin("###NavWindowingList", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings); for (int n = g.WindowsFocusOrder.Size - 1; n >= 0; n--) @@ -10101,7 +9328,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) return false; ImGuiWindow* window = g.CurrentWindow; - if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow) + if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) return false; IM_ASSERT(id != 0); if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) @@ -10129,7 +9356,7 @@ bool ImGui::BeginDragDropTarget() ImGuiWindow* window = g.CurrentWindow; if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) return false; - if (g.HoveredWindowUnderMovingWindow == NULL || window->RootWindow != g.HoveredWindowUnderMovingWindow->RootWindow) + if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) return false; const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; @@ -10424,7 +9651,6 @@ void ImGui::LogButtons() LogToClipboard(); } - //----------------------------------------------------------------------------- // [SECTION] SETTINGS //----------------------------------------------------------------------------- @@ -10679,15 +9905,9 @@ static void WindowSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; int x, y; int i; - ImU32 u1; - if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) { settings->Pos = ImVec2ih((short)x, (short)y); } - else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) { settings->Size = ImVec2ih((short)x, (short)y); } - else if (sscanf(line, "ViewportId=0x%08X", &u1) == 1) { settings->ViewportId = u1; } - else if (sscanf(line, "ViewportPos=%i,%i", &x, &y) == 2) { settings->ViewportPos = ImVec2ih((short)x, (short)y); } - 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; } - else if (sscanf(line, "ClassId=0x%X", &u1) == 1) { settings->ClassId = u1; } + if (sscanf(line, "Pos=%i,%i", &x, &y) == 2) settings->Pos = ImVec2ih((short)x, (short)y); + else if (sscanf(line, "Size=%i,%i", &x, &y) == 2) settings->Size = ImVec2ih((short)x, (short)y); + else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0); } // Apply to existing windows (if any) @@ -10721,14 +9941,8 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl window->SettingsOffset = g.SettingsWindows.offset_from_ptr(settings); } IM_ASSERT(settings->ID == window->ID); - settings->Pos = ImVec2ih(window->Pos - window->ViewportPos); - settings->Size = ImVec2ih(window->SizeFull); - settings->ViewportId = window->ViewportId; - settings->ViewportPos = ImVec2ih(window->ViewportPos); - IM_ASSERT(window->DockNode == NULL || window->DockNode->ID == window->DockId); - settings->DockId = window->DockId; - settings->ClassId = window->WindowClass.ClassId; - settings->DockOrder = window->DockOrder; + settings->Pos = ImVec2ih((short)window->Pos.x, (short)window->Pos.y); + settings->Size = ImVec2ih((short)window->SizeFull.x, (short)window->SizeFull.y); settings->Collapsed = window->Collapsed; } @@ -10738,26 +9952,9 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl { const char* settings_name = settings->GetName(); buf->appendf("[%s][%s]\n", handler->TypeName, settings_name); - if (settings->ViewportId != 0 && settings->ViewportId != ImGui::IMGUI_VIEWPORT_DEFAULT_ID) - { - buf->appendf("ViewportPos=%d,%d\n", settings->ViewportPos.x, settings->ViewportPos.y); - buf->appendf("ViewportId=0x%08X\n", settings->ViewportId); - } - if (settings->Pos.x != 0 || settings->Pos.y != 0 || settings->ViewportId == ImGui::IMGUI_VIEWPORT_DEFAULT_ID) - buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); - if (settings->Size.x != 0 || settings->Size.y != 0) - buf->appendf("Size=%d,%d\n", settings->Size.x, settings->Size.y); + buf->appendf("Pos=%d,%d\n", settings->Pos.x, settings->Pos.y); + buf->appendf("Size=%d,%d\n", settings->Size.x, 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); - if (settings->ClassId != 0) - buf->appendf("ClassId=0x%08X\n", settings->ClassId); - } buf->append("\n"); } } @@ -10766,4575 +9963,218 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl //----------------------------------------------------------------------------- // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- -// - GetMainViewport() -// - FindViewportByID() -// - FindViewportByPlatformHandle() -// - SetCurrentViewport() [Internal] -// - SetWindowViewport() [Internal] -// - GetWindowAlwaysWantOwnViewport() [Internal] -// - UpdateTryMergeWindowIntoHostViewport() [Internal] -// - UpdateTryMergeWindowIntoHostViewports() [Internal] -// - TranslateWindowsInViewport() [Internal] -// - ScaleWindowsInViewport() [Internal] -// - FindHoveredViewportFromPlatformWindowStack() [Internal] -// - UpdateViewportsNewFrame() [Internal] -// - UpdateViewportsEndFrame() [Internal] -// - AddUpdateViewport() [Internal] -// - UpdateSelectWindowViewport() [Internal] -// - UpdatePlatformWindows() -// - RenderPlatformWindowsDefault() -// - FindPlatformMonitorForPos() [Internal] -// - FindPlatformMonitorForRect() [Internal] -// - UpdateViewportPlatformMonitor() [Internal] -// - DestroyPlatformWindow() [Internal] -// - DestroyPlatformWindows() -//----------------------------------------------------------------------------- -ImGuiViewport* ImGui::GetMainViewport() -{ - ImGuiContext& g = *GImGui; - return g.Viewports[0]; -} +// (this section is filled in the 'docking' branch) -ImGuiViewport* ImGui::FindViewportByID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - for (int n = 0; n < g.Viewports.Size; n++) - if (g.Viewports[n]->ID == id) - return g.Viewports[n]; - return NULL; -} -ImGuiViewport* ImGui::FindViewportByPlatformHandle(void* platform_handle) -{ - ImGuiContext& g = *GImGui; - for (int i = 0; i != g.Viewports.Size; i++) - if (g.Viewports[i]->PlatformHandle == platform_handle) - return g.Viewports[i]; - return NULL; -} +//----------------------------------------------------------------------------- +// [SECTION] DOCKING +//----------------------------------------------------------------------------- -void ImGui::SetCurrentViewport(ImGuiWindow* current_window, ImGuiViewportP* viewport) -{ - ImGuiContext& g = *GImGui; - (void)current_window; +// (this section is filled in the 'docking' branch) - if (viewport) - viewport->LastFrameActive = g.FrameCount; - if (g.CurrentViewport == viewport) - return; - g.CurrentDpiScale = viewport ? viewport->DpiScale : 1.0f; - g.CurrentViewport = viewport; - //IMGUI_DEBUG_LOG_VIEWPORT("SetCurrentViewport changed '%s' 0x%08X\n", current_window ? current_window->Name : NULL, viewport ? viewport->ID : 0); - // Notify platform layer of viewport changes - // FIXME-DPI: This is only currently used for experimenting with handling of multiple DPI - if (g.CurrentViewport && g.PlatformIO.Platform_OnChangedViewport) - g.PlatformIO.Platform_OnChangedViewport(g.CurrentViewport); -} +//----------------------------------------------------------------------------- +// [SECTION] PLATFORM DEPENDENT HELPERS +//----------------------------------------------------------------------------- -static void SetWindowViewport(ImGuiWindow* window, ImGuiViewportP* viewport) -{ - window->Viewport = viewport; - window->ViewportId = viewport->ID; - window->ViewportOwned = (viewport->Window == window); -} +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) -static bool ImGui::GetWindowAlwaysWantOwnViewport(ImGuiWindow* window) -{ - // Tooltips and menus are not automatically forced into their own viewport when the NoMerge flag is set, however the multiplication of viewports makes them more likely to protrude and create their own. - ImGuiContext& g = *GImGui; - if (g.IO.ConfigViewportsNoAutoMerge || (window->WindowClass.ViewportFlagsOverrideSet & ImGuiViewportFlags_NoAutoMerge)) - if (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) - if (!window->DockIsActive) - if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_Tooltip)) == 0) - if ((window->Flags & ImGuiWindowFlags_Popup) == 0 || (window->Flags & ImGuiWindowFlags_Modal) != 0) - return true; - return false; -} +#ifdef _MSC_VER +#pragma comment(lib, "user32") +#pragma comment(lib, "kernel32") +#endif -static bool ImGui::UpdateTryMergeWindowIntoHostViewport(ImGuiWindow* window, ImGuiViewportP* viewport) +// Win32 clipboard implementation +// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() +static const char* GetClipboardTextFn_DefaultImpl(void*) { ImGuiContext& g = *GImGui; - if (window->Viewport == viewport) - return false; - if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) == 0) - return false; - if ((viewport->Flags & ImGuiViewportFlags_Minimized) != 0) - return false; - if (!viewport->GetMainRect().Contains(window->Rect())) - return false; - if (GetWindowAlwaysWantOwnViewport(window)) - return false; - - for (int n = 0; n < g.Windows.Size; n++) + g.ClipboardHandlerData.clear(); + if (!::OpenClipboard(NULL)) + return NULL; + HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); + if (wbuf_handle == NULL) { - ImGuiWindow* window_behind = g.Windows[n]; - if (window_behind == window) - break; - if (window_behind->WasActive && window_behind->ViewportOwned && !(window_behind->Flags & ImGuiWindowFlags_ChildWindow)) - if (window_behind->Viewport->GetMainRect().Overlaps(window->Rect())) - return false; + ::CloseClipboard(); + return NULL; } - - // Move to the existing viewport, Move child/hosted windows as well (FIXME-OPT: iterate child) - ImGuiViewportP* old_viewport = window->Viewport; - if (window->ViewportOwned) - for (int n = 0; n < g.Windows.Size; n++) - if (g.Windows[n]->Viewport == old_viewport) - SetWindowViewport(g.Windows[n], viewport); - SetWindowViewport(window, viewport); - BringWindowToDisplayFront(window); - - return true; -} - -static bool ImGui::UpdateTryMergeWindowIntoHostViewports(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - return UpdateTryMergeWindowIntoHostViewport(window, g.Viewports[0]); -} - -// Translate imgui windows when a Host Viewport has been moved -// (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) -void ImGui::TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(viewport->Window == NULL && (viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows)); - - // 1) We test if ImGuiConfigFlags_ViewportsEnable was just toggled, which allows us to conveniently - // translate imgui windows from OS-window-local to absolute coordinates or vice-versa. - // 2) If it's not going to fit into the new size, keep it at same absolute position. - // One problem with this is that most Win32 applications doesn't update their render while dragging, - // and so the window will appear to teleport when releasing the mouse. - const bool translate_all_windows = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != (g.ConfigFlagsLastFrame & ImGuiConfigFlags_ViewportsEnable); - ImRect test_still_fit_rect(old_pos, old_pos + viewport->Size); - ImVec2 delta_pos = new_pos - old_pos; - for (int window_n = 0; window_n < g.Windows.Size; window_n++) // FIXME-OPT - if (translate_all_windows || (g.Windows[window_n]->Viewport == viewport && test_still_fit_rect.Contains(g.Windows[window_n]->Rect()))) - TranslateWindow(g.Windows[window_n], delta_pos); + if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) + { + int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); + g.ClipboardHandlerData.resize(buf_len); + ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); + } + ::GlobalUnlock(wbuf_handle); + ::CloseClipboard(); + return g.ClipboardHandlerData.Data; } -// Scale all windows (position, size). Use when e.g. changing DPI. (This is a lossy operation!) -void ImGui::ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale) +static void SetClipboardTextFn_DefaultImpl(void*, const char* text) { - ImGuiContext& g = *GImGui; - if (viewport->Window) - { - ScaleWindow(viewport->Window, scale); - } - else + if (!::OpenClipboard(NULL)) + return; + const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); + HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); + if (wbuf_handle == NULL) { - for (int i = 0; i != g.Windows.Size; i++) - if (g.Windows[i]->Viewport == viewport) - ScaleWindow(g.Windows[i], scale); + ::CloseClipboard(); + return; } + WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); + ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); + ::GlobalUnlock(wbuf_handle); + ::EmptyClipboard(); + if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) + ::GlobalFree(wbuf_handle); + ::CloseClipboard(); } -// If the back-end doesn't set MouseLastHoveredViewport or doesn't honor ImGuiViewportFlags_NoInputs, we do a search ourselves. -// A) It won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. -// B) It requires Platform_GetWindowFocus to be implemented by back-end. -static ImGuiViewportP* FindHoveredViewportFromPlatformWindowStack(const ImVec2 mouse_platform_pos) +#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) + +#include // Use old API to avoid need for separate .mm file +static PasteboardRef main_clipboard = 0; + +// OSX clipboard implementation +// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! +static void SetClipboardTextFn_DefaultImpl(void*, const char* text) { - ImGuiContext& g = *GImGui; - ImGuiViewportP* best_candidate = NULL; - for (int n = 0; n < g.Viewports.Size; n++) + if (!main_clipboard) + PasteboardCreate(kPasteboardClipboard, &main_clipboard); + PasteboardClear(main_clipboard); + CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); + if (cf_data) { - ImGuiViewportP* viewport = g.Viewports[n]; - if (!(viewport->Flags & (ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_Minimized)) && viewport->GetMainRect().Contains(mouse_platform_pos)) - if (best_candidate == NULL || best_candidate->LastFrontMostStampCount < viewport->LastFrontMostStampCount) - best_candidate = viewport; + PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); + CFRelease(cf_data); } - return best_candidate; } -// Update viewports and monitor infos -// Note that this is running even if 'ImGuiConfigFlags_ViewportsEnable' is not set, in order to clear unused viewports (if any) and update monitor info. -static void ImGui::UpdateViewportsNewFrame() +static const char* GetClipboardTextFn_DefaultImpl(void*) { - ImGuiContext& g = *GImGui; - IM_ASSERT(g.PlatformIO.Viewports.Size <= g.Viewports.Size); + if (!main_clipboard) + PasteboardCreate(kPasteboardClipboard, &main_clipboard); + PasteboardSynchronize(main_clipboard); - // Update Minimized status (we need it first in order to decide if we'll apply Pos/Size of the main viewport) - const bool viewports_enabled = (g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable) != 0; - if (viewports_enabled) + ItemCount item_count = 0; + PasteboardGetItemCount(main_clipboard, &item_count); + for (ItemCount i = 0; i < item_count; i++) { - for (int n = 0; n < g.Viewports.Size; n++) + PasteboardItemID item_id = 0; + PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); + CFArrayRef flavor_type_array = 0; + PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); + for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) { - ImGuiViewportP* viewport = g.Viewports[n]; - const bool platform_funcs_available = viewport->PlatformWindowCreated; - if (g.PlatformIO.Platform_GetWindowMinimized && platform_funcs_available) + CFDataRef cf_data; + if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) { - bool minimized = g.PlatformIO.Platform_GetWindowMinimized(viewport); - if (minimized) - viewport->Flags |= ImGuiViewportFlags_Minimized; - else - viewport->Flags &= ~ImGuiViewportFlags_Minimized; + ImGuiContext& g = *GImGui; + g.ClipboardHandlerData.clear(); + int length = (int)CFDataGetLength(cf_data); + g.ClipboardHandlerData.resize(length + 1); + CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data); + g.ClipboardHandlerData[length] = 0; + CFRelease(cf_data); + return g.ClipboardHandlerData.Data; } } } + return NULL; +} - // Create/update main viewport with current platform position. - // FIXME-VIEWPORT: Size is driven by back-end/user code for backward-compatibility but we should aim to make this more consistent. - ImGuiViewportP* main_viewport = g.Viewports[0]; - IM_ASSERT(main_viewport->ID == IMGUI_VIEWPORT_DEFAULT_ID); - IM_ASSERT(main_viewport->Window == NULL); - ImVec2 main_viewport_pos = viewports_enabled ? g.PlatformIO.Platform_GetWindowPos(main_viewport) : ImVec2(0.0f, 0.0f); - ImVec2 main_viewport_size = g.IO.DisplaySize; - if (viewports_enabled && (main_viewport->Flags & ImGuiViewportFlags_Minimized)) - { - main_viewport_pos = main_viewport->Pos; // Preserve last pos/size when minimized (FIXME: We don't do the same for Size outside of the viewport path) - main_viewport_size = main_viewport->Size; - } - AddUpdateViewport(NULL, IMGUI_VIEWPORT_DEFAULT_ID, main_viewport_pos, main_viewport_size, ImGuiViewportFlags_CanHostOtherWindows); - - g.CurrentDpiScale = 0.0f; - g.CurrentViewport = NULL; - g.MouseViewport = NULL; - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - viewport->Idx = n; - - // Erase unused viewports - if (n > 0 && viewport->LastFrameActive < g.FrameCount - 2) - { - // Clear references to this viewport in windows (window->ViewportId becomes the master data) - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - if (g.Windows[window_n]->Viewport == viewport) - { - g.Windows[window_n]->Viewport = NULL; - g.Windows[window_n]->ViewportOwned = false; - } - if (viewport == g.MouseLastHoveredViewport) - g.MouseLastHoveredViewport = NULL; - g.Viewports.erase(g.Viewports.Data + n); - - // Destroy - IMGUI_DEBUG_LOG_VIEWPORT("Delete Viewport %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); - DestroyPlatformWindow(viewport); // In most circumstances the platform window will already be destroyed here. - IM_ASSERT(g.PlatformIO.Viewports.contains(viewport) == false); - IM_DELETE(viewport); - n--; - continue; - } +#else - const bool platform_funcs_available = viewport->PlatformWindowCreated; - if (viewports_enabled) - { - // Update Position and Size (from Platform Window to ImGui) if requested. - // We do it early in the frame instead of waiting for UpdatePlatformWindows() to avoid a frame of lag when moving/resizing using OS facilities. - if (!(viewport->Flags & ImGuiViewportFlags_Minimized) && platform_funcs_available) - { - if (viewport->PlatformRequestMove) - viewport->Pos = viewport->LastPlatformPos = g.PlatformIO.Platform_GetWindowPos(viewport); - if (viewport->PlatformRequestResize) - viewport->Size = viewport->LastPlatformSize = g.PlatformIO.Platform_GetWindowSize(viewport); - } - } +// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. +static const char* GetClipboardTextFn_DefaultImpl(void*) +{ + ImGuiContext& g = *GImGui; + return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin(); +} - // Update/copy monitor info - UpdateViewportPlatformMonitor(viewport); +static void SetClipboardTextFn_DefaultImpl(void*, const char* text) +{ + ImGuiContext& g = *GImGui; + g.ClipboardHandlerData.clear(); + const char* text_end = text + strlen(text); + g.ClipboardHandlerData.resize((int)(text_end - text) + 1); + memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text)); + g.ClipboardHandlerData[(int)(text_end - text)] = 0; +} - // Lock down space taken by menu bars and status bars, reset the offset for fucntions like BeginMainMenuBar() to alter them again. - viewport->WorkOffsetMin = viewport->CurrWorkOffsetMin; - viewport->WorkOffsetMax = viewport->CurrWorkOffsetMax; - viewport->CurrWorkOffsetMin = viewport->CurrWorkOffsetMax = ImVec2(0.0f, 0.0f); +#endif - // Reset alpha every frame. Users of transparency (docking) needs to request a lower alpha back. - viewport->Alpha = 1.0f; +// Win32 API IME support (for Asian languages, etc.) +#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) - // Translate imgui windows when a Host Viewport has been moved - // (This additionally keeps windows at the same place when ImGuiConfigFlags_ViewportsEnable is toggled!) - const ImVec2 viewport_delta_pos = viewport->Pos - viewport->LastPos; - if ((viewport->Flags & ImGuiViewportFlags_CanHostOtherWindows) && (viewport_delta_pos.x != 0.0f || viewport_delta_pos.y != 0.0f)) - TranslateWindowsInViewport(viewport, viewport->LastPos, viewport->Pos); +#include +#ifdef _MSC_VER +#pragma comment(lib, "imm32") +#endif - // Update DPI scale - float new_dpi_scale; - if (g.PlatformIO.Platform_GetWindowDpiScale && platform_funcs_available) - new_dpi_scale = g.PlatformIO.Platform_GetWindowDpiScale(viewport); - else if (viewport->PlatformMonitor != -1) - new_dpi_scale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; - else - new_dpi_scale = (viewport->DpiScale != 0.0f) ? viewport->DpiScale : 1.0f; - if (viewport->DpiScale != 0.0f && new_dpi_scale != viewport->DpiScale) +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) +{ + // Notify OS Input Method Editor of text input position + ImGuiIO& io = ImGui::GetIO(); + if (HWND hwnd = (HWND)io.ImeWindowHandle) + if (HIMC himc = ::ImmGetContext(hwnd)) { - float scale_factor = new_dpi_scale / viewport->DpiScale; - if (g.IO.ConfigFlags & ImGuiConfigFlags_DpiEnableScaleViewports) - ScaleWindowsInViewport(viewport, scale_factor); - //if (viewport == GetMainViewport()) - // g.PlatformInterface.SetWindowSize(viewport, viewport->Size * scale_factor); - - // Scale our window moving pivot so that the window will rescale roughly around the mouse position. - // FIXME-VIEWPORT: This currently creates a resizing feedback loop when a window is straddling a DPI transition border. - // (Minor: since our sizes do not perfectly linearly scale, deferring the click offset scale until we know the actual window scale ratio may get us slightly more precise mouse positioning.) - //if (g.MovingWindow != NULL && g.MovingWindow->Viewport == viewport) - // g.ActiveIdClickOffset = ImFloor(g.ActiveIdClickOffset * scale_factor); + COMPOSITIONFORM cf; + cf.ptCurrentPos.x = x; + cf.ptCurrentPos.y = y; + cf.dwStyle = CFS_FORCE_POSITION; + ::ImmSetCompositionWindow(himc, &cf); + ::ImmReleaseContext(hwnd, himc); } - viewport->DpiScale = new_dpi_scale; - } - - if (!viewports_enabled) - { - g.MouseViewport = main_viewport; - return; - } +} - // Mouse handling: decide on the actual mouse viewport for this frame between the active/focused viewport and the hovered viewport. - // Note that 'viewport_hovered' should skip over any viewport that has the ImGuiViewportFlags_NoInputs flags set. - ImGuiViewportP* viewport_hovered = NULL; - if (g.IO.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport) - { - viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL; - if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - { - // Back-end failed at honoring its contract if it returned a viewport with the _NoInputs flag. - IM_ASSERT(0); - viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); - } - } - else - { - // If the back-end doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search: - // A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window. - // B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO) - viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); - } - if (viewport_hovered != NULL) - g.MouseLastHoveredViewport = viewport_hovered; - else if (g.MouseLastHoveredViewport == NULL) - g.MouseLastHoveredViewport = g.Viewports[0]; +#else - // Update mouse reference viewport - // (when moving a window we aim at its viewport, but this will be overwritten below if we go in drag and drop mode) - if (g.MovingWindow) - g.MouseViewport = g.MovingWindow->Viewport; - else - g.MouseViewport = g.MouseLastHoveredViewport; +static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} - // When dragging something, always refer to the last hovered viewport. - // - when releasing a moving window we will revert to aiming behind (at viewport_hovered) - // - when we are between viewports, our dragged preview will tend to show in the last viewport _even_ if we don't have tooltips in their viewports (when lacking monitor info) - // - consider the case of holding on a menu item to browse child menus: even thou a mouse button is held, there's no active id because menu items only react on mouse release. - const bool is_mouse_dragging_with_an_expected_destination = g.DragDropActive; - if (is_mouse_dragging_with_an_expected_destination && viewport_hovered == NULL) - viewport_hovered = g.MouseLastHoveredViewport; - if (is_mouse_dragging_with_an_expected_destination || g.ActiveId == 0 || !IsAnyMouseDown()) - if (viewport_hovered != NULL && viewport_hovered != g.MouseViewport && !(viewport_hovered->Flags & ImGuiViewportFlags_NoInputs)) - g.MouseViewport = viewport_hovered; +#endif - IM_ASSERT(g.MouseViewport != NULL); -} +//----------------------------------------------------------------------------- +// [SECTION] METRICS/DEBUG WINDOW +//----------------------------------------------------------------------------- -// Update user-facing viewport list (g.Viewports -> g.PlatformIO.Viewports after filtering out some) -static void ImGui::UpdateViewportsEndFrame() +#ifndef IMGUI_DISABLE_METRICS_WINDOW +// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. +static void MetricsHelpMarker(const char* desc) { - ImGuiContext& g = *GImGui; - g.PlatformIO.MainViewport = g.Viewports[0]; - g.PlatformIO.Viewports.resize(0); - for (int i = 0; i < g.Viewports.Size; i++) + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) { - ImGuiViewportP* viewport = g.Viewports[i]; - viewport->LastPos = viewport->Pos; - if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0.0f || viewport->Size.y <= 0.0f) - if (i > 0) // Always include main viewport in the list - continue; - if (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)) - continue; - if (i > 0) - IM_ASSERT(viewport->Window != NULL); - g.PlatformIO.Viewports.push_back(viewport); + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); } - g.Viewports[0]->ClearRequestFlags(); // Clear main viewport flags because UpdatePlatformWindows() won't do it and may not even be called } -// FIXME: We should ideally refactor the system to call this every frame (we currently don't) -ImGuiViewportP* ImGui::AddUpdateViewport(ImGuiWindow* window, ImGuiID id, const ImVec2& pos, const ImVec2& size, ImGuiViewportFlags flags) +void ImGui::ShowMetricsWindow(bool* p_open) { - ImGuiContext& g = *GImGui; - IM_ASSERT(id != 0); - - if (window != NULL) - { - if (g.MovingWindow && g.MovingWindow->RootWindow == window) - flags |= ImGuiViewportFlags_NoInputs | ImGuiViewportFlags_NoFocusOnAppearing; - if ((window->Flags & ImGuiWindowFlags_NoMouseInputs) && (window->Flags & ImGuiWindowFlags_NoNavInputs)) - flags |= ImGuiViewportFlags_NoInputs; - if (window->Flags & ImGuiWindowFlags_NoFocusOnAppearing) - flags |= ImGuiViewportFlags_NoFocusOnAppearing; - } - - ImGuiViewportP* viewport = (ImGuiViewportP*)FindViewportByID(id); - if (viewport) - { - if (!viewport->PlatformRequestMove) - viewport->Pos = pos; - if (!viewport->PlatformRequestResize) - viewport->Size = size; - viewport->Flags = flags | (viewport->Flags & ImGuiViewportFlags_Minimized); // Preserve existing flags - } - else + if (!ImGui::Begin("Dear ImGui Metrics", p_open)) { - // New viewport - viewport = IM_NEW(ImGuiViewportP)(); - viewport->ID = id; - viewport->Idx = g.Viewports.Size; - viewport->Pos = viewport->LastPos = pos; - viewport->Size = size; - viewport->Flags = flags; - UpdateViewportPlatformMonitor(viewport); - g.Viewports.push_back(viewport); - IMGUI_DEBUG_LOG_VIEWPORT("Add Viewport %08X (%s)\n", id, window->Name); - - // We normally setup for all viewports in NewFrame() but here need to handle the mid-frame creation of a new viewport. - // We need to extend the fullscreen clip rect so the OverlayDrawList clip is correct for that the first frame - g.DrawListSharedData.ClipRectFullscreen.x = ImMin(g.DrawListSharedData.ClipRectFullscreen.x, viewport->Pos.x); - g.DrawListSharedData.ClipRectFullscreen.y = ImMin(g.DrawListSharedData.ClipRectFullscreen.y, viewport->Pos.y); - g.DrawListSharedData.ClipRectFullscreen.z = ImMax(g.DrawListSharedData.ClipRectFullscreen.z, viewport->Pos.x + viewport->Size.x); - g.DrawListSharedData.ClipRectFullscreen.w = ImMax(g.DrawListSharedData.ClipRectFullscreen.w, viewport->Pos.y + viewport->Size.y); - - // Store initial DpiScale before the OS platform window creation, based on expected monitor data. - // This is so we can select an appropriate font size on the first frame of our window lifetime - if (viewport->PlatformMonitor != -1) - viewport->DpiScale = g.PlatformIO.Monitors[viewport->PlatformMonitor].DpiScale; + ImGui::End(); + return; } - viewport->Window = window; - viewport->LastFrameActive = g.FrameCount; - IM_ASSERT(window == NULL || viewport->ID == window->ID); + // Debugging enums + enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type + const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" }; + enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type + const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" }; - if (window != NULL) - window->ViewportOwned = true; - - return viewport; -} - -// FIXME-VIEWPORT: This is all super messy and ought to be clarified or rewritten. -static void ImGui::UpdateSelectWindowViewport(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - ImGuiWindowFlags flags = window->Flags; - window->ViewportAllowPlatformMonitorExtend = -1; - - // Restore main viewport if multi-viewport is not supported by the back-end - ImGuiViewportP* main_viewport = g.Viewports[0]; - if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) - { - SetWindowViewport(window, main_viewport); - return; - } - window->ViewportOwned = false; - - // Appearing popups reset their viewport so they can inherit again - if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && window->Appearing) - { - window->Viewport = NULL; - window->ViewportId = 0; - } - - if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) == 0) - { - // By default inherit from parent window - if (window->Viewport == NULL && window->ParentWindow && !window->ParentWindow->IsFallbackWindow) - window->Viewport = window->ParentWindow->Viewport; - - // Attempt to restore saved viewport id (= window that hasn't been activated yet), try to restore the viewport based on saved 'window->ViewportPos' restored from .ini file - if (window->Viewport == NULL && window->ViewportId != 0) - { - window->Viewport = (ImGuiViewportP*)FindViewportByID(window->ViewportId); - if (window->Viewport == NULL && window->ViewportPos.x != FLT_MAX && window->ViewportPos.y != FLT_MAX) - window->Viewport = AddUpdateViewport(window, window->ID, window->ViewportPos, window->Size, ImGuiViewportFlags_None); - } - } - - bool lock_viewport = false; - if (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasViewport) - { - // Code explicitly request a viewport - window->Viewport = (ImGuiViewportP*)FindViewportByID(g.NextWindowData.ViewportId); - window->ViewportId = g.NextWindowData.ViewportId; // Store ID even if Viewport isn't resolved yet. - lock_viewport = true; - } - else if ((flags & ImGuiWindowFlags_ChildWindow) || (flags & ImGuiWindowFlags_ChildMenu)) - { - // Always inherit viewport from parent window - window->Viewport = window->ParentWindow->Viewport; - } - else if (flags & ImGuiWindowFlags_Tooltip) - { - window->Viewport = g.MouseViewport; - } - else if (GetWindowAlwaysWantOwnViewport(window)) - { - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); - } - else if (g.MovingWindow && g.MovingWindow->RootWindow == window && IsMousePosValid()) - { - if (window->Viewport != NULL && window->Viewport->Window == window) - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_None); - } - else - { - // Merge into host viewport? - // We cannot test window->ViewportOwned as it set lower in the function. - bool try_to_merge_into_host_viewport = (window->Viewport && window == window->Viewport->Window && g.ActiveId == 0); - if (try_to_merge_into_host_viewport) - UpdateTryMergeWindowIntoHostViewports(window); - } - - // Fallback to default viewport - if (window->Viewport == NULL) - window->Viewport = main_viewport; - - // Mark window as allowed to protrude outside of its viewport and into the current monitor - if (!lock_viewport) - { - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) - { - // We need to take account of the possibility that mouse may become invalid. - // Popups/Tooltip always set ViewportAllowPlatformMonitorExtend so GetWindowAllowedExtentRect() will return full monitor bounds. - ImVec2 mouse_ref = (flags & ImGuiWindowFlags_Tooltip) ? g.IO.MousePos : g.BeginPopupStack.back().OpenMousePos; - bool use_mouse_ref = (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow); - bool mouse_valid = IsMousePosValid(&mouse_ref); - if ((window->Appearing || (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_ChildMenu))) && (!use_mouse_ref || mouse_valid)) - window->ViewportAllowPlatformMonitorExtend = FindPlatformMonitorForPos((use_mouse_ref && mouse_valid) ? mouse_ref : NavCalcPreferredRefPos()); - else - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - } - else if (window->Viewport && window != window->Viewport->Window && window->Viewport->Window && !(flags & ImGuiWindowFlags_ChildWindow)) - { - // When called from Begin() we don't have access to a proper version of the Hidden flag yet, so we replicate this code. - const bool will_be_visible = (window->DockIsActive && !window->DockTabIsVisible) ? false : true; - if ((window->Flags & ImGuiWindowFlags_DockNodeHost) && window->Viewport->LastFrameActive < g.FrameCount && will_be_visible) - { - // Steal/transfer ownership - IMGUI_DEBUG_LOG_VIEWPORT("Window '%s' steal Viewport %08X from Window '%s'\n", window->Name, window->Viewport->ID, window->Viewport->Window->Name); - window->Viewport->Window = window; - window->Viewport->ID = window->ID; - window->Viewport->LastNameHash = 0; - } - else if (!UpdateTryMergeWindowIntoHostViewports(window)) // Merge? - { - // New viewport - window->Viewport = AddUpdateViewport(window, window->ID, window->Pos, window->Size, ImGuiViewportFlags_NoFocusOnAppearing); - } - } - else if (window->ViewportAllowPlatformMonitorExtend < 0 && (flags & ImGuiWindowFlags_ChildWindow) == 0) - { - // Regular (non-child, non-popup) windows by default are also allowed to protrude - // Child windows are kept contained within their parent. - window->ViewportAllowPlatformMonitorExtend = window->Viewport->PlatformMonitor; - } - } - - // Update flags - window->ViewportOwned = (window == window->Viewport->Window); - window->ViewportId = window->Viewport->ID; - - // If the OS window has a title bar, hide our imgui title bar - //if (window->ViewportOwned && !(window->Viewport->Flags & ImGuiViewportFlags_NoDecoration)) - // window->Flags |= ImGuiWindowFlags_NoTitleBar; -} - -// Called by user at the end of the main loop, after EndFrame() -// This will handle the creation/update of all OS windows via function defined in the ImGuiPlatformIO api. -void ImGui::UpdatePlatformWindows() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.FrameCountEnded == g.FrameCount && "Forgot to call Render() or EndFrame() before UpdatePlatformWindows()?"); - IM_ASSERT(g.FrameCountPlatformEnded < g.FrameCount); - g.FrameCountPlatformEnded = g.FrameCount; - if (!(g.ConfigFlagsCurrFrame & ImGuiConfigFlags_ViewportsEnable)) - return; - - // Create/resize/destroy platform windows to match each active viewport. - // Skip the main viewport (index 0), which is always fully handled by the application! - for (int i = 1; i < g.Viewports.Size; i++) - { - ImGuiViewportP* viewport = g.Viewports[i]; - - // Destroy platform window if the viewport hasn't been submitted or if it is hosting a hidden window - // (the implicit/fallback Debug##Default window will be registering its viewport then be disabled, causing a dummy DestroyPlatformWindow to be made each frame) - bool destroy_platform_window = false; - destroy_platform_window |= (viewport->LastFrameActive < g.FrameCount - 1); - destroy_platform_window |= (viewport->Window && !IsWindowActiveAndVisible(viewport->Window)); - if (destroy_platform_window) - { - DestroyPlatformWindow(viewport); - continue; - } - - // New windows that appears directly in a new viewport won't always have a size on their first frame - if (viewport->LastFrameActive < g.FrameCount || viewport->Size.x <= 0 || viewport->Size.y <= 0) - continue; - - // Create window - bool is_new_platform_window = (viewport->PlatformWindowCreated == false); - if (is_new_platform_window) - { - IMGUI_DEBUG_LOG_VIEWPORT("Create Platform Window %08X (%s)\n", viewport->ID, viewport->Window ? viewport->Window->Name : "n/a"); - g.PlatformIO.Platform_CreateWindow(viewport); - if (g.PlatformIO.Renderer_CreateWindow != NULL) - g.PlatformIO.Renderer_CreateWindow(viewport); - viewport->LastNameHash = 0; - viewport->LastPlatformPos = viewport->LastPlatformSize = ImVec2(FLT_MAX, FLT_MAX); // By clearing those we'll enforce a call to Platform_SetWindowPos/Size below, before Platform_ShowWindow (FIXME: Is that necessary?) - viewport->LastRendererSize = viewport->Size; // We don't need to call Renderer_SetWindowSize() as it is expected Renderer_CreateWindow() already did it. - viewport->PlatformWindowCreated = true; - } - - // Apply Position and Size (from ImGui to Platform/Renderer back-ends) - if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove) - g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos); - if ((viewport->LastPlatformSize.x != viewport->Size.x || viewport->LastPlatformSize.y != viewport->Size.y) && !viewport->PlatformRequestResize) - g.PlatformIO.Platform_SetWindowSize(viewport, viewport->Size); - if ((viewport->LastRendererSize.x != viewport->Size.x || viewport->LastRendererSize.y != viewport->Size.y) && g.PlatformIO.Renderer_SetWindowSize) - g.PlatformIO.Renderer_SetWindowSize(viewport, viewport->Size); - viewport->LastPlatformPos = viewport->Pos; - viewport->LastPlatformSize = viewport->LastRendererSize = viewport->Size; - - // Update title bar (if it changed) - if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(viewport->Window)) - { - const char* title_begin = window_for_title->Name; - char* title_end = (char*)(intptr_t)FindRenderedTextEnd(title_begin); - const ImGuiID title_hash = ImHashStr(title_begin, title_end - title_begin); - if (viewport->LastNameHash != title_hash) - { - char title_end_backup_c = *title_end; - *title_end = 0; // Cut existing buffer short instead of doing an alloc/free, no small gain. - g.PlatformIO.Platform_SetWindowTitle(viewport, title_begin); - *title_end = title_end_backup_c; - viewport->LastNameHash = title_hash; - } - } - - // Update alpha (if it changed) - if (viewport->LastAlpha != viewport->Alpha && g.PlatformIO.Platform_SetWindowAlpha) - g.PlatformIO.Platform_SetWindowAlpha(viewport, viewport->Alpha); - viewport->LastAlpha = viewport->Alpha; - - // Optional, general purpose call to allow the back-end to perform general book-keeping even if things haven't changed. - if (g.PlatformIO.Platform_UpdateWindow) - g.PlatformIO.Platform_UpdateWindow(viewport); - - if (is_new_platform_window) - { - // On startup ensure new platform window don't steal focus (give it a few frames, as nested contents may lead to viewport being created a few frames late) - if (g.FrameCount < 3) - viewport->Flags |= ImGuiViewportFlags_NoFocusOnAppearing; - - // Show window - g.PlatformIO.Platform_ShowWindow(viewport); - - // Even without focus, we assume the window becomes front-most. - // This is useful for our platform z-order heuristic when io.MouseHoveredViewport is not available. - if (viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) - viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; - } - - // Clear request flags - viewport->ClearRequestFlags(); - } - - // Update our implicit z-order knowledge of platform windows, which is used when the back-end cannot provide io.MouseHoveredViewport. - // When setting Platform_GetWindowFocus, it is expected that the platform back-end can handle calls without crashing if it doesn't have data stored. - if (g.PlatformIO.Platform_GetWindowFocus != NULL) - { - ImGuiViewportP* focused_viewport = NULL; - for (int n = 0; n < g.Viewports.Size && focused_viewport == NULL; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - if (viewport->PlatformWindowCreated) - if (g.PlatformIO.Platform_GetWindowFocus(viewport)) - focused_viewport = viewport; - } - if (focused_viewport && g.PlatformLastFocusedViewport != focused_viewport->ID) - { - if (focused_viewport->LastFrontMostStampCount != g.ViewportFrontMostStampCount) - focused_viewport->LastFrontMostStampCount = ++g.ViewportFrontMostStampCount; - g.PlatformLastFocusedViewport = focused_viewport->ID; - } - } -} - -// This is a default/basic function for performing the rendering/swap of multiple Platform Windows. -// Custom renderers may prefer to not call this function at all, and instead iterate the publicly exposed platform data and handle rendering/sync themselves. -// The Render/Swap functions stored in ImGuiPlatformIO are merely here to allow for this helper to exist, but you can do it yourself: -// -// ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); -// for (int i = 1; i < platform_io.Viewports.Size; i++) -// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) -// MyRenderFunction(platform_io.Viewports[i], my_args); -// for (int i = 1; i < platform_io.Viewports.Size; i++) -// if ((platform_io.Viewports[i]->Flags & ImGuiViewportFlags_Minimized) == 0) -// MySwapBufferFunction(platform_io.Viewports[i], my_args); -// -void ImGui::RenderPlatformWindowsDefault(void* platform_render_arg, void* renderer_render_arg) -{ - // Skip the main viewport (index 0), which is always fully handled by the application! - ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); - for (int i = 1; i < platform_io.Viewports.Size; i++) - { - ImGuiViewport* viewport = platform_io.Viewports[i]; - if (viewport->Flags & ImGuiViewportFlags_Minimized) - continue; - if (platform_io.Platform_RenderWindow) platform_io.Platform_RenderWindow(viewport, platform_render_arg); - if (platform_io.Renderer_RenderWindow) platform_io.Renderer_RenderWindow(viewport, renderer_render_arg); - } - for (int i = 1; i < platform_io.Viewports.Size; i++) - { - ImGuiViewport* viewport = platform_io.Viewports[i]; - if (viewport->Flags & ImGuiViewportFlags_Minimized) - continue; - if (platform_io.Platform_SwapBuffers) platform_io.Platform_SwapBuffers(viewport, platform_render_arg); - if (platform_io.Renderer_SwapBuffers) platform_io.Renderer_SwapBuffers(viewport, renderer_render_arg); - } -} - -static int ImGui::FindPlatformMonitorForPos(const ImVec2& pos) -{ - ImGuiContext& g = *GImGui; - for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size; monitor_n++) - { - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - if (ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize).Contains(pos)) - return monitor_n; - } - return -1; -} - -// Search for the monitor with the largest intersection area with the given rectangle -// We generally try to avoid searching loops but the monitor count should be very small here -// FIXME-OPT: We could test the last monitor used for that viewport first, and early -static int ImGui::FindPlatformMonitorForRect(const ImRect& rect) -{ - ImGuiContext& g = *GImGui; - - const int monitor_count = g.PlatformIO.Monitors.Size; - if (monitor_count <= 1) - return monitor_count - 1; - - // Use a minimum threshold of 1.0f so a zero-sized rect won't false positive, and will still find the correct monitor given its position. - // This is necessary for tooltips which always resize down to zero at first. - const float surface_threshold = ImMax(rect.GetWidth() * rect.GetHeight() * 0.5f, 1.0f); - int best_monitor_n = -1; - float best_monitor_surface = 0.001f; - - for (int monitor_n = 0; monitor_n < g.PlatformIO.Monitors.Size && best_monitor_surface < surface_threshold; monitor_n++) - { - const ImGuiPlatformMonitor& monitor = g.PlatformIO.Monitors[monitor_n]; - const ImRect monitor_rect = ImRect(monitor.MainPos, monitor.MainPos + monitor.MainSize); - if (monitor_rect.Contains(rect)) - return monitor_n; - ImRect overlapping_rect = rect; - overlapping_rect.ClipWithFull(monitor_rect); - float overlapping_surface = overlapping_rect.GetWidth() * overlapping_rect.GetHeight(); - if (overlapping_surface < best_monitor_surface) - continue; - best_monitor_surface = overlapping_surface; - best_monitor_n = monitor_n; - } - return best_monitor_n; -} - -// Update monitor from viewport rectangle (we'll use this info to clamp windows and save windows lost in a removed monitor) -static void ImGui::UpdateViewportPlatformMonitor(ImGuiViewportP* viewport) -{ - viewport->PlatformMonitor = (short)FindPlatformMonitorForRect(viewport->GetMainRect()); -} - -void ImGui::DestroyPlatformWindow(ImGuiViewportP* viewport) -{ - ImGuiContext& g = *GImGui; - if (viewport->PlatformWindowCreated) - { - if (g.PlatformIO.Renderer_DestroyWindow) - g.PlatformIO.Renderer_DestroyWindow(viewport); - if (g.PlatformIO.Platform_DestroyWindow) - g.PlatformIO.Platform_DestroyWindow(viewport); - IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL); - - // Don't clear PlatformWindowCreated for the main viewport, as we initially set that up to true in Initialize() - // The right-er way may be to leave it to the back-end to set this flag all-together, and made the flag public. - if (viewport->ID != IMGUI_VIEWPORT_DEFAULT_ID) - viewport->PlatformWindowCreated = false; - } - else - { - IM_ASSERT(viewport->RendererUserData == NULL && viewport->PlatformUserData == NULL && viewport->PlatformHandle == NULL); - } - viewport->RendererUserData = viewport->PlatformUserData = viewport->PlatformHandle = NULL; - viewport->ClearRequestFlags(); -} - -void ImGui::DestroyPlatformWindows() -{ - // We call the destroy window on every viewport (including the main viewport, index 0) to give a chance to the back-end - // to clear any data they may have stored in e.g. PlatformUserData, RendererUserData. - // It is convenient for the platform back-end code to store something in the main viewport, in order for e.g. the mouse handling - // code to operator a consistent manner. - // It is expected that the back-end can handle calls to Renderer_DestroyWindow/Platform_DestroyWindow without - // crashing if it doesn't have data stored. - ImGuiContext& g = *GImGui; - for (int i = 0; i < g.Viewports.Size; i++) - DestroyPlatformWindow(g.Viewports[i]); -} - - -//----------------------------------------------------------------------------- -// [SECTION] DOCKING -//----------------------------------------------------------------------------- -// Docking: Internal Types -// Docking: Forward Declarations -// Docking: ImGuiDockContext -// Docking: ImGuiDockContext Docking/Undocking functions -// Docking: ImGuiDockNode -// Docking: ImGuiDockNode Tree manipulation functions -// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) -// Docking: Builder Functions -// Docking: Begin/End Support Functions (called from Begin/End) -// Docking: Settings -//----------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -// Docking: Internal Types -//----------------------------------------------------------------------------- -// - ImGuiDockRequestType -// - ImGuiDockRequest -// - ImGuiDockPreviewData -// - ImGuiDockNodeSettings -// - ImGuiDockContext -//----------------------------------------------------------------------------- - -enum ImGuiDockRequestType -{ - ImGuiDockRequestType_None = 0, - ImGuiDockRequestType_Dock, - ImGuiDockRequestType_Undock, - ImGuiDockRequestType_Split // Split is the same as Dock but without a DockPayload -}; - -struct ImGuiDockRequest -{ - ImGuiDockRequestType Type; - ImGuiWindow* DockTargetWindow; // Destination/Target Window to dock into (may be a loose window or a DockNode, might be NULL in which case DockTargetNode cannot be NULL) - ImGuiDockNode* DockTargetNode; // Destination/Target Node to dock into - ImGuiWindow* DockPayload; // Source/Payload window to dock (may be a loose window or a DockNode), [Optional] - ImGuiDir DockSplitDir; - float DockSplitRatio; - bool DockSplitOuter; - ImGuiWindow* UndockTargetWindow; - ImGuiDockNode* UndockTargetNode; - - ImGuiDockRequest() - { - Type = ImGuiDockRequestType_None; - DockTargetWindow = DockPayload = UndockTargetWindow = NULL; - DockTargetNode = UndockTargetNode = NULL; - DockSplitDir = ImGuiDir_None; - DockSplitRatio = 0.5f; - DockSplitOuter = false; - } -}; - -struct ImGuiDockPreviewData -{ - ImGuiDockNode FutureNode; - bool IsDropAllowed; - bool IsCenterAvailable; - bool IsSidesAvailable; // Hold your breath, grammar freaks.. - bool IsSplitDirExplicit; // Set when hovered the drop rect (vs. implicit SplitDir==None when hovered the window) - ImGuiDockNode* SplitNode; - ImGuiDir SplitDir; - float SplitRatio; - ImRect DropRectsDraw[ImGuiDir_COUNT + 1]; // May be slightly different from hit-testing drop rects used in DockNodeCalcDropRects() - - ImGuiDockPreviewData() : FutureNode(0) { IsDropAllowed = IsCenterAvailable = IsSidesAvailable = IsSplitDirExplicit = false; SplitNode = NULL; SplitDir = ImGuiDir_None; SplitRatio = 0.f; } -}; - -// Persistent Settings data, stored contiguously in SettingsNodes (sizeof() ~32 bytes) -struct ImGuiDockNodeSettings -{ - ImGuiID ID; - ImGuiID ParentNodeId; - ImGuiID ParentWindowId; - ImGuiID SelectedWindowId; - signed char SplitAxis; - char Depth; - ImGuiDockNodeFlags Flags; // NB: We save individual flags one by one in ascii format (ImGuiDockNodeFlags_SavedFlagsMask_) - ImVec2ih Pos; - ImVec2ih Size; - ImVec2ih SizeRef; - ImGuiDockNodeSettings() { ID = ParentNodeId = ParentWindowId = SelectedWindowId = 0; SplitAxis = ImGuiAxis_None; Depth = 0; Flags = ImGuiDockNodeFlags_None; } -}; - -//----------------------------------------------------------------------------- -// Docking: Forward Declarations -//----------------------------------------------------------------------------- - -namespace ImGui -{ - // ImGuiDockContext - static ImGuiDockNode* DockContextAddNode(ImGuiContext* ctx, ImGuiID id); - static void DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node); - static void DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node); - static void DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req); - static void DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref = true); - static void DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); - static void DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx); - static ImGuiDockNode* DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id); - static ImGuiDockNode* DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window); - static void DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count); - static void DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id); // Use root_id==0 to add all - - // ImGuiDockNode - static void DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar); - static void DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); - static void DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node); - static ImGuiWindow* DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id); - static void DockNodeApplyPosSizeToWindows(ImGuiDockNode* node); - static void DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id); - static void DockNodeHideHostWindow(ImGuiDockNode* node); - static void DockNodeUpdate(ImGuiDockNode* node); - static void DockNodeUpdateForRootNode(ImGuiDockNode* node); - static void DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node); - static void DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window); - static void DockNodeAddTabBar(ImGuiDockNode* node); - static void DockNodeRemoveTabBar(ImGuiDockNode* node); - static ImGuiID DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar); - static void DockNodeUpdateVisibleFlag(ImGuiDockNode* node); - static void DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window); - static bool DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* payload_window); - static void DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, ImGuiDockPreviewData* preview_data, bool is_explicit_target, bool is_outer_docking); - static void DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* payload_window, const ImGuiDockPreviewData* preview_data); - static void DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos); - static void DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired); - static bool DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_draw, bool outer_docking, ImVec2* test_mouse_pos); - static const char* DockNodeGetHostWindowTitle(ImGuiDockNode* node, char* buf, int buf_size) { ImFormatString(buf, buf_size, "##DockNode_%02X", node->ID); return buf; } - static int DockNodeGetTabOrder(ImGuiWindow* window); - - // ImGuiDockNode tree manipulations - static void DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_first_child, float split_ratio, ImGuiDockNode* new_node); - static void DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child); - static void DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes = false); - static void DockNodeTreeUpdateSplitter(ImGuiDockNode* node); - static ImGuiDockNode* DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos); - static ImGuiDockNode* DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node); - - // Settings - static void DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id); - static void DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count); - static ImGuiDockNodeSettings* DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID node_id); - static void DockSettingsHandler_ClearAll(ImGuiContext*, ImGuiSettingsHandler*); - static void DockSettingsHandler_ApplyAll(ImGuiContext*, ImGuiSettingsHandler*); - static void* DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name); - static void DockSettingsHandler_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line); - static void DockSettingsHandler_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf); -} - -//----------------------------------------------------------------------------- -// Docking: ImGuiDockContext -//----------------------------------------------------------------------------- -// The lifetime model is different from the one of regular windows: we always create a ImGuiDockNode for each ImGuiDockNodeSettings, -// or we always hold the entire docking node tree. Nodes are frequently hidden, e.g. if the window(s) or child nodes they host are not active. -// At boot time only, we run a simple GC to remove nodes that have no references. -// Because dock node settings (which are small, contiguous structures) are always mirrored by their corresponding dock nodes (more complete structures), -// we can also very easily recreate the nodes from scratch given the settings data (this is what DockContextRebuild() does). -// This is convenient as docking reconfiguration can be implemented by mostly poking at the simpler settings data. -//----------------------------------------------------------------------------- -// - DockContextInitialize() -// - DockContextShutdown() -// - DockContextClearNodes() -// - DockContextRebuildNodes() -// - DockContextUpdateUndocking() -// - DockContextUpdateDocking() -// - DockContextFindNodeByID() -// - DockContextBindNodeToWindow() -// - DockContextGenNodeID() -// - DockContextAddNode() -// - DockContextRemoveNode() -// - ImGuiDockContextPruneNodeData -// - DockContextPruneUnusedSettingsNodes() -// - DockContextBuildNodesFromSettings() -// - DockContextBuildAddWindowsToNodes() -//----------------------------------------------------------------------------- - -void ImGui::DockContextInitialize(ImGuiContext* ctx) -{ - ImGuiContext& g = *ctx; - - // Add .ini handle for persistent docking data - ImGuiSettingsHandler ini_handler; - ini_handler.TypeName = "Docking"; - ini_handler.TypeHash = ImHashStr("Docking"); - ini_handler.ClearAllFn = DockSettingsHandler_ClearAll; - ini_handler.ReadInitFn = DockSettingsHandler_ClearAll; // Also clear on read - ini_handler.ReadOpenFn = DockSettingsHandler_ReadOpen; - ini_handler.ReadLineFn = DockSettingsHandler_ReadLine; - ini_handler.ApplyAllFn = DockSettingsHandler_ApplyAll; - ini_handler.WriteAllFn = DockSettingsHandler_WriteAll; - g.SettingsHandlers.push_back(ini_handler); -} - -void ImGui::DockContextShutdown(ImGuiContext* ctx) -{ - ImGuiDockContext* dc = &ctx->DockContext; - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - IM_DELETE(node); -} - -void ImGui::DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs) -{ - IM_UNUSED(ctx); - IM_ASSERT(ctx == GImGui); - DockBuilderRemoveNodeDockedWindows(root_id, clear_settings_refs); - DockBuilderRemoveNodeChildNodes(root_id); -} - -// [DEBUG] This function also acts as a defacto test to make sure we can rebuild from scratch without a glitch -// (Different from DockSettingsHandler_ClearAll() + DockSettingsHandler_ApplyAll() because this reuses current settings!) -void ImGui::DockContextRebuildNodes(ImGuiContext* ctx) -{ - IMGUI_DEBUG_LOG_DOCKING("DockContextRebuild()\n"); - ImGuiDockContext* dc = &ctx->DockContext; - SaveIniSettingsToMemory(); - ImGuiID root_id = 0; // Rebuild all - DockContextClearNodes(ctx, root_id, false); - DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size); - DockContextBuildAddWindowsToNodes(ctx, root_id); -} - -// Docking context update function, called by NewFrame() -void ImGui::DockContextUpdateUndocking(ImGuiContext* ctx) -{ - ImGuiContext& g = *ctx; - ImGuiDockContext* dc = &ctx->DockContext; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) - { - if (dc->Nodes.Data.Size > 0 || dc->Requests.Size > 0) - DockContextClearNodes(ctx, 0, true); - return; - } - - // Setting NoSplit at runtime merges all nodes - if (g.IO.ConfigDockingNoSplit) - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (node->IsRootNode() && node->IsSplitNode()) - { - DockBuilderRemoveNodeChildNodes(node->ID); - //dc->WantFullRebuild = true; - } - - // Process full rebuild -#if 0 - if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_C))) - dc->WantFullRebuild = true; -#endif - if (dc->WantFullRebuild) - { - DockContextRebuildNodes(ctx); - dc->WantFullRebuild = false; - } - - // Process Undocking requests (we need to process them _before_ the UpdateMouseMovingWindowNewFrame call in NewFrame) - for (int n = 0; n < dc->Requests.Size; n++) - { - ImGuiDockRequest* req = &dc->Requests[n]; - if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetWindow) - DockContextProcessUndockWindow(ctx, req->UndockTargetWindow); - else if (req->Type == ImGuiDockRequestType_Undock && req->UndockTargetNode) - DockContextProcessUndockNode(ctx, req->UndockTargetNode); - } -} - -// Docking context update function, called by NewFrame() -void ImGui::DockContextUpdateDocking(ImGuiContext* ctx) -{ - ImGuiContext& g = *ctx; - ImGuiDockContext* dc = &ctx->DockContext; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) - return; - - // Process Docking requests - for (int n = 0; n < dc->Requests.Size; n++) - if (dc->Requests[n].Type == ImGuiDockRequestType_Dock) - DockContextProcessDock(ctx, &dc->Requests[n]); - dc->Requests.resize(0); - - // Create windows for each automatic docking nodes - // We can have NULL pointers when we delete nodes, but because ID are recycled this should amortize nicely (and our node count will never be very high) - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (node->IsFloatingNode()) - DockNodeUpdate(node); -} - -static ImGuiDockNode* ImGui::DockContextFindNodeByID(ImGuiContext* ctx, ImGuiID id) -{ - return (ImGuiDockNode*)ctx->DockContext.Nodes.GetVoidPtr(id); -} - -ImGuiID ImGui::DockContextGenNodeID(ImGuiContext* ctx) -{ - // Generate an ID for new node (the exact ID value doesn't matter as long as it is not already used) - // FIXME-OPT FIXME-DOCK: This is suboptimal, even if the node count is small enough not to be a worry. We should poke in ctx->Nodes to find a suitable ID faster. - ImGuiID id = 0x0001; - while (DockContextFindNodeByID(ctx, id) != NULL) - id++; - return id; -} - -static ImGuiDockNode* ImGui::DockContextAddNode(ImGuiContext* ctx, ImGuiID id) -{ - // Generate an ID for the new node (the exact ID value doesn't matter as long as it is not already used) and add the first window. - if (id == 0) - id = DockContextGenNodeID(ctx); - else - IM_ASSERT(DockContextFindNodeByID(ctx, id) == NULL); - - // We don't set node->LastFrameAlive on construction. Nodes are always created at all time to reflect .ini settings! - IMGUI_DEBUG_LOG_DOCKING("DockContextAddNode 0x%08X\n", id); - ImGuiDockNode* node = IM_NEW(ImGuiDockNode)(id); - ctx->DockContext.Nodes.SetVoidPtr(node->ID, node); - return node; -} - -static void ImGui::DockContextRemoveNode(ImGuiContext* ctx, ImGuiDockNode* node, bool merge_sibling_into_parent_node) -{ - ImGuiContext& g = *ctx; - ImGuiDockContext* dc = &ctx->DockContext; - - IMGUI_DEBUG_LOG_DOCKING("DockContextRemoveNode 0x%08X\n", node->ID); - IM_ASSERT(DockContextFindNodeByID(ctx, node->ID) == node); - IM_ASSERT(node->ChildNodes[0] == NULL && node->ChildNodes[1] == NULL); - IM_ASSERT(node->Windows.Size == 0); - - if (node->HostWindow) - node->HostWindow->DockNodeAsHost = NULL; - - ImGuiDockNode* parent_node = node->ParentNode; - const bool merge = (merge_sibling_into_parent_node && parent_node != NULL); - if (merge) - { - IM_ASSERT(parent_node->ChildNodes[0] == node || parent_node->ChildNodes[1] == node); - ImGuiDockNode* sibling_node = (parent_node->ChildNodes[0] == node ? parent_node->ChildNodes[1] : parent_node->ChildNodes[0]); - DockNodeTreeMerge(&g, parent_node, sibling_node); - } - else - { - for (int n = 0; parent_node && n < IM_ARRAYSIZE(parent_node->ChildNodes); n++) - if (parent_node->ChildNodes[n] == node) - node->ParentNode->ChildNodes[n] = NULL; - dc->Nodes.SetVoidPtr(node->ID, NULL); - IM_DELETE(node); - } -} - -static int IMGUI_CDECL DockNodeComparerDepthMostFirst(const void* lhs, const void* rhs) -{ - const ImGuiDockNode* a = *(const ImGuiDockNode* const*)lhs; - const ImGuiDockNode* b = *(const ImGuiDockNode* const*)rhs; - return ImGui::DockNodeGetDepth(b) - ImGui::DockNodeGetDepth(a); -} - -// Pre C++0x doesn't allow us to use a function-local type (without linkage) as template parameter, so we moved this here. -struct ImGuiDockContextPruneNodeData -{ - int CountWindows, CountChildWindows, CountChildNodes; - ImGuiID RootId; - ImGuiDockContextPruneNodeData() { CountWindows = CountChildWindows = CountChildNodes = 0; RootId = 0; } -}; - -// Garbage collect unused nodes (run once at init time) -static void ImGui::DockContextPruneUnusedSettingsNodes(ImGuiContext* ctx) -{ - ImGuiContext& g = *ctx; - ImGuiDockContext* dc = &ctx->DockContext; - IM_ASSERT(g.Windows.Size == 0); - - ImPool pool; - pool.Reserve(dc->NodesSettings.Size); - - // Count child nodes and compute RootID - for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) - { - ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; - ImGuiDockContextPruneNodeData* parent_data = settings->ParentNodeId ? pool.GetByKey(settings->ParentNodeId) : 0; - pool.GetOrAddByKey(settings->ID)->RootId = parent_data ? parent_data->RootId : settings->ID; - if (settings->ParentNodeId) - pool.GetOrAddByKey(settings->ParentNodeId)->CountChildNodes++; - } - - // Count reference to dock ids from dockspaces - // We track the 'auto-DockNode <- manual-Window <- manual-DockSpace' in order to avoid 'auto-DockNode' being ditched by DockContextPruneUnusedSettingsNodes() - for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) - { - ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; - if (settings->ParentWindowId != 0) - if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->ParentWindowId)) - if (window_settings->DockId) - if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(window_settings->DockId)) - data->CountChildNodes++; - } - - // Count reference to dock ids from window settings - // We guard against the possibility of an invalid .ini file (RootID may point to a missing node) - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (ImGuiID dock_id = settings->DockId) - if (ImGuiDockContextPruneNodeData* data = pool.GetByKey(dock_id)) - { - data->CountWindows++; - if (ImGuiDockContextPruneNodeData* data_root = (data->RootId == dock_id) ? data : pool.GetByKey(data->RootId)) - data_root->CountChildWindows++; - } - - // Prune - for (int settings_n = 0; settings_n < dc->NodesSettings.Size; settings_n++) - { - ImGuiDockNodeSettings* settings = &dc->NodesSettings[settings_n]; - ImGuiDockContextPruneNodeData* data = pool.GetByKey(settings->ID); - if (data->CountWindows > 1) - continue; - ImGuiDockContextPruneNodeData* data_root = (data->RootId == settings->ID) ? data : pool.GetByKey(data->RootId); - - bool remove = false; - remove |= (data->CountWindows == 1 && settings->ParentNodeId == 0 && data->CountChildNodes == 0 && !(settings->Flags & ImGuiDockNodeFlags_CentralNode)); // Floating root node with only 1 window - remove |= (data->CountWindows == 0 && settings->ParentNodeId == 0 && data->CountChildNodes == 0); // Leaf nodes with 0 window - remove |= (data_root->CountChildWindows == 0); - if (remove) - { - IMGUI_DEBUG_LOG_DOCKING("DockContextPruneUnusedSettingsNodes: Prune 0x%08X\n", settings->ID); - DockSettingsRemoveNodeReferences(&settings->ID, 1); - settings->ID = 0; - } - } -} - -static void ImGui::DockContextBuildNodesFromSettings(ImGuiContext* ctx, ImGuiDockNodeSettings* node_settings_array, int node_settings_count) -{ - // Build nodes - for (int node_n = 0; node_n < node_settings_count; node_n++) - { - ImGuiDockNodeSettings* settings = &node_settings_array[node_n]; - if (settings->ID == 0) - continue; - ImGuiDockNode* node = DockContextAddNode(ctx, settings->ID); - node->ParentNode = settings->ParentNodeId ? DockContextFindNodeByID(ctx, settings->ParentNodeId) : NULL; - node->Pos = ImVec2(settings->Pos.x, settings->Pos.y); - node->Size = ImVec2(settings->Size.x, settings->Size.y); - node->SizeRef = ImVec2(settings->SizeRef.x, settings->SizeRef.y); - node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_DockNode; - if (node->ParentNode && node->ParentNode->ChildNodes[0] == NULL) - node->ParentNode->ChildNodes[0] = node; - else if (node->ParentNode && node->ParentNode->ChildNodes[1] == NULL) - node->ParentNode->ChildNodes[1] = node; - node->SelectedTabId = settings->SelectedWindowId; - node->SplitAxis = (ImGuiAxis)settings->SplitAxis; - node->LocalFlags |= (settings->Flags & ImGuiDockNodeFlags_SavedFlagsMask_); - - // Bind host window immediately if it already exist (in case of a rebuild) - // This is useful as the RootWindowForTitleBarHighlight links necessary to highlight the currently focused node requires node->HostWindow to be set. - char host_window_title[20]; - ImGuiDockNode* root_node = DockNodeGetRootNode(node); - node->HostWindow = FindWindowByName(DockNodeGetHostWindowTitle(root_node, host_window_title, IM_ARRAYSIZE(host_window_title))); - } -} - -void ImGui::DockContextBuildAddWindowsToNodes(ImGuiContext* ctx, ImGuiID root_id) -{ - // Rebind all windows to nodes (they can also lazily rebind but we'll have a visible glitch during the first frame) - ImGuiContext& g = *ctx; - for (int n = 0; n < g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - if (window->DockId == 0 || window->LastFrameActive < g.FrameCount - 1) - continue; - if (window->DockNode != NULL) - continue; - - ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId); - IM_ASSERT(node != NULL); // This should have been called after DockContextBuildNodesFromSettings() - if (root_id == 0 || DockNodeGetRootNode(node)->ID == root_id) - DockNodeAddWindow(node, window, true); - } -} - -//----------------------------------------------------------------------------- -// Docking: ImGuiDockContext Docking/Undocking functions -//----------------------------------------------------------------------------- -// - DockContextQueueDock() -// - DockContextQueueUndockWindow() -// - DockContextQueueUndockNode() -// - DockContextQueueNotifyRemovedNode() -// - DockContextProcessDock() -// - DockContextProcessUndockWindow() -// - DockContextProcessUndockNode() -// - DockContextCalcDropPosForDocking() -//----------------------------------------------------------------------------- - -void ImGui::DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer) -{ - IM_ASSERT(target != payload); - ImGuiDockRequest req; - req.Type = ImGuiDockRequestType_Dock; - req.DockTargetWindow = target; - req.DockTargetNode = target_node; - req.DockPayload = payload; - req.DockSplitDir = split_dir; - req.DockSplitRatio = split_ratio; - req.DockSplitOuter = split_outer; - ctx->DockContext.Requests.push_back(req); -} - -void ImGui::DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window) -{ - ImGuiDockRequest req; - req.Type = ImGuiDockRequestType_Undock; - req.UndockTargetWindow = window; - ctx->DockContext.Requests.push_back(req); -} - -void ImGui::DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) -{ - ImGuiDockRequest req; - req.Type = ImGuiDockRequestType_Undock; - req.UndockTargetNode = node; - ctx->DockContext.Requests.push_back(req); -} - -void ImGui::DockContextQueueNotifyRemovedNode(ImGuiContext* ctx, ImGuiDockNode* node) -{ - ImGuiDockContext* dc = &ctx->DockContext; - for (int n = 0; n < dc->Requests.Size; n++) - if (dc->Requests[n].DockTargetNode == node) - dc->Requests[n].Type = ImGuiDockRequestType_None; -} - -void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) -{ - IM_ASSERT((req->Type == ImGuiDockRequestType_Dock && req->DockPayload != NULL) || (req->Type == ImGuiDockRequestType_Split && req->DockPayload == NULL)); - IM_ASSERT(req->DockTargetWindow != NULL || req->DockTargetNode != NULL); - - ImGuiContext& g = *ctx; - IM_UNUSED(g); - - ImGuiWindow* payload_window = req->DockPayload; // Optional - ImGuiWindow* target_window = req->DockTargetWindow; - ImGuiDockNode* node = req->DockTargetNode; - if (payload_window) - IMGUI_DEBUG_LOG_DOCKING("DockContextProcessDock node 0x%08X target '%s' dock window '%s', split_dir %d\n", node ? node->ID : 0, target_window ? target_window->Name : "NULL", payload_window ? payload_window->Name : "NULL", req->DockSplitDir); - else - IMGUI_DEBUG_LOG_DOCKING("DockContextProcessDock node 0x%08X, split_dir %d\n", node ? node->ID : 0, req->DockSplitDir); - - // Decide which Tab will be selected at the end of the operation - ImGuiID next_selected_id = 0; - ImGuiDockNode* payload_node = NULL; - if (payload_window) - { - payload_node = payload_window->DockNodeAsHost; - payload_window->DockNodeAsHost = NULL; // Important to clear this as the node will have its life as a child which might be merged/deleted later. - if (payload_node && payload_node->IsLeafNode()) - next_selected_id = payload_node->TabBar->NextSelectedTabId ? payload_node->TabBar->NextSelectedTabId : payload_node->TabBar->SelectedTabId; - if (payload_node == NULL) - next_selected_id = payload_window->ID; - } - - // FIXME-DOCK: When we are trying to dock an existing single-window node into a loose window, transfer Node ID as well - // When processing an interactive split, usually LastFrameAlive will be < g.FrameCount. But DockBuilder operations can make it ==. - if (node) - IM_ASSERT(node->LastFrameAlive <= g.FrameCount); - if (node && target_window && node == target_window->DockNodeAsHost) - IM_ASSERT(node->Windows.Size > 0 || node->IsSplitNode() || node->IsCentralNode()); - - // Create new node and add existing window to it - if (node == NULL) - { - node = DockContextAddNode(ctx, 0); - node->Pos = target_window->Pos; - node->Size = target_window->Size; - if (target_window->DockNodeAsHost == NULL) - { - DockNodeAddWindow(node, target_window, true); - node->TabBar->Tabs[0].Flags &= ~ImGuiTabItemFlags_Unsorted; - target_window->DockIsActive = true; - } - } - - ImGuiDir split_dir = req->DockSplitDir; - if (split_dir != ImGuiDir_None) - { - // Split into one, one side will be our payload node unless we are dropping a loose window - const ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; - const int split_inheritor_child_idx = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0; // Current contents will be moved to the opposite side - const float split_ratio = req->DockSplitRatio; - DockNodeTreeSplit(ctx, node, split_axis, split_inheritor_child_idx, split_ratio, payload_node); // payload_node may be NULL here! - ImGuiDockNode* new_node = node->ChildNodes[split_inheritor_child_idx ^ 1]; - new_node->HostWindow = node->HostWindow; - node = new_node; - } - node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; - - if (node != payload_node) - { - // Create tab bar before we call DockNodeMoveWindows (which would attempt to move the old tab-bar, which would lead us to payload tabs wrongly appearing before target tabs!) - if (node->Windows.Size > 0 && node->TabBar == NULL) - { - DockNodeAddTabBar(node); - for (int n = 0; n < node->Windows.Size; n++) - TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]); - } - - if (payload_node != NULL) - { - // Transfer full payload node (with 1+ child windows or child nodes) - if (payload_node->IsSplitNode()) - { - if (node->Windows.Size > 0) - { - // We can dock a split payload into a node that already has windows _only_ if our payload is a node tree with a single visible node. - // In this situation, we move the windows of the target node into the currently visible node of the payload. - // This allows us to preserve some of the underlying dock tree settings nicely. - IM_ASSERT(payload_node->OnlyNodeWithWindows != NULL); // The docking should have been blocked by DockNodePreviewDockSetup() early on and never submitted. - ImGuiDockNode* visible_node = payload_node->OnlyNodeWithWindows; - if (visible_node->TabBar) - IM_ASSERT(visible_node->TabBar->Tabs.Size > 0); - DockNodeMoveWindows(node, visible_node); - DockNodeMoveWindows(visible_node, node); - DockSettingsRenameNodeReferences(node->ID, visible_node->ID); - } - if (node->IsCentralNode()) - { - // Central node property needs to be moved to a leaf node, pick the last focused one. - // FIXME-DOCK: If we had to transfer other flags here, what would the policy be? - ImGuiDockNode* last_focused_node = DockContextFindNodeByID(ctx, payload_node->LastFocusedNodeId); - IM_ASSERT(last_focused_node != NULL); - ImGuiDockNode* last_focused_root_node = DockNodeGetRootNode(last_focused_node); - IM_ASSERT(last_focused_root_node == DockNodeGetRootNode(payload_node)); - last_focused_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - node->LocalFlags &= ~ImGuiDockNodeFlags_CentralNode; - last_focused_root_node->CentralNode = last_focused_node; - } - - IM_ASSERT(node->Windows.Size == 0); - DockNodeMoveChildNodes(node, payload_node); - } - else - { - const ImGuiID payload_dock_id = payload_node->ID; - DockNodeMoveWindows(node, payload_node); - DockSettingsRenameNodeReferences(payload_dock_id, node->ID); - } - DockContextRemoveNode(ctx, payload_node, true); - } - else if (payload_window) - { - // Transfer single window - const ImGuiID payload_dock_id = payload_window->DockId; - node->VisibleWindow = payload_window; - DockNodeAddWindow(node, payload_window, true); - if (payload_dock_id != 0) - DockSettingsRenameNodeReferences(payload_dock_id, node->ID); - } - } - else - { - // When docking a floating single window node we want to reevaluate auto-hiding of the tab bar - node->WantHiddenTabBarUpdate = true; - } - - // Update selection immediately - if (ImGuiTabBar* tab_bar = node->TabBar) - tab_bar->NextSelectedTabId = next_selected_id; - MarkIniSettingsDirty(); -} - -void ImGui::DockContextProcessUndockWindow(ImGuiContext* ctx, ImGuiWindow* window, bool clear_persistent_docking_ref) -{ - IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockWindow window '%s', clear_persistent_docking_ref = %d\n", window->Name, clear_persistent_docking_ref); - IM_UNUSED(ctx); - if (window->DockNode) - DockNodeRemoveWindow(window->DockNode, window, clear_persistent_docking_ref ? 0 : window->DockId); - else - window->DockId = 0; - window->Collapsed = false; - window->DockIsActive = false; - window->DockTabIsVisible = false; - MarkIniSettingsDirty(); -} - -void ImGui::DockContextProcessUndockNode(ImGuiContext* ctx, ImGuiDockNode* node) -{ - IMGUI_DEBUG_LOG_DOCKING("DockContextProcessUndockNode node %08X\n", node->ID); - IM_ASSERT(node->IsLeafNode()); - IM_ASSERT(node->Windows.Size >= 1); - - if (node->IsRootNode() || node->IsCentralNode()) - { - // In the case of a root node or central node, the node will have to stay in place. Create a new node to receive the payload. - ImGuiDockNode* new_node = DockContextAddNode(ctx, 0); - DockNodeMoveWindows(new_node, node); - DockSettingsRenameNodeReferences(node->ID, new_node->ID); - for (int n = 0; n < new_node->Windows.Size; n++) - UpdateWindowParentAndRootLinks(new_node->Windows[n], new_node->Windows[n]->Flags, NULL); - node = new_node; - } - else - { - // Otherwise extract our node and merging our sibling back into the parent node. - IM_ASSERT(node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); - int index_in_parent = (node->ParentNode->ChildNodes[0] == node) ? 0 : 1; - node->ParentNode->ChildNodes[index_in_parent] = NULL; - DockNodeTreeMerge(ctx, node->ParentNode, node->ParentNode->ChildNodes[index_in_parent ^ 1]); - node->ParentNode->AuthorityForViewport = ImGuiDataAuthority_Window; // The node that stays in place keeps the viewport, so our newly dragged out node will create a new viewport - node->ParentNode = NULL; - } - node->AuthorityForPos = node->AuthorityForSize = ImGuiDataAuthority_Window; - node->WantMouseMove = true; - MarkIniSettingsDirty(); -} - -// This is mostly used for automation. -bool ImGui::DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos) -{ - if (split_outer) - { - IM_ASSERT(0); - } - else - { - ImGuiDockPreviewData split_data; - DockNodePreviewDockSetup(target, target_node, payload, &split_data, false, split_outer); - if (split_data.DropRectsDraw[split_dir+1].IsInverted()) - return false; - *out_pos = split_data.DropRectsDraw[split_dir+1].GetCenter(); - return true; - } - return false; -} - -//----------------------------------------------------------------------------- -// Docking: ImGuiDockNode -//----------------------------------------------------------------------------- -// - DockNodeGetTabOrder() -// - DockNodeAddWindow() -// - DockNodeRemoveWindow() -// - DockNodeMoveChildNodes() -// - DockNodeMoveWindows() -// - DockNodeApplyPosSizeToWindows() -// - DockNodeHideHostWindow() -// - ImGuiDockNodeFindInfoResults -// - DockNodeFindInfo() -// - DockNodeFindWindowByID() -// - DockNodeUpdateVisibleFlagAndInactiveChilds() -// - DockNodeUpdateVisibleFlag() -// - DockNodeStartMouseMovingWindow() -// - DockNodeUpdate() -// - DockNodeUpdateWindowMenu() -// - DockNodeUpdateTabBar() -// - DockNodeAddTabBar() -// - DockNodeRemoveTabBar() -// - DockNodeIsDropAllowedOne() -// - DockNodeIsDropAllowed() -// - DockNodeCalcTabBarLayout() -// - DockNodeCalcSplitRects() -// - DockNodeCalcDropRectsAndTestMousePos() -// - DockNodePreviewDockSetup() -// - DockNodePreviewDockRender() -//----------------------------------------------------------------------------- - -ImGuiDockNode::ImGuiDockNode(ImGuiID id) -{ - ID = id; - SharedFlags = LocalFlags = ImGuiDockNodeFlags_None; - ParentNode = ChildNodes[0] = ChildNodes[1] = NULL; - TabBar = NULL; - SplitAxis = ImGuiAxis_None; - - State = ImGuiDockNodeState_Unknown; - HostWindow = VisibleWindow = NULL; - CentralNode = OnlyNodeWithWindows = NULL; - LastFrameAlive = LastFrameActive = LastFrameFocused = -1; - LastFocusedNodeId = 0; - SelectedTabId = 0; - WantCloseTabId = 0; - AuthorityForPos = AuthorityForSize = ImGuiDataAuthority_DockNode; - AuthorityForViewport = ImGuiDataAuthority_Auto; - IsVisible = true; - IsFocused = HasCloseButton = HasWindowMenuButton = EnableCloseButton = false; - WantCloseAll = WantLockSizeOnce = WantMouseMove = WantHiddenTabBarUpdate = WantHiddenTabBarToggle = false; - MarkedForPosSizeWrite = false; -} - -ImGuiDockNode::~ImGuiDockNode() -{ - IM_DELETE(TabBar); - TabBar = NULL; - ChildNodes[0] = ChildNodes[1] = NULL; -} - -int ImGui::DockNodeGetTabOrder(ImGuiWindow* window) -{ - ImGuiTabBar* tab_bar = window->DockNode->TabBar; - if (tab_bar == NULL) - return -1; - ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, window->ID); - return tab ? tab_bar->GetTabOrder(tab) : -1; -} - -static void ImGui::DockNodeAddWindow(ImGuiDockNode* node, ImGuiWindow* window, bool add_to_tab_bar) -{ - ImGuiContext& g = *GImGui; (void)g; - if (window->DockNode) - { - // Can overwrite an existing window->DockNode (e.g. pointing to a disabled DockSpace node) - IM_ASSERT(window->DockNode->ID != node->ID); - DockNodeRemoveWindow(window->DockNode, window, 0); - } - IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); - IMGUI_DEBUG_LOG_DOCKING("DockNodeAddWindow node 0x%08X window '%s'\n", node->ID, window->Name); - - node->Windows.push_back(window); - node->WantHiddenTabBarUpdate = true; - window->DockNode = node; - window->DockId = node->ID; - window->DockIsActive = (node->Windows.Size > 1); - window->DockTabWantClose = false; - - // If more than 2 windows appeared on the same frame, we'll create a new hosting DockNode from the point of the second window submission. - // Then we need to hide the first window (after its been output) otherwise it would be visible as a standalone window for one frame. - if (node->HostWindow == NULL && node->Windows.Size == 2 && node->Windows[0]->WasActive == false) - { - node->Windows[0]->Hidden = true; - node->Windows[0]->HiddenFramesCanSkipItems = 1; - } - - // When reactivating a node with one or two loose window, the window pos/size/viewport are authoritative over the node storage. - // In particular it is important we init the viewport from the first window so we don't create two viewports and drop one. - if (node->HostWindow == NULL && node->IsFloatingNode()) - { - if (node->AuthorityForPos == ImGuiDataAuthority_Auto) - node->AuthorityForPos = ImGuiDataAuthority_Window; - if (node->AuthorityForSize == ImGuiDataAuthority_Auto) - node->AuthorityForSize = ImGuiDataAuthority_Window; - if (node->AuthorityForViewport == ImGuiDataAuthority_Auto) - node->AuthorityForViewport = ImGuiDataAuthority_Window; - } - - // Add to tab bar if requested - if (add_to_tab_bar) - { - if (node->TabBar == NULL) - { - DockNodeAddTabBar(node); - node->TabBar->SelectedTabId = node->TabBar->NextSelectedTabId = node->SelectedTabId; - - // Add existing windows - for (int n = 0; n < node->Windows.Size - 1; n++) - TabBarAddTab(node->TabBar, ImGuiTabItemFlags_None, node->Windows[n]); - } - TabBarAddTab(node->TabBar, ImGuiTabItemFlags_Unsorted, window); - } - - DockNodeUpdateVisibleFlag(node); - - // Update this without waiting for the next time we Begin() in the window, so our host window will have the proper title bar color on its first frame. - if (node->HostWindow) - UpdateWindowParentAndRootLinks(window, window->Flags | ImGuiWindowFlags_ChildWindow, node->HostWindow); -} - -static void ImGui::DockNodeRemoveWindow(ImGuiDockNode* node, ImGuiWindow* window, ImGuiID save_dock_id) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(window->DockNode == node); - //IM_ASSERT(window->RootWindow == node->HostWindow); - //IM_ASSERT(window->LastFrameActive < g.FrameCount); // We may call this from Begin() - IM_ASSERT(save_dock_id == 0 || save_dock_id == node->ID); - IMGUI_DEBUG_LOG_DOCKING("DockNodeRemoveWindow node 0x%08X window '%s'\n", node->ID, window->Name); - - window->DockNode = NULL; - window->DockIsActive = window->DockTabWantClose = false; - window->DockId = save_dock_id; - UpdateWindowParentAndRootLinks(window, window->Flags & ~ImGuiWindowFlags_ChildWindow, NULL); // Update immediately - - // Remove window - bool erased = false; - for (int n = 0; n < node->Windows.Size; n++) - if (node->Windows[n] == window) - { - node->Windows.erase(node->Windows.Data + n); - erased = true; - break; - } - IM_ASSERT(erased); - if (node->VisibleWindow == window) - node->VisibleWindow = NULL; - - // Remove tab and possibly tab bar - node->WantHiddenTabBarUpdate = true; - if (node->TabBar) - { - TabBarRemoveTab(node->TabBar, window->ID); - const int tab_count_threshold_for_tab_bar = node->IsCentralNode() ? 1 : 2; - if (node->Windows.Size < tab_count_threshold_for_tab_bar) - DockNodeRemoveTabBar(node); - } - - if (node->Windows.Size == 0 && !node->IsCentralNode() && !node->IsDockSpace() && window->DockId != node->ID) - { - // Automatic dock node delete themselves if they are not holding at least one tab - DockContextRemoveNode(&g, node, true); - return; - } - - if (node->Windows.Size == 1 && !node->IsCentralNode() && node->HostWindow) - { - ImGuiWindow* remaining_window = node->Windows[0]; - if (node->HostWindow->ViewportOwned && node->IsRootNode()) - { - // Transfer viewport back to the remaining loose window - IM_ASSERT(node->HostWindow->Viewport->Window == node->HostWindow); - node->HostWindow->Viewport->Window = remaining_window; - node->HostWindow->Viewport->ID = remaining_window->ID; - } - remaining_window->Collapsed = node->HostWindow->Collapsed; - } - - // Update visibility immediately is required so the DockNodeUpdateRemoveInactiveChilds() processing can reflect changes up the tree - DockNodeUpdateVisibleFlag(node); -} - -static void ImGui::DockNodeMoveChildNodes(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) -{ - IM_ASSERT(dst_node->Windows.Size == 0); - dst_node->ChildNodes[0] = src_node->ChildNodes[0]; - dst_node->ChildNodes[1] = src_node->ChildNodes[1]; - if (dst_node->ChildNodes[0]) - dst_node->ChildNodes[0]->ParentNode = dst_node; - if (dst_node->ChildNodes[1]) - dst_node->ChildNodes[1]->ParentNode = dst_node; - dst_node->SplitAxis = src_node->SplitAxis; - dst_node->SizeRef = src_node->SizeRef; - src_node->ChildNodes[0] = src_node->ChildNodes[1] = NULL; -} - -static void ImGui::DockNodeMoveWindows(ImGuiDockNode* dst_node, ImGuiDockNode* src_node) -{ - // Insert tabs in the same orders as currently ordered (node->Windows isn't ordered) - IM_ASSERT(src_node && dst_node && dst_node != src_node); - ImGuiTabBar* src_tab_bar = src_node->TabBar; - if (src_tab_bar != NULL) - IM_ASSERT(src_node->Windows.Size == src_node->TabBar->Tabs.Size); - - // If the dst_node is empty we can just move the entire tab bar (to preserve selection, scrolling, etc.) - bool move_tab_bar = (src_tab_bar != NULL) && (dst_node->TabBar == NULL); - if (move_tab_bar) - { - dst_node->TabBar = src_node->TabBar; - src_node->TabBar = NULL; - } - - for (int n = 0; n < src_node->Windows.Size; n++) - { - ImGuiWindow* window = src_tab_bar ? src_tab_bar->Tabs[n].Window : src_node->Windows[n]; - window->DockNode = NULL; - window->DockIsActive = false; - DockNodeAddWindow(dst_node, window, move_tab_bar ? false : true); - } - src_node->Windows.clear(); - - if (!move_tab_bar && src_node->TabBar) - { - if (dst_node->TabBar) - dst_node->TabBar->SelectedTabId = src_node->TabBar->SelectedTabId; - DockNodeRemoveTabBar(src_node); - } -} - -static void ImGui::DockNodeApplyPosSizeToWindows(ImGuiDockNode* node) -{ - for (int n = 0; n < node->Windows.Size; n++) - { - SetWindowPos(node->Windows[n], node->Pos, ImGuiCond_Always); // We don't assign directly to Pos because it can break the calculation of SizeContents on next frame - SetWindowSize(node->Windows[n], node->Size, ImGuiCond_Always); - } -} - -static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node) -{ - if (node->HostWindow) - { - if (node->HostWindow->DockNodeAsHost == node) - node->HostWindow->DockNodeAsHost = NULL; - node->HostWindow = NULL; - } - - if (node->Windows.Size == 1) - { - node->VisibleWindow = node->Windows[0]; - node->Windows[0]->DockIsActive = false; - } - - if (node->TabBar) - DockNodeRemoveTabBar(node); -} - -// Search function called once by root node in DockNodeUpdate() -struct ImGuiDockNodeFindInfoResults -{ - ImGuiDockNode* CentralNode; - ImGuiDockNode* FirstNodeWithWindows; - int CountNodesWithWindows; - //ImGuiWindowClass WindowClassForMerges; - - ImGuiDockNodeFindInfoResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; } -}; - -static void DockNodeFindInfo(ImGuiDockNode* node, ImGuiDockNodeFindInfoResults* results) -{ - if (node->Windows.Size > 0) - { - if (results->FirstNodeWithWindows == NULL) - results->FirstNodeWithWindows = node; - results->CountNodesWithWindows++; - } - if (node->IsCentralNode()) - { - IM_ASSERT(results->CentralNode == NULL); // Should be only one - IM_ASSERT(node->IsLeafNode() && "If you get this assert: please submit .ini file + repro of actions leading to this."); - results->CentralNode = node; - } - if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL) - return; - if (node->ChildNodes[0]) - DockNodeFindInfo(node->ChildNodes[0], results); - if (node->ChildNodes[1]) - DockNodeFindInfo(node->ChildNodes[1], results); -} - -static ImGuiWindow* ImGui::DockNodeFindWindowByID(ImGuiDockNode* node, ImGuiID id) -{ - IM_ASSERT(id != 0); - for (int n = 0; n < node->Windows.Size; n++) - if (node->Windows[n]->ID == id) - return node->Windows[n]; - return NULL; -} - -// - Remove inactive windows/nodes. -// - Update visibility flag. -static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(node->ParentNode == NULL || node->ParentNode->ChildNodes[0] == node || node->ParentNode->ChildNodes[1] == node); - - // Inherit most flags - if (node->ParentNode) - node->SharedFlags = node->ParentNode->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; - - // Recurse into children - // There is the possibility that one of our child becoming empty will delete itself and moving its sibling contents into 'node'. - // If 'node->ChildNode[0]' delete itself, then 'node->ChildNode[1]->Windows' will be moved into 'node' - // If 'node->ChildNode[1]' delete itself, then 'node->ChildNode[0]->Windows' will be moved into 'node' and the "remove inactive windows" loop will have run twice on those windows (harmless) - if (node->ChildNodes[0]) - DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[0]); - if (node->ChildNodes[1]) - DockNodeUpdateVisibleFlagAndInactiveChilds(node->ChildNodes[1]); - - // Remove inactive windows - // Merge node flags overrides stored in windows - for (int window_n = 0; window_n < node->Windows.Size; window_n++) - { - ImGuiWindow* window = node->Windows[window_n]; - IM_ASSERT(window->DockNode == node); - - bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); - bool remove = false; - remove |= node_was_active && (window->LastFrameActive + 1 < g.FrameCount); - remove |= node_was_active && (node->WantCloseAll || node->WantCloseTabId == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument); // Submit all _expected_ closure from last frame - remove |= (window->DockTabWantClose); - if (remove) - { - window->DockTabWantClose = false; - if (node->Windows.Size == 1 && !node->IsCentralNode()) - { - DockNodeHideHostWindow(node); - node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; - DockNodeRemoveWindow(node, window, node->ID); // Will delete the node so it'll be invalid on return - return; - } - DockNodeRemoveWindow(node, window, node->ID); - window_n--; - } - else - { - node->LocalFlags &= ~window->WindowClass.DockNodeFlagsOverrideClear; - node->LocalFlags |= window->WindowClass.DockNodeFlagsOverrideSet; - } - } - - // Auto-hide tab bar option - ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); - if (node->WantHiddenTabBarUpdate && node->Windows.Size == 1 && (node_flags & ImGuiDockNodeFlags_AutoHideTabBar) && !node->IsHiddenTabBar()) - node->WantHiddenTabBarToggle = true; - node->WantHiddenTabBarUpdate = false; - - // Cancel toggling if we know our tab bar is enforced to be hidden at all times - if (node->WantHiddenTabBarToggle && node->VisibleWindow && (node->VisibleWindow->WindowClass.DockNodeFlagsOverrideSet & ImGuiDockNodeFlags_HiddenTabBar)) - node->WantHiddenTabBarToggle = false; - - // Apply toggles at a single point of the frame (here!) - if (node->Windows.Size > 1) - node->LocalFlags &= ~ImGuiDockNodeFlags_HiddenTabBar; - else if (node->WantHiddenTabBarToggle) - node->LocalFlags ^= ImGuiDockNodeFlags_HiddenTabBar; - node->WantHiddenTabBarToggle = false; - - DockNodeUpdateVisibleFlag(node); -} - -static void ImGui::DockNodeUpdateVisibleFlag(ImGuiDockNode* node) -{ - // Update visibility flag - bool is_visible = (node->ParentNode == NULL) ? node->IsDockSpace() : node->IsCentralNode(); - is_visible |= (node->Windows.Size > 0); - is_visible |= (node->ChildNodes[0] && node->ChildNodes[0]->IsVisible); - is_visible |= (node->ChildNodes[1] && node->ChildNodes[1]->IsVisible); - node->IsVisible = is_visible; -} - -static void ImGui::DockNodeStartMouseMovingWindow(ImGuiDockNode* node, ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(node->WantMouseMove == true); - ImVec2 backup_active_click_offset = g.ActiveIdClickOffset; - StartMouseMovingWindow(window); - g.MovingWindow = window; // If we are docked into a non moveable root window, StartMouseMovingWindow() won't set g.MovingWindow. Override that decision. - node->WantMouseMove = false; - g.ActiveIdClickOffset = backup_active_click_offset; -} - -// Update CentralNode, OnlyNodeWithWindows, LastFocusedNodeID. Copy window class. -static void ImGui::DockNodeUpdateForRootNode(ImGuiDockNode* node) -{ - DockNodeUpdateVisibleFlagAndInactiveChilds(node); - - // FIXME-DOCK: Merge this scan into the one above. - // - Setup central node pointers - // - Find if there's only a single visible window in the hierarchy (in which case we need to display a regular title bar -> FIXME-DOCK: that last part is not done yet!) - ImGuiDockNodeFindInfoResults results; - DockNodeFindInfo(node, &results); - node->CentralNode = results.CentralNode; - node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1) ? results.FirstNodeWithWindows : NULL; - if (node->LastFocusedNodeId == 0 && results.FirstNodeWithWindows != NULL) - node->LastFocusedNodeId = results.FirstNodeWithWindows->ID; - - // Copy the window class from of our first window so it can be used for proper dock filtering. - // When node has mixed windows, prioritize the class with the most constraint (DockingAllowUnclassed = false) as the reference to copy. - // FIXME-DOCK: We don't recurse properly, this code could be reworked to work from DockNodeUpdateScanRec. - if (ImGuiDockNode* first_node_with_windows = results.FirstNodeWithWindows) - { - node->WindowClass = first_node_with_windows->Windows[0]->WindowClass; - for (int n = 1; n < first_node_with_windows->Windows.Size; n++) - if (first_node_with_windows->Windows[n]->WindowClass.DockingAllowUnclassed == false) - { - node->WindowClass = first_node_with_windows->Windows[n]->WindowClass; - break; - } - } -} - -static void ImGui::DockNodeUpdate(ImGuiDockNode* node) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(node->LastFrameActive != g.FrameCount); - node->LastFrameAlive = g.FrameCount; - node->MarkedForPosSizeWrite = false; - - node->CentralNode = node->OnlyNodeWithWindows = NULL; - if (node->IsRootNode()) - DockNodeUpdateForRootNode(node); - - // Remove tab bar if not needed - if (node->TabBar && node->IsNoTabBar()) - DockNodeRemoveTabBar(node); - - // Early out for hidden root dock nodes (when all DockId references are in inactive windows, or there is only 1 floating window holding on the DockId) - bool want_to_hide_host_window = false; - if (node->Windows.Size <= 1 && node->IsFloatingNode() && node->IsLeafNode()) - if (!g.IO.ConfigDockingAlwaysTabBar && (node->Windows.Size == 0 || !node->Windows[0]->WindowClass.DockingAlwaysTabBar)) - want_to_hide_host_window = true; - if (want_to_hide_host_window) - { - if (node->Windows.Size == 1) - { - // Floating window pos/size is authoritative - ImGuiWindow* single_window = node->Windows[0]; - node->Pos = single_window->Pos; - node->Size = single_window->SizeFull; - node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; - - // Transfer focus immediately so when we revert to a regular window it is immediately selected - if (node->HostWindow && g.NavWindow == node->HostWindow) - FocusWindow(single_window); - if (node->HostWindow) - { - single_window->Viewport = node->HostWindow->Viewport; - single_window->ViewportId = node->HostWindow->ViewportId; - if (node->HostWindow->ViewportOwned) - { - single_window->Viewport->Window = single_window; - single_window->ViewportOwned = true; - } - } - } - - DockNodeHideHostWindow(node); - node->State = ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow; - node->WantCloseAll = false; - node->WantCloseTabId = 0; - node->HasCloseButton = node->HasWindowMenuButton = node->EnableCloseButton = false; - node->LastFrameActive = g.FrameCount; - - if (node->WantMouseMove && node->Windows.Size == 1) - DockNodeStartMouseMovingWindow(node, node->Windows[0]); - return; - } - - // In some circumstance we will defer creating the host window (so everything will be kept hidden), - // while the expected visible window is resizing itself. - // This is important for first-time (no ini settings restored) single window when io.ConfigDockingAlwaysTabBar is enabled, - // otherwise the node ends up using the minimum window size. Effectively those windows will take an extra frame to show up: - // N+0: Begin(): window created (with no known size), node is created - // N+1: DockNodeUpdate(): node skip creating host window / Begin(): window size applied, not visible - // N+2: DockNodeUpdate(): node can create host window / Begin(): window becomes visible - // We could remove this frame if we could reliably calculate the expected window size during node update, before the Begin() code. - // It would require a generalization of CalcWindowExpectedSize(), probably extracting code away from Begin(). - // In reality it isn't very important as user quickly ends up with size data in .ini file. - if (node->IsVisible && node->HostWindow == NULL && node->IsFloatingNode() && node->IsLeafNode()) - { - IM_ASSERT(node->Windows.Size > 0); - ImGuiWindow* ref_window = NULL; - if (node->SelectedTabId != 0) // Note that we prune single-window-node settings on .ini loading, so this is generally 0 for them! - ref_window = DockNodeFindWindowByID(node, node->SelectedTabId); - if (ref_window == NULL) - ref_window = node->Windows[0]; - if (ref_window->AutoFitFramesX > 0 || ref_window->AutoFitFramesY > 0) - { - node->State = ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing; - return; - } - } - - const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); - - // Bind or create host window - ImGuiWindow* host_window = NULL; - bool beginned_into_host_window = false; - if (node->IsDockSpace()) - { - // [Explicit root dockspace node] - IM_ASSERT(node->HostWindow); - node->EnableCloseButton = false; - node->HasCloseButton = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; - node->HasWindowMenuButton = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; - host_window = node->HostWindow; - } - else - { - // [Automatic root or child nodes] - node->EnableCloseButton = false; - node->HasCloseButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; - node->HasWindowMenuButton = (node->Windows.Size > 0) && (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; - for (int window_n = 0; window_n < node->Windows.Size; window_n++) - { - // FIXME-DOCK: Setting DockIsActive here means that for single active window in a leaf node, DockIsActive will be cleared until the next Begin() call. - ImGuiWindow* window = node->Windows[window_n]; - window->DockIsActive = (node->Windows.Size > 1); - node->EnableCloseButton |= window->HasCloseButton; - } - - if (node->IsRootNode() && node->IsVisible) - { - ImGuiWindow* ref_window = (node->Windows.Size > 0) ? node->Windows[0] : NULL; - - // Sync Pos - if (node->AuthorityForPos == ImGuiDataAuthority_Window && ref_window) - SetNextWindowPos(ref_window->Pos); - else if (node->AuthorityForPos == ImGuiDataAuthority_DockNode) - SetNextWindowPos(node->Pos); - - // Sync Size - if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window) - SetNextWindowSize(ref_window->SizeFull); - else if (node->AuthorityForSize == ImGuiDataAuthority_DockNode) - SetNextWindowSize(node->Size); - - // Sync Collapsed - if (node->AuthorityForSize == ImGuiDataAuthority_Window && ref_window) - SetNextWindowCollapsed(ref_window->Collapsed); - - // Sync Viewport - if (node->AuthorityForViewport == ImGuiDataAuthority_Window && ref_window) - SetNextWindowViewport(ref_window->ViewportId); - - SetNextWindowClass(&node->WindowClass); - - // Begin into the host window - char window_label[20]; - DockNodeGetHostWindowTitle(node, window_label, IM_ARRAYSIZE(window_label)); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_DockNodeHost; - window_flags |= ImGuiWindowFlags_NoFocusOnAppearing; - window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoCollapse; - window_flags |= ImGuiWindowFlags_NoTitleBar; - - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - Begin(window_label, NULL, window_flags); - PopStyleVar(); - beginned_into_host_window = true; - - node->HostWindow = host_window = g.CurrentWindow; - host_window->DockNodeAsHost = node; - host_window->DC.CursorPos = host_window->Pos; - node->Pos = host_window->Pos; - node->Size = host_window->Size; - - // We set ImGuiWindowFlags_NoFocusOnAppearing because we don't want the host window to take full focus (e.g. steal NavWindow) - // But we still it bring it to the front of display. There's no way to choose this precise behavior via window flags. - // One simple case to ponder if: window A has a toggle to create windows B/C/D. Dock B/C/D together, clear the toggle and enable it again. - // When reappearing B/C/D will request focus and be moved to the top of the display pile, but they are not linked to the dock host window - // during the frame they appear. The dock host window would keep its old display order, and the sorting in EndFrame would move B/C/D back - // after the dock host window, losing their top-most status. - if (node->HostWindow->Appearing) - BringWindowToDisplayFront(node->HostWindow); - - node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto; - } - else if (node->ParentNode) - { - node->HostWindow = host_window = node->ParentNode->HostWindow; - node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Auto; - } - if (node->WantMouseMove && node->HostWindow) - DockNodeStartMouseMovingWindow(node, node->HostWindow); - } - - // Update focused node (the one whose title bar is highlight) within a node tree - if (node->IsSplitNode()) - IM_ASSERT(node->TabBar == NULL); - if (node->IsRootNode()) - if (g.NavWindow && g.NavWindow->RootWindowDockStop->DockNode && g.NavWindow->RootWindowDockStop->ParentWindow == host_window) - node->LastFocusedNodeId = g.NavWindow->RootWindowDockStop->DockNode->ID; - - // We need to draw a background at the root level if requested by ImGuiDockNodeFlags_PassthruCentralNode, but we will only know the correct pos/size after - // processing the resizing splitters. So we are using the DrawList channel splitting facility to submit drawing primitives out of order! - const bool render_dockspace_bg = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0; - if (render_dockspace_bg) - { - host_window->DrawList->ChannelsSplit(2); - host_window->DrawList->ChannelsSetCurrent(1); - } - - // Register a hit-test hole in the window unless we are currently dragging a window that is compatible with our dockspace - const ImGuiDockNode* central_node = node->CentralNode; - const bool central_node_hole = node->IsRootNode() && host_window && (node_flags & ImGuiDockNodeFlags_PassthruCentralNode) != 0 && central_node != NULL && central_node->IsEmpty(); - bool central_node_hole_register_hit_test_hole = central_node_hole; - if (central_node_hole) - if (const ImGuiPayload* payload = ImGui::GetDragDropPayload()) - if (payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) && DockNodeIsDropAllowed(host_window, *(ImGuiWindow**)payload->Data)) - central_node_hole_register_hit_test_hole = false; - if (central_node_hole_register_hit_test_hole) - { - // Add a little padding to match the "resize from edges" behavior and allow grabbing the splitter easily. - IM_ASSERT(node->IsDockSpace()); // We cannot pass this flag without the DockSpace() api. Testing this because we also setup the hole in host_window->ParentNode - ImRect central_hole(central_node->Pos, central_node->Pos + central_node->Size); - central_hole.Expand(ImVec2(-WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, -WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS)); - if (central_node_hole && !central_hole.IsInverted()) - { - SetWindowHitTestHole(host_window, central_hole.Min, central_hole.Max - central_hole.Min); - SetWindowHitTestHole(host_window->ParentWindow, central_hole.Min, central_hole.Max - central_hole.Min); - } - } - - // Update position/size, process and draw resizing splitters - if (node->IsRootNode() && host_window) - { - DockNodeTreeUpdatePosSize(node, host_window->Pos, host_window->Size); - DockNodeTreeUpdateSplitter(node); - } - - // Draw empty node background (currently can only be the Central Node) - if (host_window && node->IsEmpty() && node->IsVisible && !(node_flags & ImGuiDockNodeFlags_PassthruCentralNode)) - host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg)); - - // Draw whole dockspace background if ImGuiDockNodeFlags_PassthruCentralNode if set. - if (render_dockspace_bg && node->IsVisible) - { - host_window->DrawList->ChannelsSetCurrent(0); - if (central_node_hole) - RenderRectFilledWithHole(host_window->DrawList, node->Rect(), central_node->Rect(), GetColorU32(ImGuiCol_WindowBg), 0.0f); - else - host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_WindowBg), 0.0f); - host_window->DrawList->ChannelsMerge(); - } - - // Draw and populate Tab Bar - if (host_window && node->Windows.Size > 0) - { - DockNodeUpdateTabBar(node, host_window); - } - else - { - node->WantCloseAll = false; - node->WantCloseTabId = 0; - node->IsFocused = false; - } - if (node->TabBar && node->TabBar->SelectedTabId) - node->SelectedTabId = node->TabBar->SelectedTabId; - else if (node->Windows.Size > 0) - node->SelectedTabId = node->Windows[0]->ID; - - // Draw payload drop target - if (host_window && node->IsVisible) - if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window)) - BeginDockableDragDropTarget(host_window); - - // We update this after DockNodeUpdateTabBar() - node->LastFrameActive = g.FrameCount; - - // Recurse into children - // FIXME-DOCK FIXME-OPT: Should not need to recurse into children - if (host_window) - { - if (node->ChildNodes[0]) - DockNodeUpdate(node->ChildNodes[0]); - if (node->ChildNodes[1]) - DockNodeUpdate(node->ChildNodes[1]); - - // Render outer borders last (after the tab bar) - if (node->IsRootNode()) - RenderWindowOuterBorders(host_window); - } - - // End host window - if (beginned_into_host_window) //-V1020 - End(); -} - -// Compare TabItem nodes given the last known DockOrder (will persist in .ini file as hint), used to sort tabs when multiple tabs are added on the same frame. -static int IMGUI_CDECL TabItemComparerByDockOrder(const void* lhs, const void* rhs) -{ - ImGuiWindow* a = ((const ImGuiTabItem*)lhs)->Window; - ImGuiWindow* b = ((const ImGuiTabItem*)rhs)->Window; - if (int d = ((a->DockOrder == -1) ? INT_MAX : a->DockOrder) - ((b->DockOrder == -1) ? INT_MAX : b->DockOrder)) - return d; - return (a->BeginOrderWithinContext - b->BeginOrderWithinContext); -} - -static ImGuiID ImGui::DockNodeUpdateWindowMenu(ImGuiDockNode* node, ImGuiTabBar* tab_bar) -{ - // Try to position the menu so it is more likely to stays within the same viewport - ImGuiContext& g = *GImGui; - ImGuiID ret_tab_id = 0; - if (g.Style.WindowMenuButtonPosition == ImGuiDir_Left) - SetNextWindowPos(ImVec2(node->Pos.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(0.0f, 0.0f)); - else - SetNextWindowPos(ImVec2(node->Pos.x + node->Size.x, node->Pos.y + GetFrameHeight()), ImGuiCond_Always, ImVec2(1.0f, 0.0f)); - if (BeginPopup("#WindowMenu")) - { - node->IsFocused = true; - if (tab_bar->Tabs.Size == 1) - { - if (MenuItem("Hide tab bar", NULL, node->IsHiddenTabBar())) - node->WantHiddenTabBarToggle = true; - } - else - { - for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) - { - ImGuiTabItem* tab = &tab_bar->Tabs[tab_n]; - IM_ASSERT(tab->Window != NULL); - if (Selectable(tab->Window->Name, tab->ID == tab_bar->SelectedTabId)) - ret_tab_id = tab->ID; - SameLine(); - Text(" "); - } - } - EndPopup(); - } - return ret_tab_id; -} - -static void ImGui::DockNodeUpdateTabBar(ImGuiDockNode* node, ImGuiWindow* host_window) -{ - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - - const bool node_was_active = (node->LastFrameActive + 1 == g.FrameCount); - const bool closed_all = node->WantCloseAll && node_was_active; - const ImGuiID closed_one = node->WantCloseTabId && node_was_active; - node->WantCloseAll = false; - node->WantCloseTabId = 0; - - // Decide if we should use a focused title bar color - bool is_focused = false; - ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (g.NavWindowingTarget) - is_focused = (g.NavWindowingTarget->DockNode == node); - else if (g.NavWindow && g.NavWindow->RootWindowForTitleBarHighlight == host_window->RootWindow && root_node->LastFocusedNodeId == node->ID) - is_focused = true; - - // Hidden tab bar will show a triangle on the upper-left (in Begin) - if (node->IsHiddenTabBar() || node->IsNoTabBar()) - { - node->VisibleWindow = (node->Windows.Size > 0) ? node->Windows[0] : NULL; - node->IsFocused = is_focused; - if (is_focused) - node->LastFrameFocused = g.FrameCount; - if (node->VisibleWindow) - { - // Notify root of visible window (used to display title in OS task bar) - if (is_focused || root_node->VisibleWindow == NULL) - root_node->VisibleWindow = node->VisibleWindow; - if (node->TabBar) - node->TabBar->VisibleTabId = node->VisibleWindow->ID; - } - return; - } - - // Move ourselves to the Menu layer (so we can be accessed by tapping Alt) + undo SkipItems flag in order to draw over the title bar even if the window is collapsed - bool backup_skip_item = host_window->SkipItems; - if (!node->IsDockSpace()) - { - host_window->SkipItems = false; - host_window->DC.NavLayerCurrent = ImGuiNavLayer_Menu; - host_window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Menu); - } - - // Use PushOverrideID() instead of PushID() to use the node id _without_ the host window ID. - // This is to facilitate computing those ID from the outside, and will affect more or less only the ID of the collapse button, popup and tabs, - // as docked windows themselves will override the stack with their own root ID. - PushOverrideID(node->ID); - ImGuiTabBar* tab_bar = node->TabBar; - bool tab_bar_is_recreated = (tab_bar == NULL); // Tab bar are automatically destroyed when a node gets hidden - if (tab_bar == NULL) - { - DockNodeAddTabBar(node); - tab_bar = node->TabBar; - } - - ImGuiID focus_tab_id = 0; - node->IsFocused = is_focused; - - const ImGuiDockNodeFlags node_flags = node->GetMergedFlags(); - const bool has_window_menu_button = (node_flags & ImGuiDockNodeFlags_NoWindowMenuButton) == 0; - const bool has_close_button = (node_flags & ImGuiDockNodeFlags_NoCloseButton) == 0; - - // In a dock node, the Collapse Button turns into the Window Menu button. - // FIXME-DOCK FIXME-OPT: Could we recycle popups id across multiple dock nodes? - if (has_window_menu_button && IsPopupOpen("#WindowMenu")) - { - if (ImGuiID tab_id = DockNodeUpdateWindowMenu(node, tab_bar)) - focus_tab_id = tab_bar->NextSelectedTabId = tab_id; - is_focused |= node->IsFocused; - } - - // Layout - ImRect title_bar_rect, tab_bar_rect; - ImVec2 window_menu_button_pos; - DockNodeCalcTabBarLayout(node, &title_bar_rect, &tab_bar_rect, &window_menu_button_pos); - - // Title bar - if (is_focused) - node->LastFrameFocused = g.FrameCount; - ImU32 title_bar_col = GetColorU32(host_window->Collapsed ? ImGuiCol_TitleBgCollapsed : is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - host_window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, host_window->WindowRounding, ImDrawCornerFlags_Top); - - // Docking/Collapse button - if (has_window_menu_button) - { - if (CollapseButton(host_window->GetID("#COLLAPSE"), window_menu_button_pos, node)) - OpenPopup("#WindowMenu"); - if (IsItemActive()) - focus_tab_id = tab_bar->SelectedTabId; - } - - // Submit new tabs and apply NavWindow focus back to the tab bar. They will be added as Unsorted and sorted below based on relative DockOrder value. - const int tabs_count_old = tab_bar->Tabs.Size; - for (int window_n = 0; window_n < node->Windows.Size; window_n++) - { - ImGuiWindow* window = node->Windows[window_n]; - if (g.NavWindow && g.NavWindow->RootWindowDockStop == window) - tab_bar->SelectedTabId = window->ID; - if (TabBarFindTabByID(tab_bar, window->ID) == NULL) - TabBarAddTab(tab_bar, ImGuiTabItemFlags_Unsorted, window); - } - - // If multiple tabs are appearing on the same frame, sort them based on their persistent DockOrder value - int tabs_unsorted_start = tab_bar->Tabs.Size; - for (int tab_n = tab_bar->Tabs.Size - 1; tab_n >= 0 && (tab_bar->Tabs[tab_n].Flags & ImGuiTabItemFlags_Unsorted); tab_n--) - { - // FIXME-DOCK: Consider only clearing the flag after the tab has been alive for a few consecutive frames, allowing late comers to not break sorting? - tab_bar->Tabs[tab_n].Flags &= ~ImGuiTabItemFlags_Unsorted; - tabs_unsorted_start = tab_n; - } - if (tab_bar->Tabs.Size > tabs_unsorted_start) - { - IMGUI_DEBUG_LOG_DOCKING("In node 0x%08X: %d new appearing tabs:%s\n", node->ID, tab_bar->Tabs.Size - tabs_unsorted_start, (tab_bar->Tabs.Size > tabs_unsorted_start + 1) ? " (will sort)" : ""); - for (int tab_n = tabs_unsorted_start; tab_n < tab_bar->Tabs.Size; tab_n++) - IMGUI_DEBUG_LOG_DOCKING(" - Tab '%s' Order %d\n", tab_bar->Tabs[tab_n].Window->Name, tab_bar->Tabs[tab_n].Window->DockOrder); - if (tab_bar->Tabs.Size > tabs_unsorted_start + 1) - ImQsort(tab_bar->Tabs.Data + tabs_unsorted_start, tab_bar->Tabs.Size - tabs_unsorted_start, sizeof(ImGuiTabItem), TabItemComparerByDockOrder); - } - - // Selected newly added tabs, or persistent tab ID if the tab bar was just recreated - if (tab_bar_is_recreated && TabBarFindTabByID(tab_bar, node->SelectedTabId) != NULL) - tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = node->SelectedTabId; - else if (tab_bar->Tabs.Size > tabs_count_old) - tab_bar->SelectedTabId = tab_bar->NextSelectedTabId = tab_bar->Tabs.back().Window->ID; - - // Begin tab bar - ImGuiTabBarFlags tab_bar_flags = ImGuiTabBarFlags_Reorderable | ImGuiTabBarFlags_AutoSelectNewTabs; // | ImGuiTabBarFlags_NoTabListScrollingButtons); - tab_bar_flags |= ImGuiTabBarFlags_SaveSettings | ImGuiTabBarFlags_DockNode; - if (!host_window->Collapsed && is_focused) - tab_bar_flags |= ImGuiTabBarFlags_IsFocused; - BeginTabBarEx(tab_bar, tab_bar_rect, tab_bar_flags, node); - //host_window->DrawList->AddRect(tab_bar_rect.Min, tab_bar_rect.Max, IM_COL32(255,0,255,255)); - - // Submit actual tabs - node->VisibleWindow = NULL; - for (int window_n = 0; window_n < node->Windows.Size; window_n++) - { - ImGuiWindow* window = node->Windows[window_n]; - if ((closed_all || closed_one == window->ID) && window->HasCloseButton && !(window->Flags & ImGuiWindowFlags_UnsavedDocument)) - continue; - if (window->LastFrameActive + 1 >= g.FrameCount || !node_was_active) - { - ImGuiTabItemFlags tab_item_flags = 0; - if (window->Flags & ImGuiWindowFlags_UnsavedDocument) - tab_item_flags |= ImGuiTabItemFlags_UnsavedDocument; - if (tab_bar->Flags & ImGuiTabBarFlags_NoCloseWithMiddleMouseButton) - tab_item_flags |= ImGuiTabItemFlags_NoCloseWithMiddleMouseButton; - - bool tab_open = true; - TabItemEx(tab_bar, window->Name, window->HasCloseButton ? &tab_open : NULL, tab_item_flags, window); - if (!tab_open) - node->WantCloseTabId = window->ID; - if (tab_bar->VisibleTabId == window->ID) - node->VisibleWindow = window; - - // Store last item data so it can be queried with IsItemXXX functions after the user Begin() call - window->DockTabItemStatusFlags = host_window->DC.LastItemStatusFlags; - window->DockTabItemRect = host_window->DC.LastItemRect; - - // Update navigation ID on menu layer - if (g.NavWindow && g.NavWindow->RootWindowDockStop == window && (window->DC.NavLayerActiveMask & (1 << 1)) == 0) - host_window->NavLastIds[1] = window->ID; - } - } - - // Notify root of visible window (used to display title in OS task bar) - if (node->VisibleWindow) - if (is_focused || root_node->VisibleWindow == NULL) - root_node->VisibleWindow = node->VisibleWindow; - - // Close button (after VisibleWindow was updated) - // Note that VisibleWindow may have been overrided by CTRL+Tabbing, so VisibleWindow->ID may be != from tab_bar->SelectedTabId - if (has_close_button && node->VisibleWindow) - { - if (!node->VisibleWindow->HasCloseButton) - { - PushItemFlag(ImGuiItemFlags_Disabled, true); - PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_Text] * ImVec4(1.0f,1.0f,1.0f,0.5f)); - } - const float button_sz = g.FontSize; - if (CloseButton(host_window->GetID("#CLOSE"), title_bar_rect.GetTR() + ImVec2(-style.FramePadding.x * 2.0f - button_sz, 0.0f))) - if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->VisibleTabId)) - { - node->WantCloseTabId = tab->ID; - TabBarCloseTab(tab_bar, tab); - } - //if (IsItemActive()) - // focus_tab_id = tab_bar->SelectedTabId; - if (!node->VisibleWindow->HasCloseButton) - { - PopStyleColor(); - PopItemFlag(); - } - } - - // When clicking on the title bar outside of tabs, we still focus the selected tab for that node - // FIXME: TabItem use AllowItemOverlap so we manually perform a more specific test for now (hovered || held) - ImGuiID title_bar_id = host_window->GetID("#TITLEBAR"); - if (g.HoveredId == 0 || g.HoveredId == title_bar_id || g.ActiveId == title_bar_id) - { - bool held; - ButtonBehavior(title_bar_rect, title_bar_id, NULL, &held); - if (held) - { - if (IsMouseClicked(0)) - focus_tab_id = tab_bar->SelectedTabId; - - // Forward moving request to selected window - if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_bar->SelectedTabId)) - StartMouseMovingWindowOrNode(tab->Window, node, false); - } - } - - // Forward focus from host node to selected window - //if (is_focused && g.NavWindow == host_window && !g.NavWindowingTarget) - // focus_tab_id = tab_bar->SelectedTabId; - - // When clicked on a tab we requested focus to the docked child - // This overrides the value set by "forward focus from host node to selected window". - if (tab_bar->NextSelectedTabId) - focus_tab_id = tab_bar->NextSelectedTabId; - - // Apply navigation focus - if (focus_tab_id != 0) - if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, focus_tab_id)) - { - FocusWindow(tab->Window); - NavInitWindow(tab->Window, false); - } - - EndTabBar(); - PopID(); - - // Restore SkipItems flag - if (!node->IsDockSpace()) - { - host_window->DC.NavLayerCurrent = ImGuiNavLayer_Main; - host_window->DC.NavLayerCurrentMask = (1 << ImGuiNavLayer_Main); - host_window->SkipItems = backup_skip_item; - } -} - -static void ImGui::DockNodeAddTabBar(ImGuiDockNode* node) -{ - IM_ASSERT(node->TabBar == NULL); - node->TabBar = IM_NEW(ImGuiTabBar); -} - -static void ImGui::DockNodeRemoveTabBar(ImGuiDockNode* node) -{ - if (node->TabBar == NULL) - return; - IM_DELETE(node->TabBar); - node->TabBar = NULL; -} - -static bool DockNodeIsDropAllowedOne(ImGuiWindow* payload, ImGuiWindow* host_window) -{ - if (host_window->DockNodeAsHost && host_window->DockNodeAsHost->IsDockSpace() && payload->BeginOrderWithinContext < host_window->BeginOrderWithinContext) - return false; - - ImGuiWindowClass* host_class = host_window->DockNodeAsHost ? &host_window->DockNodeAsHost->WindowClass : &host_window->WindowClass; - ImGuiWindowClass* payload_class = &payload->WindowClass; - if (host_class->ClassId != payload_class->ClassId) - { - if (host_class->ClassId != 0 && host_class->DockingAllowUnclassed && payload_class->ClassId == 0) - return true; - if (payload_class->ClassId != 0 && payload_class->DockingAllowUnclassed && host_class->ClassId == 0) - return true; - return false; - } - - return true; -} - -static bool ImGui::DockNodeIsDropAllowed(ImGuiWindow* host_window, ImGuiWindow* root_payload) -{ - if (root_payload->DockNodeAsHost && root_payload->DockNodeAsHost->IsSplitNode()) - return true; - - const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows.Size : 1; - for (int payload_n = 0; payload_n < payload_count; payload_n++) - { - ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->Windows[payload_n] : root_payload; - if (DockNodeIsDropAllowedOne(payload, host_window)) - return true; - } - return false; -} - -// window menu button == collapse button when not in a dock node. -// FIXME: This is similar to RenderWindowTitleBarContents, may want to share code. -static void ImGui::DockNodeCalcTabBarLayout(const ImGuiDockNode* node, ImRect* out_title_rect, ImRect* out_tab_bar_rect, ImVec2* out_window_menu_button_pos) -{ - ImGuiContext& g = *GImGui; - ImRect r = ImRect(node->Pos.x, node->Pos.y, node->Pos.x + node->Size.x, node->Pos.y + g.FontSize + g.Style.FramePadding.y * 2.0f); - if (out_title_rect) { *out_title_rect = r; } - - ImVec2 window_menu_button_pos = r.Min; - r.Min.x += g.Style.FramePadding.x; - r.Max.x -= g.Style.FramePadding.x; - if (node->HasCloseButton) - { - r.Max.x -= g.FontSize;// +1.0f; // In DockNodeUpdateTabBar() we currently display a disabled close button even if there is none. - } - if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Left) - { - r.Min.x += g.FontSize; // + g.Style.ItemInnerSpacing.x; // <-- Adding ItemInnerSpacing makes the title text moves slightly when in a docking tab bar. Instead we adjusted RenderArrowDockMenu() - } - else if (node->HasWindowMenuButton && g.Style.WindowMenuButtonPosition == ImGuiDir_Right) - { - r.Max.x -= g.FontSize + g.Style.FramePadding.x; - window_menu_button_pos = ImVec2(r.Max.x, r.Min.y); - } - if (out_tab_bar_rect) { *out_tab_bar_rect = r; } - if (out_window_menu_button_pos) { *out_window_menu_button_pos = window_menu_button_pos; } -} - -void ImGui::DockNodeCalcSplitRects(ImVec2& pos_old, ImVec2& size_old, ImVec2& pos_new, ImVec2& size_new, ImGuiDir dir, ImVec2 size_new_desired) -{ - ImGuiContext& g = *GImGui; - const float dock_spacing = g.Style.ItemInnerSpacing.x; - const ImGuiAxis axis = (dir == ImGuiDir_Left || dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; - pos_new[axis ^ 1] = pos_old[axis ^ 1]; - size_new[axis ^ 1] = size_old[axis ^ 1]; - - // Distribute size on given axis (with a desired size or equally) - const float w_avail = size_old[axis] - dock_spacing; - if (size_new_desired[axis] > 0.0f && size_new_desired[axis] <= w_avail * 0.5f) - { - size_new[axis] = size_new_desired[axis]; - size_old[axis] = IM_FLOOR(w_avail - size_new[axis]); - } - else - { - size_new[axis] = IM_FLOOR(w_avail * 0.5f); - size_old[axis] = IM_FLOOR(w_avail - size_new[axis]); - } - - // Position each node - if (dir == ImGuiDir_Right || dir == ImGuiDir_Down) - { - pos_new[axis] = pos_old[axis] + size_old[axis] + dock_spacing; - } - else if (dir == ImGuiDir_Left || dir == ImGuiDir_Up) - { - pos_new[axis] = pos_old[axis]; - pos_old[axis] = pos_new[axis] + size_new[axis] + dock_spacing; - } -} - -// Retrieve the drop rectangles for a given direction or for the center + perform hit testing. -bool ImGui::DockNodeCalcDropRectsAndTestMousePos(const ImRect& parent, ImGuiDir dir, ImRect& out_r, bool outer_docking, ImVec2* test_mouse_pos) -{ - ImGuiContext& g = *GImGui; - - const float parent_smaller_axis = ImMin(parent.GetWidth(), parent.GetHeight()); - const float hs_for_central_nodes = ImMin(g.FontSize * 1.5f, ImMax(g.FontSize * 0.5f, parent_smaller_axis / 8.0f)); - float hs_w; // Half-size, longer axis - float hs_h; // Half-size, smaller axis - ImVec2 off; // Distance from edge or center - if (outer_docking) - { - //hs_w = ImFloor(ImClamp(parent_smaller_axis - hs_for_central_nodes * 4.0f, g.FontSize * 0.5f, g.FontSize * 8.0f)); - //hs_h = ImFloor(hs_w * 0.15f); - //off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h), ImFloor(parent.GetHeight() * 0.5f - GetFrameHeightWithSpacing() * 1.4f - hs_h)); - hs_w = ImFloor(hs_for_central_nodes * 1.50f); - hs_h = ImFloor(hs_for_central_nodes * 0.80f); - off = ImVec2(ImFloor(parent.GetWidth() * 0.5f - hs_h), ImFloor(parent.GetHeight() * 0.5f - hs_h)); - } - else - { - hs_w = ImFloor(hs_for_central_nodes); - hs_h = ImFloor(hs_for_central_nodes * 0.90f); - off = ImVec2(ImFloor(hs_w * 2.40f), ImFloor(hs_w * 2.40f)); - } - - ImVec2 c = ImFloor(parent.GetCenter()); - if (dir == ImGuiDir_None) { out_r = ImRect(c.x - hs_w, c.y - hs_w, c.x + hs_w, c.y + hs_w); } - else if (dir == ImGuiDir_Up) { out_r = ImRect(c.x - hs_w, c.y - off.y - hs_h, c.x + hs_w, c.y - off.y + hs_h); } - else if (dir == ImGuiDir_Down) { out_r = ImRect(c.x - hs_w, c.y + off.y - hs_h, c.x + hs_w, c.y + off.y + hs_h); } - else if (dir == ImGuiDir_Left) { out_r = ImRect(c.x - off.x - hs_h, c.y - hs_w, c.x - off.x + hs_h, c.y + hs_w); } - else if (dir == ImGuiDir_Right) { out_r = ImRect(c.x + off.x - hs_h, c.y - hs_w, c.x + off.x + hs_h, c.y + hs_w); } - - if (test_mouse_pos == NULL) - return false; - - ImRect hit_r = out_r; - if (!outer_docking) - { - // Custom hit testing for the 5-way selection, designed to reduce flickering when moving diagonally between sides - hit_r.Expand(ImFloor(hs_w * 0.30f)); - ImVec2 mouse_delta = (*test_mouse_pos - c); - float mouse_delta_len2 = ImLengthSqr(mouse_delta); - float r_threshold_center = hs_w * 1.4f; - float r_threshold_sides = hs_w * (1.4f + 1.2f); - if (mouse_delta_len2 < r_threshold_center * r_threshold_center) - return (dir == ImGuiDir_None); - if (mouse_delta_len2 < r_threshold_sides * r_threshold_sides) - return (dir == ImGetDirQuadrantFromDelta(mouse_delta.x, mouse_delta.y)); - } - return hit_r.Contains(*test_mouse_pos); -} - -// host_node may be NULL if the window doesn't have a DockNode already. -// FIXME-DOCK: This is misnamed since it's also doing the filtering. -static void ImGui::DockNodePreviewDockSetup(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, ImGuiDockPreviewData* data, bool is_explicit_target, bool is_outer_docking) -{ - ImGuiContext& g = *GImGui; - - // There is an edge case when docking into a dockspace which only has inactive nodes. - // In this case DockNodeTreeFindNodeByPos() will have selected a leaf node which is inactive. - // Because the inactive leaf node doesn't have proper pos/size yet, we'll use the root node as reference. - ImGuiDockNode* root_payload_as_host = root_payload->DockNodeAsHost; - ImGuiDockNode* ref_node_for_rect = (host_node && !host_node->IsVisible) ? DockNodeGetRootNode(host_node) : host_node; - if (ref_node_for_rect) - IM_ASSERT(ref_node_for_rect->IsVisible); - - // Filter, figure out where we are allowed to dock - ImGuiDockNodeFlags src_node_flags = root_payload_as_host ? root_payload_as_host->GetMergedFlags() : root_payload->WindowClass.DockNodeFlagsOverrideSet; - ImGuiDockNodeFlags dst_node_flags = host_node ? host_node->GetMergedFlags() : host_window->WindowClass.DockNodeFlagsOverrideSet; - data->IsCenterAvailable = true; - if (is_outer_docking) - data->IsCenterAvailable = false; - else if (dst_node_flags & ImGuiDockNodeFlags_NoDocking) - data->IsCenterAvailable = false; - else if (host_node && (dst_node_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode()) - data->IsCenterAvailable = false; - else if ((!host_node || !host_node->IsEmpty()) && root_payload_as_host && root_payload_as_host->IsSplitNode() && (root_payload_as_host->OnlyNodeWithWindows == NULL)) // Is _visibly_ split? - data->IsCenterAvailable = false; - else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingOverMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingOverOther)) - data->IsCenterAvailable = false; - - data->IsSidesAvailable = true; - if ((dst_node_flags & ImGuiDockNodeFlags_NoSplit) || g.IO.ConfigDockingNoSplit) - data->IsSidesAvailable = false; - else if (!is_outer_docking && host_node && host_node->ParentNode == NULL && host_node->IsCentralNode()) - data->IsSidesAvailable = false; - else if ((dst_node_flags & ImGuiDockNodeFlags_NoDockingSplitMe) || (src_node_flags & ImGuiDockNodeFlags_NoDockingSplitOther)) - data->IsSidesAvailable = false; - - // Build a tentative future node (reuse same structure because it is practical. Shape will be readjusted when previewing a split) - data->FutureNode.HasCloseButton = (host_node ? host_node->HasCloseButton : host_window->HasCloseButton) || (root_payload->HasCloseButton); - data->FutureNode.HasWindowMenuButton = host_node ? true : ((host_window->Flags & ImGuiWindowFlags_NoCollapse) == 0); - data->FutureNode.Pos = ref_node_for_rect ? ref_node_for_rect->Pos : host_window->Pos; - data->FutureNode.Size = ref_node_for_rect ? ref_node_for_rect->Size : host_window->Size; - - // Calculate drop shapes geometry for allowed splitting directions - IM_ASSERT(ImGuiDir_None == -1); - data->SplitNode = host_node; - data->SplitDir = ImGuiDir_None; - data->IsSplitDirExplicit = false; - if (!host_window->Collapsed) - for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) - { - if (dir == ImGuiDir_None && !data->IsCenterAvailable) - continue; - if (dir != ImGuiDir_None && !data->IsSidesAvailable) - continue; - if (DockNodeCalcDropRectsAndTestMousePos(data->FutureNode.Rect(), (ImGuiDir)dir, data->DropRectsDraw[dir+1], is_outer_docking, &g.IO.MousePos)) - { - data->SplitDir = (ImGuiDir)dir; - data->IsSplitDirExplicit = true; - } - } - - // When docking without holding Shift, we only allow and preview docking when hovering over a drop rect or over the title bar - data->IsDropAllowed = (data->SplitDir != ImGuiDir_None) || (data->IsCenterAvailable); - if (!is_explicit_target && !data->IsSplitDirExplicit && !g.IO.ConfigDockingWithShift) - data->IsDropAllowed = false; - - // Calculate split area - data->SplitRatio = 0.0f; - if (data->SplitDir != ImGuiDir_None) - { - ImGuiDir split_dir = data->SplitDir; - ImGuiAxis split_axis = (split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Right) ? ImGuiAxis_X : ImGuiAxis_Y; - ImVec2 pos_new, pos_old = data->FutureNode.Pos; - ImVec2 size_new, size_old = data->FutureNode.Size; - DockNodeCalcSplitRects(pos_old, size_old, pos_new, size_new, split_dir, root_payload->Size); - - // Calculate split ratio so we can pass it down the docking request - float split_ratio = ImSaturate(size_new[split_axis] / data->FutureNode.Size[split_axis]); - data->FutureNode.Pos = pos_new; - data->FutureNode.Size = size_new; - data->SplitRatio = (split_dir == ImGuiDir_Right || split_dir == ImGuiDir_Down) ? (1.0f - split_ratio) : (split_ratio); - } -} - -static void ImGui::DockNodePreviewDockRender(ImGuiWindow* host_window, ImGuiDockNode* host_node, ImGuiWindow* root_payload, const ImGuiDockPreviewData* data) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.CurrentWindow == host_window); // Because we rely on font size to calculate tab sizes - - // With this option, we only display the preview on the target viewport, and the payload viewport is made transparent. - // To compensate for the single layer obstructed by the payload, we'll increase the alpha of the preview nodes. - const bool is_transparent_payload = g.IO.ConfigDockingTransparentPayload; - - // In case the two windows involved are on different viewports, we will draw the overlay on each of them. - int overlay_draw_lists_count = 0; - ImDrawList* overlay_draw_lists[2]; - overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(host_window->Viewport); - if (host_window->Viewport != root_payload->Viewport && !is_transparent_payload) - overlay_draw_lists[overlay_draw_lists_count++] = GetForegroundDrawList(root_payload->Viewport); - - // Draw main preview rectangle - const ImU32 overlay_col_tabs = GetColorU32(ImGuiCol_TabActive); - const ImU32 overlay_col_main = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.60f : 0.40f); - const ImU32 overlay_col_drop = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 0.90f : 0.70f); - const ImU32 overlay_col_drop_hovered = GetColorU32(ImGuiCol_DockingPreview, is_transparent_payload ? 1.20f : 1.00f); - const ImU32 overlay_col_lines = GetColorU32(ImGuiCol_NavWindowingHighlight, is_transparent_payload ? 0.80f : 0.60f); - - // Display area preview - const bool can_preview_tabs = (root_payload->DockNodeAsHost == NULL || root_payload->DockNodeAsHost->Windows.Size > 0); - if (data->IsDropAllowed) - { - ImRect overlay_rect = data->FutureNode.Rect(); - if (data->SplitDir == ImGuiDir_None && can_preview_tabs) - overlay_rect.Min.y += GetFrameHeight(); - if (data->SplitDir != ImGuiDir_None || data->IsCenterAvailable) - for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) - overlay_draw_lists[overlay_n]->AddRectFilled(overlay_rect.Min, overlay_rect.Max, overlay_col_main, host_window->WindowRounding); - } - - // Display tab shape/label preview unless we are splitting node (it generally makes the situation harder to read) - if (data->IsDropAllowed && can_preview_tabs && data->SplitDir == ImGuiDir_None && data->IsCenterAvailable) - { - // Compute target tab bar geometry so we can locate our preview tabs - ImRect tab_bar_rect; - DockNodeCalcTabBarLayout(&data->FutureNode, NULL, &tab_bar_rect, NULL); - ImVec2 tab_pos = tab_bar_rect.Min; - if (host_node && host_node->TabBar) - { - if (!host_node->IsHiddenTabBar() && !host_node->IsNoTabBar()) - tab_pos.x += host_node->TabBar->OffsetMax + g.Style.ItemInnerSpacing.x; // We don't use OffsetNewTab because when using non-persistent-order tab bar it is incremented with each Tab submission. - else - tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_node->Windows[0]->Name, host_node->Windows[0]->HasCloseButton).x; - } - else if (!(host_window->Flags & ImGuiWindowFlags_DockNodeHost)) - { - tab_pos.x += g.Style.ItemInnerSpacing.x + TabItemCalcSize(host_window->Name, host_window->HasCloseButton).x; // Account for slight offset which will be added when changing from title bar to tab bar - } - - // Draw tab shape/label preview (payload may be a loose window or a host window carrying multiple tabbed windows) - if (root_payload->DockNodeAsHost) - IM_ASSERT(root_payload->DockNodeAsHost->Windows.Size == root_payload->DockNodeAsHost->TabBar->Tabs.Size); - const int payload_count = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs.Size : 1; - for (int payload_n = 0; payload_n < payload_count; payload_n++) - { - // Calculate the tab bounding box for each payload window - ImGuiWindow* payload = root_payload->DockNodeAsHost ? root_payload->DockNodeAsHost->TabBar->Tabs[payload_n].Window : root_payload; - if (!DockNodeIsDropAllowedOne(payload, host_window)) - continue; - - ImVec2 tab_size = TabItemCalcSize(payload->Name, payload->HasCloseButton); - ImRect tab_bb(tab_pos.x, tab_pos.y, tab_pos.x + tab_size.x, tab_pos.y + tab_size.y); - tab_pos.x += tab_size.x + g.Style.ItemInnerSpacing.x; - for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) - { - ImGuiTabItemFlags tab_flags = ImGuiTabItemFlags_Preview | ((payload->Flags & ImGuiWindowFlags_UnsavedDocument) ? ImGuiTabItemFlags_UnsavedDocument : 0); - if (!tab_bar_rect.Contains(tab_bb)) - overlay_draw_lists[overlay_n]->PushClipRect(tab_bar_rect.Min, tab_bar_rect.Max); - TabItemBackground(overlay_draw_lists[overlay_n], tab_bb, tab_flags, overlay_col_tabs); - TabItemLabelAndCloseButton(overlay_draw_lists[overlay_n], tab_bb, tab_flags, g.Style.FramePadding, payload->Name, 0, 0, false); - if (!tab_bar_rect.Contains(tab_bb)) - overlay_draw_lists[overlay_n]->PopClipRect(); - } - } - } - - // Display drop boxes - const float overlay_rounding = ImMax(3.0f, g.Style.FrameRounding); - for (int dir = ImGuiDir_None; dir < ImGuiDir_COUNT; dir++) - { - if (!data->DropRectsDraw[dir + 1].IsInverted()) - { - ImRect draw_r = data->DropRectsDraw[dir + 1]; - ImRect draw_r_in = draw_r; - draw_r_in.Expand(-2.0f); - ImU32 overlay_col = (data->SplitDir == (ImGuiDir)dir && data->IsSplitDirExplicit) ? overlay_col_drop_hovered : overlay_col_drop; - for (int overlay_n = 0; overlay_n < overlay_draw_lists_count; overlay_n++) - { - ImVec2 center = ImFloor(draw_r_in.GetCenter()); - overlay_draw_lists[overlay_n]->AddRectFilled(draw_r.Min, draw_r.Max, overlay_col, overlay_rounding); - overlay_draw_lists[overlay_n]->AddRect(draw_r_in.Min, draw_r_in.Max, overlay_col_lines, overlay_rounding); - if (dir == ImGuiDir_Left || dir == ImGuiDir_Right) - overlay_draw_lists[overlay_n]->AddLine(ImVec2(center.x, draw_r_in.Min.y), ImVec2(center.x, draw_r_in.Max.y), overlay_col_lines); - if (dir == ImGuiDir_Up || dir == ImGuiDir_Down) - overlay_draw_lists[overlay_n]->AddLine(ImVec2(draw_r_in.Min.x, center.y), ImVec2(draw_r_in.Max.x, center.y), overlay_col_lines); - } - } - - // Stop after ImGuiDir_None - if ((host_node && (host_node->GetMergedFlags() & ImGuiDockNodeFlags_NoSplit)) || g.IO.ConfigDockingNoSplit) - return; - } -} - -//----------------------------------------------------------------------------- -// Docking: ImGuiDockNode Tree manipulation functions -//----------------------------------------------------------------------------- -// - DockNodeTreeSplit() -// - DockNodeTreeMerge() -// - DockNodeTreeUpdatePosSize() -// - DockNodeTreeUpdateSplitterFindTouchingNode() -// - DockNodeTreeUpdateSplitter() -// - DockNodeTreeFindFallbackLeafNode() -// - DockNodeTreeFindNodeByPos() -//----------------------------------------------------------------------------- - -void ImGui::DockNodeTreeSplit(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiAxis split_axis, int split_inheritor_child_idx, float split_ratio, ImGuiDockNode* new_node) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(split_axis != ImGuiAxis_None); - - ImGuiDockNode* child_0 = (new_node && split_inheritor_child_idx != 0) ? new_node : DockContextAddNode(ctx, 0); - child_0->ParentNode = parent_node; - - ImGuiDockNode* child_1 = (new_node && split_inheritor_child_idx != 1) ? new_node : DockContextAddNode(ctx, 0); - child_1->ParentNode = parent_node; - - ImGuiDockNode* child_inheritor = (split_inheritor_child_idx == 0) ? child_0 : child_1; - DockNodeMoveChildNodes(child_inheritor, parent_node); - parent_node->ChildNodes[0] = child_0; - parent_node->ChildNodes[1] = child_1; - parent_node->ChildNodes[split_inheritor_child_idx]->VisibleWindow = parent_node->VisibleWindow; - parent_node->SplitAxis = split_axis; - parent_node->VisibleWindow = NULL; - parent_node->AuthorityForPos = parent_node->AuthorityForSize = ImGuiDataAuthority_DockNode; - - float size_avail = (parent_node->Size[split_axis] - DOCKING_SPLITTER_SIZE); - size_avail = ImMax(size_avail, g.Style.WindowMinSize[split_axis] * 2.0f); - IM_ASSERT(size_avail > 0.0f); // If you created a node manually with DockBuilderAddNode(), you need to also call DockBuilderSetNodeSize() before splitting. - child_0->SizeRef = child_1->SizeRef = parent_node->Size; - child_0->SizeRef[split_axis] = ImFloor(size_avail * split_ratio); - child_1->SizeRef[split_axis] = ImFloor(size_avail - child_0->SizeRef[split_axis]); - - DockNodeMoveWindows(parent_node->ChildNodes[split_inheritor_child_idx], parent_node); - DockNodeTreeUpdatePosSize(parent_node, parent_node->Pos, parent_node->Size); - - // Flags transfer (e.g. this is where we transfer the ImGuiDockNodeFlags_CentralNode property) - child_0->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; - child_1->SharedFlags = parent_node->SharedFlags & ImGuiDockNodeFlags_SharedFlagsInheritMask_; - child_inheritor->LocalFlags = parent_node->LocalFlags & ImGuiDockNodeFlags_LocalFlagsTransferMask_; - parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; - if (child_inheritor->IsCentralNode()) - DockNodeGetRootNode(parent_node)->CentralNode = child_inheritor; -} - -void ImGui::DockNodeTreeMerge(ImGuiContext* ctx, ImGuiDockNode* parent_node, ImGuiDockNode* merge_lead_child) -{ - // When called from DockContextProcessUndockNode() it is possible that one of the child is NULL. - ImGuiDockNode* child_0 = parent_node->ChildNodes[0]; - ImGuiDockNode* child_1 = parent_node->ChildNodes[1]; - IM_ASSERT(child_0 || child_1); - IM_ASSERT(merge_lead_child == child_0 || merge_lead_child == child_1); - if ((child_0 && child_0->Windows.Size > 0) || (child_1 && child_1->Windows.Size > 0)) - { - IM_ASSERT(parent_node->TabBar == NULL); - IM_ASSERT(parent_node->Windows.Size == 0); - } - IMGUI_DEBUG_LOG_DOCKING("DockNodeTreeMerge 0x%08X & 0x%08X back into parent 0x%08X\n", child_0 ? child_0->ID : 0, child_1 ? child_1->ID : 0, parent_node->ID); - - ImVec2 backup_last_explicit_size = parent_node->SizeRef; - DockNodeMoveChildNodes(parent_node, merge_lead_child); - if (child_0) - { - DockNodeMoveWindows(parent_node, child_0); // Generally only 1 of the 2 child node will have windows - DockSettingsRenameNodeReferences(child_0->ID, parent_node->ID); - } - if (child_1) - { - DockNodeMoveWindows(parent_node, child_1); - DockSettingsRenameNodeReferences(child_1->ID, parent_node->ID); - } - DockNodeApplyPosSizeToWindows(parent_node); - parent_node->AuthorityForPos = parent_node->AuthorityForSize = parent_node->AuthorityForViewport = ImGuiDataAuthority_Auto; - parent_node->VisibleWindow = merge_lead_child->VisibleWindow; - parent_node->SizeRef = backup_last_explicit_size; - - // Flags transfer - parent_node->LocalFlags &= ~ImGuiDockNodeFlags_LocalFlagsTransferMask_; // Preserve Dockspace flag - parent_node->LocalFlags |= (child_0 ? child_0->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; - parent_node->LocalFlags |= (child_1 ? child_1->LocalFlags : 0) & ImGuiDockNodeFlags_LocalFlagsTransferMask_; - - if (child_0) - { - ctx->DockContext.Nodes.SetVoidPtr(child_0->ID, NULL); - IM_DELETE(child_0); - } - if (child_1) - { - ctx->DockContext.Nodes.SetVoidPtr(child_1->ID, NULL); - IM_DELETE(child_1); - } -} - -// Update Pos/Size for a node hierarchy (don't affect child Windows yet) -void ImGui::DockNodeTreeUpdatePosSize(ImGuiDockNode* node, ImVec2 pos, ImVec2 size, bool only_write_to_marked_nodes) -{ - // During the regular dock node update we write to all nodes. - // 'only_write_to_marked_nodes' is only set when turning a node visible mid-frame and we need its size right-away. - const bool write_to_node = (only_write_to_marked_nodes == false) || (node->MarkedForPosSizeWrite); - if (write_to_node) - { - node->Pos = pos; - node->Size = size; - } - - if (node->IsLeafNode()) - return; - - ImGuiDockNode* child_0 = node->ChildNodes[0]; - ImGuiDockNode* child_1 = node->ChildNodes[1]; - ImVec2 child_0_pos = pos, child_1_pos = pos; - ImVec2 child_0_size = size, child_1_size = size; - if (child_0->IsVisible && child_1->IsVisible) - { - const float spacing = DOCKING_SPLITTER_SIZE; - const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; - const float size_avail = ImMax(size[axis] - spacing, 0.0f); - - // Size allocation policy - // 1) The first 0..WindowMinSize[axis]*2 are allocated evenly to both windows. - ImGuiContext& g = *GImGui; - const float size_min_each = ImFloor(ImMin(size_avail, g.Style.WindowMinSize[axis] * 2.0f) * 0.5f); - - // 2) Process locked absolute size (during a splitter resize we preserve the child of nodes not touching the splitter edge) - if (child_0->WantLockSizeOnce && !child_1->WantLockSizeOnce) - { - child_0_size[axis] = child_0->SizeRef[axis] = ImMin(size_avail - 1.0f, child_0->Size[axis]); - child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); - IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); - } - else if (child_1->WantLockSizeOnce && !child_0->WantLockSizeOnce) - { - child_1_size[axis] = child_1->SizeRef[axis] = ImMin(size_avail - 1.0f, child_1->Size[axis]); - child_0_size[axis] = child_0->SizeRef[axis] = (size_avail - child_1_size[axis]); - IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); - } - else if (child_0->WantLockSizeOnce && child_1->WantLockSizeOnce) - { - // FIXME-DOCK: We cannot honor the requested size, so apply ratio. - // Currently this path will only be taken if code programmatically sets WantLockSizeOnce - float ratio_0 = child_0_size[axis] / (child_0_size[axis] + child_1_size[axis]); - child_0_size[axis] = child_0->SizeRef[axis] = ImFloor(size_avail * ratio_0); - child_1_size[axis] = child_1->SizeRef[axis] = (size_avail - child_0_size[axis]); - IM_ASSERT(child_0->SizeRef[axis] > 0.0f && child_1->SizeRef[axis] > 0.0f); - } - - // 3) If one window is the central node (~ use remaining space, should be made explicit!), use explicit size from the other, and remainder for the central node - else if (child_1->IsCentralNode() && child_0->SizeRef[axis] != 0.0f) - { - child_0_size[axis] = ImMin(size_avail - size_min_each, child_0->SizeRef[axis]); - child_1_size[axis] = (size_avail - child_0_size[axis]); - } - else if (child_0->IsCentralNode() && child_1->SizeRef[axis] != 0.0f) - { - child_1_size[axis] = ImMin(size_avail - size_min_each, child_1->SizeRef[axis]); - child_0_size[axis] = (size_avail - child_1_size[axis]); - } - else - { - // 4) Otherwise distribute according to the relative ratio of each SizeRef value - float split_ratio = child_0->SizeRef[axis] / (child_0->SizeRef[axis] + child_1->SizeRef[axis]); - child_0_size[axis] = ImMax(size_min_each, ImFloor(size_avail * split_ratio + 0.5F)); - child_1_size[axis] = (size_avail - child_0_size[axis]); - } - - child_1_pos[axis] += spacing + child_0_size[axis]; - } - child_0->WantLockSizeOnce = child_1->WantLockSizeOnce = false; - - if (child_0->IsVisible) - DockNodeTreeUpdatePosSize(child_0, child_0_pos, child_0_size); - if (child_1->IsVisible) - DockNodeTreeUpdatePosSize(child_1, child_1_pos, child_1_size); -} - -static void DockNodeTreeUpdateSplitterFindTouchingNode(ImGuiDockNode* node, ImGuiAxis axis, int side, ImVector* touching_nodes) -{ - if (node->IsLeafNode()) - { - touching_nodes->push_back(node); - return; - } - if (node->ChildNodes[0]->IsVisible) - if (node->SplitAxis != axis || side == 0 || !node->ChildNodes[1]->IsVisible) - DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[0], axis, side, touching_nodes); - if (node->ChildNodes[1]->IsVisible) - if (node->SplitAxis != axis || side == 1 || !node->ChildNodes[0]->IsVisible) - DockNodeTreeUpdateSplitterFindTouchingNode(node->ChildNodes[1], axis, side, touching_nodes); -} - -void ImGui::DockNodeTreeUpdateSplitter(ImGuiDockNode* node) -{ - if (node->IsLeafNode()) - return; - - ImGuiContext& g = *GImGui; - - ImGuiDockNode* child_0 = node->ChildNodes[0]; - ImGuiDockNode* child_1 = node->ChildNodes[1]; - if (child_0->IsVisible && child_1->IsVisible) - { - // Bounding box of the splitter cover the space between both nodes (w = Spacing, h = Size[xy^1] for when splitting horizontally) - const ImGuiAxis axis = (ImGuiAxis)node->SplitAxis; - IM_ASSERT(axis != ImGuiAxis_None); - ImRect bb; - bb.Min = child_0->Pos; - bb.Max = child_1->Pos; - bb.Min[axis] += child_0->Size[axis]; - bb.Max[axis ^ 1] += child_1->Size[axis ^ 1]; - //if (g.IO.KeyCtrl) GetForegroundDrawList(g.CurrentWindow->Viewport)->AddRect(bb.Min, bb.Max, IM_COL32(255,0,255,255)); - - const ImGuiDockNodeFlags merged_flags = child_0->GetMergedFlags() | child_1->GetMergedFlags(); - const ImGuiDockNodeFlags no_resize_axis_flag = (axis == ImGuiAxis_X) ? ImGuiDockNodeFlags_NoResizeX : ImGuiDockNodeFlags_NoResizeY; - if ((merged_flags & ImGuiDockNodeFlags_NoResize) || (merged_flags & no_resize_axis_flag)) - { - ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_Separator), g.Style.FrameRounding); - } - else - { - //bb.Min[axis] += 1; // Display a little inward so highlight doesn't connect with nearby tabs on the neighbor node. - //bb.Max[axis] -= 1; - PushID(node->ID); - - // Gather list of nodes that are touching the splitter line. Find resizing limits based on those nodes. - ImVector touching_nodes[2]; - float min_size = g.Style.WindowMinSize[axis]; - float resize_limits[2]; - resize_limits[0] = node->ChildNodes[0]->Pos[axis] + min_size; - resize_limits[1] = node->ChildNodes[1]->Pos[axis] + node->ChildNodes[1]->Size[axis] - min_size; - - ImGuiID splitter_id = GetID("##Splitter"); - if (g.ActiveId == splitter_id) - { - // Only process when splitter is active - DockNodeTreeUpdateSplitterFindTouchingNode(child_0, axis, 1, &touching_nodes[0]); - DockNodeTreeUpdateSplitterFindTouchingNode(child_1, axis, 0, &touching_nodes[1]); - for (int touching_node_n = 0; touching_node_n < touching_nodes[0].Size; touching_node_n++) - resize_limits[0] = ImMax(resize_limits[0], touching_nodes[0][touching_node_n]->Rect().Min[axis] + min_size); - for (int touching_node_n = 0; touching_node_n < touching_nodes[1].Size; touching_node_n++) - resize_limits[1] = ImMin(resize_limits[1], touching_nodes[1][touching_node_n]->Rect().Max[axis] - min_size); - - /* - // [DEBUG] Render limits - ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); - for (int n = 0; n < 2; n++) - if (axis == ImGuiAxis_X) - draw_list->AddLine(ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y), ImVec2(resize_limits[n], node->ChildNodes[n]->Pos.y + node->ChildNodes[n]->Size.y), IM_COL32(255, 0, 255, 255), 3.0f); - else - draw_list->AddLine(ImVec2(node->ChildNodes[n]->Pos.x, resize_limits[n]), ImVec2(node->ChildNodes[n]->Pos.x + node->ChildNodes[n]->Size.x, resize_limits[n]), IM_COL32(255, 0, 255, 255), 3.0f); - */ - } - - // Use a short delay before highlighting the splitter (and changing the mouse cursor) in order for regular mouse movement to not highlight many splitters - float cur_size_0 = child_0->Size[axis]; - float cur_size_1 = child_1->Size[axis]; - float min_size_0 = resize_limits[0] - child_0->Pos[axis]; - float min_size_1 = child_1->Pos[axis] + child_1->Size[axis] - resize_limits[1]; - if (SplitterBehavior(bb, GetID("##Splitter"), axis, &cur_size_0, &cur_size_1, min_size_0, min_size_1, WINDOWS_RESIZE_FROM_EDGES_HALF_THICKNESS, WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER)) - { - if (touching_nodes[0].Size > 0 && touching_nodes[1].Size > 0) - { - child_0->Size[axis] = child_0->SizeRef[axis] = cur_size_0; - child_1->Pos[axis] -= cur_size_1 - child_1->Size[axis]; - child_1->Size[axis] = child_1->SizeRef[axis] = cur_size_1; - - // Lock the size of every node that is a sibling of the node we are touching - // This might be less desirable if we can merge sibling of a same axis into the same parental level. - for (int side_n = 0; side_n < 2; side_n++) - for (int touching_node_n = 0; touching_node_n < touching_nodes[side_n].Size; touching_node_n++) - { - ImGuiDockNode* touching_node = touching_nodes[side_n][touching_node_n]; - //ImDrawList* draw_list = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); - //draw_list->AddRect(touching_node->Pos, touching_node->Pos + touching_node->Size, IM_COL32(255, 128, 0, 255)); - while (touching_node->ParentNode != node) - { - if (touching_node->ParentNode->SplitAxis == axis) - { - // Mark other node so its size will be preserved during the upcoming call to DockNodeTreeUpdatePosSize(). - ImGuiDockNode* node_to_preserve = touching_node->ParentNode->ChildNodes[side_n]; - node_to_preserve->WantLockSizeOnce = true; - //draw_list->AddRect(touching_node->Pos, touching_node->Rect().Max, IM_COL32(255, 0, 0, 255)); - //draw_list->AddRectFilled(node_to_preserve->Pos, node_to_preserve->Rect().Max, IM_COL32(0, 255, 0, 100)); - } - touching_node = touching_node->ParentNode; - } - } - - DockNodeTreeUpdatePosSize(child_0, child_0->Pos, child_0->Size); - DockNodeTreeUpdatePosSize(child_1, child_1->Pos, child_1->Size); - MarkIniSettingsDirty(); - } - } - PopID(); - } - } - - if (child_0->IsVisible) - DockNodeTreeUpdateSplitter(child_0); - if (child_1->IsVisible) - DockNodeTreeUpdateSplitter(child_1); -} - -ImGuiDockNode* ImGui::DockNodeTreeFindFallbackLeafNode(ImGuiDockNode* node) -{ - if (node->IsLeafNode()) - return node; - if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[0])) - return leaf_node; - if (ImGuiDockNode* leaf_node = DockNodeTreeFindFallbackLeafNode(node->ChildNodes[1])) - return leaf_node; - return NULL; -} - -ImGuiDockNode* ImGui::DockNodeTreeFindNodeByPos(ImGuiDockNode* node, ImVec2 pos) -{ - if (!node->IsVisible) - return NULL; - - ImGuiContext& g = *GImGui; - const float dock_spacing = g.Style.ItemInnerSpacing.x; - ImRect r(node->Pos, node->Pos + node->Size); - r.Expand(dock_spacing * 0.5f); - bool inside = r.Contains(pos); - if (!inside) - return NULL; - - if (node->IsLeafNode()) - return node; - if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[0], pos)) - return hovered_node; - if (ImGuiDockNode* hovered_node = DockNodeTreeFindNodeByPos(node->ChildNodes[1], pos)) - return hovered_node; - - // There is an edge case when docking into a dockspace which only has inactive nodes (because none of the windows are active) - // In this case we need to fallback into any leaf mode, possibly the central node. - if (node->IsDockSpace() && node->IsRootNode()) - { - if (node->CentralNode && node->IsLeafNode()) // FIXME-20181220: We should not have to test for IsLeafNode() here but we have another bug to fix first. - return node->CentralNode; - return DockNodeTreeFindFallbackLeafNode(node); - } - - return NULL; -} - -//----------------------------------------------------------------------------- -// Docking: Public Functions (SetWindowDock, DockSpace, DockSpaceOverViewport) -//----------------------------------------------------------------------------- -// - SetWindowDock() [Internal] -// - DockSpace() -// - DockSpaceOverViewport() -//----------------------------------------------------------------------------- - -// [Internal] Called via SetNextWindowDockID() -void ImGui::SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond) -{ - // Test condition (NB: bit 0 is always true) and clear flags for next time - if (cond && (window->SetWindowDockAllowFlags & cond) == 0) - return; - window->SetWindowDockAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - - if (window->DockId == dock_id) - return; - - // If the user attempt to set a dock id that is a split node, we'll dig within to find a suitable docking spot - ImGuiContext* ctx = GImGui; - if (ImGuiDockNode* new_node = DockContextFindNodeByID(ctx, dock_id)) - if (new_node->IsSplitNode()) - { - // Policy: Find central node or latest focused node. We first move back to our root node. - new_node = DockNodeGetRootNode(new_node); - if (new_node->CentralNode) - { - IM_ASSERT(new_node->CentralNode->IsCentralNode()); - dock_id = new_node->CentralNode->ID; - } - else - { - dock_id = new_node->LastFocusedNodeId; - } - } - - if (window->DockId == dock_id) - return; - - if (window->DockNode) - DockNodeRemoveWindow(window->DockNode, window, 0); - window->DockId = dock_id; -} - -// Create an explicit dockspace node within an existing window. Also expose dock node flags and creates a CentralNode by default. -// The Central Node is always displayed even when empty and shrink/extend according to the requested size of its neighbors. -// DockSpace() needs to be submitted _before_ any window they can host. If you use a dockspace, submit it early in your app. -void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags flags, const ImGuiWindowClass* window_class) -{ - ImGuiContext* ctx = GImGui; - ImGuiContext& g = *ctx; - ImGuiWindow* window = GetCurrentWindow(); - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) - return; - - // Early out if parent window is hidden/collapsed - // This is faster but also DockNodeUpdateTabBar() relies on TabBarLayout() running (which won't if SkipItems=true) to set NextSelectedTabId = 0). See #2960. - // If for whichever reason this is causing problem we would need to ensure that DockNodeUpdateTabBar() ends up clearing NextSelectedTabId even if SkipItems=true. - if (window->SkipItems) - flags |= ImGuiDockNodeFlags_KeepAliveOnly; - - IM_ASSERT((flags & ImGuiDockNodeFlags_DockSpace) == 0); - ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); - if (!node) - { - IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X created\n", id); - node = DockContextAddNode(ctx, id); - node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - } - if (window_class && window_class->ClassId != node->WindowClass.ClassId) - IMGUI_DEBUG_LOG_DOCKING("DockSpace: dockspace node 0x%08X: setup WindowClass 0x%08X -> 0x%08X\n", id, node->WindowClass.ClassId, window_class->ClassId); - node->SharedFlags = flags; - node->WindowClass = window_class ? *window_class : ImGuiWindowClass(); - - // When a DockSpace transitioned form implicit to explicit this may be called a second time - // It is possible that the node has already been claimed by a docked window which appeared before the DockSpace() node, so we overwrite IsDockSpace again. - if (node->LastFrameActive == g.FrameCount && !(flags & ImGuiDockNodeFlags_KeepAliveOnly)) - { - IM_ASSERT(node->IsDockSpace() == false && "Cannot call DockSpace() twice a frame with the same ID"); - node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; - return; - } - node->LocalFlags |= ImGuiDockNodeFlags_DockSpace; - - // Keep alive mode, this is allow windows docked into this node so stay docked even if they are not visible - if (flags & ImGuiDockNodeFlags_KeepAliveOnly) - { - node->LastFrameAlive = g.FrameCount; - return; - } - - const ImVec2 content_avail = GetContentRegionAvail(); - ImVec2 size = ImFloor(size_arg); - if (size.x <= 0.0f) - size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) - if (size.y <= 0.0f) - size.y = ImMax(content_avail.y + size.y, 4.0f); - IM_ASSERT(size.x > 0.0f && size.y > 0.0f); - - node->Pos = window->DC.CursorPos; - node->Size = node->SizeRef = size; - SetNextWindowPos(node->Pos); - SetNextWindowSize(node->Size); - g.NextWindowData.PosUndock = false; - - // FIXME-DOCK Why do we need a child window to host a dockspace, could we host it in the existing window? - ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_DockNodeHost; - window_flags |= ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar; - window_flags |= ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; - - char title[256]; - ImFormatString(title, IM_ARRAYSIZE(title), "%s/DockSpace_%08X", window->Name, id); - - if (node->Windows.Size > 0 || node->IsSplitNode()) - PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0)); - PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f); - Begin(title, NULL, window_flags); - PopStyleVar(); - if (node->Windows.Size > 0 || node->IsSplitNode()) - PopStyleColor(); - - ImGuiWindow* host_window = g.CurrentWindow; - host_window->DockNodeAsHost = node; - host_window->ChildId = window->GetID(title); - node->HostWindow = host_window; - node->OnlyNodeWithWindows = NULL; - - IM_ASSERT(node->IsRootNode()); - - // We need to handle the rare case were a central node is missing. - // This can happen if the node was first created manually with DockBuilderAddNode() but _without_ the ImGuiDockNodeFlags_Dockspace. - // Doing it correctly would set the _CentralNode flags, which would then propagate according to subsequent split. - // It would also be ambiguous to attempt to assign a central node while there are split nodes, so we wait until there's a single node remaining. - // The specific sub-property of _CentralNode we are interested in recovering here is the "Don't delete when empty" property, - // as it doesn't make sense for an empty dockspace to not have this property. - if (node->IsLeafNode() && !node->IsCentralNode()) - node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - - // Update the node - DockNodeUpdate(node); - - g.WithinEndChild = true; - End(); - g.WithinEndChild = false; -} - -// Tips: Use with ImGuiDockNodeFlags_PassthruCentralNode! -// The limitation with this call is that your window won't have a menu bar. -// Even though we could pass window flags, it would also require the user to be able to call BeginMenuBar() somehow meaning we can't Begin/End in a single function. -// But you can also use BeginMainMenuBar(). If you really want a menu bar inside the same window as the one hosting the dockspace, you will need to copy this code somewhere and tweak it. -ImGuiID ImGui::DockSpaceOverViewport(ImGuiViewport* viewport, ImGuiDockNodeFlags dockspace_flags, const ImGuiWindowClass* window_class) -{ - if (viewport == NULL) - viewport = GetMainViewport(); - - SetNextWindowPos(viewport->GetWorkPos()); - SetNextWindowSize(viewport->GetWorkSize()); - SetNextWindowViewport(viewport->ID); - - ImGuiWindowFlags host_window_flags = 0; - host_window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDocking; - host_window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; - if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode) - host_window_flags |= ImGuiWindowFlags_NoBackground; - - char label[32]; - ImFormatString(label, IM_ARRAYSIZE(label), "DockSpaceViewport_%08X", viewport->ID); - - PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - Begin(label, NULL, host_window_flags); - PopStyleVar(3); - - ImGuiID dockspace_id = GetID("DockSpace"); - DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags, window_class); - End(); - - return dockspace_id; -} - -//----------------------------------------------------------------------------- -// Docking: Builder Functions -//----------------------------------------------------------------------------- -// Very early end-user API to manipulate dock nodes. -// Only available in imgui_internal.h. Expect this API to change/break! -// It is expected that those functions are all called _before_ the dockspace node submission. -//----------------------------------------------------------------------------- -// - DockBuilderDockWindow() -// - DockBuilderGetNode() -// - DockBuilderSetNodePos() -// - DockBuilderSetNodeSize() -// - DockBuilderAddNode() -// - DockBuilderRemoveNode() -// - DockBuilderRemoveNodeChildNodes() -// - DockBuilderRemoveNodeDockedWindows() -// - DockBuilderSplitNode() -// - DockBuilderCopyNodeRec() -// - DockBuilderCopyNode() -// - DockBuilderCopyWindowSettings() -// - DockBuilderCopyDockSpace() -// - DockBuilderFinish() -//----------------------------------------------------------------------------- - -void ImGui::DockBuilderDockWindow(const char* window_name, ImGuiID node_id) -{ - // We don't preserve relative order of multiple docked windows (by clearing DockOrder back to -1) - ImGuiID window_id = ImHashStr(window_name); - if (ImGuiWindow* window = FindWindowByID(window_id)) - { - // Apply to created window - SetWindowDock(window, node_id, ImGuiCond_Always); - window->DockOrder = -1; - } - else - { - // Apply to settings - ImGuiWindowSettings* settings = FindWindowSettings(window_id); - if (settings == NULL) - settings = CreateNewWindowSettings(window_name); - settings->DockId = node_id; - settings->DockOrder = -1; - } -} - -ImGuiDockNode* ImGui::DockBuilderGetNode(ImGuiID node_id) -{ - ImGuiContext* ctx = GImGui; - return DockContextFindNodeByID(ctx, node_id); -} - -void ImGui::DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos) -{ - ImGuiContext* ctx = GImGui; - ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); - if (node == NULL) - return; - node->Pos = pos; - node->AuthorityForPos = ImGuiDataAuthority_DockNode; -} - -void ImGui::DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size) -{ - ImGuiContext* ctx = GImGui; - ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); - if (node == NULL) - return; - IM_ASSERT(size.x > 0.0f && size.y > 0.0f); - node->Size = node->SizeRef = size; - node->AuthorityForSize = ImGuiDataAuthority_DockNode; -} - -// Make sure to use the ImGuiDockNodeFlags_DockSpace flag to create a dockspace node! Otherwise this will create a floating node! -// - Floating node: you can then call DockBuilderSetNodePos()/DockBuilderSetNodeSize() to position and size the floating node. -// - Dockspace node: calling DockBuilderSetNodePos() is unnecessary. -// - If you intend to split a node immediately after creation using DockBuilderSplitNode(), make sure to call DockBuilderSetNodeSize() beforehand! -// For various reason, the splitting code currently needs a base size otherwise space may not be allocated as precisely as you would expect. -// - Use (id == 0) to let the system allocate a node identifier. -ImGuiID ImGui::DockBuilderAddNode(ImGuiID id, ImGuiDockNodeFlags flags) -{ - ImGuiContext* ctx = GImGui; - ImGuiDockNode* node = NULL; - if (flags & ImGuiDockNodeFlags_DockSpace) - { - DockSpace(id, ImVec2(0, 0), (flags & ~ImGuiDockNodeFlags_DockSpace) | ImGuiDockNodeFlags_KeepAliveOnly); - node = DockContextFindNodeByID(ctx, id); - } - else - { - if (id != 0) - node = DockContextFindNodeByID(ctx, id); - if (!node) - node = DockContextAddNode(ctx, id); - node->LocalFlags = flags; - } - node->LastFrameAlive = ctx->FrameCount; // Set this otherwise BeginDocked will undock during the same frame. - return node->ID; -} - -void ImGui::DockBuilderRemoveNode(ImGuiID node_id) -{ - ImGuiContext* ctx = GImGui; - ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_id); - if (node == NULL) - return; - DockBuilderRemoveNodeDockedWindows(node_id, true); - DockBuilderRemoveNodeChildNodes(node_id); - if (node->IsCentralNode() && node->ParentNode) - node->ParentNode->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - DockContextRemoveNode(ctx, node, true); -} - -// root_id = 0 to remove all, root_id != 0 to remove child of given node. -void ImGui::DockBuilderRemoveNodeChildNodes(ImGuiID root_id) -{ - ImGuiContext* ctx = GImGui; - ImGuiDockContext* dc = &ctx->DockContext; - - ImGuiDockNode* root_node = root_id ? DockContextFindNodeByID(ctx, root_id) : NULL; - if (root_id && root_node == NULL) - return; - bool has_central_node = false; - - ImGuiDataAuthority backup_root_node_authority_for_pos = root_node ? root_node->AuthorityForPos : ImGuiDataAuthority_Auto; - ImGuiDataAuthority backup_root_node_authority_for_size = root_node ? root_node->AuthorityForSize : ImGuiDataAuthority_Auto; - - // Process active windows - ImVector nodes_to_remove; - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - { - bool want_removal = (root_id == 0) || (node->ID != root_id && DockNodeGetRootNode(node)->ID == root_id); - if (want_removal) - { - if (node->IsCentralNode()) - has_central_node = true; - if (root_id != 0) - DockContextQueueNotifyRemovedNode(ctx, node); - if (root_node) - DockNodeMoveWindows(root_node, node); - nodes_to_remove.push_back(node); - } - } - - // DockNodeMoveWindows->DockNodeAddWindow will normally set those when reaching two windows (which is only adequate during interactive merge) - // Make sure we don't lose our current pos/size. (FIXME-DOCK: Consider tidying up that code in DockNodeAddWindow instead) - if (root_node) - { - root_node->AuthorityForPos = backup_root_node_authority_for_pos; - root_node->AuthorityForSize = backup_root_node_authority_for_size; - } - - // Apply to settings - for (ImGuiWindowSettings* settings = ctx->SettingsWindows.begin(); settings != NULL; settings = ctx->SettingsWindows.next_chunk(settings)) - if (ImGuiID window_settings_dock_id = settings->DockId) - for (int n = 0; n < nodes_to_remove.Size; n++) - if (nodes_to_remove[n]->ID == window_settings_dock_id) - { - settings->DockId = root_id; - break; - } - - // Not really efficient, but easier to destroy a whole hierarchy considering DockContextRemoveNode is attempting to merge nodes - if (nodes_to_remove.Size > 1) - ImQsort(nodes_to_remove.Data, nodes_to_remove.Size, sizeof(ImGuiDockNode*), DockNodeComparerDepthMostFirst); - for (int n = 0; n < nodes_to_remove.Size; n++) - DockContextRemoveNode(ctx, nodes_to_remove[n], false); - - if (root_id == 0) - { - dc->Nodes.Clear(); - dc->Requests.clear(); - } - else if (has_central_node) - { - root_node->LocalFlags |= ImGuiDockNodeFlags_CentralNode; - root_node->CentralNode = root_node; - } -} - -void ImGui::DockBuilderRemoveNodeDockedWindows(ImGuiID root_id, bool clear_settings_refs) -{ - // Clear references in settings - ImGuiContext* ctx = GImGui; - ImGuiContext& g = *ctx; - if (clear_settings_refs) - { - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - { - bool want_removal = (root_id == 0) || (settings->DockId == root_id); - if (!want_removal && settings->DockId != 0) - if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, settings->DockId)) - if (DockNodeGetRootNode(node)->ID == root_id) - want_removal = true; - if (want_removal) - settings->DockId = 0; - } - } - - // Clear references in windows - for (int n = 0; n < g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - bool want_removal = (root_id == 0) || (window->DockNode && DockNodeGetRootNode(window->DockNode)->ID == root_id) || (window->DockNodeAsHost && window->DockNodeAsHost->ID == root_id); - if (want_removal) - { - const ImGuiID backup_dock_id = window->DockId; - IM_UNUSED(backup_dock_id); - DockContextProcessUndockWindow(ctx, window, clear_settings_refs); - if (!clear_settings_refs) - IM_ASSERT(window->DockId == backup_dock_id); - } - } -} - -// If 'out_id_at_dir' or 'out_id_at_opposite_dir' are non NULL, the function will write out the ID of the two new nodes created. -// Return value is ID of the node at the specified direction, so same as (*out_id_at_dir) if that pointer is set. -// FIXME-DOCK: We are not exposing nor using split_outer. -ImGuiID ImGui::DockBuilderSplitNode(ImGuiID id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir) -{ - ImGuiContext* ctx = GImGui; - IM_ASSERT(split_dir != ImGuiDir_None); - IMGUI_DEBUG_LOG_DOCKING("DockBuilderSplitNode node 0x%08X, split_dir %d\n", id, split_dir); - - ImGuiDockNode* node = DockContextFindNodeByID(ctx, id); - if (node == NULL) - { - IM_ASSERT(node != NULL); - return 0; - } - - IM_ASSERT(!node->IsSplitNode()); // Assert if already Split - - ImGuiDockRequest req; - req.Type = ImGuiDockRequestType_Split; - req.DockTargetWindow = NULL; - req.DockTargetNode = node; - req.DockPayload = NULL; - req.DockSplitDir = split_dir; - req.DockSplitRatio = ImSaturate((split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? size_ratio_for_node_at_dir : 1.0f - size_ratio_for_node_at_dir); - req.DockSplitOuter = false; - DockContextProcessDock(ctx, &req); - - ImGuiID id_at_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 0 : 1]->ID; - ImGuiID id_at_opposite_dir = node->ChildNodes[(split_dir == ImGuiDir_Left || split_dir == ImGuiDir_Up) ? 1 : 0]->ID; - if (out_id_at_dir) - *out_id_at_dir = id_at_dir; - if (out_id_at_opposite_dir) - *out_id_at_opposite_dir = id_at_opposite_dir; - return id_at_dir; -} - -static ImGuiDockNode* DockBuilderCopyNodeRec(ImGuiDockNode* src_node, ImGuiID dst_node_id_if_known, ImVector* out_node_remap_pairs) -{ - ImGuiContext* ctx = GImGui; - ImGuiDockNode* dst_node = ImGui::DockContextAddNode(ctx, dst_node_id_if_known); - dst_node->SharedFlags = src_node->SharedFlags; - dst_node->LocalFlags = src_node->LocalFlags; - dst_node->Pos = src_node->Pos; - dst_node->Size = src_node->Size; - dst_node->SizeRef = src_node->SizeRef; - dst_node->SplitAxis = src_node->SplitAxis; - - out_node_remap_pairs->push_back(src_node->ID); - out_node_remap_pairs->push_back(dst_node->ID); - - for (int child_n = 0; child_n < IM_ARRAYSIZE(src_node->ChildNodes); child_n++) - if (src_node->ChildNodes[child_n]) - { - dst_node->ChildNodes[child_n] = DockBuilderCopyNodeRec(src_node->ChildNodes[child_n], 0, out_node_remap_pairs); - dst_node->ChildNodes[child_n]->ParentNode = dst_node; - } - - IMGUI_DEBUG_LOG_DOCKING("Fork node %08X -> %08X (%d childs)\n", src_node->ID, dst_node->ID, dst_node->IsSplitNode() ? 2 : 0); - return dst_node; -} - -void ImGui::DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs) -{ - ImGuiContext* ctx = GImGui; - IM_ASSERT(src_node_id != 0); - IM_ASSERT(dst_node_id != 0); - IM_ASSERT(out_node_remap_pairs != NULL); - - ImGuiDockNode* src_node = DockContextFindNodeByID(ctx, src_node_id); - IM_ASSERT(src_node != NULL); - - out_node_remap_pairs->clear(); - DockBuilderRemoveNode(dst_node_id); - DockBuilderCopyNodeRec(src_node, dst_node_id, out_node_remap_pairs); - - IM_ASSERT((out_node_remap_pairs->Size % 2) == 0); -} - -void ImGui::DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name) -{ - ImGuiWindow* src_window = FindWindowByName(src_name); - if (src_window == NULL) - return; - if (ImGuiWindow* dst_window = FindWindowByName(dst_name)) - { - dst_window->Pos = src_window->Pos; - dst_window->Size = src_window->Size; - dst_window->SizeFull = src_window->SizeFull; - dst_window->Collapsed = src_window->Collapsed; - } - else if (ImGuiWindowSettings* dst_settings = FindOrCreateWindowSettings(dst_name)) - { - ImVec2ih window_pos_2ih = ImVec2ih(src_window->Pos); - if (src_window->ViewportId != 0 && src_window->ViewportId != IMGUI_VIEWPORT_DEFAULT_ID) - { - dst_settings->ViewportPos = window_pos_2ih; - dst_settings->ViewportId = src_window->ViewportId; - dst_settings->Pos = ImVec2ih(0, 0); - } - else - { - dst_settings->Pos = window_pos_2ih; - } - dst_settings->Size = ImVec2ih(src_window->SizeFull); - dst_settings->Collapsed = src_window->Collapsed; - } -} - -// FIXME: Will probably want to change this signature, in particular how the window remapping pairs are passed. -void ImGui::DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs) -{ - IM_ASSERT(src_dockspace_id != 0); - IM_ASSERT(dst_dockspace_id != 0); - IM_ASSERT(in_window_remap_pairs != NULL); - IM_ASSERT((in_window_remap_pairs->Size % 2) == 0); - - // Duplicate entire dock - // FIXME: When overwriting dst_dockspace_id, windows that aren't part of our dockspace window class but that are docked in a same node will be split apart, - // whereas we could attempt to at least keep them together in a new, same floating node. - ImVector node_remap_pairs; - DockBuilderCopyNode(src_dockspace_id, dst_dockspace_id, &node_remap_pairs); - - // Attempt to transition all the upcoming windows associated to dst_dockspace_id into the newly created hierarchy of dock nodes - // (The windows associated to src_dockspace_id are staying in place) - ImVector src_windows; - for (int remap_window_n = 0; remap_window_n < in_window_remap_pairs->Size; remap_window_n += 2) - { - const char* src_window_name = (*in_window_remap_pairs)[remap_window_n]; - const char* dst_window_name = (*in_window_remap_pairs)[remap_window_n + 1]; - ImGuiID src_window_id = ImHashStr(src_window_name); - src_windows.push_back(src_window_id); - - // Search in the remapping tables - ImGuiID src_dock_id = 0; - if (ImGuiWindow* src_window = FindWindowByID(src_window_id)) - src_dock_id = src_window->DockId; - else if (ImGuiWindowSettings* src_window_settings = FindWindowSettings(src_window_id)) - src_dock_id = src_window_settings->DockId; - ImGuiID dst_dock_id = 0; - for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2) - if (node_remap_pairs[dock_remap_n] == src_dock_id) - { - dst_dock_id = node_remap_pairs[dock_remap_n + 1]; - //node_remap_pairs[dock_remap_n] = node_remap_pairs[dock_remap_n + 1] = 0; // Clear - break; - } - - if (dst_dock_id != 0) - { - // Docked windows gets redocked into the new node hierarchy. - IMGUI_DEBUG_LOG_DOCKING("Remap live window '%s' 0x%08X -> '%s' 0x%08X\n", src_window_name, src_dock_id, dst_window_name, dst_dock_id); - DockBuilderDockWindow(dst_window_name, dst_dock_id); - } - else - { - // Floating windows gets their settings transferred (regardless of whether the new window already exist or not) - // When this is leading to a Copy and not a Move, we would get two overlapping floating windows. Could we possibly dock them together? - IMGUI_DEBUG_LOG_DOCKING("Remap window settings '%s' -> '%s'\n", src_window_name, dst_window_name); - DockBuilderCopyWindowSettings(src_window_name, dst_window_name); - } - } - - // Anything else in the source nodes of 'node_remap_pairs' are windows that were docked in src_dockspace_id but are not owned by it (unaffiliated windows, e.g. "ImGui Demo") - // Find those windows and move to them to the cloned dock node. This may be optional? - for (int dock_remap_n = 0; dock_remap_n < node_remap_pairs.Size; dock_remap_n += 2) - if (ImGuiID src_dock_id = node_remap_pairs[dock_remap_n]) - { - ImGuiID dst_dock_id = node_remap_pairs[dock_remap_n + 1]; - ImGuiDockNode* node = DockBuilderGetNode(src_dock_id); - for (int window_n = 0; window_n < node->Windows.Size; window_n++) - { - ImGuiWindow* window = node->Windows[window_n]; - if (src_windows.contains(window->ID)) - continue; - - // Docked windows gets redocked into the new node hierarchy. - IMGUI_DEBUG_LOG_DOCKING("Remap window '%s' %08X -> %08X\n", window->Name, src_dock_id, dst_dock_id); - DockBuilderDockWindow(window->Name, dst_dock_id); - } - } -} - -void ImGui::DockBuilderFinish(ImGuiID root_id) -{ - ImGuiContext* ctx = GImGui; - //DockContextRebuild(ctx); - DockContextBuildAddWindowsToNodes(ctx, root_id); -} - -//----------------------------------------------------------------------------- -// Docking: Begin/End Support Functions (called from Begin/End) -//----------------------------------------------------------------------------- -// - GetWindowAlwaysWantOwnTabBar() -// - DockContextBindNodeToWindow() -// - BeginDocked() -// - BeginDockableDragDropSource() -// - BeginDockableDragDropTarget() -//----------------------------------------------------------------------------- - -bool ImGui::GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (g.IO.ConfigDockingAlwaysTabBar || window->WindowClass.DockingAlwaysTabBar) - if ((window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) == 0) - if (!window->IsFallbackWindow) // We don't support AlwaysTabBar on the fallback/implicit window to avoid unused dock-node overhead/noise - return true; - return false; -} - -static ImGuiDockNode* ImGui::DockContextBindNodeToWindow(ImGuiContext* ctx, ImGuiWindow* window) -{ - ImGuiContext& g = *ctx; - ImGuiDockNode* node = DockContextFindNodeByID(ctx, window->DockId); - IM_ASSERT(window->DockNode == NULL); - - // We should not be docking into a split node (SetWindowDock should avoid this) - if (node && node->IsSplitNode()) - { - DockContextProcessUndockWindow(ctx, window); - return NULL; - } - - // Create node - if (node == NULL) - { - node = DockContextAddNode(ctx, window->DockId); - node->AuthorityForPos = node->AuthorityForSize = node->AuthorityForViewport = ImGuiDataAuthority_Window; - node->LastFrameAlive = g.FrameCount; - } - - // If the node just turned visible and is part of a hierarchy, it doesn't have a Size assigned by DockNodeTreeUpdatePosSize() yet, - // so we're forcing a Pos/Size update from the first ancestor that is already visible (often it will be the root node). - // If we don't do this, the window will be assigned a zero-size on its first frame, which won't ideally warm up the layout. - // This is a little wonky because we don't normally update the Pos/Size of visible node mid-frame. - if (!node->IsVisible) - { - ImGuiDockNode* ancestor_node = node; - while (!ancestor_node->IsVisible) - { - ancestor_node->IsVisible = true; - ancestor_node->MarkedForPosSizeWrite = true; - if (ancestor_node->ParentNode) - ancestor_node = ancestor_node->ParentNode; - } - IM_ASSERT(ancestor_node->Size.x > 0.0f && ancestor_node->Size.y > 0.0f); - DockNodeTreeUpdatePosSize(ancestor_node, ancestor_node->Pos, ancestor_node->Size, true); - } - - // Add window to node - DockNodeAddWindow(node, window, true); - IM_ASSERT(node == window->DockNode); - return node; -} - -void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) -{ - ImGuiContext* ctx = GImGui; - ImGuiContext& g = *ctx; - - const bool auto_dock_node = GetWindowAlwaysWantOwnTabBar(window); - if (auto_dock_node) - { - if (window->DockId == 0) - { - IM_ASSERT(window->DockNode == NULL); - window->DockId = DockContextGenNodeID(ctx); - } - } - else - { - // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) - bool want_undock = false; - want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0; - want_undock |= (g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasPos) && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock; - if (want_undock) - { - DockContextProcessUndockWindow(ctx, window); - return; - } - } - - // Bind to our dock node - ImGuiDockNode* node = window->DockNode; - if (node != NULL) - IM_ASSERT(window->DockId == node->ID); - if (window->DockId != 0 && node == NULL) - { - node = DockContextBindNodeToWindow(ctx, window); - if (node == NULL) - return; - } - -#if 0 - // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set - if (node->IsCentralNode && (node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode)) - { - DockContextProcessUndockWindow(ctx, window); - return; - } -#endif - - // Undock if our dockspace node disappeared - // Note how we are testing for LastFrameAlive and NOT LastFrameActive. A DockSpace node can be maintained alive while being inactive with ImGuiDockNodeFlags_KeepAliveOnly. - if (node->LastFrameAlive < g.FrameCount) - { - // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking() - ImGuiDockNode* root_node = DockNodeGetRootNode(node); - if (root_node->LastFrameAlive < g.FrameCount) - { - DockContextProcessUndockWindow(ctx, window); - } - else - { - window->DockIsActive = true; - window->DockTabIsVisible = false; - } - return; - } - - // Fast path return. It is common for windows to hold on a persistent DockId but be the only visible window, - // and never create neither a host window neither a tab bar. - // FIXME-DOCK: replace ->HostWindow NULL compare with something more explicit (~was initially intended as a first frame test) - if (node->HostWindow == NULL) - { - window->DockIsActive = (node->State == ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing); - window->DockTabIsVisible = false; - return; - } - - // We can have zero-sized nodes (e.g. children of a small-size dockspace) - IM_ASSERT(node->HostWindow); - IM_ASSERT(node->IsLeafNode()); - IM_ASSERT(node->Size.x >= 0.0f && node->Size.y >= 0.0f); - node->State = ImGuiDockNodeState_HostWindowVisible; - - // Undock if we are submitted earlier than the host window - if (window->BeginOrderWithinContext < node->HostWindow->BeginOrderWithinContext) - { - DockContextProcessUndockWindow(ctx, window); - return; - } - - // Position/Size window - SetNextWindowPos(node->Pos); - SetNextWindowSize(node->Size); - g.NextWindowData.PosUndock = false; // Cancel implicit undocking of SetNextWindowPos() - window->DockIsActive = true; - window->DockTabIsVisible = false; - if (node->SharedFlags & ImGuiDockNodeFlags_KeepAliveOnly) - return; - - // When the window is selected we mark it as visible. - if (node->VisibleWindow == window) - window->DockTabIsVisible = true; - - // Update window flag - IM_ASSERT((window->Flags & ImGuiWindowFlags_ChildWindow) == 0); - window->Flags |= ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_NoResize; - if (node->IsHiddenTabBar() || node->IsNoTabBar()) - window->Flags |= ImGuiWindowFlags_NoTitleBar; - else - window->Flags &= ~ImGuiWindowFlags_NoTitleBar; // Clear the NoTitleBar flag in case the user set it: confusingly enough we need a title bar height so we are correctly offset, but it won't be displayed! - - // Save new dock order only if the tab bar has been visible once. - // This allows multiple windows to be created in the same frame and have their respective dock orders preserved. - if (node->TabBar && node->TabBar->CurrFrameVisible != -1) - window->DockOrder = (short)DockNodeGetTabOrder(window); - - if ((node->WantCloseAll || node->WantCloseTabId == window->ID) && p_open != NULL) - *p_open = false; - - // Update ChildId to allow returning from Child to Parent with Escape - ImGuiWindow* parent_window = window->DockNode->HostWindow; - window->ChildId = parent_window->GetID(window->Name); -} - -void ImGui::BeginDockableDragDropSource(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.ActiveId == window->MoveId); - IM_ASSERT(g.MovingWindow == window); - - window->DC.LastItemId = window->MoveId; - window = window->RootWindow; - IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); - bool is_drag_docking = (g.IO.ConfigDockingWithShift) || ImRect(0, 0, window->SizeFull.x, GetFrameHeight()).Contains(g.ActiveIdClickOffset); - if (is_drag_docking && BeginDragDropSource(ImGuiDragDropFlags_SourceNoPreviewTooltip | ImGuiDragDropFlags_SourceNoHoldToOpenOthers | ImGuiDragDropFlags_SourceAutoExpirePayload)) - { - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, &window, sizeof(window)); - EndDragDropSource(); - } -} - -void ImGui::BeginDockableDragDropTarget(ImGuiWindow* window) -{ - ImGuiContext* ctx = GImGui; - ImGuiContext& g = *ctx; - - //IM_ASSERT(window->RootWindow == window); // May also be a DockSpace - IM_ASSERT((window->Flags & ImGuiWindowFlags_NoDocking) == 0); - if (!g.DragDropActive) - return; - if (!BeginDragDropTargetCustom(window->Rect(), window->ID)) - return; - - // Peek into the payload before calling AcceptDragDropPayload() so we can handle overlapping dock nodes with filtering - // (this is a little unusual pattern, normally most code would call AcceptDragDropPayload directly) - const ImGuiPayload* payload = &g.DragDropPayload; - if (!payload->IsDataType(IMGUI_PAYLOAD_TYPE_WINDOW) || !DockNodeIsDropAllowed(window, *(ImGuiWindow**)payload->Data)) - { - EndDragDropTarget(); - return; - } - - ImGuiWindow* payload_window = *(ImGuiWindow**)payload->Data; - if (AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_WINDOW, ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect)) - { - // Select target node - ImGuiDockNode* node = NULL; - bool allow_null_target_node = false; - if (window->DockNodeAsHost) - node = DockNodeTreeFindNodeByPos(window->DockNodeAsHost, g.IO.MousePos); - else if (window->DockNode) // && window->DockIsActive) - node = window->DockNode; - else - allow_null_target_node = true; // Dock into a regular window - - const ImRect explicit_target_rect = (node && node->TabBar && !node->IsHiddenTabBar() && !node->IsNoTabBar()) ? node->TabBar->BarRect : ImRect(window->Pos, window->Pos + ImVec2(window->Size.x, GetFrameHeight())); - const bool is_explicit_target = g.IO.ConfigDockingWithShift || IsMouseHoveringRect(explicit_target_rect.Min, explicit_target_rect.Max); - - // Preview docking request and find out split direction/ratio - //const bool do_preview = true; // Ignore testing for payload->IsPreview() which removes one frame of delay, but breaks overlapping drop targets within the same window. - const bool do_preview = payload->IsPreview() || payload->IsDelivery(); - if (do_preview && (node != NULL || allow_null_target_node)) - { - ImGuiDockPreviewData split_inner; - ImGuiDockPreviewData split_outer; - ImGuiDockPreviewData* split_data = &split_inner; - if (node && (node->ParentNode || node->IsCentralNode())) - if (ImGuiDockNode* root_node = DockNodeGetRootNode(node)) - { - DockNodePreviewDockSetup(window, root_node, payload_window, &split_outer, is_explicit_target, true); - if (split_outer.IsSplitDirExplicit) - split_data = &split_outer; - } - DockNodePreviewDockSetup(window, node, payload_window, &split_inner, is_explicit_target, false); - if (split_data == &split_outer) - split_inner.IsDropAllowed = false; - - // Draw inner then outer, so that previewed tab (in inner data) will be behind the outer drop boxes - DockNodePreviewDockRender(window, node, payload_window, &split_inner); - DockNodePreviewDockRender(window, node, payload_window, &split_outer); - - // Queue docking request - if (split_data->IsDropAllowed && payload->IsDelivery()) - DockContextQueueDock(ctx, window, split_data->SplitNode, payload_window, split_data->SplitDir, split_data->SplitRatio, split_data == &split_outer); - } - } - EndDragDropTarget(); -} - -//----------------------------------------------------------------------------- -// Docking: Settings -//----------------------------------------------------------------------------- -// - DockSettingsRenameNodeReferences() -// - DockSettingsRemoveNodeReferences() -// - DockSettingsFindNodeSettings() -// - DockSettingsHandler_ApplyAll() -// - DockSettingsHandler_ReadOpen() -// - DockSettingsHandler_ReadLine() -// - DockSettingsHandler_DockNodeToSettings() -// - DockSettingsHandler_WriteAll() -//----------------------------------------------------------------------------- - -static void ImGui::DockSettingsRenameNodeReferences(ImGuiID old_node_id, ImGuiID new_node_id) -{ - ImGuiContext& g = *GImGui; - IMGUI_DEBUG_LOG_DOCKING("DockSettingsRenameNodeReferences: from 0x%08X -> to 0x%08X\n", old_node_id, new_node_id); - for (int window_n = 0; window_n < g.Windows.Size; window_n++) - { - ImGuiWindow* window = g.Windows[window_n]; - if (window->DockId == old_node_id && window->DockNode == NULL) - window->DockId = new_node_id; - } - //// FIXME-OPT: We could remove this loop by storing the index in the map - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (settings->DockId == old_node_id) - settings->DockId = new_node_id; -} - -// Remove references stored in ImGuiWindowSettings to the given ImGuiDockNodeSettings -static void ImGui::DockSettingsRemoveNodeReferences(ImGuiID* node_ids, int node_ids_count) -{ - ImGuiContext& g = *GImGui; - int found = 0; - //// FIXME-OPT: We could remove this loop by storing the index in the map - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - for (int node_n = 0; node_n < node_ids_count; node_n++) - if (settings->DockId == node_ids[node_n]) - { - settings->DockId = 0; - settings->DockOrder = -1; - if (++found < node_ids_count) - break; - return; - } -} - -static ImGuiDockNodeSettings* ImGui::DockSettingsFindNodeSettings(ImGuiContext* ctx, ImGuiID id) -{ - // FIXME-OPT - ImGuiDockContext* dc = &ctx->DockContext; - for (int n = 0; n < dc->NodesSettings.Size; n++) - if (dc->NodesSettings[n].ID == id) - return &dc->NodesSettings[n]; - return NULL; -} - -// Clear settings data -static void ImGui::DockSettingsHandler_ClearAll(ImGuiContext* ctx, ImGuiSettingsHandler*) -{ - ImGuiDockContext* dc = &ctx->DockContext; - dc->NodesSettings.clear(); - DockContextClearNodes(ctx, 0, true); -} - -// Recreate nodes based on settings data -static void ImGui::DockSettingsHandler_ApplyAll(ImGuiContext* ctx, ImGuiSettingsHandler*) -{ - // Prune settings at boot time only - ImGuiDockContext* dc = &ctx->DockContext; - if (ctx->Windows.Size == 0) - DockContextPruneUnusedSettingsNodes(ctx); - DockContextBuildNodesFromSettings(ctx, dc->NodesSettings.Data, dc->NodesSettings.Size); - DockContextBuildAddWindowsToNodes(ctx, 0); -} - -static void* ImGui::DockSettingsHandler_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -{ - if (strcmp(name, "Data") != 0) - return NULL; - return (void*)1; -} - -static void ImGui::DockSettingsHandler_ReadLine(ImGuiContext* ctx, ImGuiSettingsHandler*, void*, const char* line) -{ - char c = 0; - int x = 0, y = 0; - int r = 0; - - // Parsing, e.g. - // " DockNode ID=0x00000001 Pos=383,193 Size=201,322 Split=Y,0.506 " - // " DockNode ID=0x00000002 Parent=0x00000001 " - // Important: this code expect currently fields in a fixed order. - ImGuiDockNodeSettings node; - line = ImStrSkipBlank(line); - if (strncmp(line, "DockNode", 8) == 0) { line = ImStrSkipBlank(line + strlen("DockNode")); } - else if (strncmp(line, "DockSpace", 9) == 0) { line = ImStrSkipBlank(line + strlen("DockSpace")); node.Flags |= ImGuiDockNodeFlags_DockSpace; } - else return; - if (sscanf(line, "ID=0x%08X%n", &node.ID, &r) == 1) { line += r; } else return; - if (sscanf(line, " Parent=0x%08X%n", &node.ParentNodeId, &r) == 1) { line += r; if (node.ParentNodeId == 0) return; } - if (sscanf(line, " Window=0x%08X%n", &node.ParentWindowId, &r) ==1) { line += r; if (node.ParentWindowId == 0) return; } - if (node.ParentNodeId == 0) - { - if (sscanf(line, " Pos=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Pos = ImVec2ih((short)x, (short)y); } else return; - if (sscanf(line, " Size=%i,%i%n", &x, &y, &r) == 2) { line += r; node.Size = ImVec2ih((short)x, (short)y); } else return; - } - else - { - if (sscanf(line, " SizeRef=%i,%i%n", &x, &y, &r) == 2) { line += r; node.SizeRef = ImVec2ih((short)x, (short)y); } - } - if (sscanf(line, " Split=%c%n", &c, &r) == 1) { line += r; if (c == 'X') node.SplitAxis = ImGuiAxis_X; else if (c == 'Y') node.SplitAxis = ImGuiAxis_Y; } - if (sscanf(line, " NoResize=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoResize; } - if (sscanf(line, " CentralNode=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_CentralNode; } - if (sscanf(line, " NoTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoTabBar; } - if (sscanf(line, " HiddenTabBar=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_HiddenTabBar; } - if (sscanf(line, " NoWindowMenuButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoWindowMenuButton; } - if (sscanf(line, " NoCloseButton=%d%n", &x, &r) == 1) { line += r; if (x != 0) node.Flags |= ImGuiDockNodeFlags_NoCloseButton; } - if (sscanf(line, " Selected=0x%08X%n", &node.SelectedWindowId,&r) == 1) { line += r; } - if (node.ParentNodeId != 0) - if (ImGuiDockNodeSettings* parent_settings = DockSettingsFindNodeSettings(ctx, node.ParentNodeId)) - node.Depth = parent_settings->Depth + 1; - ctx->DockContext.NodesSettings.push_back(node); -} - -static void DockSettingsHandler_DockNodeToSettings(ImGuiDockContext* dc, ImGuiDockNode* node, int depth) -{ - ImGuiDockNodeSettings node_settings; - IM_ASSERT(depth < (1 << (sizeof(node_settings.Depth) << 3))); - node_settings.ID = node->ID; - node_settings.ParentNodeId = node->ParentNode ? node->ParentNode->ID : 0; - node_settings.ParentWindowId = (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) ? node->HostWindow->ParentWindow->ID : 0; - node_settings.SelectedWindowId = node->SelectedTabId; - node_settings.SplitAxis = (signed char)(node->IsSplitNode() ? node->SplitAxis : ImGuiAxis_None); - node_settings.Depth = (char)depth; - node_settings.Flags = (node->LocalFlags & ImGuiDockNodeFlags_SavedFlagsMask_); - node_settings.Pos = ImVec2ih(node->Pos); - node_settings.Size = ImVec2ih(node->Size); - node_settings.SizeRef = ImVec2ih(node->SizeRef); - dc->NodesSettings.push_back(node_settings); - if (node->ChildNodes[0]) - DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[0], depth + 1); - if (node->ChildNodes[1]) - DockSettingsHandler_DockNodeToSettings(dc, node->ChildNodes[1], depth + 1); -} - -static void ImGui::DockSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -{ - ImGuiContext& g = *ctx; - ImGuiDockContext* dc = &ctx->DockContext; - if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable)) - return; - - // Gather settings data - // (unlike our windows settings, because nodes are always built we can do a full rewrite of the SettingsNode buffer) - dc->NodesSettings.resize(0); - dc->NodesSettings.reserve(dc->Nodes.Data.Size); - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (node->IsRootNode()) - DockSettingsHandler_DockNodeToSettings(dc, node, 0); - - int max_depth = 0; - for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++) - max_depth = ImMax((int)dc->NodesSettings[node_n].Depth, max_depth); - - // Write to text buffer - buf->appendf("[%s][Data]\n", handler->TypeName); - for (int node_n = 0; node_n < dc->NodesSettings.Size; node_n++) - { - const int line_start_pos = buf->size(); (void)line_start_pos; - const ImGuiDockNodeSettings* node_settings = &dc->NodesSettings[node_n]; - buf->appendf("%*s%s%*s", node_settings->Depth * 2, "", (node_settings->Flags & ImGuiDockNodeFlags_DockSpace) ? "DockSpace" : "DockNode ", (max_depth - node_settings->Depth) * 2, ""); // Text align nodes to facilitate looking at .ini file - buf->appendf(" ID=0x%08X", node_settings->ID); - if (node_settings->ParentNodeId) - { - buf->appendf(" Parent=0x%08X SizeRef=%d,%d", node_settings->ParentNodeId, node_settings->SizeRef.x, node_settings->SizeRef.y); - } - else - { - if (node_settings->ParentWindowId) - buf->appendf(" Window=0x%08X", node_settings->ParentWindowId); - buf->appendf(" Pos=%d,%d Size=%d,%d", node_settings->Pos.x, node_settings->Pos.y, node_settings->Size.x, node_settings->Size.y); - } - if (node_settings->SplitAxis != ImGuiAxis_None) - buf->appendf(" Split=%c", (node_settings->SplitAxis == ImGuiAxis_X) ? 'X' : 'Y'); - if (node_settings->Flags & ImGuiDockNodeFlags_NoResize) - buf->appendf(" NoResize=1"); - if (node_settings->Flags & ImGuiDockNodeFlags_CentralNode) - buf->appendf(" CentralNode=1"); - if (node_settings->Flags & ImGuiDockNodeFlags_NoTabBar) - buf->appendf(" NoTabBar=1"); - if (node_settings->Flags & ImGuiDockNodeFlags_HiddenTabBar) - buf->appendf(" HiddenTabBar=1"); - if (node_settings->Flags & ImGuiDockNodeFlags_NoWindowMenuButton) - buf->appendf(" NoWindowMenuButton=1"); - if (node_settings->Flags & ImGuiDockNodeFlags_NoCloseButton) - buf->appendf(" NoCloseButton=1"); - if (node_settings->SelectedWindowId) - buf->appendf(" Selected=0x%08X", node_settings->SelectedWindowId); - -#if IMGUI_DEBUG_INI_SETTINGS - // [DEBUG] Include comments in the .ini file to ease debugging - if (ImGuiDockNode* node = DockContextFindNodeByID(ctx, node_settings->ID)) - { - buf->appendf("%*s", ImMax(2, (line_start_pos + 92) - buf->size()), ""); // Align everything - if (node->IsDockSpace() && node->HostWindow && node->HostWindow->ParentWindow) - buf->appendf(" ; in '%s'", node->HostWindow->ParentWindow->Name); - // Iterate settings so we can give info about windows that didn't exist during the session. - int contains_window = 0; - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (settings->DockId == node_settings->ID) - { - if (contains_window++ == 0) - buf->appendf(" ; contains "); - buf->appendf("'%s' ", settings->GetName()); - } - } -#endif - buf->appendf("\n"); - } - buf->appendf("\n"); -} - - -//----------------------------------------------------------------------------- -// [SECTION] PLATFORM DEPENDENT HELPERS -//----------------------------------------------------------------------------- - -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_FUNCTIONS) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) - -#ifdef _MSC_VER -#pragma comment(lib, "user32") -#pragma comment(lib, "kernel32") -#endif - -// Win32 clipboard implementation -// We use g.ClipboardHandlerData for temporary storage to ensure it is freed on Shutdown() -static const char* GetClipboardTextFn_DefaultImpl(void*) -{ - ImGuiContext& g = *GImGui; - g.ClipboardHandlerData.clear(); - if (!::OpenClipboard(NULL)) - return NULL; - HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); - if (wbuf_handle == NULL) - { - ::CloseClipboard(); - return NULL; - } - if (const WCHAR* wbuf_global = (const WCHAR*)::GlobalLock(wbuf_handle)) - { - int buf_len = ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, NULL, 0, NULL, NULL); - g.ClipboardHandlerData.resize(buf_len); - ::WideCharToMultiByte(CP_UTF8, 0, wbuf_global, -1, g.ClipboardHandlerData.Data, buf_len, NULL, NULL); - } - ::GlobalUnlock(wbuf_handle); - ::CloseClipboard(); - return g.ClipboardHandlerData.Data; -} - -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) -{ - if (!::OpenClipboard(NULL)) - return; - const int wbuf_length = ::MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); - HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(WCHAR)); - if (wbuf_handle == NULL) - { - ::CloseClipboard(); - return; - } - WCHAR* wbuf_global = (WCHAR*)::GlobalLock(wbuf_handle); - ::MultiByteToWideChar(CP_UTF8, 0, text, -1, wbuf_global, wbuf_length); - ::GlobalUnlock(wbuf_handle); - ::EmptyClipboard(); - if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) - ::GlobalFree(wbuf_handle); - ::CloseClipboard(); -} - -#elif defined(__APPLE__) && TARGET_OS_OSX && defined(IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS) - -#include // Use old API to avoid need for separate .mm file -static PasteboardRef main_clipboard = 0; - -// OSX clipboard implementation -// If you enable this you will need to add '-framework ApplicationServices' to your linker command-line! -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) -{ - if (!main_clipboard) - PasteboardCreate(kPasteboardClipboard, &main_clipboard); - PasteboardClear(main_clipboard); - CFDataRef cf_data = CFDataCreate(kCFAllocatorDefault, (const UInt8*)text, strlen(text)); - if (cf_data) - { - PasteboardPutItemFlavor(main_clipboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), cf_data, 0); - CFRelease(cf_data); - } -} - -static const char* GetClipboardTextFn_DefaultImpl(void*) -{ - if (!main_clipboard) - PasteboardCreate(kPasteboardClipboard, &main_clipboard); - PasteboardSynchronize(main_clipboard); - - ItemCount item_count = 0; - PasteboardGetItemCount(main_clipboard, &item_count); - for (ItemCount i = 0; i < item_count; i++) - { - PasteboardItemID item_id = 0; - PasteboardGetItemIdentifier(main_clipboard, i + 1, &item_id); - CFArrayRef flavor_type_array = 0; - PasteboardCopyItemFlavors(main_clipboard, item_id, &flavor_type_array); - for (CFIndex j = 0, nj = CFArrayGetCount(flavor_type_array); j < nj; j++) - { - CFDataRef cf_data; - if (PasteboardCopyItemFlavorData(main_clipboard, item_id, CFSTR("public.utf8-plain-text"), &cf_data) == noErr) - { - ImGuiContext& g = *GImGui; - g.ClipboardHandlerData.clear(); - int length = (int)CFDataGetLength(cf_data); - g.ClipboardHandlerData.resize(length + 1); - CFDataGetBytes(cf_data, CFRangeMake(0, length), (UInt8*)g.ClipboardHandlerData.Data); - g.ClipboardHandlerData[length] = 0; - CFRelease(cf_data); - return g.ClipboardHandlerData.Data; - } - } - } - return NULL; -} - -#else - -// Local Dear ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers. -static const char* GetClipboardTextFn_DefaultImpl(void*) -{ - ImGuiContext& g = *GImGui; - return g.ClipboardHandlerData.empty() ? NULL : g.ClipboardHandlerData.begin(); -} - -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) -{ - ImGuiContext& g = *GImGui; - g.ClipboardHandlerData.clear(); - const char* text_end = text + strlen(text); - g.ClipboardHandlerData.resize((int)(text_end - text) + 1); - memcpy(&g.ClipboardHandlerData[0], text, (size_t)(text_end - text)); - g.ClipboardHandlerData[(int)(text_end - text)] = 0; -} - -#endif - -//----------------------------------------------------------------------------- -// [SECTION] METRICS/DEBUG WINDOW -//----------------------------------------------------------------------------- - -static void RenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - ImVec2 scale = bb.GetSize() / viewport->Size; - ImVec2 off = bb.Min - viewport->Pos * scale; - float alpha_mul = (viewport->Flags & ImGuiViewportFlags_Minimized) ? 0.30f : 1.00f; - window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul * 0.40f)); - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* thumb_window = g.Windows[i]; - if (!thumb_window->WasActive || ((thumb_window->Flags & ImGuiWindowFlags_ChildWindow))) - continue; - if (thumb_window->SkipItems && (thumb_window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME-DOCK: Skip hidden docked windows. Identify those betters. - continue; - if (thumb_window->Viewport != viewport) - continue; - - ImRect thumb_r = thumb_window->Rect(); - ImRect title_r = thumb_window->TitleBarRect(); - ImRect thumb_r_scaled = ImRect(ImFloor(off + thumb_r.Min * scale), ImFloor(off + thumb_r.Max * scale)); - ImRect title_r_scaled = ImRect(ImFloor(off + title_r.Min * scale), ImFloor(off + ImVec2(title_r.Max.x, title_r.Min.y) * scale) + ImVec2(0,5)); // Exaggerate title bar height - thumb_r_scaled.ClipWithFull(bb); - title_r_scaled.ClipWithFull(bb); - const bool window_is_focused = (g.NavWindow && thumb_window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); - window->DrawList->AddRectFilled(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_WindowBg, alpha_mul)); - window->DrawList->AddRectFilled(title_r_scaled.Min, title_r_scaled.Max, ImGui::GetColorU32(window_is_focused ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg, alpha_mul)); - window->DrawList->AddRect(thumb_r_scaled.Min, thumb_r_scaled.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul)); - if (ImGuiWindow* window_for_title = GetWindowForTitleDisplay(thumb_window)) - window->DrawList->AddText(g.Font, g.FontSize * 1.0f, title_r_scaled.Min, ImGui::GetColorU32(ImGuiCol_Text, alpha_mul), window_for_title->Name, ImGui::FindRenderedTextEnd(window_for_title->Name)); - } - draw_list->AddRect(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_Border, alpha_mul)); -} - -void ImGui::ShowViewportThumbnails() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // We don't display full monitor bounds (we could, but it often looks awkward), instead we display just enough to cover all of our viewports. - float SCALE = 1.0f / 8.0f; - ImRect bb_full; - //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++) - // bb_full.Add(GetPlatformMonitorMainRect(g.PlatformIO.Monitors[n])); - for (int n = 0; n < g.Viewports.Size; n++) - bb_full.Add(g.Viewports[n]->GetMainRect()); - ImVec2 p = window->DC.CursorPos; - ImVec2 off = p - bb_full.Min * SCALE; - //for (int n = 0; n < g.PlatformIO.Monitors.Size; n++) - // window->DrawList->AddRect(off + g.PlatformIO.Monitors[n].MainPos * SCALE, off + (g.PlatformIO.Monitors[n].MainPos + g.PlatformIO.Monitors[n].MainSize) * SCALE, ImGui::GetColorU32(ImGuiCol_Border)); - for (int n = 0; n < g.Viewports.Size; n++) - { - ImGuiViewportP* viewport = g.Viewports[n]; - ImRect viewport_draw_bb(off + (viewport->Pos) * SCALE, off + (viewport->Pos + viewport->Size) * SCALE); - RenderViewportThumbnail(window->DrawList, viewport, viewport_draw_bb); - } - ImGui::Dummy(bb_full.GetSize() * SCALE); -} - -#ifndef IMGUI_DISABLE_METRICS_WINDOW - -// Avoid naming collision with imgui_demo.cpp's HelpMarker() for unity builds. -static void MetricsHelpMarker(const char* desc) -{ - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(desc); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} - -void ImGui::ShowMetricsWindow(bool* p_open) -{ - if (!ImGui::Begin("Dear ImGui Metrics", p_open)) - { - ImGui::End(); - return; - } - - // Debugging enums - enum { WRT_OuterRect, WRT_OuterRectClipped, WRT_InnerRect, WRT_InnerClipRect, WRT_WorkRect, WRT_Content, WRT_ContentRegionRect, WRT_Count }; // Windows Rect Type - const char* wrt_rects_names[WRT_Count] = { "OuterRect", "OuterRectClipped", "InnerRect", "InnerClipRect", "WorkRect", "Content", "ContentRegionRect" }; - enum { TRT_OuterRect, TRT_WorkRect, TRT_HostClipRect, TRT_InnerClipRect, TRT_BackgroundClipRect, TRT_ColumnsRect, TRT_ColumnsClipRect, TRT_ColumnsContentHeadersUsed, TRT_ColumnsContentHeadersIdeal, TRT_ColumnsContentRowsFrozen, TRT_ColumnsContentRowsUnfrozen, TRT_Count }; // Tables Rect Type - const char* trt_rects_names[TRT_Count] = { "OuterRect", "WorkRect", "HostClipRect", "InnerClipRect", "BackgroundClipRect", "ColumnsRect", "ColumnsClipRect", "ColumnsContentHeadersUsed", "ColumnsContentHeadersIdeal", "ColumnsContentRowsFrozen", "ColumnsContentRowsUnfrozen" }; - - // State - static bool show_windows_rects = false; - static int show_windows_rect_type = WRT_WorkRect; - static bool show_windows_begin_order = false; - static bool show_tables_rects = false; - static int show_tables_rect_type = TRT_WorkRect; - static bool show_drawcmd_mesh = true; - static bool show_drawcmd_aabb = true; - static bool show_docking_nodes = false; + // State + static bool show_windows_rects = false; + static int show_windows_rect_type = WRT_WorkRect; + static bool show_windows_begin_order = false; + static bool show_tables_rects = false; + static int show_tables_rect_type = TRT_WorkRect; + static bool show_drawcmd_mesh = true; + static bool show_drawcmd_aabb = true; // Basic info ImGuiContext& g = *GImGui; @@ -15351,8 +10191,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) // - NodeColumns() // - NodeWindow() // - NodeWindows() - // - NodeViewport() - // - NodeDockNode() // - NodeTabBar() // - NodeStorage() struct Funcs @@ -15370,9 +10208,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) return ImRect(); } - static void NodeDrawCmdShowMeshAndBoundingBox(ImDrawList* fg_draw_list, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb) + static void NodeDrawCmdShowMeshAndBoundingBox(ImGuiWindow* window, const ImDrawList* draw_list, const ImDrawCmd* draw_cmd, int elem_offset, bool show_mesh, bool show_aabb) { IM_ASSERT(show_mesh || show_aabb); + ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; // Draw wire-frame version of all triangles @@ -15401,8 +10240,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) fg_draw_list->Flags = backup_flags; } - // Note that both 'window' and 'viewport' may be NULL here. Viewport is generally null of destroyed popups which previously owned a viewport. - static void NodeDrawList(ImGuiWindow* window, ImGuiViewportP* viewport, ImDrawList* draw_list, const char* label) + static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) { bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); if (draw_list == ImGui::GetWindowDrawList()) @@ -15413,8 +10251,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) return; } - ImDrawList* fg_draw_list = viewport ? GetForegroundDrawList(viewport) : NULL; // Render additional visuals into the top-most draw list - if (window && fg_draw_list && ImGui::IsItemHovered()) + ImDrawList* fg_draw_list = GetForegroundDrawList(window); // Render additional visuals into the top-most draw list + if (window && IsItemHovered()) fg_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); if (!node_open) return; @@ -15440,7 +10278,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "%s", buf); if (ImGui::IsItemHovered() && (show_drawcmd_mesh || show_drawcmd_aabb) && fg_draw_list) - NodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb); + NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, show_drawcmd_mesh, show_drawcmd_aabb); if (!pcmd_node_open) continue; @@ -15459,7 +10297,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImFormatString(buf, IM_ARRAYSIZE(buf), "Mesh: ElemCount: %d, VtxOffset: +%d, IdxOffset: +%d, Area: ~%0.f px", pcmd->ElemCount, pcmd->VtxOffset, pcmd->IdxOffset, total_area); ImGui::Selectable(buf); if (ImGui::IsItemHovered() && fg_draw_list) - NodeDrawCmdShowMeshAndBoundingBox(fg_draw_list, draw_list, pcmd, elem_offset, true, false); + NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false); // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. ImGuiListClipper clipper(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. @@ -15532,13 +10370,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::TextDisabled("Note: some memory buffers have been compacted/freed."); ImGuiWindowFlags flags = window->Flags; - NodeDrawList(window, window->Viewport, window->DrawList, "DrawList"); + NodeDrawList(window, window->DrawList, "DrawList"); ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), ContentSize (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->ContentSize.x, window->ContentSize.y); ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s%s%s%s..)", flags, (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : "", (flags & ImGuiWindowFlags_NoMouseInputs)? "NoMouseInputs":"", (flags & ImGuiWindowFlags_NoNavInputs) ? "NoNavInputs" : "", (flags & ImGuiWindowFlags_AlwaysAutoResize) ? "AlwaysAutoResize" : ""); - ImGui::BulletText("WindowClassId: 0x%08X", window->WindowClass.ClassId); ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f) Scrollbar:%s%s", window->Scroll.x, window->ScrollMax.x, window->Scroll.y, window->ScrollMax.y, window->ScrollbarX ? "X" : "", window->ScrollbarY ? "Y" : ""); ImGui::BulletText("Active: %d/%d, WriteAccessed: %d, BeginOrderWithinContext: %d", window->Active, window->WasActive, window->WriteAccessed, (window->Active || window->WasActive) ? window->BeginOrderWithinContext : -1); ImGui::BulletText("Appearing: %d, Hidden: %d (CanSkip %d Cannot %d), SkipItems: %d", window->Appearing, window->Hidden, window->HiddenFramesCanSkipItems, window->HiddenFramesCannotSkipItems, window->SkipItems); @@ -15548,13 +10385,7 @@ 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]: "); - ImGui::BulletText("Viewport: %d%s, ViewportId: 0x%08X, ViewportPos: (%.1f,%.1f)", window->Viewport ? window->Viewport->Idx : -1, window->ViewportOwned ? " (Owned)" : "", window->ViewportId, window->ViewportPos.x, window->ViewportPos.y); - ImGui::BulletText("ViewportMonitor: %d", window->Viewport ? window->Viewport->PlatformMonitor : -1); - ImGui::BulletText("DockId: 0x%04X, DockOrder: %d, Act: %d, Vis: %d", window->DockId, window->DockOrder, window->DockIsActive, window->DockTabIsVisible); - if (window->DockNode || window->DockNodeAsHost) - NodeDockNode(window->DockNodeAsHost ? window->DockNodeAsHost : window->DockNode, window->DockNodeAsHost ? "DockNodeAsHost" : "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)) @@ -15573,77 +10404,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) settings->ID, settings->GetName(), settings->Pos.x, settings->Pos.y, settings->Size.x, settings->Size.y, settings->Collapsed); } - static void NodeViewport(ImGuiViewportP* viewport) - { - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - if (ImGui::TreeNode((void*)(intptr_t)viewport->ID, "Viewport #%d, ID: 0x%08X, Parent: 0x%08X, Window: \"%s\"", viewport->Idx, viewport->ID, viewport->ParentViewportId, viewport->Window ? viewport->Window->Name : "N/A")) - { - ImGuiWindowFlags flags = viewport->Flags; - ImGui::BulletText("Main Pos: (%.0f,%.0f), Size: (%.0f,%.0f)\nWorkArea Offset Left: %.0f Top: %.0f, Right: %.0f, Bottom: %.0f\nMonitor: %d, DpiScale: %.0f%%", - viewport->Pos.x, viewport->Pos.y, viewport->Size.x, viewport->Size.y, - viewport->WorkOffsetMin.x, viewport->WorkOffsetMin.y, viewport->WorkOffsetMax.x, viewport->WorkOffsetMax.y, - viewport->PlatformMonitor, viewport->DpiScale * 100.0f); - if (viewport->Idx > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Reset Pos")) { viewport->Pos = ImVec2(200,200); if (viewport->Window) viewport->Window->Pos = ImVec2(200,200); } } - ImGui::BulletText("Flags: 0x%04X =%s%s%s%s%s%s%s", viewport->Flags, - (flags & ImGuiViewportFlags_CanHostOtherWindows) ? " CanHostOtherWindows" : "", (flags & ImGuiViewportFlags_NoDecoration) ? " NoDecoration" : "", - (flags & ImGuiViewportFlags_NoFocusOnAppearing) ? " NoFocusOnAppearing" : "", (flags & ImGuiViewportFlags_NoInputs) ? " NoInputs" : "", - (flags & ImGuiViewportFlags_NoRendererClear) ? " NoRendererClear" : "", (flags & ImGuiViewportFlags_Minimized) ? " Minimized" : "", - (flags & ImGuiViewportFlags_NoAutoMerge) ? " NoAutoMerge" : ""); - for (int layer_i = 0; layer_i < IM_ARRAYSIZE(viewport->DrawDataBuilder.Layers); layer_i++) - for (int draw_list_i = 0; draw_list_i < viewport->DrawDataBuilder.Layers[layer_i].Size; draw_list_i++) - Funcs::NodeDrawList(NULL, viewport, viewport->DrawDataBuilder.Layers[layer_i][draw_list_i], "DrawList"); - ImGui::TreePop(); - } - } - - static void NodeDockNode(ImGuiDockNode* node, const char* label) - { - ImGuiContext& g = *GImGui; - 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: %s split (vis: '%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) Ref (%.0f, %.0f)", - node->Pos.x, node->Pos.y, node->Size.x, node->Size.y, node->SizeRef.x, node->SizeRef.y); - NodeWindow(node->HostWindow, "HostWindow"); - NodeWindow(node->VisibleWindow, "VisibleWindow"); - ImGui::BulletText("SelectedTabID: 0x%08X, LastFocusedNodeID: 0x%08X", node->SelectedTabId, node->LastFocusedNodeId); - ImGui::BulletText("Misc:%s%s%s%s%s", - node->IsDockSpace() ? " IsDockSpace" : "", - node->IsCentralNode() ? " IsCentralNode" : "", - (g.FrameCount - node->LastFrameAlive < 2) ? " IsAlive" : "", - (g.FrameCount - node->LastFrameActive < 2) ? " IsActive" : "", - node->WantLockSizeOnce ? " WantLockSizeOnce" : ""); - if (ImGui::TreeNode("flags", "LocalFlags: 0x%04X SharedFlags: 0x%04X", node->LocalFlags, node->SharedFlags)) - { - ImGui::CheckboxFlags("LocalFlags: NoDocking", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoDocking); - ImGui::CheckboxFlags("LocalFlags: NoSplit", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoSplit); - ImGui::CheckboxFlags("LocalFlags: NoResize", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResize); - ImGui::CheckboxFlags("LocalFlags: NoResizeX", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResizeX); - ImGui::CheckboxFlags("LocalFlags: NoResizeY", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoResizeY); - ImGui::CheckboxFlags("LocalFlags: NoTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoTabBar); - ImGui::CheckboxFlags("LocalFlags: HiddenTabBar", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_HiddenTabBar); - ImGui::CheckboxFlags("LocalFlags: NoWindowMenuButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoWindowMenuButton); - ImGui::CheckboxFlags("LocalFlags: NoCloseButton", (ImU32*)&node->LocalFlags, ImGuiDockNodeFlags_NoCloseButton); - ImGui::TreePop(); - } - if (node->ParentNode) - NodeDockNode(node->ParentNode, "ParentNode"); - 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) { // Standalone tab bars (not associated to docking/windows functionality) currently hold no discernible strings. @@ -15651,13 +10411,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) char* p = buf; const char* buf_end = buf + IM_ARRAYSIZE(buf); p += ImFormatString(p, buf_end - p, "TabBar (%d tabs)%s", tab_bar->Tabs.Size, (tab_bar->PrevFrameVisible < ImGui::GetFrameCount() - 2) ? " *Inactive*" : ""); - if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) - { - p += ImFormatString(p, buf_end - p, " { "); - for (int tab_n = 0; tab_n < ImMin(tab_bar->Tabs.Size, 3); tab_n++) - p += ImFormatString(p, buf_end - p, "%s'%s'", tab_n > 0 ? ", " : "", tab_bar->Tabs[tab_n].Window->Name); - p += ImFormatString(p, buf_end - p, (tab_bar->Tabs.Size > 3) ? " ... }" : " } "); - } + IM_UNUSED(p); if (ImGui::TreeNode(tab_bar, "%s", buf)) { for (int tab_n = 0; tab_n < tab_bar->Tabs.Size; tab_n++) @@ -15666,7 +10420,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::PushID(tab); if (ImGui::SmallButton("<")) { TabBarQueueChangeTabOrder(tab_bar, tab, -1); } ImGui::SameLine(0, 2); if (ImGui::SmallButton(">")) { TabBarQueueChangeTabOrder(tab_bar, tab, +1); } ImGui::SameLine(); - ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->Window || tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : ""); + ImGui::Text("%02d%c Tab 0x%08X '%s'", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : ""); ImGui::PopID(); } ImGui::TreePop(); @@ -15719,28 +10473,10 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Contents Funcs::NodeWindows(g.Windows, "Windows"); //Funcs::NodeWindows(g.WindowsFocusOrder, "WindowsFocusOrder"); - if (ImGui::TreeNode("Viewport", "Viewports (%d)", g.Viewports.Size)) + if (ImGui::TreeNode("DrawLists", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) { - ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); - ImGui::ShowViewportThumbnails(); - ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - bool open = ImGui::TreeNode("Monitors", "Monitors (%d)", g.PlatformIO.Monitors.Size); - ImGui::SameLine(); - MetricsHelpMarker("Dear ImGui uses monitor data:\n- to query DPI settings on a per monitor basis\n- to position popup/tooltips so they don't straddle monitors."); - if (open) - { - for (int i = 0; i < g.PlatformIO.Monitors.Size; i++) - { - const ImGuiPlatformMonitor& mon = g.PlatformIO.Monitors[i]; - ImGui::BulletText("Monitor #%d: DPI %.0f%%\n MainMin (%.0f,%.0f), MainMax (%.0f,%.0f), MainSize (%.0f,%.0f)\n WorkMin (%.0f,%.0f), WorkMax (%.0f,%.0f), WorkSize (%.0f,%.0f)", - i, mon.DpiScale * 100.0f, - mon.MainPos.x, mon.MainPos.y, mon.MainPos.x + mon.MainSize.x, mon.MainPos.y + mon.MainSize.y, mon.MainSize.x, mon.MainSize.y, - mon.WorkPos.x, mon.WorkPos.y, mon.WorkPos.x + mon.WorkSize.x, mon.WorkPos.y + mon.WorkSize.y, mon.WorkSize.x, mon.WorkSize.y); - } - ImGui::TreePop(); - } - for (int i = 0; i < g.Viewports.Size; i++) - Funcs::NodeViewport(g.Viewports[i]); + for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) + Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); ImGui::TreePop(); } @@ -15778,19 +10514,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Details for Docking #ifdef IMGUI_HAS_DOCK - if (ImGui::TreeNode("Dock nodes")) + if (ImGui::TreeNode("Docking")) { - static bool root_nodes_only = true; - ImGuiDockContext* dc = &g.DockContext; - ImGui::Checkbox("List root nodes", &root_nodes_only); - ImGui::Checkbox("Ctrl shows window dock info", &show_docking_nodes); - if (ImGui::SmallButton("Clear nodes")) { DockContextClearNodes(&g, 0, true); } - ImGui::SameLine(); - if (ImGui::SmallButton("Rebuild all")) { dc->WantFullRebuild = true; } - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->Nodes.Data[n].val_p) - if (!root_nodes_only || node->IsRootNode()) - Funcs::NodeDockNode(node, "Node"); ImGui::TreePop(); } #endif // #ifdef IMGUI_HAS_DOCK @@ -15801,9 +10526,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::SmallButton("Clear")) ImGui::ClearIniSettings(); ImGui::SameLine(); - if (ImGui::SmallButton("Save to memory")) - ImGui::SaveIniSettingsToMemory(); - ImGui::SameLine(); if (ImGui::SmallButton("Save to disk")) ImGui::SaveIniSettingsToDisk(g.IO.IniFilename); ImGui::SameLine(); @@ -15815,7 +10537,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) if (ImGui::TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) { for (int n = 0; n < g.SettingsHandlers.Size; n++) - ImGui::BulletText("%s", g.SettingsHandlers[n].TypeName); + ImGui::TextUnformatted(g.SettingsHandlers[n].TypeName); ImGui::TreePop(); } if (ImGui::TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) @@ -15835,29 +10557,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) #endif // #ifdef IMGUI_HAS_TABLE #ifdef IMGUI_HAS_DOCK - if (ImGui::TreeNode("SettingsDocking", "Settings packed data: Docking")) - { - ImGuiDockContext* dc = &g.DockContext; - ImGui::Text("In SettingsWindows:"); - for (ImGuiWindowSettings* settings = g.SettingsWindows.begin(); settings != NULL; settings = g.SettingsWindows.next_chunk(settings)) - if (settings->DockId != 0) - ImGui::BulletText("Window '%s' -> DockId %08X", settings->GetName(), settings->DockId); - ImGui::Text("In SettingsNodes:"); - for (int n = 0; n < dc->NodesSettings.Size; n++) - { - ImGuiDockNodeSettings* settings = &dc->NodesSettings[n]; - const char* selected_tab_name = NULL; - if (settings->SelectedWindowId) - { - if (ImGuiWindow* window = FindWindowByID(settings->SelectedWindowId)) - selected_tab_name = window->Name; - else if (ImGuiWindowSettings* window_settings = FindWindowSettings(settings->SelectedWindowId)) - selected_tab_name = window_settings->GetName(); - } - ImGui::BulletText("Node %08X, Parent %08X, SelectedTab %08X ('%s')", settings->ID, settings->ParentNodeId, settings->SelectedWindowId, selected_tab_name ? selected_tab_name : settings->SelectedWindowId ? "N/A" : ""); - } - ImGui::TreePop(); - } #endif // #ifdef IMGUI_HAS_DOCK if (ImGui::TreeNode("SettingsIniData", "Settings unpacked data (.ini): %d bytes", g.SettingsIniData.size())) @@ -15875,7 +10574,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) 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"); @@ -15888,7 +10586,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); ImGui::Text("NavWindowingTarget: '%s'", g.NavWindowingTarget ? g.NavWindowingTarget->Name : "NULL"); ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); - ImGui::Text("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport->ID, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0); ImGui::TreePop(); } @@ -15932,27 +10629,6 @@ void ImGui::ShowMetricsWindow(bool* p_open) // Overlay: Display Docking info if (show_docking_nodes && g.IO.KeyCtrl) { - ImGuiDockContext* dc = &g.DockContext; - for (int n = 0; n < dc->Nodes.Data.Size; n++) - if (ImGuiDockNode* node = (ImGuiDockNode*)dc->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 = node->HostWindow ? GetForegroundDrawList(node->HostWindow) : GetForegroundDrawList((ImGuiViewportP*)GetMainViewport()); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "DockId: %X%s\n", node->ID, node->IsCentralNode() ? " *CentralNode*" : ""); - p += ImFormatString(p, buf + IM_ARRAYSIZE(buf) - p, "WindowClass: %08X\n", node->WindowClass.ClassId); - 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, "SizeRef: (%.0f, %.0f)\n", node->SizeRef.x, node->SizeRef.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); - } } #endif // #ifdef IMGUI_HAS_DOCK diff --git a/imgui_draw.cpp b/imgui_draw.cpp index d7b15519b32c..ad44b671c6e8 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -215,8 +215,6 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst) colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); - colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_HeaderActive] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -272,8 +270,6 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst) colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); - colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -330,8 +326,6 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst) colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f); colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f); colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f); - colors[ImGuiCol_DockingPreview] = colors[ImGuiCol_Header] * ImVec4(1.0f, 1.0f, 1.0f, 0.7f); - colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); @@ -3345,10 +3339,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col // - RenderBullet() // - RenderCheckMark() // - RenderMouseCursor() -// - RenderArrowDockMenu() // - RenderArrowPointingAt() // - RenderRectFilledRangeH() -// - RenderRectFilledWithHole() //----------------------------------------------------------------------------- // Function in need of a redesign (legacy mess) // - RenderColorRectWithAlphaCheckerboard() @@ -3440,14 +3432,6 @@ void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half } } -// This is less wide than RenderArrow() and we use in dock nodes instead of the regular RenderArrow() to denote a change of functionality, -// and because the saved space means that the left-most tab label can stay at exactly the same position as the label of a loose window. -void ImGui::RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col) -{ - draw_list->AddRectFilled(p_min + ImVec2(sz * 0.10f, sz * 0.15f), p_min + ImVec2(sz * 0.70f, sz * 0.30f), col); - RenderArrowPointingAt(draw_list, p_min + ImVec2(sz * 0.40f, sz * 0.85f), ImVec2(sz * 0.30f, sz * 0.40f), ImGuiDir_Down, col); -} - static inline float ImAcos01(float x) { if (x <= 0.0f) return IM_PI * 0.5f; @@ -3517,8 +3501,6 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im draw_list->PathFillConvex(col); } -// For CTRL+TAB within a docking node we need to render the dimming background in 8 steps -// (Because the root node renders the background in one shot, in order to avoid flickering when a child dock node is not submitted) void ImGui::RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding) { const bool fill_L = (inner.Min.x > outer.Min.x); diff --git a/imgui_internal.h b/imgui_internal.h index a615456858d0..ea135fce60a3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -94,10 +94,6 @@ struct ImGuiColumnData; // Storage data for a single column struct ImGuiColumns; // Storage data for a columns set struct ImGuiContext; // Main Dear ImGui context struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum -struct ImGuiDockContext; // Docking system context -struct ImGuiDockRequest; // Docking system dock/undock queued request -struct ImGuiDockNode; // Docking system node (hold a list of Windows OR two child dock nodes) -struct ImGuiDockNodeSettings; // Storage for a dock node in .ini file (we preserve those even if the associated dock node isn't active during the session) struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() internal data @@ -115,7 +111,6 @@ struct ImGuiWindowTempData; // Temporary storage for one window (that's struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. -typedef int ImGuiDataAuthority; // -> enum ImGuiDataAuthority_ // Enum: for storing the source authority (dock node vs window) of a field typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical typedef int ImGuiButtonFlags; // -> enum ImGuiButtonFlags_ // Flags: for ButtonEx(), ButtonBehavior() typedef int ImGuiColumnsFlags; // -> enum ImGuiColumnsFlags_ // Flags: BeginColumns() @@ -163,22 +158,11 @@ namespace ImStb // [SECTION] Macros //----------------------------------------------------------------------------- -// Internal Drag and Drop payload types. String starting with '_' are reserved for Dear ImGui. -#define IMGUI_PAYLOAD_TYPE_WINDOW "_IMWINDOW" // Payload == ImGuiWindow* - // Debug Logging #ifndef IMGUI_DEBUG_LOG #define IMGUI_DEBUG_LOG(_FMT,...) printf("[%05d] " _FMT, GImGui->FrameCount, __VA_ARGS__) #endif -// Debug Logging for selected systems. Remove the '((void)0) //' to enable. -//#define IMGUI_DEBUG_LOG_POPUP IMGUI_DEBUG_LOG // Enable log -//#define IMGUI_DEBUG_LOG_VIEWPORT IMGUI_DEBUG_LOG // Enable log -//#define IMGUI_DEBUG_LOG_DOCKING IMGUI_DEBUG_LOG // Enable log -#define IMGUI_DEBUG_LOG_POPUP(...) ((void)0) // Disable log -#define IMGUI_DEBUG_LOG_VIEWPORT(...) ((void)0) // Disable log -#define IMGUI_DEBUG_LOG_DOCKING(...) ((void)0) // Disable log - // Static Asserts #if (__cplusplus >= 201100) #define IM_STATIC_ASSERT(_COND) static_assert(_COND, "") @@ -935,10 +919,7 @@ enum ImGuiNextWindowDataFlags_ ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4, ImGuiNextWindowDataFlags_HasFocus = 1 << 5, ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6, - ImGuiNextWindowDataFlags_HasScroll = 1 << 7, - ImGuiNextWindowDataFlags_HasViewport = 1 << 8, - ImGuiNextWindowDataFlags_HasDock = 1 << 9, - ImGuiNextWindowDataFlags_HasWindowClass = 1 << 10 + ImGuiNextWindowDataFlags_HasScroll = 1 << 7 }; // Storage for SetNexWindow** functions @@ -948,22 +929,17 @@ struct ImGuiNextWindowData ImGuiCond PosCond; ImGuiCond SizeCond; ImGuiCond CollapsedCond; - ImGuiCond DockCond; ImVec2 PosVal; ImVec2 PosPivotVal; ImVec2 SizeVal; ImVec2 ContentSizeVal; ImVec2 ScrollVal; - bool PosUndock; bool CollapsedVal; ImRect SizeConstraintRect; ImGuiSizeCallback SizeCallback; void* SizeCallbackUserData; float BgAlphaVal; // Override background alpha - ImGuiID ViewportId; - ImGuiID DockId; - ImGuiWindowClass WindowClass; - ImVec2 MenuBarOffsetMinVal; // (Always on) This is not exposed publicly, so we don't clear it and it doesn't have a corresponding flag (could we? for consistency?) + ImVec2 MenuBarOffsetMinVal; // *Always on* This is not exposed publicly, so we don't clear it. ImGuiNextWindowData() { memset(this, 0, sizeof(*this)); } inline void ClearFlags() { Flags = ImGuiNextWindowDataFlags_None; } @@ -1076,113 +1052,7 @@ struct ImGuiColumns //----------------------------------------------------------------------------- #ifdef IMGUI_HAS_DOCK - -// Extend ImGuiDockNodeFlags_ -enum ImGuiDockNodeFlagsPrivate_ -{ - // [Internal] - ImGuiDockNodeFlags_DockSpace = 1 << 10, // Local, Saved // A dockspace is a node that occupy space within an existing user window. Otherwise the node is floating and create its own window. - ImGuiDockNodeFlags_CentralNode = 1 << 11, // Local, Saved // The central node has 2 main properties: stay visible when empty, only use "remaining" spaces from its neighbor. - ImGuiDockNodeFlags_NoTabBar = 1 << 12, // Local, Saved // Tab bar is completely unavailable. No triangle in the corner to enable it back. - ImGuiDockNodeFlags_HiddenTabBar = 1 << 13, // Local, Saved // Tab bar is hidden, with a triangle in the corner to show it again (NB: actual tab-bar instance may be destroyed as this is only used for single-window tab bar) - ImGuiDockNodeFlags_NoWindowMenuButton = 1 << 14, // Local, Saved // Disable window/docking menu (that one that appears instead of the collapse button) - ImGuiDockNodeFlags_NoCloseButton = 1 << 15, // Local, Saved // - ImGuiDockNodeFlags_NoDocking = 1 << 16, // Local, Saved // Disable any form of docking in this dockspace or individual node. (On a whole dockspace, this pretty much defeat the purpose of using a dockspace at all). Note: when turned on, existing docked nodes will be preserved. - ImGuiDockNodeFlags_NoDockingSplitMe = 1 << 17, // [EXPERIMENTAL] Prevent another window/node from splitting this node. - ImGuiDockNodeFlags_NoDockingSplitOther = 1 << 18, // [EXPERIMENTAL] Prevent this node from splitting another window/node. - ImGuiDockNodeFlags_NoDockingOverMe = 1 << 19, // [EXPERIMENTAL] Prevent another window/node to be docked over this node. - ImGuiDockNodeFlags_NoDockingOverOther = 1 << 20, // [EXPERIMENTAL] Prevent this node to be docked over another window/node. - ImGuiDockNodeFlags_NoResizeX = 1 << 21, // [EXPERIMENTAL] - ImGuiDockNodeFlags_NoResizeY = 1 << 22, // [EXPERIMENTAL] - ImGuiDockNodeFlags_SharedFlagsInheritMask_ = ~0, - ImGuiDockNodeFlags_NoResizeFlagsMask_ = ImGuiDockNodeFlags_NoResize | ImGuiDockNodeFlags_NoResizeX | ImGuiDockNodeFlags_NoResizeY, - ImGuiDockNodeFlags_LocalFlagsMask_ = ImGuiDockNodeFlags_NoSplit | ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_AutoHideTabBar | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking, - ImGuiDockNodeFlags_LocalFlagsTransferMask_ = ImGuiDockNodeFlags_LocalFlagsMask_ & ~ImGuiDockNodeFlags_DockSpace, // When splitting those flags are moved to the inheriting child, never duplicated - ImGuiDockNodeFlags_SavedFlagsMask_ = ImGuiDockNodeFlags_NoResizeFlagsMask_ | ImGuiDockNodeFlags_DockSpace | ImGuiDockNodeFlags_CentralNode | ImGuiDockNodeFlags_NoTabBar | ImGuiDockNodeFlags_HiddenTabBar | ImGuiDockNodeFlags_NoWindowMenuButton | ImGuiDockNodeFlags_NoCloseButton | ImGuiDockNodeFlags_NoDocking -}; - -// Store the source authority (dock node vs window) of a field -enum ImGuiDataAuthority_ -{ - ImGuiDataAuthority_Auto, - ImGuiDataAuthority_DockNode, - ImGuiDataAuthority_Window -}; - -enum ImGuiDockNodeState -{ - ImGuiDockNodeState_Unknown, - ImGuiDockNodeState_HostWindowHiddenBecauseSingleWindow, - ImGuiDockNodeState_HostWindowHiddenBecauseWindowsAreResizing, - ImGuiDockNodeState_HostWindowVisible -}; - -// sizeof() 116~160 -struct ImGuiDockNode -{ - ImGuiID ID; - ImGuiDockNodeFlags SharedFlags; // Flags shared by all nodes of a same dockspace hierarchy (inherited from the root node) - ImGuiDockNodeFlags LocalFlags; // Flags specific to this node - ImGuiDockNode* ParentNode; - ImGuiDockNode* ChildNodes[2]; // [Split node only] Child nodes (left/right or top/bottom). Consider switching to an array. - ImVector Windows; // Note: unordered list! Iterate TabBar->Tabs for user-order. - ImGuiTabBar* TabBar; - ImVec2 Pos; // Current position - ImVec2 Size; // Current size - ImVec2 SizeRef; // [Split node only] Last explicitly written-to size (overridden when using a splitter affecting the node), used to calculate Size. - ImGuiAxis SplitAxis; // [Split node only] Split axis (X or Y) - ImGuiWindowClass WindowClass; // [Root node only] - - ImGuiDockNodeState State; - ImGuiWindow* HostWindow; - ImGuiWindow* VisibleWindow; // Generally point to window which is ID is == SelectedTabID, but when CTRL+Tabbing this can be a different window. - ImGuiDockNode* CentralNode; // [Root node only] Pointer to central node. - ImGuiDockNode* OnlyNodeWithWindows; // [Root node only] Set when there is a single visible node within the hierarchy. - int LastFrameAlive; // Last frame number the node was updated or kept alive explicitly with DockSpace() + ImGuiDockNodeFlags_KeepAliveOnly - int LastFrameActive; // Last frame number the node was updated. - int LastFrameFocused; // Last frame number the node was focused. - ImGuiID LastFocusedNodeId; // [Root node only] Which of our child docking node (any ancestor in the hierarchy) was last focused. - ImGuiID SelectedTabId; // [Leaf node only] Which of our tab/window is selected. - ImGuiID WantCloseTabId; // [Leaf node only] Set when closing a specific tab/window. - ImGuiDataAuthority AuthorityForPos :3; - ImGuiDataAuthority AuthorityForSize :3; - ImGuiDataAuthority AuthorityForViewport :3; - bool IsVisible :1; // Set to false when the node is hidden (usually disabled as it has no active window) - bool IsFocused :1; - bool HasCloseButton :1; - bool HasWindowMenuButton :1; - bool EnableCloseButton :1; - bool WantCloseAll :1; // Set when closing all tabs at once. - bool WantLockSizeOnce :1; - bool WantMouseMove :1; // After a node extraction we need to transition toward moving the newly created host window - bool WantHiddenTabBarUpdate :1; - bool WantHiddenTabBarToggle :1; - bool MarkedForPosSizeWrite :1; // Update by DockNodeTreeUpdatePosSize() write-filtering - - ImGuiDockNode(ImGuiID id); - ~ImGuiDockNode(); - bool IsRootNode() const { return ParentNode == NULL; } - bool IsDockSpace() const { return (LocalFlags & ImGuiDockNodeFlags_DockSpace) != 0; } - bool IsFloatingNode() const { return ParentNode == NULL && (LocalFlags & ImGuiDockNodeFlags_DockSpace) == 0; } - bool IsCentralNode() const { return (LocalFlags & ImGuiDockNodeFlags_CentralNode) != 0; } - bool IsHiddenTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_HiddenTabBar) != 0; } // Hidden tab bar can be shown back by clicking the small triangle - bool IsNoTabBar() const { return (LocalFlags & ImGuiDockNodeFlags_NoTabBar) != 0; } // Never show a tab bar - bool IsSplitNode() const { return ChildNodes[0] != NULL; } - bool IsLeafNode() const { return ChildNodes[0] == NULL; } - bool IsEmpty() const { return ChildNodes[0] == NULL && Windows.Size == 0; } - ImGuiDockNodeFlags GetMergedFlags() const { return SharedFlags | LocalFlags; } - ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } -}; - -struct ImGuiDockContext -{ - ImGuiStorage Nodes; // Map ID -> ImGuiDockNode*: Active nodes - ImVector Requests; - ImVector NodesSettings; - bool WantFullRebuild; - ImGuiDockContext() { WantFullRebuild = false; } -}; - +// #endif // #ifdef IMGUI_HAS_DOCK //----------------------------------------------------------------------------- @@ -1190,38 +1060,7 @@ struct ImGuiDockContext //----------------------------------------------------------------------------- #ifdef IMGUI_HAS_VIEWPORT - -// ImGuiViewport Private/Internals fields (cardinal sin: we are using inheritance!) -// Note that every instance of ImGuiViewport is in fact a ImGuiViewportP. -struct ImGuiViewportP : public ImGuiViewport -{ - int Idx; - int LastFrameActive; // Last frame number this viewport was activated by a window - int LastFrameDrawLists[2]; // Last frame number the background (0) and foreground (1) draw lists were used - int LastFrontMostStampCount; // Last stamp number from when a window hosted by this viewport was made front-most (by comparing this value between two viewport we have an implicit viewport z-order - ImGuiID LastNameHash; - ImVec2 LastPos; - float Alpha; // Window opacity (when dragging dockable windows/viewports we make them transparent) - float LastAlpha; - short PlatformMonitor; - bool PlatformWindowCreated; - ImGuiWindow* Window; // Set when the viewport is owned by a window (and ImGuiViewportFlags_CanHostOtherWindows is NOT set) - ImDrawList* DrawLists[2]; // Convenience background (0) and foreground (1) draw lists. We use them to draw software mouser cursor when io.MouseDrawCursor is set and to draw most debug overlays. - ImDrawData DrawDataP; - ImDrawDataBuilder DrawDataBuilder; - ImVec2 LastPlatformPos; - ImVec2 LastPlatformSize; - ImVec2 LastRendererSize; - ImVec2 CurrWorkOffsetMin; // Work area top-left offset being increased during the frame - ImVec2 CurrWorkOffsetMax; // Work area bottom-right offset being decreased during the frame - - ImGuiViewportP() { Idx = -1; LastFrameActive = LastFrameDrawLists[0] = LastFrameDrawLists[1] = LastFrontMostStampCount = -1; LastNameHash = 0; Alpha = LastAlpha = 1.0f; PlatformMonitor = -1; PlatformWindowCreated = false; Window = NULL; DrawLists[0] = DrawLists[1] = NULL; LastPlatformPos = LastPlatformSize = LastRendererSize = ImVec2(FLT_MAX, FLT_MAX); } - ~ImGuiViewportP() { if (DrawLists[0]) IM_DELETE(DrawLists[0]); if (DrawLists[1]) IM_DELETE(DrawLists[1]); } - ImRect GetMainRect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - ImRect GetWorkRect() const { return ImRect(Pos.x + WorkOffsetMin.x, Pos.y + WorkOffsetMin.y, Pos.x + Size.x + WorkOffsetMax.x, Pos.y + Size.y + WorkOffsetMax.y); } - void ClearRequestFlags() { PlatformRequestClose = PlatformRequestMove = PlatformRequestResize = false; } -}; - +// #endif // #ifdef IMGUI_HAS_VIEWPORT //----------------------------------------------------------------------------- @@ -1234,17 +1073,12 @@ struct ImGuiViewportP : public ImGuiViewport struct ImGuiWindowSettings { ImGuiID ID; - ImVec2ih Pos; // NB: Settings position are stored RELATIVE to the viewport! Whereas runtime ones are absolute positions. + ImVec2ih Pos; ImVec2ih Size; - ImVec2ih ViewportPos; - ImGuiID ViewportId; - ImGuiID DockId; // ID of last known DockNode (even if the DockNode is invisible because it has only 1 active window), or 0 if none. - ImGuiID ClassId; // ID of window class if specified - short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. bool Collapsed; bool WantApply; // Set when loaded from .ini data (to enable merging/loading .ini data into an already running context) - ImGuiWindowSettings() { ID = 0; Pos = Size = ViewportPos = ImVec2ih(0, 0); ViewportId = DockId = ClassId = 0; DockOrder = -1; Collapsed = WantApply = false; } + ImGuiWindowSettings() { ID = 0; Pos = Size = ImVec2ih(0, 0); Collapsed = WantApply = false; } char* GetName() { return (char*)(this + 1); } }; @@ -1272,10 +1106,7 @@ struct ImGuiContext bool Initialized; bool FontAtlasOwnedByContext; // IO.Fonts-> is owned by the ImGuiContext and will be destructed along with it. ImGuiIO IO; - ImGuiPlatformIO PlatformIO; ImGuiStyle Style; - ImGuiConfigFlags ConfigFlagsCurrFrame; // = g.IO.ConfigFlags at the time of NewFrame() - ImGuiConfigFlags ConfigFlagsLastFrame; ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. @@ -1283,7 +1114,6 @@ struct ImGuiContext double Time; int FrameCount; int FrameCountEnded; - int FrameCountPlatformEnded; int FrameCountRendered; bool WithinFrameScope; // Set by NewFrame(), cleared by EndFrame() bool WithinFrameScopeWithImplicitWindow; // Set by NewFrame(), cleared by EndFrame() when the implicit debug window has been pushed @@ -1302,7 +1132,6 @@ struct ImGuiContext ImGuiWindow* CurrentWindow; // Window being drawn into ImGuiWindow* HoveredWindow; // Will catch mouse inputs ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) - ImGuiWindow* HoveredWindowUnderMovingWindow; // Hovered window ignoring MovingWindow. Only set if MovingWindow is set. ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; @@ -1347,15 +1176,6 @@ struct ImGuiContext ImVectorOpenPopupStack; // Which popups are open (persistent) ImVectorBeginPopupStack; // Which level of BeginPopup() we are in (reset every frame) - // Viewports - ImVector Viewports; // Active viewports (always 1+, and generally 1 unless multi-viewports are enabled). Each viewports hold their copy of ImDrawData. - float CurrentDpiScale; // == CurrentViewport->DpiScale - ImGuiViewportP* CurrentViewport; // We track changes of viewport (happening in Begin) so we can call Platform_OnChangedViewport() - ImGuiViewportP* MouseViewport; - ImGuiViewportP* MouseLastHoveredViewport; // Last known viewport that was hovered by mouse (even if we are not hovering any viewport any more) + honoring the _NoInputs flag. - ImGuiID PlatformLastFocusedViewport; // Record of last focused platform window/viewport, when this changes we stamp the viewport as front-most - int ViewportFrontMostStampCount; // Every time the front-most window changes, we stamp its viewport with an incrementing counter - // Gamepad/keyboard Navigation ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' ImGuiID NavId; // Focused item for navigation @@ -1414,7 +1234,11 @@ struct ImGuiContext bool FocusTabPressed; // // Render + ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user + ImDrawDataBuilder DrawDataBuilder; float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) + ImDrawList BackgroundDrawList; // First draw list to be rendered. + ImDrawList ForegroundDrawList; // Last draw list to be rendered. This is where we the render software mouse cursor (if io.MouseDrawCursor is set) and most debug overlays. ImGuiMouseCursor MouseCursor; // Drag and Drop @@ -1463,11 +1287,6 @@ struct ImGuiContext // Platform support ImVec2 PlatformImePos; // Cursor position request & last passed to the OS Input Method Editor ImVec2 PlatformImeLastPos; - ImGuiViewportP* PlatformImePosViewport; - - // Extensions - // FIXME: We could provide an API to register one slot in an array held in ImGuiContext? - ImGuiDockContext DockContext; // Settings bool SettingsLoaded; @@ -1500,17 +1319,16 @@ struct ImGuiContext int WantTextInputNextFrame; char TempBuffer[1024 * 3 + 1]; // Temporary text buffer - ImGuiContext(ImFontAtlas* shared_font_atlas) + ImGuiContext(ImFontAtlas* shared_font_atlas) : BackgroundDrawList(&DrawListSharedData), ForegroundDrawList(&DrawListSharedData) { Initialized = false; - ConfigFlagsCurrFrame = ConfigFlagsLastFrame = ImGuiConfigFlags_None; FontAtlasOwnedByContext = shared_font_atlas ? false : true; Font = NULL; FontSize = FontBaseSize = 0.0f; IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); Time = 0.0f; FrameCount = 0; - FrameCountEnded = FrameCountPlatformEnded = FrameCountRendered = -1; + FrameCountEnded = FrameCountRendered = -1; WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; TestEngineHookItems = false; TestEngineHookIdInfo = 0; @@ -1520,7 +1338,6 @@ struct ImGuiContext CurrentWindow = NULL; HoveredWindow = NULL; HoveredRootWindow = NULL; - HoveredWindowUnderMovingWindow = NULL; MovingWindow = NULL; WheelingWindow = NULL; WheelingWindowTimer = 0.0f; @@ -1551,12 +1368,6 @@ struct ImGuiContext LastActiveId = 0; LastActiveIdTimer = 0.0f; - CurrentDpiScale = 0.0f; - CurrentViewport = NULL; - MouseViewport = MouseLastHoveredViewport = NULL; - PlatformLastFocusedViewport = 0; - ViewportFrontMostStampCount = 0; - NavWindow = NULL; NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; NavJustTabbedId = NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; @@ -1593,6 +1404,8 @@ struct ImGuiContext FocusTabPressed = false; DimBgRatio = 0.0f; + BackgroundDrawList._OwnerName = "##Background"; // Give it a name for debugging + ForegroundDrawList._OwnerName = "##Foreground"; // Give it a name for debugging MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; @@ -1621,7 +1434,6 @@ struct ImGuiContext TooltipOverrideCount = 0; PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); - PlatformImePosViewport = 0; SettingsLoaded = false; SettingsDirtyTimer = 0.0f; @@ -1747,12 +1559,7 @@ struct IMGUI_API ImGuiWindow { char* Name; // Window name, owned by the window. ImGuiID ID; // == ImHashStr(Name) - ImGuiWindowFlags Flags, FlagsPreviousFrame; // See enum ImGuiWindowFlags_ - ImGuiWindowClass WindowClass; // Advanced users only. Set with SetNextWindowClass() - ImGuiViewportP* Viewport; // Always set in Begin(), only inactive windows may have a NULL value here - ImGuiID ViewportId; // We backup the viewport id (since the viewport may disappear or never be created if the window is inactive) - ImVec2 ViewportPos; // We backup the viewport position (since the viewport may disappear or never be created if the window is inactive) - int ViewportAllowPlatformMonitorExtend; // Reset to -1 every frame (index is guaranteed to be valid between NewFrame..EndFrame), only used in the Appearing frame of a tooltip/popup to enforce clamping to a given monitor + ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ ImVec2 Pos; // Position (always rounded-up to nearest pixel) ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) ImVec2 SizeFull; // Size when non collapsed @@ -1770,7 +1577,6 @@ struct IMGUI_API ImGuiWindow ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered ImVec2 ScrollbarSizes; // Size taken by each scrollbars on their smaller axis. Pay attention! ScrollbarSizes.x == width of the vertical scrollbar, ScrollbarSizes.y = height of the horizontal scrollbar. bool ScrollbarX, ScrollbarY; // Are scrollbars visible? - bool ViewportOwned; bool Active; // Set to true on Begin(), unless Collapsed bool WasActive; bool WriteAccessed; // Set to true when any widget access the current window @@ -1795,7 +1601,6 @@ struct IMGUI_API ImGuiWindow ImGuiCond SetWindowPosAllowFlags; // store acceptable condition flags for SetNextWindowPos() use. ImGuiCond SetWindowSizeAllowFlags; // store acceptable condition flags for SetNextWindowSize() use. ImGuiCond SetWindowCollapsedAllowFlags; // store acceptable condition flags for SetNextWindowCollapsed() use. - ImGuiCond SetWindowDockAllowFlags; // store acceptable condition flags for SetNextWindowDock() use. ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0, 0) when positioning from top-left corner; ImVec2(0.5f, 0.5f) for centering; ImVec2(1, 1) for bottom right. @@ -1810,23 +1615,21 @@ struct IMGUI_API ImGuiWindow ImRect WorkRect; // Cover the whole scrolling region, shrunk by WindowPadding*1.0f on each side. This is meant to replace ContentRegionRect over time (from 1.71+ onward). ImRect ClipRect; // Current clipping/scissoring rectangle, evolve as we are using PushClipRect(), etc. == DrawList->clip_rect_stack.back(). ImRect ContentRegionRect; // FIXME: This is currently confusing/misleading. It is essentially WorkRect but not handling of scrolling. We currently rely on it as right/bottom aligned sizing operation need some size to rely on. - ImVec2ih HitTestHoleSize, HitTestHoleOffset; + ImVec2ih HitTestHoleSize; // Define an optional rectangular hole where mouse will pass-through the window. + ImVec2ih HitTestHoleOffset; int LastFrameActive; // Last frame number the window was Active. - int LastFrameJustFocused; // Last frame number the window was made Focused. float LastTimeActive; // Last timestamp the window was Active (using float as we don't need high precision there) float ItemWidthDefault; ImGuiStorage StateStorage; ImVector ColumnsStorage; float FontWindowScale; // User scale multiplier per-window, via SetWindowFontScale() - float FontDpiScale; int SettingsOffset; // Offset into SettingsWindows[] (offsets are always valid as we only grow the array from the back) ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) ImDrawList DrawListInst; ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. - ImGuiWindow* RootWindowDockStop; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state. ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. @@ -1838,17 +1641,6 @@ struct IMGUI_API ImGuiWindow int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy int MemoryDrawListVtxCapacity; - // Docking - ImGuiDockNode* DockNode; // Which node are we docked into. Important: Prefer testing DockIsActive in many cases as this will still be set when the dock node is hidden. - ImGuiDockNode* DockNodeAsHost; // Which node are we owning (for parent windows) - ImGuiID DockId; // Backup of last valid DockNode->ID, so single window remember their dock node id even when they are not bound any more - ImGuiItemStatusFlags DockTabItemStatusFlags; - ImRect DockTabItemRect; - short DockOrder; // Order of the last time the window was visible within its DockNode. This is used to reorder windows that are reappearing on the same frame. Same value between windows that were active and windows that were none are possible. - bool DockIsActive :1; // When docking artifacts are actually visible. When this is set, DockNode is guaranteed to be != NULL. ~~ (DockNode != NULL) && (DockNode->Windows.Size > 1). - bool DockTabIsVisible :1; // Is our window visible this frame? ~~ is the corresponding tab selected? - bool DockTabWantClose :1; - public: ImGuiWindow(ImGuiContext* context, const char* name); ~ImGuiWindow(); @@ -1863,7 +1655,7 @@ struct IMGUI_API ImGuiWindow // We don't use g.FontSize because the window may be != g.CurrentWidow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } - float CalcFontSize() const { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale * FontDpiScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; } + float CalcFontSize() const { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; } float TitleBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; } ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } float MenuBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + g.Style.FramePadding.y * 2.0f : 0.0f; } @@ -1898,17 +1690,14 @@ enum ImGuiTabBarFlagsPrivate_ // Extend ImGuiTabItemFlags_ enum ImGuiTabItemFlagsPrivate_ { - ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) - ImGuiTabItemFlags_Unsorted = 1 << 21, // [Docking] Trailing tabs with the _Unsorted flag will be sorted based on the DockOrder of their Window. - ImGuiTabItemFlags_Preview = 1 << 22 // [Docking] Display tab shape for docking preview (height is adjusted slightly to compensate for the yet missing tab bar) + ImGuiTabItemFlags_NoCloseButton = 1 << 20 // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) }; -// Storage for one active tab item (sizeof() 32~40 bytes) +// Storage for one active tab item (sizeof() 26~32 bytes) struct ImGuiTabItem { ImGuiID ID; ImGuiTabItemFlags Flags; - ImGuiWindow* Window; // When TabItem is part of a DockNode's TabBar, we hold on to a window. int LastFrameVisible; int LastFrameSelected; // This allows us to infer an ordered list of the last activated tabs with little maintenance int NameOffset; // When Window==NULL, offset to name within parent ImGuiTabBar::TabsNames @@ -1916,7 +1705,7 @@ struct ImGuiTabItem float Width; // Width currently displayed float ContentWidth; // Width of actual contents, stored during BeginTabItem() call - ImGuiTabItem() { ID = 0; Flags = ImGuiTabItemFlags_None; Window = NULL; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = ContentWidth = 0.0f; } + ImGuiTabItem() { ID = 0; Flags = ImGuiTabItemFlags_None; LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; Offset = Width = ContentWidth = 0.0f; } }; // Storage for a tab bar (sizeof() 92~96 bytes) @@ -1951,8 +1740,6 @@ struct ImGuiTabBar int GetTabOrder(const ImGuiTabItem* tab) const { return Tabs.index_from_ptr(tab); } const char* GetTabName(const ImGuiTabItem* tab) const { - if (tab->Window) - return tab->Window->Name; IM_ASSERT(tab->NameOffset != -1 && tab->NameOffset < TabsNames.Buf.Size); return TabsNames.Buf.Data + tab->NameOffset; } @@ -2002,7 +1789,7 @@ namespace ImGui // Fonts, drawing IMGUI_API void SetCurrentFont(ImFont* font); inline ImFont* GetDefaultFont() { ImGuiContext& g = *GImGui; return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; } - inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { return GetForegroundDrawList(window->Viewport); } + inline ImDrawList* GetForegroundDrawList(ImGuiWindow* window) { IM_UNUSED(window); ImGuiContext& g = *GImGui; return &g.ForegroundDrawList; } // This seemingly unnecessary wrapper simplifies compatibility between the 'master' and 'docking' branches. // Init IMGUI_API void Initialize(ImGuiContext* context); @@ -2011,16 +1798,9 @@ namespace ImGui // NewFrame IMGUI_API void UpdateHoveredWindowAndCaptureFlags(); IMGUI_API void StartMouseMovingWindow(ImGuiWindow* window); - IMGUI_API void StartMouseMovingWindowOrNode(ImGuiWindow* window, ImGuiDockNode* node, bool undock_floating_node); IMGUI_API void UpdateMouseMovingWindowNewFrame(); IMGUI_API void UpdateMouseMovingWindowEndFrame(); - // Viewports - IMGUI_API void TranslateWindowsInViewport(ImGuiViewportP* viewport, const ImVec2& old_pos, const ImVec2& new_pos); - IMGUI_API void ScaleWindowsInViewport(ImGuiViewportP* viewport, float scale); - IMGUI_API void DestroyPlatformWindow(ImGuiViewportP* viewport); - IMGUI_API void ShowViewportThumbnails(); - // Settings IMGUI_API void MarkIniSettingsDirty(); IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); @@ -2116,52 +1896,6 @@ namespace ImGui inline bool IsNavInputTest(ImGuiNavInput n, ImGuiInputReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); } IMGUI_API ImGuiKeyModFlags GetMergedKeyModFlags(); - // Docking - // (some functions are only declared in imgui.cpp, see Docking section) - IMGUI_API void DockContextInitialize(ImGuiContext* ctx); - IMGUI_API void DockContextShutdown(ImGuiContext* ctx); - IMGUI_API void DockContextClearNodes(ImGuiContext* ctx, ImGuiID root_id, bool clear_settings_refs); // Use root_id==0 to clear all - IMGUI_API void DockContextRebuildNodes(ImGuiContext* ctx); - IMGUI_API void DockContextUpdateUndocking(ImGuiContext* ctx); - IMGUI_API void DockContextUpdateDocking(ImGuiContext* ctx); - IMGUI_API ImGuiID DockContextGenNodeID(ImGuiContext* ctx); - IMGUI_API void DockContextQueueDock(ImGuiContext* ctx, ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, float split_ratio, bool split_outer); - IMGUI_API void DockContextQueueUndockWindow(ImGuiContext* ctx, ImGuiWindow* window); - IMGUI_API void DockContextQueueUndockNode(ImGuiContext* ctx, ImGuiDockNode* node); - IMGUI_API bool DockContextCalcDropPosForDocking(ImGuiWindow* target, ImGuiDockNode* target_node, ImGuiWindow* payload, ImGuiDir split_dir, bool split_outer, ImVec2* out_pos); - inline ImGuiDockNode* DockNodeGetRootNode(ImGuiDockNode* node) { while (node->ParentNode) node = node->ParentNode; return node; } - inline int DockNodeGetDepth(const ImGuiDockNode* node) { int depth = 0; while (node->ParentNode) { node = node->ParentNode; depth++; } return depth; } - inline ImGuiDockNode* GetWindowDockNode() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DockNode; } - IMGUI_API bool GetWindowAlwaysWantOwnTabBar(ImGuiWindow* window); - IMGUI_API void BeginDocked(ImGuiWindow* window, bool* p_open); - IMGUI_API void BeginDockableDragDropSource(ImGuiWindow* window); - IMGUI_API void BeginDockableDragDropTarget(ImGuiWindow* window); - IMGUI_API void SetWindowDock(ImGuiWindow* window, ImGuiID dock_id, ImGuiCond cond); - - // Docking - Builder function needs to be generally called before the node is used/submitted. - // - The DockBuilderXXX functions are designed to _eventually_ become a public API, but it is too early to expose it and guarantee stability. - // - Do not hold on ImGuiDockNode* pointers! They may be invalidated by any split/merge/remove operation and every frame. - // - To create a DockSpace() node, make sure to set the ImGuiDockNodeFlags_DockSpace flag when calling DockBuilderAddNode(). - // You can create dockspace nodes (attached to a window) _or_ floating nodes (carry its own window) with this API. - // - DockBuilderSplitNode() create 2 child nodes within 1 node. The initial node becomes a parent node. - // - If you intend to split the node immediately after creation using DockBuilderSplitNode(), make sure - // to call DockBuilderSetNodeSize() beforehand. If you don't, the resulting split sizes may not be reliable. - // - Call DockBuilderFinish() after you are done. - IMGUI_API void DockBuilderDockWindow(const char* window_name, ImGuiID node_id); - IMGUI_API ImGuiDockNode*DockBuilderGetNode(ImGuiID node_id); - inline ImGuiDockNode* DockBuilderGetCentralNode(ImGuiID node_id) { ImGuiDockNode* node = DockBuilderGetNode(node_id); if (!node) return NULL; return DockNodeGetRootNode(node)->CentralNode; } - IMGUI_API ImGuiID DockBuilderAddNode(ImGuiID node_id = 0, ImGuiDockNodeFlags flags = 0); - IMGUI_API void DockBuilderRemoveNode(ImGuiID node_id); // Remove node and all its child, undock all windows - IMGUI_API void DockBuilderRemoveNodeDockedWindows(ImGuiID node_id, bool clear_settings_refs = true); - IMGUI_API void DockBuilderRemoveNodeChildNodes(ImGuiID node_id); // Remove all split/hierarchy. All remaining docked windows will be re-docked to the remaining root node (node_id). - IMGUI_API void DockBuilderSetNodePos(ImGuiID node_id, ImVec2 pos); - IMGUI_API void DockBuilderSetNodeSize(ImGuiID node_id, ImVec2 size); - IMGUI_API ImGuiID DockBuilderSplitNode(ImGuiID node_id, ImGuiDir split_dir, float size_ratio_for_node_at_dir, ImGuiID* out_id_at_dir, ImGuiID* out_id_at_opposite_dir); // Create 2 child nodes in this parent node. - IMGUI_API void DockBuilderCopyDockSpace(ImGuiID src_dockspace_id, ImGuiID dst_dockspace_id, ImVector* in_window_remap_pairs); - IMGUI_API void DockBuilderCopyNode(ImGuiID src_node_id, ImGuiID dst_node_id, ImVector* out_node_remap_pairs); - IMGUI_API void DockBuilderCopyWindowSettings(const char* src_name, const char* dst_name); - IMGUI_API void DockBuilderFinish(ImGuiID node_id); - // Drag and Drop IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API void ClearDragDrop(); @@ -2180,14 +1914,12 @@ namespace ImGui IMGUI_API float GetColumnNormFromOffset(const ImGuiColumns* columns, float offset); // Tab Bars - IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags, ImGuiDockNode* dock_node); + IMGUI_API bool BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& bb, ImGuiTabBarFlags flags); IMGUI_API ImGuiTabItem* TabBarFindTabByID(ImGuiTabBar* tab_bar, ImGuiID tab_id); - IMGUI_API ImGuiTabItem* TabBarFindMostRecentlySelectedTabForActiveWindow(ImGuiTabBar* tab_bar); - IMGUI_API void TabBarAddTab(ImGuiTabBar* tab_bar, ImGuiTabItemFlags tab_flags, ImGuiWindow* window); IMGUI_API void TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id); IMGUI_API void TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab); IMGUI_API void TabBarQueueChangeTabOrder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int dir); - IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); + IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); IMGUI_API bool TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible); @@ -2213,7 +1945,6 @@ namespace ImGui IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor, ImU32 col_fill, ImU32 col_border, ImU32 col_shadow); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); - IMGUI_API void RenderArrowDockMenu(ImDrawList* draw_list, ImVec2 p_min, float sz, ImU32 col); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); IMGUI_API void RenderRectFilledWithHole(ImDrawList* draw_list, ImRect outer, ImRect inner, ImU32 col, float rounding); @@ -2227,7 +1958,7 @@ namespace ImGui IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); - IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos, ImGuiDockNode* dock_node); + IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API void Scrollbar(ImGuiAxis axis); IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* p_scroll_v, float avail_v, float contents_v, ImDrawCornerFlags rounding_corners);