summaryrefslogtreecommitdiff
path: root/src/ui/text.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-10-13 09:49:44 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-10-13 09:49:44 +0300
commit6b931c95725eef2ebb7e831c4017d3d67b33294f (patch)
tree254595663d93c0dcc33baad475fb2f6c3ddeec40 /src/ui/text.c
parentdd0a8798f32bf192ed703c6c512d4a76c4d407bc (diff)
Text attributes that change inside a run
These changes concern the situation when the attributes of text (i.e., font, color) are changed via escape sequences. The concept of "base attributes" was added so that the low-level text renderer knows which font/color to set when a "reset" escape sequence is encountered. This depends on what kind of text is being renderer, e.g., preformatted or regular paragraphs. The base attributes were added as variables in Text because it was getting unwieldy to pass all the information via the draw/measure/WrapText functions. GmDocument now has a GmTheme struct that collects the font and color information into a single place.
Diffstat (limited to 'src/ui/text.c')
-rw-r--r--src/ui/text.c196
1 files changed, 124 insertions, 72 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index fd865fbd..52c6c4e0 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -262,6 +262,8 @@ struct Impl_Text {
262 SDL_Palette * grayscale; 262 SDL_Palette * grayscale;
263 SDL_Palette * blackAndWhite; /* unsmoothed glyph palette */ 263 SDL_Palette * blackAndWhite; /* unsmoothed glyph palette */
264 iRegExp * ansiEscape; 264 iRegExp * ansiEscape;
265 int baseFontId; /* base attributes (for restoring via escapes) */
266 int baseColorId;
265}; 267};
266 268
267iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) 269iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render)
@@ -537,10 +539,10 @@ void init_Text(iText *d, SDL_Renderer *render) {
537 iText *oldActive = activeText_; 539 iText *oldActive = activeText_;
538 activeText_ = d; 540 activeText_ = d;
539 init_Array(&d->fonts, sizeof(iFont)); 541 init_Array(&d->fonts, sizeof(iFont));
540// d->contentFont = nunito_TextFont;
541// d->headingFont = nunito_TextFont;
542 d->contentFontSize = contentScale_Text_; 542 d->contentFontSize = contentScale_Text_;
543 d->ansiEscape = new_RegExp("[[()]([0-9;AB]*)m", 0); 543 d->ansiEscape = new_RegExp("[[()]([0-9;AB]*)m", 0);
544 d->baseFontId = -1;
545 d->baseColorId = -1;
544 d->render = render; 546 d->render = render;
545 /* A grayscale palette for rasterized glyphs. */ { 547 /* A grayscale palette for rasterized glyphs. */ {
546 SDL_Color colors[256]; 548 SDL_Color colors[256];
@@ -581,13 +583,10 @@ void setOpacity_Text(float opacity) {
581 SDL_SetTextureAlphaMod(activeText_->cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f); 583 SDL_SetTextureAlphaMod(activeText_->cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f);
582} 584}
583 585
584//void setFont_Text(iText *d, int fontId, const char *fontSpecId) { 586void setBaseAttributes_Text(int fontId, int colorId) {
585// setupFontVariants_Text_(d, findSpec_Fonts(fontSpecId), fontId); 587 activeText_->baseFontId = fontId;
586// if (d->contentFont != font) { 588 activeText_->baseColorId = colorId;
587// d->contentFont = font; 589}
588// resetFonts_Text(d);
589// }
590//}
591 590
592void setDocumentFontSize_Text(iText *d, float fontSizeFactor) { 591void setDocumentFontSize_Text(iText *d, float fontSizeFactor) {
593 fontSizeFactor *= contentScale_Text_; 592 fontSizeFactor *= contentScale_Text_;
@@ -855,25 +854,45 @@ static iBool isControl_Char_(iChar c) {
855iDeclareType(AttributedRun) 854iDeclareType(AttributedRun)
856 855
857struct Impl_AttributedRun { 856struct Impl_AttributedRun {
858 iRangei logical; /* UTF-32 codepoint indices in the logical-order text */ 857 iRangei logical; /* UTF-32 codepoint indices in the logical-order text */
859 iFont * font; 858 iTextAttrib attrib;
860 iColor fgColor; 859 iFont *font;
860 iColor fgColor_; /* any RGB color; A > 0 */
861 struct { 861 struct {
862 uint8_t isLineBreak : 1; 862 uint8_t isLineBreak : 1;
863 uint8_t isRTL : 1; 863// uint8_t isRTL : 1;
864 uint8_t isArabic : 1; /* Arabic script detected */ 864 uint8_t isArabic : 1; /* Arabic script detected */
865 } flags; 865 } flags;
866}; 866};
867 867
868static iColor fgColor_AttributedRun_(const iAttributedRun *d) {
869 if (d->fgColor_.a) {
870 return d->fgColor_;
871 }
872 if (d->attrib.colorId == none_ColorId) {
873 return (iColor){ 255, 255, 255, 255 };
874 }
875 return get_Color(d->attrib.colorId);
876}
877
878static void setFgColor_AttributedRun_(iAttributedRun *d, int colorId) {
879 d->attrib.colorId = colorId;
880 d->fgColor_.a = 0;
881}
882
868iDeclareType(AttributedText) 883iDeclareType(AttributedText)
869iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, 884iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font,
870 iColor fgColor, int baseDir, iChar overrideChar) 885 int colorId, int baseDir, iFont *baseFont, int baseColorId,
886 iChar overrideChar)
871 887
872struct Impl_AttributedText { 888struct Impl_AttributedText {
873 iRangecc source; /* original source text */ 889 iRangecc source; /* original source text */
874 size_t maxLen; 890 size_t maxLen;
875 iFont * font; 891 iFont * font;
876 iColor fgColor; 892 int colorId;
893 iFont * baseFont;
894 int baseColorId;
895 iBool isBaseRTL;
877 iArray runs; 896 iArray runs;
878 iArray logical; /* UTF-32 text in logical order (mixed directions; matches source) */ 897 iArray logical; /* UTF-32 text in logical order (mixed directions; matches source) */
879 iArray visual; /* UTF-32 text in visual order (LTR) */ 898 iArray visual; /* UTF-32 text in visual order (LTR) */
@@ -881,13 +900,14 @@ struct Impl_AttributedText {
881 iArray visualToLogical; 900 iArray visualToLogical;
882 iArray logicalToSourceOffset; /* map logical character to an UTF-8 offset in the source text */ 901 iArray logicalToSourceOffset; /* map logical character to an UTF-8 offset in the source text */
883 char * bidiLevels; 902 char * bidiLevels;
884 iBool isBaseRTL;
885}; 903};
886 904
887iDefineTypeConstructionArgs(AttributedText, 905iDefineTypeConstructionArgs(AttributedText,
888 (iRangecc text, size_t maxLen, iFont *font, iColor fgColor, 906 (iRangecc text, size_t maxLen, iFont *font, int colorId,
889 int baseDir, iChar overrideChar), 907 int baseDir, iFont *baseFont, int baseColorId,
890 text, maxLen, font, fgColor, baseDir, overrideChar) 908 iChar overrideChar),
909 text, maxLen, font, colorId, baseDir, baseFont, baseColorId,
910 overrideChar)
891 911
892static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { 912static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) {
893 const int *logToSource = constData_Array(&d->logicalToSourceOffset); 913 const int *logToSource = constData_Array(&d->logicalToSourceOffset);
@@ -916,16 +936,22 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, i
916 run->logical.start = endAt; 936 run->logical.start = endAt;
917} 937}
918 938
919static iFont *withStyle_Font_(const iFont *d, enum iFontStyle styleId) { 939int fontWithSize_Text(int font, enum iFontSize sizeId) {
920 const int fontId = (fontId_Text_(d) / maxVariants_Fonts) * maxVariants_Fonts; 940 const int familyId = (font / maxVariants_Fonts) * maxVariants_Fonts;
921 const int sizeId = sizeId_Text_(d); 941 const int styleId = (font / max_FontSize) % max_FontStyle;
922 return font_Text_(FONT_ID(fontId, styleId, sizeId)); 942 return FONT_ID(familyId, styleId, sizeId);
943}
944
945int fontWithStyle_Text(int font, enum iFontStyle styleId) {
946 const int familyId = (font / maxVariants_Fonts) * maxVariants_Fonts;
947 const int sizeId = font % max_FontSize;
948 return FONT_ID(familyId, styleId, sizeId);
923} 949}
924 950
925static iFont *withFontId_Font_(const iFont *d, enum iFontId fontId) { 951int fontWithFamily_Text(int font, enum iFontId familyId) {
926 const int styleId = styleId_Text_(d); 952 const int styleId = (font / max_FontSize) % max_FontStyle;
927 const int sizeId = sizeId_Text_(d); 953 const int sizeId = font % max_FontSize;
928 return font_Text_(FONT_ID(fontId, styleId, sizeId)); 954 return FONT_ID(familyId, styleId, sizeId);
929} 955}
930 956
931static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { 957static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) {
@@ -982,15 +1008,17 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
982 pushBack_Array(&d->logicalToVisual, &(int){ length }); 1008 pushBack_Array(&d->logicalToVisual, &(int){ length });
983 pushBack_Array(&d->visualToLogical, &(int){ length }); 1009 pushBack_Array(&d->visualToLogical, &(int){ length });
984 } 1010 }
985 iAttributedRun run = { .logical = { 0, length }, 1011 iAttributedRun run = {
986 .font = d->font, 1012 .logical = { 0, length },
987 .fgColor = d->fgColor }; 1013 .attrib = { .colorId = d->colorId, .isBaseRTL = d->isBaseRTL },
988 const int * logToSource = constData_Array(&d->logicalToSourceOffset); 1014 .font = d->font,
1015 };
1016 const int *logToSource = constData_Array(&d->logicalToSourceOffset);
989 const int * logToVis = constData_Array(&d->logicalToVisual); 1017 const int * logToVis = constData_Array(&d->logicalToVisual);
990 const iChar * logicalText = constData_Array(&d->logical); 1018 const iChar * logicalText = constData_Array(&d->logical);
991 iBool isRTL = d->isBaseRTL; 1019 iBool isRTL = d->isBaseRTL;
992 int numNonSpace = 0; 1020 int numNonSpace = 0;
993 iFont * activeFont = d->font; 1021 iFont * attribFont = d->font;
994 for (int pos = 0; pos < length; pos++) { 1022 for (int pos = 0; pos < length; pos++) {
995 const iChar ch = logicalText[pos]; 1023 const iChar ch = logicalText[pos];
996#if defined (LAGRANGE_ENABLE_FRIBIDI) 1024#if defined (LAGRANGE_ENABLE_FRIBIDI)
@@ -1010,7 +1038,7 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
1010#else 1038#else
1011 const iBool isNeutral = iTrue; 1039 const iBool isNeutral = iTrue;
1012#endif 1040#endif
1013 run.flags.isRTL = isRTL; 1041 run.attrib.isRTL = isRTL;
1014 if (ch == 0x1b) { /* ANSI escape. */ 1042 if (ch == 0x1b) { /* ANSI escape. */
1015 pos++; 1043 pos++;
1016 const char *srcPos = d->source.start + logToSource[pos]; 1044 const char *srcPos = d->source.start + logToSource[pos];
@@ -1020,21 +1048,36 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
1020 if (match_RegExp(activeText_->ansiEscape, srcPos, d->source.end - srcPos, &m)) { 1048 if (match_RegExp(activeText_->ansiEscape, srcPos, d->source.end - srcPos, &m)) {
1021 finishRun_AttributedText_(d, &run, pos - 1); 1049 finishRun_AttributedText_(d, &run, pos - 1);
1022 const iRangecc sequence = capturedRange_RegExpMatch(&m, 1); 1050 const iRangecc sequence = capturedRange_RegExpMatch(&m, 1);
1051 /* TODO: Bold/italic attributes are assumed to be inside body text.
1052 We don't know what the current text style is supposed to be.
1053 That should be an additional attribute passed to WrapText, or a feature of
1054 WrapText that can be called both from here and in the run typesetter.
1055 The styling here is hardcoded to match `typesetOneLine_RunTypesetter_()`. */
1023 if (equal_Rangecc(sequence, "1")) { 1056 if (equal_Rangecc(sequence, "1")) {
1024 activeFont = withStyle_Font_(activeFont, bold_FontStyle); 1057 run.attrib.bold = iTrue;
1058 if (d->baseColorId == tmParagraph_ColorId) {
1059 setFgColor_AttributedRun_(&run, tmFirstParagraph_ColorId);
1060 }
1061 attribFont = font_Text_(fontWithStyle_Text(fontId_Text_(d->baseFont), bold_FontStyle));
1025 } 1062 }
1026 else if (equal_Rangecc(sequence, "3")) { 1063 else if (equal_Rangecc(sequence, "3")) {
1027 activeFont = withStyle_Font_(activeFont, italic_FontStyle); 1064 run.attrib.italic = iTrue;
1065 attribFont = font_Text_(fontWithStyle_Text(fontId_Text_(d->baseFont), italic_FontStyle));
1028 } 1066 }
1029 else if (equal_Rangecc(sequence, "4")) { 1067 else if (equal_Rangecc(sequence, "4")) {
1030 activeFont = withFontId_Font_(activeFont, monospace_FontId); 1068 run.attrib.monospace = iTrue;
1069 setFgColor_AttributedRun_(&run, tmPreformatted_ColorId);
1070 attribFont = font_Text_(fontWithFamily_Text(fontId_Text_(d->baseFont), monospace_FontId));
1031 } 1071 }
1032 else if (equal_Rangecc(sequence, "0")) { 1072 else if (equal_Rangecc(sequence, "0")) {
1033 activeFont = d->font; /* restore original */ 1073 run.attrib.bold = iFalse;
1034 run.fgColor = d->fgColor; 1074 run.attrib.italic = iFalse;
1075 run.attrib.monospace = iFalse;
1076 attribFont = d->baseFont;
1077 setFgColor_AttributedRun_(&run, d->baseColorId);
1035 } 1078 }
1036 else { 1079 else {
1037 run.fgColor = ansiForeground_Color(sequence, tmParagraph_ColorId); 1080 run.fgColor_ = ansiForeground_Color(sequence, tmParagraph_ColorId);
1038 } 1081 }
1039 pos += length_Rangecc(capturedRange_RegExpMatch(&m, 0)); 1082 pos += length_Rangecc(capturedRange_RegExpMatch(&m, 0));
1040 iAssert(logToSource[pos] == end_RegExpMatch(&m) - d->source.start); 1083 iAssert(logToSource[pos] == end_RegExpMatch(&m) - d->source.start);
@@ -1056,7 +1099,7 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
1056 colorNum = esc - asciiBase_ColorEscape; 1099 colorNum = esc - asciiBase_ColorEscape;
1057 } 1100 }
1058 run.logical.start = pos + 1; 1101 run.logical.start = pos + 1;
1059 run.fgColor = (colorNum >= 0 ? get_Color(colorNum) : d->fgColor); 1102 setFgColor_AttributedRun_(&run, colorNum >= 0 ? colorNum : d->colorId);
1060 continue; 1103 continue;
1061 } 1104 }
1062 if (ch == '\n') { 1105 if (ch == '\n') {
@@ -1077,7 +1120,7 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
1077 } 1120 }
1078 continue; 1121 continue;
1079 } 1122 }
1080 iFont *currentFont = activeFont; 1123 iFont *currentFont = attribFont;
1081 if (run.font->fontSpec->flags & arabic_FontSpecFlag && isPunct_Char(ch)) { 1124 if (run.font->fontSpec->flags & arabic_FontSpecFlag && isPunct_Char(ch)) {
1082 currentFont = run.font; /* remain as Arabic for whitespace */ 1125 currentFont = run.font; /* remain as Arabic for whitespace */
1083 } 1126 }
@@ -1113,12 +1156,15 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
1113#endif 1156#endif
1114} 1157}
1115 1158
1116void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor, 1159void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, int colorId,
1117 int baseDir, iChar overrideChar) { 1160 int baseDir, iFont *baseFont, int baseColorId, iChar overrideChar) {
1118 d->source = text; 1161 d->source = text;
1119 d->maxLen = maxLen ? maxLen : iInvalidSize; 1162 d->maxLen = maxLen ? maxLen : iInvalidSize;
1120 d->font = font; 1163 d->font = font;
1121 d->fgColor = fgColor; 1164 d->colorId = colorId;
1165 d->baseFont = baseFont;
1166 d->baseColorId = baseColorId;
1167 d->isBaseRTL = iFalse;
1122 init_Array(&d->runs, sizeof(iAttributedRun)); 1168 init_Array(&d->runs, sizeof(iAttributedRun));
1123 init_Array(&d->logical, sizeof(iChar)); 1169 init_Array(&d->logical, sizeof(iChar));
1124 init_Array(&d->visual, sizeof(iChar)); 1170 init_Array(&d->visual, sizeof(iChar));
@@ -1126,7 +1172,6 @@ void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont
1126 init_Array(&d->visualToLogical, sizeof(int)); 1172 init_Array(&d->visualToLogical, sizeof(int));
1127 init_Array(&d->logicalToSourceOffset, sizeof(int)); 1173 init_Array(&d->logicalToSourceOffset, sizeof(int));
1128 d->bidiLevels = NULL; 1174 d->bidiLevels = NULL;
1129 d->isBaseRTL = iFalse;
1130 prepare_AttributedText_(d, baseDir, overrideChar); 1175 prepare_AttributedText_(d, baseDir, overrideChar);
1131} 1176}
1132 1177
@@ -1278,7 +1323,7 @@ static void cacheTextGlyphs_Font_(iFont *d, const iRangecc text) {
1278 iArray glyphIndices; 1323 iArray glyphIndices;
1279 init_Array(&glyphIndices, sizeof(uint32_t)); 1324 init_Array(&glyphIndices, sizeof(uint32_t));
1280 iAttributedText attrText; 1325 iAttributedText attrText;
1281 init_AttributedText(&attrText, text, 0, d, (iColor){}, 0, 0); 1326 init_AttributedText(&attrText, text, 0, d, none_ColorId, 0, d, none_ColorId, 0);
1282 /* We use AttributedText here so the font lookup matches the behavior during text drawing -- 1327 /* We use AttributedText here so the font lookup matches the behavior during text drawing --
1283 glyphs may be selected from a font that's different than `d`. */ 1328 glyphs may be selected from a font that's different than `d`. */
1284 const iChar *logicalText = constData_Array(&attrText.logical); 1329 const iChar *logicalText = constData_Array(&attrText.logical);
@@ -1333,17 +1378,17 @@ struct Impl_RunArgs {
1333 /* TODO: Cleanup using TextMetrics 1378 /* TODO: Cleanup using TextMetrics
1334 Use TextMetrics output pointer instead of return value & cursorAdvance_out. */ 1379 Use TextMetrics output pointer instead of return value & cursorAdvance_out. */
1335 iInt2 * cursorAdvance_out; 1380 iInt2 * cursorAdvance_out;
1336// const char ** continueFrom_out;
1337 int * runAdvance_out; 1381 int * runAdvance_out;
1338}; 1382};
1339 1383
1340static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int advance, iBool isBaseRTL) { 1384static iBool notify_WrapText_(iWrapText *d, const char *ending, iTextAttrib attrib,
1385 int origin, int advance) {
1341 if (d && d->wrapFunc && d->wrapRange_.start) { 1386 if (d && d->wrapFunc && d->wrapRange_.start) {
1342 /* `wrapRange_` uses logical indices. */ 1387 /* `wrapRange_` uses logical indices. */
1343 const char *end = ending ? ending : d->wrapRange_.end; 1388 const char *end = ending ? ending : d->wrapRange_.end;
1344 iRangecc range = { d->wrapRange_.start, end }; 1389 iRangecc range = { d->wrapRange_.start, end };
1345 iAssert(range.start <= range.end); 1390 iAssert(range.start <= range.end);
1346 const iBool result = d->wrapFunc(d, range, origin, advance, isBaseRTL); 1391 const iBool result = d->wrapFunc(d, range, attrib, origin, advance);
1347 if (result) { 1392 if (result) {
1348 d->wrapRange_.start = end; 1393 d->wrapRange_.start = end;
1349 } 1394 }
@@ -1470,8 +1515,11 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1470 font is used and other attributes such as color. (HarfBuzz shaping is done 1515 font is used and other attributes such as color. (HarfBuzz shaping is done
1471 with one specific font.) */ 1516 with one specific font.) */
1472 iAttributedText attrText; 1517 iAttributedText attrText;
1473 init_AttributedText(&attrText, args->text, args->maxLen, d, get_Color(args->color), 1518 init_AttributedText(&attrText, args->text, args->maxLen, d, args->color,
1474 args->baseDir, wrap ? wrap->overrideChar : 0); 1519 args->baseDir,
1520 activeText_->baseFontId >= 0 ? font_Text_(activeText_->baseFontId) : d,
1521 activeText_->baseColorId,
1522 wrap ? wrap->overrideChar : 0);
1475 if (wrap) { 1523 if (wrap) {
1476 wrap->baseDir = attrText.isBaseRTL ? -1 : +1; 1524 wrap->baseDir = attrText.isBaseRTL ? -1 : +1;
1477 /* TODO: Duplicated args? */ 1525 /* TODO: Duplicated args? */
@@ -1522,6 +1570,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1522 iRangei wrapPosRange = { 0, textLen }; 1570 iRangei wrapPosRange = { 0, textLen };
1523 int wrapResumePos = textLen; /* logical position where next line resumes */ 1571 int wrapResumePos = textLen; /* logical position where next line resumes */
1524 size_t wrapResumeRunIndex = runCount; /* index of run where next line resumes */ 1572 size_t wrapResumeRunIndex = runCount; /* index of run where next line resumes */
1573 iTextAttrib attrib = { .colorId = args->color, .isBaseRTL = attrText.isBaseRTL };
1574 iTextAttrib wrapAttrib = attrib;
1575 iTextAttrib lastAttrib = attrib;
1525 const int layoutBound = (wrap ? wrap->maxWidth : 0); 1576 const int layoutBound = (wrap ? wrap->maxWidth : 0);
1526 iBool isFirst = iTrue; 1577 iBool isFirst = iTrue;
1527 const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2()); 1578 const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2());
@@ -1545,6 +1596,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1545 /* Determine ends of wrapRuns and wrapVisRange. */ 1596 /* Determine ends of wrapRuns and wrapVisRange. */
1546 for (size_t runIndex = wrapRuns.start; runIndex < wrapRuns.end; runIndex++) { 1597 for (size_t runIndex = wrapRuns.start; runIndex < wrapRuns.end; runIndex++) {
1547 const iAttributedRun *run = at_Array(&attrText.runs, runIndex); 1598 const iAttributedRun *run = at_Array(&attrText.runs, runIndex);
1599 /* Update the attributes. */
1548 if (run->flags.isLineBreak) { 1600 if (run->flags.isLineBreak) {
1549 if (checkHitChar && 1601 if (checkHitChar &&
1550 wrap->hitChar == sourcePtr_AttributedText_(&attrText, run->logical.start)) { 1602 wrap->hitChar == sourcePtr_AttributedText_(&attrText, run->logical.start)) {
@@ -1554,7 +1606,6 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1554 wrapResumePos = run->logical.end; 1606 wrapResumePos = run->logical.end;
1555 wrapRuns.end = runIndex; 1607 wrapRuns.end = runIndex;
1556 wrapResumeRunIndex = runIndex + 1; 1608 wrapResumeRunIndex = runIndex + 1;
1557 //yCursor += d->height;
1558 break; 1609 break;
1559 } 1610 }
1560 wrapResumeRunIndex = runCount; 1611 wrapResumeRunIndex = runCount;
@@ -1564,8 +1615,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1564 shape_GlyphBuffer_(buf); 1615 shape_GlyphBuffer_(buf);
1565 int safeBreakPos = -1; 1616 int safeBreakPos = -1;
1566 iChar prevCh = 0; 1617 iChar prevCh = 0;
1618 lastAttrib = run->attrib;
1567 for (unsigned int ir = 0; ir < buf->glyphCount; ir++) { 1619 for (unsigned int ir = 0; ir < buf->glyphCount; ir++) {
1568 const int i = (run->flags.isRTL ? buf->glyphCount - ir - 1 : ir); 1620 const int i = (run->attrib.isRTL ? buf->glyphCount - ir - 1 : ir);
1569 const hb_glyph_info_t *info = &buf->glyphInfo[i]; 1621 const hb_glyph_info_t *info = &buf->glyphInfo[i];
1570 const hb_codepoint_t glyphId = info->codepoint; 1622 const hb_codepoint_t glyphId = info->codepoint;
1571 const int logPos = info->cluster; 1623 const int logPos = info->cluster;
@@ -1603,10 +1655,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1603 prevCh = ch; 1655 prevCh = ch;
1604 } 1656 }
1605 else { 1657 else {
1606 //if (~glyphFlags & HB_GLYPH_FLAG_UNSAFE_TO_BREAK) { 1658 safeBreakPos = logPos;
1607 safeBreakPos = logPos; 1659 breakAdvance = wrapAdvance;
1608 breakAdvance = wrapAdvance; 1660 wrapAttrib = run->attrib;
1609 //}
1610 } 1661 }
1611 if (isHitPointOnThisLine) { 1662 if (isHitPointOnThisLine) {
1612 if (wrap->hitPoint.x >= orig.x + wrapAdvance && 1663 if (wrap->hitPoint.x >= orig.x + wrapAdvance &&
@@ -1638,7 +1689,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1638 wrapPosRange.end = logPos; 1689 wrapPosRange.end = logPos;
1639 breakAdvance = wrapAdvance; 1690 breakAdvance = wrapAdvance;
1640 } 1691 }
1641 wrapResumePos = wrapPosRange.end; 1692 wrapResumePos = wrapPosRange.end;
1642 if (args->wrap->mode != anyCharacter_WrapTextMode) { 1693 if (args->wrap->mode != anyCharacter_WrapTextMode) {
1643 while (wrapResumePos < textLen && isSpace_Char(logicalText[wrapResumePos])) { 1694 while (wrapResumePos < textLen && isSpace_Char(logicalText[wrapResumePos])) {
1644 wrapResumePos++; /* skip space */ 1695 wrapResumePos++; /* skip space */
@@ -1688,7 +1739,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1688 for (size_t runIndex = wrapRuns.start; runIndex < wrapRuns.end; runIndex++) { 1739 for (size_t runIndex = wrapRuns.start; runIndex < wrapRuns.end; runIndex++) {
1689 const iAttributedRun *run = at_Array(&attrText.runs, runIndex); 1740 const iAttributedRun *run = at_Array(&attrText.runs, runIndex);
1690 if (!attrText.isBaseRTL) { /* left-to-right */ 1741 if (!attrText.isBaseRTL) { /* left-to-right */
1691 if (run->flags.isRTL) { 1742 if (run->attrib.isRTL) {
1692 if (oppositeInsertIndex == iInvalidPos) { 1743 if (oppositeInsertIndex == iInvalidPos) {
1693 oppositeInsertIndex = size_Array(&runOrder); 1744 oppositeInsertIndex = size_Array(&runOrder);
1694 } 1745 }
@@ -1700,7 +1751,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1700 } 1751 }
1701 } 1752 }
1702 else { /* right-to-left */ 1753 else { /* right-to-left */
1703 if (!run->flags.isRTL) { 1754 if (!run->attrib.isRTL) {
1704 if (oppositeInsertIndex == iInvalidPos) { 1755 if (oppositeInsertIndex == iInvalidPos) {
1705 oppositeInsertIndex = 0; 1756 oppositeInsertIndex = 0;
1706 } 1757 }
@@ -1739,11 +1790,12 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1739 if (wrap && wrap->wrapFunc && 1790 if (wrap && wrap->wrapFunc &&
1740 !notify_WrapText_(args->wrap, 1791 !notify_WrapText_(args->wrap,
1741 sourcePtr_AttributedText_(&attrText, wrapResumePos), 1792 sourcePtr_AttributedText_(&attrText, wrapResumePos),
1793 wrapAttrib,
1742 origin, 1794 origin,
1743 iRound(wrapAdvance), 1795 iRound(wrapAdvance))) {
1744 attrText.isBaseRTL)) {
1745 willAbortDueToWrap = iTrue; 1796 willAbortDueToWrap = iTrue;
1746 } 1797 }
1798 wrapAttrib = lastAttrib;
1747 xCursor = origin; 1799 xCursor = origin;
1748 /* We have determined a possible wrap position and alignment for the work runs, 1800 /* We have determined a possible wrap position and alignment for the work runs,
1749 so now we can process the glyphs. */ 1801 so now we can process the glyphs. */
@@ -1796,8 +1848,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1796 orig.y + yCursor - yOffset + glyph->font->baseline + glyph->d[hoff].y, 1848 orig.y + yCursor - yOffset + glyph->font->baseline + glyph->d[hoff].y,
1797 glyph->rect[hoff].size.x, 1849 glyph->rect[hoff].size.x,
1798 glyph->rect[hoff].size.y }; 1850 glyph->rect[hoff].size.y };
1799 if (run->font->height < d->height) { 1851 if (run->font->height < attrText.baseFont->height) {
1800 dst.y += d->baseline - run->font->baseline; 1852 dst.y += attrText.baseFont->baseline - run->font->baseline;
1801 } 1853 }
1802 if (mode & visualFlag_RunMode) { 1854 if (mode & visualFlag_RunMode) {
1803 if (isEmpty_Rect(bounds)) { 1855 if (isEmpty_Rect(bounds)) {
@@ -1819,7 +1871,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1819 iAssert(isRasterized_Glyph_(glyph, hoff)); 1871 iAssert(isRasterized_Glyph_(glyph, hoff));
1820 } 1872 }
1821 if (~mode & permanentColorFlag_RunMode) { 1873 if (~mode & permanentColorFlag_RunMode) {
1822 const iColor clr = run->fgColor; 1874 const iColor clr = fgColor_AttributedRun_(run);
1823 SDL_SetTextureColorMod(activeText_->cache, clr.r, clr.g, clr.b); 1875 SDL_SetTextureColorMod(activeText_->cache, clr.r, clr.g, clr.b);
1824 if (args->mode & fillBackground_RunMode) { 1876 if (args->mode & fillBackground_RunMode) {
1825 SDL_SetRenderDrawColor(activeText_->render, clr.r, clr.g, clr.b, 0); 1877 SDL_SetRenderDrawColor(activeText_->render, clr.r, clr.g, clr.b, 0);
@@ -1939,9 +1991,9 @@ static int runFlagsFromId_(enum iFontId fontId) {
1939 return runFlags; 1991 return runFlags;
1940} 1992}
1941 1993
1942static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int advance, 1994static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, iTextAttrib attrib, int origin,
1943 iBool isBaseRTL) { 1995 int advance) {
1944 iUnused(origin, advance, isBaseRTL); 1996 iUnused(attrib, origin, advance);
1945 *((const char **) d->context) = range.end; 1997 *((const char **) d->context) = range.end;
1946 return iFalse; /* just one line */ 1998 return iFalse; /* just one line */
1947} 1999}