Merge pull request #1004 from boolean-false/fix/vector-font-textbox-metrics

Исправил расчёт ширины текста и положение каретки
This commit is contained in:
MihailRis 2026-09-29 22:16:01 +03:00 • committed by GitHub
commit 7e572d620f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 63 additions and 31 deletions

View file

@ -29,6 +29,10 @@ Font::Font(
if (this->fontFile.has_value()) {
if (auto fontPtr = this->fontFile->lock()) {
monospace = fontPtr->isMonospace();
if (monospace && this->glyphs.size() > '0' &&
this->glyphs['0'].xAdvance > 0) {
glyphInterval = this->glyphs['0'].xAdvance;
}
}
}
}
@ -62,21 +66,13 @@ bool Font::isPrintableChar(uint codepoint) const {
int FontMetrics::calcWidth(std::wstring_view text, size_t offset, size_t length) const {
auto font = this->font.has_value() ? this->font->lock() : nullptr;
if (font == nullptr || font->isMonospace()) {
return std::min(text.length() - offset, length) * _glyphInterval;
if (font != nullptr) {
return font->calcWidth(text, offset, length);
}
int totalWidth = 0;
for (int i = offset; i < offset + length && i < text.length(); i++) {
auto codepoint = text[i];
if (!font->isPrintableChar(codepoint)) {
totalWidth += _glyphInterval;
} else if (auto glyph = font->getGlyph(codepoint)) {
totalWidth += glyph->xAdvance;
} else {
totalWidth += _glyphInterval;
}
if (offset >= text.length()) {
return 0;
}
return totalWidth;
return std::min(text.length() - offset, length) * _glyphInterval;
}
int Font::calcWidth(std::wstring_view text, size_t length) const {
@ -84,7 +80,24 @@ int Font::calcWidth(std::wstring_view text, size_t length) const {
}
int Font::calcWidth(std::wstring_view text, size_t offset, size_t length) const {
return std::min(text.length()-offset, length) * glyphInterval;
if (offset >= text.length()) {
return 0;
}
size_t end = offset + std::min(text.length() - offset, length);
int width = 0;
for (size_t i = offset; i < end; i++) {
width += getAdvance(text[i]);
}
return width;
}
int Font::getAdvance(uint codepoint) const {
if (isPrintableChar(codepoint)) {
if (auto glyph = getGlyph(codepoint)) {
return glyph->xAdvance;
}
}
return glyphInterval;
}
static inline void draw_glyph(
@ -194,11 +207,10 @@ static inline void draw_text(
}
int yOffset = 0;
int xOffset = 0;
float advance = 1.0f;
float advance = font.getAdvance(c) / cellAdvance;
if (auto glyph = font.getGlyph(c)) {
yOffset = glyph->yOffset;
xOffset = glyph->xOffset;
advance = glyph->xAdvance / cellAdvance;
}
uint charpage = c >> 8;
if (charpage == page){
@ -325,7 +337,7 @@ std::unique_ptr<Font> Font::createBitmapFont(
return std::make_unique<Font>(std::move(textures), std::move(glyphs), res, 4);
}
const Glyph* Font::getGlyph(int codepoint) {
const Glyph* Font::getGlyph(int codepoint) const {
if (codepoint < 0) {
return nullptr;
}

View file

@ -57,6 +57,10 @@ public:
/// @return pixel width of the substring
int calcWidth(std::wstring_view text, size_t offset, size_t length) const;
/// @brief Get the horizontal advance of a character in pixels
/// @param codepoint character unicode codepoint
int getAdvance(uint codepoint) const;
/// @brief Check if character is visible (non-whitespace)
/// @param codepoint character unicode codepoint
bool isPrintableChar(uint codepoint) const;
@ -87,7 +91,7 @@ public:
return {std::nullopt, lineHeight, yoffset, glyphInterval};
}
const Glyph* getGlyph(int codepoint);
const Glyph* getGlyph(int codepoint) const;
static std::unique_ptr<Font> createBitmapFont(
std::vector<std::unique_ptr<ImageData>> pages
@ -97,7 +101,7 @@ private:
int yoffset;
int glyphInterval;
bool monospace = true;
std::vector<std::unique_ptr<Texture>> pages;
std::vector<Glyph> glyphs;
mutable std::vector<std::unique_ptr<Texture>> pages;
mutable std::vector<Glyph> glyphs;
std::optional<std::weak_ptr<vector_fonts::FontFile>> fontFile;
};

View file

@ -255,9 +255,9 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
// drawing caret here
if (isFocused() && editable && static_cast<int>((time - caretLastMove) * 2) % 2 == 0) {
uint line = label->getLineByTextIndex(caret);
auto linestart = label->getTextLineOffset(line);
uint lcaret = caret - label->getTextLineOffset(line);
int width = rawTextCache.metrics.calcWidth(input.substr(linestart), 0, lcaret);
size_t lineStart = label->getTextLineOffset(line);
size_t lineCaret = caret - lineStart;
int width = rawTextCache.metrics.calcWidth(input, lineStart, lineCaret);
batch->rect(
lcoord.x + width,
@ -275,11 +275,13 @@ 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));
size_t startLineOffset = label->getTextLineOffset(startLine);
size_t endLineOffset = label->getTextLineOffset(endLine);
int start = rawTextCache.metrics.calcWidth(
labelText, 0, selectionStart - label->getTextLineOffset(startLine)
labelText, startLineOffset, selectionStart - startLineOffset
);
int end = rawTextCache.metrics.calcWidth(
labelText, 0, selectionEnd - label->getTextLineOffset(endLine)
labelText, endLineOffset, selectionEnd - endLineOffset
);
int lineY = label->getLineYOffset(startLine);
@ -352,6 +354,11 @@ void TextBox::draw(const DrawContext& pctx, const Assets& assets) {
}
void TextBox::drawBackground(const DrawContext& pctx, const Assets& assets) {
if (lineNumbersLabel->getFontName() != label->getFontName()) {
lineNumbersLabel->setFontName(label->getFontName());
}
lineNumbersLabel->setLineInterval(label->getLineInterval());
auto font = assets.getShared<Font>(label->getFontName());
if (font != nullptr) {
rawTextCache.prepare(font, font->getMetrics(), label->getSize().x);
@ -681,13 +688,19 @@ int TextBox::calcIndexAt(int x, int y) const {
uint line = label->getLineByYOffset(y - lcoord.y);
line = std::min(line, label->getLinesNumber() - 1);
size_t lineLength = getLineLength(line);
size_t lineStart = label->getTextLineOffset(line);
uint offset = 0;
while (lcoord.x + rawTextCache.metrics.calcWidth(labelText, 0, offset) < x &&
offset < lineLength - 1) {
while (offset < lineLength - 1) {
int width = rawTextCache.metrics.calcWidth(
labelText, lineStart, offset
);
if (lcoord.x + width >= x) {
break;
}
offset++;
}
return std::min(
offset + label->getTextLineOffset(line), labelText.length()
offset + lineStart, labelText.length()
);
}
@ -1224,9 +1237,12 @@ void TextBox::setCaret(size_t position) {
offset -= getSize().y;
scrolled(-glm::ceil(offset / static_cast<double>(scrollStep) + 0.5f));
}
int lcaret = caret - rawTextCache.getTextLineOffset(line);
int realoffset = rawTextCache.metrics.calcWidth(labelText, 0, lcaret) -
static_cast<int>(textOffset);
size_t lineStart = rawTextCache.getTextLineOffset(line);
size_t lineCaret = caret - lineStart;
int caretWidth = rawTextCache.metrics.calcWidth(
labelText, lineStart, lineCaret
);
int realoffset = caretWidth - static_cast<int>(textOffset);
if (realoffset - width > 0) {
setTextOffset(textOffset + realoffset - width);