From edc681028331cd6b43e612e1c5d48c84d67470cd Mon Sep 17 00:00:00 2001 From: MihailRis Date: Tue, 30 Sep 2025 18:22:39 +0300 Subject: [PATCH] fix label font cache --- src/graphics/core/Font.cpp | 4 +++ src/graphics/core/Font.hpp | 6 ++++ src/graphics/core/FontMetics.hpp | 11 +++++++ src/graphics/ui/elements/Label.cpp | 35 ++++++++++++--------- src/graphics/ui/elements/Label.hpp | 8 +++-- src/graphics/ui/elements/TextBox.cpp | 46 +++++++++++++++++----------- src/graphics/ui/elements/TextBox.hpp | 1 - 7 files changed, 75 insertions(+), 36 deletions(-) create mode 100644 src/graphics/core/FontMetics.hpp diff --git a/src/graphics/core/Font.cpp b/src/graphics/core/Font.cpp index 19abb9f0..73718b4f 100644 --- a/src/graphics/core/Font.cpp +++ b/src/graphics/core/Font.cpp @@ -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 { return calcWidth(text, 0, length); } diff --git a/src/graphics/core/Font.hpp b/src/graphics/core/Font.hpp index eabfbcc6..12371595 100644 --- a/src/graphics/core/Font.hpp +++ b/src/graphics/core/Font.hpp @@ -4,7 +4,9 @@ #include #include #include + #include "typedefs.hpp" +#include "FontMetics.hpp" class Texture; class Batch2D; @@ -90,4 +92,8 @@ public: ) const; const Texture* getPage(int page) const; + + FontMetrics getMetrics() const { + return {lineHeight, yoffset, glyphInterval}; + } }; diff --git a/src/graphics/core/FontMetics.hpp b/src/graphics/core/FontMetics.hpp new file mode 100644 index 00000000..d58a4fd9 --- /dev/null +++ b/src/graphics/core/FontMetics.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +struct FontMetrics { + int lineHeight; + int yoffset; + int glyphInterval = 8; + + int calcWidth(std::wstring_view text, size_t offset=0, size_t length=-1) const; +}; diff --git a/src/graphics/ui/elements/Label.cpp b/src/graphics/ui/elements/Label.cpp index c11f119c..80b28fc2 100644 --- a/src/graphics/ui/elements/Label.cpp +++ b/src/graphics/ui/elements/Label.cpp @@ -11,10 +11,11 @@ using namespace gui; -void LabelCache::prepare(Font* font, size_t wrapWidth) { - if (font != this->font) { +void LabelCache::prepare(std::ptrdiff_t fontId, FontMetrics metrics, size_t wrapWidth) { + if (fontId != this->fontId) { resetFlag = true; - this->font = font; + this->fontId = fontId; + this->metrics = metrics; } if (wrapWidth != this->wrapWidth) { resetFlag = true; @@ -41,7 +42,7 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) { lines.clear(); lines.push_back(LineScheme {0, false}); - if (font == nullptr) { + if (fontId == 0) { wrap = false; } @@ -52,7 +53,7 @@ void LabelCache::update(std::wstring_view text, bool multiline, bool wrap) { lines.push_back(LineScheme {i+1, false}); len = 0; } 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) { // starting a fake line 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; for (int i = 0; i < lines.size() - 1; i++) { const auto& next = lines[i + 1]; const auto& cur = lines[i]; maxWidth = std::max( - font->calcWidth( + metrics.calcWidth( text.substr(cur.offset, next.offset - cur.offset) ), maxWidth ); } maxWidth = std::max( - font->calcWidth( + metrics.calcWidth( text.substr(lines[lines.size() - 1].offset) ), maxWidth @@ -105,8 +106,8 @@ Label::Label(GUI& gui, const std::wstring& text, std::string fontName) Label::~Label() = default; glm::vec2 Label::calcSize() { - auto font = cache.font; - uint lineHeight = font->getLineHeight(); + auto& metrics = cache.metrics; + uint lineHeight = metrics.lineHeight; if (cache.lines.size() > 1) { lineHeight *= lineInterval; } @@ -114,12 +115,12 @@ glm::vec2 Label::calcSize() { if (multiline) { return glm::vec2( cache.multilineWidth, - lineHeight * cache.lines.size() + font->getYOffset() + lineHeight * cache.lines.size() + metrics.yoffset ); } return glm::vec2 ( - cache.font->calcWidth(view), - lineHeight * cache.lines.size() + font->getYOffset() + cache.metrics.calcWidth(view), + lineHeight * cache.lines.size() + metrics.yoffset ); } @@ -135,7 +136,7 @@ void Label::setText(std::wstring text) { this->text = std::move(text); cache.update(this->text, multiline, textWrap); - if (cache.font && autoresize) { + if (cache.fontId != 0 && autoresize) { setSize(calcSize()); } } @@ -203,7 +204,11 @@ uint Label::getLinesNumber() const { void Label::draw(const DrawContext& pctx, const Assets& assets) { auto batch = pctx.getBatch2D(); auto font = assets.get(fontName); - cache.prepare(font, static_cast(glm::abs(getSize().x))); + cache.prepare( + reinterpret_cast(font), + font->getMetrics(), + static_cast(glm::abs(getSize().x)) + ); if (supplier) { setText(supplier()); diff --git a/src/graphics/ui/elements/Label.hpp b/src/graphics/ui/elements/Label.hpp index 7e682757..df6e4c6a 100644 --- a/src/graphics/ui/elements/Label.hpp +++ b/src/graphics/ui/elements/Label.hpp @@ -3,6 +3,8 @@ #include "UINode.hpp" #include "constants.hpp" +#include "graphics/core/FontMetics.hpp" + class Font; struct FontStylesScheme; @@ -13,14 +15,16 @@ namespace gui { }; struct LabelCache { - Font* font = nullptr; + ptrdiff_t fontId = 0; + FontMetrics metrics; + std::vector lines; /// @brief Reset cache flag bool resetFlag = true; size_t wrapWidth = -1; 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); size_t getTextLineOffset(size_t line) const; diff --git a/src/graphics/ui/elements/TextBox.cpp b/src/graphics/ui/elements/TextBox.cpp index 1755b6b9..a05ba5df 100644 --- a/src/graphics/ui/elements/TextBox.cpp +++ b/src/graphics/ui/elements/TextBox.cpp @@ -231,8 +231,6 @@ TextBox::~TextBox() = default; void TextBox::draw(const DrawContext& pctx, const Assets& assets) { Container::draw(pctx, assets); - font = assets.get(label->getFontName()); - if (!isFocused()) { return; } @@ -244,7 +242,8 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { auto subctx = pctx.sub(); 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(); lcoord.y -= 2; auto batch = pctx.getBatch2D(); @@ -256,7 +255,7 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { if (editable && static_cast((time - caretLastMove) * 2) % 2 == 0) { uint line = rawTextCache.getLineByTextIndex(caret); uint lcaret = caret - rawTextCache.getTextLineOffset(line); - int width = font->calcWidth(input, lcaret); + int width = rawTextCache.metrics.calcWidth(input, 0, lcaret); batch->rect( lcoord.x + width, lcoord.y + label->getLineYOffset(line), @@ -272,10 +271,10 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) { uint endLine = label->getLineByTextIndex(selectionEnd); 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) ); - int end = font->calcWidth( + int end = rawTextCache.metrics.calcWidth( labelText, selectionEnd - label->getTextLineOffset(endLine) ); 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(label->getFontName()); + rawTextCache.prepare( + reinterpret_cast(font), + font->getMetrics(), + label->getSize().x + ); + glm::vec2 pos = calcPos(); auto batch = pctx.getBatch2D(); @@ -375,7 +381,11 @@ void TextBox::drawBackground(const DrawContext& pctx, const Assets&) { } void TextBox::refreshLabel() { - rawTextCache.prepare(font, static_cast(getSize().x)); + rawTextCache.prepare( + rawTextCache.fontId, + rawTextCache.metrics, + static_cast(getSize().x) + ); rawTextCache.update(input, multiline, false); 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)); } - if (autoresize && font) { + if (autoresize && rawTextCache.fontId) { auto size = getSize(); int newy = glm::min( static_cast(parent->getSize().y), static_cast( label->getLinesNumber() * label->getLineInterval() * - font->getLineHeight() + rawTextCache.metrics.lineHeight ) + 1 ); if (newy != static_cast(size.y)) { @@ -430,9 +440,9 @@ void TextBox::refreshLabel() { } } - if (multiline && font) { + if (multiline && rawTextCache.fontId) { setScrollable(true); - uint height = label->getLinesNumber() * font->getLineHeight() * + uint height = label->getLinesNumber() * rawTextCache.metrics.lineHeight * label->getLineInterval(); label->setSize(glm::vec2(label->getSize().x, height)); actualLength = height; @@ -644,14 +654,14 @@ size_t TextBox::normalizeIndex(int index) { /// @param y screen Y position /// @return non-normalized character index int TextBox::calcIndexAt(int x, int y) const { - if (font == nullptr) return 0; + if (rawTextCache.fontId == 0) return 0; const auto& labelText = label->getText(); glm::vec2 lcoord = label->calcPos(); uint line = label->getLineByYOffset(y - lcoord.y); line = std::min(line, label->getLinesNumber() - 1); size_t lineLength = getLineLength(line); uint offset = 0; - while (lcoord.x + font->calcWidth(labelText, offset) < x && + while (lcoord.x + rawTextCache.metrics.calcWidth(labelText, offset) < x && offset < lineLength - 1) { offset++; } @@ -1177,19 +1187,19 @@ size_t TextBox::getCaret() const { void TextBox::setCaret(size_t position) { const auto& labelText = label->getText(); caret = std::min(static_cast(position), input.length()); - if (font == nullptr) { + if (rawTextCache.fontId == 0) { return; } int width = label->getSize().x; - rawTextCache.prepare(font, width); + rawTextCache.prepare(rawTextCache.fontId, rawTextCache.metrics, width); rawTextCache.update(input, multiline, label->isTextWrapping()); caretLastMove = gui.getWindow().time(); uint line = rawTextCache.getLineByTextIndex(caret); int offset = label->getLineYOffset(line) + getContentOffset().y; - uint lineHeight = font->getLineHeight() * label->getLineInterval(); + uint lineHeight = rawTextCache.metrics.lineHeight * label->getLineInterval(); if (scrollStep == 0) { scrollStep = lineHeight; } @@ -1201,7 +1211,7 @@ void TextBox::setCaret(size_t position) { } int lcaret = caret - rawTextCache.getTextLineOffset(line); int realoffset = - font->calcWidth(labelText, lcaret) - static_cast(textOffset) + 2; + rawTextCache.metrics.calcWidth(labelText, lcaret) - static_cast(textOffset) + 2; if (realoffset - width > 0) { setTextOffset(textOffset + realoffset - width); diff --git a/src/graphics/ui/elements/TextBox.hpp b/src/graphics/ui/elements/TextBox.hpp index 9bbfac39..c4b88f90 100644 --- a/src/graphics/ui/elements/TextBox.hpp +++ b/src/graphics/ui/elements/TextBox.hpp @@ -52,7 +52,6 @@ namespace gui { int textInitX = 0; /// @brief Last time of the caret was moved (used for blink animation) double caretLastMove = 0.0; - Font* font = nullptr; // Note: selection does not include markup size_t selectionStart = 0;