summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-08-02 13:01:33 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-08-02 13:01:33 +0300
commit320d3fe327ca768f35c53f255bb1ed4d57e0f148 (patch)
tree6dbcd66402736f3a66c7a7366cf5062c205491d4 /src/ui
parent565844ffdaef1e748ae011af55c17776bf34f20c (diff)
Text: Fixed drawing wrapped text without HarfBuzz
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/text.c26
-rw-r--r--src/ui/text_simple.c33
2 files changed, 52 insertions, 7 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index 866ae7fa..0a8386e4 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -1824,7 +1824,6 @@ static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int origin, int adv
1824} 1824}
1825 1825
1826iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { 1826iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) {
1827 /* FIXME: Get rid of this. Caller could use WrapText directly? */
1828 iWrapText wrap = { .mode = word_WrapTextMode, 1827 iWrapText wrap = { .mode = word_WrapTextMode,
1829 .text = text, 1828 .text = text,
1830 .maxWidth = width, 1829 .maxWidth = width,
@@ -1835,8 +1834,7 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos)
1835} 1834}
1836 1835
1837iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { 1836iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) {
1838 /* TODO: "NoWrap" means words aren't wrapped; the line is broken at nearest character. */ 1837 /* "NoWrap" means words aren't wrapped; the line is broken at nearest character. */
1839 /* FIXME: Get rid of this. Caller could use WrapText directly? */
1840 iWrapText wrap = { .mode = anyCharacter_WrapTextMode, 1838 iWrapText wrap = { .mode = anyCharacter_WrapTextMode,
1841 .text = text, 1839 .text = text,
1842 .maxWidth = width, 1840 .maxWidth = width,
@@ -2019,6 +2017,27 @@ iTextMetrics measure_WrapText(iWrapText *d, int fontId) {
2019 2017
2020iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { 2018iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) {
2021 iTextMetrics tm; 2019 iTextMetrics tm;
2020#if !defined (LAGRANGE_ENABLE_HARFBUZZ)
2021 /* In simple mode, each line must be wrapped first so we can break at the right points
2022 and do wrap notifications before drawing. */
2023 iRangecc text = d->text;
2024 iZap(tm);
2025 d->wrapRange_ = (iRangecc){ d->text.start, d->text.start };
2026 const iInt2 orig = pos;
2027 while (!isEmpty_Range(&text)) {
2028 const char *endPos;
2029 const int width = d->mode == word_WrapTextMode
2030 ? tryAdvance_Text(fontId, text, d->maxWidth, &endPos).x
2031 : tryAdvanceNoWrap_Text(fontId, text, d->maxWidth, &endPos).x;
2032 notify_WrapText_(d, endPos, 0, width, iFalse);
2033 drawRange_Text(fontId, pos, color, (iRangecc){ text.start, endPos });
2034 text.start = endPos;
2035 pos.y += lineHeight_Text(fontId);
2036 tm.bounds.size.x = iMax(tm.bounds.size.x, width);
2037 tm.bounds.size.y = pos.y - orig.y;
2038 }
2039 tm.advance = sub_I2(pos, orig);
2040#else
2022 tm.bounds = run_Font_(font_Text_(fontId), 2041 tm.bounds = run_Font_(font_Text_(fontId),
2023 &(iRunArgs){ .mode = draw_RunMode | runFlagsFromId_(fontId) | 2042 &(iRunArgs){ .mode = draw_RunMode | runFlagsFromId_(fontId) |
2024 (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) | 2043 (color & permanent_ColorId ? permanentColorFlag_RunMode : 0) |
@@ -2029,6 +2048,7 @@ iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) {
2029 .color = color & mask_ColorId, 2048 .color = color & mask_ColorId,
2030 .cursorAdvance_out = &tm.advance, 2049 .cursorAdvance_out = &tm.advance,
2031 }); 2050 });
2051#endif
2032 return tm; 2052 return tm;
2033} 2053}
2034 2054
diff --git a/src/ui/text_simple.c b/src/ui/text_simple.c
index a1b8e859..3afc631a 100644
--- a/src/ui/text_simple.c
+++ b/src/ui/text_simple.c
@@ -60,8 +60,8 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
60 /* This function shapes text using a simplified, incomplete algorithm. It works for English 60 /* This function shapes text using a simplified, incomplete algorithm. It works for English
61 and other non-complex LTR scripts. Composed glyphs are not supported (must rely on text 61 and other non-complex LTR scripts. Composed glyphs are not supported (must rely on text
62 being in a pre-composed form). This algorithm is used if HarfBuzz is not available. */ 62 being in a pre-composed form). This algorithm is used if HarfBuzz is not available. */
63 iRect bounds = zero_Rect();
64 const iInt2 orig = args->pos; 63 const iInt2 orig = args->pos;
64 iRect bounds = { orig, init_I2(0, d->height) };
65 float xpos = orig.x; 65 float xpos = orig.x;
66 float xposMax = xpos; 66 float xposMax = xpos;
67 float monoAdvance = 0; 67 float monoAdvance = 0;
@@ -76,13 +76,17 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
76 //iAssert(xposLimit == 0 || isMeasuring_(mode)); 76 //iAssert(xposLimit == 0 || isMeasuring_(mode));
77 iAssert(args->text.end >= args->text.start); 77 iAssert(args->text.end >= args->text.start);
78 if (wrap) { 78 if (wrap) {
79 wrap->wrapRange_ = args->text; 79 wrap->wrapRange_ = args->text;
80 wrap->hitAdvance_out = zero_I2();
81 wrap->hitChar_out = NULL;
82 wrap->hitGlyphNormX_out = 0.0f;
80 } 83 }
81// if (args->continueFrom_out) { 84// if (args->continueFrom_out) {
82// *args->continueFrom_out = args->text.end; 85// *args->continueFrom_out = args->text.end;
83// } 86// }
84 iChar prevCh = 0; 87 const iBool checkHitPoint = wrap && !isEqual_I2(wrap->hitPoint, zero_I2());
85 const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode); 88 const iBool checkHitChar = wrap && wrap->hitChar;
89 const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode);
86 if (isMonospaced) { 90 if (isMonospaced) {
87 monoAdvance = glyph_Font_(d, 'M')->advance; 91 monoAdvance = glyph_Font_(d, 'M')->advance;
88 } 92 }
@@ -91,10 +95,21 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
91 SDL_SetRenderDrawColor(text_.render, initial.r, initial.g, initial.b, 0); 95 SDL_SetRenderDrawColor(text_.render, initial.r, initial.g, initial.b, 0);
92 } 96 }
93 /* Text rendering is not very straightforward! Let's dive in... */ 97 /* Text rendering is not very straightforward! Let's dive in... */
98 iChar prevCh = 0;
94 const char *chPos; 99 const char *chPos;
95 for (chPos = args->text.start; chPos != args->text.end; ) { 100 for (chPos = args->text.start; chPos != args->text.end; ) {
96 iAssert(chPos < args->text.end); 101 iAssert(chPos < args->text.end);
97 const char *currentPos = chPos; 102 const char *currentPos = chPos;
103 const iBool isHitPointOnThisLine = (checkHitPoint && wrap->hitPoint.y >= ypos &&
104 wrap->hitPoint.y < ypos + d->height);
105 if (checkHitChar && currentPos == wrap->hitChar) {
106 wrap->hitAdvance_out = sub_I2(init_I2(xpos, ypos), orig);
107 }
108 /* Check if the hit point is on the left side of the line. */
109 if (isHitPointOnThisLine && !wrap->hitChar_out && wrap->hitPoint.x < orig.x) {
110 wrap->hitChar_out = currentPos;
111 wrap->hitGlyphNormX_out = 0.0f;
112 }
98 if (*chPos == 0x1b) { /* ANSI escape. */ 113 if (*chPos == 0x1b) { /* ANSI escape. */
99 chPos++; 114 chPos++;
100 iRegExpMatch m; 115 iRegExpMatch m;
@@ -213,6 +228,13 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
213 glyph = glyph_Font_(d, ch); /* cache may have been reset */ 228 glyph = glyph_Font_(d, ch); /* cache may have been reset */
214 } 229 }
215 int x2 = x1 + glyph->rect[hoff].size.x; 230 int x2 = x1 + glyph->rect[hoff].size.x;
231 if (isHitPointOnThisLine) {
232 if (wrap->hitPoint.x >= x1) { /* may also be off to the right */
233 wrap->hitChar_out = currentPos;
234 wrap->hitGlyphNormX_out =
235 wrap->hitPoint.x < x2 ? (wrap->hitPoint.x - x1) / glyph->advance : 1.0f;
236 }
237 }
216 /* Out of the allotted space on the line? */ 238 /* Out of the allotted space on the line? */
217 if (xposLimit > 0 && x2 > xposLimit) { 239 if (xposLimit > 0 && x2 > xposLimit) {
218 iAssert(wrap); 240 iAssert(wrap);
@@ -340,6 +362,9 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
340 } 362 }
341 } 363 }
342 notify_WrapText_(wrap, chPos, 0, xpos - orig.x, iFalse); 364 notify_WrapText_(wrap, chPos, 0, xpos - orig.x, iFalse);
365 if (checkHitChar && wrap->hitChar == args->text.end) {
366 wrap->hitAdvance_out = sub_I2(init_I2(xpos, ypos), orig);
367 }
343 if (args->cursorAdvance_out) { 368 if (args->cursorAdvance_out) {
344 *args->cursorAdvance_out = sub_I2(init_I2(xpos, ypos), orig); 369 *args->cursorAdvance_out = sub_I2(init_I2(xpos, ypos), orig);
345 } 370 }