diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-13 21:02:33 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-13 21:02:33 +0300 |
commit | d939dcd1109d7c92e6290976eaf84b94984abef3 (patch) | |
tree | 8e07462d687bec980095203e4d351b5e3a9774c0 /src/ui | |
parent | f3a3b7785489f1d7c782e02d2e753d012729ab80 (diff) |
Drawing document RTL text runs
The base text direction of each line of text is determined when the document is laid out. When drawing runs, use this predetermined base direction.
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/documentwidget.c | 29 | ||||
-rw-r--r-- | src/ui/text.c | 54 | ||||
-rw-r--r-- | src/ui/text.h | 2 |
3 files changed, 50 insertions, 35 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 3c6c0039..ea4909eb 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -1585,7 +1585,7 @@ static void parseUser_DocumentWidget_(iDocumentWidget *d) { | |||
1585 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { | 1585 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { |
1586 | iUnused(data); | 1586 | iUnused(data); |
1587 | if (!isEmpty_Range(&run->text)) { | 1587 | if (!isEmpty_Range(&run->text)) { |
1588 | cache_Text(run->font, run->text); | 1588 | cache_Text(run->textParams.font, run->text); |
1589 | } | 1589 | } |
1590 | } | 1590 | } |
1591 | 1591 | ||
@@ -3910,14 +3910,14 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
3910 | contains_Range(&mark, run->text.start))) { | 3910 | contains_Range(&mark, run->text.start))) { |
3911 | int x = 0; | 3911 | int x = 0; |
3912 | if (!*isInside) { | 3912 | if (!*isInside) { |
3913 | x = measureRange_Text(run->font, | 3913 | x = measureRange_Text(run->textParams.font, |
3914 | (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) | 3914 | (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) |
3915 | .advance.x; | 3915 | .advance.x; |
3916 | } | 3916 | } |
3917 | int w = width_Rect(run->visBounds) - x; | 3917 | int w = width_Rect(run->visBounds) - x; |
3918 | if (contains_Range(&run->text, mark.end) || mark.end < run->text.start) { | 3918 | if (contains_Range(&run->text, mark.end) || mark.end < run->text.start) { |
3919 | w = measureRange_Text( | 3919 | w = measureRange_Text( |
3920 | run->font, | 3920 | run->textParams.font, |
3921 | !*isInside ? mark | 3921 | !*isInside ? mark |
3922 | : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }) | 3922 | : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }) |
3923 | .advance.x; | 3923 | .advance.x; |
@@ -3973,15 +3973,15 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 | |||
3973 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | 3973 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); |
3974 | if (icon) { | 3974 | if (icon) { |
3975 | appendChar_String(&str, icon); | 3975 | appendChar_String(&str, icon); |
3976 | const iRect iconRect = visualBounds_Text(run->font, range_String(&str)); | 3976 | const iRect iconRect = visualBounds_Text(run->textParams.font, range_String(&str)); |
3977 | drawRange_Text( | 3977 | drawRange_Text( |
3978 | run->font, | 3978 | run->textParams.font, |
3979 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | 3979 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->textParams.font) / 2), |
3980 | tmBannerIcon_ColorId, | 3980 | tmBannerIcon_ColorId, |
3981 | range_String(&str)); | 3981 | range_String(&str)); |
3982 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | 3982 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; |
3983 | } | 3983 | } |
3984 | drawRange_Text(run->font, | 3984 | drawRange_Text(run->textParams.font, |
3985 | bpos, | 3985 | bpos, |
3986 | tmBannerTitle_ColorId, | 3986 | tmBannerTitle_ColorId, |
3987 | bannerText_DocumentWidget_(d->widget)); | 3987 | bannerText_DocumentWidget_(d->widget)); |
@@ -4088,7 +4088,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4088 | /* Media UIs are drawn afterwards as a dynamic overlay. */ | 4088 | /* Media UIs are drawn afterwards as a dynamic overlay. */ |
4089 | return; | 4089 | return; |
4090 | } | 4090 | } |
4091 | enum iColorId fg = run->color; | 4091 | enum iColorId fg = run->textParams.color; |
4092 | const iGmDocument *doc = d->widget->doc; | 4092 | const iGmDocument *doc = d->widget->doc; |
4093 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); | 4093 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); |
4094 | /* Hover state of a link. */ | 4094 | /* Hover state of a link. */ |
@@ -4152,8 +4152,11 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4152 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); | 4152 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); |
4153 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); | 4153 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); |
4154 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); | 4154 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); |
4155 | drawWrapRange_Text(run->font, add_I2(visPos, margin), | 4155 | drawWrapRange_Text(run->textParams.font, |
4156 | run->visBounds.size.x - 2 * margin.x, run->color, run->text); | 4156 | add_I2(visPos, margin), |
4157 | run->visBounds.size.x - 2 * margin.x, | ||
4158 | run->textParams.color, | ||
4159 | run->text); | ||
4157 | } | 4160 | } |
4158 | else if (run->flags & siteBanner_GmRunFlag) { | 4161 | else if (run->flags & siteBanner_GmRunFlag) { |
4159 | /* Banner background. */ | 4162 | /* Banner background. */ |
@@ -4194,7 +4197,11 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4194 | height_Rect(run->visBounds), | 4197 | height_Rect(run->visBounds), |
4195 | tmQuoteIcon_ColorId); | 4198 | tmQuoteIcon_ColorId); |
4196 | } | 4199 | } |
4197 | drawBoundRange_Text(run->font, visPos, width_Rect(run->visBounds), fg, run->text); | 4200 | drawBoundRange_Text(run->textParams.font, |
4201 | visPos, | ||
4202 | (run->textParams.isRTL ? -1 : 1) * width_Rect(run->visBounds), | ||
4203 | fg, | ||
4204 | run->text); | ||
4198 | runDrawn:; | 4205 | runDrawn:; |
4199 | } | 4206 | } |
4200 | /* Presentation of links. */ | 4207 | /* Presentation of links. */ |
diff --git a/src/ui/text.c b/src/ui/text.c index e1638368..03ce46c7 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -792,7 +792,8 @@ struct Impl_AttributedRun { | |||
792 | }; | 792 | }; |
793 | 793 | ||
794 | iDeclareType(AttributedText) | 794 | iDeclareType(AttributedText) |
795 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) | 795 | iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, |
796 | iColor fgColor, int baseDir) | ||
796 | 797 | ||
797 | struct Impl_AttributedText { | 798 | struct Impl_AttributedText { |
798 | iRangecc source; /* original source text */ | 799 | iRangecc source; /* original source text */ |
@@ -809,8 +810,9 @@ struct Impl_AttributedText { | |||
809 | iBool isBaseRTL; | 810 | iBool isBaseRTL; |
810 | }; | 811 | }; |
811 | 812 | ||
812 | iDefineTypeConstructionArgs(AttributedText, (iRangecc text, size_t maxLen, iFont *font, iColor fgColor), | 813 | iDefineTypeConstructionArgs(AttributedText, |
813 | text, maxLen, font, fgColor) | 814 | (iRangecc text, size_t maxLen, iFont *font, iColor fgColor, int baseDir), |
815 | text, maxLen, font, fgColor, baseDir) | ||
814 | 816 | ||
815 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { | 817 | static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { |
816 | const int *logToSource = constData_Array(&d->logicalToSourceOffset); | 818 | const int *logToSource = constData_Array(&d->logicalToSourceOffset); |
@@ -867,7 +869,7 @@ static enum iFontId fontId_Text_(const iFont *font) { | |||
867 | return (enum iFontId) (font - text_.fonts); | 869 | return (enum iFontId) (font - text_.fonts); |
868 | } | 870 | } |
869 | 871 | ||
870 | static void prepare_AttributedText_(iAttributedText *d) { | 872 | static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir) { |
871 | iAssert(isEmpty_Array(&d->runs)); | 873 | iAssert(isEmpty_Array(&d->runs)); |
872 | size_t length = 0; | 874 | size_t length = 0; |
873 | /* Prepare the UTF-32 logical string. */ { | 875 | /* Prepare the UTF-32 logical string. */ { |
@@ -896,7 +898,7 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
896 | data_Array(&d->logicalToVisual), | 898 | data_Array(&d->logicalToVisual), |
897 | data_Array(&d->visualToLogical), | 899 | data_Array(&d->visualToLogical), |
898 | (FriBidiLevel *) d->bidiLevels); | 900 | (FriBidiLevel *) d->bidiLevels); |
899 | d->isBaseRTL = FRIBIDI_IS_RTL(baseDir); | 901 | d->isBaseRTL = (overrideBaseDir == 0 ? FRIBIDI_IS_RTL(baseDir) : (overrideBaseDir < 0)); |
900 | #endif | 902 | #endif |
901 | } | 903 | } |
902 | /* The mapping needs to include the terminating NULL position. */ { | 904 | /* The mapping needs to include the terminating NULL position. */ { |
@@ -1017,7 +1019,8 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
1017 | #endif | 1019 | #endif |
1018 | } | 1020 | } |
1019 | 1021 | ||
1020 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) { | 1022 | void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor, |
1023 | int baseDir) { | ||
1021 | d->source = text; | 1024 | d->source = text; |
1022 | d->maxLen = maxLen ? maxLen : iInvalidSize; | 1025 | d->maxLen = maxLen ? maxLen : iInvalidSize; |
1023 | d->font = font; | 1026 | d->font = font; |
@@ -1030,7 +1033,7 @@ void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont | |||
1030 | init_Array(&d->logicalToSourceOffset, sizeof(int)); | 1033 | init_Array(&d->logicalToSourceOffset, sizeof(int)); |
1031 | d->bidiLevels = NULL; | 1034 | d->bidiLevels = NULL; |
1032 | d->isBaseRTL = iFalse; | 1035 | d->isBaseRTL = iFalse; |
1033 | prepare_AttributedText_(d); | 1036 | prepare_AttributedText_(d, baseDir); |
1034 | } | 1037 | } |
1035 | 1038 | ||
1036 | void deinit_AttributedText(iAttributedText *d) { | 1039 | void deinit_AttributedText(iAttributedText *d) { |
@@ -1181,7 +1184,7 @@ static void cacheTextGlyphs_Font_(iFont *d, const iRangecc text) { | |||
1181 | iArray glyphIndices; | 1184 | iArray glyphIndices; |
1182 | init_Array(&glyphIndices, sizeof(uint32_t)); | 1185 | init_Array(&glyphIndices, sizeof(uint32_t)); |
1183 | iAttributedText attrText; | 1186 | iAttributedText attrText; |
1184 | init_AttributedText(&attrText, text, 0, d, (iColor){}); | 1187 | init_AttributedText(&attrText, text, 0, d, (iColor){}, 0); |
1185 | /* We use AttributedText here so the font lookup matches the behavior during text drawing -- | 1188 | /* We use AttributedText here so the font lookup matches the behavior during text drawing -- |
1186 | glyphs may be selected from a font that's different than `d`. */ | 1189 | glyphs may be selected from a font that's different than `d`. */ |
1187 | const iChar *logicalText = constData_Array(&attrText.logical); | 1190 | const iChar *logicalText = constData_Array(&attrText.logical); |
@@ -1229,9 +1232,10 @@ struct Impl_RunArgs { | |||
1229 | size_t maxLen; /* max characters to process */ | 1232 | size_t maxLen; /* max characters to process */ |
1230 | iInt2 pos; | 1233 | iInt2 pos; |
1231 | iWrapText * wrap; | 1234 | iWrapText * wrap; |
1232 | int xposLimit; /* hard limit for wrapping */ | 1235 | int xposLimit; /* hard limit for wrapping */ |
1233 | int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ | 1236 | int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ |
1234 | int color; | 1237 | int color; |
1238 | int baseDir; | ||
1235 | /* TODO: Cleanup using TextMetrics | 1239 | /* TODO: Cleanup using TextMetrics |
1236 | Use TextMetrics output pointer instead of return value & cursorAdvance_out. */ | 1240 | Use TextMetrics output pointer instead of return value & cursorAdvance_out. */ |
1237 | iInt2 * cursorAdvance_out; | 1241 | iInt2 * cursorAdvance_out; |
@@ -1239,13 +1243,13 @@ struct Impl_RunArgs { | |||
1239 | int * runAdvance_out; | 1243 | int * runAdvance_out; |
1240 | }; | 1244 | }; |
1241 | 1245 | ||
1242 | static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int advance) { | 1246 | static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int advance, iBool isBaseRTL) { |
1243 | if (d && d->wrapFunc && d->wrapRange_.start) { | 1247 | if (d && d->wrapFunc && d->wrapRange_.start) { |
1244 | /* `wrapRange_` uses logical indices. */ | 1248 | /* `wrapRange_` uses logical indices. */ |
1245 | const char *end = ending ? ending : d->wrapRange_.end; | 1249 | const char *end = ending ? ending : d->wrapRange_.end; |
1246 | iRangecc range = { d->wrapRange_.start, end }; | 1250 | iRangecc range = { d->wrapRange_.start, end }; |
1247 | iAssert(range.start <= range.end); | 1251 | iAssert(range.start <= range.end); |
1248 | const iBool result = d->wrapFunc(d, range, origin, advance); | 1252 | const iBool result = d->wrapFunc(d, range, origin, advance, isBaseRTL); |
1249 | if (result) { | 1253 | if (result) { |
1250 | d->wrapRange_.start = end; | 1254 | d->wrapRange_.start = end; |
1251 | } | 1255 | } |
@@ -1380,7 +1384,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1380 | font is used and other attributes such as color. (HarfBuzz shaping is done | 1384 | font is used and other attributes such as color. (HarfBuzz shaping is done |
1381 | with one specific font.) */ | 1385 | with one specific font.) */ |
1382 | iAttributedText attrText; | 1386 | iAttributedText attrText; |
1383 | init_AttributedText(&attrText, args->text, args->maxLen, d, get_Color(args->color)); | 1387 | init_AttributedText(&attrText, args->text, args->maxLen, d, get_Color(args->color), |
1388 | args->baseDir); | ||
1384 | if (args->wrap) { | 1389 | if (args->wrap) { |
1385 | /* TODO: Duplicated args? */ | 1390 | /* TODO: Duplicated args? */ |
1386 | iAssert(equalRange_Rangecc(args->wrap->text, args->text)); | 1391 | iAssert(equalRange_Rangecc(args->wrap->text, args->text)); |
@@ -1390,7 +1395,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1390 | const iChar *logicalText = constData_Array(&attrText.logical); | 1395 | const iChar *logicalText = constData_Array(&attrText.logical); |
1391 | const iChar *visualText = constData_Array(&attrText.visual); | 1396 | const iChar *visualText = constData_Array(&attrText.visual); |
1392 | const int * logToVis = constData_Array(&attrText.logicalToVisual); | 1397 | const int * logToVis = constData_Array(&attrText.logicalToVisual); |
1393 | const int * visToLog = constData_Array(&attrText.visualToLogical); | 1398 | // const int * visToLog = constData_Array(&attrText.visualToLogical); |
1394 | const size_t runCount = size_Array(&attrText.runs); | 1399 | const size_t runCount = size_Array(&attrText.runs); |
1395 | iArray buffers; | 1400 | iArray buffers; |
1396 | init_Array(&buffers, sizeof(iGlyphBuffer)); | 1401 | init_Array(&buffers, sizeof(iGlyphBuffer)); |
@@ -1580,7 +1585,8 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1580 | if (!notify_WrapText_(args->wrap, | 1585 | if (!notify_WrapText_(args->wrap, |
1581 | sourcePtr_AttributedText_(&attrText, wrapResumePos), | 1586 | sourcePtr_AttributedText_(&attrText, wrapResumePos), |
1582 | origin, | 1587 | origin, |
1583 | iRound(wrapAdvance))) { | 1588 | iRound(wrapAdvance), |
1589 | attrText.isBaseRTL)) { | ||
1584 | willAbortDueToWrap = iTrue; | 1590 | willAbortDueToWrap = iTrue; |
1585 | } | 1591 | } |
1586 | xCursor = origin; | 1592 | xCursor = origin; |
@@ -1743,8 +1749,9 @@ static int runFlagsFromId_(enum iFontId fontId) { | |||
1743 | return runFlags; | 1749 | return runFlags; |
1744 | } | 1750 | } |
1745 | 1751 | ||
1746 | static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int advance) { | 1752 | static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int advance, |
1747 | iUnused(origin, advance); | 1753 | iBool isBaseRTL) { |
1754 | iUnused(origin, advance, isBaseRTL); | ||
1748 | *((const char **) d->context) = range.end; | 1755 | *((const char **) d->context) = range.end; |
1749 | return iFalse; /* just one line */ | 1756 | return iFalse; /* just one line */ |
1750 | } | 1757 | } |
@@ -1785,9 +1792,9 @@ iTextMetrics measureN_Text(int fontId, const char *text, size_t n) { | |||
1785 | } | 1792 | } |
1786 | 1793 | ||
1787 | static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text, size_t maxLen) { | 1794 | static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text, size_t maxLen) { |
1788 | iText *d = &text_; | 1795 | iText * d = &text_; |
1789 | iFont *font = font_Text_(fontId); | 1796 | iFont * font = font_Text_(fontId); |
1790 | const iColor clr = get_Color(color & mask_ColorId); | 1797 | const iColor clr = get_Color(color & mask_ColorId); |
1791 | SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); | 1798 | SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b); |
1792 | run_Font_(font, | 1799 | run_Font_(font, |
1793 | &(iRunArgs){ .mode = draw_RunMode | | 1800 | &(iRunArgs){ .mode = draw_RunMode | |
@@ -1798,7 +1805,8 @@ static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, | |||
1798 | .maxLen = maxLen, | 1805 | .maxLen = maxLen, |
1799 | .pos = pos, | 1806 | .pos = pos, |
1800 | .xposLayoutBound = xposBound, | 1807 | .xposLayoutBound = xposBound, |
1801 | .color = color & mask_ColorId }); | 1808 | .color = color & mask_ColorId, |
1809 | .baseDir = xposBound ? iSign(xposBound - pos.x) : 0 }); | ||
1802 | } | 1810 | } |
1803 | 1811 | ||
1804 | static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { | 1812 | static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { |
diff --git a/src/ui/text.h b/src/ui/text.h index 341f94da..4630b9f6 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -205,7 +205,7 @@ struct Impl_WrapText { | |||
205 | iRangecc text; | 205 | iRangecc text; |
206 | int maxWidth; | 206 | int maxWidth; |
207 | enum iWrapTextMode mode; | 207 | enum iWrapTextMode mode; |
208 | iBool (*wrapFunc)(iWrapText *, iRangecc wrappedText, int origin, int advance); | 208 | iBool (*wrapFunc)(iWrapText *, iRangecc wrappedText, int origin, int advance, iBool isBaseRTL); |
209 | void * context; | 209 | void * context; |
210 | /* internal */ | 210 | /* internal */ |
211 | iRangecc wrapRange_; | 211 | iRangecc wrapRange_; |