fix label font cache

This commit is contained in:
MihailRis 2025-09-30 18:22:39 +03:00
parent c8ba5b5dbb
commit edc6810283
7 changed files with 75 additions and 36 deletions

View File

@ -38,6 +38,10 @@ bool Font::isPrintableChar(uint codepoint) const {
} }
} }
int FontMetrics::calcWidth(std::wstring_view text, size_t offset, size_t length) const {
return std::min(text.length() - offset, length) * glyphInterval;
}
int Font::calcWidth(std::wstring_view text, size_t length) const { int Font::calcWidth(std::wstring_view text, size_t length) const {
return calcWidth(text, 0, length); return calcWidth(text, 0, length);
} }

View File

@ -4,7 +4,9 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "typedefs.hpp" #include "typedefs.hpp"
#include "FontMetics.hpp"
class Texture; class Texture;
class Batch2D; class Batch2D;
@ -90,4 +92,8 @@ public:
) const; ) const;
const Texture* getPage(int page) const; const Texture* getPage(int page) const;
FontMetrics getMetrics() const {
return {lineHeight, yoffset, glyphInterval};
}
}; };

View File

@ -0,0 +1,11 @@
#pragma once
#include <string>
struct FontMetrics {
int lineHeight;
int yoffset;
int glyphInterval = 8;
int calcWidth(std::wstring_view text, size_t offset=0, size_t length=-1) const;
};

View File

