summaryrefslogtreecommitdiff
path: root/src/ui/text.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/text.c')
-rw-r--r--src/ui/text.c80
1 files changed, 54 insertions, 26 deletions
diff --git a/src/ui/text.c b/src/ui/text.c
index 1b483952..50b3bbab 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -23,6 +23,8 @@ iDeclareType(Font)
23iDeclareType(Glyph) 23iDeclareType(Glyph)
24iDeclareTypeConstructionArgs(Glyph, iChar ch) 24iDeclareTypeConstructionArgs(Glyph, iChar ch)
25 25
26int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */
27
26struct Impl_Glyph { 28struct Impl_Glyph {
27 iHashNode node; 29 iHashNode node;
28 const iFont *font; /* may come from symbols/emoji */ 30 const iFont *font; /* may come from symbols/emoji */
@@ -189,7 +191,7 @@ void deinit_Text(void) {
189 iRelease(d->ansiEscape); 191 iRelease(d->ansiEscape);
190} 192}
191 193
192iFont *font_Text_(enum iFontId id) { 194iLocalDef iFont *font_Text_(enum iFontId id) {
193 return &text_.fonts[id]; 195 return &text_.fonts[id];
194} 196}
195 197
@@ -306,7 +308,13 @@ static const iGlyph *glyph_Font_(iFont *d, iChar ch) {
306 return glyph; 308 return glyph;
307} 309}
308 310
309enum iRunMode { measure_RunMode, measureNoWrap_RunMode, draw_RunMode, drawPermanentColor_RunMode }; 311enum iRunMode {
312 measure_RunMode,
313 measureNoWrap_RunMode,
314 measureVisual_RunMode, /* actual visible bounding box of the glyph, e.g., for icons */
315 draw_RunMode,
316 drawPermanentColor_RunMode
317};
310 318
311static iChar nextChar_(const char **chPos, const char *end) { 319static iChar nextChar_(const char **chPos, const char *end) {
312 if (*chPos == end) { 320 if (*chPos == end) {
@@ -322,8 +330,6 @@ static iChar nextChar_(const char **chPos, const char *end) {
322 return ch; 330 return ch;
323} 331}
324 332
325int enableHalfPixelGlyphs_Text = iTrue;
326
327iLocalDef iBool isWrapBoundary_(iChar a, iChar b) { 333iLocalDef iBool isWrapBoundary_(iChar a, iChar b) {
328 if (b == '/' || b == '-' || b == ',' || b == ';' || b == ':') { 334 if (b == '/' || b == '-' || b == ',' || b == ';' || b == ':') {
329 return iTrue; 335 return iTrue;
@@ -331,13 +337,18 @@ iLocalDef iBool isWrapBoundary_(iChar a, iChar b) {
331 return !isSpace_Char(a) && isSpace_Char(b); 337 return !isSpace_Char(a) && isSpace_Char(b);
332} 338}
333 339
334static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, 340iLocalDef iBool isMeasuring_(enum iRunMode mode) {
341 return mode == measure_RunMode || mode == measureNoWrap_RunMode ||
342 mode == measureVisual_RunMode;
343}
344
345static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos,
335 int xposLimit, const char **continueFrom_out, int *runAdvance_out) { 346 int xposLimit, const char **continueFrom_out, int *runAdvance_out) {
336 iInt2 size = zero_I2(); 347 iRect bounds = zero_Rect();
337 const iInt2 orig = pos; 348 const iInt2 orig = pos;
338 float xpos = pos.x; 349 float xpos = pos.x;
339 float xposMax = xpos; 350 float xposMax = xpos;
340 iAssert(xposLimit == 0 || mode == measure_RunMode || mode == measureNoWrap_RunMode); 351 iAssert(xposLimit == 0 || isMeasuring_(mode));
341 const char *lastWordEnd = text.start; 352 const char *lastWordEnd = text.start;
342 if (continueFrom_out) { 353 if (continueFrom_out) {
343 *continueFrom_out = text.end; 354 *continueFrom_out = text.end;
@@ -386,13 +397,24 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
386 *continueFrom_out = lastWordEnd; 397 *continueFrom_out = lastWordEnd;
387 break; 398 break;
388 } 399 }
389 size.x = iMax(size.x, x2 - orig.x); 400 const SDL_Rect dst = { x1 + glyph->d[hoff].x,
390 size.y = iMax(size.y, pos.y + glyph->font->height - orig.y); 401 pos.y + glyph->font->baseline + glyph->d[hoff].y,
391 if (mode != measure_RunMode && mode != measureNoWrap_RunMode) { 402 glyph->rect[hoff].size.x,
392 SDL_Rect dst = { x1 + glyph->d[hoff].x, 403 glyph->rect[hoff].size.y };
393 pos.y + glyph->font->baseline + glyph->d[hoff].y, 404 /* Update the bounding box. */
394 glyph->rect[hoff].size.x, 405 if (mode == measureVisual_RunMode) {
395 glyph->rect[hoff].size.y }; 406 if (isEmpty_Rect(bounds)) {
407 bounds = init_Rect(dst.x, dst.y, dst.w, dst.h);
408 }
409 else {
410 bounds = union_Rect(bounds, init_Rect(dst.x, dst.y, dst.w, dst.h));
411 }
412 }
413 else {
414 bounds.size.x = iMax(bounds.size.x, x2 - orig.x);
415 bounds.size.y = iMax(bounds.size.y, pos.y + glyph->font->height - orig.y);
416 }
417 if (!isMeasuring_(mode)) {
396 SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst); 418 SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect[hoff], &dst);
397 } 419 }
398 xpos += glyph->advance; 420 xpos += glyph->advance;
@@ -421,7 +443,7 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe
421 if (runAdvance_out) { 443 if (runAdvance_out) {
422 *runAdvance_out = xposMax - orig.x; 444 *runAdvance_out = xposMax - orig.x;
423 } 445 }
424 return size; 446 return bounds;
425} 447}
426 448
427int lineHeight_Text(int fontId) { 449int lineHeight_Text(int fontId) {
@@ -439,7 +461,12 @@ iInt2 measureRange_Text(int fontId, iRangecc text) {
439 zero_I2(), 461 zero_I2(),
440 0, 462 0,
441 NULL, 463 NULL,
442 NULL); 464 NULL).size;
465}
466
467iRect visualBounds_Text(int fontId, iRangecc text) {
468 return run_Font_(
469 font_Text_(fontId), measureVisual_RunMode, text, iInvalidSize, zero_I2(), 0, NULL, NULL);
443} 470}
444 471
445iInt2 measure_Text(int fontId, const char *text) { 472iInt2 measure_Text(int fontId, const char *text) {
@@ -456,7 +483,7 @@ iInt2 advanceRange_Text(int fontId, iRangecc text) {
456 0, 483 0,
457 NULL, 484 NULL,
458 &advance) 485 &advance)
459 .y; 486 .size.y;
460 return init_I2(advance, height); 487 return init_I2(advance, height);
461} 488}
462 489
@@ -470,7 +497,7 @@ iInt2 tryAdvance_Text(int fontId, iRangecc text, int width, const char **endPos)
470 width, 497 width,
471 endPos, 498 endPos,
472 &advance) 499 &advance)
473 .y; 500 .size.y;
474 return init_I2(advance, height); 501 return init_I2(advance, height);
475} 502}
476 503
@@ -484,7 +511,7 @@ iInt2 tryAdvanceNoWrap_Text(int fontId, iRangecc text, int width, const char **e
484 width, 511 width,
485 endPos, 512 endPos,
486 &advance) 513 &advance)
487 .y; 514 .size.y;
488 return init_I2(advance, height); 515 return init_I2(advance, height);
489} 516}
490 517
@@ -530,7 +557,7 @@ void drawAlign_Text(int fontId, iInt2 pos, int color, enum iAlignment align, con
530 else if (align == right_Alignment) { 557 else if (align == right_Alignment) {
531 pos.x -= measure_Text(fontId, cstr_Block(&chars)).x; 558 pos.x -= measure_Text(fontId, cstr_Block(&chars)).x;
532 } 559 }
533 draw_Text_(fontId, pos, color, (iRangecc){ constBegin_Block(&chars), constEnd_Block(&chars) }); 560 draw_Text_(fontId, pos, color, range_Block(&chars));
534 deinit_Block(&chars); 561 deinit_Block(&chars);
535} 562}
536 563
@@ -542,7 +569,7 @@ void draw_Text(int fontId, iInt2 pos, int color, const char *text, ...) {
542 vprintf_Block(&chars, text, args); 569 vprintf_Block(&chars, text, args);
543 va_end(args); 570 va_end(args);
544 } 571 }
545 draw_Text_(fontId, pos, color, (iRangecc){ constBegin_Block(&chars), constEnd_Block(&chars) }); 572 draw_Text_(fontId, pos, color, range_Block(&chars));
546 deinit_Block(&chars); 573 deinit_Block(&chars);
547} 574}
548 575
@@ -550,7 +577,7 @@ void drawString_Text(int fontId, iInt2 pos, int color, const iString *text) {
550 draw_Text_(fontId, pos, color, range_String(text)); 577 draw_Text_(fontId, pos, color, range_String(text));
551} 578}
552 579
553void drawCentered_Text(int fontId, iRect rect, int color, const char *format, ...) { 580void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, const char *format, ...) {
554 iBlock chars; 581 iBlock chars;
555 init_Block(&chars, 0); { 582 init_Block(&chars, 0); {
556 va_list args; 583 va_list args;
@@ -558,10 +585,11 @@ void drawCentered_Text(int fontId, iRect rect, int color, const char *format, ..
558 vprintf_Block(&chars, format, args); 585 vprintf_Block(&chars, format, args);
559 va_end(args); 586 va_end(args);
560 } 587 }
561 const char *text = cstr_Block(&chars); 588 const iRangecc text = range_Block(&chars);
562 iInt2 textSize = advance_Text(fontId, text); 589 iRect textBounds = alignVisual ? visualBounds_Text(fontId, text)
563 draw_Text_(fontId, sub_I2(mid_Rect(rect), divi_I2(textSize, 2)), color, 590 : (iRect){ zero_I2(), advanceRange_Text(fontId, text) };
564 (iRangecc){ constBegin_Block(&chars), constEnd_Block(&chars) }); 591 textBounds.pos = sub_I2(mid_Rect(rect), mid_Rect(textBounds));
592 draw_Text_(fontId, textBounds.pos, color, text);
565 deinit_Block(&chars); 593 deinit_Block(&chars);
566} 594}
567 595