@@ -437,6 +437,88 @@ int GetLineStartX(UiFlags flags, const Rectangle &rect, int lineWidth)
437437 return rect.position .x ;
438438}
439439
440+ void DrawLine (
441+ const Surface &out,
442+ std::string_view text,
443+ Point characterPosition,
444+ Rectangle rect,
445+ UiFlags flags,
446+ int curSpacing,
447+ GameFontTables size,
448+ text_color color,
449+ bool outline,
450+ const TextRenderOptions &opts,
451+ size_t lineStartPos,
452+ int totalWidth)
453+ {
454+ CurrentFont currentFont;
455+
456+ std::string_view lineCopy = text;
457+
458+ size_t currentPos = 0 ;
459+
460+ size_t cpLen;
461+
462+ const auto maybeDrawCursor = [&]() {
463+ const auto byteIndex = static_cast <int >(lineStartPos + currentPos);
464+ Point position = characterPosition;
465+ if (opts.cursorPosition == byteIndex) {
466+ if (GetAnimationFrame (2 , 500 ) != 0 || opts.cursorStatic ) {
467+ FontStack baseFont = LoadFont (size, color, 0 );
468+ if (baseFont.has_value ()) {
469+ DrawFont (out, position, baseFont.glyph (' |' ), color, outline);
470+ }
471+ }
472+ if (opts.renderedCursorPositionOut != nullptr ) {
473+ *opts.renderedCursorPositionOut = position;
474+ }
475+ }
476+ };
477+
478+ // Start from the beginning of the line
479+ characterPosition.x = GetLineStartX (flags, rect, totalWidth);
480+
481+ while (!lineCopy.empty ()) {
482+ char32_t c = DecodeFirstUtf8CodePoint (lineCopy, &cpLen);
483+ if (c == Utf8DecodeError) break ;
484+ if (c == ZWSP) {
485+ lineCopy.remove_prefix (cpLen);
486+ continue ;
487+ }
488+
489+ if (!currentFont.load (size, color, c)) {
490+ c = U' ?' ;
491+ if (!currentFont.load (size, color, c)) {
492+ app_fatal (" Missing fonts" );
493+ }
494+ }
495+ const uint8_t frame = c & 0xFF ;
496+
497+ const ClxSprite glyph = currentFont.glyph (frame);
498+ const int charWidth = glyph.width ();
499+
500+ const auto byteIndex = static_cast <int >(lineStartPos + currentPos);
501+
502+ // Draw highlight
503+ if (byteIndex >= opts.highlightRange .begin && byteIndex < opts.highlightRange .end ) {
504+ const bool lastInRange = static_cast <int >(byteIndex + cpLen) == opts.highlightRange .end ;
505+ FillRect (out, characterPosition.x , characterPosition.y ,
506+ glyph.width () + (lastInRange ? 0 : curSpacing), glyph.height (),
507+ opts.highlightColor );
508+ }
509+
510+ DrawFont (out, characterPosition, glyph, color, outline);
511+ maybeDrawCursor ();
512+
513+ // Move to the next position
514+ characterPosition.x += charWidth + curSpacing;
515+ currentPos += cpLen;
516+ lineCopy.remove_prefix (cpLen);
517+ }
518+ assert (currentPos == text.size ());
519+ maybeDrawCursor ();
520+ }
521+
440522uint32_t DoDrawString (const Surface &out, std::string_view text, Rectangle rect, Point &characterPosition,
441523 int lineWidth, int charactersInLine, int rightMargin, int bottomMargin, GameFontTables size, text_color color, bool outline,
442524 TextRenderOptions &opts)
@@ -455,19 +537,26 @@ uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect,
455537 std::string_view remaining = text;
456538 size_t cpLen;
457539
458- const auto maybeDrawCursor = [&]() {
459- if (opts.cursorPosition == static_cast <int >(text.size () - remaining.size ())) {
460- Point position = characterPosition;
461- MaybeWrap (position, 2 , rightMargin, position.x , opts.lineHeight );
462- if (GetAnimationFrame (2 , 500 ) != 0 ) {
463- FontStack baseFont = LoadFont (size, color, 0 );
464- if (baseFont.has_value ()) {
465- DrawFont (out, position, baseFont.glyph (' |' ), color, outline);
466- }
467- }
468- if (opts.renderedCursorPositionOut != nullptr ) {
469- *opts.renderedCursorPositionOut = position;
470- }
540+ // Track line boundaries
541+ size_t lineStartPos = 0 ;
542+ size_t lineEndPos = 0 ;
543+
544+ const auto drawLine = [&]() {
545+ std::string_view lineText = text.substr (lineStartPos, lineEndPos - lineStartPos);
546+ if (!lineText.empty ()) {
547+ DrawLine (
548+ out,
549+ lineText,
550+ characterPosition,
551+ rect,
552+ opts.flags ,
553+ curSpacing,
554+ size,
555+ color,
556+ outline,
557+ opts,
558+ lineStartPos,
559+ lineWidth);
471560 }
472561 };
473562
@@ -487,8 +576,10 @@ uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect,
487576 const uint8_t frame = next & 0xFF ;
488577 const uint16_t width = currentFont.glyph (frame).width ();
489578 if (next == U' \n ' || characterPosition.x + width > rightMargin) {
490- if (next == ' \n ' )
491- maybeDrawCursor ();
579+ lineEndPos = text.size () - remaining.size ();
580+
581+ drawLine ();
582+
492583 const int nextLineY = characterPosition.y + opts.lineHeight ;
493584 if (nextLineY >= bottomMargin)
494585 break ;
@@ -506,26 +597,26 @@ uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect,
506597 }
507598 characterPosition.x = GetLineStartX (opts.flags , rect, lineWidth);
508599
600+ // Start a new line
601+ lineStartPos = next == U' \n ' ? (text.size () - remaining.size () + cpLen) : (text.size () - remaining.size ());
602+ lineEndPos = lineStartPos;
603+
509604 if (next == U' \n ' )
510605 continue ;
511606 }
512607
513- const ClxSprite glyph = currentFont.glyph (frame);
514- const auto byteIndex = static_cast <int >(text.size () - remaining.size ());
515-
516- // Draw highlight
517- if (byteIndex >= opts.highlightRange .begin && byteIndex < opts.highlightRange .end ) {
518- const bool lastInRange = static_cast <int >(byteIndex + cpLen) == opts.highlightRange .end ;
519- FillRect (out, characterPosition.x , characterPosition.y ,
520- glyph.width () + (lastInRange ? 0 : curSpacing), glyph.height (),
521- opts.highlightColor );
522- }
608+ // Update end position as we add characters
609+ lineEndPos = text.size () - remaining.size () + cpLen;
523610
524- DrawFont (out, characterPosition, glyph, color, outline);
525- maybeDrawCursor ();
611+ // Update position for the next character
526612 characterPosition.x += width + curSpacing;
527613 }
528- maybeDrawCursor ();
614+
615+ // Draw any remaining characters in the last line
616+ if (lineStartPos < lineEndPos) {
617+ drawLine ();
618+ }
619+
529620 return static_cast <uint32_t >(remaining.data () - text.data ());
530621}
531622
0 commit comments