diff options
-rw-r--r-- | src/ui/text.c | 205 | ||||
-rw-r--r-- | src/ui/text.h | 22 |
2 files changed, 186 insertions, 41 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 411d82d3..54a6049f 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -257,19 +257,19 @@ static iText text_; | |||
257 | static iBlock *userFont_; | 257 | static iBlock *userFont_; |
258 | 258 | ||
259 | static void initFonts_Text_(iText *d) { | 259 | static void initFonts_Text_(iText *d) { |
260 | const float textSize = fontSize_UI * d->contentFontSize; | 260 | const float textSize = fontSize_UI * d->contentFontSize; |
261 | const float monoSize = textSize * 0.71f; | 261 | const float monoSize = textSize * 0.71f; |
262 | const float smallMonoSize = monoSize * 0.8f; | 262 | const float smallMonoSize = monoSize * 0.8f; |
263 | const iBlock *regularFont = &fontNunitoRegular_Embedded; | 263 | const iBlock *regularFont = &fontNunitoRegular_Embedded; |
264 | const iBlock *boldFont = &fontNunitoBold_Embedded; | 264 | const iBlock *boldFont = &fontNunitoBold_Embedded; |
265 | const iBlock *italicFont = &fontNunitoLightItalic_Embedded; | 265 | const iBlock *italicFont = &fontNunitoLightItalic_Embedded; |
266 | const iBlock *h12Font = &fontNunitoExtraBold_Embedded; | 266 | const iBlock *h12Font = &fontNunitoExtraBold_Embedded; |
267 | const iBlock *h3Font = &fontNunitoRegular_Embedded; | 267 | const iBlock *h3Font = &fontNunitoRegular_Embedded; |
268 | const iBlock *lightFont = &fontNunitoExtraLight_Embedded; | 268 | const iBlock *lightFont = &fontNunitoExtraLight_Embedded; |
269 | float scaling = 1.0f; /* glyph scaling (<=1.0), for increasing line spacing */ | 269 | float scaling = 1.0f; /* glyph scaling (<=1.0), for increasing line spacing */ |
270 | float italicScaling= 1.0f; | 270 | float italicScaling = 1.0f; |
271 | float lightScaling = 1.0f; | 271 | float lightScaling = 1.0f; |
272 | float h123Scaling = 1.0f; /* glyph scaling (<=1.0), for increasing line spacing */ | 272 | float h123Scaling = 1.0f; /* glyph scaling (<=1.0), for increasing line spacing */ |
273 | if (d->contentFont == firaSans_TextFont) { | 273 | if (d->contentFont == firaSans_TextFont) { |
274 | regularFont = &fontFiraSansRegular_Embedded; | 274 | regularFont = &fontFiraSansRegular_Embedded; |
275 | boldFont = &fontFiraSansSemiBold_Embedded; | 275 | boldFont = &fontFiraSansSemiBold_Embedded; |
@@ -754,17 +754,18 @@ struct Impl_AttributedRun { | |||
754 | }; | 754 | }; |
755 | 755 | ||
756 | iDeclareType(AttributedText) | 756 | iDeclareType(AttributedText) |
757 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, iFont *font, iColor fgColor) | 757 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) |
758 | 758 | ||
759 | struct Impl_AttributedText { | 759 | struct Impl_AttributedText { |
760 | iRangecc text; | 760 | iRangecc text; |
761 | size_t maxLen; | ||
761 | iFont * font; | 762 | iFont * font; |
762 | iColor fgColor; | 763 | iColor fgColor; |
763 | iArray runs; | 764 | iArray runs; |
764 | }; | 765 | }; |
765 | 766 | ||
766 | iDefineTypeConstructionArgs(AttributedText, (iRangecc text, iFont *font, iColor fgColor), | 767 | iDefineTypeConstructionArgs(AttributedText, (iRangecc text, size_t maxLen, iFont *font, iColor fgColor), |
767 | text, font, fgColor) | 768 | text, maxLen, font, fgColor) |
768 | 769 | ||
769 | static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, | 770 | static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, |
770 | const char *endAt) { | 771 | const char *endAt) { |
@@ -774,13 +775,15 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, | |||
774 | pushBack_Array(&d->runs, &finishedRun); | 775 | pushBack_Array(&d->runs, &finishedRun); |
775 | run->lineBreaks = 0; | 776 | run->lineBreaks = 0; |
776 | } | 777 | } |
777 | run->text.start = endAt; | 778 | run->text.start = run->text.end = endAt; |
778 | } | 779 | } |
779 | 780 | ||
780 | static void prepare_AttributedText_(iAttributedText *d) { | 781 | static void prepare_AttributedText_(iAttributedText *d) { |
781 | iAssert(isEmpty_Array(&d->runs)); | 782 | iAssert(isEmpty_Array(&d->runs)); |
783 | size_t avail = d->maxLen; | ||
782 | const char *chPos = d->text.start; | 784 | const char *chPos = d->text.start; |
783 | iAttributedRun run = { .text = d->text, .font = d->font, .fgColor = d->fgColor }; | 785 | iAttributedRun run = { .text = (iRangecc){ d->text.start, d->text.start }, |
786 | .font = d->font, .fgColor = d->fgColor }; | ||
784 | while (chPos < d->text.end) { | 787 | while (chPos < d->text.end) { |
785 | const char *currentPos = chPos; | 788 | const char *currentPos = chPos; |
786 | if (*chPos == 0x1b) { /* ANSI escape. */ | 789 | if (*chPos == 0x1b) { /* ANSI escape. */ |
@@ -824,10 +827,13 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
824 | isFitzpatrickType_Char(ch)) { | 827 | isFitzpatrickType_Char(ch)) { |
825 | continue; | 828 | continue; |
826 | } | 829 | } |
830 | if (avail-- == 0) { | ||
831 | /* TODO: Check the combining class; only count base characters here. */ | ||
832 | break; | ||
833 | } | ||
827 | const iGlyph *glyph = glyph_Font_(d->font, ch); | 834 | const iGlyph *glyph = glyph_Font_(d->font, ch); |
828 | /* TODO: Look for ANSI/color escapes. */ | ||
829 | if (index_Glyph_(glyph) && glyph->font != run.font) { | 835 | if (index_Glyph_(glyph) && glyph->font != run.font) { |
830 | /* A different font is being used for this glyph. */ | 836 | /* A different font is being used for this character. */ |
831 | finishRun_AttributedText_(d, &run, currentPos); | 837 | finishRun_AttributedText_(d, &run, currentPos); |
832 | run.font = glyph->font; | 838 | run.font = glyph->font; |
833 | } | 839 | } |
@@ -837,8 +843,9 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
837 | } | 843 | } |
838 | } | 844 | } |
839 | 845 | ||
840 | void init_AttributedText(iAttributedText *d, iRangecc text, iFont *font, iColor fgColor) { | 846 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) { |
841 | d->text = text; | 847 | d->text = text; |
848 | d->maxLen = maxLen ? maxLen : iInvalidSize; | ||
842 | d->font = font; | 849 | d->font = font; |
843 | d->fgColor = fgColor; | 850 | d->fgColor = fgColor; |
844 | init_Array(&d->runs, sizeof(iAttributedRun)); | 851 | init_Array(&d->runs, sizeof(iAttributedRun)); |
@@ -991,9 +998,14 @@ static void cacheTextGlyphs_Font_(iFont *d, const iRangecc text) { | |||
991 | const char *oldPos = chPos; | 998 | const char *oldPos = chPos; |
992 | const iChar ch = nextChar_(&chPos, text.end); | 999 | const iChar ch = nextChar_(&chPos, text.end); |
993 | if (chPos == oldPos) break; | 1000 | if (chPos == oldPos) break; |
994 | const uint32_t glyphIndex = glyphIndex_Font_(d, ch); | 1001 | if (!isSpace_Char(ch) && |
995 | if (glyphIndex) { | 1002 | !isDefaultIgnorable_Char(ch) && |
996 | pushBack_Array(&glyphIndices, &glyphIndex); | 1003 | !isVariationSelector_Char(ch) && |
1004 | !isFitzpatrickType_Char(ch)) { | ||
1005 | const uint32_t glyphIndex = glyphIndex_Font_(d, ch); | ||
1006 | if (glyphIndex) { | ||
1007 | pushBack_Array(&glyphIndices, &glyphIndex); | ||
1008 | } | ||
997 | } | 1009 | } |
998 | } | 1010 | } |
999 | cacheGlyphs_Font_(d, &glyphIndices); | 1011 | cacheGlyphs_Font_(d, &glyphIndices); |
@@ -1025,6 +1037,7 @@ struct Impl_RunArgs { | |||
1025 | iRangecc text; | 1037 | iRangecc text; |
1026 | size_t maxLen; /* max characters to process */ | 1038 | size_t maxLen; /* max characters to process */ |
1027 | iInt2 pos; | 1039 | iInt2 pos; |
1040 | iWrapText * wrap; | ||
1028 | int xposLimit; /* hard limit for wrapping */ | 1041 | int xposLimit; /* hard limit for wrapping */ |
1029 | int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ | 1042 | int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ |
1030 | int color; | 1043 | int color; |
@@ -1032,6 +1045,14 @@ struct Impl_RunArgs { | |||
1032 | int * runAdvance_out; | 1045 | int * runAdvance_out; |
1033 | }; | 1046 | }; |
1034 | 1047 | ||
1048 | static void notify_WrapText_(iWrapText *d, const char *ending, int advance) { | ||
1049 | if (d && d->wrapFunc) { | ||
1050 | const char *end = ending ? ending : d->wrapRange_.end; | ||
1051 | d->wrapFunc(d, (iRangecc){ d->wrapRange_.start, end }, advance); | ||
1052 | d->wrapRange_.start = end; | ||
1053 | } | ||
1054 | } | ||
1055 | |||
1035 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) | 1056 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) |
1036 | static iRect run_Font_(iFont *d, const iRunArgs *args) { | 1057 | static iRect run_Font_(iFont *d, const iRunArgs *args) { |
1037 | const int mode = args->mode; | 1058 | const int mode = args->mode; |
@@ -1040,22 +1061,54 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1040 | float xCursor = 0.0f; | 1061 | float xCursor = 0.0f; |
1041 | float yCursor = 0.0f; | 1062 | float yCursor = 0.0f; |
1042 | float xCursorMax = 0.0f; | 1063 | float xCursorMax = 0.0f; |
1064 | iRangecc wrapRange = args->text; | ||
1043 | iAssert(args->text.end >= args->text.start); | 1065 | iAssert(args->text.end >= args->text.start); |
1044 | if (args->continueFrom_out) { | 1066 | if (args->continueFrom_out) { |
1045 | *args->continueFrom_out = args->text.end; | 1067 | *args->continueFrom_out = args->text.end; |
1046 | } | 1068 | } |
1047 | hb_buffer_t *hbBuf = hb_buffer_create(); | 1069 | hb_buffer_t *hbBuf = hb_buffer_create(); |
1048 | /* Split the text into a number of attributed runs that specify exactly which font is | 1070 | /* Split the text into a number of attributed runs that specify exactly which |
1049 | used and other attributes such as color. (HarfBuzz shaping is done with one specific font.) */ | 1071 | font is used and other attributes such as color. (HarfBuzz shaping is done |
1050 | iAttributedText *attrText = new_AttributedText(args->text, d, get_Color(args->color)); | 1072 | with one specific font.) */ |
1051 | iConstForEach(Array, i, &attrText->runs) { | 1073 | iAttributedText *attrText = |
1052 | const iAttributedRun *run = i.value; | 1074 | new_AttributedText(args->text, args->maxLen, d, get_Color(args->color)); |
1053 | if (run->lineBreaks) { | 1075 | if (args->wrap) { |
1076 | /* TODO: Duplicated args? */ | ||
1077 | iAssert(equalRange_Rangecc(args->wrap->text, args->text)); | ||
1078 | /* Initialize the wrap range. */ | ||
1079 | args->wrap->wrapRange_ = args->text; | ||
1080 | } | ||
1081 | const char *wrapResumePos = NULL; | ||
1082 | for (size_t runIndex = 0; runIndex < size_Array(&attrText->runs); runIndex++) { | ||
1083 | const iAttributedRun *run = at_Array(&attrText->runs, runIndex); | ||
1084 | iRangecc runText = run->text; | ||
1085 | if (wrapResumePos) { | ||
1054 | xCursor = 0.0f; | 1086 | xCursor = 0.0f; |
1055 | yCursor += d->height * run->lineBreaks; | 1087 | yCursor += d->height; |
1088 | runText.start = wrapResumePos; | ||
1089 | wrapResumePos = NULL; | ||
1090 | } | ||
1091 | else if (run->lineBreaks) { | ||
1092 | for (int i = 0; i < run->lineBreaks; i++) { | ||
1093 | /* One callback for each "wrapped" line even if they're empty. */ | ||
1094 | notify_WrapText_(args->wrap, run->text.start, iRound(xCursor)); | ||
1095 | xCursor = 0.0f; | ||
1096 | yCursor += d->height; | ||
1097 | } | ||
1056 | } | 1098 | } |
1057 | hb_buffer_clear_contents(hbBuf); | 1099 | hb_buffer_clear_contents(hbBuf); |
1058 | hb_buffer_add_utf8(hbBuf, run->text.start, size_Range(&run->text), 0, -1); | 1100 | /* Cluster values are used to determine offset inside the UTF-8 source string. */ |
1101 | //hb_buffer_set_cluster_level(hbBuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); | ||
1102 | for (const char *pos = runText.start; pos < runText.end; ) { | ||
1103 | iChar ucp = 0; | ||
1104 | const int len = decodeBytes_MultibyteChar(pos, runText.end, &ucp); | ||
1105 | if (len > 0) { | ||
1106 | hb_buffer_add(hbBuf, ucp, pos - runText.start); | ||
1107 | pos += len; | ||
1108 | } | ||
1109 | else break; | ||
1110 | } | ||
1111 | hb_buffer_set_content_type(hbBuf, HB_BUFFER_CONTENT_TYPE_UNICODE); | ||
1059 | hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR); /* TODO: FriBidi? */ | 1112 | hb_buffer_set_direction(hbBuf, HB_DIRECTION_LTR); /* TODO: FriBidi? */ |
1060 | /* hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN); */ /* will be autodetected */ | 1113 | /* hb_buffer_set_script(hbBuf, HB_SCRIPT_LATIN); */ /* will be autodetected */ |
1061 | hb_buffer_set_language(hbBuf, hb_language_from_string("en", -1)); /* TODO: language from document/UI, if known */ | 1114 | hb_buffer_set_language(hbBuf, hb_language_from_string("en", -1)); /* TODO: language from document/UI, if known */ |
@@ -1063,9 +1116,45 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1063 | unsigned int glyphCount = 0; | 1116 | unsigned int glyphCount = 0; |
1064 | const hb_glyph_info_t * glyphInfo = hb_buffer_get_glyph_infos(hbBuf, &glyphCount); | 1117 | const hb_glyph_info_t * glyphInfo = hb_buffer_get_glyph_infos(hbBuf, &glyphCount); |
1065 | const hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(hbBuf, &glyphCount); | 1118 | const hb_glyph_position_t *glyphPos = hb_buffer_get_glyph_positions(hbBuf, &glyphCount); |
1119 | const char *breakPos = NULL; | ||
1120 | /* Check if this run needs to be wrapped. If so, we'll draw the portion that fits on | ||
1121 | the line, and re-run the loop resuming the run from the wrap point. */ | ||
1122 | if (args->wrap && args->wrap->maxWidth > 0) { | ||
1123 | float x = xCursor; | ||
1124 | const char *safeBreak = NULL; | ||
1125 | for (unsigned int i = 0; i < glyphCount; i++) { | ||
1126 | const hb_glyph_info_t *info = &glyphInfo[i]; | ||
1127 | const hb_codepoint_t glyphId = info->codepoint; | ||
1128 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); | ||
1129 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); | ||
1130 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; | ||
1131 | const float xAdvance = run->font->xScale * glyphPos[i].x_advance; | ||
1132 | const char *textPos = runText.start + info->cluster; | ||
1133 | if (~glyphFlags & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) { | ||
1134 | safeBreak = textPos; | ||
1135 | } | ||
1136 | if (x + xOffset + glyph->d[0].x + glyph->rect[0].size.x > args->wrap->maxWidth) { | ||
1137 | if (safeBreak) { | ||
1138 | breakPos = safeBreak; | ||
1139 | } | ||
1140 | else { | ||
1141 | breakPos = textPos; | ||
1142 | } | ||
1143 | break; | ||
1144 | } | ||
1145 | x += xAdvance; | ||
1146 | } | ||
1147 | /* Make a callback for each wrapped line. */ | ||
1148 | if (breakPos) { | ||
1149 | notify_WrapText_(args->wrap, breakPos, iRound(x)); | ||
1150 | } | ||
1151 | } | ||
1066 | /* Draw each glyph. */ | 1152 | /* Draw each glyph. */ |
1067 | for (unsigned int i = 0; i < glyphCount; i++) { | 1153 | for (unsigned int i = 0; i < glyphCount; i++) { |
1068 | const hb_codepoint_t glyphId = glyphInfo[i].codepoint; | 1154 | const hb_glyph_info_t *info = &glyphInfo[i]; |
1155 | const hb_codepoint_t glyphId = info->codepoint; | ||
1156 | const char *textPos = runText.start + info->cluster; | ||
1157 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); | ||
1069 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; | 1158 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; |
1070 | const float yOffset = run->font->yScale * glyphPos[i].y_offset; | 1159 | const float yOffset = run->font->yScale * glyphPos[i].y_offset; |
1071 | const float xAdvance = run->font->xScale * glyphPos[i].x_advance; | 1160 | const float xAdvance = run->font->xScale * glyphPos[i].x_advance; |
@@ -1073,7 +1162,11 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1073 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); | 1162 | const iGlyph *glyph = glyphByIndex_Font_(run->font, glyphId); |
1074 | const float xf = xCursor + xOffset; | 1163 | const float xf = xCursor + xOffset; |
1075 | const int hoff = enableHalfPixelGlyphs_Text ? (xf - ((int) xf) > 0.5f ? 1 : 0) : 0; | 1164 | const int hoff = enableHalfPixelGlyphs_Text ? (xf - ((int) xf) > 0.5f ? 1 : 0) : 0; |
1076 | /* draw_glyph(glyphid, cursor_x + x_offset, cursor_y + y_offset); */ | 1165 | if (textPos == breakPos) { |
1166 | runIndex--; /* stay on the same run */ | ||
1167 | wrapResumePos = textPos; | ||
1168 | break; | ||
1169 | } | ||
1077 | /* Draw the glyph. */ { | 1170 | /* Draw the glyph. */ { |
1078 | SDL_Rect dst = { orig.x + xCursor + xOffset + glyph->d[hoff].x, | 1171 | SDL_Rect dst = { orig.x + xCursor + xOffset + glyph->d[hoff].x, |
1079 | orig.y + yCursor + yOffset + glyph->font->baseline + glyph->d[hoff].y, | 1172 | orig.y + yCursor + yOffset + glyph->font->baseline + glyph->d[hoff].y, |
@@ -1098,17 +1191,18 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1098 | iAssert(isRasterized_Glyph_(glyph, hoff)); | 1191 | iAssert(isRasterized_Glyph_(glyph, hoff)); |
1099 | } | 1192 | } |
1100 | if (~mode & permanentColorFlag_RunMode) { | 1193 | if (~mode & permanentColorFlag_RunMode) { |
1101 | //const iColor clr = get_Color(colorNum); | 1194 | const iColor clr = run->fgColor; |
1102 | SDL_SetTextureColorMod(text_.cache, run->fgColor.r, run->fgColor.g, run->fgColor.b); | 1195 | SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b); |
1103 | // if (args->mode & fillBackground_RunMode) { | 1196 | if (args->mode & fillBackground_RunMode) { |
1104 | // SDL_SetRenderDrawColor(text_.render, clr.r, clr.g, clr.b, 0); | 1197 | SDL_SetRenderDrawColor(text_.render, clr.r, clr.g, clr.b, 0); |
1105 | // } | 1198 | } |
1106 | } | 1199 | } |
1107 | SDL_Rect src; | 1200 | SDL_Rect src; |
1108 | memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); | 1201 | memcpy(&src, &glyph->rect[hoff], sizeof(SDL_Rect)); |
1109 | if (args->mode & fillBackground_RunMode) { | 1202 | if (args->mode & fillBackground_RunMode) { |
1110 | /* Alpha blending looks much better if the RGB components don't change in | 1203 | /* Alpha blending looks much better if the RGB components don't change in |
1111 | the partially transparent pixels. */ | 1204 | the partially transparent pixels. */ |
1205 | /* TODO: Backgrounds of all glyphs should be cleared before drawing anything else. */ | ||
1112 | SDL_RenderFillRect(text_.render, &dst); | 1206 | SDL_RenderFillRect(text_.render, &dst); |
1113 | } | 1207 | } |
1114 | SDL_RenderCopy(text_.render, text_.cache, &src, &dst); | 1208 | SDL_RenderCopy(text_.render, text_.cache, &src, &dst); |
@@ -1119,6 +1213,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1119 | xCursorMax = iMax(xCursorMax, xCursor); | 1213 | xCursorMax = iMax(xCursorMax, xCursor); |
1120 | } | 1214 | } |
1121 | } | 1215 | } |
1216 | /* The final, unbroken line. */ | ||
1217 | notify_WrapText_(args->wrap, NULL, xCursor); | ||
1122 | if (args->runAdvance_out) { | 1218 | if (args->runAdvance_out) { |
1123 | *args->runAdvance_out = xCursorMax; | 1219 | *args->runAdvance_out = xCursorMax; |
1124 | } | 1220 | } |
@@ -1207,6 +1303,35 @@ iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **e | |||
1207 | return init_I2(advance, height); | 1303 | return init_I2(advance, height); |
1208 | } | 1304 | } |
1209 | 1305 | ||
1306 | iInt2 advance_WrapText(iWrapText *d, int fontId) { | ||
1307 | run_Font_(font_Text_(fontId), | ||
1308 | &(iRunArgs){ .mode = measure_RunMode | runFlagsFromId_(fontId), | ||
1309 | .text = d->text, | ||
1310 | .wrap = d | ||
1311 | }); | ||
1312 | return d->cursor_out; | ||
1313 | } | ||
1314 | |||
1315 | iRect measure_WrapText(iWrapText *d, int fontId) { | ||
1316 | return run_Font_(font_Text_(fontId), | ||
1317 | &(iRunArgs){ .mode = measure_RunMode | runFlagsFromId_(fontId), | ||
1318 | .text = d->text, | ||
1319 | .wrap = d | ||
1320 | }); | ||
1321 | } | ||
1322 | |||
1323 | void draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { | ||
1324 | run_Font_(font_Text_(fontId), | ||
1325 | &(iRunArgs){ .mode = draw_RunMode | runFlagsFromId_(fontId) | | ||
1326 | (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) | | ||
1327 | (color & fillBackground_ColorId ? fillBackground_RunMode : 0), | ||
1328 | .text = d->text, | ||
1329 | .pos = pos, | ||
1330 | .wrap = d, | ||
1331 | .color = color, | ||
1332 | }); | ||
1333 | } | ||
1334 | |||
1210 | iInt2 advance_Text(int fontId, const char *text) { | 1335 | iInt2 advance_Text(int fontId, const char *text) { |
1211 | return advanceRange_Text(fontId, range_CStr(text)); | 1336 | return advanceRange_Text(fontId, range_CStr(text)); |
1212 | } | 1337 | } |
diff --git a/src/ui/text.h b/src/ui/text.h index 5a099142..fe42a308 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
24 | 24 | ||
25 | #include <the_Foundation/rect.h> | 25 | #include <the_Foundation/rect.h> |
26 | #include <the_Foundation/string.h> | 26 | #include <the_Foundation/string.h> |
27 | #include <the_Foundation/vec2.h> | ||
27 | 28 | ||
28 | #include <SDL_render.h> | 29 | #include <SDL_render.h> |
29 | 30 | ||
@@ -164,7 +165,8 @@ void draw_Text (int fontId, iInt2 pos, int color, const char *t | |||
164 | void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); | 165 | void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); |
165 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); | 166 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); |
166 | void drawCenteredRange_Text (int fontId, iRect rect, iBool alignVisual, int color, iRangecc text); | 167 | void drawCenteredRange_Text (int fontId, iRect rect, iBool alignVisual, int color, iRangecc text); |
167 | void drawCenteredOutline_Text(int fontId, iRect rect, iBool alignVisual, int outlineColor, int fillColor, const char *text, ...); | 168 | void drawCenteredOutline_Text(int fontId, iRect rect, iBool alignVisual, int outlineColor, |
169 | int fillColor, const char *text, ...); | ||
168 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); | 170 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); |
169 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); | 171 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); |
170 | void drawRangeN_Text (int fontId, iInt2 pos, int color, iRangecc text, size_t maxLen); | 172 | void drawRangeN_Text (int fontId, iInt2 pos, int color, iRangecc text, size_t maxLen); |
@@ -172,6 +174,24 @@ void drawOutline_Text (int fontId, iInt2 pos, int outlineColor, int fi | |||
172 | void drawBoundRange_Text (int fontId, iInt2 pos, int boundWidth, int color, iRangecc text); /* bound does not wrap */ | 174 | void drawBoundRange_Text (int fontId, iInt2 pos, int boundWidth, int color, iRangecc text); /* bound does not wrap */ |
173 | int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ | 175 | int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ |
174 | 176 | ||
177 | iDeclareType(WrapText) | ||
178 | |||
179 | struct Impl_WrapText { | ||
180 | /* arguments */ | ||
181 | iRangecc text; | ||
182 | int maxWidth; | ||
183 | iBool (*wrapFunc)(iWrapText *, iRangecc wrappedText, int advance); | ||
184 | void * context; | ||
185 | /* output */ | ||
186 | iInt2 cursor_out; | ||
187 | /* internal */ | ||
188 | iRangecc wrapRange_; | ||
189 | }; | ||
190 | |||
191 | iRect measure_WrapText (iWrapText *, int fontId); | ||
192 | iInt2 advance_WrapText (iWrapText *, int fontId); | ||
193 | void draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); | ||
194 | |||
175 | SDL_Texture * glyphCache_Text (void); | 195 | SDL_Texture * glyphCache_Text (void); |
176 | 196 | ||
177 | enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode }; | 197 | enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode }; |