diff options
-rw-r--r-- | src/ui/text.c | 26 | ||||
-rw-r--r-- | src/ui/text_simple.c | 33 |
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 | ||
1826 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { | 1826 | iInt2 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 | ||
1837 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { | 1836 | iInt2 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 | ||
2020 | iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { | 2018 | iTextMetrics 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 | } |