summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);