|
|
|
|
@ -437,6 +437,88 @@ int GetLineStartX(UiFlags flags, const Rectangle &rect, int lineWidth)
|
|
|
|
|
return rect.position.x; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void DrawLine( |
|
|
|
|
const Surface &out, |
|
|
|
|
std::string_view text, |
|
|
|
|
Point characterPosition, |
|
|
|
|
Rectangle rect, |
|
|
|
|
UiFlags flags, |
|
|
|
|
int curSpacing, |
|
|
|
|
GameFontTables size, |
|
|
|
|
text_color color, |
|
|
|
|
bool outline, |
|
|
|
|
const TextRenderOptions &opts, |
|
|
|
|
size_t lineStartPos, |
|
|
|
|
int totalWidth) |
|
|
|
|
{ |
|
|
|
|
CurrentFont currentFont; |
|
|
|
|
|
|
|
|
|
std::string_view lineCopy = text; |
|
|
|
|
|
|
|
|
|
size_t currentPos = 0; |
|
|
|
|
|
|
|
|
|
size_t cpLen; |
|
|
|
|
|
|
|
|
|
const auto maybeDrawCursor = [&]() { |
|
|
|
|
const auto byteIndex = static_cast<int>(lineStartPos + currentPos); |
|
|
|
|
Point position = characterPosition; |
|
|
|
|
if (opts.cursorPosition == byteIndex) { |
|
|
|
|
if (GetAnimationFrame(2, 500) != 0 || opts.cursorStatic) { |
|
|
|
|
FontStack baseFont = LoadFont(size, color, 0); |
|
|
|
|
if (baseFont.has_value()) { |
|
|
|
|
DrawFont(out, position, baseFont.glyph('|'), color, outline); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (opts.renderedCursorPositionOut != nullptr) { |
|
|
|
|
*opts.renderedCursorPositionOut = position; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Start from the beginning of the line
|
|
|
|
|
characterPosition.x = GetLineStartX(flags, rect, totalWidth); |
|
|
|
|
|
|
|
|
|
while (!lineCopy.empty()) { |
|
|
|
|
char32_t c = DecodeFirstUtf8CodePoint(lineCopy, &cpLen); |
|
|
|
|
if (c == Utf8DecodeError) break; |
|
|
|
|
if (c == ZWSP) { |
|
|
|
|
lineCopy.remove_prefix(cpLen); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!currentFont.load(size, color, c)) { |
|
|
|
|
c = U'?'; |
|
|
|
|
if (!currentFont.load(size, color, c)) { |
|
|
|
|
app_fatal("Missing fonts"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
const uint8_t frame = c & 0xFF; |
|
|
|
|
|
|
|
|
|
const ClxSprite glyph = currentFont.glyph(frame); |
|
|
|
|
const int charWidth = glyph.width(); |
|
|
|
|
|
|
|
|
|
const auto byteIndex = static_cast<int>(lineStartPos + currentPos); |
|
|
|
|
|
|
|
|
|
// Draw highlight
|
|
|
|
|
if (byteIndex >= opts.highlightRange.begin && byteIndex < opts.highlightRange.end) { |
|
|
|
|
const bool lastInRange = static_cast<int>(byteIndex + cpLen) == opts.highlightRange.end; |
|
|
|
|
FillRect(out, characterPosition.x, characterPosition.y, |
|
|
|
|
glyph.width() + (lastInRange ? 0 : curSpacing), glyph.height(), |
|
|
|
|
opts.highlightColor); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DrawFont(out, characterPosition, glyph, color, outline); |
|
|
|
|
maybeDrawCursor(); |
|
|
|
|
|
|
|
|
|
// Move to the next position
|
|
|
|
|
characterPosition.x += charWidth + curSpacing; |
|
|
|
|
currentPos += cpLen; |
|
|
|
|
lineCopy.remove_prefix(cpLen); |
|
|
|
|
} |
|
|
|
|
assert(currentPos == text.size()); |
|
|
|
|
maybeDrawCursor(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect, Point &characterPosition, |
|
|
|
|
int lineWidth, int charactersInLine, int rightMargin, int bottomMargin, GameFontTables size, text_color color, bool outline, |
|
|
|
|
TextRenderOptions &opts) |
|
|
|
|
@ -455,19 +537,26 @@ uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect,
|
|
|
|
|
std::string_view remaining = text; |
|
|
|
|
size_t cpLen; |
|
|
|
|
|
|
|
|
|
const auto maybeDrawCursor = [&]() { |
|
|
|
|
if (opts.cursorPosition == static_cast<int>(text.size() - remaining.size())) { |
|
|
|
|
Point position = characterPosition; |
|
|
|
|
MaybeWrap(position, 2, rightMargin, position.x, opts.lineHeight); |
|
|
|
|
if (GetAnimationFrame(2, 500) != 0) { |
|
|
|
|
FontStack baseFont = LoadFont(size, color, 0); |
|
|
|
|
if (baseFont.has_value()) { |
|
|
|
|
DrawFont(out, position, baseFont.glyph('|'), color, outline); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (opts.renderedCursorPositionOut != nullptr) { |
|
|
|
|
*opts.renderedCursorPositionOut = position; |
|
|
|
|
} |
|
|
|
|
// Track line boundaries
|
|
|
|
|
size_t lineStartPos = 0; |
|
|
|
|
size_t lineEndPos = 0; |
|
|
|
|
|
|
|
|
|
const auto drawLine = [&]() { |
|
|
|
|
std::string_view lineText = text.substr(lineStartPos, lineEndPos - lineStartPos); |
|
|
|
|
if (!lineText.empty()) { |
|
|
|
|
DrawLine( |
|
|
|
|
out, |
|
|
|
|
lineText, |
|
|
|
|
characterPosition, |
|
|
|
|
rect, |
|
|
|
|
opts.flags, |
|
|
|
|
curSpacing, |
|
|
|
|
size, |
|
|
|
|
color, |
|
|
|
|
outline, |
|
|
|
|
opts, |
|
|
|
|
lineStartPos, |
|
|
|
|
lineWidth); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
@ -487,8 +576,10 @@ uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect,
|
|
|
|
|
const uint8_t frame = next & 0xFF; |
|
|
|
|
const uint16_t width = currentFont.glyph(frame).width(); |
|
|
|
|
if (next == U'\n' || characterPosition.x + width > rightMargin) { |
|
|
|
|
if (next == '\n') |
|
|
|
|
maybeDrawCursor(); |
|
|
|
|
lineEndPos = text.size() - remaining.size(); |
|
|
|
|
|
|
|
|
|
drawLine(); |
|
|
|
|
|
|
|
|
|
const int nextLineY = characterPosition.y + opts.lineHeight; |
|
|
|
|
if (nextLineY >= bottomMargin) |
|
|
|
|
break; |
|
|
|
|
@ -506,26 +597,26 @@ uint32_t DoDrawString(const Surface &out, std::string_view text, Rectangle rect,
|
|
|
|
|
} |
|
|
|
|
characterPosition.x = GetLineStartX(opts.flags, rect, lineWidth); |
|
|
|
|
|
|
|
|
|
// Start a new line
|
|
|
|
|
lineStartPos = next == U'\n' ? (text.size() - remaining.size() + cpLen) : (text.size() - remaining.size()); |
|
|
|
|
lineEndPos = lineStartPos; |
|
|
|
|
|
|
|
|
|
if (next == U'\n') |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const ClxSprite glyph = currentFont.glyph(frame); |
|
|
|
|
const auto byteIndex = static_cast<int>(text.size() - remaining.size()); |
|
|
|
|
|
|
|
|
|
// Draw highlight
|
|
|
|
|
if (byteIndex >= opts.highlightRange.begin && byteIndex < opts.highlightRange.end) { |
|
|
|
|
const bool lastInRange = static_cast<int>(byteIndex + cpLen) == opts.highlightRange.end; |
|
|
|
|
FillRect(out, characterPosition.x, characterPosition.y, |
|
|
|
|
glyph.width() + (lastInRange ? 0 : curSpacing), glyph.height(), |
|
|
|
|
opts.highlightColor); |
|
|
|
|
} |
|
|
|
|
// Update end position as we add characters
|
|
|
|
|
lineEndPos = text.size() - remaining.size() + cpLen; |
|
|
|
|
|
|
|
|
|
DrawFont(out, characterPosition, glyph, color, outline); |
|
|
|
|
maybeDrawCursor(); |
|
|
|
|
// Update position for the next character
|
|
|
|
|
characterPosition.x += width + curSpacing; |
|
|
|
|
} |
|
|
|
|
maybeDrawCursor(); |
|
|
|
|
|
|
|
|
|
// Draw any remaining characters in the last line
|
|
|
|
|
if (lineStartPos < lineEndPos) { |
|
|
|
|
drawLine(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return static_cast<uint32_t>(remaining.data() - text.data()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|