From 048d7e10b5bcc48e20850694d693fbad71c799b5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 27 Oct 2026 13:02:28 +0100 Subject: [PATCH] Style: made default arrow stroked. Added SymbolArrowSizeFactor, SymbolArrowThicknessFactor to style arrow. (#5233, #2431, #2235, #6730, #8243) Internal function takes a angle: #5812. This doesn't cover generic rendering override for symbols: #8243, #2431 (arrows, somehow less needed now), #3351 (checkmarks), #6730 (tree nodes) --- docs/CHANGELOG.txt | 4 +++ imgui.cpp | 4 +++ imgui.h | 4 +++ imgui_demo.cpp | 4 +++ imgui_draw.cpp | 67 +++++++++++++++++++++++++++------------------- imgui_internal.h | 1 + 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b0e396e4d..2cb513c3b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -67,6 +67,10 @@ Autumn cleanup of stuff marked obsolete more than two years ago: Other Changes: - Style: `StyleColorsDark()`: tweaked WindowBg to be darker than in 1.93.0. +- Style: added `style.SymbolArrowSizeFactor`, `style.SymbolArrowThicknessFactor` to tweak + the shape and look of arrows. Made default arrow stroked. (#5233, #2431, #2235, #6730, #8243) + Legacy arrow: size: (0.60f,0.70f), thickness: 0.0f + New arrow: size: (0.72f,0.40f), thickness: 0.30f - ImVec2: added a check for cases where a custom `IM_VEC2_CLASS_EXTRA` macro adds an extra `operator=` to ImVec2. This isn't supported but should not be needed as you should have a constructor to convert from your type to ImVec2. diff --git a/imgui.cpp b/imgui.cpp index 585e8a9b2..f8a2def00 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1628,6 +1628,10 @@ ImGuiStyle::ImGuiStyle() DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. + // Symbols + SymbolArrowSizeFactor = ImVec2(0.72f,0.40f); // Width/height factor of arrows (0.0f..1.0f). Legacy arrows use (0.60f,0.70f) + SymbolArrowThicknessFactor = 0.30f; // Thickness factor of arrows (0.0f..0.5f). 0.0f=Fill. >0.0f=Stroke. Legacy arrows use 0.0f. + // Rendering & Tessellation AntiAliasedLines = true; // Enable anti-aliased lines/borders. Used at the beginning of the frame to set ImDrawFlags_AALines in all draw-lists. AntiAliasedLineEnds = false; // Enable anti-aliased lines/borders ends. Nicer for thick lines but more expensive. Used at the beginning of the frame to set ImDrawFlags_AALineEnds in all draw-lists. diff --git a/imgui.h b/imgui.h index fdbcf80ba..67e11d18c 100644 --- a/imgui.h +++ b/imgui.h @@ -2396,6 +2396,10 @@ struct ImGuiStyle ImVec2 DisplaySafeAreaPadding; // Apply to every windows, menus, popups, tooltips: amount where we avoid displaying contents. Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured). float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). We apply per-monitor DPI scaling over this scale. May be removed later. + // Symbols + ImVec2 SymbolArrowSizeFactor; // Width/height factor of arrows (0.0f..1.0f). + float SymbolArrowThicknessFactor; // Thickness factor of arrows (0.0f..0.5f). 0.0f=Fill. >0.0f=Stroke. + // Rendering & Tessellation bool AntiAliasedLines; // Enable anti-aliased lines/borders. Used at the beginning of the frame to set ImDrawFlags_AALines in all draw-lists. bool AntiAliasedLineEnds; // Enable anti-aliased lines/borders ends. Nicer for thick lines but more expensive. Used at the beginning of the frame to set ImDrawFlags_AALineEnds in all draw-lists. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ede2eeb07..aeb896305 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -8807,6 +8807,10 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) SliderFloat("ImageRounding", &style.ImageRounding, 0.0f, 12.0f, "%.0f"); SliderFloat("ImageBorderSize", &style.ImageBorderSize, 0.0f, max_border_size, "%.0f"); + SeparatorText("Symbols"); + DragFloat2("SymbolArrowSizeFactor", &style.SymbolArrowSizeFactor.x, 0.01f, 0.0f, 1.0f); + DragFloat("SymbolArrowThicknessFactor", &style.SymbolArrowThicknessFactor, 0.01f, 0.0f, 0.5f); + SeparatorText("Tooltips"); for (int n = 0; n < 2; n++) if (TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav")) diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 1b9af8822..6ab6c6c45 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -7558,36 +7558,47 @@ begin: // - RenderColorRectWithAlphaCheckerboard() //----------------------------------------------------------------------------- -// Render an arrow aimed to be aligned with text (p_min is a position in the same space text would be positioned). To e.g. denote expanded/collapsed state +static const ImVec2 g_ImGuiDirCosSin[4] = { { -1.0f, 0.0f }, { +1.0f, 0.0f }, { 0.0f, -1.0f }, { 0.0f, +1.0f } }; + +// arrow_h, arrow_w, arrow_th are normalized: 0.0f..1.0f +// arrow_th: 0.0f == fill, >0.0f == stroke weight. +void ImGui::RenderArrowEx(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float cos_a, float sin_a, float scale, ImVec2 arrow_sizes, float arrow_th) +{ + const bool is_fill = (arrow_th <= 0.0f); + const float r = draw_list->_Data->FontSize * 0.50f * scale; + const float thickness = is_fill ? 0.0f : IM_ROUND(ImClamp(r * arrow_th, 1.0f, r * 0.5f)); + + // Bias for shape to feel more centered + const float bias = is_fill ? (r * 0.05f) : (thickness * 0.8f * 0.7071f); + const ImVec2 center = ImVec2(pos.x + r + bias * cos_a, pos.y + r + bias * sin_a); + + const float arrow_h = arrow_sizes.y * r; + const float arrow_w = arrow_sizes.x * r; + ImVec2 p[3]; + p[0] = ImVec2(center.x - arrow_h * cos_a - arrow_w * -sin_a, center.y - arrow_h * sin_a - arrow_w * cos_a); + p[1] = ImVec2(center.x + arrow_h * cos_a, center.y + arrow_h * sin_a); + p[2] = ImVec2(center.x - arrow_h * cos_a + arrow_w * -sin_a, center.y - arrow_h * sin_a + arrow_w * cos_a); + + // Pixel align when at a right angle + if (is_fill && (cos_a * sin_a >= -FLT_EPSILON) && (cos_a * sin_a <= FLT_EPSILON)) + { + p[0] = ImRoundSigned64(p[0]); + p[1] = ImRoundSigned64(p[1]); + p[2] = ImRoundSigned64(p[2]); + } + + if (is_fill) + draw_list->AddConvexPolyFilled(p, 3, col); + else + draw_list->_AddPolyline(p, 3, col, thickness, ImDrawFlags_StrokeInside | ImDrawFlags_AALineEnds, FLT_MAX); // FIXME-OPT: baked filled shape? +} + +// Render an arrow aimed to be aligned with text. void ImGui::RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale) { - const float h = draw_list->_Data->FontSize * 1.00f; - float r = h * 0.40f * scale; - ImVec2 center = pos + ImVec2(h * 0.50f * scale, h * 0.50f * scale); - - ImVec2 a, b, c; - switch (dir) - { - case ImGuiDir_Up: - case ImGuiDir_Down: - if (dir == ImGuiDir_Up) r = -r; - a = ImVec2(+0.000f, +0.750f) * r; - b = ImVec2(-0.866f, -0.750f) * r; - c = ImVec2(+0.866f, -0.750f) * r; - break; - case ImGuiDir_Left: - case ImGuiDir_Right: - if (dir == ImGuiDir_Left) r = -r; - a = ImVec2(+0.750f, +0.000f) * r; - b = ImVec2(-0.750f, +0.866f) * r; - c = ImVec2(-0.750f, -0.866f) * r; - break; - case ImGuiDir_None: - case ImGuiDir_COUNT: - IM_ASSERT(0); - break; - } - draw_list->AddTriangleFilled(center + a, center + b, center + c, col); + ImGuiContext& g = *GImGui; + ImVec2 cos_sin = g_ImGuiDirCosSin[dir]; + RenderArrowEx(draw_list, pos, col, cos_sin.x, cos_sin.y, scale, g.Style.SymbolArrowSizeFactor, g.Style.SymbolArrowThicknessFactor); } void ImGui::RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col) diff --git a/imgui_internal.h b/imgui_internal.h index eaeb52b31..9cc49e343 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3803,6 +3803,7 @@ namespace ImGui // Render helpers (those functions don't access any ImGui state!) IMGUI_API void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImU32 col, ImGuiDir dir, float scale = 1.0f); + IMGUI_API void RenderArrowEx(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float cos_a, float sin_a, float scale, ImVec2 arrow_sizes, float arrow_th); IMGUI_API void RenderBullet(ImDrawList* draw_list, ImVec2 pos, ImU32 col); IMGUI_API void RenderCheckMark(ImDrawList* draw_list, ImVec2 pos, ImU32 col, float sz); IMGUI_API void RenderRectFilledInRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float fill_x0, float fill_x1, float rounding);