From 7bc2f1b1dd089bfd362ad04dad47ab38c6d9ff37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Ker=C3=A4nen?= Date: Sat, 12 Dec 2020 10:41:44 +0200 Subject: [PATCH 1/1] InputWidget: Allow variable-width fonts --- src/ui/inputwidget.c | 8 +++-- src/ui/text.c | 86 +++++++++++++++++++++++++++++--------------- src/ui/text.h | 2 ++ 3 files changed, 65 insertions(+), 31 deletions(-) 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, -- 2.34.1