Fix text after an unclosed ^ tag not wrapping
This commit is contained in:
parent
c24fc5aeaf
commit
dd67777238
@ -1,7 +1,5 @@
|
||||
{
|
||||
"movementThreshold" : 0.5,
|
||||
"bubbleOffset" : [0, 1.875],
|
||||
"textStyle" : {
|
||||
"backDirectives" : "?border=1;000a;0000?border=1;000a;0004"
|
||||
}
|
||||
"textStyle" : { "backDirectives" : "?border=1;111;1110?border=1;111;1117" }
|
||||
}
|
@ -31,21 +31,20 @@ TextStyle& TextStyle::loadJson(Json const& config) {
|
||||
}
|
||||
|
||||
namespace Text {
|
||||
std::string const AllEsc = strf("{:c}{:c}", CmdEsc, StartEsc);
|
||||
std::string const AllEscEnd = strf("{:c}{:c}{:c}", CmdEsc, StartEsc, EndEsc);
|
||||
|
||||
static auto stripEscapeRegex = std::regex(strf("\\{:c}[^;]*{:c}", CmdEsc, EndEsc));
|
||||
String stripEscapeCodes(String const& s) {
|
||||
return std::regex_replace(s.utf8(), stripEscapeRegex, "");
|
||||
}
|
||||
|
||||
static std::string escapeChars = strf("{:c}{:c}", CmdEsc, StartEsc);
|
||||
|
||||
bool processText(StringView text, TextCallback textFunc, CommandsCallback commandsFunc, bool includeCommandSides) {
|
||||
std::string_view escChars(escapeChars);
|
||||
|
||||
std::string_view str = text.utf8();
|
||||
while (true) {
|
||||
size_t escape = str.find_first_of(escChars);
|
||||
size_t escape = str.find_first_of(AllEsc);
|
||||
if (escape != NPos) {
|
||||
escape = str.find_first_not_of(escChars, escape) - 1; // jump to the last ^
|
||||
escape = str.find_first_not_of(AllEsc, escape) - 1; // jump to the last ^
|
||||
|
||||
size_t end = str.find_first_of(EndEsc, escape);
|
||||
if (end != NPos) {
|
||||
|
@ -30,6 +30,8 @@ namespace Text {
|
||||
unsigned char const EndEsc = ';';
|
||||
unsigned char const CmdEsc = '^';
|
||||
unsigned char const SpecialCharLimit = ' ';
|
||||
extern std::string const AllEsc;
|
||||
extern std::string const AllEscEnd;
|
||||
|
||||
String stripEscapeCodes(String const& s);
|
||||
inline bool isEscapeCode(Utf32Type c) { return c == CmdEsc || c == StartEsc; }
|
||||
|
@ -411,7 +411,7 @@ void Object::render(RenderCallback* renderCallback) {
|
||||
renderCallback->addAudios(m_networkedAnimatorDynamicTarget.pullNewAudios());
|
||||
renderCallback->addParticles(m_networkedAnimatorDynamicTarget.pullNewParticles());
|
||||
|
||||
if (m_networkedAnimator->parts().count() > 0) {
|
||||
if (m_networkedAnimator->constParts().size() > 0) {
|
||||
renderCallback->addDrawables(m_networkedAnimator->drawables(position() + m_animationPosition + damageShake()), renderLayer());
|
||||
} else {
|
||||
if (m_orientationIndex != NPos)
|
||||
|
@ -130,34 +130,39 @@ int TextPainter::stringWidth(StringView s, unsigned charLimit) {
|
||||
bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapTextCallback textFunc) {
|
||||
String font = m_renderSettings.font, setFont = font;
|
||||
m_fontTextureGroup.switchFont(font);
|
||||
auto iterator = text.begin(), end = text.end();
|
||||
|
||||
unsigned lines = 0;
|
||||
|
||||
auto it = text.begin();
|
||||
auto end = text.end();
|
||||
|
||||
unsigned linePixelWidth = 0; // How wide is this line so far
|
||||
unsigned lineCharSize = 0; // how many characters in this line ?
|
||||
|
||||
auto escIt = end;
|
||||
unsigned splitWidth = 0; // How wide was the string there ?
|
||||
|
||||
auto lineStartIt = it; // Where does this line start ?
|
||||
auto splitIt = end; // != end if we last saw a place to split the string
|
||||
auto lineStartIterator = iterator, splitIterator = end;
|
||||
unsigned linePixelWidth = 0, splitPixelWidth = 0;
|
||||
size_t commandStart = NPos, commandEnd = NPos;
|
||||
bool finished = true;
|
||||
|
||||
auto slice = [](StringView::const_iterator a, StringView::const_iterator b) -> StringView {
|
||||
const char* aPtr = &*a.base();
|
||||
return StringView(aPtr, &*b.base() - aPtr);
|
||||
};
|
||||
|
||||
while (it != end) {
|
||||
auto character = *it;
|
||||
|
||||
if (Text::isEscapeCode(character))
|
||||
escIt = it;
|
||||
|
||||
if (escIt != end) {
|
||||
if (character == Text::EndEsc) {
|
||||
StringView inner = slice(++escIt, it);
|
||||
while (iterator != end) {
|
||||
auto character = *iterator;
|
||||
finished = false; // assume at least one character if we get here
|
||||
bool noMoreCommands = commandStart != NPos && commandEnd == NPos;
|
||||
if (!noMoreCommands && Text::isEscapeCode(character)) {
|
||||
size_t index = &*iterator.base() - text.utf8Ptr();
|
||||
if (commandStart == NPos) {
|
||||
for (size_t escOrEnd = commandStart = index;
|
||||
(escOrEnd = text.utf8().find_first_of(Text::AllEscEnd, escOrEnd + 1)) != NPos;) {
|
||||
if (text.utf8().at(escOrEnd) != Text::EndEsc)
|
||||
commandStart = escOrEnd;
|
||||
else {
|
||||
commandEnd = escOrEnd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (commandStart == index && commandEnd != NPos) {
|
||||
const char* commandStr = text.utf8Ptr() + ++commandStart;
|
||||
StringView inner(commandStr, commandEnd - commandStart);
|
||||
inner.forEachSplitView(",", [&](StringView command, size_t, size_t) {
|
||||
if (command == "reset") {
|
||||
m_fontTextureGroup.switchFont(font = setFont);
|
||||
@ -167,63 +172,61 @@ bool TextPainter::processWrapText(StringView text, unsigned* wrapWidth, WrapText
|
||||
m_fontTextureGroup.switchFont(font = command.substr(5));
|
||||
}
|
||||
});
|
||||
escIt = end;
|
||||
// jump the iterator to the character after the command
|
||||
iterator = text.utf8().begin() + commandEnd + 1;
|
||||
commandStart = commandEnd = NPos;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
lineCharSize++;
|
||||
} else {
|
||||
lineCharSize++; // assume at least one character if we get here.
|
||||
|
||||
// is this a linefeed / cr / whatever that forces a line split ?
|
||||
if (character == '\n' || character == '\v') {
|
||||
// knock one off the end because we don't render the CR
|
||||
if (!textFunc(slice(lineStartIt, it), lines++))
|
||||
if (!textFunc(slice(lineStartIterator, iterator), lines++))
|
||||
return false;
|
||||
|
||||
lineStartIt = it;
|
||||
++lineStartIt;
|
||||
lineStartIterator = iterator;
|
||||
++lineStartIterator;
|
||||
// next line starts after the CR with no characters in it and no known splits.
|
||||
lineCharSize = linePixelWidth = 0;
|
||||
splitIt = end;
|
||||
linePixelWidth = 0;
|
||||
splitIterator = end;
|
||||
finished = true;
|
||||
} else {
|
||||
int charWidth = glyphWidth(character);
|
||||
int characterWidth = glyphWidth(character);
|
||||
// is it a place where we might want to split the line ?
|
||||
if (character == ' ' || character == '\t') {
|
||||
splitIt = it;
|
||||
splitWidth = linePixelWidth + charWidth; // the width of the string at
|
||||
// the split point, i.e. after the space.
|
||||
splitIterator = iterator;
|
||||
splitPixelWidth = linePixelWidth + characterWidth;
|
||||
}
|
||||
|
||||
// would the line be too long if we render this next character ?
|
||||
if (wrapWidth && (linePixelWidth + charWidth) > *wrapWidth) {
|
||||
if (wrapWidth && (linePixelWidth + characterWidth) > *wrapWidth) {
|
||||
// did we find somewhere to split the line ?
|
||||
if (splitIt != end) {
|
||||
if (!textFunc(slice(lineStartIt, splitIt), lines++))
|
||||
if (splitIterator != end) {
|
||||
if (!textFunc(slice(lineStartIterator, splitIterator), lines++))
|
||||
return false;
|
||||
unsigned stringWidth = linePixelWidth - splitWidth;
|
||||
linePixelWidth = stringWidth + charWidth; // and is as wide as the bit after the space.
|
||||
lineStartIt = ++splitIt;
|
||||
splitIt = end;
|
||||
// do not include the split character on the next line
|
||||
unsigned stringWidth = linePixelWidth - splitPixelWidth;
|
||||
linePixelWidth = stringWidth + characterWidth;
|
||||
lineStartIterator = ++splitIterator;
|
||||
splitIterator = end;
|
||||
} else {
|
||||
if (!textFunc(slice(lineStartIt, it), lines++))
|
||||
if (!textFunc(slice(lineStartIterator, iterator), lines++))
|
||||
return false;
|
||||
lineStartIt = it; // include that character on the next line.
|
||||
lineCharSize = 1; // next line has that character in
|
||||
linePixelWidth = charWidth; // and is as wide as that character
|
||||
// include that character on the next line
|
||||
lineStartIterator = iterator;
|
||||
linePixelWidth = characterWidth;
|
||||
finished = false;
|
||||
}
|
||||
} else {
|
||||
linePixelWidth += charWidth;
|
||||
}
|
||||
linePixelWidth += characterWidth;
|
||||
}
|
||||
}
|
||||
|
||||
++it;
|
||||
++iterator;
|
||||
};
|
||||
|
||||
// if we hit the end of the string before hitting the end of the line.
|
||||
if (lineCharSize > 0)
|
||||
return textFunc(slice(lineStartIt, end), lines);
|
||||
|
||||
return true;
|
||||
// if we hit the end of the string before hitting the end of the line
|
||||
return finished || textFunc(slice(lineStartIterator, end), lines);
|
||||
}
|
||||
|
||||
List<StringView> TextPainter::wrapTextViews(StringView s, Maybe<unsigned> wrapWidth) {
|
||||
@ -293,18 +296,20 @@ void TextPainter::setProcessingDirectives(StringView directives, bool back) {
|
||||
}
|
||||
|
||||
void TextPainter::setFont(String const& font) {
|
||||
m_renderSettings.font = font;
|
||||
m_fontTextureGroup.switchFont(m_renderSettings.font = font);
|
||||
}
|
||||
|
||||
TextStyle& TextPainter::setTextStyle(TextStyle const& textStyle) {
|
||||
TextStyle& style = m_renderSettings = textStyle;
|
||||
modifyDirectives(style.directives);
|
||||
modifyDirectives(style.backDirectives);
|
||||
m_fontTextureGroup.switchFont(style.font);
|
||||
return style;
|
||||
}
|
||||
|
||||
void TextPainter::clearTextStyle() {
|
||||
m_renderSettings = m_defaultRenderSettings;
|
||||
m_fontTextureGroup.switchFont(m_renderSettings.font);
|
||||
}
|
||||
|
||||
void TextPainter::addFont(FontPtr const& font, String const& name) {
|
||||
@ -343,17 +348,18 @@ void TextPainter::applyCommands(StringView unsplitCommands) {
|
||||
try {
|
||||
if (command == "reset") {
|
||||
m_renderSettings = m_savedRenderSettings;
|
||||
m_fontTextureGroup.switchFont(m_renderSettings.font);
|
||||
} else if (command == "set") {
|
||||
m_savedRenderSettings = m_renderSettings;
|
||||
} else if (command.beginsWith("shadow")) {
|
||||
if (command.utf8Size() == 6)
|
||||
m_renderSettings.shadow = Color::Black.toRgba();
|
||||
else if (command[6] == '=')
|
||||
else if (command.utf8()[6] == '=')
|
||||
m_renderSettings.shadow = Color(command.substr(7)).toRgba();
|
||||
} else if (command == "noshadow") {
|
||||
m_renderSettings.shadow = Color::Clear.toRgba();
|
||||
} else if (command.beginsWith("font=")) {
|
||||
m_renderSettings.font = command.substr(5);
|
||||
setFont(m_renderSettings.font = command.substr(5));
|
||||
} else if (command.beginsWith("directives=")) {
|
||||
setProcessingDirectives(command.substr(11));
|
||||
} else if (command.beginsWith("backdirectives=")) {
|
||||
@ -404,6 +410,7 @@ RectF TextPainter::doRenderText(StringView s, TextPositioning const& position, b
|
||||
}
|
||||
|
||||
m_renderSettings = std::move(backup);
|
||||
m_fontTextureGroup.switchFont(m_renderSettings.font);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
@ -455,7 +462,6 @@ RectF TextPainter::doRenderLine(StringView text, TextPositioning const& position
|
||||
RectF TextPainter::doRenderGlyph(String::Char c, TextPositioning const& position, bool reallyRender) {
|
||||
if (c == '\n' || c == '\v' || c == '\r')
|
||||
return RectF();
|
||||
m_fontTextureGroup.switchFont(m_renderSettings.font);
|
||||
|
||||
int width = glyphWidth(c);
|
||||
// Offset left by width if right anchored.
|
||||
|
Loading…
Reference in New Issue
Block a user