Tabs: adjust handling of ellipsis now that Close Button visibility changed. (#8387) Internals: remove extra parameter to RenderTextEllipsis().

This requires RenderTextEllipsis() to use fine CPU-side clippoing.
Users of RenderTextEllipsis(): #7024, #6236, #5267, #5745, #4269, #2775
This commit is contained in:
ocornut
2025-04-16 20:27:23 +02:00
parent e4a865177e
commit 97d85338e8
5 changed files with 29 additions and 21 deletions

View File

@@ -76,6 +76,8 @@ Other changes:
- The feature is unlikely to ever work properly when using a coarse clipper - The feature is unlikely to ever work properly when using a coarse clipper
such as ImGuiListClipper. such as ImGuiListClipper.
- TreeNode: fixed incorrect clipping of arrow/bullet when using ImGuiTreeNodeFlags_SpanAllColumns. - TreeNode: fixed incorrect clipping of arrow/bullet when using ImGuiTreeNodeFlags_SpanAllColumns.
- Tabs: fixes small issues with how "..." ellipsis moved depending on visibility
of Close Button or Unsaved Document marker. (#8387)
- Nav: fixed assertion when holding gamepad FaceLeft/West button to open - Nav: fixed assertion when holding gamepad FaceLeft/West button to open
CTRL+Tab windowing + pressing a keyboard key. (#8525) CTRL+Tab windowing + pressing a keyboard key. (#8525)
- Error Handling: added better error report and recovery for extraneous - Error Handling: added better error report and recovery for extraneous

View File

@@ -3691,18 +3691,18 @@ void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, cons
} }
// Another overly complex function until we reorganize everything into a nice all-in-one helper. // Another overly complex function until we reorganize everything into a nice all-in-one helper.
// This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) which define _where_ the ellipsis is, from actual clipping of text and limit of the ellipsis display. // This is made more complex because we have dissociated the layout rectangle (pos_min..pos_max) from 'ellipsis_max_x' which may be beyond it.
// This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move. // This is because in the context of tabs we selectively hide part of the text when the Close Button appears, but we don't want the ellipsis to move.
void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known) void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end_full, const ImVec2* text_size_if_known)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (text_end_full == NULL) if (text_end_full == NULL)
text_end_full = FindRenderedTextEnd(text); text_end_full = FindRenderedTextEnd(text);
const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f); const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_end_full, false, 0.0f);
//draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 4), IM_COL32(0, 0, 255, 255)); //draw_list->AddLine(ImVec2(pos_max.x, pos_min.y - 4), ImVec2(pos_max.x, pos_max.y + 6), IM_COL32(0, 0, 255, 255));
//draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y-2), ImVec2(ellipsis_max_x, pos_max.y+2), IM_COL32(0, 255, 0, 255)); //draw_list->AddLine(ImVec2(ellipsis_max_x, pos_min.y - 2), ImVec2(ellipsis_max_x, pos_max.y + 3), IM_COL32(0, 255, 0, 255));
//draw_list->AddLine(ImVec2(clip_max_x, pos_min.y), ImVec2(clip_max_x, pos_max.y), IM_COL32(255, 0, 0, 255));
// FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels. // FIXME: We could technically remove (last_glyph->AdvanceX - last_glyph->X1) from text_size.x here and save a few pixels.
if (text_size.x > pos_max.x - pos_min.x) if (text_size.x > pos_max.x - pos_min.x)
{ {
@@ -3734,15 +3734,15 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, con
} }
// Render text, render ellipsis // Render text, render ellipsis
RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f)); RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
ImVec4 cpu_fine_clip_rect(pos_min.x, pos_min.y, pos_max.x, pos_max.y);
ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y)); ImVec2 ellipsis_pos = ImTrunc(ImVec2(pos_min.x + text_size_clipped_x, pos_min.y));
if (ellipsis_pos.x + ellipsis_width <= ellipsis_max_x) for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale)
for (int i = 0; i < font->EllipsisCharCount; i++, ellipsis_pos.x += font->EllipsisCharStep * font_scale) font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar, &cpu_fine_clip_rect);
font->RenderChar(draw_list, font_size, ellipsis_pos, GetColorU32(ImGuiCol_Text), font->EllipsisChar);
} }
else else
{ {
RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_full, &text_size, ImVec2(0.0f, 0.0f)); RenderTextClippedEx(draw_list, pos_min, pos_max, text, text_end_full, &text_size, ImVec2(0.0f, 0.0f));
} }
if (g.LogEnabled) if (g.LogEnabled)

View File

@@ -3424,7 +3424,7 @@ namespace ImGui
IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width);
IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL); IMGUI_API void RenderTextClippedEx(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0, 0), const ImRect* clip_rect = NULL);
IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float clip_max_x, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known); IMGUI_API void RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min, const ImVec2& pos_max, float ellipsis_max_x, const char* text, const char* text_end, const ImVec2* text_size_if_known);
IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f); IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool borders = true, float rounding = 0.0f);
IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f);
IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0); IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImDrawList* draw_list, ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, ImDrawFlags flags = 0);

