Fixed a special line wrapping scenario where a trailing space should be removed

This commit is contained in:
Leon Styhre 2024-08-20 17:16:19 +02:00
parent 3552c6e228
commit 3f2f8f9b57
2 changed files with 35 additions and 11 deletions

View file

@ -292,7 +292,7 @@ TextCache* Font::buildTextCache(const std::string& text,
std::vector<ShapeSegment> segmentsHB; std::vector<ShapeSegment> segmentsHB;
shapeText(text, segmentsHB); shapeText(text, segmentsHB);
wrapText(segmentsHB, maxLength, height, lineSpacing, multiLine); wrapText(segmentsHB, maxLength, height, lineSpacing, multiLine, needGlyphsPos);
size_t segmentIndex {0}; size_t segmentIndex {0};
float x {0.0f}; float x {0.0f};
@ -370,6 +370,13 @@ TextCache* Font::buildTextCache(const std::string& text,
isNewLine = true; isNewLine = true;
continue; continue;
} }
else if (segment.glyphIndexes[cursor].second == -1) {
// Special scenario where a space glyph at the end of a segment should be omitted,
// in which case it's set to -1 advance in wrapText(). We can't set it to 0 as
// that's actually a valid value for some fonts such as when having an apostrophe
// followed by a comma.
continue;
}
if (segment.doShape) if (segment.doShape)
glyph = glyph =
@ -496,7 +503,7 @@ float Font::getSizeReference()
} }
} }
mSizeReference = advance; mSizeReference = static_cast<float>(advance);
return mSizeReference; return mSizeReference;
} }
@ -745,7 +752,8 @@ void Font::wrapText(std::vector<ShapeSegment>& segmentsHB,
float maxLength, float maxLength,
const float maxHeight, const float maxHeight,
const float lineSpacing, const float lineSpacing,
const bool multiLine) const bool multiLine,
const bool needGlyphsPos)
{ {
std::vector<ShapeSegment> resultSegments; std::vector<ShapeSegment> resultSegments;
@ -773,8 +781,8 @@ void Font::wrapText(std::vector<ShapeSegment>& segmentsHB,
(segmentsHB.size() == 1 && segmentsHB.front().shapedWidth <= maxLength)) (segmentsHB.size() == 1 && segmentsHB.front().shapedWidth <= maxLength))
return; return;
// Additionally this captures shorter multi-segment text that does not require more involved // Additionally this captures shorter single-line multi-segment text that does not require
// line breaking or abbreviations. // more involved line breaking or abbreviations.
float combinedWidth {0.0f}; float combinedWidth {0.0f};
bool hasNewline {false}; bool hasNewline {false};
for (auto& segment : segmentsHB) { for (auto& segment : segmentsHB) {
@ -911,9 +919,24 @@ void Font::wrapText(std::vector<ShapeSegment>& segmentsHB,
newShapedWidth -= newSegment.glyphIndexes.back().second; newShapedWidth -= newSegment.glyphIndexes.back().second;
newSegment.glyphIndexes.pop_back(); newSegment.glyphIndexes.pop_back();
} }
// If all glyphs were removed and the last character of the previous
// segment was a space, then set its advance to -1 so it gets excluded
// in buildTextCache(). That is, unless needGlyphPos is true as that
// means the text is needed for TextEditComponent and should therefore
// not be altered.
if (!needGlyphsPos && newSegment.glyphIndexes.empty() &&
!resultSegments.empty()) {
if (resultSegments.back().glyphIndexes.back().first ==
resultSegments.back().spaceChar) {
resultSegments.back().shapedWidth -=
resultSegments.back().glyphIndexes.back().second;
resultSegments.back().glyphIndexes.back().second = -1;
}
}
} }
newSegment.length = newSegment.glyphIndexes.size(); newSegment.length =
static_cast<unsigned int>(newSegment.glyphIndexes.size());
newSegment.shapedWidth = newShapedWidth; newSegment.shapedWidth = newShapedWidth;
if (newSegment.glyphIndexes.size() != 0) if (newSegment.glyphIndexes.size() != 0)
@ -974,7 +997,7 @@ void Font::wrapText(std::vector<ShapeSegment>& segmentsHB,
else else
lastSegmentSpace = false; lastSegmentSpace = false;
newSegment.length = newSegment.glyphIndexes.size(); newSegment.length = static_cast<unsigned int>(newSegment.glyphIndexes.size());
newSegment.shapedWidth = newShapedWidth; newSegment.shapedWidth = newShapedWidth;
if (newSegment.glyphIndexes.size() != 0) if (newSegment.glyphIndexes.size() != 0)

View file

@ -166,7 +166,7 @@ private:
FontTexture* texture; FontTexture* texture;
hb_font_t* fontHB; hb_font_t* fontHB;
glm::vec2 texPos; glm::vec2 texPos;
glm::vec2 texSize; // In texels. glm::vec2 texSize;
glm::ivec2 advance; glm::ivec2 advance;
glm::ivec2 bearing; glm::ivec2 bearing;
int rows; int rows;
@ -217,9 +217,10 @@ private:
// Inserts newlines to make text wrap properly and also abbreviates when necessary. // Inserts newlines to make text wrap properly and also abbreviates when necessary.
void wrapText(std::vector<ShapeSegment>& segmentsHB, void wrapText(std::vector<ShapeSegment>& segmentsHB,
float maxLength, float maxLength,
const float maxHeight = 0.0f, const float maxHeight,
const float lineSpacing = 1.5f, const float lineSpacing,
const bool multiLine = false); const bool multiLine,
const bool needGlyphsPos);
// Completely recreate the texture data for all glyph atlas entries. // Completely recreate the texture data for all glyph atlas entries.
void rebuildTextures(); void rebuildTextures();