diff options
-rw-r--r-- | src/ui/documentwidget.c | 3 | ||||
-rw-r--r-- | src/ui/text.c | 202 | ||||
-rw-r--r-- | src/ui/text.h | 1 |
3 files changed, 110 insertions, 96 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 43850043..3a03dce6 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -2692,8 +2692,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
2692 | height_Rect(run->visBounds), | 2692 | height_Rect(run->visBounds), |
2693 | tmQuoteIcon_ColorId); | 2693 | tmQuoteIcon_ColorId); |
2694 | } | 2694 | } |
2695 | drawRange_Text(run->font, visPos, fg, run->text); | 2695 | drawBoundRange_Text(run->font, visPos, width_Rect(run->bounds), fg, run->text); |
2696 | // printf("{%s}\n", cstr_Rangecc(run->text)); | ||
2697 | runDrawn:; | 2696 | runDrawn:; |
2698 | } | 2697 | } |
2699 | /* Presentation of links. */ | 2698 | /* Presentation of links. */ |
diff --git a/src/ui/text.c b/src/ui/text.c index 96799c9e..6b42610f 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -631,33 +631,48 @@ iLocalDef iBool isMeasuring_(enum iRunMode mode) { | |||
631 | return (mode & modeMask_RunMode) == measure_RunMode; | 631 | return (mode & modeMask_RunMode) == measure_RunMode; |
632 | } | 632 | } |
633 | 633 | ||
634 | static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, | 634 | iDeclareType(RunArgs) |
635 | int xposLimit, const char **continueFrom_out, int *runAdvance_out) { | 635 | |
636 | iRect bounds = zero_Rect(); | 636 | struct Impl_RunArgs { |
637 | const iInt2 orig = pos; | 637 | enum iRunMode mode; |
638 | float xpos = pos.x; | 638 | iRangecc text; |
639 | float xposExtend = pos.x; /* allows wide glyphs to use more space; restored by whitespace */ | 639 | size_t maxLen; /* max characters to process */ |
640 | float xposMax = xpos; | 640 | iInt2 pos; |
641 | float monoAdvance = 0; | 641 | int xposLimit; /* hard limit for wrapping */ |
642 | iAssert(xposLimit == 0 || isMeasuring_(mode)); | 642 | int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ |
643 | const char *lastWordEnd = text.start; | 643 | const char ** continueFrom_out; |
644 | if (continueFrom_out) { | 644 | int * runAdvance_out; |
645 | *continueFrom_out = text.end; | 645 | }; |
646 | |||
647 | static iRect run_Font_(iFont *d, const iRunArgs *args) { | ||
648 | iRect bounds = zero_Rect(); | ||
649 | const iInt2 orig = args->pos; | ||
650 | float xpos = orig.x; | ||
651 | float xposMax = xpos; | ||
652 | float monoAdvance = 0; | ||
653 | int ypos = orig.y; | ||
654 | size_t maxLen = args->maxLen ? args->maxLen : iInvalidSize; | ||
655 | float xposExtend = orig.x; /* allows wide glyphs to use more space; restored by whitespace */ | ||
656 | const enum iRunMode mode = args->mode; | ||
657 | const char * lastWordEnd = args->text.start; | ||
658 | iAssert(args->xposLimit == 0 || isMeasuring_(mode)); | ||
659 | if (args->continueFrom_out) { | ||
660 | *args->continueFrom_out = args->text.end; | ||
646 | } | 661 | } |
647 | iChar prevCh = 0; | 662 | iChar prevCh = 0; |
648 | const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode); | 663 | const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode); |
649 | if (isMonospaced) { | 664 | if (isMonospaced) { |
650 | monoAdvance = glyph_Font_(d, 'M')->advance; | 665 | monoAdvance = glyph_Font_(d, 'M')->advance; |
651 | } | 666 | } |
652 | for (const char *chPos = text.start; chPos != text.end; ) { | 667 | for (const char *chPos = args->text.start; chPos != args->text.end; ) { |
653 | iAssert(chPos < text.end); | 668 | iAssert(chPos < args->text.end); |
654 | const char *currentPos = chPos; | 669 | const char *currentPos = chPos; |
655 | if (*chPos == 0x1b) { | 670 | if (*chPos == 0x1b) { |
656 | /* ANSI escape. */ | 671 | /* ANSI escape. */ |
657 | chPos++; | 672 | chPos++; |
658 | iRegExpMatch m; | 673 | iRegExpMatch m; |
659 | init_RegExpMatch(&m); | 674 | init_RegExpMatch(&m); |
660 | if (match_RegExp(text_.ansiEscape, chPos, text.end - chPos, &m)) { | 675 | if (match_RegExp(text_.ansiEscape, chPos, args->text.end - chPos, &m)) { |
661 | if (mode & draw_RunMode && ~mode & permanentColorFlag_RunMode) { | 676 | if (mode & draw_RunMode && ~mode & permanentColorFlag_RunMode) { |
662 | /* Change the color. */ | 677 | /* Change the color. */ |
663 | const iColor clr = | 678 | const iColor clr = |
@@ -668,14 +683,14 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
668 | continue; | 683 | continue; |
669 | } | 684 | } |
670 | } | 685 | } |
671 | iChar ch = nextChar_(&chPos, text.end); | 686 | iChar ch = nextChar_(&chPos, args->text.end); |
672 | iBool isEmoji = isEmoji_Char(ch); | 687 | iBool isEmoji = isEmoji_Char(ch); |
673 | if (ch == 0x200d) { /* zero-width joiner */ | 688 | if (ch == 0x200d) { /* zero-width joiner */ |
674 | /* We don't have the composited Emojis. */ | 689 | /* We don't have the composited Emojis. */ |
675 | if (isEmoji_Char(prevCh)) { | 690 | if (isEmoji_Char(prevCh)) { |
676 | /* skip */ | 691 | /* skip */ |
677 | ch = nextChar_(&chPos, text.end); | 692 | ch = nextChar_(&chPos, args->text.end); |
678 | ch = nextChar_(&chPos, text.end); | 693 | ch = nextChar_(&chPos, args->text.end); |
679 | } | 694 | } |
680 | } | 695 | } |
681 | #if 0 | 696 | #if 0 |
@@ -690,17 +705,17 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
690 | } | 705 | } |
691 | #endif | 706 | #endif |
692 | if (isVariationSelector_Char(ch)) { | 707 | if (isVariationSelector_Char(ch)) { |
693 | ch = nextChar_(&chPos, text.end); /* skip it */ | 708 | ch = nextChar_(&chPos, args->text.end); /* skip it */ |
694 | } | 709 | } |
695 | /* Special instructions. */ { | 710 | /* Special instructions. */ { |
696 | if (ch == 0xad) { /* soft hyphen */ | 711 | if (ch == 0xad) { /* soft hyphen */ |
697 | lastWordEnd = chPos; | 712 | lastWordEnd = chPos; |
698 | if (isMeasuring_(mode)) { | 713 | if (isMeasuring_(mode)) { |
699 | if (xposLimit > 0) { | 714 | if (args->xposLimit > 0) { |
700 | const char *postHyphen = chPos; | 715 | const char *postHyphen = chPos; |
701 | iChar nextCh = nextChar_(&postHyphen, text.end); | 716 | iChar nextCh = nextChar_(&postHyphen, args->text.end); |
702 | if ((int) xpos + glyph_Font_(d, ch)->rect[0].size.x + | 717 | if ((int) xpos + glyph_Font_(d, ch)->rect[0].size.x + |
703 | glyph_Font_(d, nextCh)->rect[0].size.x > xposLimit) { | 718 | glyph_Font_(d, nextCh)->rect[0].size.x > args->xposLimit) { |
704 | /* Wraps after hyphen, should show it. */ | 719 | /* Wraps after hyphen, should show it. */ |
705 | } | 720 | } |
706 | else continue; | 721 | else continue; |
@@ -709,26 +724,37 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
709 | } | 724 | } |
710 | else { | 725 | else { |
711 | /* Only show it at the end. */ | 726 | /* Only show it at the end. */ |
712 | if (chPos != text.end) { | 727 | if (chPos != args->text.end) { |
713 | continue; | 728 | continue; |
714 | } | 729 | } |
715 | } | 730 | } |
716 | } | 731 | } |
717 | if (ch == '\n') { | 732 | if (ch == '\n') { |
718 | xpos = xposExtend = pos.x; | 733 | xpos = xposExtend = orig.x; |
719 | pos.y += d->height; | 734 | ypos += d->height; |
720 | prevCh = ch; | 735 | prevCh = ch; |
721 | continue; | 736 | continue; |
722 | } | 737 | } |
723 | if (ch == '\t') { | 738 | if (ch == '\t') { |
724 | const int tabStopWidth = d->height * 8; | 739 | const int tabStopWidth = d->height * 10; |
725 | xpos = pos.x + ((int) ((xpos - pos.x) / tabStopWidth) + 1) * tabStopWidth; | 740 | const int halfWidth = (iMax(args->xposLimit, args->xposLayoutBound) - orig.x) / 2; |
741 | const int xRel = xpos - orig.x; | ||
742 | /* First stop is always to half width. */ | ||
743 | if (halfWidth > 0 && xRel < halfWidth) { | ||
744 | xpos = orig.x + halfWidth; | ||
745 | } | ||
746 | else if (halfWidth > 0 && xRel < halfWidth * 3 / 2) { | ||
747 | xpos = orig.x + halfWidth * 3 / 2; | ||
748 | } | ||
749 | else { | ||
750 | xpos = orig.x + ((xRel / tabStopWidth) + 1) * tabStopWidth; | ||
751 | } | ||
726 | xposExtend = iMax(xposExtend, xpos); | 752 | xposExtend = iMax(xposExtend, xpos); |
727 | prevCh = 0; | 753 | prevCh = 0; |
728 | continue; | 754 | continue; |
729 | } | 755 | } |
730 | if (ch == '\r') { | 756 | if (ch == '\r') { |
731 | const iChar esc = nextChar_(&chPos, text.end); | 757 | const iChar esc = nextChar_(&chPos, args->text.end); |
732 | if (mode & draw_RunMode && ~mode & permanentColorFlag_RunMode) { | 758 | if (mode & draw_RunMode && ~mode & permanentColorFlag_RunMode) { |
733 | const iColor clr = get_Color(esc - asciiBase_ColorEscape); | 759 | const iColor clr = get_Color(esc - asciiBase_ColorEscape); |
734 | SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b); | 760 | SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b); |
@@ -745,18 +771,18 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
745 | const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; | 771 | const int hoff = enableHalfPixelGlyphs_Text ? (xpos - x1 > 0.5f ? 1 : 0) : 0; |
746 | int x2 = x1 + glyph->rect[hoff].size.x; | 772 | int x2 = x1 + glyph->rect[hoff].size.x; |
747 | /* Out of the allotted space? */ | 773 | /* Out of the allotted space? */ |
748 | if (xposLimit > 0 && x2 > xposLimit) { | 774 | if (args->xposLimit > 0 && x2 > args->xposLimit) { |
749 | if (lastWordEnd != text.start) { | 775 | if (lastWordEnd != args->text.start) { |
750 | *continueFrom_out = lastWordEnd; | 776 | *args->continueFrom_out = lastWordEnd; |
751 | } | 777 | } |
752 | else { | 778 | else { |
753 | *continueFrom_out = currentPos; /* forced break */ | 779 | *args->continueFrom_out = currentPos; /* forced break */ |
754 | } | 780 | } |
755 | break; | 781 | break; |
756 | } | 782 | } |
757 | const int yLineMax = pos.y + d->height; | 783 | const int yLineMax = ypos + d->height; |
758 | SDL_Rect dst = { x1 + glyph->d[hoff].x, | 784 | SDL_Rect dst = { x1 + glyph->d[hoff].x, |
759 | pos.y + glyph->font->baseline + glyph->d[hoff].y, | 785 | ypos + glyph->font->baseline + glyph->d[hoff].y, |
760 | glyph->rect[hoff].size.x, | 786 | glyph->rect[hoff].size.x, |
761 | glyph->rect[hoff].size.y }; | 787 | glyph->rect[hoff].size.y }; |
762 | if (glyph->font != d) { | 788 | if (glyph->font != d) { |
@@ -776,7 +802,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
776 | } | 802 | } |
777 | else { | 803 | else { |
778 | bounds.size.x = iMax(bounds.size.x, x2 - orig.x); | 804 | bounds.size.x = iMax(bounds.size.x, x2 - orig.x); |
779 | bounds.size.y = iMax(bounds.size.y, pos.y + glyph->font->height - orig.y); | 805 | bounds.size.y = iMax(bounds.size.y, ypos + glyph->font->height - orig.y); |
780 | } | 806 | } |
781 | /* Symbols and emojis are NOT monospaced, so must conform when the primary font | 807 | /* 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. */ | 808 | is monospaced. Except with Japanese script, that's larger than the normal monospace. */ |
@@ -797,8 +823,8 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
797 | src.h -= over; | 823 | src.h -= over; |
798 | dst.h -= over; | 824 | dst.h -= over; |
799 | } | 825 | } |
800 | if (dst.y < pos.y) { | 826 | if (dst.y < ypos) { |
801 | const int over = pos.y - dst.y; | 827 | const int over = ypos - dst.y; |
802 | dst.y += over; | 828 | dst.y += over; |
803 | dst.h -= over; | 829 | dst.h -= over; |
804 | src.y += over; | 830 | src.y += over; |
@@ -812,7 +838,7 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
812 | } | 838 | } |
813 | xposExtend = iMax(xposExtend, xpos); | 839 | xposExtend = iMax(xposExtend, xpos); |
814 | xposMax = iMax(xposMax, xposExtend); | 840 | xposMax = iMax(xposMax, xposExtend); |
815 | if (continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) { | 841 | if (args->continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) { |
816 | lastWordEnd = chPos; | 842 | lastWordEnd = chPos; |
817 | } | 843 | } |
818 | #if defined (LAGRANGE_ENABLE_KERNING) | 844 | #if defined (LAGRANGE_ENABLE_KERNING) |
@@ -820,19 +846,19 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
820 | if (!isMonospaced && glyph->font == d) { | 846 | if (!isMonospaced && glyph->font == d) { |
821 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ | 847 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ |
822 | const char *peek = chPos; | 848 | const char *peek = chPos; |
823 | const iChar next = nextChar_(&peek, text.end); | 849 | const iChar next = nextChar_(&peek, args->text.end); |
824 | if (enableKerning_Text && !d->manualKernOnly && next) { | 850 | if (enableKerning_Text && !d->manualKernOnly && next) { |
825 | xpos += d->xScale * stbtt_GetGlyphKernAdvance(&d->font, glyph->glyphIndex, next); | 851 | xpos += d->xScale * stbtt_GetGlyphKernAdvance(&d->font, glyph->glyphIndex, next); |
826 | } | 852 | } |
827 | } | 853 | } |
828 | #endif | 854 | #endif |
829 | prevCh = ch; | 855 | prevCh = ch; |
830 | if (--maxLen == 0) { | 856 | if (maxLen == 0) { |
831 | break; | 857 | break; |
832 | } | 858 | } |
833 | } | 859 | } |
834 | if (runAdvance_out) { | 860 | if (args->runAdvance_out) { |
835 | *runAdvance_out = xposMax - orig.x; | 861 | *args->runAdvance_out = xposMax - orig.x; |
836 | } | 862 | } |
837 | return bounds; | 863 | return bounds; |
838 | } | 864 | } |
@@ -845,25 +871,15 @@ iInt2 measureRange_Text(int fontId, iRangecc text) { | |||
845 | if (isEmpty_Range(&text)) { | 871 | if (isEmpty_Range(&text)) { |
846 | return init_I2(0, lineHeight_Text(fontId)); | 872 | return init_I2(0, lineHeight_Text(fontId)); |
847 | } | 873 | } |
848 | return run_Font_(font_Text_(fontId), | 874 | return run_Font_(font_Text_(fontId), &(iRunArgs){ .mode = measure_RunMode, .text = text }).size; |
849 | measure_RunMode, | ||
850 | text, | ||
851 | iInvalidSize, | ||
852 | zero_I2(), | ||
853 | 0, | ||
854 | NULL, | ||
855 | NULL).size; | ||
856 | } | 875 | } |
857 | 876 | ||
858 | iRect visualBounds_Text(int fontId, iRangecc text) { | 877 | iRect visualBounds_Text(int fontId, iRangecc text) { |
859 | return run_Font_(font_Text_(fontId), | 878 | return run_Font_(font_Text_(fontId), |
860 | measure_RunMode | visualFlag_RunMode, | 879 | &(iRunArgs){ |
861 | text, | 880 | .mode = measure_RunMode | visualFlag_RunMode, |
862 | iInvalidSize, | 881 | .text = text, |
863 | zero_I2(), | 882 | }); |
864 | 0, | ||
865 | NULL, | ||
866 | NULL); | ||
867 | } | 883 | } |
868 | 884 | ||
869 | iInt2 measure_Text(int fontId, const char *text) { | 885 | iInt2 measure_Text(int fontId, const char *text) { |
@@ -881,13 +897,9 @@ static int runFlagsFromId_(enum iFontId fontId) { | |||
881 | iInt2 advanceRange_Text(int fontId, iRangecc text) { | 897 | iInt2 advanceRange_Text(int fontId, iRangecc text) { |
882 | int advance; | 898 | int advance; |
883 | const int height = run_Font_(font_Text_(fontId), | 899 | const int height = run_Font_(font_Text_(fontId), |
884 | measure_RunMode | runFlagsFromId_(fontId), | 900 | &(iRunArgs){ .mode = measure_RunMode | runFlagsFromId_(fontId), |
885 | text, | 901 | .text = text, |
886 | iInvalidSize, | 902 | .runAdvance_out = &advance }) |
887 | zero_I2(), | ||
888 | 0, | ||
889 | NULL, | ||
890 | &advance) | ||
891 | .size.y; | 903 | .size.y; |
892 | return init_I2(advance, height); | 904 | return init_I2(advance, height); |
893 | } | 905 | } |
@@ -895,13 +907,11 @@ iInt2 advanceRange_Text(int fontId, iRangecc text) { | |||
895 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { | 907 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { |
896 | int advance; | 908 | int advance; |
897 | const int height = run_Font_(font_Text_(fontId), | 909 | const int height = run_Font_(font_Text_(fontId), |
898 | measure_RunMode | runFlagsFromId_(fontId), | 910 | &(iRunArgs){ .mode = measure_RunMode | runFlagsFromId_(fontId), |
899 | text, | 911 | .text = text, |
900 | iInvalidSize, | 912 | .xposLimit = width, |
901 | zero_I2(), | 913 | .continueFrom_out = endPos, |
902 | width, | 914 | .runAdvance_out = &advance }) |
903 | endPos, | ||
904 | &advance) | ||
905 | .size.y; | 915 | .size.y; |
906 | return init_I2(advance, height); | 916 | return init_I2(advance, height); |
907 | } | 917 | } |
@@ -909,13 +919,12 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) | |||
909 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { | 919 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { |
910 | int advance; | 920 | int advance; |
911 | const int height = run_Font_(font_Text_(fontId), | 921 | const int height = run_Font_(font_Text_(fontId), |
912 | measure_RunMode | noWrapFlag_RunMode | runFlagsFromId_(fontId), | 922 | &(iRunArgs){ .mode = measure_RunMode | noWrapFlag_RunMode | |
913 | text, | 923 | runFlagsFromId_(fontId), |
914 | iInvalidSize, | 924 | .text = text, |
915 | zero_I2(), | 925 | .xposLimit = width, |
916 | width, | 926 | .continueFrom_out = endPos, |
917 | endPos, | 927 | .runAdvance_out = &advance }) |
918 | &advance) | ||
919 | .size.y; | 928 | .size.y; |
920 | return init_I2(advance, height); | 929 | return init_I2(advance, height); |
921 | } | 930 | } |
@@ -930,29 +939,28 @@ iInt2 advanceN_Text(int fontId, const char *text, size_t n) { | |||
930 | } | 939 | } |
931 | int advance; | 940 | int advance; |
932 | run_Font_(font_Text_(fontId), | 941 | run_Font_(font_Text_(fontId), |
933 | measure_RunMode | runFlagsFromId_(fontId), | 942 | &(iRunArgs){ .mode = measure_RunMode | runFlagsFromId_(fontId), |
934 | range_CStr(text), | 943 | .text = range_CStr(text), |
935 | n, | 944 | .maxLen = n, |
936 | zero_I2(), | 945 | .runAdvance_out = &advance }); |
937 | 0, | ||
938 | NULL, | ||
939 | &advance); | ||
940 | return init_I2(advance, lineHeight_Text(fontId)); | 946 | return init_I2(advance, lineHeight_Text(fontId)); |
941 | } | 947 | } |
942 | 948 | ||
943 | static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) { | 949 | static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { |
944 | iText *d = &text_; | 950 | iText *d = &text_; |
945 | const iColor clr = get_Color(color & mask_ColorId); | 951 | const iColor clr = get_Color(color & mask_ColorId); |
946 | SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); | 952 | SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); |
947 | run_Font_(font_Text_(fontId), | 953 | run_Font_(font_Text_(fontId), |
948 | draw_RunMode | (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) | | 954 | &(iRunArgs){ .mode = draw_RunMode | |
949 | runFlagsFromId_(fontId), | 955 | (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) | |
950 | text, | 956 | runFlagsFromId_(fontId), |
951 | iInvalidSize, | 957 | .text = text, |
952 | pos, | 958 | .pos = pos, |
953 | 0, | 959 | .xposLayoutBound = xposBound }); |
954 | NULL, | 960 | } |
955 | NULL); | 961 | |
962 | static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) { | ||
963 | drawBounded_Text_(fontId, pos, 0, color, text); | ||
956 | } | 964 | } |
957 | 965 | ||
958 | void drawAlign_Text(int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...) { | 966 | void drawAlign_Text(int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...) { |
@@ -1005,6 +1013,12 @@ iInt2 advanceWrapRange_Text(int fontId, int maxWidth, iRangecc text) { | |||
1005 | return size; | 1013 | return size; |
1006 | } | 1014 | } |
1007 | 1015 | ||
1016 | void drawBoundRange_Text(int fontId, iInt2 pos, int boundWidth, int color, iRangecc text) { | ||
1017 | /* This function is used together with text that has already been wrapped, so we'll know | ||
1018 | the bound width but don't have to re-wrap the text. */ | ||
1019 | drawBounded_Text_(fontId, pos, pos.x + boundWidth, color, text); | ||
1020 | } | ||
1021 | |||
1008 | int drawWrapRange_Text(int fontId, iInt2 pos, int maxWidth, int color, iRangecc text) { | 1022 | int drawWrapRange_Text(int fontId, iInt2 pos, int maxWidth, int color, iRangecc text) { |
1009 | const char *endp; | 1023 | const char *endp; |
1010 | while (!isEmpty_Range(&text)) { | 1024 | while (!isEmpty_Range(&text)) { |
diff --git a/src/ui/text.h b/src/ui/text.h index 5867a84b..f78f570a 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -175,6 +175,7 @@ void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment a | |||
175 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); | 175 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); |
176 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); | 176 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); |
177 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); | 177 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); |
178 | void drawBoundRange_Text (int fontId, iInt2 pos, int boundWidth, int color, iRangecc text); /* bound does not wrap */ | ||
178 | int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ | 179 | int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ |
179 | 180 | ||
180 | SDL_Texture * glyphCache_Text (void); | 181 | SDL_Texture * glyphCache_Text (void); |