diff options
-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); |