diff --git a/imgui.cpp b/imgui.cpp index 086c51514..6a4f49e15 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4935,6 +4935,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Find or create ImGuiWindow* window = FindWindowByName(name); const bool window_just_created = (window == NULL); + const bool window_is_fallback = (g.CurrentWindowStack.Size == 0); if (window_just_created) { ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here. @@ -4983,10 +4984,15 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) IM_ASSERT(window->DockNode == NULL || window->DockNodeAsHost == NULL); // Cannot be both if (g.NextWindowData.DockCond) SetWindowDock(window, g.NextWindowData.DockId, g.NextWindowData.DockCond); - if (first_begin_of_the_frame && (window->DockId != 0 || window->DockNode != NULL)) + if (first_begin_of_the_frame) { - BeginDocked(window, p_open); - flags = window->Flags; + bool has_dock_node = (window->DockId != 0 || window->DockNode != NULL); + bool new_auto_dock_node = !has_dock_node && g.IO.ConfigDockingTabBarOnSingleWindows && !(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)) && !window_is_fallback; + if (has_dock_node || new_auto_dock_node) + { + BeginDocked(window, p_open); + flags = window->Flags; + } } // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack @@ -10247,7 +10253,7 @@ void ImGui::DockContextProcessDock(ImGuiContext* ctx, ImGuiDockRequest* req) if (target_node) IM_ASSERT(target_node->LastFrameAlive < g.FrameCount); if (target_node && target_window && target_node == target_window->DockNodeAsHost) - IM_ASSERT(target_node->Windows.Size > 1 || target_node->IsSplitNode() || target_node->IsCentralNode); + IM_ASSERT(target_node->Windows.Size > 0 || target_node->IsSplitNode() || target_node->IsCentralNode); // Create new node and add existing window to it if (target_node == NULL) @@ -10758,7 +10764,7 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* 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) - if (node->IsRootNode() && node->IsLeafNode() && node->Windows.Size <= 1 && !node->IsDockSpace) + if (node->Windows.Size <= 1 && node->IsRootNode() && node->IsLeafNode() && !node->IsDockSpace && !g.IO.ConfigDockingTabBarOnSingleWindows) { if (node->Windows.Size == 1) { @@ -12288,15 +12294,28 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) ImGuiContext* ctx = GImGui; ImGuiContext& g = *ctx; - // Calling SetNextWindowPos() undock windows by default (by setting PosUndock) - bool want_undock = false; - want_undock |= (window->Flags & ImGuiWindowFlags_NoDocking) != 0; - want_undock |= (g.NextWindowData.PosCond && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock); - g.NextWindowData.PosUndock = false; - if (want_undock) + const bool auto_dock_node = (g.IO.ConfigDockingTabBarOnSingleWindows) && !(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoDocking)); + + if (auto_dock_node) { - DockContextProcessUndockWindow(ctx, window); - return; + 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.PosCond && (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) && g.NextWindowData.PosUndock); + g.NextWindowData.PosUndock = false; + if (want_undock) + { + DockContextProcessUndockWindow(ctx, window); + return; + } } // Bind to our dock node @@ -12337,7 +12356,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open) // 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 (dock_node->LastFrameAlive < g.FrameCount) + if (dock_node->LastFrameAlive < g.FrameCount && !auto_dock_node) { // If the window has been orphaned, transition the docknode to an implicit node processed in DockContextUpdateDocking() ImGuiDockNode* root_node = DockNodeGetRootNode(dock_node); diff --git a/imgui.h b/imgui.h index a36ccf55d..8e94caecb 100644 --- a/imgui.h +++ b/imgui.h @@ -1278,6 +1278,7 @@ struct ImGuiIO // Miscellaneous configuration options bool MouseDrawCursor; // = false // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). Cannot be easily renamed to 'io.ConfigXXX' because this is frequently used by back-end implementations. bool ConfigDockingWithShift; // = false // Enable docking with holding Shift key (reduce visual noise, allows dropping in wider space) + bool ConfigDockingTabBarOnSingleWindows;//= false // [BETA] Make every single floating window display within a docking node. bool ConfigDockingTransparentPayload; // = false // [BETA] Make window or viewport transparent when docking and only display docking boxes on the target viewport. Useful if rendering of multiple viewport can be synced. Best used with ImGuiConfigFlags_ViewportsNoMerge. bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl (was called io.OptMacOSXBehaviors prior to 1.63) bool ConfigInputTextCursorBlink; // = true // Set to false to disable blinking cursor, for users who consider it distracting. (was called: io.OptCursorBlink prior to 1.63) diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 39bb1ba04..6a6505d19 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -353,6 +353,8 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.ConfigDockingWithShift", &io.ConfigDockingWithShift); ImGui::SameLine(); ShowHelpMarker("Enable docking when holding Shift only (allows to drop in wider space, reduce visual noise)"); + ImGui::Checkbox("io.ConfigDockingTabBarOnSingleWindows", &io.ConfigDockingTabBarOnSingleWindows); + ImGui::SameLine(); ShowHelpMarker("Associate a docking node and tab-bar to sinle floating windows."); ImGui::Checkbox("io.ConfigInputTextCursorBlink", &io.ConfigInputTextCursorBlink); ImGui::SameLine(); ShowHelpMarker("Set to false to disable blinking cursor, for users who consider it distracting"); ImGui::Checkbox("io.ConfigResizeWindowsFromEdges [beta]", &io.ConfigResizeWindowsFromEdges); diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 1096941c1..7a59a9d3e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6452,9 +6452,19 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (!held) SetItemAllowOverlap(); - // Drag and drop - if (held && !tab_appearing && IsMouseDragging()) + // Drag and drop a single floating window node moves it + // FIXME-DOCK: In theory we shouldn't test for the ConfigDockingNodifySingleWindows flag here. + // When our single window node and OnlyNodeWithWindows are working properly we may remove this check here. + ImGuiDockNode* node = docked_window->DockNode; + const bool single_window_node = node->IsRootNode() && node->Windows.Size == 1 && g.IO.ConfigDockingTabBarOnSingleWindows; + if (held && single_window_node && IsMouseDragging(0, 0.0f)) { + // Move + StartMouseMovingWindow(docked_window); + } + else if (held && !tab_appearing && IsMouseDragging(0)) + { + // Drag and drop // Re-order local or dockable tabs float drag_distance_from_edge_x = 0.0f; if (!g.DragDropActive && ((tab_bar->Flags & ImGuiTabBarFlags_Reorderable) || (flags & ImGuiTabItemFlags_DockedWindow))) @@ -6478,9 +6488,8 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, if (flags & ImGuiTabItemFlags_DockedWindow) { // We use a variable threshold to distinguish dragging tabs within a tab bar and extracting them out of the tab bar - //ImVec2 drag_delta = GetMouseDragDelta(); bool undocking_tab = (g.DragDropActive && g.DragDropPayload.SourceId == id); - if (!undocking_tab && held)// && (drag_delta.x != 0.0f || drag_delta.y != 0.0f)) + if (!undocking_tab && held) { //if (!g.IO.ConfigDockingWithShift || g.IO.KeyShift) { @@ -6499,9 +6508,9 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, } } - // Undock - if (undocking_tab && g.ActiveId == id && IsMouseDragging()) + if (undocking_tab) { + // Undock DockContextQueueUndockWindow(&g, docked_window); g.MovingWindow = docked_window; g.ActiveId = g.MovingWindow->MoveId;