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)
This commit is contained in:
ocornut 2026-10-27 13:02:28 +01:00
parent 726399ff76
commit 048d7e10b5
6 changed files with 56 additions and 28 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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"))

View File

@ -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)

View File

@ -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);