Font: Narrow ellipsis: various minor stylistic tweaks (#2775)
diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt
index 6e487ae..36ba83b 100644
--- a/docs/CHANGELOG.txt
+++ b/docs/CHANGELOG.txt
@@ -50,6 +50,10 @@
 - SliderScalar: Improved assert when using U32 or U64 types with a large v_max value. (#2765) [@loicmouton]
 - DragInt, DragFloat, DragScalar: Using (v_min > v_max) allows locking any edit to the value.
 - DragScalar: Fixed dragging of unsigned values on ARM cpu. (#2780) [@dBagrat]
+- Font: Better ellipsis drawing implementation. Instead of drawing three pixel-ey dots (which was glaringly
+  unfitting with many types of fonts) we first attempt to find a standard ellipsis glyphs within the loaded set.
+  Otherwise we render ellipsis using '.' from the font from where we trim excessive spacing to make it as narrow
+  as possible. (#2775) [@rokups]
 - ImDrawList: clarified the name of many parameters so reading the code is a little easier. (#2740)
 - Using offsetof() when available in C++11. Avoids Clang sanitizer complaining about old-style macros. (#94)
 - Added a mechanism to compact/free the larger allocations of unused windows (buffers are compacted when
diff --git a/docs/TODO.txt b/docs/TODO.txt
index 70c64c4..df413b7 100644
--- a/docs/TODO.txt
+++ b/docs/TODO.txt
@@ -275,6 +275,7 @@
  - font: MergeMode: flags to select overwriting or not (this is now very easy with refactored ImFontAtlasBuildWithStbTruetype)
  - font: free the Alpha buffer if user only requested RGBA.
 !- font: better CalcTextSizeA() API, at least for simple use cases. current one is horrible (perhaps have simple vs extended versions).
+ - font: for the purpose of RenderTextEllipsis(), it might be useful that CalcTextSizeA() can ignore the trailing padding?
  - font: a CalcTextHeight() helper could run faster than CalcTextSize().y
  - font: enforce monospace through ImFontConfig (for icons?) + create dual ImFont output from same input, reusing rasterized data but with different glyphs/AdvanceX
  - font: finish CustomRectRegister() to allow mapping Unicode codepoint to custom texture data
diff --git a/imgui.cpp b/imgui.cpp
index 7921b1d..4a606da 100644
--- a/imgui.cpp
+++ b/imgui.cpp
@@ -2506,38 +2506,32 @@
         const ImFont* font = draw_list->_Data->Font;
         const float font_size = draw_list->_Data->FontSize;
         const char* text_end_ellipsis = NULL;
-        const ImFontGlyph* glyph;
-        int ellipsis_char_num = 1;
-        ImWchar ellipsis_codepoint = font->EllipsisCodePoint;
 
-        if (ellipsis_codepoint != (ImWchar)-1)
-            glyph = font->FindGlyph(ellipsis_codepoint);
-        else
+        ImWchar ellipsis_char = font->EllipsisChar;
+        int ellipsis_char_count = 1;
+        if (ellipsis_char == (ImWchar)-1)
         {
-            ellipsis_codepoint = (ImWchar)'.';
-            glyph = font->FindGlyph(ellipsis_codepoint);
-            ellipsis_char_num = 3;
+            ellipsis_char = (ImWchar)'.';
+            ellipsis_char_count = 3;
         }
+        const ImFontGlyph* glyph = font->FindGlyph(ellipsis_char);
 
-        float ellipsis_glyph_width = glyph->X1;                      // Width of the glyph with no padding on either side
-        float ellipsis_width = ellipsis_glyph_width;                 // Full width of entire ellipsis
-        float push_left = 1.f;
+        float ellipsis_glyph_width = glyph->X1;                 // Width of the glyph with no padding on either side
+        float ellipsis_total_width = ellipsis_glyph_width;      // Full width of entire ellipsis
+        float push_left = 1.0f;
         
-        if (ellipsis_char_num > 1)
+        if (ellipsis_char_count > 1)
         {
-            const float spacing_between_dots = 1.f * (draw_list->_Data->FontSize / font->FontSize);
-            ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
             // Full ellipsis size without free spacing after it.
-            ellipsis_width = ellipsis_glyph_width * (float)ellipsis_char_num - spacing_between_dots;
-            if (glyph->X0 > 1.f)
-            {
-                // Pushing ellipsis to the left will be accomplished by rendering the dot (X0).
-                push_left = 0.f;
-            }
+            const float spacing_between_dots = 1.0f * (draw_list->_Data->FontSize / font->FontSize);
+            ellipsis_glyph_width = glyph->X1 - glyph->X0 + spacing_between_dots;
+            ellipsis_total_width = ellipsis_glyph_width * (float)ellipsis_char_count - spacing_between_dots;
+            if (glyph->X0 > 1.0f)
+                push_left = 0.0f; // Pushing ellipsis to the left will be accomplished by rendering the dot (X0).
         }
         
-        float text_width = ImMax((pos_max.x - ellipsis_width) - pos_min.x, 1.0f);
-        float text_size_clipped_x = font->CalcTextSizeA(font_size, text_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
+        float text_avail_width = ImMax((pos_max.x - ellipsis_total_width) - pos_min.x, 1.0f);
+        float text_size_clipped_x = font->CalcTextSizeA(font_size, text_avail_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
 
         if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
         {
@@ -2547,7 +2541,7 @@
         }
         while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1]))
         {
-            // Trim trailing space before ellipsis
+            // Trim trailing space before ellipsis (FIXME: Supporting non-ascii blanks would be nice, for this we need a function to backtrack in UTF-8 text)
             text_end_ellipsis--;
             text_size_clipped_x -= font->CalcTextSizeA(font_size, FLT_MAX, 0.0f, text_end_ellipsis, text_end_ellipsis + 1).x; // Ascii blanks are always 1 byte
         }
@@ -2560,16 +2554,15 @@
             //  ||||
             //   \ \__ extra_spacing when two characters got hidden
             //    \___ extra_spacing when one character got hidden
-            unsigned c = 0;
-            float extra_spacing = 0;
+            unsigned int c = 0;
+            float extra_spacing = 0.0f;
             const char* text_end_ellipsis_prev = text_end_ellipsis;
             text_end_ellipsis += ImTextCharFromUtf8(&c, text_end_ellipsis, text_end_full);
             if (c && !ImCharIsBlankW(c))
             {
-                const ImFontGlyph* hidden_glyph = font->FindGlyph(c);
                 // Free space after first invisible glyph
+                const ImFontGlyph* hidden_glyph = font->FindGlyph((ImWchar)c);
                 extra_spacing = hidden_glyph->AdvanceX - hidden_glyph->X1;
-                c = 0;
                 text_end_ellipsis += ImTextCharFromUtf8(&c, text_end_ellipsis, text_end_full);
                 if (c && !ImCharIsBlankW(c))
                 {
@@ -2587,11 +2580,11 @@
             if (extra_spacing > 0)
             {
                 // Repeat calculation hoping that we will get extra character visible
-                text_width += extra_spacing;
+                text_avail_width += extra_spacing;
                 // Text length calculation is essentially an optimized version of this:
                 //   text_size_clipped_x = font->CalcTextSizeA(font_size, text_width, 0.0f, text, text_end_full, &text_end_ellipsis).x;
                 // It avoids calculating entire width of the string.
-                text_size_clipped_x += font->CalcTextSizeA(font_size, text_width - text_size_clipped_x, 0.0f, text_end_ellipsis_prev, text_end_full, &text_end_ellipsis).x;
+                text_size_clipped_x += font->CalcTextSizeA(font_size, text_avail_width - text_size_clipped_x, 0.0f, text_end_ellipsis_prev, text_end_full, &text_end_ellipsis).x;
             }
             else
                 text_end_ellipsis = text_end_ellipsis_prev;
@@ -2603,14 +2596,12 @@
         // ellipsis character contained in the font. If we render ellipsis manually space is already adequate and extra
         // spacing is not needed.
         float ellipsis_x = pos_min.x + text_size_clipped_x + push_left;
-        if (ellipsis_x + ellipsis_width - push_left <= ellipsis_max_x)
-        {
-            for (int i = 0; i < ellipsis_char_num; i++)
+        if (ellipsis_x + ellipsis_total_width - push_left <= ellipsis_max_x)
+            for (int i = 0; i < ellipsis_char_count; i++)
             {
-                font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_codepoint);
+                font->RenderChar(draw_list, font_size, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_char);
                 ellipsis_x += ellipsis_glyph_width;
             }
-        }
     }
     else
     {
diff --git a/imgui.h b/imgui.h
index d4a81c3..9e875f8 100644
--- a/imgui.h
+++ b/imgui.h
@@ -2011,7 +2011,7 @@
     bool            MergeMode;              // false    // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.
     unsigned int    RasterizerFlags;        // 0x00     // Settings for custom font rasterizer (e.g. ImGuiFreeType). Leave as zero if you aren't using one.
     float           RasterizerMultiply;     // 1.0f     // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable.
-    ImWchar         EllipsisCodePoint;      // -1       // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
+    ImWchar         EllipsisChar;           // -1       // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
 
     // [Internal]
     char            Name[40];               // Name (strictly to ease debugging)
@@ -2188,12 +2188,12 @@
     ImFontAtlas*                ContainerAtlas;     // 4-8   // out //            // What we has been loaded into
     const ImFontConfig*         ConfigData;         // 4-8   // in  //            // Pointer within ContainerAtlas->ConfigData
     short                       ConfigDataCount;    // 2     // in  // ~ 1        // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont.
-    ImWchar                     FallbackChar;       // 2     // in  // = '?'      // Replacement glyph if one isn't found. Only set via SetFallbackChar()
+    ImWchar                     FallbackChar;       // 2     // in  // = '?'      // Replacement character if a glyph isn't found. Only set via SetFallbackChar()
+    ImWchar                     EllipsisChar;       // 2     // out // = -1       // Character used for ellipsis rendering.
     float                       Scale;              // 4     // in  // = 1.f      // Base font scale, multiplied by the per-window font scale which you can adjust with SetWindowFontScale()
     float                       Ascent, Descent;    // 4+4   // out //            // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize]
     int                         MetricsTotalSurface;// 4     // out //            // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs)
     bool                        DirtyLookupTables;  // 1     // out //
-    ImWchar                     EllipsisCodePoint;  // -1    // out //            // Override a codepoint used for ellipsis rendering.
 
     // Methods
     IMGUI_API ImFont();
diff --git a/imgui_demo.cpp b/imgui_demo.cpp
index fd1d85a..e8e8746 100644
--- a/imgui_demo.cpp
+++ b/imgui_demo.cpp
@@ -3283,7 +3283,8 @@
                     ImGui::SameLine(); HelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)");
                     ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, "%.0f");
                     ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
-                    ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar);
+                    ImGui::Text("Fallback character: '%c' (U+%04X)", font->FallbackChar, font->FallbackChar);
+                    ImGui::Text("Ellipsis character: '%c' (U+%04X)", font->EllipsisChar);
                     const float surface_sqrt = sqrtf((float)font->MetricsTotalSurface);
                     ImGui::Text("Texture surface: %d pixels (approx) ~ %dx%d", font->MetricsTotalSurface, (int)surface_sqrt, (int)surface_sqrt);
                     for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)
diff --git a/imgui_draw.cpp b/imgui_draw.cpp
index b4801c2..dd21523 100644
--- a/imgui_draw.cpp
+++ b/imgui_draw.cpp
@@ -1426,9 +1426,9 @@
     MergeMode = false;
     RasterizerFlags = 0x00;
     RasterizerMultiply = 1.0f;
+    EllipsisChar = (ImWchar)-1;
     memset(Name, 0, sizeof(Name));
     DstFont = NULL;
-    EllipsisCodePoint = (ImWchar)-1;
 }
 
 //-----------------------------------------------------------------------------
@@ -1619,8 +1619,8 @@
         memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize);
     }
 
