Refactor: Moved Tree/Selectable functions from imgui.cpp to imgui_widgets.cpp (#2036)
diff --git a/imgui.cpp b/imgui.cpp
index 4c22f4b..98631de 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -7945,341 +7945,6 @@
LogToClipboard(g.LogAutoExpandMaxDepth);
}
-bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
-{
- if (flags & ImGuiTreeNodeFlags_Leaf)
- return true;
-
- // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions)
- ImGuiContext& g = *GImGui;
- ImGuiWindow* window = g.CurrentWindow;
- ImGuiStorage* storage = window->DC.StateStorage;
-
- bool is_open;
- if (g.NextTreeNodeOpenCond != 0)
- {
- if (g.NextTreeNodeOpenCond & ImGuiCond_Always)
- {
- is_open = g.NextTreeNodeOpenVal;
- storage->SetInt(id, is_open);
- }
- else
- {
- // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
- const int stored_value = storage->GetInt(id, -1);
- if (stored_value == -1)
- {
- is_open = g.NextTreeNodeOpenVal;
- storage->SetInt(id, is_open);
- }
- else
- {
- is_open = stored_value != 0;
- }
- }
- g.NextTreeNodeOpenCond = 0;
- }
- else
- {
- is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
- }
-
- // 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_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
- is_open = true;
-
- return is_open;
-}
-
-bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- ImGuiContext& g = *GImGui;
- const ImGuiStyle& style = g.Style;
- const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
- const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
-
- 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(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
- const float frame_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
- ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
- if (display_frame)
- {
- // Framed header expand a little outside the default padding
- frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
- frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
- }
-
- const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing
- const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
- ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
-
- // 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 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 ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_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_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
- window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth);
-
- bool item_add = ItemAdd(interact_bb, id);
- window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
- window->DC.LastItemDisplayRect = frame_bb;
-
- if (!item_add)
- {
- if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
- TreePushRawID(id);
- return is_open;
- }
-
- // Flags that affects opening behavior:
- // - 0(default) ..................... single-click anywhere to open
- // - OpenOnDoubleClick .............. double-click anywhere to open
- // - OpenOnArrow .................... single-click on arrow to open
- // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
- ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0);
- if (!(flags & ImGuiTreeNodeFlags_Leaf))
- button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
- if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
- button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
-
- bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
- if (!(flags & ImGuiTreeNodeFlags_Leaf))
- {
- bool toggled = false;
- if (pressed)
- {
- toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id);
- if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
- toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover);
- if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
- toggled |= g.IO.MouseDoubleClicked[0];
- if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
- toggled = false;
- }
-
- if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
- {
- toggled = true;
- NavMoveRequestCancel();
- }
- if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?
- {
- toggled = true;
- NavMoveRequestCancel();
- }
-
- if (toggled)
- {
- is_open = !is_open;
- window->DC.StateStorage->SetInt(id, is_open);
- }
- }
- if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
- SetItemAllowOverlap();
-
- // Render
- const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
- const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y);
- if (display_frame)
- {
- // Framed type
- RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding);
- RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
- RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
- if (g.LogEnabled)
- {
- // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
- const char log_prefix[] = "\n##";
- const char log_suffix[] = "##";
- LogRenderedText(&text_pos, log_prefix, log_prefix+3);
- RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
- LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
- }
- else
- {
- RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
- }
- }
- else
- {
- // Unframed typed for tree nodes
- if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
- {
- RenderFrame(frame_bb.Min, frame_bb.Max, col, false);
- RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
- }
-
- if (flags & ImGuiTreeNodeFlags_Bullet)
- RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
- else if (!(flags & ImGuiTreeNodeFlags_Leaf))
- RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
- if (g.LogEnabled)
- LogRenderedText(&text_pos, ">");
- RenderText(text_pos, label, label_end, false);
- }
-
- if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
- TreePushRawID(id);
- return is_open;
-}
-
-// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
-// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
-bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label);
-}
-
-bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- if (p_open && !*p_open)
- return false;
-
- ImGuiID id = window->GetID(label);
- bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 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.
- ImGuiContext& g = *GImGui;
- ImGuiItemHoveredDataBackup last_item_backup;
- float button_radius = g.FontSize * 0.5f;
- ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y);
- if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), button_center, button_radius))
- *p_open = false;
- last_item_backup.Restore();
- }
-
- return is_open;
-}
-
-bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
-}
-
-bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- ImGuiContext& g = *GImGui;
- const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
- return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
-}
-
-bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- ImGuiContext& g = *GImGui;
- const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
- return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
-}
-
-bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
-{
- return TreeNodeExV(str_id, 0, fmt, args);
-}
-
-bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
-{
- return TreeNodeExV(ptr_id, 0, fmt, args);
-}
-
-bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- bool is_open = TreeNodeExV(str_id, flags, fmt, args);
- va_end(args);
- return is_open;
-}
-
-bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
- va_end(args);
- return is_open;
-}
-
-bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- bool is_open = TreeNodeExV(str_id, 0, fmt, args);
- va_end(args);
- return is_open;
-}
-
-bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
- bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
- va_end(args);
- return is_open;
-}
-
-bool ImGui::TreeNode(const char* label)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
- return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
-}
-
-void ImGui::TreeAdvanceToLabelPos()
-{
- ImGuiContext& g = *GImGui;
- g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
-}
-
-// Horizontal distance preceding label when using TreeNode() or Bullet()
-float ImGui::GetTreeNodeToLabelSpacing()
-{
- ImGuiContext& g = *GImGui;
- return g.FontSize + (g.Style.FramePadding.x * 2.0f);
-}
-
-void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
-{
- ImGuiContext& g = *GImGui;
- if (g.CurrentWindow->SkipItems)
- return;
- g.NextTreeNodeOpenVal = is_open;
- g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
-}
-
void ImGui::PushID(const char* str_id)
{
ImGuiWindow* window = GetCurrentWindowRead();
@@ -10540,109 +10205,6 @@
return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags);
}
-// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
-// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
-bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
-{
- ImGuiWindow* window = GetCurrentWindow();
- if (window->SkipItems)
- return false;
-
- ImGuiContext& g = *GImGui;
- const ImGuiStyle& style = g.Style;
-
- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped.
- PopClipRect();
-
- ImGuiID id = window->GetID(label);
- ImVec2 label_size = CalcTextSize(label, NULL, true);
- ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
- ImVec2 pos = window->DC.CursorPos;
- pos.y += window->DC.CurrentLineTextBaseOffset;
- ImRect bb_inner(pos, pos + size);
- ItemSize(bb_inner);
-
- // Fill horizontal space.
- ImVec2 window_padding = window->WindowPadding;
- float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
- float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
- ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
- ImRect bb(pos, pos + size_draw);
- if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
- bb.Max.x += window_padding.x;
-
- // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
- float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
- float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
- float spacing_R = style.ItemSpacing.x - spacing_L;
- float spacing_D = style.ItemSpacing.y - spacing_U;
- bb.Min.x -= spacing_L;
- bb.Min.y -= spacing_U;
- bb.Max.x += spacing_R;
- bb.Max.y += spacing_D;
- if (!ItemAdd(bb, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id))
- {
- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
- PushColumnClipRect();
- return false;
- }
-
- // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
- ImGuiButtonFlags button_flags = 0;
- if (flags & ImGuiSelectableFlags_NoHoldingActiveID) button_flags |= ImGuiButtonFlags_NoHoldingActiveID;
- if (flags & ImGuiSelectableFlags_PressedOnClick) button_flags |= ImGuiButtonFlags_PressedOnClick;
- if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease;
- if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
- if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
- bool hovered, held;
- bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
- if (flags & ImGuiSelectableFlags_Disabled)
- selected = false;
-
- // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets)
- if (pressed || hovered)
- if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
- {
- g.NavDisableHighlight = true;
- SetNavID(id, window->DC.NavLayerCurrent);
- }
- if (pressed)
- MarkItemEdited(id);
-
- // Render
- if (hovered || selected)
- {
- const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
- RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
- RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
- }
-
- if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
- {
- PushColumnClipRect();
- bb.Max.x -= (GetContentRegionMax().x - max_x);
- }
-
- if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
- RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
- if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
-
- // Automatically close popups
- if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
- CloseCurrentPopup();
- return pressed;
-}
-
-bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
-{
- if (Selectable(label, *p_selected, flags, size_arg))
- {
- *p_selected = !*p_selected;
- return true;
- }
- return false;
-}
-
// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set.
void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags)
{
@@ -12026,49 +11588,6 @@
window->DC.CursorPos.x = window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x;
}
-void ImGui::TreePush(const char* str_id)
-{
- ImGuiWindow* window = GetCurrentWindow();
- Indent();
- window->DC.TreeDepth++;
- PushID(str_id ? str_id : "#TreePush");
-}
-
-void ImGui::TreePush(const void* ptr_id)
-{
- ImGuiWindow* window = GetCurrentWindow();
- Indent();
- window->DC.TreeDepth++;
- PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
-}
-
-void ImGui::TreePushRawID(ImGuiID id)
-{
- ImGuiWindow* window = GetCurrentWindow();
- Indent();
- window->DC.TreeDepth++;
- window->IDStack.push_back(id);
-}
-
-void ImGui::TreePop()
-{
- 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.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth)))
- {
- SetNavID(window->IDStack.back(), g.NavLayer);
- NavMoveRequestCancel();
- }
- window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1;
-
- IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
- PopID();
-}
-
//-----------------------------------------------------------------------------
// DRAG AND DROP
//-----------------------------------------------------------------------------
diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp
index 60308d6..8514669 100644
--- a/imgui_widgets.cpp
+++ b/imgui_widgets.cpp
@@ -1240,12 +1240,491 @@
// - CollapsingHeader()
//-------------------------------------------------------------------------
+bool ImGui::TreeNode(const char* str_id, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ bool is_open = TreeNodeExV(str_id, 0, fmt, args);
+ va_end(args);
+ return is_open;
+}
+
+bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ bool is_open = TreeNodeExV(ptr_id, 0, fmt, args);
+ va_end(args);
+ return is_open;
+}
+
+bool ImGui::TreeNode(const char* label)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+ return TreeNodeBehavior(window->GetID(label), 0, label, NULL);
+}
+
+bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args)
+{
+ return TreeNodeExV(str_id, 0, fmt, args);
+}
+
+bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args)
+{
+ return TreeNodeExV(ptr_id, 0, fmt, args);
+}
+
+bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ return TreeNodeBehavior(window->GetID(label), flags, label, NULL);
+}
+
+bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ bool is_open = TreeNodeExV(str_id, flags, fmt, args);
+ va_end(args);
+ return is_open;
+}
+
+bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ bool is_open = TreeNodeExV(ptr_id, flags, fmt, args);
+ va_end(args);
+ return is_open;
+}
+
+bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ ImGuiContext& g = *GImGui;
+ const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
+ return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end);
+}
+
+bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ ImGuiContext& g = *GImGui;
+ const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args);
+ return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end);
+}
+
+bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags)
+{
+ if (flags & ImGuiTreeNodeFlags_Leaf)
+ return true;
+
+ // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions)
+ ImGuiContext& g = *GImGui;
+ ImGuiWindow* window = g.CurrentWindow;
+ ImGuiStorage* storage = window->DC.StateStorage;
+
+ bool is_open;
+ if (g.NextTreeNodeOpenCond != 0)
+ {
+ if (g.NextTreeNodeOpenCond & ImGuiCond_Always)
+ {
+ is_open = g.NextTreeNodeOpenVal;
+ storage->SetInt(id, is_open);
+ }
+ else
+ {
+ // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently.
+ const int stored_value = storage->GetInt(id, -1);
+ if (stored_value == -1)
+ {
+ is_open = g.NextTreeNodeOpenVal;
+ storage->SetInt(id, is_open);
+ }
+ else
+ {
+ is_open = stored_value != 0;
+ }
+ }
+ g.NextTreeNodeOpenCond = 0;
+ }
+ else
+ {
+ is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0;
+ }
+
+ // 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_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth)
+ is_open = true;
+
+ return is_open;
+}
+
+bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ ImGuiContext& g = *GImGui;
+ const ImGuiStyle& style = g.Style;
+ const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0;
+ const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f);
+
+ 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(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it
+ const float frame_height = ImMax(ImMin(window->DC.CurrentLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
+ ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height));
+ if (display_frame)
+ {
+ // Framed header expand a little outside the default padding
+ frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1;
+ frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1;
+ }
+
+ const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing
+ const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
+ ItemSize(ImVec2(text_width, frame_height), text_base_offset_y);
+
+ // 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 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 ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_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_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
+ window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth);
+
+ bool item_add = ItemAdd(interact_bb, id);
+ window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
+ window->DC.LastItemDisplayRect = frame_bb;
+
+ if (!item_add)
+ {
+ if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
+ TreePushRawID(id);
+ return is_open;
+ }
+
+ // Flags that affects opening behavior:
+ // - 0(default) ..................... single-click anywhere to open
+ // - OpenOnDoubleClick .............. double-click anywhere to open
+ // - OpenOnArrow .................... single-click on arrow to open
+ // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open
+ ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0);
+ if (!(flags & ImGuiTreeNodeFlags_Leaf))
+ button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
+ if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
+ button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
+
+ bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags);
+ if (!(flags & ImGuiTreeNodeFlags_Leaf))
+ {
+ bool toggled = false;
+ if (pressed)
+ {
+ toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id);
+ if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
+ toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover);
+ if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
+ toggled |= g.IO.MouseDoubleClicked[0];
+ if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again.
+ toggled = false;
+ }
+
+ if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open)
+ {
+ toggled = true;
+ NavMoveRequestCancel();
+ }
+ if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority?
+ {
+ toggled = true;
+ NavMoveRequestCancel();
+ }
+
+ if (toggled)
+ {
+ is_open = !is_open;
+ window->DC.StateStorage->SetInt(id, is_open);
+ }
+ }
+ if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
+ SetItemAllowOverlap();
+
+ // Render
+ const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
+ const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y);
+ if (display_frame)
+ {
+ // Framed type
+ RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding);
+ RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
+ RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
+ if (g.LogEnabled)
+ {
+ // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here.
+ const char log_prefix[] = "\n##";
+ const char log_suffix[] = "##";
+ LogRenderedText(&text_pos, log_prefix, log_prefix+3);
+ RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
+ LogRenderedText(&text_pos, log_suffix+1, log_suffix+3);
+ }
+ else
+ {
+ RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
+ }
+ }
+ else
+ {
+ // Unframed typed for tree nodes
+ if (hovered || (flags & ImGuiTreeNodeFlags_Selected))
+ {
+ RenderFrame(frame_bb.Min, frame_bb.Max, col, false);
+ RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin);
+ }
+
+ if (flags & ImGuiTreeNodeFlags_Bullet)
+ RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y));
+ else if (!(flags & ImGuiTreeNodeFlags_Leaf))
+ RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
+ if (g.LogEnabled)
+ LogRenderedText(&text_pos, ">");
+ RenderText(text_pos, label, label_end, false);
+ }
+
+ if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
+ TreePushRawID(id);
+ return is_open;
+}
+
+void ImGui::TreePush(const char* str_id)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ Indent();
+ window->DC.TreeDepth++;
+ PushID(str_id ? str_id : "#TreePush");
+}
+
+void ImGui::TreePush(const void* ptr_id)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ Indent();
+ window->DC.TreeDepth++;
+ PushID(ptr_id ? ptr_id : (const void*)"#TreePush");
+}
+
+void ImGui::TreePushRawID(ImGuiID id)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ Indent();
+ window->DC.TreeDepth++;
+ window->IDStack.push_back(id);
+}
+
+void ImGui::TreePop()
+{
+ 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.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth)))
+ {
+ SetNavID(window->IDStack.back(), g.NavLayer);
+ NavMoveRequestCancel();
+ }
+ window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1;
+
+ IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much.
+ PopID();
+}
+
+void ImGui::TreeAdvanceToLabelPos()
+{
+ ImGuiContext& g = *GImGui;
+ g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing();
+}
+
+// Horizontal distance preceding label when using TreeNode() or Bullet()
+float ImGui::GetTreeNodeToLabelSpacing()
+{
+ ImGuiContext& g = *GImGui;
+ return g.FontSize + (g.Style.FramePadding.x * 2.0f);
+}
+
+void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond)
+{
+ ImGuiContext& g = *GImGui;
+ if (g.CurrentWindow->SkipItems)
+ return;
+ g.NextTreeNodeOpenVal = is_open;
+ g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always;
+}
+
+// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag).
+// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode().
+bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label);
+}
+
+bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ if (p_open && !*p_open)
+ return false;
+
+ ImGuiID id = window->GetID(label);
+ bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 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.
+ ImGuiContext& g = *GImGui;
+ ImGuiItemHoveredDataBackup last_item_backup;
+ float button_radius = g.FontSize * 0.5f;
+ ImVec2 button_center = ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_radius, window->DC.LastItemRect.GetCenter().y);
+ if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), button_center, button_radius))
+ *p_open = false;
+ last_item_backup.Restore();
+ }
+
+ return is_open;
+}
//-------------------------------------------------------------------------
// WIDGETS: Selectables
// - Selectable()
//-------------------------------------------------------------------------
+// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image.
+// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID or use ##unique_id.
+bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
+{
+ ImGuiWindow* window = GetCurrentWindow();
+ if (window->SkipItems)
+ return false;
+
+ ImGuiContext& g = *GImGui;
+ const ImGuiStyle& style = g.Style;
+
+ if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped.
+ PopClipRect();
+
+ ImGuiID id = window->GetID(label);
+ ImVec2 label_size = CalcTextSize(label, NULL, true);
+ ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y);
+ ImVec2 pos = window->DC.CursorPos;
+ pos.y += window->DC.CurrentLineTextBaseOffset;
+ ImRect bb_inner(pos, pos + size);
+ ItemSize(bb_inner);
+
+ // Fill horizontal space.
+ ImVec2 window_padding = window->WindowPadding;
+ float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x;
+ float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x);
+ ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y);
+ ImRect bb(pos, pos + size_draw);
+ if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth))
+ bb.Max.x += window_padding.x;
+
+ // Selectables are tightly packed together, we extend the box to cover spacing between selectable.
+ float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f);
+ float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f);
+ float spacing_R = style.ItemSpacing.x - spacing_L;
+ float spacing_D = style.ItemSpacing.y - spacing_U;
+ bb.Min.x -= spacing_L;
+ bb.Min.y -= spacing_U;
+ bb.Max.x += spacing_R;
+ bb.Max.y += spacing_D;
+ if (!ItemAdd(bb, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id))
+ {
+ if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
+ PushColumnClipRect();
+ return false;
+ }
+
+ // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries
+ ImGuiButtonFlags button_flags = 0;
+ if (flags & ImGuiSelectableFlags_NoHoldingActiveID) button_flags |= ImGuiButtonFlags_NoHoldingActiveID;
+ if (flags & ImGuiSelectableFlags_PressedOnClick) button_flags |= ImGuiButtonFlags_PressedOnClick;
+ if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease;
+ if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled;
+ if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick;
+ bool hovered, held;
+ bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
+ if (flags & ImGuiSelectableFlags_Disabled)
+ selected = false;
+
+ // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets)
+ if (pressed || hovered)
+ if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent)
+ {
+ g.NavDisableHighlight = true;
+ SetNavID(id, window->DC.NavLayerCurrent);
+ }
+ if (pressed)
+ MarkItemEdited(id);
+
+ // Render
+ if (hovered || selected)
+ {
+ const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
+ RenderFrame(bb.Min, bb.Max, col, false, 0.0f);
+ RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
+ }
+
+ if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet)
+ {
+ PushColumnClipRect();
+ bb.Max.x -= (GetContentRegionMax().x - max_x);
+ }
+
+ if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
+ RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f));
+ if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
+
+ // Automatically close popups
+ if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup))
+ CloseCurrentPopup();
+ return pressed;
+}
+
+bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
+{
+ if (Selectable(label, *p_selected, flags, size_arg))
+ {
+ *p_selected = !*p_selected;
+ return true;
+ }
+ return false;
+}
//-------------------------------------------------------------------------
// WIDGETS: List Box