summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-01 09:02:56 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-01 09:02:56 +0300
commita29e262a7a7aeacb2f6a1e34e1efacb440022529 (patch)
tree60be7cdfd457a10f1bc2ceb70c34533ff617be60 /src
parent5fec4c221b860ef33a45ced9919bc356d6dbb3d3 (diff)
Text: Working on line wrapping
Diffstat (limited to 'src')
-rw-r--r--src/ui/text.c60
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
781static void prepare_AttributedText_(iAttributedText *d) { 781static 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
1048static void notify_WrapText_(iWrapText *d, const char *ending, int advance) { 1050static 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
1299static iBool cbAdvanceOneLine_(iWrapText *d, iRangecc range, int advance) {
1300 *((const char **) d->context) = range.end;
1301 return iFalse; /* just one line */
1302}
1303
1279iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos) { 1304iInt2 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
1292iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { 1323iInt2 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
1306iInt2 advance_WrapText(iWrapText *d, int fontId) { 1344iInt2 advance_WrapText(iWrapText *d, int fontId) {
@@ -1314,7 +1352,7 @@ iInt2 advance_WrapText(iWrapText *d, int fontId) {
1314 1352
1315iRect measure_WrapText(iWrapText *d, int fontId) { 1353iRect 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 });