diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-22 15:34:08 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-07-22 15:34:08 +0300 |
commit | e0e53e4a51afcbd22345d12416645d3fc5483a18 (patch) | |
tree | 37c7ca552d60b6ccb179a807d9dbcfee8b097cfd /src/ui/text.c | |
parent | e16f37f75399ef4dd2f267efbc720768489f96a1 (diff) |
Text wrapping; basic scrolling in DocumentWidget
Diffstat (limited to 'src/ui/text.c')
-rw-r--r-- | src/ui/text.c | 95 |
1 files changed, 64 insertions, 31 deletions
diff --git a/src/ui/text.c b/src/ui/text.c index 23e28e4d..afad778c 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <the_Foundation/vec2.h> | 15 | #include <the_Foundation/vec2.h> |
16 | 16 | ||
17 | #include <SDL_surface.h> | 17 | #include <SDL_surface.h> |
18 | #include <SDL_hints.h> | ||
18 | #include <stdarg.h> | 19 | #include <stdarg.h> |
19 | 20 | ||
20 | iDeclareType(Glyph) | 21 | iDeclareType(Glyph) |
@@ -115,12 +116,12 @@ void init_Text(SDL_Renderer *render) { | |||
115 | /* Load the fonts. */ { | 116 | /* Load the fonts. */ { |
116 | const struct { const iBlock *ttf; int size; } fontData[max_FontId] = { | 117 | const struct { const iBlock *ttf; int size; } fontData[max_FontId] = { |
117 | { &fontFiraSansRegular_Embedded, fontSize_UI }, | 118 | { &fontFiraSansRegular_Embedded, fontSize_UI }, |
118 | { &fontFiraMonoRegular_Embedded, fontSize_UI }, | 119 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.85f }, |
119 | { &fontFiraSansRegular_Embedded, fontSize_UI * 1.5f }, | 120 | { &fontFiraSansRegular_Embedded, fontSize_UI * 1.5f }, |
120 | { &fontFiraSansLightItalic_Embedded, fontSize_UI }, | 121 | { &fontFiraSansLightItalic_Embedded, fontSize_UI }, |
121 | { &fontFiraSansBold_Embedded, fontSize_UI }, | 122 | { &fontFiraSansBold_Embedded, fontSize_UI }, |
122 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.5f }, | 123 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.35f }, |
123 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.75f }, | 124 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.7f }, |
124 | { &fontFiraSansBold_Embedded, fontSize_UI * 2.0f }, | 125 | { &fontFiraSansBold_Embedded, fontSize_UI * 2.0f }, |
125 | }; | 126 | }; |
126 | iForIndices(i, fontData) { | 127 | iForIndices(i, fontData) { |
@@ -377,18 +378,26 @@ static iChar nextChar_(const char **chPos, const char *end) { | |||
377 | } | 378 | } |
378 | 379 | ||
379 | static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, | 380 | static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, |
380 | int *runAdvance_out) { | 381 | int xposLimit, const char **continueFrom_out, int *runAdvance_out) { |
381 | iInt2 size = zero_I2(); | 382 | iInt2 size = zero_I2(); |
382 | const iInt2 orig = pos; | 383 | const iInt2 orig = pos; |
383 | const stbtt_fontinfo *info = &d->font; | 384 | const stbtt_fontinfo *info = &d->font; |
384 | float xpos = pos.x; | 385 | float xpos = pos.x; |
385 | float xposMax = xpos; | 386 | float xposMax = xpos; |
387 | iAssert(xposLimit == 0 || mode == measure_RunMode); | ||
388 | const char *lastWordEnd = text.start; | ||
389 | if (continueFrom_out) { | ||
390 | *continueFrom_out = text.end; | ||
391 | } | ||
392 | iChar prevCh = 0; | ||
386 | for (const char *chPos = text.start; chPos != text.end; ) { | 393 | for (const char *chPos = text.start; chPos != text.end; ) { |
394 | iAssert(chPos < text.end); | ||
387 | iChar ch = nextChar_(&chPos, text.end); | 395 | iChar ch = nextChar_(&chPos, text.end); |
388 | /* Special instructions. */ { | 396 | /* Special instructions. */ { |
389 | if (ch == '\n') { | 397 | if (ch == '\n') { |
390 | xpos = pos.x; | 398 | xpos = pos.x; |
391 | pos.y += d->height; | 399 | pos.y += d->height; |
400 | prevCh = ch; | ||
392 | continue; | 401 | continue; |
393 | } | 402 | } |
394 | if (ch == '\r') { | 403 | if (ch == '\r') { |
@@ -397,20 +406,26 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
397 | if (mode == draw_RunMode) { | 406 | if (mode == draw_RunMode) { |
398 | SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b); | 407 | SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b); |
399 | } | 408 | } |
409 | prevCh = 0; | ||
400 | continue; | 410 | continue; |
401 | } | 411 | } |
402 | } | 412 | } |
403 | const iGlyph *glyph = glyph_Font_(d, ch); | 413 | const iGlyph *glyph = glyph_Font_(d, ch); |
404 | int x1 = iRound(xpos); | 414 | float x1 = xpos; |
405 | int x2 = x1 + glyph->rect.size.x; | 415 | float x2 = x1 + glyph->rect.size.x; |
416 | if (xposLimit > 0 && x2 > xposLimit) { | ||
417 | /* Out of space. */ | ||
418 | *continueFrom_out = lastWordEnd; | ||
419 | break; | ||
420 | } | ||
406 | size.x = iMax(size.x, x2 - orig.x); | 421 | size.x = iMax(size.x, x2 - orig.x); |
407 | size.y = iMax(size.y, pos.y + d->height - orig.y); | 422 | size.y = iMax(size.y, pos.y + d->height - orig.y); |
408 | if (mode != measure_RunMode) { | 423 | if (mode != measure_RunMode) { |
409 | SDL_Rect dst = { x1 + glyph->dx, | 424 | SDL_FRect dst = { x1 + glyph->dx, |
410 | pos.y + d->baseline + glyph->dy, | 425 | pos.y + d->baseline + glyph->dy, |
411 | glyph->rect.size.x, | 426 | glyph->rect.size.x, |
412 | glyph->rect.size.y }; | 427 | glyph->rect.size.y }; |
413 | SDL_RenderCopy(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect, &dst); | 428 | SDL_RenderCopyF(text_.render, text_.cache, (const SDL_Rect *) &glyph->rect, &dst); |
414 | } | 429 | } |
415 | int advance, lsb; | 430 | int advance, lsb; |
416 | const iBool spec = isSpecialChar_(ch); | 431 | const iBool spec = isSpecialChar_(ch); |
@@ -420,6 +435,9 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
420 | } | 435 | } |
421 | xpos += d->scale * advance; | 436 | xpos += d->scale * advance; |
422 | xposMax = iMax(xposMax, xpos); | 437 | xposMax = iMax(xposMax, xpos); |
438 | if (!isSpace_Char(prevCh) && isSpace_Char(ch)) { | ||
439 | lastWordEnd = chPos; | ||
440 | } | ||
423 | /* Check the next character. */ { | 441 | /* Check the next character. */ { |
424 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ | 442 | /* TODO: No need to decode the next char twice; check this on the next iteration. */ |
425 | const char *peek = chPos; | 443 | const char *peek = chPos; |
@@ -428,6 +446,7 @@ static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe | |||
428 | xpos += d->scale * stbtt_GetCodepointKernAdvance(info, ch, next); | 446 | xpos += d->scale * stbtt_GetCodepointKernAdvance(info, ch, next); |
429 | } | 447 | } |
430 | } | 448 | } |
449 | prevCh = ch; | ||
431 | if (--maxLen == 0) { | 450 | if (--maxLen == 0) { |
432 | break; | 451 | break; |
433 | } | 452 | } |
@@ -446,44 +465,56 @@ iInt2 measure_Text(int fontId, const char *text) { | |||
446 | if (!*text) { | 465 | if (!*text) { |
447 | return init_I2(0, lineHeight_Text(fontId)); | 466 | return init_I2(0, lineHeight_Text(fontId)); |
448 | } | 467 | } |
449 | return run_Font_( | 468 | return run_Font_(&text_.fonts[fontId], |
450 | &text_.fonts[fontId], measure_RunMode, range_CStr(text), iInvalidSize, zero_I2(), NULL); | 469 | measure_RunMode, |
470 | range_CStr(text), | ||
471 | iInvalidSize, | ||
472 | zero_I2(), | ||
473 | 0, | ||
474 | NULL, | ||
475 | NULL); | ||
451 | } | 476 | } |
452 | 477 | ||
453 | iInt2 advance_Text(int fontId, const char *text) { | 478 | iInt2 advanceRange_Text(int fontId, iRangecc text) { |
454 | int advance; | 479 | int advance; |
455 | const int height = run_Font_(&text_.fonts[fontId], | 480 | const int height = run_Font_(&text_.fonts[fontId], |
456 | measure_RunMode, | 481 | measure_RunMode, |
457 | range_CStr(text), | 482 | text, |
458 | iInvalidSize, | 483 | iInvalidSize, |
459 | zero_I2(), | 484 | zero_I2(), |
485 | 0, | ||
486 | NULL, | ||
460 | &advance) | 487 | &advance) |
461 | .y; | 488 | .y; |
462 | return init_I2(advance, height); | 489 | return init_I2(advance, height); |
463 | } | 490 | } |
464 | 491 | ||
465 | iInt2 advanceN_Text(int fontId, const char *text, size_t n) { | 492 | iInt2 tryAdvanceRange_Text(int fontId, iRangecc text, int width, const char **endPos) { |
466 | if (n == 0) { | ||
467 | return init_I2(0, lineHeight_Text(fontId)); | ||
468 | } | ||
469 | int advance; | 493 | int advance; |
470 | run_Font_(&text_.fonts[fontId], measure_RunMode, range_CStr(text), n, zero_I2(), &advance); | 494 | const int height = run_Font_(&text_.fonts[fontId], |
471 | return init_I2(advance, lineHeight_Text(fontId)); | 495 | measure_RunMode, |
496 | text, | ||
497 | iInvalidSize, | ||
498 | zero_I2(), | ||
499 | width, | ||
500 | endPos, | ||
501 | &advance) | ||
502 | .y; | ||
503 | return init_I2(advance, height); | ||
472 | } | 504 | } |
473 | 505 | ||
474 | iInt2 advanceRange_Text(int fontId, iRangecc text) { | 506 | iInt2 advance_Text(int fontId, const char *text) { |
475 | /* TODO: Rangecc should be the default for runs; no need to copy here */ | 507 | return advanceRange_Text(fontId, range_CStr(text)); |
476 | iString str; | ||
477 | initRange_String(&str, text); | ||
478 | const iInt2 metrics = advance_Text(fontId, cstr_String(&str)); | ||
479 | deinit_String(&str); | ||
480 | return metrics; | ||
481 | } | 508 | } |
482 | 509 | ||
483 | iInt2 tryAdvanceRange_Text(int fontId, iRangecc text, int width, const char **endPos) { | 510 | iInt2 advanceN_Text(int fontId, const char *text, size_t n) { |
511 | if (n == 0) { | ||
512 | return init_I2(0, lineHeight_Text(fontId)); | ||
513 | } | ||
484 | int advance; | 514 | int advance; |
485 | const int height = run_Font_(&text_.fonts[fontId], measure_RunMode, text, iInvalidSize, zero_I2(), &advance).y; | 515 | run_Font_( |
486 | 516 | &text_.fonts[fontId], measure_RunMode, range_CStr(text), n, zero_I2(), 0, NULL, &advance); | |
517 | return init_I2(advance, lineHeight_Text(fontId)); | ||
487 | } | 518 | } |
488 | 519 | ||
489 | static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) { | 520 | static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) { |
@@ -495,6 +526,8 @@ static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) { | |||
495 | text, | 526 | text, |
496 | iInvalidSize, | 527 | iInvalidSize, |
497 | pos, | 528 | pos, |
529 | 0, | ||
530 | NULL, | ||
498 | NULL); | 531 | NULL); |
499 | } | 532 | } |
500 | 533 | ||