1
0
Fork 0
mirror of https://github.com/ocornut/imgui.git synced 2026-01-16 00:54:19 +00:00

Merge branch 'master' into docking

# Conflicts:
#	imgui.cpp
#	imgui.h
This commit is contained in:
ocornut 2024-07-25 16:41:56 +02:00
commit 3552df562e
7 changed files with 178 additions and 184 deletions

View file

@ -6403,10 +6403,9 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags
frame_bb.Max.y = window->DC.CursorPos.y + frame_height;
if (display_frame)
{
// 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 -= IM_TRUNC(window->WindowPadding.x * 0.5f - 1.0f);
frame_bb.Max.x += IM_TRUNC(window->WindowPadding.x * 0.5f);
const float outer_extend = IM_TRUNC(window->WindowPadding.x * 0.5f); // Framed header expand a little outside of current limits
frame_bb.Min.x -= outer_extend;
frame_bb.Max.x += outer_extend;
}
ImVec2 text_pos(window->DC.CursorPos.x + text_offset_x, window->DC.CursorPos.y + text_offset_y);
@ -6417,26 +6416,27 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags
if ((flags & (ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_SpanTextWidth | ImGuiTreeNodeFlags_SpanAllColumns)) == 0)
interact_bb.Max.x = frame_bb.Min.x + text_width + (label_size.x > 0.0f ? style.ItemSpacing.x * 2.0f : 0.0f);
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
const float backup_clip_rect_min_x = window->ClipRect.Min.x;
const float backup_clip_rect_max_x = window->ClipRect.Max.x;
if (span_all_columns)
{
window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
}
// Compute open and multi-select states before ItemAdd() as it clear NextItem data.
bool is_open = TreeNodeUpdateNextOpen(storage_id, flags);
bool item_add = ItemAdd(interact_bb, id);
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
g.LastItemData.DisplayRect = frame_bb;
bool is_visible;
if (span_all_columns)
{
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
const float backup_clip_rect_min_x = window->ClipRect.Min.x;
const float backup_clip_rect_max_x = window->ClipRect.Max.x;
window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
is_visible = ItemAdd(interact_bb, id);
window->ClipRect.Min.x = backup_clip_rect_min_x;
window->ClipRect.Max.x = backup_clip_rect_max_x;
}
else
{
is_visible = ItemAdd(interact_bb, id);
}
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HasDisplayRect;
g.LastItemData.DisplayRect = frame_bb;
// If a NavLeft request is happening and ImGuiTreeNodeFlags_NavLeftJumpsBackHere enabled:
// Store data for the current depth to allow returning to this node from any child item.
@ -6451,7 +6451,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags
}
const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0;
if (!item_add)
if (!is_visible)
{
if (store_tree_node_stack_data && is_open)
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID()
@ -6573,53 +6573,55 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiID storage_id, ImGuiTreeNodeFlags
g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection;
// Render
const ImU32 text_col = GetColorU32(ImGuiCol_Text);
ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact;
if (is_multi_select)
nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle
if (display_frame)
{
// Framed type
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);
RenderNavHighlight(frame_bb, id, nav_highlight_flags);
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f);
else // Leaf without bullet, left-adjusted text
text_pos.x -= text_offset_x -padding.x;
if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton)
frame_bb.Max.x -= g.FontSize + style.FramePadding.x;
if (g.LogEnabled)
LogSetNextTextDecoration("###", "###");
}
else
{
// Unframed typed for tree nodes
if (hovered || selected)
const ImU32 text_col = GetColorU32(ImGuiCol_Text);
ImGuiNavHighlightFlags nav_highlight_flags = ImGuiNavHighlightFlags_Compact;
if (is_multi_select)
nav_highlight_flags |= ImGuiNavHighlightFlags_AlwaysDraw; // Always show the nav rectangle
if (display_frame)
{
// Framed type
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, true, style.FrameRounding);
RenderNavHighlight(frame_bb, id, nav_highlight_flags);
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f);
else // Leaf without bullet, left-adjusted text
text_pos.x -= text_offset_x - padding.x;
if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton)
frame_bb.Max.x -= g.FontSize + style.FramePadding.x;
if (g.LogEnabled)
LogSetNextTextDecoration("###", "###");
}
RenderNavHighlight(frame_bb, id, nav_highlight_flags);
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f);
if (g.LogEnabled)
LogSetNextTextDecoration(">", NULL);
else
{
// Unframed typed for tree nodes
if (hovered || selected)
{
const ImU32 bg_col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header);
RenderFrame(frame_bb.Min, frame_bb.Max, bg_col, false);
}
RenderNavHighlight(frame_bb, id, nav_highlight_flags);
if (flags & ImGuiTreeNodeFlags_Bullet)
RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.5f, text_pos.y + g.FontSize * 0.5f), text_col);
else if (!is_leaf)
RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y + g.FontSize * 0.15f), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 0.70f);
if (g.LogEnabled)
LogSetNextTextDecoration(">", NULL);
}
if (span_all_columns)
TablePopBackgroundChannel();
// Label
if (display_frame)
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
else
RenderText(text_pos, label, label_end, false);
}
if (span_all_columns)
TablePopBackgroundChannel();
// Label
if (display_frame)
RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size);
else
RenderText(text_pos, label, label_end, false);
if (store_tree_node_stack_data && is_open)
TreeNodeStoreStackData(flags); // Call before TreePushOverrideID()
if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen))
@ -6787,7 +6789,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
const ImVec2 text_max(min_x + size.x, pos.y + size.y);
// Selectables are meant to be tightly packed together with no click-gap, so we extend their box to cover spacing between selectable.
// FIXME: Not part of layout so not included in clipper calculation, but ItemSize currenty doesn't allow offsetting CursorPos.
// FIXME: Not part of layout so not included in clipper calculation, but ItemSize currently doesn't allow offsetting CursorPos.
ImRect bb(min_x, pos.y, text_max.x, text_max.y);
if ((flags & ImGuiSelectableFlags_NoPadWithHalfSpacing) == 0)
{
@ -6802,23 +6804,24 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl
}
//if (g.IO.KeyCtrl) { GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(0, 255, 0, 255)); }
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
const float backup_clip_rect_min_x = window->ClipRect.Min.x;
const float backup_clip_rect_max_x = window->ClipRect.Max.x;
const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
const ImGuiItemFlags extra_item_flags = disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None;
bool is_visible;
if (span_all_columns)
{
// Modify ClipRect for the ItemAdd(), faster than doing a PushColumnsBackground/PushTableBackgroundChannel for every Selectable..
const float backup_clip_rect_min_x = window->ClipRect.Min.x;
const float backup_clip_rect_max_x = window->ClipRect.Max.x;
window->ClipRect.Min.x = window->ParentWorkRect.Min.x;
window->ClipRect.Max.x = window->ParentWorkRect.Max.x;
}
const bool disabled_item = (flags & ImGuiSelectableFlags_Disabled) != 0;
const bool is_visible = ItemAdd(bb, id, NULL, disabled_item ? (ImGuiItemFlags)ImGuiItemFlags_Disabled : ImGuiItemFlags_None);
if (span_all_columns)
{
is_visible = ItemAdd(bb, id, NULL, extra_item_flags);
window->ClipRect.Min.x = backup_clip_rect_min_x;
window->ClipRect.Max.x = backup_clip_rect_max_x;
}
else
{
is_visible = ItemAdd(bb, id, NULL, extra_item_flags);
}
const bool is_multi_select = (g.LastItemData.InFlags & ImGuiItemFlags_IsMultiSelect) != 0;
if (!is_visible)
@ -7073,7 +7076,7 @@ static int ImStrimatchlen(const char* s1, const char* s1_end, const char* s2)
// When SingleCharMode is set:
// - it is better to NOT display a tooltip of other on-screen display indicator.
// - the index of the currently focused item is required.
// if your SetNextItemSelectionData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData.
// if your SetNextItemSelectionUserData() values are indices, you can obtain it from ImGuiMultiSelectIO::NavIdItem, otherwise from g.NavLastValidSelectionUserData.
int ImGui::TypingSelectFindMatch(ImGuiTypingSelectRequest* req, int items_count, const char* (*get_item_name_func)(void*, int), void* user_data, int nav_item_idx)
{
if (req == NULL || req->SelectRequest == false) // Support NULL parameter so both calls can be done from same spot.
@ -7165,6 +7168,7 @@ static void BoxSelectPreStartDrag(ImGuiID id, ImGuiSelectionUserData clicked_ite
bs->ID = id;
bs->IsStarting = true; // Consider starting box-select.
bs->IsStartedFromVoid = (clicked_item == ImGuiSelectionUserData_Invalid);
bs->IsStartedSetNavIdOnce = bs->IsStartedFromVoid;
bs->KeyMods = g.IO.KeyMods;
bs->StartPosRel = bs->EndPosRel = ImGui::WindowPosAbsToRel(g.CurrentWindow, g.IO.MousePos);
bs->ScrollAccum = ImVec2(0.0f, 0.0f);
@ -7401,7 +7405,6 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel
if (flags & (ImGuiMultiSelectFlags_BoxSelect1d | ImGuiMultiSelectFlags_BoxSelect2d))
{
ms->BoxSelectId = GetID("##BoxSelect");
ms->BoxSelectLastitem = ImGuiSelectionUserData_Invalid;
if (BeginBoxSelect(window, ms->BoxSelectId, flags))
request_clear |= bs->RequestClear;
}
@ -7430,12 +7433,12 @@ ImGuiMultiSelectIO* ImGui::BeginMultiSelect(ImGuiMultiSelectFlags flags, int sel
if (request_clear || request_select_all)
{
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, request_select_all, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid };
MultiSelectAddSetAll(ms, request_select_all);
if (!request_select_all)
storage->LastSelectionSize = 0;
ms->IO.Requests.push_back(req);
}
ms->LoopRequestSetAll = request_select_all ? 1 : request_clear ? 0 : -1;
ms->LastSubmittedItem = ImGuiSelectionUserData_Invalid;
if (g.DebugLogFlags & ImGuiDebugLogFlags_EventSelection)
DebugLogMultiSelectRequests("BeginMultiSelect", &ms->IO);
@ -7500,11 +7503,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect()
if (ms->Flags & ImGuiMultiSelectFlags_ClearOnClickVoid)
if (IsMouseReleased(0) && IsMouseDragPastThreshold(0) == false && g.IO.KeyMods == ImGuiMod_None)
{
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid };
ms->IO.Requests.resize(0);
ms->IO.Requests.push_back(req);
}
MultiSelectAddSetAll(ms, false);
}
// Courtesy nav wrapping helper flag
@ -7691,24 +7690,18 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
const bool rect_overlap_prev = bs->BoxSelectRectPrev.Overlaps(g.LastItemData.Rect);
if ((rect_overlap_curr && !rect_overlap_prev && !selected) || (rect_overlap_prev && !rect_overlap_curr))
{
if (storage->LastSelectionSize <= 0 && bs->IsStartedFromVoid)
if (storage->LastSelectionSize <= 0 && bs->IsStartedSetNavIdOnce)
{
pressed = true; // First item act as a pressed: code below will emit selection request and set NavId (whatever we emit here will be overridden anyway)
bs->IsStartedSetNavIdOnce = false;
}
else
{
selected = !selected;
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, +1, item_data, item_data };
// Merge continuous ranges (unless NoRangeSelect is set)
ImGuiSelectionRequest* prev = ms->IO.Requests.Size > 0 ? &ms->IO.Requests.Data[ms->IO.Requests.Size - 1] : NULL;
if (prev && prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->BoxSelectLastitem && prev->Selected == selected && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0)
prev->RangeLastItem = item_data; // Merge span into same request
else
ms->IO.Requests.push_back(req);
MultiSelectAddSetRange(ms, selected, +1, item_data, item_data);
}
storage->LastSelectionSize = ImMax(storage->LastSelectionSize + 1, 1);
}
ms->BoxSelectLastitem = item_data;
}
// Right-click handling.
@ -7766,11 +7759,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
else if ((input_source == ImGuiInputSource_Keyboard || input_source == ImGuiInputSource_Gamepad) && is_shift && !is_ctrl)
request_clear = true; // With is_shift==false the RequestClear was done in BeginIO, not necessary to do again.
if (request_clear)
{
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, false, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid };
ms->IO.Requests.resize(0);
ms->IO.Requests.push_back(req);
}
MultiSelectAddSetAll(ms, false);
}
int range_direction;
@ -7808,9 +7797,7 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
range_selected = selected;
range_direction = +1;
}
ImGuiSelectionUserData range_dst_item = item_data;
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, range_selected, (ImS8)range_direction, (range_direction > 0) ? storage->RangeSrcItem : range_dst_item, (range_direction > 0) ? range_dst_item : storage->RangeSrcItem };
ms->IO.Requests.push_back(req);
MultiSelectAddSetRange(ms, range_selected, range_direction, storage->RangeSrcItem, item_data);
}
// Update/store the selection state of the Source item (used by CTRL+SHIFT, when Source is unselected we perform a range unselect)
@ -7825,11 +7812,36 @@ void ImGui::MultiSelectItemFooter(ImGuiID id, bool* p_selected, bool* p_pressed)
}
if (storage->NavIdItem == item_data)
ms->NavIdPassedBy = true;
ms->LastSubmittedItem = item_data;
*p_selected = selected;
*p_pressed = pressed;
}
void ImGui::MultiSelectAddSetAll(ImGuiMultiSelectTempData* ms, bool selected)
{
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetAll, selected, 0, ImGuiSelectionUserData_Invalid, ImGuiSelectionUserData_Invalid };
ms->IO.Requests.resize(0); // Can always clear previous requests
ms->IO.Requests.push_back(req); // Add new request
}
void ImGui::MultiSelectAddSetRange(ImGuiMultiSelectTempData* ms, bool selected, int range_dir, ImGuiSelectionUserData first_item, ImGuiSelectionUserData last_item)
{
// Merge contiguous spans into same request (unless NoRangeSelect is set which guarantees single-item ranges)
if (ms->IO.Requests.Size > 0 && first_item == last_item && (ms->Flags & ImGuiMultiSelectFlags_NoRangeSelect) == 0)
{
ImGuiSelectionRequest* prev = &ms->IO.Requests.Data[ms->IO.Requests.Size - 1];
if (prev->Type == ImGuiSelectionRequestType_SetRange && prev->RangeLastItem == ms->LastSubmittedItem && prev->Selected == selected)
{
prev->RangeLastItem = last_item;
return;
}
}
ImGuiSelectionRequest req = { ImGuiSelectionRequestType_SetRange, selected, (ImS8)range_dir, (range_dir > 0) ? first_item : last_item, (range_dir > 0) ? last_item : first_item };
ms->IO.Requests.push_back(req); // Add new request
}
void ImGui::DebugNodeMultiSelectState(ImGuiMultiSelectState* storage)
{
#ifndef IMGUI_DISABLE_DEBUG_TOOLS