summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-13 21:02:33 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-13 21:02:33 +0300
commitd939dcd1109d7c92e6290976eaf84b94984abef3 (patch)
tree8e07462d687bec980095203e4d351b5e3a9774c0 /src/ui
parentf3a3b7785489f1d7c782e02d2e753d012729ab80 (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.c29
-rw-r--r--src/ui/text.c54
-rw-r--r--src/ui/text.h2
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) {
1585static void cacheRunGlyphs_(void *data, const iGmRun *run) { 1585static 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
794iDeclareType(AttributedText) 794iDeclareType(AttributedText)
795iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) 795iDeclareTypeConstructionArgs(AttributedText, iRangecc text, size_t maxLen, iFont *font,
796 iColor fgColor, int baseDir)
796 797
797struct Impl_AttributedText { 798struct 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
812iDefineTypeConstructionArgs(AttributedText, (iRangecc text, size_t maxLen, iFont *font, iColor fgColor), 813iDefineTypeConstructionArgs(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
815static const char *sourcePtr_AttributedText_(const iAttributedText *d, int logicalPos) { 817static 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
870static void prepare_AttributedText_(iAttributedText *d) { 872static 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
1020void init_AttributedText(iAttributedText *d, iRangecc text, size_t maxLen, iFont *font, iColor fgColor) { 1022void 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
1036void deinit_AttributedText(iAttributedText *d) { 1039void 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
1242static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int advance) { 1246static 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
1746static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int advance) { 1752static 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
1787static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text, size_t maxLen) { 1794static 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
1804static void drawBounded_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text) { 1812static 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_;