|
@@ -5329,30 +5329,39 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
|
|
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 = 0;
|
|
|
+ ImGuiButtonFlags button_flags = ImGuiTreeNodeFlags_None;
|
|
|
if (flags & ImGuiTreeNodeFlags_AllowItemOverlap)
|
|
|
button_flags |= ImGuiButtonFlags_AllowItemOverlap;
|
|
|
- if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
|
|
|
- button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0);
|
|
|
- else
|
|
|
- button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
|
|
|
if (!is_leaf)
|
|
|
button_flags |= ImGuiButtonFlags_PressedOnDragDropHold;
|
|
|
|
|
|
// We allow clicking on the arrow section with keyboard modifiers held, in order to easily
|
|
|
// allow browsing a tree while preserving selection with code implementing multi-selection patterns.
|
|
|
// When clicking on the rest of the tree node we always disallow keyboard modifiers.
|
|
|
- const float hit_padding_x = style.TouchExtraPadding.x;
|
|
|
- const float arrow_hit_x1 = (text_pos.x - text_offset_x) - hit_padding_x;
|
|
|
- const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + hit_padding_x;
|
|
|
- if (window != g.HoveredWindow || !(g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2))
|
|
|
+ const float arrow_hit_x1 = (text_pos.x - text_offset_x) - style.TouchExtraPadding.x;
|
|
|
+ const float arrow_hit_x2 = (text_pos.x - text_offset_x) + (g.FontSize + padding.x * 2.0f) + style.TouchExtraPadding.x;
|
|
|
+ const bool is_mouse_x_over_arrow = (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2);
|
|
|
+ if (window != g.HoveredWindow || !is_mouse_x_over_arrow)
|
|
|
button_flags |= ImGuiButtonFlags_NoKeyModifiers;
|
|
|
|
|
|
+ // Open behaviors can be altered with the _OpenOnArrow and _OnOnDoubleClick flags.
|
|
|
+ // Some alteration have subtle effects (e.g. toggle on MouseUp vs MouseDown events) due to requirements for multi-selection and drag and drop support.
|
|
|
+ // - Single-click on label = Toggle on MouseUp (default)
|
|
|
+ // - Single-click on arrow = Toggle on MouseUp (when _OpenOnArrow=0)
|
|
|
+ // - Single-click on arrow = Toggle on MouseDown (when _OpenOnArrow=1)
|
|
|
+ // - Double-click on label = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1)
|
|
|
+ // - Double-click on arrow = Toggle on MouseDoubleClick (when _OpenOnDoubleClick=1 and _OpenOnArrow=0)
|
|
|
+ // This makes _OpenOnArrow have a subtle effect on _OpenOnDoubleClick: arrow click reacts on Down rather than Up.
|
|
|
+ // It is rather standard that arrow click react on Down rather than Up and we'd be tempted to make it the default
|
|
|
+ // (by removing the _OpenOnArrow test below), however this would have a perhaps surprising effect on CollapsingHeader()?
|
|
|
+ // So right now we are making this optional. May evolve later.
|
|
|
+ if (is_mouse_x_over_arrow && (flags & ImGuiTreeNodeFlags_OpenOnArrow))
|
|
|
+ button_flags |= ImGuiButtonFlags_PressedOnClick;
|
|
|
+ else if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick)
|
|
|
+ button_flags |= ImGuiButtonFlags_PressedOnDoubleClick;
|
|
|
+ else
|
|
|
+ button_flags |= ImGuiButtonFlags_PressedOnClickRelease;
|
|
|
+
|
|
|
bool selected = (flags & ImGuiTreeNodeFlags_Selected) != 0;
|
|
|
const bool was_selected = selected;
|
|
|
|
|
@@ -5366,7 +5375,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l
|
|
|
if ((flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) == 0 || (g.NavActivateId == id))
|
|
|
toggled = true;
|
|
|
if (flags & ImGuiTreeNodeFlags_OpenOnArrow)
|
|
|
- toggled |= (g.IO.MousePos.x >= arrow_hit_x1 && g.IO.MousePos.x < arrow_hit_x2) && (!g.NavDisableMouseHover); // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job
|
|
|
+ toggled |= is_mouse_x_over_arrow && !g.NavDisableMouseHover; // Lightweight equivalent of IsMouseHoveringRect() since ButtonBehavior() already did the job
|
|
|
if ((flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) && g.IO.MouseDoubleClicked[0])
|
|
|
toggled = true;
|
|
|
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.
|
|
@@ -5568,6 +5577,8 @@ bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags
|
|
|
|
|
|
// Tip: pass a non-visible 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.
|
|
|
+// With this scheme, ImGuiSelectableFlags_SpanAllColumns and ImGuiSelectableFlags_AllowItemOverlap are also frequently used flags.
|
|
|
+// FIXME: Selectable() with (size.x == 0.0f) and (SelectableTextAlign.x > 0.0f) followed by SameLine() is currently not supported.
|
|
|
bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg)
|
|
|
{
|
|
|
ImGuiWindow* window = GetCurrentWindow();
|
|
@@ -5580,44 +5591,48 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
|
|
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns) // FIXME-OPT: Avoid if vertically clipped.
|
|
|
PushColumnsBackground();
|
|
|
|
|
|
+ // Submit label or explicit size to ItemSize(), whereas ItemAdd() will submit a larger/spanning rectangle.
|
|
|
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.CurrLineTextBaseOffset;
|
|
|
- ImRect bb_inner(pos, pos + size);
|
|
|
ItemSize(size, 0.0f);
|
|
|
|
|
|
- // 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 - pos.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;
|
|
|
+ // Fill horizontal space
|
|
|
+ const float min_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ContentRegionRect.Min.x : pos.x;
|
|
|
+ const float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? window->ContentRegionRect.Max.x : GetContentRegionMaxAbs().x;
|
|
|
+ if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_SpanAvailWidth))
|
|
|
+ size.x = ImMax(label_size.x, max_x - min_x);
|
|
|
+
|
|
|
+ // Text stays at the submission position, but bounding box may be extended on both sides
|
|
|
+ const ImVec2 text_min = pos;
|
|
|
+ const ImVec2 text_max(min_x + size.x, pos.y + size.y);
|
|
|
|
|
|
- // Selectables are tightly packed together so we extend the box to cover spacing between selectable.
|
|
|
+ // Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
|
|
|
+ ImRect bb_enlarged(min_x, pos.y, text_max.x, text_max.y);
|
|
|
const float spacing_x = style.ItemSpacing.x;
|
|
|
const float spacing_y = style.ItemSpacing.y;
|
|
|
const float spacing_L = IM_FLOOR(spacing_x * 0.50f);
|
|
|
const float spacing_U = IM_FLOOR(spacing_y * 0.50f);
|
|
|
- bb.Min.x -= spacing_L;
|
|
|
- bb.Min.y -= spacing_U;
|
|
|
- bb.Max.x += (spacing_x - spacing_L);
|
|
|
- bb.Max.y += (spacing_y - spacing_U);
|
|
|
+ bb_enlarged.Min.x -= spacing_L;
|
|
|
+ bb_enlarged.Min.y -= spacing_U;
|
|
|
+ bb_enlarged.Max.x += (spacing_x - spacing_L);
|
|
|
+ bb_enlarged.Max.y += (spacing_y - spacing_U);
|
|
|
+ //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb_align.Min, bb_align.Max, IM_COL32(255, 0, 0, 255)); }
|
|
|
+ //if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb_enlarged.Min, bb_enlarged.Max, IM_COL32(0, 255, 0, 255)); }
|
|
|
|
|
|
bool item_add;
|
|
|
if (flags & ImGuiSelectableFlags_Disabled)
|
|
|
{
|
|
|
ImGuiItemFlags backup_item_flags = window->DC.ItemFlags;
|
|
|
window->DC.ItemFlags |= ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNavDefaultFocus;
|
|
|
- item_add = ItemAdd(bb, id);
|
|
|
+ item_add = ItemAdd(bb_enlarged, id);
|
|
|
window->DC.ItemFlags = backup_item_flags;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- item_add = ItemAdd(bb, id);
|
|
|
+ item_add = ItemAdd(bb_enlarged, id);
|
|
|
}
|
|
|
if (!item_add)
|
|
|
{
|
|
@@ -5640,7 +5655,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
|
|
|
|
|
const bool was_selected = selected;
|
|
|
bool hovered, held;
|
|
|
- bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags);
|
|
|
+ bool pressed = ButtonBehavior(bb_enlarged, id, &hovered, &held, button_flags);
|
|
|
|
|
|
// Update NavId when clicking or when Hovering (this doesn't happen on most widgets), so navigation can be resumed with gamepad/keyboard
|
|
|
if (pressed || (hovered && (flags & ImGuiSelectableFlags_SetNavIdOnHover)))
|
|
@@ -5667,18 +5682,15 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
|
|
|
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);
|
|
|
+ RenderFrame(bb_enlarged.Min, bb_enlarged.Max, col, false, 0.0f);
|
|
|
+ RenderNavHighlight(bb_enlarged, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding);
|
|
|
}
|
|
|
|
|
|
if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.CurrentColumns)
|
|
|
- {
|
|
|
PopColumnsBackground();
|
|
|
- bb.Max.x -= (GetContentRegionMax().x - max_x);
|
|
|
- }
|
|
|
|
|
|
if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]);
|
|
|
- RenderTextClipped(bb_inner.Min, bb_inner.Max, label, NULL, &label_size, style.SelectableTextAlign, &bb);
|
|
|
+ RenderTextClipped(text_min, text_max, label, NULL, &label_size, style.SelectableTextAlign, &bb_enlarged);
|
|
|
if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor();
|
|
|
|
|
|
// Automatically close popups
|
|
@@ -6259,10 +6271,12 @@ bool ImGui::BeginMenu(const char* label, bool enabled)
|
|
|
else
|
|
|
{
|
|
|
// Menu inside a menu
|
|
|
+ // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
|
|
|
+ // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
|
|
|
popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y);
|
|
|
- float w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame
|
|
|
- float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
|
|
|
- pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f));
|
|
|
+ float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, 0.0f, IM_FLOOR(g.FontSize * 1.20f)); // Feedback to next frame
|
|
|
+ float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
|
|
|
+ pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_SpanAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(min_w, 0.0f));
|
|
|
ImU32 text_col = GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled);
|
|
|
RenderArrow(window->DrawList, pos + ImVec2(window->DC.MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), text_col, ImGuiDir_Right);
|
|
|
}
|
|
@@ -6407,11 +6421,14 @@ bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, boo
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f);
|
|
|
- float w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_size.x, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame
|
|
|
- float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w);
|
|
|
- pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f));
|
|
|
- if (shortcut_size.x > 0.0f)
|
|
|
+ // Menu item inside a vertical menu
|
|
|
+ // (In a typical menu window where all items are BeginMenu() or MenuItem() calls, extra_w will always be 0.0f.
|
|
|
+ // Only when they are other items sticking out we're going to add spacing, yet only register minimum width into the layout system.
|
|
|
+ float shortcut_w = shortcut ? CalcTextSize(shortcut, NULL).x : 0.0f;
|
|
|
+ float min_w = window->DC.MenuColumns.DeclColumns(label_size.x, shortcut_w, IM_FLOOR(g.FontSize * 1.20f)); // Feedback for next frame
|
|
|
+ float extra_w = ImMax(0.0f, GetContentRegionAvail().x - min_w);
|
|
|
+ pressed = Selectable(label, false, flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f));
|
|
|
+ if (shortcut_w > 0.0f)
|
|
|
{
|
|
|
PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]);
|
|
|
RenderText(pos + ImVec2(window->DC.MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false);
|