diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-12 10:41:44 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-12-12 10:41:44 +0200 |
commit | 7bc2f1b1dd089bfd362ad04dad47ab38c6d9ff37 (patch) | |
tree | f0e2f02a5adb9038401c2713772c2bc82a2c57c3 /src/ui/text.c | |
parent | 96a7ee14151b250e0587ec81a50afb7fe34c7567 (diff) |
InputWidget: Allow variable-width fonts
Diffstat (limited to 'src/ui/text.c')
-rw-r--r-- | src/ui/text.c | 86 |
1 files changed, 58 insertions, 28 deletions
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) { | |||
426 | } | 426 | } |
427 | 427 | ||
428 | iLocalDef iFont *font_Text_(enum iFontId id) { | 428 | iLocalDef iFont *font_Text_(enum iFontId id) { |
429 | return &text_.fonts[id]; | 429 | return &text_.fonts[id & mask_FontId]; |
430 | } | 430 | } |
431 | 431 | ||
432 | static void freeBmp_(void *ptr) { | 432 | static void freeBmp_(void *ptr) { |
@@ -582,11 +582,14 @@ static const iGlyph *glyph_Font_(iFont *d, iChar ch) { | |||
582 | } | 582 | } |
583 | 583 | ||
584 | enum iRunMode { | 584 | enum iRunMode { |
585 | measure_RunMode, | 585 | measure_RunMode = 0, |
586 | measureNoWrap_RunMode, | 586 | draw_RunMode = 1, |
587 | measureVisual_RunMode, /* actual visible bounding box of the glyph, e.g., for icons */ | 587 | modeMask_RunMode = 0x00ff, |
588 | draw_RunMode, | 588 | flagsMask_RunMode = 0xff00, |
589 | drawPermanentColor_RunMode | 589 | noWrapFlag_RunMode = iBit(9), |
590 | visualFlag_RunMode = iBit(10), /* actual visible bounding box of the glyph, e.g., for icons */ | ||
591 | permanentColorFlag_RunMode = iBit(11), | ||
592 | alwaysVariableWidthFlag_RunMode = iBit(12), | ||
590 | }; | 593 | }; |
591 | 594 | ||
592 | static iChar nextChar_(const char **chPos, const char *end) { | 595 | static iChar nextChar_(const char **chPos, const char *end) { |
@@ -625,8 +628,7 @@ iLocalDef iBool isWrapBoundary_(iChar prevC, iChar c) { | |||
625 | } | 628 | } |
626 | 629 | ||
627 | iLocalDef iBool isMeasuring_(enum iRunMode mode) { | 630 | iLocalDef iBool isMeasuring_(enum iRunMode mode) { |
628 | return mode == measure_RunMode || mode == measureNoWrap_RunMode || | 631 | return (mode & modeMask_RunMode) == measure_RunMode; |
629 | mode == measureVisual_RunMode; | ||
630 | } | 632 | } |
631 | 633 | ||
632 | static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, | 634 | 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 | |||
642 | *continueFrom_out = text.end; | 644 | *continueFrom_out = text.end; |
643 | } | 645 | } |
644 | iChar prevCh = 0; | 646 | iChar prevCh = 0; |
645 | if (d->isMonospaced) { | 647 | const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode); |
648 | if (isMonospaced) { | ||
646 | monoAdvance = glyph_Font_(d, 'M')->advance; | 649 | monoAdvance = glyph_Font_(d, 'M')->advance; |
647 | } | 650 | } |
648 | for (const char *chPos = text.start; chPos != text.end; ) { | 651 | 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 | |||
654 | iRegExpMatch m; | 657 | iRegExpMatch m; |
655 | init_RegExpMatch(&m); | 658 | init_RegExpMatch(&m); |
656 | if (match_RegExp(text_.ansiEscape, chPos, text.end - chPos, &m)) { | 659 | if (match_RegExp(text_.ansiEscape, chPos, text.end - chPos, &m)) { |
657 | if (mode == draw_RunMode) { | 660 | if (mode & draw_RunMode) { |
658 | /* Change the color. */ | 661 | /* Change the color. */ |
659 | const iColor clr = | 662 | const iColor clr = |
660 | ansiForeground_Color(capturedRange_RegExpMatch(&m, 1), tmParagraph_ColorId); | 663 | 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 | |||
713 | } | 716 | } |
714 | if (ch == '\r') { | 717 | if (ch == '\r') { |
715 | const iChar esc = nextChar_(&chPos, text.end); | 718 | const iChar esc = nextChar_(&chPos, text.end); |
716 | if (mode == draw_RunMode) { | 719 | if (mode & draw_RunMode) { |
717 | const iColor clr = get_Color(esc - asciiBase_ColorEscape); | 720 | const iColor clr = get_Color(esc - asciiBase_ColorEscape); |
718 | SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b); | 721 | SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b); |
719 | } | 722 | } |
@@ -743,8 +746,14 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
743 | pos.y + glyph->font->baseline + glyph->d[hoff].y, | 746 | pos.y + glyph->font->baseline + glyph->d[hoff].y, |
744 | glyph->rect[hoff].size.x, | 747 | glyph->rect[hoff].size.x, |
745 | glyph->rect[hoff].size.y }; | 748 | glyph->rect[hoff].size.y }; |
749 | if (glyph->font != d) { | ||
750 | if (glyph->font->height > d->height) { | ||
751 | /* Center-align vertically so the baseline isn't totally offset. */ | ||
752 | dst.y -= (glyph->font->height - d->height) / 2; | ||
753 | } | ||
754 | } | ||
746 | /* Update the bounding box. */ | 755 | /* Update the bounding box. */ |
747 | if (mode == measureVisual_RunMode) { | 756 | if (mode & visualFlag_RunMode) { |
748 | if (isEmpty_Rect(bounds)) { | 757 | if (isEmpty_Rect(bounds)) { |
749 | bounds = init_Rect(dst.x, dst.y, dst.w, dst.h); | 758 | bounds = init_Rect(dst.x, dst.y, dst.w, dst.h); |
750 | } | 759 | } |
@@ -779,12 +788,12 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
779 | is monospaced. Except with Japanese script, that's larger than the normal monospace. */ | 788 | is monospaced. Except with Japanese script, that's larger than the normal monospace. */ |
780 | xpos += advance; | 789 | xpos += advance; |
781 | xposMax = iMax(xposMax, xpos); | 790 | xposMax = iMax(xposMax, xpos); |
782 | if (continueFrom_out && (mode == measureNoWrap_RunMode || isWrapBoundary_(prevCh, ch))) { | 791 | if (continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) { |
783 | lastWordEnd = chPos; | 792 | lastWordEnd = chPos; |
784 | } | 793 | } |
785 | #if defined (LAGRANGE_ENABLE_KERNING) | 794 | #if defined (LAGRANGE_ENABLE_KERNING) |
786 | /* Check the next character. */ | 795 | /* Check the next character. */ |
787 | if (!d->isMonospaced && glyph->font == d) { | 796 | if (!isMonospaced && glyph->font == d) { |
788 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ | 797 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ |
789 | const char *peek = chPos; | 798 | const char *peek = chPos; |
790 | const iChar next = nextChar_(&peek, text.end); | 799 | 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 | |||
805 | } | 814 | } |
806 | 815 | ||
807 | int lineHeight_Text(int fontId) { | 816 | int lineHeight_Text(int fontId) { |
808 | return text_.fonts[fontId].height; | 817 | return font_Text_(fontId)->height; |
809 | } | 818 | } |
810 | 819 | ||
811 | iInt2 measureRange_Text(int fontId, iRangecc text) { | 820 | iInt2 measureRange_Text(int fontId, iRangecc text) { |
812 | if (isEmpty_Range(&text)) { | 821 | if (isEmpty_Range(&text)) { |
813 | return init_I2(0, lineHeight_Text(fontId)); | 822 | return init_I2(0, lineHeight_Text(fontId)); |
814 | } | 823 | } |
815 | return run_Font_(&text_.fonts[fontId], | 824 | return run_Font_(font_Text_(fontId), |
816 | measure_RunMode, | 825 | measure_RunMode, |
817 | text, | 826 | text, |
818 | iInvalidSize, | 827 | iInvalidSize, |
@@ -823,18 +832,32 @@ iInt2 measureRange_Text(int fontId, iRangecc text) { | |||
823 | } | 832 | } |
824 | 833 | ||
825 | iRect visualBounds_Text(int fontId, iRangecc text) { | 834 | iRect visualBounds_Text(int fontId, iRangecc text) { |
826 | return run_Font_( | 835 | return run_Font_(font_Text_(fontId), |
827 | font_Text_(fontId), measureVisual_RunMode, text, iInvalidSize, zero_I2(), 0, NULL, NULL); | 836 | measure_RunMode | visualFlag_RunMode, |
837 | text, | ||
838 | iInvalidSize, | ||
839 | zero_I2(), | ||
840 | 0, | ||
841 | NULL, | ||
842 | NULL); | ||
828 | } | 843 | } |
829 | 844 | ||
830 | iInt2 measure_Text(int fontId, const char *text) { | 845 | iInt2 measure_Text(int fontId, const char *text) { |
831 | return measureRange_Text(fontId, range_CStr(text)); | 846 | return measureRange_Text(fontId, range_CStr(text)); |
832 | } | 847 | } |
833 | 848 | ||
849 | static int runFlagsFromId_(enum iFontId fontId) { | ||
850 | int runFlags = 0; | ||
851 | if (fontId & alwaysVariableFlag_FontId) { | ||
852 | runFlags |= alwaysVariableWidthFlag_RunMode; | ||
853 | } | ||
854 | return runFlags; | ||
855 | } | ||
856 | |||
834 | iInt2 advanceRange_Text(int fontId, iRangecc text) { | 857 | iInt2 advanceRange_Text(int fontId, iRangecc text) { |
835 | int advance; | 858 | int advance; |
836 | const int height = run_Font_(&text_.fonts[fontId], | 859 | const int height = run_Font_(font_Text_(fontId), |
837 | measure_RunMode, | 860 | measure_RunMode | runFlagsFromId_(fontId), |
838 | text, | 861 | text, |
839 | iInvalidSize, | 862 | iInvalidSize, |
840 | zero_I2(), | 863 | zero_I2(), |
@@ -847,8 +870,8 @@ iInt2 advanceRange_Text(int fontId, iRangecc text) { | |||
847 | 870 | ||
848 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { | 871 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { |
849 | int advance; | 872 | int advance; |
850 | const int height = run_Font_(&text_.fonts[fontId], | 873 | const int height = run_Font_(font_Text_(fontId), |
851 | measure_RunMode, | 874 | measure_RunMode | runFlagsFromId_(fontId), |
852 | text, | 875 | text, |
853 | iInvalidSize, | 876 | iInvalidSize, |
854 | zero_I2(), | 877 | zero_I2(), |
@@ -861,8 +884,8 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) | |||
861 | 884 | ||
862 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { | 885 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { |
863 | int advance; | 886 | int advance; |
864 | const int height = run_Font_(&text_.fonts[fontId], | 887 | const int height = run_Font_(font_Text_(fontId), |
865 | measureNoWrap_RunMode, | 888 | measure_RunMode | noWrapFlag_RunMode | runFlagsFromId_(fontId), |
866 | text, | 889 | text, |
867 | iInvalidSize, | 890 | iInvalidSize, |
868 | zero_I2(), | 891 | zero_I2(), |
@@ -882,8 +905,14 @@ iInt2 advanceN_Text(int fontId, const char *text, size_t n) { | |||
882 | return init_I2(0, lineHeight_Text(fontId)); | 905 | return init_I2(0, lineHeight_Text(fontId)); |
883 | } | 906 | } |
884 | int advance; | 907 | int advance; |
885 | run_Font_( | 908 | run_Font_(font_Text_(fontId), |
886 | &text_.fonts[fontId], measure_RunMode, range_CStr(text), n, zero_I2(), 0, NULL, &advance); | 909 | measure_RunMode | runFlagsFromId_(fontId), |
910 | range_CStr(text), | ||
911 | n, | ||
912 | zero_I2(), | ||
913 | 0, | ||
914 | NULL, | ||
915 | &advance); | ||
887 | return init_I2(advance, lineHeight_Text(fontId)); | 916 | return init_I2(advance, lineHeight_Text(fontId)); |
888 | } | 917 | } |
889 | 918 | ||
@@ -891,8 +920,9 @@ static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) { | |||
891 | iText *d = &text_; | 920 | iText *d = &text_; |
892 | const iColor clr = get_Color(color & mask_ColorId); | 921 | const iColor clr = get_Color(color & mask_ColorId); |
893 | SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); | 922 | SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); |
894 | run_Font_(&d->fonts[fontId], | 923 | run_Font_(font_Text_(fontId), |
895 | color & permanent_ColorId ? drawPermanentColor_RunMode : draw_RunMode, | 924 | draw_RunMode | (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) | |
925 | runFlagsFromId_(fontId), | ||
896 | text, | 926 | text, |
897 | iInvalidSize, | 927 | iInvalidSize, |
898 | pos, | 928 | pos, |