Lagrange [work/v1.8]

New monospace font; flexible mono spacing

=> ef07b24ad3431496372d3cfd2884b0609b940e27

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 995432cb..394f1b6b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -61,7 +61,7 @@ set (EMBED_RESOURCES
     res/fonts/Tinos-Regular.ttf
     res/fonts/Tinos-Italic.ttf
     res/fonts/Tinos-Bold.ttf
-    res/fonts/FiraMono-Regular.ttf
+    res/fonts/IosevkaTerm-Extended.ttf
     res/fonts/FiraSans-Bold.ttf
     res/fonts/FiraSans-Italic.ttf
     res/fonts/FiraSans-Light.ttf
diff --git a/res/about/license.gmi b/res/about/license.gmi
index 0ea0897a..d5ffe3c4 100644
--- a/res/about/license.gmi
+++ b/res/about/license.gmi
@@ -100,7 +100,8 @@ The libunistring library is covered by the GNU Lesser General Public License (LG
 
 This application uses fonts licensed under the Open Font License.
 
-=> https://github.com/mozilla/Fira/blob/master/LICENSE  Fira Sans, Fira Mono
+=> https://github.com/mozilla/Fira/blob/master/LICENSE  Fira Sans
+=> https://typeof.net/Iosevka/  Iosevka
 => https://github.com/googlefonts/literata/blob/master/OFL.txt  Literata
 => https://github.com/google/fonts/blob/master/ofl/nanumgothic/OFL.txt  Nanum Gothic
 => https://www.google.com/get/noto/help/cjk/  Noto Sans CJK JP
diff --git a/res/about/version.gmi b/res/about/version.gmi
index 42475e13..1932baa1 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -8,9 +8,9 @@
 
 ## 0.13
 * Support for Internationalized Domain Names (IDN) in network requests.
-* IDNs show up in decoded form in the UI.
+* UI shows decoded IDNs.
 * Percent-encoded Unicode characters in URL paths are decoded for the UI, and encoded in outgoing requests.
-* Added option to disable decoding of percent-encoded paths.
+* Added option to disable decoding of percent-encoded paths for the UI.
 * Quick search via URL bar shows entries from subscribed feeds.
 * Added keybindings for zooming.
 * Improved usability of page content searching (${CTRL+}F, Escape).
@@ -19,6 +19,7 @@
 * Fixed handling of Unicode joiners and modifiers (by ignoring them, since we lack the glyphs).
 * Fixed a layout issue with sidebars where the bottommost content line was occasionally not visible.
 * Fixed exit when a hook program didn't read its input.
+* Fixed use of variable-width fonts in input fields.
 * Fixed crash when using an identity (with LibreSSL on OpenBSD).
 
 ## 0.12.1
diff --git a/res/fonts/FiraMono-Regular.ttf b/res/fonts/FiraMono-Regular.ttf
deleted file mode 100644
index 59e1e1a1..00000000
Binary files a/res/fonts/FiraMono-Regular.ttf and /dev/null differ
diff --git a/res/fonts/IosevkaTerm-Extended.ttf b/res/fonts/IosevkaTerm-Extended.ttf
new file mode 100644
index 00000000..16989a85
Binary files /dev/null and b/res/fonts/IosevkaTerm-Extended.ttf differ
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,
             d->xScale *= floorf(advance) / advance;
         }
     }
-    d->vertOffset = height * (1.0f - scale) / 2;
+    d->vertOffset = height * (1.0f - scale) / 2;    
     int ascent;
     stbtt_GetFontVMetrics(&d->font, &ascent, NULL, NULL);
-    d->baseline     = ceil(ascent * d->yScale);
+    d->baseline     = /*ceil*/(ascent * d->yScale);
     d->symbolsFont  = symbolsFont;
     d->japaneseFont = regularJapanese_FontId;
     d->koreanFont   = regularKorean_FontId;