-    if (new_font_cfg.DstFont->EllipsisCodePoint == (ImWchar)-1)
-        new_font_cfg.DstFont->EllipsisCodePoint = font_cfg->EllipsisCodePoint;
+    if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
+        new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
 
     // Invalidate texture
     ClearTexData();
@@ -1656,7 +1656,7 @@
         font_cfg.SizePixels = 13.0f * 1.0f;
     if (font_cfg.Name[0] == '\0')
         ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
-    font_cfg.EllipsisCodePoint = (ImWchar)0x0085;
+    font_cfg.EllipsisChar = (ImWchar)0x0085;
 
     const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
     const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
@@ -2204,22 +2204,19 @@
 
     // Ellipsis character is required for rendering elided text. We prefer using U+2026 (horizontal ellipsis).
     // However some old fonts may contain ellipsis at U+0085. Here we auto-detect most suitable ellipsis character.
+    // FIXME: Also note that 0x2026 is currently seldomly included in our font ranges. Because of this we are more likely to use three individual dots.
     for (int i = 0; i < atlas->Fonts.size(); i++)
     {
         ImFont* font = atlas->Fonts[i];
-        if (font->EllipsisCodePoint == (ImWchar)-1)
-        {
-            const ImWchar ellipsis_variants[] = {(ImWchar)0x2026, (ImWchar)0x0085, (ImWchar)0};
-            for (int j = 0; ellipsis_variants[j] != (ImWchar) 0; j++)
+        if (font->EllipsisChar != (ImWchar)-1)
+            continue;
+        const ImWchar ellipsis_variants[] = { (ImWchar)0x2026, (ImWchar)0x0085 };
+        for (int j = 0; j < IM_ARRAYSIZE(ellipsis_variants); j++)
+            if (font->FindGlyphNoFallback(ellipsis_variants[j]) != NULL) // Verify glyph exists
             {
-                ImWchar ellipsis_codepoint = ellipsis_variants[j];
-                if (font->FindGlyph(ellipsis_codepoint) != font->FallbackGlyph)     // Verify glyph exists
-                {
-                    font->EllipsisCodePoint = ellipsis_codepoint;
-                    break;
-                }
+                font->EllipsisChar = ellipsis_variants[j];
+                break;
             }
-        }
     }
 }
 
@@ -2490,6 +2487,7 @@
     FontSize = 0.0f;
     FallbackAdvanceX = 0.0f;
     FallbackChar = (ImWchar)'?';
+    EllipsisChar = (ImWchar)-1;
     DisplayOffset = ImVec2(0.0f, 0.0f);
     FallbackGlyph = NULL;
     ContainerAtlas = NULL;
@@ -2499,7 +2497,6 @@
     Scale = 1.0f;
     Ascent = Descent = 0.0f;
     MetricsTotalSurface = 0;
-    EllipsisCodePoint = (ImWchar)-1;
 }
 
 ImFont::~ImFont()