Lagrange [release]

InputWidget: Allow variable-width fonts

=> 7bc2f1b1dd089bfd362ad04dad47ab38c6d9ff37

diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 4f9bd367..018daec8 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -98,7 +98,7 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
     init_Array(&d->oldText, sizeof(iChar));
     init_String(&d->hint);
     init_Array(&d->undoStack, sizeof(iInputUndo));
-    d->font             = uiInput_FontId;
+    d->font             = uiInput_FontId | alwaysVariableFlag_FontId;
     d->cursor           = 0;
     d->lastCursor       = 0;
     d->inFlags          = eatEscape_InputWidgetFlag;
@@ -789,11 +789,13 @@ static void draw_InputWidget_(const iInputWidget *d) {
         else {
             initCStr_String(&cur, " ");
         }
+        /* The `gap_UI` offsets below are a hack. They are used because for some reason the
+           cursor rect and the glyph inside don't quite position like during `run_Text_()`. */
         const iInt2 prefixSize = advanceN_Text(d->font, cstr_String(text), d->cursor);
         const iInt2 curPos     = addX_I2(textOrigin, prefixSize.x);
-        const iRect curRect    = { curPos, addX_I2(advance_Text(d->font, cstr_String(&cur)), 1) };
+        const iRect curRect    = { curPos, addX_I2(advance_Text(d->font, cstr_String(&cur)), iMin(2, gap_UI / 4)) };
         fillRect_Paint(&p, curRect, uiInputCursor_ColorId);
-        draw_Text(d->font, curPos, uiInputCursorText_ColorId, "%s", cstr_String(&cur));
+        draw_Text(d->font, addX_I2(curPos, iMin(1, gap_UI / 8)), uiInputCursorText_ColorId, "%s", cstr_String(&cur));
         deinit_String(&cur);
     }
     delete_String(text);