@ -11,10 +11,11 @@
using namespace gui; using namespace gui;
void LabelCache::prepare(Font* font, size_t wrapWidth) { void LabelCache::prepare(std::ptrdiff_t fontId, FontMetrics metrics, size_t wrapWidth) {
if (font != this->font) { if (fontId != this->fontId) {
resetFlag = true; resetFlag = true;
this->font = font; this->fontId = fontId;
this->metrics = metrics;
} }
if (wrapWidth != this->wrapWidth) { if (wrapWidth != this->wrapWidth) {
resetFlag = true; resetFlag = true;
@ -41,7 +42,7 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) {
lines.clear(); lines.clear();
lines.push_back(LineScheme {0, false}); lines.push_back(LineScheme {0, false});
if (font == nullptr) { if (fontId == 0) {
wrap = false; wrap = false;
} }
@ -52,7 +53,7 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) {
lines.push_back(LineScheme {i+1, false}); lines.push_back(LineScheme {i+1, false});
len = 0; len = 0;
} else if (i > 0 && i+1 < text.length() && wrap && text[i+1] != L'\n') { } else if (i > 0 && i+1 < text.length() && wrap && text[i+1] != L'\n') {
size_t width = font->calcWidth(text, i-len-1, i-(i-len)+2); size_t width = metrics.calcWidth(text, i-len-1, i-(i-len)+2);
if (width >= wrapWidth) { if (width >= wrapWidth) {
// starting a fake line // starting a fake line
lines.push_back(LineScheme {i+1, true}); lines.push_back(LineScheme {i+1, true});
@ -60,20 +61,20 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) {
} }
} }
} }
if (font != nullptr) { if (fontId != 0) {
int maxWidth = 0; int maxWidth = 0;
for (int i = 0; i < lines.size() - 1; i++) { for (int i = 0; i < lines.size() - 1; i++) {
const auto& next = lines[i + 1]; const auto& next = lines[i + 1];
const auto& cur = lines[i]; const auto& cur = lines[i];
maxWidth = std::max( maxWidth = std::max(
font->calcWidth( metrics.calcWidth(
text.substr(cur.offset, next.offset - cur.offset) text.substr(cur.offset, next.offset - cur.offset)
), ),
maxWidth maxWidth
); );
} }
maxWidth = std::max( maxWidth = std::max(
font->calcWidth( metrics.calcWidth(
text.substr(lines[lines.size() - 1].offset) text.substr(lines[lines.size() - 1].offset)
), ),
maxWidth maxWidth
@ -105,8 +106,8 @@ Label::Label(GUI& gui, const std::wstring& text, std::string fontName)
Label::~Label() = default; Label::~Label() = default;
glm::vec2 Label::calcSize() { glm::vec2 Label::calcSize() {
auto font = cache.font; auto& metrics = cache.metrics;
uint lineHeight = font->getLineHeight(); uint lineHeight = metrics.lineHeight;
if (cache.lines.size() > 1) { if (cache.lines.size() > 1) {
lineHeight *= lineInterval; lineHeight *= lineInterval;
} }
@ -114,12 +115,12 @@ glm::vec2 Label::calcSize() {
if (multiline) { if (multiline) {
return glm::vec2( return glm::vec2(
cache.multilineWidth, cache.multilineWidth,
lineHeight * cache.lines.size() + font->getYOffset() lineHeight * cache.lines.size() + metrics.yoffset
); );
} }
return glm::vec2 ( return glm::vec2 (
cache.font->calcWidth(view), cache.metrics.calcWidth(view),
lineHeight * cache.lines.size() + font->getYOffset() lineHeight * cache.lines.size() + metrics.yoffset
); );
} }
@ -135,7 +136,7 @@ void Label::setText(std::wstring text) {
this->text = std::move(text); this->text = std::move(text);
cache.update(this->text, multiline, textWrap); cache.update(this->text, multiline, textWrap);
if (cache.font && autoresize) { if (cache.fontId != 0 && autoresize) {
setSize(calcSize()); setSize(calcSize());
} }
} }
@ -203,7 +204,11 @@ uint Label::getLinesNumber() const {
void Label::draw(const DrawContext& pctx, const Assets& assets) { void Label::draw(const DrawContext& pctx, const Assets& assets) {
auto batch = pctx.getBatch2D(); auto batch = pctx.getBatch2D();
auto font = assets.get<Font>(fontName); auto font = assets.get<Font>(fontName);
cache.prepare(font, static_cast<size_t>(glm::abs(getSize().x))); cache.prepare(
reinterpret_cast<ptrdiff_t>(font),
font->getMetrics(),
static_cast<size_t>(glm::abs(getSize().x))
);
if (supplier) { if (supplier) {
setText(supplier()); setText(supplier());

View File

@ -3,6 +3,8 @@
#include "UINode.hpp" #include "UINode.hpp"
#include "constants.hpp" #include "constants.hpp"
#include "graphics/core/FontMetics.hpp"
class Font; class Font;
struct FontStylesScheme; struct FontStylesScheme;
@ -13,14 +15,16 @@ namespace gui {
}; };
struct LabelCache { struct LabelCache {
Font* font = nullptr; ptrdiff_t fontId = 0;
FontMetrics metrics;
std::vector<LineScheme> lines; std::vector<LineScheme> lines;
/// @brief Reset cache flag /// @brief Reset cache flag
bool resetFlag = true; bool resetFlag = true;
size_t wrapWidth = -1; size_t wrapWidth = -1;
int multilineWidth = 0; int multilineWidth = 0;
void prepare(Font* font, size_t wrapWidth); void prepare(std::ptrdiff_t fontId, FontMetrics metrics, size_t wrapWidth);
void update(std::wstring_view text, bool multiline, bool wrap); void update(std::wstring_view text, bool multiline, bool wrap);
size_t getTextLineOffset(size_t line) const; size_t getTextLineOffset(size_t line) const;

View File

@ -231,8 +231,6 @@ TextBox::~TextBox() = default;
void TextBox::draw(const DrawContext& pctx, const Assets& assets) { void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
Container::draw(pctx, assets); Container::draw(pctx, assets);
font = assets.get<Font>(label->getFontName());
if (!isFocused()) { if (!isFocused()) {
return; return;
} }
@ -244,7 +242,8 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
auto subctx = pctx.sub(); auto subctx = pctx.sub();
subctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y)); subctx.setScissors(glm::vec4(pos.x, pos.y, size.x, size.y));
const int lineHeight = font->getLineHeight() * label->getLineInterval(); const int lineHeight =
rawTextCache.metrics.lineHeight * label->getLineInterval();
glm::vec2 lcoord = label->calcPos(); glm::vec2 lcoord = label->calcPos();
lcoord.y -= 2; lcoord.y -= 2;
auto batch = pctx.getBatch2D(); auto batch = pctx.getBatch2D();
@ -256,7 +255,7 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
if (editable && static_cast<int>((time - caretLastMove) * 2) % 2 == 0) { if (editable && static_cast<int>((time - caretLastMove) * 2) % 2 == 0) {
uint line = rawTextCache.getLineByTextIndex(caret); uint line = rawTextCache.getLineByTextIndex(caret);
uint lcaret = caret - rawTextCache.getTextLineOffset(line); uint lcaret = caret - rawTextCache.getTextLineOffset(line);
int width = font->calcWidth(input, lcaret); int width = rawTextCache.metrics.calcWidth(input, 0, lcaret);
batch->rect( batch->rect(
lcoord.x + width, lcoord.x + width,
lcoord.y + label->getLineYOffset(line), lcoord.y + label->getLineYOffset(line),
@ -272,10 +271,10 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
uint endLine = label->getLineByTextIndex(selectionEnd); uint endLine = label->getLineByTextIndex(selectionEnd);
batch->setColor(glm::vec4(0.8f, 0.9f, 1.0f, 0.25f)); batch->setColor(glm::vec4(0.8f, 0.9f, 1.0f, 0.25f));
int start = font->calcWidth( int start = rawTextCache.metrics.calcWidth(
labelText, selectionStart - label->getTextLineOffset(startLine) labelText, selectionStart - label->getTextLineOffset(startLine)
); );
int end = font->calcWidth( int end = rawTextCache.metrics.calcWidth(
labelText, selectionEnd - label->getTextLineOffset(endLine) labelText, selectionEnd - label->getTextLineOffset(endLine)
); );
int lineY = label->getLineYOffset(startLine); int lineY = label->getLineYOffset(startLine);
@ -346,7 +345,14 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
} }
} }
void TextBox::drawBackground(const DrawContext& pctx, const Assets&) { void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) {
auto font = assets.get<Font>(label->getFontName());
rawTextCache.prepare(
reinterpret_cast<ptrdiff_t>(font),
font->getMetrics(),
label->getSize().x
);
glm::vec2 pos = calcPos(); glm::vec2 pos = calcPos();
auto batch = pctx.getBatch2D(); auto batch = pctx.getBatch2D();
@ -375,7 +381,11 @@ void TextBox::drawBackground(const DrawContext& pctx, const Assets&) {
} }
void TextBox::refreshLabel() { void TextBox::refreshLabel() {
rawTextCache.prepare(font, static_cast<size_t>(getSize().x)); rawTextCache.prepare(
rawTextCache.fontId,
rawTextCache.metrics,
static_cast<size_t>(getSize().x)
);
rawTextCache.update(input, multiline, false); rawTextCache.update(input, multiline, false);
label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f)); label->setColor(textColor * glm::vec4(input.empty() ? 0.5f : 1.0f));
@ -412,13 +422,13 @@ void TextBox::refreshLabel() {
lineNumbersLabel->setColor(glm::vec4(1, 1, 1, 0.25f)); lineNumbersLabel->setColor(glm::vec4(1, 1, 1, 0.25f));
} }
if (autoresize && font) { if (autoresize && rawTextCache.fontId) {
auto size = getSize(); auto size = getSize();
int newy = glm::min( int newy = glm::min(
static_cast<int>(parent->getSize().y), static_cast<int>(parent->getSize().y),
static_cast<int>( static_cast<int>(
label->getLinesNumber() * label->getLineInterval() * label->getLinesNumber() * label->getLineInterval() *
font->getLineHeight() rawTextCache.metrics.lineHeight
) + 1 ) + 1
); );
if (newy != static_cast<int>(size.y)) { if (newy != static_cast<int>(size.y)) {
@ -430,9 +440,9 @@ void TextBox::refreshLabel() {
} }
} }
if (multiline && font) { if (multiline && rawTextCache.fontId) {
setScrollable(true); setScrollable(true);
uint height = label->getLinesNumber() * font->getLineHeight() * uint height = label->getLinesNumber() * rawTextCache.metrics.lineHeight *
label->getLineInterval(); label->getLineInterval();
label->setSize(glm::vec2(label->getSize().x, height)); label->setSize(glm::vec2(label->getSize().x, height));
actualLength = height; actualLength = height;
@ -644,14 +654,14 @@ size_t TextBox::normalizeIndex(int index) {
/// @param y screen Y position /// @param y screen Y position
/// @return non-normalized character index /// @return non-normalized character index
int TextBox::calcIndexAt(int x, int y) const { int TextBox::calcIndexAt(int x, int y) const {
if (font == nullptr) return 0; if (rawTextCache.fontId == 0) return 0;
const auto& labelText = label->getText(); const auto& labelText = label->getText();
glm::vec2 lcoord = label->calcPos(); glm::vec2 lcoord = label->calcPos();
uint line = label->getLineByYOffset(y - lcoord.y); uint line = label->getLineByYOffset(y - lcoord.y);
line = std::min(line, label->getLinesNumber() - 1); line = std::min(line, label->getLinesNumber() - 1);
size_t lineLength = getLineLength(line); size_t lineLength = getLineLength(line);
uint offset = 0; uint offset = 0;
while (lcoord.x + font->calcWidth(labelText, offset) < x && while (lcoord.x + rawTextCache.metrics.calcWidth(labelText, offset) < x &&
offset < lineLength - 1) { offset < lineLength - 1) {
offset++; offset++;
} }
@ -1177,19 +1187,19 @@ size_t TextBox::getCaret() const {
void TextBox::setCaret(size_t position) { void TextBox::setCaret(size_t position) {
const auto& labelText = label->getText(); const auto& labelText = label->getText();
caret = std::min(static_cast<size_t>(position), input.length()); caret = std::min(static_cast<size_t>(position), input.length());
if (font == nullptr) { if (rawTextCache.fontId == 0) {
return; return;
} }
int width = label->getSize().x; int width = label->getSize().x;
rawTextCache.prepare(font, width); rawTextCache.prepare(rawTextCache.fontId, rawTextCache.metrics, width);
rawTextCache.update(input, multiline, label->isTextWrapping()); rawTextCache.update(input, multiline, label->isTextWrapping());
caretLastMove = gui.getWindow().time(); caretLastMove = gui.getWindow().time();
uint line = rawTextCache.getLineByTextIndex(caret); uint line = rawTextCache.getLineByTextIndex(caret);
int offset = label->getLineYOffset(line) + getContentOffset().y; int offset = label->getLineYOffset(line) + getContentOffset().y;
uint lineHeight = font->getLineHeight() * label->getLineInterval(); uint lineHeight = rawTextCache.metrics.lineHeight * label->getLineInterval();
if (scrollStep == 0) { if (scrollStep == 0) {
scrollStep = lineHeight; scrollStep = lineHeight;
} }
@ -1201,7 +1211,7 @@ void TextBox::setCaret(size_t position) {
} }
int lcaret = caret - rawTextCache.getTextLineOffset(line); int lcaret = caret - rawTextCache.getTextLineOffset(line);
int realoffset = int realoffset =
font->calcWidth(labelText, lcaret) - static_cast<int>(textOffset) + 2; rawTextCache.metrics.calcWidth(labelText, lcaret) - static_cast<int>(textOffset) + 2;
if (realoffset - width > 0) { if (realoffset - width > 0) {
setTextOffset(textOffset + realoffset - width); setTextOffset(textOffset + realoffset - width);

View File

@ -52,7 +52,6 @@ namespace gui {
int textInitX = 0; int textInitX = 0;
/// @brief Last time of the caret was moved (used for blink animation) /// @brief Last time of the caret was moved (used for blink animation)
double caretLastMove = 0.0; double caretLastMove = 0.0;
Font* font = nullptr;
// Note: selection does not include markup // Note: selection does not include markup
size_t selectionStart = 0; size_t selectionStart = 0;