| 5940 | } |
| 5941 | |
| 5942 | bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) |
| 5943 | { |
| 5944 | ImGuiWindow* window = GetCurrentWindow(); |
| 5945 | if (window->SkipItems) |
| 5946 | return false; |
| 5947 | |
| 5948 | ImGuiContext& g = *GImGui; |
| 5949 | const ImGuiStyle& style = g.Style; |
| 5950 | const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; |
| 5951 | const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, ImMin(window->DC.CurrLineTextBaseOffset, style.FramePadding.y)); |
| 5952 | |
| 5953 | if (!label_end) |
| 5954 | label_end = FindRenderedTextEnd(label); |
| 5955 | const ImVec2 label_size = CalcTextSize(label, label_end, false); |
| 5956 | |
| 5957 | // We vertically grow up to current line height up the typical widget height. |
| 5958 | const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y * 2), label_size.y + padding.y * 2); |
| 5959 | ImRect frame_bb; |
| 5960 | frame_bb.Min.x = (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x; |
| 5961 | frame_bb.Min.y = window->DC.CursorPos.y; |
| 5962 | frame_bb.Max.x = window->WorkRect.Max.x; |
| 5963 | frame_bb.Max.y = window->DC.CursorPos.y + frame_height; |
| 5964 | if (display_frame) |
| 5965 | { |
| 5966 | // Framed header expand a little outside the default padding, to the edge of InnerClipRect |
| 5967 | // (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x instead of WindowPadding.x*0.5f) |
| 5968 | frame_bb.Min.x -= IM_FLOOR(window->WindowPadding.x * 0.5f - 1.0f); |
| 5969 | frame_bb.Max.x += IM_FLOOR(window->WindowPadding.x * 0.5f); |
| 5970 | } |
| 5971 | |
| 5972 | const float text_offset_x = g.FontSize + (display_frame ? padding.x * 3 : padding.x * 2); // Collapser arrow width + Spacing |
| 5973 | const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it |
| 5974 | const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x * 2 : 0.0f); // Include collapser |
| 5975 | ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y); |
| 5976 | ItemSize(ImVec2(text_width, frame_height), padding.y); |
| 5977 | |
| 5978 | // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing |
| 5979 | ImRect interact_bb = frame_bb; |
| 5980 | if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth)) == 0) |
| 5981 | interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f; |
| 5982 | |
| 5983 | // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. |
| 5984 | // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). |
| 5985 | // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. |
| 5986 | const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; |
| 5987 | bool is_open = TreeNodeUpdateNextOpen(id, flags); |
| 5988 | if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) |
| 5989 | window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); |
| 5990 | |
| 5991 | bool item_add = ItemAdd(interact_bb, id); |
| 5992 | g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; |
| 5993 | g.LastItemData.DisplayRect = frame_bb; |
| 5994 | |
| 5995 | if (!item_add) |
| 5996 | { |
| 5997 | if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) |
| 5998 | TreePushOverrideID(id); |
| 5999 | IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | (is_leaf ? 0 : ImGuiItemStatusFlags_Openable) | (is_open ? ImGuiItemStatusFlags_Opened : 0)); |