diff --git a/imgui.cpp b/imgui.cpp index a62fe7824..210132a74 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5924,6 +5924,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.TextWrapPosStack.resize(0); window->DC.ColumnsSet = NULL; window->DC.TreeDepth = 0; + window->DC.TreeDepthMayCloseOnPop = 0x00; window->DC.StateStorage = &window->StateStorage; window->DC.GroupStack.resize(0); window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); @@ -7883,6 +7884,13 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y); bool is_open = TreeNodeBehaviorIsOpen(id, flags); + + // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. + // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). + // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. + if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavCloseFromChild) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + window->DC.TreeDepthMayCloseOnPop |= (1 << window->DC.TreeDepth); + if (!ItemAdd(interact_bb, id)) { if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) @@ -12529,9 +12537,21 @@ void ImGui::TreePushRawID(ImGuiID id) void ImGui::TreePop() { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; Unindent(); + window->DC.TreeDepth--; + if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) + if (g.NavIdIsAlive && (window->DC.TreeDepthMayCloseOnPop & (1 << window->DC.TreeDepth))) + { + ImGuiID id = window->IDStack.back(); + window->DC.StateStorage->SetBool(id, false); + SetNavID(id, g.NavLayer); + NavMoveRequestCancel(); + } + window->DC.TreeDepthMayCloseOnPop &= (1 << window->DC.TreeDepth) - 1; + PopID(); } diff --git a/imgui.h b/imgui.h index 5238b8c9f..d441497ca 100644 --- a/imgui.h +++ b/imgui.h @@ -602,6 +602,7 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding(). //ImGuITreeNodeFlags_SpanAllAvailWidth = 1 << 11, // FIXME: TODO: Extend hit box horizontally even if not framed //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 12, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible + ImGuiTreeNodeFlags_NavCloseFromChild = 1 << 13, // (WIP) Nav: left direction may close this TreeNode() when focusing on any child (items submitted between TreeNode and TreePop) ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoAutoOpenOnLog // Obsolete names (will be removed) diff --git a/imgui_internal.h b/imgui_internal.h index 37b326514..9b6bc742f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -817,6 +817,7 @@ struct IMGUI_API ImGuiDrawContext float PrevLineTextBaseOffset; float LogLinePosY; int TreeDepth; + ImU32 TreeDepthMayCloseOnPop; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31 ImGuiID LastItemId; ImRect LastItemRect; bool LastItemRectHoveredRect; @@ -855,6 +856,7 @@ struct IMGUI_API ImGuiDrawContext CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; LogLinePosY = -1.0f; TreeDepth = 0; + TreeDepthMayCloseOnPop = 0x00; LastItemId = 0; LastItemRect = ImRect(); LastItemRectHoveredRect = false;