TreeNode: Added ImGuiTreeNodeFlags_SpanAvailWidth and ImGuiTreeNodeFlags_SpanFullWidth flags (#2451, #2438, #1897)

Added demo bits.
This commit is contained in:
omar 2019-09-18 17:13:41 +02:00
parent 74e01e62ce
commit 9d02ed51e3
4 changed files with 40 additions and 22 deletions

View File

@ -50,6 +50,13 @@ Other Changes:
- SliderScalar: Improved assert when using U32 or U64 types with a large v_max value. (#2765) [@loicmouton] - SliderScalar: Improved assert when using U32 or U64 types with a large v_max value. (#2765) [@loicmouton]
- DragInt, DragFloat, DragScalar: Using (v_min > v_max) allows locking any edit to the value. - DragInt, DragFloat, DragScalar: Using (v_min > v_max) allows locking any edit to the value.
- DragScalar: Fixed dragging of unsigned values on ARM cpu. (#2780) [@dBagrat] - DragScalar: Fixed dragging of unsigned values on ARM cpu. (#2780) [@dBagrat]
- TreeNode: Added ImGuiTreeNodeFlags_SpanAvailWidth flag. (#2451, #2438, #1897) [@Melix19, @PathogenDavid]
This extends the hit-box to the right-most edge, even if the node is not framed.
(Note: this is not the default in order to allow adding other items on the same line. In the future we will
aim toward refactoring the hit-system to be front-to-back, allowing more natural overlapping of items,
and then we will be able to make this the default.)
- TreeNode: Added ImGuiTreeNodeFlags_SpanFullWidth flag. This extends the hit-box to both the left-most and
right-most edge of the working area, bypassing indentation.
- Style: Allow style.WindowMenuButtonPosition to be set to ImGuiDir_None to hide the collapse button. (#2634, #2639) - Style: Allow style.WindowMenuButtonPosition to be set to ImGuiDir_None to hide the collapse button. (#2634, #2639)
- Font: Better ellipsis drawing implementation. Instead of drawing three pixel-ey dots (which was glaringly - Font: Better ellipsis drawing implementation. Instead of drawing three pixel-ey dots (which was glaringly
unfitting with many types of fonts) we first attempt to find a standard ellipsis glyphs within the loaded set. unfitting with many types of fonts) we first attempt to find a standard ellipsis glyphs within the loaded set.

View File

@ -791,9 +791,10 @@ enum ImGuiTreeNodeFlags_
ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes).
ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow
ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding(). ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding().
//ImGuiTreeNodeFlags_SpanAllAvailWidth = 1 << 11, // FIXME: TODO: Extend hit box horizontally even if not framed ImGuiTreeNodeFlags_SpanAvailWidth = 1 << 11, // Extend hit box to the right-most edge, even if not framed. This is not the default in order to allow adding other items on the same line. In the future we may refactor the hit system to be front-to-back, allowing natural overlaps and then this can become the default.
//ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 12, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (bypass the indented area).
ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop)
//ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 14, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible
ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog
// Obsolete names (will be removed) // Obsolete names (will be removed)

View File

@ -608,7 +608,12 @@ static void ShowDemoWindowWidgets()
if (ImGui::TreeNode("Advanced, with Selectable nodes")) if (ImGui::TreeNode("Advanced, with Selectable nodes"))
{ {
HelpMarker("This is a more typical looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); HelpMarker("This is a more typical looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open.");
static ImGuiTreeNodeFlags base_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
static bool align_label_with_current_x_position = false; static bool align_label_with_current_x_position = false;
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnArrow", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_OpenOnArrow);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_OpenOnDoubleClick", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_OpenOnDoubleClick);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanAvailWidth", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_SpanAvailWidth);
ImGui::CheckboxFlags("ImGuiTreeNodeFlags_SpanFullWidth", (unsigned int*)&base_flags, ImGuiTreeNodeFlags_SpanFullWidth);
ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position);
ImGui::Text("Hello!"); ImGui::Text("Hello!");
if (align_label_with_current_x_position) if (align_label_with_current_x_position)
@ -616,12 +621,12 @@ static void ShowDemoWindowWidgets()
static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit. static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit.
int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc. int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc.
ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents.
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
{ {
// Disable the default open on single-click behavior and pass in Selected flag according to our selection state. // Disable the default open on single-click behavior and pass in Selected flag according to our selection state.
ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick; ImGuiTreeNodeFlags node_flags = base_flags;
if (selection_mask & (1 << i)) const bool is_selected = (selection_mask & (1 << i));
if (is_selected)
node_flags |= ImGuiTreeNodeFlags_Selected; node_flags |= ImGuiTreeNodeFlags_Selected;
if (i < 3) if (i < 3)
{ {
@ -631,7 +636,7 @@ static void ShowDemoWindowWidgets()
node_clicked = i; node_clicked = i;
if (node_open) if (node_open)
{ {
ImGui::Text("Blah blah\nBlah Blah"); ImGui::BulletText("Blah blah\nBlah Blah");
ImGui::TreePop(); ImGui::TreePop();
} }
} }
@ -654,7 +659,6 @@ static void ShowDemoWindowWidgets()
else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection
selection_mask = (1 << node_clicked); // Click to single-select selection_mask = (1 << node_clicked); // Click to single-select
} }
ImGui::PopStyleVar();
if (align_label_with_current_x_position) if (align_label_with_current_x_position)
ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
ImGui::TreePop(); ImGui::TreePop();

View File

@ -5177,29 +5177,36 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const ImVec2 label_size = CalcTextSize(label, label_end, false); const ImVec2 label_size = CalcTextSize(label, label_end, false);
// We vertically grow up to current line height up the typical widget height. // We vertically grow up to current line height up the typical widget height.
const float text_base_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it
const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); const float frame_height = ImMax(ImMin(window->DC.CurrLineSize.y, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2);
ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->WorkRect.Max.x, window->DC.CursorPos.y + frame_height)); ImRect frame_bb;
frame_bb.Min.x = (flags & ImGuiTreeNodeFlags_SpanFullWidth) ? window->WorkRect.Min.x : window->DC.CursorPos.x;
frame_bb.Min.y = window->DC.CursorPos.y;
frame_bb.Max.x = window->WorkRect.Max.x;
frame_bb.Max.y = window->DC.CursorPos.y + frame_height;
if (display_frame) if (display_frame)
{ {
// Framed header expand a little outside the default padding // Framed header expand a little outside the default padding, to the edge of InnerClipRect
// (FIXME: May remove this at some point and make InnerClipRect align with WindowPadding.x instead of WindowPadding.x*0.5f)
frame_bb.Min.x -= (float)(int)(window->WindowPadding.x * 0.5f - 1.0f); frame_bb.Min.x -= (float)(int)(window->WindowPadding.x * 0.5f - 1.0f);
frame_bb.Max.x += (float)(int)(window->WindowPadding.x * 0.5f); frame_bb.Max.x += (float)(int)(window->WindowPadding.x * 0.5f);
} }
const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing 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 const float text_offset_y = ImMax(padding.y, window->DC.CurrLineTextBaseOffset); // Latch before ItemSize changes it
ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser
const ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);
ItemSize(ImVec2(text_width, frame_height), text_offset_y);
// For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing // 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) ImRect interact_bb = frame_bb;
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); if (!display_frame && (flags & (ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth)) == 0)
bool is_open = TreeNodeBehaviorIsOpen(id, flags); interact_bb.Max.x = frame_bb.Min.x + text_width + style.ItemSpacing.x * 2.0f;
bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
// Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. // 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(). // 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. // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero.
const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
bool is_open = TreeNodeBehaviorIsOpen(id, flags);
if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
window->DC.TreeStoreMayJumpToParentOnPop |= (1 << window->DC.TreeDepth); window->DC.TreeStoreMayJumpToParentOnPop |= (1 << window->DC.TreeDepth);
@ -5273,7 +5280,6 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
// Render // Render
const ImU32 text_col = GetColorU32(ImGuiCol_Text); const ImU32 text_col = GetColorU32(ImGuiCol_Text);
const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y);
ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin; ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_TypeThin;
if (display_frame) if (display_frame)
{ {
@ -5281,7 +5287,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding); RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);
RenderNavHighlight(frame_bb, id, nav_highlight_flags); RenderNavHighlight(frame_bb, id, nav_highlight_flags);
RenderArrow(window->DrawList, frame_bb.Min + ImVec2(padding.x, text_base_offset_y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f);
if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton)
frame_bb.Max.x -= g.FontSize + style.FramePadding.x; frame_bb.Max.x -= g.FontSize + style.FramePadding.x;
if (g.LogEnabled) if (g.LogEnabled)
@ -5309,9 +5315,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
} }
if (flags & ImGuiTreeNodeFlags_Bullet) if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y), text_col); RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize*0.50f), text_col);
else if (!is_leaf) else if (!is_leaf)
RenderArrow(window->DrawList, frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize*0.15f), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f);
if (g.LogEnabled) if (g.LogEnabled)
LogRenderedText(&text_pos, ">"); LogRenderedText(&text_pos, ">");
RenderText(text_pos, label, label_end, false); RenderText(text_pos, label, label_end, false);