summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2020-08-04 14:54:02 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2020-08-04 14:54:02 +0300
commit137d5b32a4b8d29e9a9578a1fb47c673290647be (patch)
tree96b69b479aa6d14e88b0da5cfa664c7dfcddb634
parenteb1e613d1309c51e299888290766896a850e9171 (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--.gitignore2
-rw-r--r--src/app.c6
-rw-r--r--src/gmdocument.c10
-rw-r--r--src/ui/documentwidget.c85
-rw-r--r--src/ui/labelwidget.c2
-rw-r--r--src/ui/metrics.h2
-rw-r--r--src/ui/text.c160
-rw-r--r--src/ui/text.h14
-rw-r--r--src/ui/window.c4
9 files changed, 202 insertions, 83 deletions
diff --git a/.gitignore b/.gitignore
index 1d5e5a5b..f7f28a4b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
1.DS_Store 1.DS_Store
2*.user 2*.user
3build-* 3build-*
4/.vsbuild
5/.vscode
diff --git a/src/app.c b/src/app.c
index 5ac211f9..36e55970 100644
--- a/src/app.c
+++ b/src/app.c
@@ -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) {
154static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { 156static 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
160static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { 163static 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
537iDeclareType(MiddleRunParams)
538struct Impl_MiddleRunParams {
539 int midY;
540 const iGmRun *closest;
541 int distance;
542};
543
544static 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
556static 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_, &params);
560 return params.closest;
561}
562
534static void removeMediaRequest_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId) { 563static 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
628static 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
599static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { 642static 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
243void init_LabelWidget(iLabelWidget *d, const char *label, int key, int kmods, const char *cmd) { 243void 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;
6extern int fontSize_UI; 6extern int fontSize_UI;
7extern iInt2 gap2_UI; 7extern iInt2 gap2_UI;
8 8
9void setPixelRatio_Metrics (float pixelRatio); 9void 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)
23iDeclareType(Glyph) 23iDeclareType(Glyph)
24iDeclareTypeConstructionArgs(Glyph, iChar ch) 24iDeclareTypeConstructionArgs(Glyph, iChar ch)
25 25
26int gap_Text; /* cf. gap_UI in metrics.h */
26int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ 27int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */
27 28
28struct Impl_Glyph { 29struct Impl_Glyph {
@@ -97,6 +98,7 @@ struct Impl_CacheRow {
97}; 98};
98 99
99struct Impl_Text { 100struct 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
111static iText text_; 113static iText text_;
112 114
115static 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
156static void deinitFonts_Text_(iText *d) {
157 iForIndices(i, d->fonts) {
158 deinit_Font(&d->fonts[i]);
159 }
160}
161
162static 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
183static void deinitCache_Text_(iText *d) {
184 deinit_Array(&d->cacheRows);
185 SDL_DestroyTexture(d->cache);
186}
187
113void init_Text(SDL_Renderer *render) { 188void 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
182void deinit_Text(void) { 205void 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
214void 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
222void 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
194iLocalDef iFont *font_Text_(enum iFontId id) { 230iLocalDef 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
8enum iFontId { 8enum 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
50extern int gap_Text; /* affected by content font size */
51
47void init_Text (SDL_Renderer *); 52void init_Text (SDL_Renderer *);
48void deinit_Text (void); 53void deinit_Text (void);
49 54
55void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */
56void resetFonts_Text (void);
57
50int lineHeight_Text (int fontId); 58int lineHeight_Text (int fontId);
51iInt2 measure_Text (int fontId, const char *text); 59iInt2 measure_Text (int fontId, const char *text);
52iInt2 measureRange_Text (int fontId, iRangecc text); 60iInt2 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);