summaryrefslogtreecommitdiff
path: root/src/ui/text.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/text.c')
-rw-r--r--src/ui/text.c166
1 files changed, 117 insertions, 49 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index 889aa2e4..ffe08fca 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32 32
33#include <the_Foundation/array.h> 33#include <the_Foundation/array.h>
34#include <the_Foundation/file.h> 34#include <the_Foundation/file.h>
35#include <the_Foundation/fileinfo.h>
35#include <the_Foundation/hash.h> 36#include <the_Foundation/hash.h>
36#include <the_Foundation/math.h> 37#include <the_Foundation/math.h>
37#include <the_Foundation/stringlist.h> 38#include <the_Foundation/stringlist.h>
@@ -124,10 +125,6 @@ struct Impl_Font {
124 iBool isMonospaced; 125 iBool isMonospaced;
125 iBool manualKernOnly; 126 iBool manualKernOnly;
126 enum iFontSize sizeId; /* used to look up different fonts of matching size */ 127 enum iFontSize sizeId; /* used to look up different fonts of matching size */
127// enum iFontId
128// enum iFontId japaneseFont; /* font to use for Japanese glyphs */
129// enum iFontId chineseFont; /* font to use for Simplified Chinese glyphs */
130// enum iFontId koreanFont; /* font to use for Korean glyphs */
131 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ 128 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */
132}; 129};
133 130
@@ -155,13 +152,17 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale,
155 d->xScale *= floorf(advance) / advance; 152 d->xScale *= floorf(advance) / advance;
156 } 153 }
157 } 154 }
158 d->vertOffset = height * (1.0f - scale) / 2; 155 d->baseline = ascent * d->yScale;
159 d->baseline = ascent * d->yScale; 156 d->vertOffset = height * (1.0f - scale) / 2;
160 d->sizeId = sizeId; 157 /* Custom tweaks. */
161// d->symbolsFont = symbolsFont; 158 if (data == &fontNotoSansSymbolsRegular_Embedded ||
162// d->japaneseFont = regularJapanese_FontId; 159 data == &fontNotoSansSymbols2Regular_Embedded) {
163// d->chineseFont = regularChinese_FontId; 160 d->vertOffset /= 2;
164// d->koreanFont = regularKorean_FontId; 161 }
162 else if (data == &fontNotoEmojiRegular_Embedded) {
163 //d->vertOffset -= height / 30;
164 }
165 d->sizeId = sizeId;
165 memset(d->indexTable, 0xff, sizeof(d->indexTable)); 166 memset(d->indexTable, 0xff, sizeof(d->indexTable));
166} 167}
167 168
@@ -215,7 +216,8 @@ struct Impl_Text {
215 iRegExp * ansiEscape; 216 iRegExp * ansiEscape;
216}; 217};
217 218
218static iText text_; 219static iText text_;
220static iBlock *userFont_;
219 221
220static void initFonts_Text_(iText *d) { 222static void initFonts_Text_(iText *d) {
221 const float textSize = fontSize_UI * d->contentFontSize; 223 const float textSize = fontSize_UI * d->contentFontSize;
@@ -321,27 +323,31 @@ static void initFonts_Text_(iText *d) {
321 { &fontIosevkaTermExtended_Embedded, smallMonoSize, 1.0f, contentMonoSmall_FontSize }, 323 { &fontIosevkaTermExtended_Embedded, smallMonoSize, 1.0f, contentMonoSmall_FontSize },
322 { &fontIosevkaTermExtended_Embedded, monoSize, 1.0f, contentMono_FontSize }, 324 { &fontIosevkaTermExtended_Embedded, monoSize, 1.0f, contentMono_FontSize },
323 /* extra content fonts */ 325 /* extra content fonts */
324 { &fontSourceSans3Regular_Embedded, textSize, scaling, contentRegular_FontSize }, 326 { &fontSourceSans3Regular_Embedded, textSize, scaling, contentRegular_FontSize },
325 { &fontIosevkaTermExtended_Embedded, textSize, 0.866f, contentRegular_FontSize }, 327 { &fontSourceSans3Regular_Embedded, textSize * 0.80f, scaling, contentRegular_FontSize },
326 /* symbols and scripts */ 328 /* symbols and scripts */
327#define DEFINE_FONT_SET(data) \ 329#define DEFINE_FONT_SET(data, glyphScale) \
328 { &data, uiSize, 1.0f, uiNormal_FontSize }, \ 330 { (data), uiSize, glyphScale, uiNormal_FontSize }, \
329 { &data, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, \ 331 { (data), uiSize * 1.125f, glyphScale, uiMedium_FontSize }, \
330 { &data, uiSize * 1.333f, 1.0f, uiBig_FontSize }, \ 332 { (data), uiSize * 1.333f, glyphScale, uiBig_FontSize }, \
331 { &data, uiSize * 1.666f, 1.0f, uiLarge_FontSize }, \ 333 { (data), uiSize * 1.666f, glyphScale, uiLarge_FontSize }, \
332 { &data, textSize, 1.0f, contentRegular_FontSize }, \ 334 { (data), textSize, glyphScale, contentRegular_FontSize }, \
333 { &data, textSize * 1.200f, 1.0f, contentMedium_FontSize }, \ 335 { (data), textSize * 1.200f, glyphScale, contentMedium_FontSize }, \
334 { &data, textSize * 1.333f, 1.0f, contentBig_FontSize }, \ 336 { (data), textSize * 1.333f, glyphScale, contentBig_FontSize }, \
335 { &data, textSize * 1.666f, 1.0f, contentLarge_FontSize }, \ 337 { (data), textSize * 1.666f, glyphScale, contentLarge_FontSize }, \
336 { &data, textSize * 2.000f, 1.0f, contentHuge_FontSize }, \ 338 { (data), textSize * 2.000f, glyphScale, contentHuge_FontSize }, \
337 { &data, smallMonoSize, 1.0f, contentMonoSmall_FontSize }, \ 339 { (data), smallMonoSize, glyphScale, contentMonoSmall_FontSize }, \
338 { &data, monoSize, 1.0f, contentMono_FontSize } 340 { (data), monoSize, glyphScale, contentMono_FontSize }
339 DEFINE_FONT_SET(fontSymbola_Embedded), 341 DEFINE_FONT_SET(userFont_ ? userFont_ : &fontIosevkaTermExtended_Embedded, 1.0f),
340 DEFINE_FONT_SET(fontNotoEmojiRegular_Embedded), 342 DEFINE_FONT_SET(&fontIosevkaTermExtended_Embedded, 0.866f),
341 DEFINE_FONT_SET(fontNotoSansJPRegular_Embedded), 343 DEFINE_FONT_SET(&fontNotoSansSymbolsRegular_Embedded, 1.45f),
342 DEFINE_FONT_SET(fontNotoSansSCRegular_Embedded), 344 DEFINE_FONT_SET(&fontNotoSansSymbols2Regular_Embedded, 1.45f),
343 DEFINE_FONT_SET(fontNanumGothicRegular_Embedded), /* TODO: should use Noto Sans here, too */ 345 DEFINE_FONT_SET(&fontSmolEmojiRegular_Embedded, 1.0f),
344 DEFINE_FONT_SET(fontNotoSansArabicUIRegular_Embedded), 346 DEFINE_FONT_SET(&fontNotoEmojiRegular_Embedded, 1.10f),
347 DEFINE_FONT_SET(&fontNotoSansJPRegular_Embedded, 1.0f),
348 DEFINE_FONT_SET(&fontNotoSansSCRegular_Embedded, 1.0f),
349 DEFINE_FONT_SET(&fontNanumGothicRegular_Embedded, 1.0f), /* TODO: should use Noto Sans here, too */
350 DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f),
345 }; 351 };
346 iForIndices(i, fontData) { 352 iForIndices(i, fontData) {
347 iFont *font = &d->fonts[i]; 353 iFont *font = &d->fonts[i];
@@ -401,8 +407,28 @@ static void deinitCache_Text_(iText *d) {
401 SDL_DestroyTexture(d->cache); 407 SDL_DestroyTexture(d->cache);
402} 408}
403 409
410void loadUserFonts_Text(void) {
411 if (userFont_) {
412 delete_Block(userFont_);
413 userFont_ = NULL;
414 }
415 /* Load the system font. */
416 const iPrefs *prefs = prefs_App();
417 if (!isEmpty_String(&prefs->symbolFontPath)) {
418 iFile *f = new_File(&prefs->symbolFontPath);
419 if (open_File(f, readOnly_FileMode)) {
420 userFont_ = readAll_File(f);
421 }
422 else {
423 fprintf(stderr, "[Text] failed to open: %s\n", cstr_String(&prefs->symbolFontPath));
424 }
425 iRelease(f);
426 }
427}
428
404void init_Text(SDL_Renderer *render) { 429void init_Text(SDL_Renderer *render) {
405 iText *d = &text_; 430 iText *d = &text_;
431 loadUserFonts_Text();
406 d->contentFont = nunito_TextFont; 432 d->contentFont = nunito_TextFont;
407 d->headingFont = nunito_TextFont; 433 d->headingFont = nunito_TextFont;
408 d->contentFontSize = contentScale_Text_; 434 d->contentFontSize = contentScale_Text_;
@@ -542,14 +568,36 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
542} 568}
543 569
544iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { 570iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
571 if (isVariationSelector_Char(ch)) {
572 return d;
573 }
574 /* Smol Emoji overrides all other fonts. */
575 if (ch != 0x20) {
576 iFont *smol = font_Text_(smolEmoji_FontId + d->sizeId);
577 if (smol != d && (*glyphIndex = glyphIndex_Font_(smol, ch)) != 0) {
578 return smol;
579 }
580 }
581 /* Manual exceptions. */ {
582 if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) {
583 d = font_Text_(iosevka_FontId + d->sizeId);
584 *glyphIndex = glyphIndex_Font_(d, ch);
585 return d;
586 }
587 }
545 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { 588 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) {
546 return d; 589 return d;
547 } 590 }
548 /* Not defined in current font, try Noto Emoji (for selected characters). */ 591 const int fallbacks[] = {
549 if ((ch >= 0x1f300 && ch < 0x1f600) || (ch >= 0x1f680 && ch <= 0x1f6c5)) { 592 notoEmoji_FontId,
550 iFont *emoji = font_Text_(emoji_FontId + d->sizeId); 593 symbols2_FontId,
551 if (emoji != d && (*glyphIndex = glyphIndex_Font_(emoji, ch)) != 0) { 594 symbols_FontId
552 return emoji; 595 };
596 /* First fallback is Smol Emoji. */
597 iForIndices(i, fallbacks) {
598 iFont *fallback = font_Text_(fallbacks[i] + d->sizeId);
599 if (fallback != d && (*glyphIndex = glyphIndex_Font_(fallback, ch)) != 0) {
600 return fallback;
553 } 601 }
554 } 602 }
555 /* Try Simplified Chinese. */ 603 /* Try Simplified Chinese. */
@@ -584,17 +632,25 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
584 /* White up arrow is used for the Shift key on macOS. Symbola's glyph is not a great 632 /* White up arrow is used for the Shift key on macOS. Symbola's glyph is not a great
585 match to the other text, so use the UI font instead. */ 633 match to the other text, so use the UI font instead. */
586 if ((ch == 0x2318 || ch == 0x21e7) && d == font_Text_(regular_FontId)) { 634 if ((ch == 0x2318 || ch == 0x21e7) && d == font_Text_(regular_FontId)) {
587 *glyphIndex = glyphIndex_Font_(d = font_Text_(defaultContentSized_FontId), ch); 635 *glyphIndex = glyphIndex_Font_(d = font_Text_(defaultContentRegular_FontId), ch);
588 return d; 636 return d;
589 } 637 }
590#endif 638#endif
591 /* Fall back to Symbola for anything else. */ 639 /* User's symbols font. */ {
592 iFont *font = font_Text_(symbols_FontId + d->sizeId); 640 iFont *sys = font_Text_(userSymbols_FontId + d->sizeId);
593 *glyphIndex = glyphIndex_Font_(font, ch); 641 if (sys != d && (*glyphIndex = glyphIndex_Font_(sys, ch)) != 0) {
594// if (!*glyphIndex) { 642 return sys;
595// fprintf(stderr, "failed to find %08x (%lc)\n", ch, ch); fflush(stderr); 643 }
596// } 644 }
597 return font; 645 /* Final fallback. */
646 iFont *font = font_Text_(iosevka_FontId + d->sizeId);
647 if (d != font) {
648 *glyphIndex = glyphIndex_Font_(font, ch);
649 }
650 if (!*glyphIndex) {
651 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr);
652 }
653 return d;
598} 654}
599 655
600static iGlyph *glyph_Font_(iFont *d, iChar ch) { 656static iGlyph *glyph_Font_(iFont *d, iChar ch) {
@@ -1190,7 +1246,7 @@ iInt2 advanceN_Text(int fontId, const char *text, size_t n) {
1190 return init_I2(advance, lineHeight_Text(fontId)); 1246 return init_I2(advance, lineHeight_Text(fontId));
1191} 1247}
1192 1248
1193static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { 1249static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text, size_t maxLen) {
1194 iText *d = &text_; 1250 iText *d = &text_;
1195 iFont *font = font_Text_(fontId); 1251 iFont *font = font_Text_(fontId);
1196 const iColor clr = get_Color(color & mask_ColorId); 1252 const iColor clr = get_Color(color & mask_ColorId);
@@ -1201,11 +1257,16 @@ static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, i
1201 (color & fillBackground_ColorId ? fillBackground_RunMode : 0) | 1257 (color & fillBackground_ColorId ? fillBackground_RunMode : 0) |
1202 runFlagsFromId_(fontId), 1258 runFlagsFromId_(fontId),
1203 .text = text, 1259 .text = text,
1260 .maxLen = maxLen,
1204 .pos = pos, 1261 .pos = pos,
1205 .xposLayoutBound = xposBound, 1262 .xposLayoutBound = xposBound,
1206 .color = color & mask_ColorId }); 1263 .color = color & mask_ColorId });
1207} 1264}
1208 1265
1266static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) {
1267 drawBoundedN_Text_(fontId, pos, xposBound, color, text, 0);
1268}
1269
1209static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) { 1270static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) {
1210 drawBounded_Text_(fontId, pos, 0, color, text); 1271 drawBounded_Text_(fontId, pos, 0, color, text);
1211} 1272}
@@ -1248,6 +1309,10 @@ void drawRange_Text(int fontId, iInt2 pos, int color, iRangecc text) {
1248 draw_Text_(fontId, pos, color, text); 1309 draw_Text_(fontId, pos, color, text);
1249} 1310}
1250 1311
1312void drawRangeN_Text(int fontId, iInt2 pos, int color, iRangecc text, size_t maxChars) {
1313 drawBoundedN_Text_(fontId, pos, 0, color, text, maxChars);
1314}
1315
1251iInt2 advanceWrapRange_Text(int fontId, int maxWidth, iRangecc text) { 1316iInt2 advanceWrapRange_Text(int fontId, int maxWidth, iRangecc text) {
1252 iInt2 size = zero_I2(); 1317 iInt2 size = zero_I2();
1253 const char *endp; 1318 const char *endp;
@@ -1285,13 +1350,16 @@ void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, con
1285 vprintf_Block(&chars, format, args); 1350 vprintf_Block(&chars, format, args);
1286 va_end(args); 1351 va_end(args);
1287 } 1352 }
1288 const iRangecc text = range_Block(&chars); 1353 drawCenteredRange_Text(fontId, rect, alignVisual, color, range_Block(&chars));
1289 iRect textBounds = alignVisual ? visualBounds_Text(fontId, text) 1354 deinit_Block(&chars);
1355}
1356
1357void drawCenteredRange_Text(int fontId, iRect rect, iBool alignVisual, int color, iRangecc text) {
1358 iRect textBounds = alignVisual ? visualBounds_Text(fontId, text)
1290 : (iRect){ zero_I2(), advanceRange_Text(fontId, text) }; 1359 : (iRect){ zero_I2(), advanceRange_Text(fontId, text) };
1291 textBounds.pos = sub_I2(mid_Rect(rect), mid_Rect(textBounds)); 1360 textBounds.pos = sub_I2(mid_Rect(rect), mid_Rect(textBounds));
1292 textBounds.pos.x = iMax(textBounds.pos.x, left_Rect(rect)); /* keep left edge visible */ 1361 textBounds.pos.x = iMax(textBounds.pos.x, left_Rect(rect)); /* keep left edge visible */
1293 draw_Text_(fontId, textBounds.pos, color, text); 1362 draw_Text_(fontId, textBounds.pos, color, text);
1294 deinit_Block(&chars);
1295} 1363}
1296 1364
1297SDL_Texture *glyphCache_Text(void) { 1365SDL_Texture *glyphCache_Text(void) {