diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-01 09:02:56 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-07-01 09:02:56 +0300 |
commit | a29e262a7a7aeacb2f6a1e34e1efacb440022529 (patch) | |
tree | 60be7cdfd457a10f1bc2ceb70c34533ff617be60 /src | |
parent | 5fec4c221b860ef33a45ced9919bc356d6dbb3d3 (diff) |
Text: Working on line wrapping
Diffstat (limited to 'src')
-rw-r--r-- | src/ui/text.c | 60 |
1 files changed, 49 insertions, 11 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 54a6049f..f83f05a9 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -775,15 +775,16 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, | |||
775 | pushBack_Array(&d->runs, &finishedRun); | 775 | pushBack_Array(&d->runs, &finishedRun); |
776 | run->lineBreaks = 0; | 776 | run->lineBreaks = 0; |
777 | } | 777 | } |
778 | run->text.start = run->text.end = endAt; | 778 | run->text.start = endAt; |
779 | } | 779 | } |
780 | 780 | ||
781 | static void prepare_AttributedText_(iAttributedText *d) { | 781 | static void prepare_AttributedText_(iAttributedText *d) { |
782 | iAssert(isEmpty_Array(&d->runs)); | 782 | iAssert(isEmpty_Array(&d->runs)); |
783 | size_t avail = d->maxLen; | 783 | size_t avail = d->maxLen; |
784 | const char *chPos = d->text.start; | 784 | const char *chPos = d->text.start; |
785 | iAttributedRun run = { .text = (iRangecc){ d->text.start, d->text.start }, | 785 | iAttributedRun run = { .text = d->text, |
786 | .font = d->font, .fgColor = d->fgColor }; | 786 | .font = d->font, |
787 | .fgColor = d->fgColor }; | ||
787 | while (chPos < d->text.end) { | 788 | while (chPos < d->text.end) { |
788 | const char *currentPos = chPos; | 789 | const char *currentPos = chPos; |
789 | if (*chPos == 0x1b) { /* ANSI escape. */ | 790 | if (*chPos == 0x1b) { /* ANSI escape. */ |
@@ -829,6 +830,7 @@ static void prepare_AttributedText_(iAttributedText *d) { | |||
829 | } | 830 | } |
830 | if (avail-- == 0) { | 831 | if (avail-- == 0) { |
831 | /* TODO: Check the combining class; only count base characters here. */ | 832 | /* TODO: Check the combining class; only count base characters here. */ |
833 | run.text.end = currentPos; | ||
832 | break; | 834 | break; |
833 | } | 835 | } |
834 | const iGlyph *glyph = glyph_Font_(d->font, ch); | 836 | const iGlyph *glyph = glyph_Font_(d->font, ch); |
@@ -1045,12 +1047,19 @@ struct Impl_RunArgs { | |||
1045 | int * runAdvance_out; | 1047 | int * runAdvance_out; |
1046 | }; | 1048 | }; |
1047 | 1049 | ||
1048 | static void notify_WrapText_(iWrapText *d, const char *ending, int advance) { | 1050 | static iBool notify_WrapText_(iWrapText *d, const char *ending, int advance) { |
1049 | if (d && d->wrapFunc) { | 1051 | if (d && d->wrapFunc && d->wrapRange_.start) { |
1050 | const char *end = ending ? ending : d->wrapRange_.end; | 1052 | const char *end = ending ? ending : d->wrapRange_.end; |
1051 | d->wrapFunc(d, (iRangecc){ d->wrapRange_.start, end }, advance); | 1053 | const iBool result = d->wrapFunc(d, (iRangecc){ d->wrapRange_.start, end }, advance); |
1052 | d->wrapRange_.start = end; | 1054 | if (result) { |
1055 | d->wrapRange_.start = end; | ||
1056 | } | ||
1057 | else { | ||
1058 | d->wrapRange_ = iNullRange; | ||
1059 | } | ||
1060 | return result; | ||
1053 | } | 1061 | } |
1062 | return iTrue; | ||
1054 | } | 1063 | } |
1055 | 1064 | ||
1056 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) | 1065 | #if defined (LAGRANGE_ENABLE_HARFBUZZ) |
@@ -1079,6 +1088,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1079 | args->wrap->wrapRange_ = args->text; | 1088 | args->wrap->wrapRange_ = args->text; |
1080 | } | 1089 | } |
1081 | const char *wrapResumePos = NULL; | 1090 | const char *wrapResumePos = NULL; |
1091 | iBool willAbortDueToWrap = iFalse; | ||
1082 | for (size_t runIndex = 0; runIndex < size_Array(&attrText->runs); runIndex++) { | 1092 | for (size_t runIndex = 0; runIndex < size_Array(&attrText->runs); runIndex++) { |
1083 | const iAttributedRun *run = at_Array(&attrText->runs, runIndex); | 1093 | const iAttributedRun *run = at_Array(&attrText->runs, runIndex); |
1084 | iRangecc runText = run->text; | 1094 | iRangecc runText = run->text; |
@@ -1091,7 +1101,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1091 | else if (run->lineBreaks) { | 1101 | else if (run->lineBreaks) { |
1092 | for (int i = 0; i < run->lineBreaks; i++) { | 1102 | for (int i = 0; i < run->lineBreaks; i++) { |
1093 | /* One callback for each "wrapped" line even if they're empty. */ | 1103 | /* One callback for each "wrapped" line even if they're empty. */ |
1094 | notify_WrapText_(args->wrap, run->text.start, iRound(xCursor)); | 1104 | if (!notify_WrapText_(args->wrap, run->text.start, iRound(xCursor))) { |
1105 | break; | ||
1106 | } | ||
1095 | xCursor = 0.0f; | 1107 | xCursor = 0.0f; |
1096 | yCursor += d->height; | 1108 | yCursor += d->height; |
1097 | } | 1109 | } |
@@ -1146,14 +1158,16 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1146 | } | 1158 | } |
1147 | /* Make a callback for each wrapped line. */ | 1159 | /* Make a callback for each wrapped line. */ |
1148 | if (breakPos) { | 1160 | if (breakPos) { |
1149 | notify_WrapText_(args->wrap, breakPos, iRound(x)); | 1161 | if (!notify_WrapText_(args->wrap, breakPos, iRound(x))) { |
1162 | willAbortDueToWrap = iTrue; | ||
1163 | } | ||
1150 | } | 1164 | } |
1151 | } | 1165 | } |
1152 | /* Draw each glyph. */ | 1166 | /* Draw each glyph. */ |
1153 | for (unsigned int i = 0; i < glyphCount; i++) { | 1167 | for (unsigned int i = 0; i < glyphCount; i++) { |
1154 | const hb_glyph_info_t *info = &glyphInfo[i]; | 1168 | const hb_glyph_info_t *info = &glyphInfo[i]; |
1155 | const hb_codepoint_t glyphId = info->codepoint; | 1169 | const hb_codepoint_t glyphId = info->codepoint; |
1156 | const char *textPos = runText.start + info->cluster; | 1170 | const char *textPos = runText.start + info->cluster; |
1157 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); | 1171 | const int glyphFlags = hb_glyph_info_get_glyph_flags(info); |
1158 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; | 1172 | const float xOffset = run->font->xScale * glyphPos[i].x_offset; |
1159 | const float yOffset = run->font->yScale * glyphPos[i].y_offset; | 1173 | const float yOffset = run->font->yScale * glyphPos[i].y_offset; |
@@ -1212,6 +1226,12 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { | |||
1212 | yCursor += yAdvance; | 1226 | yCursor += yAdvance; |
1213 | xCursorMax = iMax(xCursorMax, xCursor); | 1227 | xCursorMax = iMax(xCursorMax, xCursor); |
1214 | } | 1228 | } |
1229 | if (willAbortDueToWrap) { | ||
1230 | break; | ||
1231 | } | ||
1232 | } | ||
1233 | if (args->wrap) { | ||
1234 | args->wrap->cursor_out = init_I2(xCursor, yCursor); | ||
1215 | } | 1235 | } |
1216 | /* The final, unbroken line. */ | 1236 | /* The final, unbroken line. */ |
1217 | notify_WrapText_(args->wrap, NULL, xCursor); | 1237 | notify_WrapText_(args->wrap, NULL, xCursor); |
@@ -1276,7 +1296,17 @@ iInt2 advanceRange_Text(int fontId, iRangecc text) { | |||
1276 | return init_I2(advance, height); | 1296 | return init_I2(advance, height); |
1277 | } | 1297 | } |
1278 | 1298 | ||
1299 | static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int advance) { | ||
1300 | *((const char **) d->context) = range.end; | ||
1301 | return iFalse; /* just one line */ | ||
1302 | } | ||
1303 | |||
1279 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { | 1304 | iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { |
1305 | iWrapText wrap = { .text = text, .maxWidth = width, | ||
1306 | .wrapFunc = cbAdvanceOneLine_, .context = endPos }; | ||
1307 | const int x = advance_WrapText(&wrap, fontId).x; | ||
1308 | return init_I2(x, lineHeight_Text(fontId)); | ||
1309 | #if 0 | ||
1280 | int advance; | 1310 | int advance; |
1281 | const int height = run_Font_(font_Text_(fontId), | 1311 | const int height = run_Font_(font_Text_(fontId), |
1282 | &(iRunArgs){ .mode = measure_RunMode | stopAtNewline_RunMode | | 1312 | &(iRunArgs){ .mode = measure_RunMode | stopAtNewline_RunMode | |
@@ -1287,9 +1317,16 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) | |||
1287 | .runAdvance_out = &advance }) | 1317 | .runAdvance_out = &advance }) |
1288 | .size.y; | 1318 | .size.y; |
1289 | return init_I2(advance, height); | 1319 | return init_I2(advance, height); |
1320 | #endif | ||
1290 | } | 1321 | } |
1291 | 1322 | ||
1292 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { | 1323 | iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { |
1324 | /* TODO: "NoWrap" means words aren't wrapped; the line is broken at nearest character. */ | ||
1325 | iWrapText wrap = { .text = text, .maxWidth = width, | ||
1326 | .wrapFunc = cbAdvanceOneLine_, .context = endPos }; | ||
1327 | const int x = advance_WrapText(&wrap, fontId).x; | ||
1328 | return init_I2(x, lineHeight_Text(fontId)); | ||
1329 | #if 0 | ||
1293 | int advance; | 1330 | int advance; |
1294 | const int height = run_Font_(font_Text_(fontId), | 1331 | const int height = run_Font_(font_Text_(fontId), |
1295 | &(iRunArgs){ .mode = measure_RunMode | noWrapFlag_RunMode | | 1332 | &(iRunArgs){ .mode = measure_RunMode | noWrapFlag_RunMode | |
@@ -1301,6 +1338,7 @@ iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **e | |||
1301 | .runAdvance_out = &advance }) | 1338 | .runAdvance_out = &advance }) |
1302 | .size.y; | 1339 | .size.y; |
1303 | return init_I2(advance, height); | 1340 | return init_I2(advance, height); |
1341 | #endif | ||
1304 | } | 1342 | } |
1305 | 1343 | ||
1306 | iInt2 advance_WrapText(iWrapText *d, int fontId) { | 1344 | iInt2 advance_WrapText(iWrapText *d, int fontId) { |
@@ -1314,7 +1352,7 @@ iInt2 advance_WrapText(iWrapText *d, int fontId) { | |||
1314 | 1352 | ||
1315 | iRect measure_WrapText(iWrapText *d, int fontId) { | 1353 | iRect measure_WrapText(iWrapText *d, int fontId) { |
1316 | return run_Font_(font_Text_(fontId), | 1354 | return run_Font_(font_Text_(fontId), |
1317 | &(iRunArgs){ .mode = measure_RunMode | runFlagsFromId_(fontId), | 1355 | &(iRunArgs){ .mode = measure_RunMode | visualFlag_RunMode | runFlagsFromId_(fontId), |
1318 | .text = d->text, | 1356 | .text = d->text, |
1319 | .wrap = d | 1357 | .wrap = d |
1320 | }); | 1358 | }); |