diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-21 22:09:27 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-09-21 22:09:27 +0300 |
commit | 10999deaeec452e8db76b4d017e922f74a6dccf6 (patch) | |
tree | 8169c7f016de7e59f826646d85acf754a6b78d75 | |
parent | 5729595e6e3e4c44f272eaae86aeaa8ae31872ab (diff) |
Text: Draw tab indicators; jump to tab stops
-rw-r--r-- | src/ui/text.c | 41 |
1 files changed, 35 insertions, 6 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index bf71b0e9..2c17b3da 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -121,6 +121,8 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) | |||
121 | 121 | ||
122 | /*-----------------------------------------------------------------------------------------------*/ | 122 | /*-----------------------------------------------------------------------------------------------*/ |
123 | 123 | ||
124 | static iGlyph *glyph_Font_(iFont *d, iChar ch); | ||
125 | |||
124 | struct Impl_Font { | 126 | struct Impl_Font { |
125 | iBlock * data; | 127 | iBlock * data; |
126 | enum iTextFont family; | 128 | enum iTextFont family; |
@@ -131,6 +133,7 @@ struct Impl_Font { | |||
131 | int baseline; | 133 | int baseline; |
132 | iHash glyphs; /* key is glyph index in the font */ /* TODO: does not need to be a Hash */ | 134 | iHash glyphs; /* key is glyph index in the font */ /* TODO: does not need to be a Hash */ |
133 | iBool isMonospaced; | 135 | iBool isMonospaced; |
136 | float emAdvance; | ||
134 | enum iFontSize sizeId; /* used to look up different fonts of matching size */ | 137 | enum iFontSize sizeId; /* used to look up different fonts of matching size */ |
135 | uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ | 138 | uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ |
136 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) | 139 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) |
@@ -178,20 +181,20 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale, | |||
178 | d->height = height; | 181 | d->height = height; |
179 | iZap(d->font); | 182 | iZap(d->font); |
180 | stbtt_InitFont(&d->font, constData_Block(data), 0); | 183 | stbtt_InitFont(&d->font, constData_Block(data), 0); |
181 | int ascent, descent; | 184 | int ascent, descent, emAdv; |
182 | stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL); | 185 | stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL); |
186 | stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL); | ||
183 | d->xScale = d->yScale = stbtt_ScaleForPixelHeight(&d->font, height) * scale; | 187 | d->xScale = d->yScale = stbtt_ScaleForPixelHeight(&d->font, height) * scale; |
184 | if (d->isMonospaced) { | 188 | if (d->isMonospaced) { |
185 | /* It is important that monospaced fonts align 1:1 with the pixel grid so that | 189 | /* It is important that monospaced fonts align 1:1 with the pixel grid so that |
186 | box-drawing characters don't have partially occupied edge pixels, leading to seams | 190 | box-drawing characters don't have partially occupied edge pixels, leading to seams |
187 | between adjacent glyphs. */ | 191 | between adjacent glyphs. */ |
188 | int adv; | 192 | const float advance = (float) emAdv * d->xScale; |
189 | stbtt_GetCodepointHMetrics(&d->font, 'M', &adv, NULL); | ||
190 | const float advance = (float) adv * d->xScale; | ||
191 | if (advance > 4) { /* not too tiny */ | 193 | if (advance > 4) { /* not too tiny */ |
192 | d->xScale *= floorf(advance) / advance; | 194 | d->xScale *= floorf(advance) / advance; |
193 | } | 195 | } |
194 | } | 196 | } |
197 | d->emAdvance = emAdv * d->xScale; | ||
195 | d->baseline = ascent * d->yScale; | 198 | d->baseline = ascent * d->yScale; |
196 | d->vertOffset = height * (1.0f - scale) / 2; | 199 | d->vertOffset = height * (1.0f - scale) / 2; |
197 | /* Custom tweaks. */ | 200 | /* Custom tweaks. */ |
@@ -1335,6 +1338,11 @@ static void shape_GlyphBuffer_(iGlyphBuffer *d) { | |||
1335 | } | 1338 | } |
1336 | } | 1339 | } |
1337 | 1340 | ||
1341 | static float nextTabStop_Font_(const iFont *d, float x) { | ||
1342 | const float stop = 4 * d->emAdvance; | ||
1343 | return floorf(x / stop) * stop + stop; | ||
1344 | } | ||
1345 | |||
1338 | static float advance_GlyphBuffer_(const iGlyphBuffer *d, iRangei wrapPosRange) { | 1346 | static float advance_GlyphBuffer_(const iGlyphBuffer *d, iRangei wrapPosRange) { |
1339 | float x = 0.0f; | 1347 | float x = 0.0f; |
1340 | for (unsigned int i = 0; i < d->glyphCount; i++) { | 1348 | for (unsigned int i = 0; i < d->glyphCount; i++) { |
@@ -1343,6 +1351,9 @@ static float advance_GlyphBuffer_(const iGlyphBuffer *d, iRangei wrapPosRange) { | |||
1343 | continue; | 1351 | continue; |
1344 | } | 1352 | } |
1345 | x += d->font->xScale * d->glyphPos[i].x_advance; | 1353 | x += d->font->xScale * d->glyphPos[i].x_advance; |
1354 | if (d->logicalText[logPos] == '\t') { | ||
1355 | x = nextTabStop_Font_(d->font, x); | ||
1356 | } | ||
1346 | if (i + 1 < d->glyphCount) { | 1357 | if (i + 1 < d->glyphCount) { |
1347 | x += horizKern_Font_(d->font, | 1358 | x += horizKern_Font_(d->font, |
1348 | d->glyphInfo[i].codepoint, | 1359 | d->glyphInfo[i].codepoint, |
@@ -1354,7 +1365,7 @@ static float advance_GlyphBuffer_(const iGlyphBuffer *d, iRangei wrapPosRange) { | |||
1354 | 1365 | ||
1355 | static void evenMonospaceAdvances_GlyphBuffer_(iGlyphBuffer *d, iFont *baseFont) { | 1366 | static void evenMonospaceAdvances_GlyphBuffer_(iGlyphBuffer *d, iFont *baseFont) { |
1356 | shape_GlyphBuffer_(d); | 1367 | shape_GlyphBuffer_(d); |
1357 | const float monoAdvance = glyph_Font_(baseFont, 'M')->advance; | 1368 | const float monoAdvance = baseFont->emAdvance; |
1358 | for (unsigned int i = 0; i < d->glyphCount; ++i) { | 1369 | for (unsigned int i = 0; i < d->glyphCount; ++i) { |
1359 | const hb_glyph_info_t *info = d->glyphInfo + i; | 1370 | const hb_glyph_info_t *info = d->glyphInfo + i; |
1360 | if (d->glyphPos[i].x_advance > 0 && d->font != baseFont) { | 1371 | if (d->glyphPos[i].x_advance > 0 && d->font != baseFont) { |
@@ -1498,10 +1509,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1498 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); | 1509 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); |
1499 | const float xOffset = run->font->xScale * buf->glyphPos[i].x_offset; | 1510 | const float xOffset = run->font->xScale * buf->glyphPos[i].x_offset; |
1500 | const float xAdvance = run->font->xScale * buf->glyphPos[i].x_advance; | 1511 | const float xAdvance = run->font->xScale * buf->glyphPos[i].x_advance; |
1512 | const iChar ch = logicalText[logPos]; | ||
1501 | iAssert(xAdvance >= 0); | 1513 | iAssert(xAdvance >= 0); |
1502 | if (args->wrap->mode == word_WrapTextMode) { | 1514 | if (args->wrap->mode == word_WrapTextMode) { |
1503 | /* When word wrapping, only consider certain places breakable. */ | 1515 | /* When word wrapping, only consider certain places breakable. */ |
1504 | const iChar ch = logicalText[logPos]; | ||
1505 | if ((ch >= 128 || !ispunct(ch)) && (prevCh == '-' || prevCh == '/')) { | 1516 | if ((ch >= 128 || !ispunct(ch)) && (prevCh == '-' || prevCh == '/')) { |
1506 | safeBreakPos = logPos; | 1517 | safeBreakPos = logPos; |
1507 | breakAdvance = wrapAdvance; | 1518 | breakAdvance = wrapAdvance; |
@@ -1527,6 +1538,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1527 | wrap->hitGlyphNormX_out = (wrap->hitPoint.x - wrapAdvance) / xAdvance; | 1538 | wrap->hitGlyphNormX_out = (wrap->hitPoint.x - wrapAdvance) / xAdvance; |
1528 | } | 1539 | } |
1529 | } | 1540 | } |
1541 | if (ch == '\t') { | ||
1542 | wrapAdvance = nextTabStop_Font_(d, wrapAdvance) - xAdvance; | ||
1543 | } | ||
1530 | /* Out of room? */ | 1544 | /* Out of room? */ |
1531 | if (wrap->maxWidth > 0 && | 1545 | if (wrap->maxWidth > 0 && |
1532 | wrapAdvance + xOffset + glyph->d[0].x + glyph->rect[0].size.x > | 1546 | wrapAdvance + xOffset + glyph->d[0].x + glyph->rect[0].size.x > |
@@ -1681,6 +1695,21 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1681 | const float xAdvance = run->font->xScale * buf->glyphPos[i].x_advance; | 1695 | const float xAdvance = run->font->xScale * buf->glyphPos[i].x_advance; |
1682 | const float yAdvance = run->font->yScale * buf->glyphPos[i].y_advance; | 1696 | const float yAdvance = run->font->yScale * buf->glyphPos[i].y_advance; |
1683 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); | 1697 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); |
1698 | if (logicalText[logPos] == '\t') { | ||
1699 | if (mode & draw_RunMode) { | ||
1700 | /* Tab indicator. */ | ||
1701 | iColor tabColor = get_Color(uiTextAction_ColorId); | ||
1702 | SDL_SetRenderDrawColor(activeText_->render, tabColor.r, tabColor.g, tabColor.b, 255); | ||
1703 | const int pad = d->height / 6; | ||
1704 | SDL_RenderFillRect(activeText_->render, &(SDL_Rect){ | ||
1705 | orig.x + xCursor, | ||
1706 | orig.y + yCursor + d->height / 2 - pad / 2, | ||
1707 | pad, | ||
1708 | pad | ||
1709 | }); | ||
1710 | } | ||
1711 | xCursor = nextTabStop_Font_(d, xCursor) - xAdvance; | ||
1712 | } | ||
1684 | const float xf = xCursor + xOffset; | 1713 | const float xf = xCursor + xOffset; |
1685 | const int hoff = enableHalfPixelGlyphs_Text ? (xf - ((int) xf) > 0.5f ? 1 : 0) : 0; | 1714 | const int hoff = enableHalfPixelGlyphs_Text ? (xf - ((int) xf) > 0.5f ? 1 : 0) : 0; |
1686 | /* Output position for the glyph. */ | 1715 | /* Output position for the glyph. */ |