diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index 28519ddd0..c1609ee84 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -235,11 +235,6 @@ void DrawFont(const Surface &out, Point position, ClxSprite glyph, text_color co } } -bool IsWhitespace(char32_t c) -{ - return IsAnyOf(c, U' ', U' ', ZWSP); -} - bool IsFullWidthPunct(char32_t c) { return IsAnyOf(c, U',', U'、', U'。', U'?', U'!'); @@ -641,7 +636,7 @@ std::string WordWrapString(std::string_view text, unsigned width, GameFontTables lineWidth += (*currentFont.sprite)[frame].width() + spacing; } - if (IsWhitespace(codepoint)) { + if (IsBreakableWhitespace(codepoint)) { lastBreakablePos = remaining.data() - begin - codepointLen; lastBreakableLen = codepointLen; continue; @@ -833,4 +828,9 @@ uint8_t PentSpn2Spin() return (SDL_GetTicks() / 50) % 8; } +bool IsBreakableWhitespace(char32_t c) +{ + return IsAnyOf(c, U' ', U' ', ZWSP); +} + } // namespace devilution diff --git a/Source/engine/render/text_render.hpp b/Source/engine/render/text_render.hpp index 234d3ec61..0a04698b6 100644 --- a/Source/engine/render/text_render.hpp +++ b/Source/engine/render/text_render.hpp @@ -262,4 +262,7 @@ inline void DrawStringWithColors(const Surface &out, std::string_view fmt, std:: uint8_t PentSpn2Spin(); void UnloadFonts(); +/** @brief Whether this character can be substituted by a newline when word-wrapping. */ +bool IsBreakableWhitespace(char32_t c); + } // namespace devilution diff --git a/Source/panels/console.cpp b/Source/panels/console.cpp index 7ef9ac01c..b342b8c14 100644 --- a/Source/panels/console.cpp +++ b/Source/panels/console.cpp @@ -209,18 +209,24 @@ void DrawAutocompleteSuggestions(const Surface &out, const std::vector(Prompt.size()); - bool prevIsOriginalNewline = false; + bool prevIsOriginalWhitespace = false; const Surface inputTextSurface = out.subregion(rect.position.x, rect.position.y, rect.size.width, rect.size.height); std::optional renderedCursorPositionOut; for (const std::string_view line : SplitByChar(wrappedInputText, '\n')) { const int lineCursorPosition = static_cast(ConsoleInputCursor.position) - numRendered; - const bool isCursorOnPrevLine = lineCursorPosition == 0 && !prevIsOriginalNewline && numRendered > 0; + const bool isCursorOnPrevLine = lineCursorPosition == 0 && !prevIsOriginalWhitespace && numRendered > 0; DrawString( inputTextSurface, line, { 0, lineY }, TextRenderOptions { @@ -231,10 +237,18 @@ void DrawInputText(const Surface &out, .renderedCursorPositionOut = &renderedCursorPositionOut }); lineY += LineHeight; numRendered += static_cast(line.size()); - prevIsOriginalNewline = static_cast(numRendered) < originalInputText.size() - && originalInputText[static_cast(numRendered)] == '\n'; - if (prevIsOriginalNewline) - ++numRendered; + + size_t whitespaceLength; + prevIsOriginalWhitespace = static_cast(numRendered) < originalInputText.size() + && IsBreakStart(originalInputText.substr(static_cast(numRendered)), whitespaceLength); + if (prevIsOriginalWhitespace) { + // If we replaced an original whitespace with a newline, count the original whitespace as rendered. + numRendered += static_cast(whitespaceLength); + } + if (numRendered < 0 && IsBreakStart(Prompt.substr(Prompt.size() - static_cast(-numRendered)), whitespaceLength)) { + // If we replaced the whitespace in a prompt with a newline, count it as rendered. + numRendered += static_cast(whitespaceLength); + } } if (!AutocompleteSuggestions.empty() && renderedCursorPositionOut.has_value()) {