summaryrefslogtreecommitdiff
path: root/src/ui/text.c
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-07-22 15:34:08 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-07-22 15:34:08 +0300
commite0e53e4a51afcbd22345d12416645d3fc5483a18 (patch)
tree37c7ca552d60b6ccb179a807d9dbcfee8b097cfd /src/ui/text.c
parente16f37f75399ef4dd2f267efbc720768489f96a1 (diff)
Text wrapping; basic scrolling in DocumentWidget
Diffstat (limited to 'src/ui/text.c')
-rw-r--r--src/ui/text.c95
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
20iDeclareType(Glyph) 21iDeclareType(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
379static iInt2 run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLen, iInt2 pos, 380static 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
453iInt2 advance_Text(int fontId, const char *text) { 478iInt2 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
465iInt2 advanceN_Text(int fontId, const char *text, size_t n) { 492iInt2 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
474iInt2 advanceRange_Text(int fontId, iRangecc text) { 506iInt2 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
483iInt2 tryAdvanceRange_Text(int fontId, iRangecc text, int width, const char **endPos) { 510iInt2 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
489static void draw_Text_(int fontId, iInt2 pos, int color, iRangecc text) { 520static 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