diff options
Diffstat (limited to 'src/ui/text.c')
-rw-r--r-- | src/ui/text.c | 80 |
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) | |||
23 | iDeclareType(Glyph) | 23 | iDeclareType(Glyph) |
24 | iDeclareTypeConstructionArgs(Glyph, iChar ch) | 24 | iDeclareTypeConstructionArgs(Glyph, iChar ch) |
25 | 25 | ||
26 | int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ | ||
27 | |||
26 | struct Impl_Glyph { | 28 | struct 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 | ||
192 | iFont *font_Text_(enum iFontId id) { | 194 | iLocalDef 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 | ||
309 | enum iRunMode { measure_RunMode, measureNoWrap_RunMode, draw_RunMode, drawPermanentColor_RunMode }; | 311 | enum 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 | ||
311 | static iChar nextChar_(const char **chPos, const char *end) { | 319 | static 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 | ||
325 | int enableHalfPixelGlyphs_Text = iTrue; | ||
326 | |||
327 | iLocalDef iBool isWrapBoundary_(iChar a, iChar b) { | 333 | iLocalDef 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 | ||
334 | static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, | 340 | iLocalDef iBool isMeasuring_(enum iRunMode mode) { |
341 | return mode == measure_RunMode || mode == measureNoWrap_RunMode || | ||
342 | mode == measureVisual_RunMode; | ||
343 | } | ||
344 | |||
345 | static 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 | ||
427 | int lineHeight_Text(int fontId) { | 449 | int 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 | |||
467 | iRect 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 | ||
445 | iInt2 measure_Text(int fontId, const char *text) { | 472 | iInt2 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 | ||
553 | void drawCentered_Text(int fontId, iRect rect, int color, const char *format, ...) { | 580 | void 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 | ||