diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-12 15:37:20 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-12 15:37:20 +0200 |
commit | ef07b24ad3431496372d3cfd2884b0609b940e27 (patch) | |
tree | e8a23f15dabc3fd5e2d34c87619c698da478f2ef /src/ui/text.c | |
parent | 8864cc5aee3b134b0ffc5f53c1593645f4109f8c (diff) |
New monospace font; flexible mono spacing
Iosevka replaces Fira Mono as the monospace font.
Emojis are now allowed to be wider than normal monospace glyphs. However, if whitespace permits, the original monospacing is restored.
IssueID #73
Diffstat (limited to 'src/ui/text.c')
-rw-r--r-- | src/ui/text.c | 51 |
1 files changed, 34 insertions, 17 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 4229217c..ec240c75 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -120,10 +120,10 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale, | |||
120 | d->xScale *= floorf(advance) / advance; | 120 | d->xScale *= floorf(advance) / advance; |
121 | } | 121 | } |
122 | } | 122 | } |
123 | d->vertOffset = height * (1.0f - scale) / 2; | 123 | d->vertOffset = height * (1.0f - scale) / 2; |
124 | int ascent; | 124 | int ascent; |
125 | stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL); | 125 | stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL); |
126 | d->baseline = ceil(ascent * d->yScale); | 126 | d->baseline = /*ceil*/(ascent * d->yScale); |
127 | d->symbolsFont = symbolsFont; | 127 | d->symbolsFont = symbolsFont; |
128 | d->japaneseFont = regularJapanese_FontId; | 128 | d->japaneseFont = regularJapanese_FontId; |
129 | d->koreanFont = regularKorean_FontId; | 129 | d->koreanFont = regularKorean_FontId; |
@@ -176,8 +176,8 @@ static iText text_; | |||
176 | 176 | ||
177 | static void initFonts_Text_(iText *d) { | 177 | static void initFonts_Text_(iText *d) { |
178 | const float textSize = fontSize_UI * d->contentFontSize; | 178 | const float textSize = fontSize_UI * d->contentFontSize; |
179 | const float monoSize = fontSize_UI * d->contentFontSize / contentScale_Text_ * 0.866f; | 179 | const float monoSize = textSize * 0.71f; //fontSize_UI * d->contentFontSize / contentScale_Text_ * 1.0f; //0.866f; |
180 | const float smallMonoSize = monoSize * 0.866f; | 180 | const float smallMonoSize = monoSize * 0.8f; |
181 | const iBlock *regularFont = &fontNunitoRegular_Embedded; | 181 | const iBlock *regularFont = &fontNunitoRegular_Embedded; |
182 | const iBlock *italicFont = &fontNunitoLightItalic_Embedded; | 182 | const iBlock *italicFont = &fontNunitoLightItalic_Embedded; |
183 | const iBlock *h12Font = &fontNunitoExtraBold_Embedded; | 183 | const iBlock *h12Font = &fontNunitoExtraBold_Embedded; |
@@ -226,12 +226,12 @@ static void initFonts_Text_(iText *d) { | |||
226 | { &fontSourceSansProRegular_Embedded, fontSize_UI, 1.0f, defaultSymbols_FontId }, | 226 | { &fontSourceSansProRegular_Embedded, fontSize_UI, 1.0f, defaultSymbols_FontId }, |
227 | { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.125f, 1.0f, defaultMediumSymbols_FontId }, | 227 | { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.125f, 1.0f, defaultMediumSymbols_FontId }, |
228 | { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.666f, 1.0f, defaultLargeSymbols_FontId }, | 228 | { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.666f, 1.0f, defaultLargeSymbols_FontId }, |
229 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, 1.0f, defaultSymbols_FontId }, | 229 | { &fontIosevkaTermExtended_Embedded, fontSize_UI * 0.866f, 1.0f, defaultSymbols_FontId }, |
230 | { &fontSourceSansProRegular_Embedded, textSize, scaling, symbols_FontId }, | 230 | { &fontSourceSansProRegular_Embedded, textSize, scaling, symbols_FontId }, |
231 | /* content fonts */ | 231 | /* content fonts */ |
232 | { regularFont, textSize, scaling, symbols_FontId }, | 232 | { regularFont, textSize, scaling, symbols_FontId }, |
233 | { &fontFiraMonoRegular_Embedded, monoSize, 1.0f, monospaceSymbols_FontId }, | 233 | { &fontIosevkaTermExtended_Embedded, monoSize, 1.0f, monospaceSymbols_FontId }, |
234 | { &fontFiraMonoRegular_Embedded, smallMonoSize, 1.0f, monospaceSmallSymbols_FontId }, | 234 | { &fontIosevkaTermExtended_Embedded, smallMonoSize, 1.0f, monospaceSmallSymbols_FontId }, |
235 | { regularFont, textSize * 1.200f, scaling, mediumSymbols_FontId }, | 235 | { regularFont, textSize * 1.200f, scaling, mediumSymbols_FontId }, |
236 | { h3Font, textSize * 1.333f, h123Scaling, bigSymbols_FontId }, | 236 | { h3Font, textSize * 1.333f, h123Scaling, bigSymbols_FontId }, |
237 | { italicFont, textSize, scaling, symbols_FontId }, | 237 | { italicFont, textSize, scaling, symbols_FontId }, |
@@ -239,7 +239,7 @@ static void initFonts_Text_(iText *d) { | |||
239 | { h12Font, textSize * 2.000f, h123Scaling, hugeSymbols_FontId }, | 239 | { h12Font, textSize * 2.000f, h123Scaling, hugeSymbols_FontId }, |
240 | { lightFont, textSize * 1.666f, lightScaling, largeSymbols_FontId }, | 240 | { lightFont, textSize * 1.666f, lightScaling, largeSymbols_FontId }, |
241 | /* monospace content fonts */ | 241 | /* monospace content fonts */ |
242 | { &fontFiraMonoRegular_Embedded, textSize, 0.8f, symbols_FontId }, | 242 | { &fontIosevkaTermExtended_Embedded, textSize, 0.866f, symbols_FontId }, |
243 | /* symbol fonts */ | 243 | /* symbol fonts */ |
244 | { &fontSymbola_Embedded, fontSize_UI, 1.0f, defaultSymbols_FontId }, | 244 | { &fontSymbola_Embedded, fontSize_UI, 1.0f, defaultSymbols_FontId }, |
245 | { &fontSymbola_Embedded, fontSize_UI * 1.125f, 1.0f, defaultMediumSymbols_FontId }, | 245 | { &fontSymbola_Embedded, fontSize_UI * 1.125f, 1.0f, defaultMediumSymbols_FontId }, |
@@ -288,7 +288,7 @@ static void initFonts_Text_(iText *d) { | |||
288 | fontData[i].size, | 288 | fontData[i].size, |
289 | fontData[i].scaling, | 289 | fontData[i].scaling, |
290 | fontData[i].symbolsFont, | 290 | fontData[i].symbolsFont, |
291 | fontData[i].ttf == &fontFiraMonoRegular_Embedded); | 291 | fontData[i].ttf == &fontIosevkaTermExtended_Embedded); |
292 | if (i == default_FontId || i == defaultMedium_FontId) { | 292 | if (i == default_FontId || i == defaultMedium_FontId) { |
293 | font->manualKernOnly = iTrue; | 293 | font->manualKernOnly = iTrue; |
294 | } | 294 | } |
@@ -636,6 +636,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
636 | iRect bounds = zero_Rect(); | 636 | iRect bounds = zero_Rect(); |
637 | const iInt2 orig = pos; | 637 | const iInt2 orig = pos; |
638 | float xpos = pos.x; | 638 | float xpos = pos.x; |
639 | float xposExtend = pos.x; /* allows wide glyphs to use more space; restored by whitespace */ | ||
639 | float xposMax = xpos; | 640 | float xposMax = xpos; |
640 | float monoAdvance = 0; | 641 | float monoAdvance = 0; |
641 | iAssert(xposLimit == 0 || isMeasuring_(mode)); | 642 | iAssert(xposLimit == 0 || isMeasuring_(mode)); |
@@ -668,6 +669,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
668 | } | 669 | } |
669 | } | 670 | } |
670 | iChar ch = nextChar_(&chPos, text.end); | 671 | iChar ch = nextChar_(&chPos, text.end); |
672 | iBool isEmoji = isEmoji_Char(ch); | ||
671 | if (ch == 0x200d) { /* zero-width joiner */ | 673 | if (ch == 0x200d) { /* zero-width joiner */ |
672 | /* We don't have the composited Emojis. */ | 674 | /* We don't have the composited Emojis. */ |
673 | if (isEmoji_Char(prevCh)) { | 675 | if (isEmoji_Char(prevCh)) { |
@@ -676,9 +678,19 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
676 | ch = nextChar_(&chPos, text.end); | 678 | ch = nextChar_(&chPos, text.end); |
677 | } | 679 | } |
678 | } | 680 | } |
681 | #if 0 | ||
682 | iChar nextCh = 0; { | ||
683 | /* TODO: Since we're peeking ahead, should use this on the next loop iteration. */ | ||
684 | const char *ncp = chPos; | ||
685 | nextCh = nextChar_(&ncp, text.end); | ||
686 | } | ||
687 | /* VS15: Peek ahead and treat as Emoji font. */ | ||
688 | if (nextCh == emojiVariationSelector_Char) { | ||
689 | isEmoji = iTrue; | ||
690 | } | ||
691 | #endif | ||
679 | if (isVariationSelector_Char(ch)) { | 692 | if (isVariationSelector_Char(ch)) { |
680 | /* TODO: VS15: Should peek ahead for this and prefer the Emoji font. */ | 693 | ch = nextChar_(&chPos, text.end); /* skip it */ |
681 | ch = nextChar_(&chPos, text.end); /* just ignore */ | ||
682 | } | 694 | } |
683 | /* Special instructions. */ { | 695 | /* Special instructions. */ { |
684 | if (ch == 0xad) { /* soft hyphen */ | 696 | if (ch == 0xad) { /* soft hyphen */ |
@@ -703,7 +715,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
703 | } | 715 | } |
704 | } | 716 | } |
705 | if (ch == '\n') { | 717 | if (ch == '\n') { |
706 | xpos = pos.x; | 718 | xpos = xposExtend = pos.x; |
707 | pos.y += d->height; | 719 | pos.y += d->height; |
708 | prevCh = ch; | 720 | prevCh = ch; |
709 | continue; | 721 | continue; |
@@ -711,6 +723,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
711 | if (ch == '\t') { | 723 | if (ch == '\t') { |
712 | const int tabStopWidth = d->height * 8; | 724 | const int tabStopWidth = d->height * 8; |
713 | xpos = pos.x + ((int) ((xpos - pos.x) / tabStopWidth) + 1) * tabStopWidth; | 725 | xpos = pos.x + ((int) ((xpos - pos.x) / tabStopWidth) + 1) * tabStopWidth; |
726 | xposExtend = iMax(xposExtend, xpos); | ||
714 | prevCh = 0; | 727 | prevCh = 0; |
715 | continue; | 728 | continue; |
716 | } | 729 | } |
@@ -728,7 +741,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
728 | } | 741 | } |
729 | } | 742 | } |
730 | const iGlyph *glyph = glyph_Font_(d, ch); | 743 | const iGlyph *glyph = glyph_Font_(d, ch); |
731 | int x1 = xpos; | 744 | int x1 = iMax(xpos, xposExtend); |
732 | const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; | 745 | const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; |
733 | int x2 = x1 + glyph->rect[hoff].size.x; | 746 | int x2 = x1 + glyph->rect[hoff].size.x; |
734 | /* Out of the allotted space? */ | 747 | /* Out of the allotted space? */ |
@@ -765,11 +778,13 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
765 | bounds.size.x = iMax(bounds.size.x, x2 - orig.x); | 778 | bounds.size.x = iMax(bounds.size.x, x2 - orig.x); |
766 | bounds.size.y = iMax(bounds.size.y, pos.y + glyph->font->height - orig.y); | 779 | bounds.size.y = iMax(bounds.size.y, pos.y + glyph->font->height - orig.y); |
767 | } | 780 | } |
781 | /* Symbols and emojis are NOT monospaced, so must conform when the primary font | ||
782 | is monospaced. Except with Japanese script, that's larger than the normal monospace. */ | ||
768 | const iBool useMonoAdvance = | 783 | const iBool useMonoAdvance = |
769 | monoAdvance > 0 && !isJapanese_FontId(fontId_Text_(glyph->font)); | 784 | monoAdvance > 0 && !isJapanese_FontId(fontId_Text_(glyph->font)); |
770 | const float advance = (useMonoAdvance ? monoAdvance : glyph->advance); | 785 | const float advance = (useMonoAdvance ? monoAdvance : glyph->advance); |
771 | if (!isMeasuring_(mode)) { | 786 | if (!isMeasuring_(mode)) { |
772 | if (useMonoAdvance && dst.w > advance && glyph->font != d) { | 787 | if (useMonoAdvance && dst.w > advance && glyph->font != d && !isEmoji) { |
773 | /* Glyphs from a different font may need recentering to look better. */ | 788 | /* Glyphs from a different font may need recentering to look better. */ |
774 | dst.x -= (dst.w - advance) / 2; | 789 | dst.x -= (dst.w - advance) / 2; |
775 | } | 790 | } |
@@ -784,10 +799,12 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
784 | } | 799 | } |
785 | SDL_RenderCopy(text_.render, text_.cache, &src, &dst); | 800 | SDL_RenderCopy(text_.render, text_.cache, &src, &dst); |
786 | } | 801 | } |
787 | /* Symbols and emojis are NOT monospaced, so must conform when the primary font | ||
788 | is monospaced. Except with Japanese script, that's larger than the normal monospace. */ | ||
789 | xpos += advance; | 802 | xpos += advance; |
790 | xposMax = iMax(xposMax, xpos); | 803 | if (!isSpace_Char(ch)) { |
804 | xposExtend += isEmoji ? glyph->advance : advance; | ||
805 | } | ||
806 | xposExtend = iMax(xposExtend, xpos); | ||
807 | xposMax = iMax(xposMax, xposExtend); | ||
791 | if (continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) { | 808 | if (continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) { |
792 | lastWordEnd = chPos; | 809 | lastWordEnd = chPos; |
793 | } | 810 | } |