diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-13 09:49:44 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-13 09:49:44 +0300 |
commit | 6b931c95725eef2ebb7e831c4017d3d67b33294f (patch) | |
tree | 254595663d93c0dcc33baad475fb2f6c3ddeec40 /src/ui/text.c | |
parent | dd0a8798f32bf192ed703c6c512d4a76c4d407bc (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.c | 196 |
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 | ||
267 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 269 | iDefineTypeConstructionArgs(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) { | 586 | void 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 | ||
592 | void setDocumentFontSize_Text(iText *d, float fontSizeFactor) { | 591 | void setDocumentFontSize_Text(iText *d, float fontSizeFactor) { |
593 | fontSizeFactor *= contentScale_Text_; | 592 | fontSizeFactor *= contentScale_Text_; |
@@ -855,25 +854,45 @@ static iBool isControl_Char_(iChar c) { | |||
855 | iDeclareType(AttributedRun) | 854 | iDeclareType(AttributedRun) |
856 | 855 | ||
857 | struct Impl_AttributedRun { | 856 | struct 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 | ||
868 | static 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 | |||
878 | static void setFgColor_AttributedRun_(iAttributedRun *d, int colorId) { | ||
879 | d->attrib.colorId = colorId; | ||
880 | d->fgColor_.a = 0; | ||
881 | } | ||
882 | |||
868 | iDeclareType(AttributedText) | 883 | iDeclareType(AttributedText) |
869 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, | 884 | iDeclareTypeConstructionArgs(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 | ||
872 | struct Impl_AttributedText { | 888 | struct 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 | ||
887 | iDefineTypeConstructionArgs(AttributedText, | 905 | iDefineTypeConstructionArgs(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 | ||
892 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { | 912 | static 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 | ||
919 | static iFont *withStyle_Font_(const iFont *d, enum iFontStyle styleId) { | 939 | int 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 | |||
945 | int 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 | ||
925 | static iFont *withFontId_Font_(const iFont *d, enum iFontId fontId) { | 951 | int 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 | ||
931 | static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { | 957 | static 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 | ||
1116 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor, | 1159 | void 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 | ||
1340 | static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int advance, iBool isBaseRTL) { | 1384 | static 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 | ||
1942 | static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int advance, | 1994 | static 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 | } |