summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-12-12 15:37:20 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-12-12 15:37:20 +0200
commitef07b24ad3431496372d3cfd2884b0609b940e27 (patch)
treee8a23f15dabc3fd5e2d34c87619c698da478f2ef /src/ui
parent8864cc5aee3b134b0ffc5f53c1593645f4109f8c (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')
-rw-r--r--src/ui/text.c51
-rw-r--r--src/ui/text.h21
2 files changed, 45 insertions, 27 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
177static void initFonts_Text_(iText *d) { 177static 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 }
diff --git a/src/ui/text.h b/src/ui/text.h
index 6728bb62..5867a84b 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -127,7 +127,8 @@ iLocalDef iBool isDefaultIgnorable_Char(iChar c) {
127 c == 0xfeff; 127 c == 0xfeff;
128} 128}
129iLocalDef iBool isEmoji_Char(iChar c) { 129iLocalDef iBool isEmoji_Char(iChar c) {
130 return (c >= 0x1f300 && c < 0x1f700) || (c >= 0x1f900 && c <= 0x1f9ff); 130 return (c >= 0x1f300 && c < 0x1f700) || (c >= 0x1f7e0 && c <= 0x1f7eb) ||
131 (c >= 0x1f900 && c <= 0x1f9ff);
131} 132}
132 133
133#define emojiVariationSelector_Char ((iChar) 0xfe0f) 134#define emojiVariationSelector_Char ((iChar) 0xfe0f)
@@ -141,21 +142,21 @@ enum iTextFont {
141 142
142extern int gap_Text; /* affected by content font size */ 143extern int gap_Text; /* affected by content font size */
143 144
144void init_Text (SDL_Renderer *); 145void init_Text (SDL_Renderer *);
145void deinit_Text (void); 146void deinit_Text (void);
146 147
147void setContentFont_Text (enum iTextFont font); 148void setContentFont_Text (enum iTextFont font);
148void setHeadingFont_Text (enum iTextFont font); 149void setHeadingFont_Text (enum iTextFont font);
149void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ 150void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */
150void resetFonts_Text (void); 151void resetFonts_Text (void);
151 152
152int lineHeight_Text (int fontId); 153int lineHeight_Text (int fontId);
153iInt2 measure_Text (int fontId, const char *text); 154iInt2 measure_Text (int fontId, const char *text);
154iInt2 measureRange_Text (int fontId, iRangecc text); 155iInt2 measureRange_Text (int fontId, iRangecc text);
155iRect visualBounds_Text (int fontId, iRangecc text); 156iRect visualBounds_Text (int fontId, iRangecc text);
156iInt2 advance_Text (int fontId, const char *text); 157iInt2 advance_Text (int fontId, const char *text);
157iInt2 advanceN_Text (int fontId, const char *text, size_t n); /* `n` in characters */ 158iInt2 advanceN_Text (int fontId, const char *text, size_t n); /* `n` in characters */
158iInt2 advanceRange_Text (int fontId, iRangecc text); 159iInt2 advanceRange_Text (int fontId, iRangecc text);
159iInt2 advanceWrapRange_Text (int fontId, int maxWidth, iRangecc text); 160iInt2 advanceWrapRange_Text (int fontId, int maxWidth, iRangecc text);
160 161
161iInt2 tryAdvance_Text (int fontId, iRangecc text, int width, const char **endPos); 162iInt2 tryAdvance_Text (int fontId, iRangecc text, int width, const char **endPos);