mirror of
https://github.com/Drezil/imgui.git
synced 2024-12-22 07:36:35 +00:00
Refactor: Moved Tree/Selectable functions from imgui.cpp to imgui_widgets.cpp (#2036)
This commit is contained in:
parent
a265c62636
commit
5dc954f5d7
481
imgui.cpp
481
imgui.cpp
@ -7945,341 +7945,6 @@ void ImGui::LogButtons()
|
||||
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 @@ bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_fla
|
||||
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 @@ void ImGui::Unindent(float indent_w)
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1240,12 +1240,491 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa
|
||||
// - 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
|
||||
|
Loading…
Reference in New Issue
Block a user