@@ -17,7 +17,7 @@ Index of this file:
1717// [SECTION] ImFontAtlas: backend for stb_truetype
1818// [SECTION] ImFontAtlas: glyph ranges helpers
1919// [SECTION] ImFontGlyphRangesBuilder
20- // [SECTION] ImFont
20+ // [SECTION] ImFontBaked, ImFont
2121// [SECTION] ImGui Internal Render Helpers
2222// [SECTION] Decompression code
2323// [SECTION] Default font data (ProggyClean.ttf)
@@ -5068,7 +5068,7 @@ void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
50685068}
50695069
50705070// -----------------------------------------------------------------------------
5071- // [SECTION] ImFont
5071+ // [SECTION] ImFontBaked, ImFont
50725072// -----------------------------------------------------------------------------
50735073
50745074ImFontBaked::ImFontBaked ()
@@ -5371,6 +5371,12 @@ const char* ImTextCalcWordWrapNextLineStart(const char* text, const char* text_e
53715371 return text;
53725372}
53735373
5374+ // Character classification for word-wrapping logic
5375+ enum
5376+ {
5377+ ImWcharClass_Blank, ImWcharClass_Punct, ImWcharClass_Other
5378+ };
5379+
53745380// Simple word-wrapping for English, not full-featured. Please submit failing cases!
53755381// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end.
53765382// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.)
@@ -5392,16 +5398,20 @@ const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* t
53925398 const float scale = size / baked->Size ;
53935399
53945400 float line_width = 0 .0f ;
5395- float word_width = 0 .0f ;
53965401 float blank_width = 0 .0f ;
53975402 wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters
53985403
5399- const char * word_end = text;
5400- const char * prev_word_end = NULL ;
5401- bool inside_word = true ;
5402-
54035404 const char * s = text;
54045405 IM_ASSERT (text_end != NULL );
5406+
5407+ int prev_type = ImWcharClass_Other;
5408+ const bool keep_blanks = (flags & ImDrawTextFlags_WrapKeepBlanks) != 0 ;
5409+
5410+ // Find next wrapping point
5411+ // const char* span_begin = s;
5412+ const char * span_end = s;
5413+ float span_width = 0 .0f ;
5414+
54055415 while (s < text_end)
54065416 {
54075417 unsigned int c = (unsigned int )*s;
@@ -5417,7 +5427,7 @@ const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* t
54175427 return s; // Direct return, skip "Wrap_width is too small to fit anything" path.
54185428 if (c == ' \r ' )
54195429 {
5420- s = next_s;
5430+ s = next_s; // Fast-skip
54215431 continue ;
54225432 }
54235433 }
@@ -5427,46 +5437,56 @@ const char* ImFontCalcWordWrapPositionEx(ImFont* font, float size, const char* t
54275437 if (char_width < 0 .0f )
54285438 char_width = BuildLoadGlyphGetAdvanceOrFallback (baked, c);
54295439
5430- if (ImCharIsBlankW (c))
5440+ // Classify current character
5441+ int curr_type;
5442+ if (c == ' ' || c == ' \t ' || c == 0x3000 ) // Inline version of ImCharIsBlankW(c)
5443+ curr_type = ImWcharClass_Blank;
5444+ else if (c == ' .' || c == ' ,' || c == ' ;' || c == ' !' || c == ' ?' || c == ' \" ' || c == 0x3001 || c == 0x3002 )
5445+ curr_type = ImWcharClass_Punct;
5446+ else
5447+ curr_type = ImWcharClass_Other;
5448+
5449+ if (curr_type == ImWcharClass_Blank)
54315450 {
5432- if (inside_word)
5451+ // End span: 'A ' or '. '
5452+ if (prev_type != ImWcharClass_Blank && !keep_blanks)
54335453 {
5434- line_width += blank_width ;
5435- blank_width = 0 . 0f ;
5436- word_end = s ;
5454+ span_end = s ;
5455+ line_width += span_width ;
5456+ span_width = 0 . 0f ;
54375457 }
54385458 blank_width += char_width;
5439- inside_word = false ;
54405459 }
54415460 else
54425461 {
5443- word_width += char_width;
5444- if (inside_word )
5462+ // End span: '.X' unless X is a digit
5463+ if (prev_type == ImWcharClass_Punct && curr_type != ImWcharClass_Punct && !(c >= ' 0 ' && c <= ' 9 ' ) )
54455464 {
5446- word_end = next_s;
5465+ span_end = s;
5466+ line_width += span_width + blank_width;
5467+ span_width = blank_width = 0 .0f ;
54475468 }
5448- else
5469+ // End span: 'A ' or '. '
5470+ else if (prev_type == ImWcharClass_Blank && keep_blanks)
54495471 {
5450- prev_word_end = word_end;
5451- line_width += word_width + blank_width;
5452- if ((flags & ImDrawTextFlags_WrapKeepBlanks) && line_width <= wrap_width)
5453- prev_word_end = s;
5454- word_width = blank_width = 0 .0f ;
5472+ span_end = s;
5473+ line_width += span_width + blank_width;
5474+ span_width = blank_width = 0 .0f ;
54555475 }
5456-
5457- // Allow wrapping after punctuation.
5458- inside_word = (c != ' .' && c != ' ,' && c != ' ;' && c != ' !' && c != ' ?' && c != ' \" ' && c != 0x3001 && c != 0x3002 );
5476+ span_width += char_width;
54595477 }
54605478
5461- // We ignore blank width at the end of the line (they can be skipped)
5462- if (line_width + word_width > wrap_width)
5479+ if (span_width + blank_width + line_width > wrap_width)
54635480 {
5464- // Words that cannot possibly fit within an entire line will be cut anywhere.
5465- if (word_width < wrap_width)
5466- s = prev_word_end ? prev_word_end : word_end;
5467- break ;
5481+ if (span_width + blank_width > wrap_width)
5482+ break ;
5483+ // FIXME: Narrow wrapping e.g. "A quick brown" -> "Quic|k br|own", would require knowing if span is going to be longer than wrap_width.
5484+ // if (span_width > wrap_width && !is_blank && !was_blank)
5485+ // return s;
5486+ return span_end;
54685487 }
54695488
5489+ prev_type = curr_type;
54705490 s = next_s;
54715491 }
54725492
0 commit comments