View File

@@ -3275,7 +3275,7 @@ void ImGui::TableHeader(const char* label)
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will // Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
// be merged into a single draw call. // be merged into a single draw call.
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE); //window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, label, label_end, &label_size);
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
if (text_clipped && hovered && g.ActiveId == 0) if (text_clipped && hovered && g.ActiveId == 0)
@@ -3427,7 +3427,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height)); ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
int vtx_idx_begin = draw_list->_VtxCurrentIdx; int vtx_idx_begin = draw_list->_VtxCurrentIdx;
PushStyleColor(ImGuiCol_Text, request->TextColor); PushStyleColor(ImGuiCol_Text, request->TextColor);
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size); RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
PopStyleColor(); PopStyleColor();
int vtx_idx_end = draw_list->_VtxCurrentIdx; int vtx_idx_end = draw_list->_VtxCurrentIdx;

View File

@@ -1690,7 +1690,7 @@ void ImGui::SeparatorTextEx(ImGuiID id, const char* label, const char* label_end
window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness); window->DrawList->AddLine(ImVec2(sep2_x1, seps_y), ImVec2(sep2_x2, seps_y), separator_col, separator_thickness);
if (g.LogEnabled) if (g.LogEnabled)
LogSetNextTextDecoration("---", NULL); LogSetNextTextDecoration("---", NULL);
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, bb.Max.x, label, label_end, &label_size); RenderTextEllipsis(window->DrawList, label_pos, ImVec2(bb.Max.x, bb.Max.y + style.ItemSpacing.y), bb.Max.x, label, label_end, &label_size);
} }
else else
{ {
@@ -10467,12 +10467,11 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
// Render text label (with clipping + alpha gradient) + unsaved marker // Render text label (with clipping + alpha gradient) + unsaved marker
ImRect text_ellipsis_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y); ImRect text_ellipsis_clip_bb(bb.Min.x + frame_padding.x, bb.Min.y + frame_padding.y, bb.Max.x - frame_padding.x, bb.Max.y);
float text_pixel_clip_bb_max_x = text_ellipsis_clip_bb.Max.x;
// Return clipped state ignoring the close button // Return clipped state ignoring the close button
if (out_text_clipped) if (out_text_clipped)
{ {
*out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_pixel_clip_bb_max_x; *out_text_clipped = (text_ellipsis_clip_bb.Min.x + label_size.x) > text_ellipsis_clip_bb.Max.x;
//draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255)); //draw_list->AddCircle(text_ellipsis_clip_bb.Min, 3.0f, *out_text_clipped ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));
} }
@@ -10518,15 +10517,22 @@ void ImGui::TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb,
// This is all rather complicated // This is all rather complicated
// (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position) // (the main idea is that because the close button only appears on hover, we don't want it to alter the ellipsis position)
// FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist.. // FIXME: if FramePadding is noticeably large, ellipsis_max_x will be wrong here (e.g. #3497), maybe for consistency that parameter of RenderTextEllipsis() shouldn't exist..
float ellipsis_max_x = close_button_visible ? text_pixel_clip_bb_max_x : bb.Max.x - 1.0f; float ellipsis_max_x = text_ellipsis_clip_bb.Max.x;
if (close_button_visible || unsaved_marker_visible) if (close_button_visible || unsaved_marker_visible)
{ {
text_pixel_clip_bb_max_x -= close_button_visible ? (button_sz) : (button_sz * 0.80f); const bool visible_without_hover = unsaved_marker_visible || (is_contents_visible ? g.Style.TabCloseButtonMinWidthSelected : g.Style.TabCloseButtonMinWidthUnselected) < 0.0f;
text_ellipsis_clip_bb.Max.x -= unsaved_marker_visible ? (button_sz * 0.80f) : 0.0f; if (visible_without_hover)
ellipsis_max_x = text_pixel_clip_bb_max_x; {
text_ellipsis_clip_bb.Max.x -= button_sz * 0.90f;
ellipsis_max_x -= button_sz * 0.90f;
}
else
{
text_ellipsis_clip_bb.Max.x -= button_sz * 1.00f;
}
} }
LogSetNextTextDecoration("/", "\\"); LogSetNextTextDecoration("/", "\\");
RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, text_pixel_clip_bb_max_x, ellipsis_max_x, label, NULL, &label_size); RenderTextEllipsis(draw_list, text_ellipsis_clip_bb.Min, text_ellipsis_clip_bb.Max, ellipsis_max_x, label, NULL, &label_size);
#if 0 #if 0
if (!is_contents_visible) if (!is_contents_visible)