void Text::UpdateCharLocations() { // Remember the font face to see if it's still valid when it's time to render FontFace* face = font_ ? font_->GetFace(fontSize_) : nullptr; if (!face) return; fontFace_ = face; auto rowHeight = RoundToInt(rowSpacing_ * rowHeight_); // Store position & size of each character, and locations per texture page unsigned numChars = unicodeText_.size(); charLocations_.resize(numChars + 1); pageGlyphLocations_.resize(face->GetTextures().size()); for (unsigned i = 0; i < pageGlyphLocations_.size(); ++i) pageGlyphLocations_[i].clear(); IntVector2 offset = font_->GetTotalGlyphOffset(fontSize_); unsigned rowIndex = 0; unsigned lastFilled = 0; float x = Round(GetRowStartPosition(rowIndex) + offset.x_); float y = Round(offset.y_); for (unsigned i = 0; i < printText_.size(); ++i) { CharLocation loc; loc.position_ = Vector2(x, y); unsigned c = printText_[i]; if (c != '\n') { const FontGlyph* glyph = face->GetGlyph(c); loc.size_ = Vector2(glyph ? glyph->advanceX_ : 0, rowHeight_); if (glyph) { // Store glyph's location for rendering. Verify that glyph page is valid if (glyph->page_ < pageGlyphLocations_.size()) pageGlyphLocations_[glyph->page_].push_back(GlyphLocation(x, y, glyph)); x += glyph->advanceX_; if (i < printText_.size() - 1) x += face->GetKerning(c, printText_[i + 1]); } } else { loc.size_ = Vector2::ZERO; x = GetRowStartPosition(++rowIndex); y += rowHeight; } if (lastFilled > printToText_[i]) lastFilled = printToText_[i]; // Fill gaps in case characters were skipped from printing for (unsigned j = lastFilled; j <= printToText_[i]; ++j) charLocations_[j] = loc; lastFilled = printToText_[i] + 1; } // Store the ending position charLocations_[numChars].position_ = Vector2(x, y); charLocations_[numChars].size_ = Vector2::ZERO; charLocationsDirty_ = false; }
void Text::UpdateText(bool onResize) { rowWidths_.clear(); printText_.clear(); if (font_) { FontFace* face = font_->GetFace(fontSize_); if (!face) return; rowHeight_ = face->GetRowHeight(); int width = 0; int height = 0; int rowWidth = 0; auto rowHeight = RoundToInt(rowSpacing_ * rowHeight_); // First see if the text must be split up if (!wordWrap_) { printText_ = unicodeText_; printToText_.resize(printText_.size()); for (unsigned i = 0; i < printText_.size(); ++i) printToText_[i] = i; } else { int maxWidth = GetWidth(); unsigned nextBreak = 0; unsigned lineStart = 0; printToText_.clear(); for (unsigned i = 0; i < unicodeText_.size(); ++i) { unsigned j; unsigned c = unicodeText_[i]; if (c != '\n') { bool ok = true; if (nextBreak <= i) { int futureRowWidth = rowWidth; for (j = i; j < unicodeText_.size(); ++j) { unsigned d = unicodeText_[j]; if (d == ' ' || d == '\n') { nextBreak = j; break; } const FontGlyph* glyph = face->GetGlyph(d); if (glyph) { futureRowWidth += glyph->advanceX_; if (j < unicodeText_.size() - 1) futureRowWidth += face->GetKerning(d, unicodeText_[j + 1]); } if (d == '-' && futureRowWidth <= maxWidth) { nextBreak = j + 1; break; } if (futureRowWidth > maxWidth) { ok = false; break; } } } if (!ok) { // If did not find any breaks on the line, copy until j, or at least 1 char, to prevent infinite loop if (nextBreak == lineStart) { while (i < j) { printText_.push_back(unicodeText_[i]); printToText_.push_back(i); ++i; } } // Eliminate spaces that have been copied before the forced break while (printText_.size() && printText_.back() == ' ') { printText_.pop_back(); printToText_.pop_back(); } printText_.push_back('\n'); printToText_.push_back(Min(i, unicodeText_.size() - 1)); rowWidth = 0; nextBreak = lineStart = i; } if (i < unicodeText_.size()) { // When copying a space, position is allowed to be over row width c = unicodeText_[i]; const FontGlyph* glyph = face->GetGlyph(c); if (glyph) { rowWidth += glyph->advanceX_; if (i < unicodeText_.size() - 1) rowWidth += face->GetKerning(c, unicodeText_[i + 1]); } if (rowWidth <= maxWidth) { printText_.push_back(c); printToText_.push_back(i); } } } else { printText_.push_back('\n'); printToText_.push_back(Min(i, unicodeText_.size() - 1)); rowWidth = 0; nextBreak = lineStart = i; } } } rowWidth = 0; for (unsigned i = 0; i < printText_.size(); ++i) { unsigned c = printText_[i]; if (c != '\n') { const FontGlyph* glyph = face->GetGlyph(c); if (glyph) { rowWidth += glyph->advanceX_; if (i < printText_.size() - 1) rowWidth += face->GetKerning(c, printText_[i + 1]); } } else { width = Max(width, rowWidth); height += rowHeight; rowWidths_.push_back(rowWidth); rowWidth = 0; } } if (rowWidth) { width = Max(width, rowWidth); height += rowHeight; rowWidths_.push_back(rowWidth); } // Set at least one row height even if text is empty if (!height) height = rowHeight; // Set minimum and current size according to the text size, but respect fixed width if set if (!IsFixedWidth()) { if (wordWrap_) SetMinWidth(0); else { SetMinWidth(width); SetWidth(width); } } SetFixedHeight(height); charLocationsDirty_ = true; } else { // No font, nothing to render pageGlyphLocations_.clear(); } // If wordwrap is on, parent may need layout update to correct for overshoot in size. However, do not do this when the // update is a response to resize, as that could cause infinite recursion if (wordWrap_ && !onResize) { UIElement* parent = GetParent(); if (parent && parent->GetLayoutMode() != LM_FREE) parent->UpdateLayout(); } }