diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-04 14:54:02 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-08-04 14:54:02 +0300 |
commit | 137d5b32a4b8d29e9a9578a1fb47c673290647be (patch) | |
tree | 96b69b479aa6d14e88b0da5cfa664c7dfcddb634 | |
parent | eb1e613d1309c51e299888290766896a850e9171 (diff) |
Changing the document content text size
The glyph cache is purged whenever font sizes change. UI fonts are
deliberately not changed in size so that UI metrics don't have to be
recomputed.
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | src/app.c | 6 | ||||
-rw-r--r-- | src/gmdocument.c | 10 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 85 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 2 | ||||
-rw-r--r-- | src/ui/metrics.h | 2 | ||||
-rw-r--r-- | src/ui/text.c | 160 | ||||
-rw-r--r-- | src/ui/text.h | 14 | ||||
-rw-r--r-- | src/ui/window.c | 4 |
9 files changed, 202 insertions, 83 deletions
@@ -1,3 +1,5 @@ | |||
1 | .DS_Store | 1 | .DS_Store |
2 | *.user | 2 | *.user |
3 | build-* | 3 | build-* |
4 | /.vsbuild | ||
5 | /.vscode | ||
@@ -405,6 +405,12 @@ iBool handleCommand_App(const char *cmd) { | |||
405 | delete_String(homePath); | 405 | delete_String(homePath); |
406 | return iTrue; | 406 | return iTrue; |
407 | } | 407 | } |
408 | else if (equal_Command(cmd, "font.setfactor")) { | ||
409 | setContentFontSize_Text((float) arg_Command(cmd) / 100.0f); | ||
410 | postCommand_App("font.changed"); | ||
411 | refresh_App(); | ||
412 | return iTrue; | ||
413 | } | ||
408 | else { | 414 | else { |
409 | return iFalse; | 415 | return iFalse; |
410 | } | 416 | } |
diff --git a/src/gmdocument.c b/src/gmdocument.c index cf30a081..cb9e2acb 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -382,9 +382,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
382 | /* List bullet. */ | 382 | /* List bullet. */ |
383 | run.color = colors[type]; | 383 | run.color = colors[type]; |
384 | if (type == bullet_GmLineType) { | 384 | if (type == bullet_GmLineType) { |
385 | run.visBounds.pos = addX_I2(pos, indent * gap_UI); | 385 | run.visBounds.pos = addX_I2(pos, indent * gap_Text); |
386 | run.visBounds.size = advance_Text(run.font, bullet); | 386 | run.visBounds.size = advance_Text(run.font, bullet); |
387 | run.visBounds.pos.x -= 4 * gap_UI - width_Rect(run.visBounds) / 2; | 387 | run.visBounds.pos.x -= 4 * gap_Text - width_Rect(run.visBounds) / 2; |
388 | run.bounds = zero_Rect(); /* just visual */ | 388 | run.bounds = zero_Rect(); /* just visual */ |
389 | run.text = range_CStr(bullet); | 389 | run.text = range_CStr(bullet); |
390 | pushBack_Array(&d->layout, &run); | 390 | pushBack_Array(&d->layout, &run); |
@@ -392,14 +392,14 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
392 | /* Link icon. */ | 392 | /* Link icon. */ |
393 | if (type == link_GmLineType) { | 393 | if (type == link_GmLineType) { |
394 | run.visBounds.pos = pos; | 394 | run.visBounds.pos = pos; |
395 | run.visBounds.size = init_I2(indent * gap_UI, lineHeight_Text(run.font)); | 395 | run.visBounds.size = init_I2(indent * gap_Text, lineHeight_Text(run.font)); |
396 | run.bounds = zero_Rect(); /* just visual */ | 396 | run.bounds = zero_Rect(); /* just visual */ |
397 | const iGmLink *link = constAt_PtrArray(&d->links, run.linkId - 1); | 397 | const iGmLink *link = constAt_PtrArray(&d->links, run.linkId - 1); |
398 | run.text = range_CStr(link->flags & file_GmLinkFlag | 398 | run.text = range_CStr(link->flags & file_GmLinkFlag |
399 | ? folder | 399 | ? folder |
400 | : link->flags & remote_GmLinkFlag ? globe : arrow); | 400 | : link->flags & remote_GmLinkFlag ? globe : arrow); |
401 | if (link->flags & remote_GmLinkFlag) { | 401 | if (link->flags & remote_GmLinkFlag) { |
402 | run.visBounds.pos.x -= gap_UI / 2; | 402 | run.visBounds.pos.x -= gap_Text / 2; |
403 | } | 403 | } |
404 | run.color = linkColor_GmDocument(d, run.linkId); | 404 | run.color = linkColor_GmDocument(d, run.linkId); |
405 | if (link->flags & visited_GmLinkFlag) { | 405 | if (link->flags & visited_GmLinkFlag) { |
@@ -433,7 +433,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
433 | runLine.start != line.start) { | 433 | runLine.start != line.start) { |
434 | pos.y += midRunSkip * lineHeight_Text(run.font); | 434 | pos.y += midRunSkip * lineHeight_Text(run.font); |
435 | } | 435 | } |
436 | run.bounds.pos = addX_I2(pos, indent * gap_UI); | 436 | run.bounds.pos = addX_I2(pos, indent * gap_Text); |
437 | const char *contPos; | 437 | const char *contPos; |
438 | const int avail = d->size.x - run.bounds.pos.x; | 438 | const int avail = d->size.x - run.bounds.pos.x; |
439 | const iInt2 dims = | 439 | const iInt2 dims = |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index d13f6f01..a61d5b88 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -82,6 +82,7 @@ struct Impl_DocumentWidget { | |||
82 | iGmRequest *request; | 82 | iGmRequest *request; |
83 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ | 83 | iAtomicInt isRequestUpdated; /* request has new content, need to parse it */ |
84 | iObjectList *media; | 84 | iObjectList *media; |
85 | int textSizePercent; | ||
85 | iGmDocument *doc; | 86 | iGmDocument *doc; |
86 | int certFlags; | 87 | int certFlags; |
87 | iDate certExpiry; | 88 | iDate certExpiry; |
@@ -114,6 +115,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
114 | d->request = NULL; | 115 | d->request = NULL; |
115 | d->isRequestUpdated = iFalse; | 116 | d->isRequestUpdated = iFalse; |
116 | d->media = new_ObjectList(); | 117 | d->media = new_ObjectList(); |
118 | d->textSizePercent = 100; | ||
117 | d->doc = new_GmDocument(); | 119 | d->doc = new_GmDocument(); |
118 | d->certFlags = 0; | 120 | d->certFlags = 0; |
119 | d->selecting = iFalse; | 121 | d->selecting = iFalse; |
@@ -154,7 +156,8 @@ void deinit_DocumentWidget(iDocumentWidget *d) { | |||
154 | static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | 156 | static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { |
155 | const iWidget *w = constAs_Widget(d); | 157 | const iWidget *w = constAs_Widget(d); |
156 | const iRect bounds = bounds_Widget(w); | 158 | const iRect bounds = bounds_Widget(w); |
157 | return iMini(bounds.size.x - gap_UI * d->pageMargin * 2, fontSize_UI * 40); | 159 | return iMini(bounds.size.x - gap_UI * d->pageMargin * 2, |
160 | fontSize_UI * 40 * d->textSizePercent / 100); | ||
158 | } | 161 | } |
159 | 162 | ||
160 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | 163 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { |
@@ -531,6 +534,32 @@ static const char *sourceLoc_DocumentWidget_(const iDocumentWidget *d, iInt2 pos | |||
531 | return findLoc_GmDocument(d->doc, documentPos_DocumentWidget_(d, pos)); | 534 | return findLoc_GmDocument(d->doc, documentPos_DocumentWidget_(d, pos)); |
532 | } | 535 | } |
533 | 536 | ||
537 | iDeclareType(MiddleRunParams) | ||
538 | struct Impl_MiddleRunParams { | ||
539 | int midY; | ||
540 | const iGmRun *closest; | ||
541 | int distance; | ||
542 | }; | ||
543 | |||
544 | static void find_MiddleRunParams_(void *params, const iGmRun *run) { | ||
545 | iMiddleRunParams *d = params; | ||
546 | if (isEmpty_Rect(run->bounds)) { | ||
547 | return; | ||
548 | } | ||
549 | const int distance = iAbs(mid_Rect(run->bounds).y - d->midY); | ||
550 | if (!d->closest || distance < d->distance) { | ||
551 | d->closest = run; | ||
552 | d->distance = distance; | ||
553 | } | ||
554 | } | ||
555 | |||
556 | static const iGmRun *middleRun_DocumentWidget_(const iDocumentWidget *d) { | ||
557 | iRangei visRange = visibleRange_DocumentWidget_(d); | ||
558 | iMiddleRunParams params = { (visRange.start + visRange.end) / 2, NULL, 0 }; | ||
559 | render_GmDocument(d->doc, visRange, find_MiddleRunParams_, ¶ms); | ||
560 | return params.closest; | ||
561 | } | ||
562 | |||
534 | static void removeMediaRequest_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId) { | 563 | static void removeMediaRequest_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId) { |
535 | iForEach(ObjectList, i, d->media) { | 564 | iForEach(ObjectList, i, d->media) { |
536 | iMediaRequest *req = (iMediaRequest *) i.object; | 565 | iMediaRequest *req = (iMediaRequest *) i.object; |
@@ -596,12 +625,34 @@ static iBool handleMediaEvent_DocumentWidget_(iDocumentWidget *d, const char *cm | |||
596 | return iFalse; | 625 | return iFalse; |
597 | } | 626 | } |
598 | 627 | ||
628 | static void changeTextSize_DocumentWidget_(iDocumentWidget *d, int delta) { | ||
629 | if (delta == 0) { | ||
630 | d->textSizePercent = 100; | ||
631 | } | ||
632 | else { | ||
633 | if (d->textSizePercent < 100 || (delta < 0 && d->textSizePercent == 100)) { | ||
634 | delta /= 2; | ||
635 | } | ||
636 | d->textSizePercent += delta; | ||
637 | d->textSizePercent = iClamp(d->textSizePercent, 50, 200); | ||
638 | } | ||
639 | postCommandf_App("font.setfactor arg:%d", d->textSizePercent); | ||
640 | } | ||
641 | |||
599 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { | 642 | static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { |
600 | iWidget *w = as_Widget(d); | 643 | iWidget *w = as_Widget(d); |
601 | if (isResize_UserEvent(ev)) { | 644 | if (isResize_UserEvent(ev) || isCommand_UserEvent(ev, "font.changed")) { |
645 | const iGmRun *mid = middleRun_DocumentWidget_(d); | ||
646 | const char *midLoc = (mid ? mid->text.start : NULL); | ||
602 | setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); | 647 | setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d)); |
603 | scroll_DocumentWidget_(d, 0); | 648 | scroll_DocumentWidget_(d, 0); |
604 | updateVisible_DocumentWidget_(d); | 649 | updateVisible_DocumentWidget_(d); |
650 | if (midLoc) { | ||
651 | mid = findRunAtLoc_GmDocument(d->doc, midLoc); | ||
652 | if (mid) { | ||
653 | scrollTo_DocumentWidget_(d, mid_Rect(mid->bounds).y); | ||
654 | } | ||
655 | } | ||
605 | refresh_Widget(w); | 656 | refresh_Widget(w); |
606 | } | 657 | } |
607 | else if (isCommand_UserEvent(ev, "server.showcert")) { | 658 | else if (isCommand_UserEvent(ev, "server.showcert")) { |
@@ -762,7 +813,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
762 | case SDLK_UP: | 813 | case SDLK_UP: |
763 | case SDLK_DOWN: | 814 | case SDLK_DOWN: |
764 | if (mods == 0) { | 815 | if (mods == 0) { |
765 | scroll_DocumentWidget_(d, 2 * lineHeight_Text(paragraph_FontId) * | 816 | scroll_DocumentWidget_(d, 2 * lineHeight_Text(default_FontId) * |
766 | (key == SDLK_UP ? -1 : 1)); | 817 | (key == SDLK_UP ? -1 : 1)); |
767 | return iTrue; | 818 | return iTrue; |
768 | } | 819 | } |
@@ -772,6 +823,16 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
772 | case ' ': | 823 | case ' ': |
773 | postCommand_Widget(w, "scroll.page arg:%d", key == SDLK_PAGEUP ? -1 : +1); | 824 | postCommand_Widget(w, "scroll.page arg:%d", key == SDLK_PAGEUP ? -1 : +1); |
774 | return iTrue; | 825 | return iTrue; |
826 | case SDLK_MINUS: | ||
827 | case SDLK_EQUALS: | ||
828 | case SDLK_0: | ||
829 | if (mods == KMOD_PRIMARY) { | ||
830 | changeTextSize_DocumentWidget_( | ||
831 | d, key == SDLK_EQUALS ? 10 : key == SDLK_MINUS ? -10 : 0); | ||
832 | return iTrue; | ||
833 | } | ||
834 | break; | ||
835 | #if 0 | ||
775 | case '0': { | 836 | case '0': { |
776 | extern int enableHalfPixelGlyphs_Text; | 837 | extern int enableHalfPixelGlyphs_Text; |
777 | enableHalfPixelGlyphs_Text = !enableHalfPixelGlyphs_Text; | 838 | enableHalfPixelGlyphs_Text = !enableHalfPixelGlyphs_Text; |
@@ -780,10 +841,15 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
780 | fflush(stdout); | 841 | fflush(stdout); |
781 | break; | 842 | break; |
782 | } | 843 | } |
844 | #endif | ||
783 | } | 845 | } |
784 | } | 846 | } |
785 | else if (ev->type == SDL_MOUSEWHEEL) { | 847 | else if (ev->type == SDL_MOUSEWHEEL) { |
786 | scroll_DocumentWidget_(d, -3 * ev->wheel.y * lineHeight_Text(paragraph_FontId)); | 848 | if (keyMods_Sym(SDL_GetModState()) == KMOD_PRIMARY) { |
849 | changeTextSize_DocumentWidget_(d, ev->wheel.y > 0 ? 10 : -10); | ||
850 | return iTrue; | ||
851 | } | ||
852 | scroll_DocumentWidget_(d, -3 * ev->wheel.y * lineHeight_Text(default_FontId)); | ||
787 | return iTrue; | 853 | return iTrue; |
788 | } | 854 | } |
789 | else if (ev->type == SDL_MOUSEMOTION) { | 855 | else if (ev->type == SDL_MOUSEMOTION) { |
@@ -954,6 +1020,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
954 | deinit_String(&text); | 1020 | deinit_String(&text); |
955 | /* Presentation of links. */ | 1021 | /* Presentation of links. */ |
956 | if (run->linkId) { | 1022 | if (run->linkId) { |
1023 | const int metaFont = paragraph_FontId; | ||
957 | /* TODO: Show status of an ongoing media request. */ | 1024 | /* TODO: Show status of an ongoing media request. */ |
958 | const int flags = linkFlags_GmDocument(doc, run->linkId); | 1025 | const int flags = linkFlags_GmDocument(doc, run->linkId); |
959 | const iRect linkRect = moved_Rect(run->visBounds, origin); | 1026 | const iRect linkRect = moved_Rect(run->visBounds, origin); |
@@ -971,7 +1038,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
971 | appendFormat_String( | 1038 | appendFormat_String( |
972 | &text, " %s\U0001f7a8", isHover ? white_ColorEscape : ""); | 1039 | &text, " %s\U0001f7a8", isHover ? white_ColorEscape : ""); |
973 | } | 1040 | } |
974 | drawAlign_Text(uiLabel_FontId, | 1041 | drawAlign_Text(metaFont, |
975 | add_I2(topRight_Rect(run->bounds), origin), | 1042 | add_I2(topRight_Rect(run->bounds), origin), |
976 | fg, | 1043 | fg, |
977 | right_Alignment, | 1044 | right_Alignment, |
@@ -982,7 +1049,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
982 | else if (run->flags & endOfLine_GmRunFlag && | 1049 | else if (run->flags & endOfLine_GmRunFlag && |
983 | (mr = findMediaRequest_DocumentWidget_(d->widget, run->linkId)) != NULL) { | 1050 | (mr = findMediaRequest_DocumentWidget_(d->widget, run->linkId)) != NULL) { |
984 | if (!isFinished_GmRequest(mr->req)) { | 1051 | if (!isFinished_GmRequest(mr->req)) { |
985 | draw_Text(uiLabel_FontId, | 1052 | draw_Text(metaFont, |
986 | topRight_Rect(linkRect), | 1053 | topRight_Rect(linkRect), |
987 | linkColor_GmDocument(doc, run->linkId), | 1054 | linkColor_GmDocument(doc, run->linkId), |
988 | " \u2014 Fetching\u2026"); | 1055 | " \u2014 Fetching\u2026"); |
@@ -1021,7 +1088,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1021 | cstr_String(collect_String(format_Date(&date, "%b %d")))); | 1088 | cstr_String(collect_String(format_Date(&date, "%b %d")))); |
1022 | } | 1089 | } |
1023 | if (!isEmpty_String(&str)) { | 1090 | if (!isEmpty_String(&str)) { |
1024 | const iInt2 textSize = measure_Text(uiLabel_FontId, cstr_String(&str)); | 1091 | const iInt2 textSize = measure_Text(metaFont, cstr_String(&str)); |
1025 | int tx = topRight_Rect(linkRect).x; | 1092 | int tx = topRight_Rect(linkRect).x; |
1026 | const char *msg = cstr_String(&str); | 1093 | const char *msg = cstr_String(&str); |
1027 | if (tx + textSize.x > right_Rect(d->widgetBounds)) { | 1094 | if (tx + textSize.x > right_Rect(d->widgetBounds)) { |
@@ -1029,9 +1096,9 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
1029 | fillRect_Paint(&d->paint, (iRect){ init_I2(tx, top_Rect(linkRect)), textSize }, | 1096 | fillRect_Paint(&d->paint, (iRect){ init_I2(tx, top_Rect(linkRect)), textSize }, |
1030 | black_ColorId); | 1097 | black_ColorId); |
1031 | msg += 4; /* skip the space and dash */ | 1098 | msg += 4; /* skip the space and dash */ |
1032 | tx += measure_Text(uiLabel_FontId, " \u2014").x / 2; | 1099 | tx += measure_Text(metaFont, " \u2014").x / 2; |
1033 | } | 1100 | } |
1034 | drawAlign_Text(uiLabel_FontId, | 1101 | drawAlign_Text(metaFont, |
1035 | init_I2(tx, top_Rect(linkRect)), | 1102 | init_I2(tx, top_Rect(linkRect)), |
1036 | fg - 1, | 1103 | fg - 1, |
1037 | left_Alignment, | 1104 | left_Alignment, |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index c246c0c4..0ec1c5f8 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -242,7 +242,7 @@ void updateSize_LabelWidget(iLabelWidget *d) { | |||
242 | 242 | ||
243 | void init_LabelWidget(iLabelWidget *d, const char *label, int key, int kmods, const char *cmd) { | 243 | void init_LabelWidget(iLabelWidget *d, const char *label, int key, int kmods, const char *cmd) { |
244 | init_Widget(&d->widget); | 244 | init_Widget(&d->widget); |
245 | d->font = default_FontId; | 245 | d->font = uiLabel_FontId; |
246 | initCStr_String(&d->label, label); | 246 | initCStr_String(&d->label, label); |
247 | if (cmd) { | 247 | if (cmd) { |
248 | initCStr_String(&d->command, cmd); | 248 | initCStr_String(&d->command, cmd); |
diff --git a/src/ui/metrics.h b/src/ui/metrics.h index d59f726a..39fc6415 100644 --- a/src/ui/metrics.h +++ b/src/ui/metrics.h | |||
@@ -6,4 +6,4 @@ extern int gap_UI; | |||
6 | extern int fontSize_UI; | 6 | extern int fontSize_UI; |
7 | extern iInt2 gap2_UI; | 7 | extern iInt2 gap2_UI; |
8 | 8 | ||
9 | void setPixelRatio_Metrics (float pixelRatio); | 9 | void setPixelRatio_Metrics (float pixelRatio); |
diff --git a/src/ui/text.c b/src/ui/text.c index f562e642..ca3f5fba 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -23,6 +23,7 @@ iDeclareType(Font) | |||
23 | iDeclareType(Glyph) | 23 | iDeclareType(Glyph) |
24 | iDeclareTypeConstructionArgs(Glyph, iChar ch) | 24 | iDeclareTypeConstructionArgs(Glyph, iChar ch) |
25 | 25 | ||
26 | int gap_Text; /* cf. gap_UI in metrics.h */ | ||
26 | int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ | 27 | int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ |
27 | 28 | ||
28 | struct Impl_Glyph { | 29 | struct Impl_Glyph { |
@@ -97,6 +98,7 @@ struct Impl_CacheRow { | |||
97 | }; | 98 | }; |
98 | 99 | ||
99 | struct Impl_Text { | 100 | struct Impl_Text { |
101 | float contentFontSize; | ||
100 | iFont fonts[max_FontId]; | 102 | iFont fonts[max_FontId]; |
101 | SDL_Renderer *render; | 103 | SDL_Renderer *render; |
102 | SDL_Texture * cache; | 104 | SDL_Texture * cache; |
@@ -110,11 +112,84 @@ struct Impl_Text { | |||
110 | 112 | ||
111 | static iText text_; | 113 | static iText text_; |
112 | 114 | ||
115 | static void initFonts_Text_(iText *d) { | ||
116 | const float textSize = fontSize_UI * d->contentFontSize; | ||
117 | const struct { | ||
118 | const iBlock *ttf; | ||
119 | int size; | ||
120 | int symbolsFont; | ||
121 | } fontData[max_FontId] = { | ||
122 | { &fontSourceSansProRegular_Embedded, fontSize_UI, defaultSymbols_FontId }, | ||
123 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, defaultSymbols_FontId }, | ||
124 | { &fontFiraSansRegular_Embedded, textSize, symbols_FontId }, | ||
125 | { &fontFiraMonoRegular_Embedded, textSize * 0.866f, smallSymbols_FontId }, | ||
126 | { &fontFiraMonoRegular_Embedded, textSize * 0.666f, smallSymbols_FontId }, | ||
127 | { &fontFiraSansRegular_Embedded, textSize * 1.333f, mediumSymbols_FontId }, | ||
128 | { &fontFiraSansItalic_Embedded, textSize, symbols_FontId }, | ||
129 | { &fontFiraSansBold_Embedded, textSize, symbols_FontId }, | ||
130 | { &fontFiraSansBold_Embedded, textSize * 1.333f, mediumSymbols_FontId }, | ||
131 | { &fontFiraSansBold_Embedded, textSize * 1.666f, largeSymbols_FontId }, | ||
132 | { &fontFiraSansBold_Embedded, textSize * 2.000f, hugeSymbols_FontId }, | ||
133 | { &fontSymbola_Embedded, fontSize_UI, defaultSymbols_FontId }, | ||
134 | { &fontSymbola_Embedded, textSize, symbols_FontId }, | ||
135 | { &fontSymbola_Embedded, textSize * 1.333f, mediumSymbols_FontId }, | ||
136 | { &fontSymbola_Embedded, textSize * 1.666f, largeSymbols_FontId }, | ||
137 | { &fontSymbola_Embedded, textSize * 2.000f, hugeSymbols_FontId }, | ||
138 | { &fontSymbola_Embedded, textSize * 0.866f, smallSymbols_FontId }, | ||
139 | { &fontNotoEmojiRegular_Embedded, fontSize_UI, defaultSymbols_FontId }, | ||
140 | { &fontNotoEmojiRegular_Embedded, textSize, symbols_FontId }, | ||
141 | { &fontNotoEmojiRegular_Embedded, textSize * 1.333f, mediumSymbols_FontId }, | ||
142 | { &fontNotoEmojiRegular_Embedded, textSize * 1.666f, largeSymbols_FontId }, | ||
143 | { &fontNotoEmojiRegular_Embedded, textSize * 2.000f, hugeSymbols_FontId }, | ||
144 | { &fontNotoEmojiRegular_Embedded, textSize * 0.866f, smallSymbols_FontId }, | ||
145 | }; | ||
146 | iForIndices(i, fontData) { | ||
147 | iFont *font = &d->fonts[i]; | ||
148 | init_Font(font, fontData[i].ttf, fontData[i].size, fontData[i].symbolsFont); | ||
149 | if (fontData[i].ttf == &fontFiraMonoRegular_Embedded) { | ||
150 | font->enableKerning = iFalse; | ||
151 | } | ||
152 | } | ||
153 | gap_Text = iRound(gap_UI * d->contentFontSize); | ||
154 | } | ||
155 | |||
156 | static void deinitFonts_Text_(iText *d) { | ||
157 | iForIndices(i, d->fonts) { | ||
158 | deinit_Font(&d->fonts[i]); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | static void initCache_Text_(iText *d) { | ||
163 | init_Array(&d->cacheRows, sizeof(iCacheRow)); | ||
164 | const int textSize = d->contentFontSize * fontSize_UI; | ||
165 | iAssert(textSize > 0); | ||
166 | const iInt2 cacheDims = init_I2(16, 80); | ||
167 | d->cacheSize = mul_I2(cacheDims, init1_I2(iMax(textSize, fontSize_UI))); | ||
168 | d->cacheRowAllocStep = iMax(2, textSize / 6); | ||
169 | /* Allocate initial (empty) rows. These will be assigned actual locations in the cache | ||
170 | once at least one glyph is stored. */ | ||
171 | for (int h = d->cacheRowAllocStep; h <= 2 * textSize; h += d->cacheRowAllocStep) { | ||
172 | pushBack_Array(&d->cacheRows, &(iCacheRow){ .height = 0 }); | ||
173 | } | ||
174 | d->cacheBottom = 0; | ||
175 | d->cache = SDL_CreateTexture(d->render, | ||
176 | SDL_PIXELFORMAT_RGBA8888, | ||
177 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
178 | d->cacheSize.x, | ||
179 | d->cacheSize.y); | ||
180 | SDL_SetTextureBlendMode(d->cache, SDL_BLENDMODE_BLEND); | ||
181 | } | ||
182 | |||
183 | static void deinitCache_Text_(iText *d) { | ||
184 | deinit_Array(&d->cacheRows); | ||
185 | SDL_DestroyTexture(d->cache); | ||
186 | } | ||
187 | |||
113 | void init_Text(SDL_Renderer *render) { | 188 | void init_Text(SDL_Renderer *render) { |
114 | iText *d = &text_; | 189 | iText *d = &text_; |
115 | init_Array(&d->cacheRows, sizeof(iCacheRow)); | 190 | d->contentFontSize = 1.0f; |
116 | d->ansiEscape = new_RegExp("\\[([0-9;]+)m", 0); | 191 | d->ansiEscape = new_RegExp("\\[([0-9;]+)m", 0); |
117 | d->render = render; | 192 | d->render = render; |
118 | /* A grayscale palette for rasterized glyphs. */ { | 193 | /* A grayscale palette for rasterized glyphs. */ { |
119 | SDL_Color colors[256]; | 194 | SDL_Color colors[256]; |
120 | for (int i = 0; i < 256; ++i) { | 195 | for (int i = 0; i < 256; ++i) { |
@@ -123,74 +198,35 @@ void init_Text(SDL_Renderer *render) { | |||
123 | d->grayscale = SDL_AllocPalette(256); | 198 | d->grayscale = SDL_AllocPalette(256); |
124 | SDL_SetPaletteColors(d->grayscale, colors, 0, 256); | 199 | SDL_SetPaletteColors(d->grayscale, colors, 0, 256); |
125 | } | 200 | } |
126 | /* Initialize the glyph cache. */ { | 201 | initCache_Text_(d); |
127 | d->cacheSize = init_I2(fontSize_UI * 16, fontSize_UI * 64); | 202 | initFonts_Text_(d); |
128 | d->cacheRowAllocStep = fontSize_UI / 6; | ||
129 | /* Allocate initial (empty) rows. These will be assigned actual locations in the cache | ||
130 | once at least one glyph is stored. */ | ||
131 | for (int h = d->cacheRowAllocStep; h <= 2 * fontSize_UI; h += d->cacheRowAllocStep) { | ||
132 | pushBack_Array(&d->cacheRows, &(iCacheRow){ .height = 0 }); | ||
133 | } | ||
134 | d->cacheBottom = 0; | ||
135 | d->cache = SDL_CreateTexture(render, | ||
136 | SDL_PIXELFORMAT_RGBA8888, | ||
137 | SDL_TEXTUREACCESS_STATIC | SDL_TEXTUREACCESS_TARGET, | ||
138 | d->cacheSize.x, | ||
139 | d->cacheSize.y); | ||
140 | SDL_SetTextureBlendMode(d->cache, SDL_BLENDMODE_BLEND); | ||
141 | } | ||
142 | /* Load the fonts. */ { | ||
143 | const struct { | ||
144 | const iBlock *ttf; | ||
145 | int size; | ||
146 | int symbolsFont; | ||
147 | } fontData[max_FontId] = { | ||
148 | { &fontSourceSansProRegular_Embedded, fontSize_UI, symbols_FontId }, | ||
149 | { &fontFiraSansRegular_Embedded, fontSize_UI, symbols_FontId }, | ||
150 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.866f, smallSymbols_FontId }, | ||
151 | { &fontFiraMonoRegular_Embedded, fontSize_UI * 0.666f, smallSymbols_FontId }, | ||
152 | { &fontFiraSansRegular_Embedded, fontSize_UI * 1.333f, mediumSymbols_FontId }, | ||
153 | { &fontFiraSansItalic_Embedded, fontSize_UI, symbols_FontId }, | ||
154 | { &fontFiraSansBold_Embedded, fontSize_UI, symbols_FontId }, | ||
155 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.333f, mediumSymbols_FontId }, | ||
156 | { &fontFiraSansBold_Embedded, fontSize_UI * 1.666f, largeSymbols_FontId }, | ||
157 | { &fontFiraSansBold_Embedded, fontSize_UI * 2.000f, hugeSymbols_FontId }, | ||
158 | { &fontSymbola_Embedded, fontSize_UI, symbols_FontId }, | ||
159 | { &fontSymbola_Embedded, fontSize_UI * 1.333f, mediumSymbols_FontId }, | ||
160 | { &fontSymbola_Embedded, fontSize_UI * 1.666f, largeSymbols_FontId }, | ||
161 | { &fontSymbola_Embedded, fontSize_UI * 2.000f, hugeSymbols_FontId }, | ||
162 | { &fontSymbola_Embedded, fontSize_UI * 0.866f, smallSymbols_FontId }, | ||
163 | { &fontNotoEmojiRegular_Embedded, fontSize_UI, symbols_FontId }, | ||
164 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.333f, mediumSymbols_FontId }, | ||
165 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 1.666f, largeSymbols_FontId }, | ||
166 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 2.000f, hugeSymbols_FontId }, | ||
167 | { &fontNotoEmojiRegular_Embedded, fontSize_UI * 0.866f, smallSymbols_FontId }, | ||
168 | }; | ||
169 | iForIndices(i, fontData) { | ||
170 | iFont *font = &d->fonts[i]; | ||
171 | init_Font(font, | ||
172 | fontData[i].ttf, | ||
173 | fontData[i].size, | ||
174 | fontData[i].symbolsFont); | ||
175 | if (fontData[i].ttf == &fontFiraMonoRegular_Embedded) { | ||
176 | font->enableKerning = iFalse; | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | } | 203 | } |
181 | 204 | ||
182 | void deinit_Text(void) { | 205 | void deinit_Text(void) { |
183 | iText *d = &text_; | 206 | iText *d = &text_; |
184 | SDL_FreePalette(d->grayscale); | 207 | SDL_FreePalette(d->grayscale); |
185 | iForIndices(i, d->fonts) { | 208 | deinitFonts_Text_(d); |
186 | deinit_Font(&d->fonts[i]); | 209 | deinitCache_Text_(d); |
187 | } | ||
188 | deinit_Array(&d->cacheRows); | ||
189 | SDL_DestroyTexture(d->cache); | ||
190 | d->render = NULL; | 210 | d->render = NULL; |
191 | iRelease(d->ansiEscape); | 211 | iRelease(d->ansiEscape); |
192 | } | 212 | } |
193 | 213 | ||
214 | void setContentFontSize_Text(float fontSizeFactor) { | ||
215 | iAssert(fontSizeFactor > 0); | ||
216 | if (iAbs(text_.contentFontSize - fontSizeFactor) > 0.001f) { | ||
217 | text_.contentFontSize = fontSizeFactor; | ||
218 | resetFonts_Text(); | ||
219 | } | ||
220 | } | ||
221 | |||
222 | void resetFonts_Text(void) { | ||
223 | iText *d = &text_; | ||
224 | deinitFonts_Text_(d); | ||
225 | deinitCache_Text_(d); | ||
226 | initCache_Text_(d); | ||
227 | initFonts_Text_(d); | ||
228 | } | ||
229 | |||
194 | iLocalDef iFont *font_Text_(enum iFontId id) { | 230 | iLocalDef iFont *font_Text_(enum iFontId id) { |
195 | return &text_.fonts[id]; | 231 | return &text_.fonts[id]; |
196 | } | 232 | } |
diff --git a/src/ui/text.h b/src/ui/text.h index f165c5b4..ea58abb6 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -7,8 +7,9 @@ | |||
7 | 7 | ||
8 | enum iFontId { | 8 | enum iFontId { |
9 | default_FontId, | 9 | default_FontId, |
10 | defaultMonospace_FontId, | ||
10 | regular_FontId, | 11 | regular_FontId, |
11 | monospace_FontId, | 12 | monospace_FontId, |
12 | monospaceSmall_FontId, | 13 | monospaceSmall_FontId, |
13 | medium_FontId, | 14 | medium_FontId, |
14 | italic_FontId, | 15 | italic_FontId, |
@@ -16,11 +17,13 @@ enum iFontId { | |||
16 | mediumBold_FontId, | 17 | mediumBold_FontId, |
17 | largeBold_FontId, | 18 | largeBold_FontId, |
18 | hugeBold_FontId, | 19 | hugeBold_FontId, |
20 | defaultSymbols_FontId, | ||
19 | symbols_FontId, | 21 | symbols_FontId, |
20 | mediumSymbols_FontId, | 22 | mediumSymbols_FontId, |
21 | largeSymbols_FontId, | 23 | largeSymbols_FontId, |
22 | hugeSymbols_FontId, | 24 | hugeSymbols_FontId, |
23 | smallSymbols_FontId, | 25 | smallSymbols_FontId, |
26 | defaultEmoji_FontId, | ||
24 | emoji_FontId, | 27 | emoji_FontId, |
25 | mediumEmoji_FontId, | 28 | mediumEmoji_FontId, |
26 | largeEmoji_FontId, | 29 | largeEmoji_FontId, |
@@ -28,11 +31,11 @@ enum iFontId { | |||
28 | smallEmoji_FontId, | 31 | smallEmoji_FontId, |
29 | max_FontId, | 32 | max_FontId, |
30 | /* Meta: */ | 33 | /* Meta: */ |
31 | fromSymbolsToEmojiOffset_FontId = 5, | 34 | fromSymbolsToEmojiOffset_FontId = 6, |
32 | /* UI fonts: */ | 35 | /* UI fonts: */ |
33 | uiLabel_FontId = default_FontId, | 36 | uiLabel_FontId = default_FontId, |
34 | uiShortcuts_FontId = default_FontId, | 37 | uiShortcuts_FontId = default_FontId, |
35 | uiInput_FontId = monospace_FontId, | 38 | uiInput_FontId = defaultMonospace_FontId, |
36 | /* Document fonts: */ | 39 | /* Document fonts: */ |
37 | paragraph_FontId = regular_FontId, | 40 | paragraph_FontId = regular_FontId, |
38 | firstParagraph_FontId = medium_FontId, | 41 | firstParagraph_FontId = medium_FontId, |
@@ -44,9 +47,14 @@ enum iFontId { | |||
44 | header3_FontId = medium_FontId, | 47 | header3_FontId = medium_FontId, |
45 | }; | 48 | }; |
46 | 49 | ||
50 | extern int gap_Text; /* affected by content font size */ | ||
51 | |||
47 | void init_Text (SDL_Renderer *); | 52 | void init_Text (SDL_Renderer *); |
48 | void deinit_Text (void); | 53 | void deinit_Text (void); |
49 | 54 | ||
55 | void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ | ||
56 | void resetFonts_Text (void); | ||
57 | |||
50 | int lineHeight_Text (int fontId); | 58 | int lineHeight_Text (int fontId); |
51 | iInt2 measure_Text (int fontId, const char *text); | 59 | iInt2 measure_Text (int fontId, const char *text); |
52 | iInt2 measureRange_Text (int fontId, iRangecc text); | 60 | iInt2 measureRange_Text (int fontId, iRangecc text); |
diff --git a/src/ui/window.c b/src/ui/window.c index 7db0977b..eaab6ac5 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -205,7 +205,7 @@ static void setupUserInterface_Window(iWindow *d) { | |||
205 | iClob(newIcon_LabelWidget("\U0001f513", 0, 0, "server.showcert")), | 205 | iClob(newIcon_LabelWidget("\U0001f513", 0, 0, "server.showcert")), |
206 | frameless_WidgetFlag | tight_WidgetFlag); | 206 | frameless_WidgetFlag | tight_WidgetFlag); |
207 | setId_Widget(as_Widget(lock), "navbar.lock"); | 207 | setId_Widget(as_Widget(lock), "navbar.lock"); |
208 | setFont_LabelWidget(lock, symbols_FontId); | 208 | setFont_LabelWidget(lock, defaultSymbols_FontId); |
209 | updateTextCStr_LabelWidget(lock, "\U0001f512"); | 209 | updateTextCStr_LabelWidget(lock, "\U0001f512"); |
210 | iInputWidget *url = new_InputWidget(0); | 210 | iInputWidget *url = new_InputWidget(0); |
211 | setId_Widget(as_Widget(url), "url"); | 211 | setId_Widget(as_Widget(url), "url"); |
@@ -545,7 +545,7 @@ void draw_Window(iWindow *d) { | |||
545 | #if 0 | 545 | #if 0 |
546 | /* Text cache debugging. */ { | 546 | /* Text cache debugging. */ { |
547 | SDL_Texture *cache = glyphCache_Text(); | 547 | SDL_Texture *cache = glyphCache_Text(); |
548 | SDL_Rect rect = { d->root->rect.size.x - 640, 0, 640, 4 * 640 }; | 548 | SDL_Rect rect = { d->root->rect.size.x - 640, 0, 640, 5 * 640 }; |
549 | SDL_SetRenderDrawColor(d->render, 0, 0, 0, 255); | 549 | SDL_SetRenderDrawColor(d->render, 0, 0, 0, 255); |
550 | SDL_RenderFillRect(d->render, &rect); | 550 | SDL_RenderFillRect(d->render, &rect); |
551 | SDL_RenderCopy(d->render, glyphCache_Text(), NULL, &rect); | 551 | SDL_RenderCopy(d->render, glyphCache_Text(), NULL, &rect); |