diff --git a/imgui.cpp b/imgui.cpp index d15f3b01..f6b0742a 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -152,6 +152,7 @@ Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Also read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337). - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) @@ -5260,7 +5261,15 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } bool pressed = false; - const bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0); + bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0); + + // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. + if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) + { + SetHoveredID(id); + hovered = false; + } + if (hovered) { SetHoveredID(id); @@ -5626,14 +5635,13 @@ bool ImGui::TreeNodeBehaviorIsOpened(ImGuiID id, ImGuiTreeNodeFlags flags) // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoExpandOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) + if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) opened = true; return opened; } -// FIXME: Split into CollapsingHeader(label, default_open?) and TreeNodeBehavior(label), obsolete the 4 parameters function. -bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display_frame, bool default_open) +bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) @@ -5641,20 +5649,16 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display ImGuiState& g = *GImGui; const ImGuiStyle& style = g.Style; + const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); - IM_ASSERT(str_id != NULL || label != NULL); - if (str_id == NULL) - str_id = label; - if (label == NULL) - label = str_id; - const bool label_hide_text_after_double_hash = (label == str_id); // Only search and hide text after ## if we have passed label and ID separately, otherwise allow "##" within format string. - const ImGuiID id = window->GetID(str_id); - const ImVec2 label_size = CalcTextSize(label, NULL, label_hide_text_after_double_hash); + if (!label_end) + label_end = FindRenderedTextEnd(label); + const ImVec2 label_size = CalcTextSize(label, label_end, false); // We vertically grow up to current line height up the typical widget height. const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it - const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), label_size.y + padding.y*2); + const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); if (display_frame) { @@ -5670,12 +5674,15 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing // (Ideally we'd want to add a flag for the user to specify we want 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 opened = TreeNodeBehaviorIsOpened(id, (default_open ? ImGuiTreeNodeFlags_DefaultOpen : 0) | (display_frame ? ImGuiTreeNodeFlags_NoAutoExpandOnLog : 0)); + bool opened = TreeNodeBehaviorIsOpened(id, flags); if (!ItemAdd(interact_bb, &id)) + { + if (opened && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushRawID(id); return opened; + } - bool hovered, held; - bool pressed = ButtonBehavior(interact_bb, id, &hovered, &held, ImGuiButtonFlags_NoKeyModifiers); + bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0)); if (pressed) { opened = !opened; @@ -5696,12 +5703,12 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display const char log_prefix[] = "\n##"; const char log_suffix[] = "##"; LogRenderedText(text_pos, log_prefix, log_prefix+3); - RenderTextClipped(text_pos, bb.Max, label, NULL, &label_size); + RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); LogRenderedText(text_pos, log_suffix+1, log_suffix+3); } else { - RenderTextClipped(text_pos, bb.Max, label, NULL, &label_size); + RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); } } else @@ -5713,44 +5720,58 @@ bool ImGui::CollapsingHeader(const char* label, const char* str_id, bool display RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), opened, 0.70f, false); if (g.LogEnabled) LogRenderedText(text_pos, ">"); - RenderText(text_pos, label, NULL, label_hide_text_after_double_hash); + RenderText(text_pos, label, label_end, false); } + if (opened && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushRawID(id); return opened; } -// If returning 'true' the node is open and the user is responsible for calling TreePop -bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) +bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) { ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - ImGuiState& g = *GImGui; - ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - if (!str_id || !str_id[0]) - str_id = fmt; + return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label); +} - ImGui::PushID(str_id); - const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false); - ImGui::PopID(); +bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; - if (opened) - ImGui::TreePush(str_id); + if (p_open && !*p_open) + return false; + + ImGuiID id = window->GetID(label); + bool opened = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label); + if (p_open) + { + // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. + ImGuiState& g = *GImGui; + SetItemAllowOverlap(); + float button_sz = g.FontSize * 0.5f; + if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(window->DC.LastItemRect.Max.x - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz)) + *p_open = false; + } return opened; } -bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) +bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) { - va_list args; - va_start(args, fmt); - bool s = TreeNodeV(str_id, fmt, args); - va_end(args); - return s; + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiState& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(str_id), 0, g.TempBuffer, label_end); } -// If returning 'true' the node is open and the user is responsible for calling TreePop bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) { ImGuiWindow* window = GetCurrentWindow(); @@ -5758,18 +5779,16 @@ bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) return false; ImGuiState& g = *GImGui; - ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - - if (!ptr_id) - ptr_id = fmt; - - ImGui::PushID(ptr_id); - const bool opened = ImGui::CollapsingHeader(g.TempBuffer, "", false); - ImGui::PopID(); - - if (opened) - ImGui::TreePush(ptr_id); + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(ptr_id ? ptr_id : fmt), 0, g.TempBuffer, label_end); +} +bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool opened = TreeNodeV(str_id, fmt, args); + va_end(args); return opened; } @@ -5777,14 +5796,18 @@ bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) { va_list args; va_start(args, fmt); - bool s = TreeNodeV(ptr_id, fmt, args); + bool opened = TreeNodeV(ptr_id, fmt, args); va_end(args); - return s; + return opened; } -bool ImGui::TreeNode(const char* str_label_id) +bool ImGui::TreeNode(const char* label) { - return TreeNode(str_label_id, "%s", str_label_id); + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), 0, label, NULL); } void ImGui::SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond) @@ -9127,6 +9150,14 @@ void ImGui::TreePush(const void* ptr_id) PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); } +void ImGui::TreePushRawID(ImGuiID id) +{ + ImGuiWindow* window = GetCurrentWindow(); + ImGui::Indent(); + window->DC.TreeDepth++; + window->IDStack.push_back(id); +} + void ImGui::TreePop() { ImGuiWindow* window = GetCurrentWindow(); diff --git a/imgui.h b/imgui.h index a451cea7..6a46035b 100644 --- a/imgui.h +++ b/imgui.h @@ -70,6 +70,7 @@ typedef int ImGuiWindowFlags; // window flags for Begin*() // e typedef int ImGuiSetCond; // condition flags for Set*() // enum ImGuiSetCond_ typedef int ImGuiInputTextFlags; // flags for InputText*() // enum ImGuiInputTextFlags_ typedef int ImGuiSelectableFlags; // flags for Selectable() // enum ImGuiSelectableFlags_ +typedef int ImGuiTreeNodeFlags; // flags for TreeNode*(), Collapsing*() // enum ImGuiTreeNodeFlags_ typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); // Others helpers at bottom of the file: @@ -251,7 +252,6 @@ namespace ImGui IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding - IMGUI_API bool CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false); IMGUI_API bool Checkbox(const char* label, bool* v); IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); IMGUI_API bool RadioButton(const char* label, bool active); @@ -307,15 +307,17 @@ namespace ImGui IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f"); // Widgets: Trees - IMGUI_API bool TreeNode(const char* str_label_id); // if returning 'true' the node is open and the user is responsible for calling TreePop(). + IMGUI_API bool TreeNode(const char* label); // if returning 'true' the node is open and the tree id is pushed into the id stack. user is responsible for calling TreePop(). IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_PRINTFARGS(2); // read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_PRINTFARGS(2); // " IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args); // " IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args); // " - IMGUI_API void TreePush(const char* str_id = NULL); // already called by TreeNode(), but you can call Push/Pop yourself for layouting purpose + IMGUI_API void TreePush(const char* str_id = NULL); // already called by TreeNode(), but you can call Push/Pop yourself for layout purpose IMGUI_API void TreePush(const void* ptr_id = NULL); // " IMGUI_API void TreePop(); IMGUI_API void SetNextTreeNodeOpened(bool opened, ImGuiSetCond cond = 0); // set next tree node/collapsing header to be opened. + IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. user doesn't have to call TreePop(). + IMGUI_API bool CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = 0); // when 'p_open' isn't NULL, display an additional small close button on upper right of the header // Widgets: Selectable / Lists IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height @@ -441,6 +443,7 @@ namespace ImGui // Obsolete (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + static inline bool CollapsingHeader(const char* label, const char* str_id, bool display_frame = true, bool default_open = false) { (void)str_id; (void)display_frame; ImGuiTreeNodeFlags default_open_flags = 1<<5; return CollapsingHeader(label, (default_open ? default_open_flags : 0)); } // OBSOLETE 1.49+ static inline ImFont* GetWindowFont() { return GetFont(); } // OBSOLETE 1.48+ static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETE 1.48+ static inline void OpenNextNode(bool open) { ImGui::SetNextTreeNodeOpened(open, 0); } // OBSOLETE 1.34+ @@ -512,6 +515,18 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() }; +// Flags for ImGui::TreeNode*(), ImGui::CollapsingHeader*() +enum ImGuiTreeNodeFlags_ +{ + ImGuiTreeNodeFlags_Selected = 1 << 0, + ImGuiTreeNodeFlags_Framed = 1 << 1, + ImGuiTreeNodeFlags_AllowOverlapMode = 1 << 2, + ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, + ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, + ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, + ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoAutoOpenOnLog, +}; + // Flags for ImGui::Selectable() enum ImGuiSelectableFlags_ { diff --git a/imgui_demo.cpp b/imgui_demo.cpp index cf6cf21b..ac88f842 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -284,6 +284,23 @@ void ImGui::ShowTestWindow(bool* p_opened) ImGui::TreePop(); } + if (ImGui::TreeNode("Collapsing Headers")) + { + static bool closable_group = true; + if (ImGui::CollapsingHeader("Header")) + { + ImGui::Checkbox("Enable extra group", &closable_group); + for (int i = 0; i < 5; i++) + ImGui::Text("Some content %d", i); + } + if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) + { + for (int i = 0; i < 5; i++) + ImGui::Text("More content %d", i); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Bullets")) { ImGui::BulletText("Bullet point 1"); diff --git a/imgui_internal.h b/imgui_internal.h index 41fb04f9..d099d3b7 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -161,13 +161,8 @@ enum ImGuiButtonFlags_ ImGuiButtonFlags_DontClosePopups = 1 << 5, // disable automatically closing parent popup on press ImGuiButtonFlags_Disabled = 1 << 6, // disable interaction ImGuiButtonFlags_AlignTextBaseLine = 1 << 7, // vertically align button to match text baseline - ButtonEx() only - ImGuiButtonFlags_NoKeyModifiers = 1 << 8 // disable interaction if a key modifier is held -}; - -enum ImGuiTreeNodeFlags_ -{ - ImGuiTreeNodeFlags_DefaultOpen = 1 << 0, - ImGuiTreeNodeFlags_NoAutoExpandOnLog = 1 << 1 + ImGuiButtonFlags_NoKeyModifiers = 1 << 8, // disable interaction if a key modifier is held + ImGuiButtonFlags_AllowOverlapMode = 1 << 9 // require previous frame HoveredId to either match id or be null before being usable }; enum ImGuiSliderFlags_ @@ -730,7 +725,9 @@ namespace ImGui IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags); IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision); + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); IMGUI_API bool TreeNodeBehaviorIsOpened(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging + IMGUI_API void TreePushRawID(ImGuiID id); IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size);