@@ -176,8 +176,8 @@ static iText text_;
 
 static void initFonts_Text_(iText *d) {
     const float textSize = fontSize_UI * d->contentFontSize;
-    const float monoSize = fontSize_UI * d->contentFontSize / contentScale_Text_ * 0.866f;
-    const float smallMonoSize = monoSize * 0.866f;
+    const float monoSize = textSize * 0.71f; //fontSize_UI * d->contentFontSize / contentScale_Text_ * 1.0f; //0.866f;
+    const float smallMonoSize = monoSize * 0.8f;
     const iBlock *regularFont  = &fontNunitoRegular_Embedded;
     const iBlock *italicFont   = &fontNunitoLightItalic_Embedded;
     const iBlock *h12Font      = &fontNunitoExtraBold_Embedded;
@@ -226,12 +226,12 @@ static void initFonts_Text_(iText *d) {
         { &fontSourceSansProRegular_Embedded, fontSize_UI,          1.0f, defaultSymbols_FontId },
         { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.125f, 1.0f, defaultMediumSymbols_FontId },
         { &fontSourceSansProRegular_Embedded, fontSize_UI * 1.666f, 1.0f, defaultLargeSymbols_FontId },
-        { &fontFiraMonoRegular_Embedded,      fontSize_UI * 0.866f, 1.0f, defaultSymbols_FontId },
+        { &fontIosevkaTermExtended_Embedded,  fontSize_UI * 0.866f, 1.0f, defaultSymbols_FontId },
         { &fontSourceSansProRegular_Embedded, textSize,             scaling, symbols_FontId },
         /* content fonts */
         { regularFont,                        textSize,             scaling,      symbols_FontId },
-        { &fontFiraMonoRegular_Embedded,      monoSize,             1.0f,         monospaceSymbols_FontId },
-        { &fontFiraMonoRegular_Embedded,      smallMonoSize,        1.0f,         monospaceSmallSymbols_FontId },
+        { &fontIosevkaTermExtended_Embedded,  monoSize,             1.0f,         monospaceSymbols_FontId },
+        { &fontIosevkaTermExtended_Embedded,  smallMonoSize,        1.0f,         monospaceSmallSymbols_FontId },
         { regularFont,                        textSize * 1.200f,    scaling,      mediumSymbols_FontId },
         { h3Font,                             textSize * 1.333f,    h123Scaling,  bigSymbols_FontId },
         { italicFont,                         textSize,             scaling,      symbols_FontId },
@@ -239,7 +239,7 @@ static void initFonts_Text_(iText *d) {
         { h12Font,                            textSize * 2.000f,    h123Scaling,  hugeSymbols_FontId },
         { lightFont,                          textSize * 1.666f,    lightScaling, largeSymbols_FontId },
         /* monospace content fonts */
-        { &fontFiraMonoRegular_Embedded,      textSize,             0.8f, symbols_FontId },
+        { &fontIosevkaTermExtended_Embedded,  textSize,             0.866f, symbols_FontId },
         /* symbol fonts */
         { &fontSymbola_Embedded,              fontSize_UI,          1.0f, defaultSymbols_FontId },
         { &fontSymbola_Embedded,              fontSize_UI * 1.125f, 1.0f, defaultMediumSymbols_FontId },
@@ -288,7 +288,7 @@ static void initFonts_Text_(iText *d) {
                   fontData[i].size,
                   fontData[i].scaling,
                   fontData[i].symbolsFont,
-                  fontData[i].ttf == &fontFiraMonoRegular_Embedded);
+                  fontData[i].ttf == &fontIosevkaTermExtended_Embedded);
         if (i == default_FontId || i == defaultMedium_FontId) {
             font->manualKernOnly = iTrue;
         }
@@ -636,6 +636,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
     iRect bounds = zero_Rect();
     const iInt2 orig = pos;
     float xpos = pos.x;
+    float xposExtend = pos.x; /* allows wide glyphs to use more space; restored by whitespace */
     float xposMax = xpos;
     float monoAdvance = 0;
     iAssert(xposLimit == 0 || isMeasuring_(mode));
@@ -668,6 +669,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
             }
         }
         iChar ch = nextChar_(&chPos, text.end);
+        iBool isEmoji = isEmoji_Char(ch);
         if (ch == 0x200d) { /* zero-width joiner */
             /* We don't have the composited Emojis. */
             if (isEmoji_Char(prevCh)) {
@@ -676,9 +678,19 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
                 ch = nextChar_(&chPos, text.end);
             }
         }
+#if 0
+        iChar nextCh = 0; {
+            /* TODO: Since we're peeking ahead, should use this on the next loop iteration. */
+            const char *ncp = chPos;
+            nextCh = nextChar_(&ncp, text.end);
+        }
+        /* VS15: Peek ahead and treat as Emoji font. */
+        if (nextCh == emojiVariationSelector_Char) {
+            isEmoji = iTrue;
+        }
+#endif
         if (isVariationSelector_Char(ch)) {
-            /* TODO: VS15: Should peek ahead for this and prefer the Emoji font. */
-            ch = nextChar_(&chPos, text.end); /* just ignore */
+            ch = nextChar_(&chPos, text.end); /* skip it */
         }
         /* Special instructions. */ {
             if (ch == 0xad) { /* soft hyphen */
@@ -703,7 +715,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
                 }
             }
             if (ch == '\n') {
-                xpos = pos.x;
+                xpos = xposExtend = pos.x;
                 pos.y += d->height;
                 prevCh = ch;
                 continue;
@@ -711,6 +723,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
             if (ch == '\t') {
                 const int tabStopWidth = d->height * 8;
                 xpos = pos.x + ((int) ((xpos - pos.x) / tabStopWidth) + 1) * tabStopWidth;
+                xposExtend = iMax(xposExtend, xpos);
                 prevCh = 0;
                 continue;
             }
@@ -728,7 +741,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
             }
         }
         const iGlyph *glyph = glyph_Font_(d, ch);
