Browse Source

Console input: Word-wrap-related cursor fixes

1. Handle word breaks in the prompt, e.g. `"> " -> "\n"`.
2. Handle word breaks in the original newline for all whitespace, not just `"\n"`.
pull/6790/head
Gleb Mazovetskiy 2 years ago
parent
commit
e02dc1f9dd
  1. 12
      Source/engine/render/text_render.cpp
  2. 3
      Source/engine/render/text_render.hpp
  3. 26
      Source/panels/console.cpp

12
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

3
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

26
Source/panels/console.cpp

@ -209,18 +209,24 @@ void DrawAutocompleteSuggestions(const Surface &out, const std::vector<LuaAutoco
}
}
bool IsBreakStart(std::string_view str, size_t &breakLen)
{
const char32_t cp = DecodeFirstUtf8CodePoint(str, &breakLen);
return cp == U'\n' || IsBreakableWhitespace(cp);
}
void DrawInputText(const Surface &out,
Rectangle rect, std::string_view originalInputText, std::string_view wrappedInputText)
{
int lineY = 0;
int numRendered = -static_cast<int>(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<Point> renderedCursorPositionOut;
for (const std::string_view line : SplitByChar(wrappedInputText, '\n')) {
const int lineCursorPosition = static_cast<int>(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<int>(line.size());
prevIsOriginalNewline = static_cast<size_t>(numRendered) < originalInputText.size()
&& originalInputText[static_cast<size_t>(numRendered)] == '\n';
if (prevIsOriginalNewline)
++numRendered;
size_t whitespaceLength;
prevIsOriginalWhitespace = static_cast<size_t>(numRendered) < originalInputText.size()
&& IsBreakStart(originalInputText.substr(static_cast<size_t>(numRendered)), whitespaceLength);
if (prevIsOriginalWhitespace) {
// If we replaced an original whitespace with a newline, count the original whitespace as rendered.
numRendered += static_cast<int>(whitespaceLength);
}
if (numRendered < 0 && IsBreakStart(Prompt.substr(Prompt.size() - static_cast<size_t>(-numRendered)), whitespaceLength)) {
// If we replaced the whitespace in a prompt with a newline, count it as rendered.
numRendered += static_cast<int>(whitespaceLength);
}
}
if (!AutocompleteSuggestions.empty() && renderedCursorPositionOut.has_value()) {

Loading…
Cancel
Save