Browse Source

Docking: Added ImGuiDockNodeFlags_PassthruDockspace mode (subdivided in three flags: ImGuiDockNodeFlags_NoDockingInCentralNode, ImGuiDockNodeFlags_PassthruInEmptyNodes, ImGuiDockNodeFlags_RenderWindowBg). Added internal facility for register a rectangular hit-test hole in window. Updated DockSpace demo accordingly. (#2109)

omar 7 năm trước cách đây
mục cha
commit
4e30698706
5 tập tin đã thay đổi với 167 bổ sung86 xóa
  1. 21 19
      docs/CHANGELOG.txt
  2. 112 41
      imgui.cpp
  3. 6 2
      imgui.h
  4. 27 24
      imgui_demo.cpp
  5. 1 0
      imgui_internal.h

+ 21 - 19
docs/CHANGELOG.txt

@@ -33,25 +33,27 @@ HOW TO UPDATE?
  DOCKING BRANCH (In Progress)
 -----------------------------------------------------------------------
 
-- Added ImGuiConfigFlags_DockingEnable flag to enable Docking. [BETA]
-  Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`.
-- Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API. (#261, #351)
-- Added ImGuiTabBarFlags flags for BeginTabBar().
-- Added ImGuiTabItemFlags flags for BeginTabItem().
-- Added DockSpace() API. (#351)
-- Added ImGuiDockNodeFlags flags for DockSpace().
-- Added SetNextWindowDock(), SetNextWindowDockFamily() API. (#351)
-- Added GetWindowDockId(), IsWindowDocked() API. (#351)
-- Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked.
-  Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set. (#351)
-- Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering the ID, 
-  as a convenience to avoid using the ### operator. 
-- Added io.ConfigDockingWithShift option to configure docking mode.
-- Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors. (#351)
-- Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors. (#261, #351)
-- Demo: Added Layout->Tabs demo code. (#261, #351)
-- Demo: Added "Documents" example app showcasing possible use for tabs. (#261, #351)
-- Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes. (#351)
+- Added Docking system: [BETA] (#2109, #351)
+  - Added ImGuiConfigFlags_DockingEnable flag to enable Docking.
+    Set with `io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;`.
+  - Added DockSpace() API.
+  - Added ImGuiDockNodeFlags flags for DockSpace().
+  - Added SetNextWindowDock(), SetNextWindowDockFamily() API.
+  - Added GetWindowDockId(), IsWindowDocked() API.
+  - Added ImGuiWindowFlags_NoDocking window flag to disable the possibility for a window to be docked.
+    Popup, Menu and Child windows always have the ImGuiWindowFlags_NoDocking flag set.
+  - Added io.ConfigDockingWithShift option to configure docking mode.
+  - Style: Added ImGuiCol_DockingPreview, ImGuiCol_DockingEmptyBg colors.
+  - Demo: Added "DockSpace" example app showcasing use of explicit dockspace nodes.
+- Added Tab Bar/Tabs widgets: (#261, #351)
+  - Added BeginTabBar(), EndTabBar(), BeginTabItem(), EndTabItem(), SetTabItemClosed() API.
+  - Added ImGuiTabBarFlags flags for BeginTabBar().
+  - Added ImGuiTabItemFlags flags for BeginTabItem().
+  - Style: Added ImGuiCol_Tab, ImGuiCol_TabHovered, ImGuiCol_TabActive, ImGuiCol_TabUnfocused, ImGuiCol_TabUnfocusedActive colors.
+  - Demo: Added Layout->Tabs demo code.
+  - Demo: Added "Documents" example app showcasing possible use for tabs.
+- Added ImGuiWindowFlags_UnsavedDocument window flag to append '*' to title without altering 
+  the ID, as a convenience to avoid using the ### operator. 
 
 
 -----------------------------------------------------------------------

+ 112 - 41
imgui.cpp

@@ -918,6 +918,7 @@ static void             SetCurrentWindow(ImGuiWindow* window);
 static void             SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond);
 static void             SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond);
 static void             SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond);
+static void             SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size);
 static void             FindHoveredWindow();
 static ImGuiWindow*     CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags);
 static void             CheckStacksSize(ImGuiWindow* window, bool write);
@@ -4002,6 +4003,15 @@ static void FindHoveredWindow()
             bb.Expand(padding_for_resize_from_edges);
         if (!bb.Contains(g.IO.MousePos))
             continue;
+
+        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))
+                continue;
+        }
         
         if (hovered_window == NULL)
             hovered_window = window;
@@ -5501,6 +5511,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags)
             }
         }
 
+        // Clear hit test shape every frame
+        window->HitTestHoleSize.x = window->HitTestHoleSize.y = 0;
+
         // Save clipped aabb so we can access it in constant-time in FindHoveredWindow()
         window->OuterRectClipped = window->Rect();
         if (window->DockIsActive)
@@ -6271,6 +6284,13 @@ static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond co
     window->Collapsed = collapsed;
 }
 
+static void SetWindowHitTestHole(ImGuiWindow* window, const ImVec2& pos, const ImVec2& size)
+{
+    IM_ASSERT(window->HitTestHoleSize.x == 0);     // We don't support multiple holes/hit test filters
+    window->HitTestHoleSize = ImVec2ih((short)size.x, (short)size.y);
+    window->HitTestHoleOffset = ImVec2ih((short)(pos.x - window->Pos.x), (short)(pos.y - window->Pos.y));
+}
+
 void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond)
 {
     SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond);
@@ -10430,20 +10450,35 @@ static void ImGui::DockNodeHideHostWindow(ImGuiDockNode* node)
     }
 }
 
-static void DockNodeUpdateFindOnlyNodeWithWindowsRec(ImGuiDockNode* node, int* p_count, ImGuiDockNode** p_first_node_with_windows)
+struct ImGuiDockNodeUpdateScanResults
+{
+    ImGuiDockNode*  CentralNode;
+    ImGuiDockNode*  FirstNodeWithWindows;
+    int             CountNodesWithWindows;
+    ImGuiDockFamily DockFamilyForMerges;
+
+    ImGuiDockNodeUpdateScanResults() { CentralNode = FirstNodeWithWindows = NULL; CountNodesWithWindows = 0; }
+};
+
+static void DockNodeUpdateScanRec(ImGuiDockNode* node, ImGuiDockNodeUpdateScanResults* results)
 {
     if (node->Windows.Size > 0)
     {
-        if (*p_first_node_with_windows == NULL)
-            *p_first_node_with_windows = node;
-        (*p_count)++;
+        if (results->FirstNodeWithWindows == NULL)
+            results->FirstNodeWithWindows = node;
+        results->CountNodesWithWindows++;
     }
-    if (*p_count > 1)
+    if (node->IsCentralNode)
+    {
+        IM_ASSERT(results->CentralNode == NULL); // Should be only one
+        results->CentralNode = node;
+    }
+    if (results->CountNodesWithWindows > 1 && results->CentralNode != NULL)
         return;
     if (node->ChildNodes[0])
-        DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[0], p_count, p_first_node_with_windows);
+        DockNodeUpdateScanRec(node->ChildNodes[0], results);
     if (node->ChildNodes[1])
-        DockNodeUpdateFindOnlyNodeWithWindowsRec(node->ChildNodes[1], p_count, p_first_node_with_windows);
+        DockNodeUpdateScanRec(node->ChildNodes[1], results);
 }
 
 static void ImGui::DockNodeUpdateVisibleFlagAndInactiveChilds(ImGuiDockNode* node)
@@ -10519,32 +10554,31 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
     IM_ASSERT(node->LastFrameActive != g.FrameCount);
     node->LastFrameAlive = g.FrameCount;
 
+    ImGuiDockNode* central_node = NULL;
     if (node->IsRootNode())
     {
         DockNodeUpdateVisibleFlagAndInactiveChilds(node);
 
         // 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!)
-        if (!node->IsDockSpace)
-        {
-            int count = 0;
-            ImGuiDockNode* first_node_with_windows = NULL;
-            DockNodeUpdateFindOnlyNodeWithWindowsRec(node, &count, &first_node_with_windows);
-            node->OnlyNodeWithWindows = (count == 1 ? first_node_with_windows : NULL);
-            if (node->LastFocusedNodeID == 0 && first_node_with_windows != NULL)
-                node->LastFocusedNodeID = first_node_with_windows->ID;
-
-            // Copy the dock family from of our window so it can be used for proper dock filtering.
-            // When node has mixed windows, prioritize the family with the most constraint (CompatibleWithNeutral = false) as the reference to copy.
-            if (first_node_with_windows)
-            {
-                node->DockFamily = first_node_with_windows->Windows[0]->DockFamily;
-                for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
-                    if (first_node_with_windows->Windows[n]->DockFamily.CompatibleWithFamilyZero == false)
-                    {
-                        node->DockFamily = first_node_with_windows->Windows[n]->DockFamily;
-                        break;
-                    }
-            }
+        ImGuiDockNodeUpdateScanResults results;
+        DockNodeUpdateScanRec(node, &results);
+        node->OnlyNodeWithWindows = (results.CountNodesWithWindows == 1 ? results.FirstNodeWithWindows : NULL);
+        if (node->LastFocusedNodeID == 0 && results.FirstNodeWithWindows != NULL)
+            node->LastFocusedNodeID = results.FirstNodeWithWindows->ID;
+        central_node = results.CentralNode;
+
+        // Copy the dock family from of our window so it can be used for proper dock filtering.
+        // When node has mixed windows, prioritize the family with the most constraint (CompatibleWithNeutral = 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->DockFamily = first_node_with_windows->Windows[0]->DockFamily;
+            for (int n = 1; n < first_node_with_windows->Windows.Size; n++)
+                if (first_node_with_windows->Windows[n]->DockFamily.CompatibleWithFamilyZero == false)
+                {
+                    node->DockFamily = first_node_with_windows->Windows[n]->DockFamily;
+                    break;
+                }
         }
     }
 
@@ -10660,6 +10694,35 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
         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 if requested by ImGuiDockNodeFlags_RenderWindowBg, 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_RenderWindowBg) != 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 our dockspace
+    bool central_node_hole = node->IsRootNode() && host_window && (node->Flags & ImGuiDockNodeFlags_PassthruInEmptyNodes) != 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(-RESIZE_WINDOWS_FROM_EDGES_HALF_THICKNESS, -RESIZE_WINDOWS_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)
     {
@@ -10667,6 +10730,21 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
         DockNodeTreeUpdateSplitter(node);
     }
 
+    // Draw empty node background (currently can only be the Central Node)
+    if (host_window && node->IsEmpty() && node->IsVisible && !(node->Flags & ImGuiDockNodeFlags_PassthruInEmptyNodes))
+        host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg));
+
+    // Draw whole dockspace background if ImGuiDockNodeFlags_RenderWindowBg 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)
     {
@@ -10681,16 +10759,11 @@ static void ImGui::DockNodeUpdate(ImGuiDockNode* node)
         if (node->Windows.Size > 0)
             node->SelectedTabID = node->Windows[0]->ID;
     }
-    if (host_window && node->IsVisible)
-    {
-        // Background for empty nodes
-        if (node->Windows.Size == 0 && !node->IsSplitNode())
-            host_window->DrawList->AddRectFilled(node->Pos, node->Pos + node->Size, GetColorU32(ImGuiCol_DockingEmptyBg));
 
-        // Draw drop target
+    // Draw payload drop target
+    if (host_window && node->IsVisible)
         if (node->IsRootNode() && (g.MovingWindow == NULL || g.MovingWindow->RootWindow != host_window))
             BeginAsDockableDragDropTarget(host_window);
-    }
 
     node->LastFrameActive = g.FrameCount;
 
@@ -11060,7 +11133,7 @@ static bool ImGui::DockNodePreviewDockCalc(ImGuiWindow* host_window, ImGuiDockNo
     data->IsCenterAvailable = !is_outer_docking;
     if (src_is_visibly_splitted && (!host_node || !host_node->IsEmpty()))
         data->IsCenterAvailable = false;
-    if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode) && host_node->IsCentralNode)
+    if (host_node && (host_node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode) && host_node->IsCentralNode)
         data->IsCenterAvailable = false;
 
     data->IsSidesAvailable = true;
@@ -11568,11 +11641,9 @@ void ImGui::DockSpace(ImGuiID id, const ImVec2& size_arg, ImGuiDockNodeFlags doc
 
     if (node->Windows.Size > 0 || node->IsSplitNode())
         PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 0));
-    if (dockspace_flags & ImGuiDockNodeFlags_NoOuterBorder)
-        PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
+    PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
     Begin(title, NULL, window_flags);
-    if (dockspace_flags & ImGuiDockNodeFlags_NoOuterBorder)
-        PopStyleVar();
+    PopStyleVar();
     if (node->Windows.Size > 0 || node->IsSplitNode())
         PopStyleColor();
 
@@ -11953,7 +12024,7 @@ void ImGui::BeginDocked(ImGuiWindow* window, bool* p_open)
     }
 
     // Undock if the ImGuiDockNodeFlags_NoDockingInCentralNode got set
-    if (dock_node->IsCentralNode && (dock_node->Flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode))
+    if (dock_node->IsCentralNode && (dock_node->Flags & ImGuiDockNodeFlags_NoDockingInCentralNode))
     {
         DockContextProcessUndockWindow(ctx, window);
         return;

+ 6 - 2
imgui.h

@@ -798,8 +798,12 @@ enum ImGuiDockNodeFlags_
     ImGuiDockNodeFlags_None                         = 0,
     ImGuiDockNodeFlags_KeepAliveOnly                = 1 << 0,   // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked.
     ImGuiDockNodeFlags_NoSplit                      = 1 << 1,   // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion)
-    ImGuiDockNodeFlags_NoOuterBorder                = 1 << 2,   // Disable outer border on a DockSpace() node.
-    ImGuiDockNodeFlags_NoDockingInsideCentralNode   = 1 << 3    // Disable docking inside the central node (which can stay empty). Useful if it is kept empty and invisible.
+    //ImGuiDockNodeFlags_NoCentralNode              = 1 << 2,   // Disable Central Node (the node which can stay empty)
+    //ImGuiDockNodeFlags_NoOuterBorder              = 1 << 3,   // Disable outer border on a DockSpace() node.
+    ImGuiDockNodeFlags_NoDockingInCentralNode       = 1 << 4,   // Disable docking inside the Central Node, which will be always kept empty.
+    ImGuiDockNodeFlags_PassthruInEmptyNodes         = 1 << 5,   // When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background.
+    ImGuiDockNodeFlags_RenderWindowBg               = 1 << 6,   // DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node (when empty). Meaning the host window should properly use SetNextWindowBgAlpha(0.0f) + ImGuiDockNodeFlags_NoOuterBorder prior to Begin() when using this.
+    ImGuiDockNodeFlags_PassthruDockspace            = ImGuiDockNodeFlags_NoDockingInCentralNode | ImGuiDockNodeFlags_RenderWindowBg | ImGuiDockNodeFlags_PassthruInEmptyNodes
 };
 
 // Flags for ImGui::IsWindowFocused()

+ 27 - 24
imgui_demo.cpp

@@ -3715,13 +3715,12 @@ static void ShowExampleAppCustomRendering(bool* p_open)
 void ShowExampleAppDockSpace(bool* p_open)
 {
     static bool opt_fullscreen_persistant = true;
+    static ImGuiDockNodeFlags opt_flags = ImGuiDockNodeFlags_None;
     bool opt_fullscreen = opt_fullscreen_persistant;
 
-    // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into.
-    // Because 1) it would be confusing to have two docking targets within each others.
-    //     and 2) we want our main DockSpace node to always be visible (never hidden within a tab bar): if the DockSpace node disappear its child windows will be orphaned.
-    ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar;
-    flags |= ImGuiWindowFlags_NoDocking;
+    // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
+    // because it would be confusing to have two docking targets within each others.
+    ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
     if (opt_fullscreen)
     {
         ImGuiViewport* viewport = ImGui::GetMainViewport();
@@ -3729,22 +3728,28 @@ void ShowExampleAppDockSpace(bool* p_open)
         ImGui::SetNextWindowSize(viewport->Size);
         ImGui::SetNextWindowViewport(viewport->ID);
         ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
-        flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
-        flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
+        ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
+        window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
+        window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
     }
 
-    static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
+    // When using ImGuiDockNodeFlags_RenderWindowBg or ImGuiDockNodeFlags_InvisibleDockspace, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background.
+    if (opt_flags & ImGuiDockNodeFlags_RenderWindowBg)
+        ImGui::SetNextWindowBgAlpha(0.0f);
+
     ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
-    ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
-    ImGui::Begin("DockSpace Demo", p_open, flags);
-    ImGui::PopStyleVar(2);
+    ImGui::Begin("DockSpace Demo", p_open, window_flags);
+    ImGui::PopStyleVar();
+
+    if (opt_fullscreen)
+        ImGui::PopStyleVar(2);
 
     // Dockspace
     ImGuiIO& io = ImGui::GetIO();
     if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
     {
         ImGuiID dockspace_id = ImGui::GetID("MyDockspace");
-        ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags | ImGuiDockNodeFlags_NoOuterBorder);
+        ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), opt_flags);
     }
     else
     {
@@ -3753,21 +3758,21 @@ void ShowExampleAppDockSpace(bool* p_open)
 
     if (ImGui::BeginMenuBar())
     {
-        if (ImGui::BeginMenu("Options"))
+        if (ImGui::BeginMenu("Docking"))
         {
-            if (ImGui::MenuItem("Remove DockSpace", NULL, false, p_open != NULL))
-                *p_open = false;
-            ImGui::Separator();
-
             // Disabling fullscreen would allow the window to be moved to the front of other windows, 
             // which we can't undo at the moment without finer window depth/z control.
             //ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant);
 
-            if (ImGui::MenuItem("Flag: NoSplit", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != 0))
-                dockspace_flags ^= ImGuiDockNodeFlags_NoSplit;
-            if (ImGui::MenuItem("Flag: NoDockingInsideCentralNode", "", (dockspace_flags & ImGuiDockNodeFlags_NoDockingInsideCentralNode) != 0))
-                dockspace_flags ^= ImGuiDockNodeFlags_NoDockingInsideCentralNode;
-
+            if (ImGui::MenuItem("Flag: NoSplit",                "", (opt_flags & ImGuiDockNodeFlags_NoSplit) != 0))                opt_flags ^= ImGuiDockNodeFlags_NoSplit;
+            if (ImGui::MenuItem("Flag: NoDockingInCentralNode", "", (opt_flags & ImGuiDockNodeFlags_NoDockingInCentralNode) != 0)) opt_flags ^= ImGuiDockNodeFlags_NoDockingInCentralNode;
+            if (ImGui::MenuItem("Flag: PassthruInEmptyNodes",   "", (opt_flags & ImGuiDockNodeFlags_PassthruInEmptyNodes) != 0))   opt_flags ^= ImGuiDockNodeFlags_PassthruInEmptyNodes;
+            if (ImGui::MenuItem("Flag: RenderWindowBg",         "", (opt_flags & ImGuiDockNodeFlags_RenderWindowBg) != 0))         opt_flags ^= ImGuiDockNodeFlags_RenderWindowBg;
+            if (ImGui::MenuItem("Flag: PassthruDockspace (all 3 above)", "", (opt_flags & ImGuiDockNodeFlags_PassthruDockspace) == ImGuiDockNodeFlags_PassthruDockspace))
+                opt_flags = (opt_flags & ~ImGuiDockNodeFlags_PassthruDockspace) | ((opt_flags & ImGuiDockNodeFlags_PassthruDockspace) == ImGuiDockNodeFlags_PassthruDockspace) ? 0 : ImGuiDockNodeFlags_PassthruDockspace;
+            ImGui::Separator();
+            if (ImGui::MenuItem("Close DockSpace", NULL, false, p_open != NULL))
+                *p_open = false;
             ImGui::EndMenu();
         }
         ShowHelpMarker(
@@ -3782,8 +3787,6 @@ void ShowExampleAppDockSpace(bool* p_open)
     }
 
     ImGui::End();
-    if (opt_fullscreen)
-        ImGui::PopStyleVar();
 }
 
 //-----------------------------------------------------------------------------

+ 1 - 0
imgui_internal.h

@@ -1218,6 +1218,7 @@ struct IMGUI_API ImGuiWindow
     ImRect                  ClipRect;                           // Current clipping rectangle. = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2.
     ImRect                  OuterRectClipped;                   // = WindowRect just after setup in Begin(). == window->Rect() for root window.
     ImRect                  InnerMainRect, InnerClipRect;
+    ImVec2ih                HitTestHoleSize, HitTestHoleOffset;
     ImRect                  ContentsRegionRect;                 // FIXME: This is currently confusing/misleading. Maximum visible content position ~~ Pos + (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis
     int                     LastFrameActive;                    // Last frame number the window was Active.
     float                   ItemWidthDefault;