1
0
mirror of https://github.com/ocornut/imgui.git synced 2024-11-30 18:34:34 +01:00

Merge branch 'features/ellipsis_rendering'

# Conflicts:
#	imgui.cpp
This commit is contained in:
omar 2019-09-17 12:07:30 +02:00
commit 3b014d0c31
7 changed files with 71 additions and 27 deletions

View File

@ -50,6 +50,10 @@ Other Changes:
- SliderScalar: Improved assert when using U32 or U64 types with a large v_max value. (#2765) [@loicmouton] - 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. - 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] - 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) - 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) - 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 - Added a mechanism to compact/free the larger allocations of unused windows (buffers are compacted when

View File

@ -275,6 +275,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- font: MergeMode: flags to select overwriting or not (this is now very easy with refactored ImFontAtlasBuildWithStbTruetype) - 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: 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: 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: 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: 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 - font: finish CustomRectRegister() to allow mapping Unicode codepoint to custom texture data

View File

@ -2496,6 +2496,10 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min,
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(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(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.
if (text_size.x > pos_max.x - pos_min.x) if (text_size.x > pos_max.x - pos_min.x)
{ {
// Hello wo... // Hello wo...
@ -2503,15 +2507,33 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min,
// min max ellipsis_max // min max ellipsis_max
// <-> this is generally some padding value // <-> this is generally some padding value
// FIXME-STYLE: RenderPixelEllipsis() style should use actual font data.
const ImFont* font = draw_list->_Data->Font; const ImFont* font = draw_list->_Data->Font;
const float font_size = draw_list->_Data->FontSize; const float font_size = draw_list->_Data->FontSize;
const int ellipsis_dot_count = 3;
const float ellipsis_width = (1.0f + 1.0f) * ellipsis_dot_count - 1.0f;
const char* text_end_ellipsis = NULL; const char* text_end_ellipsis = NULL;
float text_width = ImMax((pos_max.x - ellipsis_width) - pos_min.x, 1.0f); ImWchar ellipsis_char = font->EllipsisChar;
float text_size_clipped_x = font->CalcTextSizeA(font_size, text_width, 0.0f, text, text_end_full, &text_end_ellipsis).x; int ellipsis_char_count = 1;
if (ellipsis_char == (ImWchar)-1)
{
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_total_width = ellipsis_glyph_width; // Full width of entire ellipsis
if (ellipsis_char_count > 1)
{
// Full ellipsis size without free spacing after it.
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;
}
// We can now claim the space between pos_max.x and ellipsis_max.x
const float text_avail_width = ImMax((ImMax(pos_max.x, ellipsis_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) if (text == text_end_ellipsis && text_end_ellipsis < text_end_full)
{ {
// Always display at least 1 character if there's no room for character + ellipsis // Always display at least 1 character if there's no room for character + ellipsis
@ -2520,15 +2542,20 @@ void ImGui::RenderTextEllipsis(ImDrawList* draw_list, const ImVec2& pos_min,
} }
while (text_end_ellipsis > text && ImCharIsBlankA(text_end_ellipsis[-1])) 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_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 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
} }
RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
const float ellipsis_x = pos_min.x + text_size_clipped_x + 1.0f; // Render text, render ellipsis
if (ellipsis_x + ellipsis_width - 1.0f <= ellipsis_max_x) RenderTextClippedEx(draw_list, pos_min, ImVec2(clip_max_x, pos_max.y), text, text_end_ellipsis, &text_size, ImVec2(0.0f, 0.0f));
RenderPixelEllipsis(draw_list, ImVec2(ellipsis_x, pos_min.y), GetColorU32(ImGuiCol_Text), ellipsis_dot_count); float ellipsis_x = pos_min.x + text_size_clipped_x;
if (ellipsis_x + ellipsis_total_width <= 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_char);
ellipsis_x += ellipsis_glyph_width;
}
} }
else else
{ {

View File

@ -2011,6 +2011,7 @@ struct ImFontConfig
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. 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. 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. 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 EllipsisChar; // -1 // Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
// [Internal] // [Internal]
char Name[40]; // Name (strictly to ease debugging) char Name[40]; // Name (strictly to ease debugging)
@ -2187,7 +2188,8 @@ struct ImFont
ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into ImFontAtlas* ContainerAtlas; // 4-8 // out // // What we has been loaded into
const ImFontConfig* ConfigData; // 4-8 // in // // Pointer within ContainerAtlas->ConfigData 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. 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 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] 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) 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)

View File

@ -3283,7 +3283,8 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref)
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::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::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("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); 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); 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++) for (int config_i = 0; config_i < font->ConfigDataCount; config_i++)

View File

@ -1426,6 +1426,7 @@ ImFontConfig::ImFontConfig()
MergeMode = false; MergeMode = false;
RasterizerFlags = 0x00; RasterizerFlags = 0x00;
RasterizerMultiply = 1.0f; RasterizerMultiply = 1.0f;
EllipsisChar = (ImWchar)-1;
memset(Name, 0, sizeof(Name)); memset(Name, 0, sizeof(Name));
DstFont = NULL; DstFont = NULL;
} }
@ -1618,6 +1619,9 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize); memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize);
} }
if (new_font_cfg.DstFont->EllipsisChar == (ImWchar)-1)
new_font_cfg.DstFont->EllipsisChar = font_cfg->EllipsisChar;
// Invalidate texture // Invalidate texture
ClearTexData(); ClearTexData();
return new_font_cfg.DstFont; return new_font_cfg.DstFont;
@ -1652,6 +1656,7 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
font_cfg.SizePixels = 13.0f * 1.0f; font_cfg.SizePixels = 13.0f * 1.0f;
if (font_cfg.Name[0] == '\0') if (font_cfg.Name[0] == '\0')
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels); ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
font_cfg.EllipsisChar = (ImWchar)0x0085;
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault(); const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
@ -2196,6 +2201,23 @@ void ImFontAtlasBuildFinish(ImFontAtlas* atlas)
for (int i = 0; i < atlas->Fonts.Size; i++) for (int i = 0; i < atlas->Fonts.Size; i++)
if (atlas->Fonts[i]->DirtyLookupTables) if (atlas->Fonts[i]->DirtyLookupTables)
atlas->Fonts[i]->BuildLookupTable(); atlas->Fonts[i]->BuildLookupTable();
// 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->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
{
font->EllipsisChar = ellipsis_variants[j];
break;
}
}
} }
// Retrieve list of range (2 int per range, values are inclusive) // Retrieve list of range (2 int per range, values are inclusive)
@ -2465,6 +2487,7 @@ ImFont::ImFont()
FontSize = 0.0f; FontSize = 0.0f;
FallbackAdvanceX = 0.0f; FallbackAdvanceX = 0.0f;
FallbackChar = (ImWchar)'?'; FallbackChar = (ImWchar)'?';
EllipsisChar = (ImWchar)-1;
DisplayOffset = ImVec2(0.0f, 0.0f); DisplayOffset = ImVec2(0.0f, 0.0f);
FallbackGlyph = NULL; FallbackGlyph = NULL;
ContainerAtlas = NULL; ContainerAtlas = NULL;
@ -3012,7 +3035,6 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
// - RenderMouseCursor() // - RenderMouseCursor()
// - RenderArrowPointingAt() // - RenderArrowPointingAt()
// - RenderRectFilledRangeH() // - RenderRectFilledRangeH()
// - RenderPixelEllipsis()
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor) void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor)
@ -3122,18 +3144,6 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
draw_list->PathFillConvex(col); draw_list->PathFillConvex(col);
} }
// FIXME: Rendering an ellipsis "..." is a surprisingly tricky problem for us... we cannot rely on font glyph having it,
// and regular dot are typically too wide. If we render a dot/shape ourselves it comes with the risk that it wouldn't match
// the boldness or positioning of what the font uses...
void ImGui::RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, ImU32 col, int count)
{
ImFont* font = draw_list->_Data->Font;
const float font_scale = draw_list->_Data->FontSize / font->FontSize;
pos.y += (float)(int)(font->DisplayOffset.y + font->Ascent * font_scale + 0.5f - 1.0f);
for (int dot_n = 0; dot_n < count; dot_n++)
draw_list->AddRectFilled(ImVec2(pos.x + dot_n * 2.0f, pos.y), ImVec2(pos.x + dot_n * 2.0f + 1.0f, pos.y + 1.0f), col);
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Decompression code // [SECTION] Decompression code
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -1635,7 +1635,6 @@ namespace ImGui
IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow); IMGUI_API void RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor = ImGuiMouseCursor_Arrow);
IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col); IMGUI_API void RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col);
IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding);
IMGUI_API void RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, ImU32 col, int count);
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// 2019/06/07: Updating prototypes of some of the internal functions. Leaving those for reference for a short while. // 2019/06/07: Updating prototypes of some of the internal functions. Leaving those for reference for a short while.