-        int x1 = xpos;
+        int x1 = iMax(xpos, xposExtend);
         const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0;
         int x2 = x1 + glyph->rect[hoff].size.x;
         /* Out of the allotted space? */
@@ -765,11 +778,13 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
             bounds.size.x = iMax(bounds.size.x, x2 - orig.x);
             bounds.size.y = iMax(bounds.size.y, pos.y + glyph->font->height - orig.y);
         }
+        /* Symbols and emojis are NOT monospaced, so must conform when the primary font
+           is monospaced. Except with Japanese script, that's larger than the normal monospace. */
         const iBool useMonoAdvance =
             monoAdvance > 0 && !isJapanese_FontId(fontId_Text_(glyph->font));
         const float advance = (useMonoAdvance ? monoAdvance : glyph->advance);
         if (!isMeasuring_(mode)) {
-            if (useMonoAdvance && dst.w > advance && glyph->font != d) {
+            if (useMonoAdvance && dst.w > advance && glyph->font != d && !isEmoji) {
                 /* Glyphs from a different font may need recentering to look better. */
                 dst.x -= (dst.w - advance) / 2;
             }
@@ -784,10 +799,12 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
             }
             SDL_RenderCopy(text_.render, text_.cache, &src, &dst);
         }
-        /* Symbols and emojis are NOT monospaced, so must conform when the primary font
-           is monospaced. Except with Japanese script, that's larger than the normal monospace. */
         xpos += advance;
-        xposMax = iMax(xposMax, xpos);
+        if (!isSpace_Char(ch)) {
+            xposExtend += isEmoji ? glyph->advance : advance;
+        }
+        xposExtend = iMax(xposExtend, xpos);
+        xposMax    = iMax(xposMax, xposExtend);
         if (continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) {
             lastWordEnd = chPos;
         }
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) {
            c == 0xfeff;
 }
 iLocalDef iBool isEmoji_Char(iChar c) {
-    return (c >= 0x1f300 && c < 0x1f700) || (c >= 0x1f900 && c <= 0x1f9ff);
+    return (c >= 0x1f300 && c < 0x1f700) || (c >= 0x1f7e0 && c <= 0x1f7eb) ||
+           (c >= 0x1f900 && c <= 0x1f9ff);
 }
 
 #define emojiVariationSelector_Char     ((iChar) 0xfe0f)
@@ -141,21 +142,21 @@ enum iTextFont {
 
 extern int gap_Text; /* affected by content font size */
 
-void    init_Text           (SDL_Renderer *);
-void    deinit_Text         (void);
+void    init_Text               (SDL_Renderer *);
+void    deinit_Text             (void);
 
 void    setContentFont_Text     (enum iTextFont font);
 void    setHeadingFont_Text     (enum iTextFont font);
 void    setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */
 void    resetFonts_Text         (void);
 
-int     lineHeight_Text     (int fontId);
-iInt2   measure_Text        (int fontId, const char *text);
-iInt2   measureRange_Text   (int fontId, iRangecc text);
-iRect   visualBounds_Text   (int fontId, iRangecc text);
-iInt2   advance_Text        (int fontId, const char *text);
-iInt2   advanceN_Text       (int fontId, const char *text, size_t n); /* `n` in characters */
-iInt2   advanceRange_Text   (int fontId, iRangecc text);
+int     lineHeight_Text         (int fontId);
+iInt2   measure_Text            (int fontId, const char *text);
+iInt2   measureRange_Text       (int fontId, iRangecc text);
+iRect   visualBounds_Text       (int fontId, iRangecc text);
+iInt2   advance_Text            (int fontId, const char *text);
+iInt2   advanceN_Text           (int fontId, const char *text, size_t n); /* `n` in characters */
+iInt2   advanceRange_Text       (int fontId, iRangecc text);
 iInt2   advanceWrapRange_Text   (int fontId, int maxWidth, iRangecc text);
 
 iInt2   tryAdvance_Text         (int fontId, iRangecc text, int width, const char **endPos);
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.8/cdiff/ef07b24ad3431496372d3cfd2884b0609b940e27
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
65.573164 milliseconds
Gemini-to-HTML Time
0.568689 milliseconds

This content has been proxied by September (ba2dc).