diff --git a/src/ui/text.c b/src/ui/text.c
index 3e1d732a..4229217c 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -426,7 +426,7 @@ void resetFonts_Text(void) {
 }
 
 iLocalDef iFont *font_Text_(enum iFontId id) {
-    return &text_.fonts[id];
+    return &text_.fonts[id & mask_FontId];
 }
 
 static void freeBmp_(void *ptr) {
@@ -582,11 +582,14 @@ static const iGlyph *glyph_Font_(iFont *d, iChar ch) {
 }
 
 enum iRunMode {
-    measure_RunMode,
-    measureNoWrap_RunMode,
-    measureVisual_RunMode, /* actual visible bounding box of the glyph, e.g., for icons */
-    draw_RunMode,
-    drawPermanentColor_RunMode
+    measure_RunMode    = 0,
+    draw_RunMode       = 1,
+    modeMask_RunMode   = 0x00ff,
+    flagsMask_RunMode  = 0xff00,
+    noWrapFlag_RunMode = iBit(9),
+    visualFlag_RunMode = iBit(10), /* actual visible bounding box of the glyph, e.g., for icons */
+    permanentColorFlag_RunMode      = iBit(11),
+    alwaysVariableWidthFlag_RunMode = iBit(12),
 };
 
 static iChar nextChar_(const char **chPos, const char *end) {
@@ -625,8 +628,7 @@ iLocalDef iBool isWrapBoundary_(iChar prevC, iChar c) {
 }
 
 iLocalDef iBool isMeasuring_(enum iRunMode mode) {
-    return mode == measure_RunMode || mode == measureNoWrap_RunMode ||
-           mode == measureVisual_RunMode;
+    return (mode & modeMask_RunMode) == measure_RunMode;
 }
 
 static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos,
@@ -642,7 +644,8 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
         *continueFrom_out = text.end;
     }
     iChar prevCh = 0;
-    if (d->isMonospaced) {
+    const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode);
+    if (isMonospaced) {
         monoAdvance = glyph_Font_(d, 'M')->advance;
     }
     for (const char *chPos = text.start; chPos != text.end; ) {
@@ -654,7 +657,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
             iRegExpMatch m;
             init_RegExpMatch(&m);
             if (match_RegExp(text_.ansiEscape, chPos, text.end - chPos, &m)) {
-                if (mode == draw_RunMode) {
+                if (mode & draw_RunMode) {
                     /* Change the color. */
                     const iColor clr =
                         ansiForeground_Color(capturedRange_RegExpMatch(&m, 1), tmParagraph_ColorId);
@@ -713,7 +716,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
             }
             if (ch == '\r') {
                 const iChar esc = nextChar_(&chPos, text.end);
-                if (mode == draw_RunMode) {
+                if (mode & draw_RunMode) {
                     const iColor clr = get_Color(esc - asciiBase_ColorEscape);
                     SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b);
                 }
@@ -743,8 +746,14 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
                          pos.y + glyph->font->baseline + glyph->d[hoff].y,
                          glyph->rect[hoff].size.x,
                          glyph->rect[hoff].size.y };
+        if (glyph->font != d) {
+            if (glyph->font->height > d->height) {
+                /* Center-align vertically so the baseline isn't totally offset. */
+                dst.y -= (glyph->font->height - d->height) / 2;
+            }
+        }
         /* Update the bounding box. */
-        if (mode == measureVisual_RunMode) {
+        if (mode & visualFlag_RunMode) {
             if (isEmpty_Rect(bounds)) {
                 bounds = init_Rect(dst.x, dst.y, dst.w, dst.h);
             }
@@ -779,12 +788,12 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
            is monospaced. Except with Japanese script, that's larger than the normal monospace. */
         xpos += advance;
         xposMax = iMax(xposMax, xpos);
-        if (continueFrom_out && (mode == measureNoWrap_RunMode || isWrapBoundary_(prevCh, ch))) {
+        if (continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) {
             lastWordEnd = chPos;
         }
 #if defined (LAGRANGE_ENABLE_KERNING)
         /* Check the next character. */
-        if (!d->isMonospaced && glyph->font == d) {
+        if (!isMonospaced && glyph->font == d) {
             /* TODO: No need to decode the next char twice; check this on the next iteration. */
             const char *peek = chPos;
             const iChar next = nextChar_(&peek, text.end);
@@ -805,14 +814,14 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
 }
 
 int lineHeight_Text(int fontId) {
-    return text_.fonts[fontId].height;
+    return font_Text_(fontId)->height;
 }
 
 iInt2 measureRange_Text(int fontId, iRangecc text) {
     if (isEmpty_Range(&text)) {
         return init_I2(0, lineHeight_Text(fontId));
     }
-    return run_Font_(&text_.fonts[fontId],
+    return run_Font_(font_Text_(fontId),
                      measure_RunMode,
                      text,
                      iInvalidSize,
@@ -823,18 +832,32 @@ iInt2 measureRange_Text(int fontId, iRangecc text) {
 }
 
 iRect visualBounds_Text(int fontId, iRangecc text) {
-    return run_Font_(
-        font_Text_(fontId), measureVisual_RunMode, text, iInvalidSize, zero_I2(), 0, NULL, NULL);
+    return run_Font_(font_Text_(fontId),
+                     measure_RunMode | visualFlag_RunMode,
+                     text,
+                     iInvalidSize,
+                     zero_I2(),
+                     0,
+                     NULL,
+                     NULL);
 }
 
 iInt2 measure_Text(int fontId, const char *text) {
     return measureRange_Text(fontId, range_CStr(text));
 }
 
+static int runFlagsFromId_(enum iFontId fontId) {
+    int runFlags = 0;
+    if (fontId & alwaysVariableFlag_FontId) {
+        runFlags |= alwaysVariableWidthFlag_RunMode;
+    }
+    return runFlags;
+}
+
 iInt2 advanceRange_Text(int fontId, iRangecc text) {
     int advance;
-    const int height = run_Font_(&text_.fonts[fontId],
-                                 measure_RunMode,
+    const int height = run_Font_(font_Text_(fontId),
+                                 measure_RunMode | runFlagsFromId_(fontId),
                                  text,
                                  iInvalidSize,
                                  zero_I2(),
@@ -847,8 +870,8 @@ iInt2 advanceRange_Text(int fontId, iRangecc text) {
 
 iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) {
     int advance;
-    const int height = run_Font_(&text_.fonts[fontId],
-                                 measure_RunMode,
+    const int height = run_Font_(font_Text_(fontId),
+                                 measure_RunMode | runFlagsFromId_(fontId),
                                  text,
                                  iInvalidSize,
                                  zero_I2(),
@@ -861,8 +884,8 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos)
 
 iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) {
     int advance;
-    const int height = run_Font_(&text_.fonts[fontId],
-                                 measureNoWrap_RunMode,
+    const int height = run_Font_(font_Text_(fontId),
+                                 measure_RunMode | noWrapFlag_RunMode | runFlagsFromId_(fontId),
                                  text,
                                  iInvalidSize,
                                  zero_I2(),
@@ -882,8 +905,14 @@ iInt2 advanceN_Text(int fontId, const char *text, size_t n) {
         return init_I2(0, lineHeight_Text(fontId));
     }
     int advance;
-    run_Font_(
-        &text_.fonts[fontId], measure_RunMode, range_CStr(text), n, zero_I2(), 0, NULL, &advance);
+    run_Font_(font_Text_(fontId),
+              measure_RunMode | runFlagsFromId_(fontId),
+              range_CStr(text),
+              n,
+              zero_I2(),
+              0,
+              NULL,
+              &advance);
     return init_I2(advance, lineHeight_Text(fontId));
 }
 
@@ -891,8 +920,9 @@ static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) {
     iText *d = &text_;
     const iColor clr = get_Color(color & mask_ColorId);
     SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b);
-    run_Font_(&d->fonts[fontId],
-              color & permanent_ColorId ? drawPermanentColor_RunMode : draw_RunMode,
+    run_Font_(font_Text_(fontId),
+              draw_RunMode | (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) |
+                  runFlagsFromId_(fontId),
               text,
               iInvalidSize,
               pos,
diff --git a/src/ui/text.h b/src/ui/text.h
index a0b2dc1a..6728bb62 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -91,6 +91,8 @@ enum iFontId {
 
     /* Meta: */
     fromSymbolsToEmojiOffset_FontId = 10,
+    mask_FontId                     = 0xffff,
+    alwaysVariableFlag_FontId       = 0x10000,
 
     /* UI fonts: */
     uiLabel_FontId          = default_FontId,
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/release/cdiff/7bc2f1b1dd089bfd362ad04dad47ab38c6d9ff37
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
28.774711 milliseconds
Gemini-to-HTML Time
0.581554 milliseconds

This content has been proxied by September (ba2dc).