fix label font cache
This commit is contained in:
parent
c8ba5b5dbb
commit
edc6810283
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
11
src/graphics/core/FontMetics.hpp
Normal file
11
src/graphics/core/FontMetics.hpp
Normal 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;
|
||||||
|
};
|
||||||
@ -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());
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user