summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ui/text.c35
-rw-r--r--src/ui/text_simple.c72
2 files changed, 67 insertions, 40 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index 3683aee4..4b82191f 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -895,6 +895,14 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir) {
895 NULL, 895 NULL,
896 (FriBidiLevel *) d->bidiLevels); 896 (FriBidiLevel *) d->bidiLevels);
897 d->isBaseRTL = (overrideBaseDir == 0 ? FRIBIDI_IS_RTL(baseDir) : (overrideBaseDir < 0)); 897 d->isBaseRTL = (overrideBaseDir == 0 ? FRIBIDI_IS_RTL(baseDir) : (overrideBaseDir < 0));
898#else
899 /* 1:1 mapping. */
900 setCopy_Array(&d->visual, &d->logical);
901 resize_Array(&d->logicalToVisual, length);
902 for (size_t i = 0; i < length; i++) {
903 set_Array(&d->logicalToVisual, i, &(int){ i });
904 }
905 d->isBaseRTL = iFalse;
898#endif 906#endif
899 } 907 }
900 /* The mapping needs to include the terminating NULL position. */ { 908 /* The mapping needs to include the terminating NULL position. */ {
@@ -1208,13 +1216,13 @@ enum iRunMode {
1208 draw_RunMode = 1, 1216 draw_RunMode = 1,
1209 modeMask_RunMode = 0x00ff, 1217 modeMask_RunMode = 0x00ff,
1210 flagsMask_RunMode = 0xff00, 1218 flagsMask_RunMode = 0xff00,
1211 noWrapFlag_RunMode = iBit(9), 1219// noWrapFlag_RunMode = iBit(9),
1212 visualFlag_RunMode = iBit(10), /* actual visible bounding box of the glyph, 1220 visualFlag_RunMode = iBit(10), /* actual visible bounding box of the glyph,
1213 e.g., for icons */ 1221 e.g., for icons */
1214 permanentColorFlag_RunMode = iBit(11), 1222 permanentColorFlag_RunMode = iBit(11),
1215 alwaysVariableWidthFlag_RunMode = iBit(12), 1223 alwaysVariableWidthFlag_RunMode = iBit(12),
1216 fillBackground_RunMode = iBit(13), 1224 fillBackground_RunMode = iBit(13),
1217 stopAtNewline_RunMode = iBit(14), /* don't advance past \n, consider it a wrap pos */ 1225// stopAtNewline_RunMode = iBit(14), /* don't advance past \n, consider it a wrap pos */
1218}; 1226};
1219 1227
1220iDeclareType(RunArgs) 1228iDeclareType(RunArgs)
@@ -1225,14 +1233,14 @@ struct Impl_RunArgs {
1225 size_t maxLen; /* max characters to process */ 1233 size_t maxLen; /* max characters to process */
1226 iInt2 pos; 1234 iInt2 pos;
1227 iWrapText * wrap; 1235 iWrapText * wrap;
1228 int xposLimit; /* hard limit for wrapping */ 1236// int xposLimit; /* hard limit for wrapping */
1229 int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */ 1237// int xposLayoutBound; /* visible bound for layout purposes; does not affect wrapping */
1230 int color; 1238 int color;
1231 int baseDir; 1239 int baseDir;
1232 /* TODO: Cleanup using TextMetrics 1240 /* TODO: Cleanup using TextMetrics
1233 Use TextMetrics output pointer instead of return value & cursorAdvance_out. */ 1241 Use TextMetrics output pointer instead of return value & cursorAdvance_out. */
1234 iInt2 * cursorAdvance_out; 1242 iInt2 * cursorAdvance_out;
1235 const char ** continueFrom_out; 1243// const char ** continueFrom_out;
1236 int * runAdvance_out; 1244 int * runAdvance_out;
1237}; 1245};
1238 1246
@@ -1368,9 +1376,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1368 float xCursorMax = 0.0f; 1376 float xCursorMax = 0.0f;
1369 const iBool isMonospaced = d->isMonospaced; 1377 const iBool isMonospaced = d->isMonospaced;
1370 iAssert(args->text.end >= args->text.start); 1378 iAssert(args->text.end >= args->text.start);
1371 if (args->continueFrom_out) { 1379// if (args->continueFrom_out) {
1372 *args->continueFrom_out = args->text.end; 1380// *args->continueFrom_out = args->text.end;
1373 } 1381// }
1374 /* Split the text into a number of attributed runs that specify exactly which 1382 /* Split the text into a number of attributed runs that specify exactly which
1375 font is used and other attributes such as color. (HarfBuzz shaping is done 1383 font is used and other attributes such as color. (HarfBuzz shaping is done
1376 with one specific font.) */ 1384 with one specific font.) */
@@ -1575,10 +1583,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1575 if (layoutBound > 0) { 1583 if (layoutBound > 0) {
1576 origin = layoutBound - wrapAdvance; 1584 origin = layoutBound - wrapAdvance;
1577 } 1585 }
1578 else if (args->xposLayoutBound > 0) { 1586// else if (args->xposLayoutBound > 0) {
1579 iAssert(mode & draw_RunMode); 1587// iAssert(mode & draw_RunMode);
1580// origin = args->xposLayoutBound - orig.x - wrapAdvance * 2; 1588//// origin = args->xposLayoutBound - orig.x - wrapAdvance * 2;
1581 } 1589// }
1582 } 1590 }
1583 /* Make a callback for each wrapped line. */ 1591 /* Make a callback for each wrapped line. */
1584 if (!notify_WrapText_(args->wrap, 1592 if (!notify_WrapText_(args->wrap,
@@ -1768,6 +1776,7 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos)
1768 1776
1769iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) { 1777iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **endPos) {
1770 /* TODO: "NoWrap" means words aren't wrapped; the line is broken at nearest character. */ 1778 /* TODO: "NoWrap" means words aren't wrapped; the line is broken at nearest character. */
1779 /* FIXME: Get rid of this. Caller could use WrapText directly? */
1771 iWrapText wrap = { .mode = anyCharacter_WrapTextMode, 1780 iWrapText wrap = { .mode = anyCharacter_WrapTextMode,
1772 .text = text, 1781 .text = text,
1773 .maxWidth = width, 1782 .maxWidth = width,
@@ -1803,7 +1812,7 @@ static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color,
1803 .text = text, 1812 .text = text,
1804 .maxLen = maxLen, 1813 .maxLen = maxLen,
1805 .pos = pos, 1814 .pos = pos,
1806 .xposLayoutBound = xposBound, 1815// .xposLayoutBound = xposBound,
1807 .color = color & mask_ColorId, 1816 .color = color & mask_ColorId,
1808 .baseDir = xposBound ? iSign(xposBound - pos.x) : 0 }); 1817 .baseDir = xposBound ? iSign(xposBound - pos.x) : 0 });
1809} 1818}
diff --git a/src/ui/text_simple.c b/src/ui/text_simple.c
index 575d00cb..b843f2e8 100644
--- a/src/ui/text_simple.c
+++ b/src/ui/text_simple.c
@@ -68,13 +68,19 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
68 int ypos = orig.y; 68 int ypos = orig.y;
69 size_t maxLen = args->maxLen ? args->maxLen : iInvalidSize; 69 size_t maxLen = args->maxLen ? args->maxLen : iInvalidSize;
70 float xposExtend = orig.x; /* allows wide glyphs to use more space; restored by whitespace */ 70 float xposExtend = orig.x; /* allows wide glyphs to use more space; restored by whitespace */
71 iWrapText * wrap = args->wrap;
72 int wrapAdvance = 0;
73 const int xposLimit = (wrap && wrap->maxWidth ? orig.x + wrap->maxWidth : 0);
71 const enum iRunMode mode = args->mode; 74 const enum iRunMode mode = args->mode;
72 const char * lastWordEnd = args->text.start; 75 const char * lastWordEnd = args->text.start;
73 iAssert(args->xposLimit == 0 || isMeasuring_(mode)); 76 iAssert(xposLimit == 0 || isMeasuring_(mode));
74 iAssert(args->text.end >= args->text.start); 77 iAssert(args->text.end >= args->text.start);
75 if (args->continueFrom_out) { 78 if (wrap) {
76 *args->continueFrom_out = args->text.end; 79 wrap->wrapRange_ = args->text;
77 } 80 }
81// if (args->continueFrom_out) {
82// *args->continueFrom_out = args->text.end;
83// }
78 iChar prevCh = 0; 84 iChar prevCh = 0;
79 const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode); 85 const iBool isMonospaced = d->isMonospaced && !(mode & alwaysVariableWidthFlag_RunMode);
80 if (isMonospaced) { 86 if (isMonospaced) {
@@ -85,7 +91,8 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
85 SDL_SetRenderDrawColor(text_.render, initial.r, initial.g, initial.b, 0); 91 SDL_SetRenderDrawColor(text_.render, initial.r, initial.g, initial.b, 0);
86 } 92 }
87 /* Text rendering is not very straightforward! Let's dive in... */ 93 /* Text rendering is not very straightforward! Let's dive in... */
88 for (const char *chPos = args->text.start; chPos != args->text.end; ) { 94 const char *chPos;
95 for (chPos = args->text.start; chPos != args->text.end; ) {
89 iAssert(chPos < args->text.end); 96 iAssert(chPos < args->text.end);
90 const char *currentPos = chPos; 97 const char *currentPos = chPos;
91 if (*chPos == 0x1b) { /* ANSI escape. */ 98 if (*chPos == 0x1b) { /* ANSI escape. */
@@ -123,11 +130,11 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
123 if (ch == 0xad) { /* soft hyphen */ 130 if (ch == 0xad) { /* soft hyphen */
124 lastWordEnd = chPos; 131 lastWordEnd = chPos;
125 if (isMeasuring_(mode)) { 132 if (isMeasuring_(mode)) {
126 if (args->xposLimit > 0) { 133 if (xposLimit > 0) {
127 const char *postHyphen = chPos; 134 const char *postHyphen = chPos;
128 iChar nextCh = nextChar_(&postHyphen, args->text.end); 135 iChar nextCh = nextChar_(&postHyphen, args->text.end);
129 if ((int) xpos + glyph_Font_(d, ch)->rect[0].size.x + 136 if ((int) xpos + glyph_Font_(d, ch)->rect[0].size.x +
130 glyph_Font_(d, nextCh)->rect[0].size.x > args->xposLimit) { 137 glyph_Font_(d, nextCh)->rect[0].size.x > xposLimit) {
131 /* Wraps after hyphen, should show it. */ 138 /* Wraps after hyphen, should show it. */
132 } 139 }
133 else continue; 140 else continue;
@@ -143,11 +150,8 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
143 } 150 }
144 /* TODO: Check out if `uc_wordbreak_property()` from libunistring can be used here. */ 151 /* TODO: Check out if `uc_wordbreak_property()` from libunistring can be used here. */
145 if (ch == '\n') { 152 if (ch == '\n') {
146 if (args->xposLimit > 0 && mode & stopAtNewline_RunMode) { 153 /* Notify about the wrap. */
147 /* Stop the line here, this is a hard warp. */ 154 if (!notify_WrapText_(wrap, chPos, 0, iMax(xpos, xposExtend) - orig.x, iFalse)) {
148 if (args->continueFrom_out) {
149 *args->continueFrom_out = chPos;
150 }
151 break; 155 break;
152 } 156 }
153 xpos = xposExtend = orig.x; 157 xpos = xposExtend = orig.x;
@@ -157,7 +161,7 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
157 } 161 }
158 if (ch == '\t') { 162 if (ch == '\t') {
159 const int tabStopWidth = d->height * 10; 163 const int tabStopWidth = d->height * 10;
160 const int halfWidth = (iMax(args->xposLimit, args->xposLayoutBound) - orig.x) / 2; 164 const int halfWidth = (xposLimit - orig.x) / 2;
161 const int xRel = xpos - orig.x; 165 const int xRel = xpos - orig.x;
162 /* First stop is always to half width. */ 166 /* First stop is always to half width. */
163 if (halfWidth > 0 && xRel < halfWidth) { 167 if (halfWidth > 0 && xRel < halfWidth) {
@@ -204,23 +208,32 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
204 if (mode & draw_RunMode && ch != 0x20 && ch != 0 && !isRasterized_Glyph_(glyph, hoff)) { 208 if (mode & draw_RunMode && ch != 0x20 && ch != 0 && !isRasterized_Glyph_(glyph, hoff)) {
205 /* Need to pause here and make sure all glyphs have been cached in the text. */ 209 /* Need to pause here and make sure all glyphs have been cached in the text. */
206// printf("[Text] missing from cache: %lc (%x)\n", (int) ch, ch); 210// printf("[Text] missing from cache: %lc (%x)\n", (int) ch, ch);
207 cacheTextGlyphs_Font_(d, args->text); 211 //cacheTextGlyphs_Font_(d, args->text);
212 cacheSingleGlyph_Font_(glyph->font, index_Glyph_(glyph));
208 glyph = glyph_Font_(d, ch); /* cache may have been reset */ 213 glyph = glyph_Font_(d, ch); /* cache may have been reset */
209 } 214 }
210 int x2 = x1 + glyph->rect[hoff].size.x; 215 int x2 = x1 + glyph->rect[hoff].size.x;
211 /* Out of the allotted space on the line? */ 216 /* Out of the allotted space on the line? */
212 if (args->xposLimit > 0 && x2 > args->xposLimit) { 217 if (xposLimit > 0 && x2 > xposLimit) {
213 if (args->continueFrom_out) { 218 iAssert(wrap);
214 if (lastWordEnd != args->text.start && ~mode & noWrapFlag_RunMode) { 219 const char *wrapPos = currentPos;
215 *args->continueFrom_out = skipSpace_CStr(lastWordEnd); 220 int advance = x1 - orig.x;
216 *args->continueFrom_out = iMin(*args->continueFrom_out, 221 if (lastWordEnd != args->text.start && wrap->mode == word_WrapTextMode) {
217 args->text.end); 222 wrapPos = skipSpace_CStr(lastWordEnd);
218 } 223 wrapPos = iMin(wrapPos, args->text.end);
219 else { 224 advance = wrapAdvance;
220 *args->continueFrom_out = currentPos; /* forced break */
221 }
222 } 225 }
223 break; 226// if (args->continueFrom_out) {
227// *args->continueFrom_out = wrapPos;
228// }
229 if (!notify_WrapText_(wrap, wrapPos, 0, advance, iFalse)) {
230 break;
231 }
232 xpos = xposExtend = orig.x;
233 ypos += d->height;
234 prevCh = 0;
235 chPos = wrapPos; /* go back */
236 continue;
224 } 237 }
225 const int yLineMax = ypos + d->height; 238 const int yLineMax = ypos + d->height;
226 SDL_Rect dst = { x1 + glyph->d[hoff].x, 239 SDL_Rect dst = { x1 + glyph->d[hoff].x,
@@ -289,10 +302,10 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
289 /* TODO: No need to decode the next char twice; check this on the next iteration. */ 302 /* TODO: No need to decode the next char twice; check this on the next iteration. */
290 const char *peek = chPos; 303 const char *peek = chPos;
291 const iChar next = nextChar_(&peek, args->text.end); 304 const iChar next = nextChar_(&peek, args->text.end);
292 if (enableKerning_Text && !d->manualKernOnly && next) { 305 if (enableKerning_Text && next) {
293 const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next); 306 const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next);
294 int kern = stbtt_GetGlyphKernAdvance( 307 int kern = stbtt_GetGlyphKernAdvance(
295 &glyph->font->font, glyph->glyphIndex, nextGlyphIndex); 308 &glyph->font->font, index_Glyph_(glyph), nextGlyphIndex);
296 /* Nunito needs some kerning fixes. */ 309 /* Nunito needs some kerning fixes. */
297 if (glyph->font->family == nunito_TextFont) { 310 if (glyph->font->family == nunito_TextFont) {
298 if (ch == 'W' && (next == 'i' || next == 'h')) { 311 if (ch == 'W' && (next == 'i' || next == 'h')) {
@@ -317,14 +330,19 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {
317#endif 330#endif
318 xposExtend = iMax(xposExtend, xpos); 331 xposExtend = iMax(xposExtend, xpos);
319 xposMax = iMax(xposMax, xposExtend); 332 xposMax = iMax(xposMax, xposExtend);
320 if (args->continueFrom_out && ((mode & noWrapFlag_RunMode) || isWrapBoundary_(prevCh, ch))) { 333 if ((wrap && wrap->mode == anyCharacter_WrapTextMode) || isWrapBoundary_(prevCh, ch)) {
321 lastWordEnd = currentPos; /* mark word wrap position */ 334 lastWordEnd = currentPos; /* mark word wrap position */
335 wrapAdvance = x2 - orig.x;
322 } 336 }
323 prevCh = ch; 337 prevCh = ch;
324 if (--maxLen == 0) { 338 if (--maxLen == 0) {
325 break; 339 break;
326 } 340 }
327 } 341 }
342 notify_WrapText_(wrap, chPos, 0, xpos - orig.x, iFalse);
343 if (args->cursorAdvance_out) {
344 *args->cursorAdvance_out = sub_I2(init_I2(xpos, ypos), orig);
345 }
328 if (args->runAdvance_out) { 346 if (args->runAdvance_out) {
329 *args->runAdvance_out = xposMax - orig.x; 347 *args->runAdvance_out = xposMax - orig.x;
330 } 348 }