summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-10-06 12:16:43 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-10-06 12:16:43 +0300
commit61a3dc017067be43472dadb7909094aa04d1fe9d (patch)
tree18b87895489844b4e516f79cd1588038f1d49494 /src/ui
parentf6a54d5375aab9c41af3f7c8a5e8fcbd1e0c9287 (diff)
Revised runtime font management
The built-in fonts are loaded via FontPack, and the font table is now constructed dynamically based on available fonts. A full set of variants (style, size) are prepared for each font, but some of the data gets allocated lazily when needed. GmRun needed a larger allocation for fonts, so now all the fields are combined into a single bit field. TODO: Glyph scaling, vertical offsets, and symbol lookup are still not fully working.
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/color.h2
-rw-r--r--src/ui/documentwidget.c49
-rw-r--r--src/ui/mediaui.c4
-rw-r--r--src/ui/mobile.c8
-rw-r--r--src/ui/root.c13
-rw-r--r--src/ui/sidebarwidget.c18
-rw-r--r--src/ui/text.c337
-rw-r--r--src/ui/text.h131
-rw-r--r--src/ui/uploadwidget.c2
-rw-r--r--src/ui/util.c10
-rw-r--r--src/ui/window.c4
11 files changed, 322 insertions, 256 deletions
diff --git a/src/ui/color.h b/src/ui/color.h
index 179db3e9..c9dff598 100644
--- a/src/ui/color.h
+++ b/src/ui/color.h
@@ -160,7 +160,7 @@ enum iColorId {
160 tmGopherLinkDomain_ColorId, 160 tmGopherLinkDomain_ColorId,
161 tmGopherLinkLastVisitDate_ColorId, 161 tmGopherLinkLastVisitDate_ColorId,
162 162
163 max_ColorId, 163 max_ColorId, /* note: GmRun packs color into limited number of bits */
164 tmMax_ColorId = max_ColorId - tmFirst_ColorId 164 tmMax_ColorId = max_ColorId - tmFirst_ColorId
165}; 165};
166 166
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 589b9e56..b83490f9 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -928,8 +928,9 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) {
928 pushBackCStr_StringArray(title, "Lagrange"); 928 pushBackCStr_StringArray(title, "Lagrange");
929 } 929 }
930 /* Take away parts if it doesn't fit. */ 930 /* Take away parts if it doesn't fit. */
931 const int avail = bounds_Widget(as_Widget(tabButton)).size.x - 3 * gap_UI; 931 const int avail = bounds_Widget(as_Widget(tabButton)).size.x - 3 * gap_UI;
932 iBool setWindow = (document_App() == d && isUnderKeyRoot_Widget(d)); 932 iBool setWindow = (document_App() == d && isUnderKeyRoot_Widget(d));
933 const int font = uiLabel_FontId;
933 for (;;) { 934 for (;;) {
934 iString *text = collect_String(joinCStr_StringArray(title, " \u2014 ")); 935 iString *text = collect_String(joinCStr_StringArray(title, " \u2014 "));
935 if (setWindow) { 936 if (setWindow) {
@@ -945,7 +946,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) {
945 prependChar_String(text, siteIcon); 946 prependChar_String(text, siteIcon);
946 prependCStr_String(text, escape_Color(uiIcon_ColorId)); 947 prependCStr_String(text, escape_Color(uiIcon_ColorId));
947 } 948 }
948 const int width = measureRange_Text(default_FontId, range_String(text)).advance.x; 949 const int width = measureRange_Text(font, range_String(text)).advance.x;
949 if (width <= avail || 950 if (width <= avail ||
950 isEmpty_StringArray(title)) { 951 isEmpty_StringArray(title)) {
951 updateText_LabelWidget(tabButton, text); 952 updateText_LabelWidget(tabButton, text);
@@ -954,9 +955,9 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) {
954 if (size_StringArray(title) == 1) { 955 if (size_StringArray(title) == 1) {
955 /* Just truncate to fit. */ 956 /* Just truncate to fit. */
956 const char *endPos; 957 const char *endPos;
957 tryAdvanceNoWrap_Text(default_FontId, 958 tryAdvanceNoWrap_Text(font,
958 range_String(text), 959 range_String(text),
959 avail - measure_Text(default_FontId, "...").advance.x, 960 avail - measure_Text(font, "...").advance.x,
960 &endPos); 961 &endPos);
961 updateText_LabelWidget( 962 updateText_LabelWidget(
962 tabButton, 963 tabButton,
@@ -1619,7 +1620,7 @@ static void parseUser_DocumentWidget_(iDocumentWidget *d) {
1619static void cacheRunGlyphs_(void *data, const iGmRun *run) { 1620static void cacheRunGlyphs_(void *data, const iGmRun *run) {
1620 iUnused(data); 1621 iUnused(data);
1621 if (!isEmpty_Range(&run->text)) { 1622 if (!isEmpty_Range(&run->text)) {
1622 cache_Text(run->textParams.font, run->text); 1623 cache_Text(run->font, run->text);
1623 } 1624 }
1624} 1625}
1625 1626
@@ -4020,14 +4021,14 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol
4020 contains_Range(&mark, run->text.start))) { 4021 contains_Range(&mark, run->text.start))) {
4021 int x = 0; 4022 int x = 0;
4022 if (!*isInside) { 4023 if (!*isInside) {
4023 x = measureRange_Text(run->textParams.font, 4024 x = measureRange_Text(run->font,
4024 (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) 4025 (iRangecc){ run->text.start, iMax(run->text.start, mark.start) })
4025 .advance.x; 4026 .advance.x;
4026 } 4027 }
4027 int w = width_Rect(run->visBounds) - x; 4028 int w = width_Rect(run->visBounds) - x;
4028 if (contains_Range(&run->text, mark.end) || mark.end < run->text.start) { 4029 if (contains_Range(&run->text, mark.end) || mark.end < run->text.start) {
4029 w = measureRange_Text( 4030 w = measureRange_Text(
4030 run->textParams.font, 4031 run->font,
4031 !*isInside ? mark 4032 !*isInside ? mark
4032 : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }) 4033 : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) })
4033 .advance.x; 4034 .advance.x;
@@ -4083,15 +4084,15 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2
4083 iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); 4084 iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2));
4084 if (icon) { 4085 if (icon) {
4085 appendChar_String(&str, icon); 4086 appendChar_String(&str, icon);
4086 const iRect iconRect = visualBounds_Text(run->textParams.font, range_String(&str)); 4087 const iRect iconRect = visualBounds_Text(run->font, range_String(&str));
4087 drawRange_Text( 4088 drawRange_Text(
4088 run->textParams.font, 4089 run->font,
4089 addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->textParams.font) / 2), 4090 addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2),
4090 tmBannerIcon_ColorId, 4091 tmBannerIcon_ColorId,
4091 range_String(&str)); 4092 range_String(&str));
4092 bpos.x += right_Rect(iconRect) + 3 * gap_Text; 4093 bpos.x += right_Rect(iconRect) + 3 * gap_Text;
4093 } 4094 }
4094 drawRange_Text(run->textParams.font, 4095 drawRange_Text(run->font,
4095 bpos, 4096 bpos,
4096 tmBannerTitle_ColorId, 4097 tmBannerTitle_ColorId,
4097 bannerText_DocumentWidget_(d->widget)); 4098 bannerText_DocumentWidget_(d->widget));
@@ -4198,7 +4199,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
4198 /* Media UIs are drawn afterwards as a dynamic overlay. */ 4199 /* Media UIs are drawn afterwards as a dynamic overlay. */
4199 return; 4200 return;
4200 } 4201 }
4201 enum iColorId fg = run->textParams.color; 4202 enum iColorId fg = run->color;
4202 const iGmDocument *doc = d->widget->doc; 4203 const iGmDocument *doc = d->widget->doc;
4203 const int linkFlags = linkFlags_GmDocument(doc, run->linkId); 4204 const int linkFlags = linkFlags_GmDocument(doc, run->linkId);
4204 /* Hover state of a link. */ 4205 /* Hover state of a link. */
@@ -4265,10 +4266,10 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
4265 const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); 4266 const iInt2 margin = preRunMargin_GmDocument(doc, run->preId);
4266 fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); 4267 fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId);
4267 drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); 4268 drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId);
4268 drawWrapRange_Text(run->textParams.font, 4269 drawWrapRange_Text(run->font,
4269 add_I2(visPos, margin), 4270 add_I2(visPos, margin),
4270 run->visBounds.size.x - 2 * margin.x, 4271 run->visBounds.size.x - 2 * margin.x,
4271 run->textParams.color, 4272 run->color,
4272 run->text); 4273 run->text);
4273 } 4274 }
4274 else if (run->flags & siteBanner_GmRunFlag) { 4275 else if (run->flags & siteBanner_GmRunFlag) {
@@ -4287,17 +4288,17 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
4287 linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); 4288 linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase);
4288 if (ordChar) { 4289 if (ordChar) {
4289 const char *circle = "\u25ef"; /* Large Circle */ 4290 const char *circle = "\u25ef"; /* Large Circle */
4290 const int circleFont = defaultContentRegular_FontId; 4291 const int circleFont = FONT_ID(default_FontId, regular_FontStyle, contentRegular_FontSize);
4291 iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), 4292 iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y),
4292 init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) }; 4293 init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) };
4293 drawRange_Text( 4294 drawRange_Text(
4294 circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); 4295 circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle));
4295 iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle)); 4296 iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle));
4296 addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); 4297 addv_I2(&circleArea.pos, topLeft_Rect(nbArea));
4297 drawCentered_Text(defaultContentSmall_FontId, 4298 drawCentered_Text(FONT_ID(default_FontId, regular_FontStyle, contentSmall_FontSize),
4298 circleArea, 4299 circleArea,
4299 iTrue, 4300 iTrue,
4300 tmQuote_ColorId, 4301 tmQuote_ColorId,
4301 "%lc", 4302 "%lc",
4302 (int) ordChar); 4303 (int) ordChar);
4303 goto runDrawn; 4304 goto runDrawn;
@@ -4307,15 +4308,15 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
4307 if (run->flags & quoteBorder_GmRunFlag) { 4308 if (run->flags & quoteBorder_GmRunFlag) {
4308 drawVLine_Paint(&d->paint, 4309 drawVLine_Paint(&d->paint,
4309 addX_I2(visPos, 4310 addX_I2(visPos,
4310 !run->textParams.isRTL 4311 !run->isRTL
4311 ? -gap_Text * 5 / 2 4312 ? -gap_Text * 5 / 2
4312 : (width_Rect(run->visBounds) + gap_Text * 5 / 2)), 4313 : (width_Rect(run->visBounds) + gap_Text * 5 / 2)),
4313 height_Rect(run->visBounds), 4314 height_Rect(run->visBounds),
4314 tmQuoteIcon_ColorId); 4315 tmQuoteIcon_ColorId);
4315 } 4316 }
4316 drawBoundRange_Text(run->textParams.font, 4317 drawBoundRange_Text(run->font,
4317 visPos, 4318 visPos,
4318 (run->textParams.isRTL ? -1 : 1) * width_Rect(run->visBounds), 4319 (run->isRTL ? -1 : 1) * width_Rect(run->visBounds),
4319 fg, 4320 fg,
4320 run->text); 4321 run->text);
4321 runDrawn:; 4322 runDrawn:;
@@ -4431,7 +4432,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
4431 append_String(&str, collect_String(format_Date(&date, "%b %d"))); 4432 append_String(&str, collect_String(format_Date(&date, "%b %d")));
4432 } 4433 }
4433 if (!isEmpty_String(&str)) { 4434 if (!isEmpty_String(&str)) {
4434 if (run->textParams.isRTL) { 4435 if (run->isRTL) {
4435 appendCStr_String(&str, " \u2014 "); 4436 appendCStr_String(&str, " \u2014 ");
4436 } 4437 }
4437 else { 4438 else {
@@ -4440,7 +4441,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
4440 const iInt2 textSize = measure_Text(metaFont, cstr_String(&str)).bounds.size; 4441 const iInt2 textSize = measure_Text(metaFont, cstr_String(&str)).bounds.size;
4441 int tx = topRight_Rect(linkRect).x; 4442 int tx = topRight_Rect(linkRect).x;
4442 const char *msg = cstr_String(&str); 4443 const char *msg = cstr_String(&str);
4443 if (run->textParams.isRTL) { 4444 if (run->isRTL) {
4444 tx = topLeft_Rect(linkRect).x - textSize.x; 4445 tx = topLeft_Rect(linkRect).x - textSize.x;
4445 } 4446 }
4446 if (tx + textSize.x > right_Rect(d->widgetBounds)) { 4447 if (tx + textSize.x > right_Rect(d->widgetBounds)) {
@@ -4909,7 +4910,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
4909 } 4910 }
4910 /* Pinch zoom indicator. */ 4911 /* Pinch zoom indicator. */
4911 if (d->flags & pinchZoom_DocumentWidgetFlag) { 4912 if (d->flags & pinchZoom_DocumentWidgetFlag) {
4912 const int font = defaultLargeBold_FontId; 4913 const int font = uiLabelLargeBold_FontId;
4913 const int height = lineHeight_Text(font) * 2; 4914 const int height = lineHeight_Text(font) * 2;
4914 const iInt2 size = init_I2(height * 2, height); 4915 const iInt2 size = init_I2(height * 2, height);
4915 const iRect rect = { sub_I2(mid_Rect(bounds), divi_I2(size, 2)), size }; 4916 const iRect rect = { sub_I2(mid_Rect(bounds), divi_I2(size, 2)), size };
diff --git a/src/ui/mediaui.c b/src/ui/mediaui.c
index b622a554..22552027 100644
--- a/src/ui/mediaui.c
+++ b/src/ui/mediaui.c
@@ -86,7 +86,7 @@ static int drawSevenSegmentTime_(iInt2 pos, int color, int align, int seconds) {
86 const int hours = seconds / 3600; 86 const int hours = seconds / 3600;
87 const int mins = (seconds / 60) % 60; 87 const int mins = (seconds / 60) % 60;
88 const int secs = seconds % 60; 88 const int secs = seconds % 60;
89 const int font = defaultBig_FontId; 89 const int font = uiLabelBig_FontId;
90 iString num; 90 iString num;
91 init_String(&num); 91 init_String(&num);
92 if (hours) { 92 if (hours) {
@@ -123,7 +123,7 @@ void draw_PlayerUI(iPlayerUI *d, iPaint *p) {
123 drawPlayerButton_( 123 drawPlayerButton_(
124 p, d->volumeRect, volumeChar_(volume_Player(d->player)), uiContentSymbols_FontId); 124 p, d->volumeRect, volumeChar_(volume_Player(d->player)), uiContentSymbols_FontId);
125 } 125 }
126 const int hgt = lineHeight_Text(defaultBig_FontId); 126 const int hgt = lineHeight_Text(uiLabelBig_FontId);
127 const int yMid = mid_Rect(d->scrubberRect).y; 127 const int yMid = mid_Rect(d->scrubberRect).y;
128 const float playTime = time_Player(d->player); 128 const float playTime = time_Player(d->player);
129 const float totalTime = duration_Player(d->player); 129 const float totalTime = duration_Player(d->player);
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index 3cb6e631..f11769f5 100644
--- a/src/ui/mobile.c
+++ b/src/ui/mobile.c
@@ -48,11 +48,11 @@ static iBool isSideBySideLayout_(void) {
48} 48}
49 49
50static enum iFontId labelFont_(void) { 50static enum iFontId labelFont_(void) {
51 return deviceType_App() == phone_AppDeviceType ? defaultBig_FontId : defaultMedium_FontId; 51 return deviceType_App() == phone_AppDeviceType ? uiLabelBig_FontId : uiLabelMedium_FontId;
52} 52}
53 53
54static enum iFontId labelBoldFont_(void) { 54static enum iFontId labelBoldFont_(void) {
55 return deviceType_App() == phone_AppDeviceType ? defaultBigBold_FontId : defaultMediumBold_FontId; 55 return deviceType_App() == phone_AppDeviceType ? uiLabelBigBold_FontId : uiLabelMediumBold_FontId;
56} 56}
57 57
58static void updatePanelSheetMetrics_(iWidget *sheet) { 58static void updatePanelSheetMetrics_(iWidget *sheet) {
@@ -546,7 +546,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
546 updateSize_LabelWidget(button); 546 updateSize_LabelWidget(button);
547 } 547 }
548 setId_Widget(as_Widget(button), radId); 548 setId_Widget(as_Widget(button), radId);
549 setFont_LabelWidget(button, defaultMedium_FontId); 549 setFont_LabelWidget(button, uiLabelMedium_FontId);
550 addChildFlags_Widget(widget, iClob(button), flags); 550 addChildFlags_Widget(widget, iClob(button), flags);
551 } 551 }
552 } 552 }
@@ -1056,7 +1056,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget,
1056 iForEach(ObjectList, sub, children_Widget(value)) { 1056 iForEach(ObjectList, sub, children_Widget(value)) {
1057 if (isInstance_Object(sub.object, &Class_LabelWidget)) { 1057 if (isInstance_Object(sub.object, &Class_LabelWidget)) {
1058 iLabelWidget *opt = sub.object; 1058 iLabelWidget *opt = sub.object;
1059 setFont_LabelWidget(opt, defaultMedium_FontId); 1059 setFont_LabelWidget(opt, uiLabelMedium_FontId);
1060 setFlags_Widget(as_Widget(opt), noBackground_WidgetFlag, iTrue); 1060 setFlags_Widget(as_Widget(opt), noBackground_WidgetFlag, iTrue);
1061 } 1061 }
1062 } 1062 }
diff --git a/src/ui/root.c b/src/ui/root.c
index 63cce62d..a2e6062f 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -447,7 +447,7 @@ static void updateNavBarIdentity_(iWidget *navBar) {
447 if (toolName) { 447 if (toolName) {
448 setOutline_LabelWidget(toolButton, ident == NULL); 448 setOutline_LabelWidget(toolButton, ident == NULL);
449 updateTextCStr_LabelWidget(toolName, subjectName ? cstr_String(subjectName) : ""); 449 updateTextCStr_LabelWidget(toolName, subjectName ? cstr_String(subjectName) : "");
450 setFont_LabelWidget(toolButton, subjectName ? defaultMedium_FontId : uiLabelLarge_FontId); 450 setFont_LabelWidget(toolButton, subjectName ? uiLabelMedium_FontId : uiLabelLarge_FontId);
451 arrange_Widget(parent_Widget(toolButton)); 451 arrange_Widget(parent_Widget(toolButton));
452 } 452 }
453} 453}
@@ -996,7 +996,7 @@ void updateMetrics_Root(iRoot *d) {
996 const iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); 996 const iWidget *toolBar = findChild_Widget(d->widget, "toolbar");
997 const iWidget *viewButton = findChild_Widget(d->widget, "toolbar.view"); 997 const iWidget *viewButton = findChild_Widget(d->widget, "toolbar.view");
998 const iWidget *idButton = findChild_Widget(toolBar, "toolbar.ident"); 998 const iWidget *idButton = findChild_Widget(toolBar, "toolbar.ident");
999 const int font = defaultTiny_FontId; 999 const int font = uiLabelTiny_FontId;
1000 setFont_LabelWidget(idName, font); 1000 setFont_LabelWidget(idName, font);
1001 setPos_Widget(as_Widget(idName), 1001 setPos_Widget(as_Widget(idName),
1002 windowToLocal_Widget(as_Widget(idName), 1002 windowToLocal_Widget(as_Widget(idName),
@@ -1125,7 +1125,7 @@ void createUserInterface_Root(iRoot *d) {
1125 iClob(newIcon_LabelWidget("\U0001f513", SDLK_i, KMOD_PRIMARY, "document.info")), 1125 iClob(newIcon_LabelWidget("\U0001f513", SDLK_i, KMOD_PRIMARY, "document.info")),
1126 embedFlags | moveToParentLeftEdge_WidgetFlag); 1126 embedFlags | moveToParentLeftEdge_WidgetFlag);
1127 setId_Widget(as_Widget(lock), "navbar.lock"); 1127 setId_Widget(as_Widget(lock), "navbar.lock");
1128 setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize); 1128// setFont_LabelWidget(lock, symbols_FontId + uiNormal_FontSize);
1129 updateTextCStr_LabelWidget(lock, "\U0001f512"); 1129 updateTextCStr_LabelWidget(lock, "\U0001f512");
1130 } 1130 }
1131 /* Button for clearing the URL bar contents. */ { 1131 /* Button for clearing the URL bar contents. */ {
@@ -1134,7 +1134,8 @@ void createUserInterface_Root(iRoot *d) {
1134 iClob(newIcon_LabelWidget(delete_Icon, 0, 0, "navbar.clear")), 1134 iClob(newIcon_LabelWidget(delete_Icon, 0, 0, "navbar.clear")),
1135 hidden_WidgetFlag | embedFlags | moveToParentLeftEdge_WidgetFlag | tight_WidgetFlag); 1135 hidden_WidgetFlag | embedFlags | moveToParentLeftEdge_WidgetFlag | tight_WidgetFlag);
1136 setId_Widget(as_Widget(clear), "navbar.clear"); 1136 setId_Widget(as_Widget(clear), "navbar.clear");
1137 setFont_LabelWidget(clear, symbols2_FontId + uiNormal_FontSize); 1137// setFont_LabelWidget(clear, symbols2_FontId + uiNormal_FontSize);
1138 setFont_LabelWidget(clear, uiLabelSymbols_FontId);
1138// setFlags_Widget(as_Widget(clear), noBackground_WidgetFlag, iFalse); 1139// setFlags_Widget(as_Widget(clear), noBackground_WidgetFlag, iFalse);
1139// setBackgroundColor_Widget(as_Widget(clear), uiBackground_ColorId); 1140// setBackgroundColor_Widget(as_Widget(clear), uiBackground_ColorId);
1140 } 1141 }
@@ -1149,7 +1150,7 @@ void createUserInterface_Root(iRoot *d) {
1149 iLabelWidget *queryInd = new_LabelWidget("${status.query} " return_Icon, NULL); 1150 iLabelWidget *queryInd = new_LabelWidget("${status.query} " return_Icon, NULL);
1150 setId_Widget(as_Widget(queryInd), "input.indicator.search"); 1151 setId_Widget(as_Widget(queryInd), "input.indicator.search");
1151 setTextColor_LabelWidget(queryInd, uiTextAction_ColorId); 1152 setTextColor_LabelWidget(queryInd, uiTextAction_ColorId);
1152 setFont_LabelWidget(queryInd, defaultSmall_FontId); 1153 setFont_LabelWidget(queryInd, uiLabelSmall_FontId);
1153 setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId); 1154 setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId);
1154 setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId); 1155 setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId);
1155// setAlignVisually_LabelWidget(queryInd, iTrue); 1156// setAlignVisually_LabelWidget(queryInd, iTrue);
@@ -1162,7 +1163,7 @@ void createUserInterface_Root(iRoot *d) {
1162 iLabelWidget *fprog = new_LabelWidget("", NULL); 1163 iLabelWidget *fprog = new_LabelWidget("", NULL);
1163 setId_Widget(as_Widget(fprog), "feeds.progress"); 1164 setId_Widget(as_Widget(fprog), "feeds.progress");
1164 setTextColor_LabelWidget(fprog, uiTextCaution_ColorId); 1165 setTextColor_LabelWidget(fprog, uiTextCaution_ColorId);
1165 setFont_LabelWidget(fprog, defaultSmall_FontId); 1166 setFont_LabelWidget(fprog, uiLabelSmall_FontId);
1166 setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId); 1167 setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId);
1167// setAlignVisually_LabelWidget(fprog, iTrue); 1168// setAlignVisually_LabelWidget(fprog, iTrue);
1168 setNoAutoMinHeight_LabelWidget(fprog, iTrue); 1169 setNoAutoMinHeight_LabelWidget(fprog, iTrue);
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 057ff614..42661f6b 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -188,7 +188,7 @@ static iLabelWidget *addActionButton_SidebarWidget_(iSidebarWidget *d, const cha
188 // extraPadding_WidgetFlag : 0) | 188 // extraPadding_WidgetFlag : 0) |
189 flags); 189 flags);
190 setFont_LabelWidget(btn, deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide 190 setFont_LabelWidget(btn, deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide
191 ? defaultBig_FontId 191 ? uiLabelBig_FontId
192 : d->buttonFont); 192 : d->buttonFont);
193 checkIcon_LabelWidget(btn); 193 checkIcon_LabelWidget(btn);
194 return btn; 194 return btn;
@@ -742,7 +742,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
742 d->itemFonts[1] = uiContentBold_FontId; 742 d->itemFonts[1] = uiContentBold_FontId;
743#if defined (iPlatformMobile) 743#if defined (iPlatformMobile)
744 if (deviceType_App() == phone_AppDeviceType) { 744 if (deviceType_App() == phone_AppDeviceType) {
745 d->itemFonts[0] = defaultBig_FontId; 745 d->itemFonts[0] = uiLabelBig_FontId;
746 d->itemFonts[1] = defaultBigBold_FontId; 746 d->itemFonts[1] = defaultBigBold_FontId;
747 } 747 }
748 d->widthAsGaps = 73.0f; 748 d->widthAsGaps = 73.0f;
@@ -774,7 +774,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) {
774 format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))), 774 format_CStr("%s.mode arg:%d", cstr_String(id_Widget(w)), i))),
775 frameless_WidgetFlag | noBackground_WidgetFlag); 775 frameless_WidgetFlag | noBackground_WidgetFlag);
776 } 776 }
777 setButtonFont_SidebarWidget(d, isPhone ? defaultBig_FontId : uiLabel_FontId); 777 setButtonFont_SidebarWidget(d, isPhone ? uiLabelBig_FontId : uiLabel_FontId);
778 addChildFlags_Widget(vdiv, 778 addChildFlags_Widget(vdiv,
779 iClob(buttons), 779 iClob(buttons),
780 arrangeHorizontal_WidgetFlag | 780 arrangeHorizontal_WidgetFlag |
@@ -936,8 +936,8 @@ static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) {
936 if (deviceType_App() == phone_AppDeviceType) { 936 if (deviceType_App() == phone_AppDeviceType) {
937 /* Change font size depending on orientation. */ 937 /* Change font size depending on orientation. */
938 const int fonts[2] = { 938 const int fonts[2] = {
939 isPortrait_App() ? defaultBig_FontId : uiContent_FontId, 939 isPortrait_App() ? uiLabelBig_FontId : uiContent_FontId,
940 isPortrait_App() ? defaultBigBold_FontId : uiContentBold_FontId 940 isPortrait_App() ? uiLabelBigBold_FontId : uiContentBold_FontId
941 }; 941 };
942 if (d->itemFonts[0] != fonts[0]) { 942 if (d->itemFonts[0] != fonts[0]) {
943 d->itemFonts[0] = fonts[0]; 943 d->itemFonts[0] = fonts[0];
@@ -945,7 +945,7 @@ static void checkModeButtonLayout_SidebarWidget_(iSidebarWidget *d) {
945// updateMetrics_SidebarWidget_(d); 945// updateMetrics_SidebarWidget_(d);
946 updateItemHeight_SidebarWidget_(d); 946 updateItemHeight_SidebarWidget_(d);
947 } 947 }
948 setButtonFont_SidebarWidget(d, isPortrait_App() ? defaultBig_FontId : uiLabel_FontId); 948 setButtonFont_SidebarWidget(d, isPortrait_App() ? uiLabelBig_FontId : uiLabel_FontId);
949 } 949 }
950 const iBool isTight = 950 const iBool isTight =
951 (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth); 951 (width_Rect(bounds_Widget(as_Widget(d->modeButtons[0]))) < d->maxButtonLabelWidth);
@@ -1962,7 +1962,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
1962 deinit_String(&str); 1962 deinit_String(&str);
1963 const iInt2 textPos = addY_I2(topRight_Rect(iconArea), (itemHeight - lineHeight_Text(font)) / 2); 1963 const iInt2 textPos = addY_I2(topRight_Rect(iconArea), (itemHeight - lineHeight_Text(font)) / 2);
1964 drawRange_Text(font, textPos, fg, range_String(&d->label)); 1964 drawRange_Text(font, textPos, fg, range_String(&d->label));
1965 const int metaFont = default_FontId; 1965 const int metaFont = uiLabel_FontId;
1966 const int metaIconWidth = 4.5f * gap_UI; 1966 const int metaIconWidth = 4.5f * gap_UI;
1967 const iInt2 metaPos = 1967 const iInt2 metaPos =
1968 init_I2(right_Rect(itemRect) - 1968 init_I2(right_Rect(itemRect) -
@@ -2045,7 +2045,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
2045 const int indent = 1.4f * lineHeight_Text(font); 2045 const int indent = 1.4f * lineHeight_Text(font);
2046 addv_I2(&cPos, 2046 addv_I2(&cPos,
2047 init_I2(3 * gap_UI, 2047 init_I2(3 * gap_UI,
2048 (itemHeight - lineHeight_Text(default_FontId) * 2 - lineHeight_Text(font)) / 2048 (itemHeight - lineHeight_Text(uiLabel_FontId) * 2 - lineHeight_Text(font)) /
2049 2)); 2049 2));
2050 const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId 2050 const int metaFg = isHover ? permanent_ColorId | (isPressing ? uiTextPressed_ColorId
2051 : uiTextFramelessHover_ColorId) 2051 : uiTextFramelessHover_ColorId)
@@ -2064,7 +2064,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
2064 add_I2(cPos, init_I2(indent, 0)), 2064 add_I2(cPos, init_I2(indent, 0)),
2065 fg, 2065 fg,
2066 range_String(&d->label)); 2066 range_String(&d->label));
2067 drawRange_Text(default_FontId, 2067 drawRange_Text(uiLabel_FontId,
2068 add_I2(cPos, init_I2(indent, lineHeight_Text(font))), 2068 add_I2(cPos, init_I2(indent, lineHeight_Text(font))),
2069 metaFg, 2069 metaFg,
2070 range_String(&d->meta)); 2070 range_String(&d->meta));
diff --git a/src/ui/text.c b/src/ui/text.c
index e3f7f7c4..86fdc084 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -123,40 +123,57 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch)
123 123
124static iGlyph *glyph_Font_(iFont *d, iChar ch); 124static iGlyph *glyph_Font_(iFont *d, iChar ch);
125 125
126struct Impl_Font { 126iDeclareType(GlyphTable)
127 iBlock * data; 127
128 enum iTextFont family; 128struct Impl_GlyphTable {
129 stbtt_fontinfo font; 129 iHash glyphs; /* key is glyph index in the font */
130 float xScale, yScale; 130 /* TODO: `glyphs` does not need to be a Hash.
131 int vertOffset; /* offset due to scaling */ 131 We could lazily allocate an array with glyphCount elements instead. */
132 int height;
133 int baseline;
134 iHash glyphs; /* key is glyph index in the font */ /* TODO: does not need to be a Hash */
135 iBool isMonospaced;
136 float emAdvance;
137 enum iFontSize sizeId; /* used to look up different fonts of matching size */
138 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ 132 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */
139#if defined (LAGRANGE_ENABLE_HARFBUZZ)
140 hb_blob_t * hbBlob; /* raw TrueType data */
141 hb_face_t * hbFace;
142 hb_font_t * hbMainFont;
143 hb_font_t * hbFont; /* may be a sub-font with customized font metrics */
144#endif
145}; 133};
146 134
147static iFont *font_Text_(enum iFontId id); 135static void clearGlyphs_Font_(iGlyphTable *d) {
148 136 iForEach(Hash, i, &d->glyphs) {
149#if 0 137 delete_Glyph((iGlyph *) i.value);
150static hb_position_t hbGlyphHKernForNunito_(hb_font_t *font, void *fontData, 138 }
151 hb_codepoint_t firstGlyph, hb_codepoint_t secondGlyph, 139 clear_Hash(&d->glyphs);
152 void *userData) {
153 return 100;
154} 140}
155#endif
156 141
157static void init_Font(iFont *d, const iBlock *data, int height, float scale, 142static void init_GlyphTable(iGlyphTable *d) {
158 enum iFontSize sizeId, iBool isMonospaced) {
159 init_Hash(&d->glyphs); 143 init_Hash(&d->glyphs);
144 memset(d->indexTable, 0xff, sizeof(d->indexTable));
145}
146
147static void deinit_GlyphTable(iGlyphTable *d) {
148 clearGlyphs_Font_(d);
149 deinit_Hash(&d->glyphs);
150}
151
152iDefineTypeConstruction(GlyphTable)
153
154struct Impl_Font {
155 const iFontSpec *fontSpec;
156 const iFontFile *fontFile;
157 int height;
158 int baseline;
159 int vertOffset; /* offset due to glyph scaling */
160 float xScale, yScale;
161 float emAdvance;
162 iGlyphTable * table;
163};
164
165iLocalDef iBool isMonospaced_Font(const iFont *d) {
166 return (d->fontSpec->flags & monospace_FontSpecFlag) != 0;
167}
168
169static iFont *font_Text_(enum iFontId id);
170
171static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *fontFile,
172 enum iFontSize sizeId, int height) {
173 d->fontSpec = fontSpec;
174 d->fontFile = fontFile;
175 /* TODO: Nunito kerning fixes need to be a font parameter of its own. */
176#if 0
160 d->data = NULL; 177 d->data = NULL;
161 d->family = undefined_TextFont; 178 d->family = undefined_TextFont;
162 /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ 179 /* Note: We only use `family` currently for applying a kerning fix to Nunito. */
@@ -177,95 +194,49 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale,
177 data == &fontSmolEmojiRegular_Embedded) { 194 data == &fontSmolEmojiRegular_Embedded) {
178 d->family = emojiAndSymbols_TextFont; 195 d->family = emojiAndSymbols_TextFont;
179 } 196 }
180 d->isMonospaced = isMonospaced; 197#endif
198// d->isMonospaced = (fontSpec->flags & monospace_FontSpecFlag) != 0;
181 d->height = height; 199 d->height = height;
182 iZap(d->font); 200 //iZap(d->font);
183 stbtt_InitFont(&d->font, constData_Block(data), 0); 201// stbtt_InitFont(&d->font, constData_Block(data), 0);
184 int ascent, descent, emAdv; 202// int ascent, descent, emAdv;
185 stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL); 203// stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL);
186 stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL); 204// stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL);
187 d->xScale = d->yScale = stbtt_ScaleForPixelHeight(&d->font, height) * scale; 205 const float scale = fontSpec->scaling;
188 if (d->isMonospaced) { 206 d->xScale = d->yScale = scaleForPixelHeight_FontFile(fontFile, height) * scale;
207 if (isMonospaced_Font(d)) {
189 /* It is important that monospaced fonts align 1:1 with the pixel grid so that 208 /* It is important that monospaced fonts align 1:1 with the pixel grid so that
190 box-drawing characters don't have partially occupied edge pixels, leading to seams 209 box-drawing characters don't have partially occupied edge pixels, leading to seams
191 between adjacent glyphs. */ 210 between adjacent glyphs. */
192 const float advance = (float) emAdv * d->xScale; 211 const float advance = (float) fontFile->emAdvance * d->xScale;
193 if (advance > 4) { /* not too tiny */ 212 if (advance > 4) { /* not too tiny */
194 d->xScale *= floorf(advance) / advance; 213 d->xScale *= floorf(advance) / advance;
195 } 214 }
196 } 215 }
197 d->emAdvance = emAdv * d->xScale; 216 d->emAdvance = fontFile->emAdvance * d->xScale;
198 d->baseline = ascent * d->yScale; 217 d->baseline = fontFile->ascent * d->yScale;
199 d->vertOffset = height * (1.0f - scale) / 2; 218 d->vertOffset = height * (1.0f - scale) / 2 * fontSpec->vertOffset;
200 /* Custom tweaks. */ 219 d->table = NULL;
201 if (data == &fontNotoSansSymbolsRegular_Embedded) {
202 d->vertOffset *= 1.2f;
203 }
204 else if (data == &fontNotoSansSymbols2Regular_Embedded) {
205 d->vertOffset /= 2;
206 }
207 else if (data == &fontNotoEmojiRegular_Embedded) {
208 //d->vertOffset -= height / 30;
209 }
210 d->sizeId = sizeId;
211 memset(d->indexTable, 0xff, sizeof(d->indexTable));
212#if defined(LAGRANGE_ENABLE_HARFBUZZ)
213 /* HarfBuzz will read the font data. */ {
214 d->hbBlob = hb_blob_create(constData_Block(data), size_Block(data),
215 HB_MEMORY_MODE_READONLY, NULL, NULL);
216 d->hbFace = hb_face_create(d->hbBlob, 0);
217 d->hbMainFont = hb_font_create(d->hbFace);
218#if 0
219 /* TODO: The custom kerning function doesn't get called?
220 Maybe HarfBuzz needs FreeType to do kerning? */
221 if (d->family == nunito_TextFont) {
222 /* Customize the kerning of Nunito. */
223 d->hbFont = hb_font_create_sub_font(d->hbMainFont);
224 hb_font_funcs_t *ffs = hb_font_funcs_create();
225 hb_font_funcs_set_glyph_h_kerning_func(ffs, hbGlyphHKernForNunito_, d, NULL);
226 hb_font_set_funcs(d->hbFont, ffs, NULL, NULL);
227 hb_font_funcs_destroy(ffs);
228 }
229 else
230#endif
231 {
232 d->hbFont = hb_font_reference(d->hbMainFont);
233 }
234 }
235#endif
236}
237
238static void clearGlyphs_Font_(iFont *d) {
239 iForEach(Hash, i, &d->glyphs) {
240 delete_Glyph((iGlyph *) i.value);
241 }
242 clear_Hash(&d->glyphs);
243} 220}
244 221
245static void deinit_Font(iFont *d) { 222static void deinit_Font(iFont *d) {
246#if defined(LAGRANGE_ENABLE_HARFBUZZ) 223 delete_GlyphTable(d->table);
247 /* HarfBuzz objects. */ {
248 hb_font_destroy(d->hbFont);
249 hb_font_destroy(d->hbMainFont);
250 hb_face_destroy(d->hbFace);
251 hb_blob_destroy(d->hbBlob);
252 }
253#endif
254 clearGlyphs_Font_(d);
255 deinit_Hash(&d->glyphs);
256 delete_Block(d->data);
257} 224}
258 225
259static uint32_t glyphIndex_Font_(iFont *d, iChar ch) { 226static uint32_t glyphIndex_Font_(iFont *d, iChar ch) {
260 /* TODO: Add a small cache of ~5 most recently found indices. */ 227 /* TODO: Add a small cache of ~5 most recently found indices. */
261 const size_t entry = ch - 32; 228 const size_t entry = ch - 32;
262 if (entry < iElemCount(d->indexTable)) { 229 if (!d->table) {
263 if (d->indexTable[entry] == ~0u) { 230 d->table = new_GlyphTable();
264 d->indexTable[entry] = stbtt_FindGlyphIndex(&d->font, ch); 231 }
232 iGlyphTable *table = d->table;
233 if (entry < iElemCount(table->indexTable)) {
234 if (table->indexTable[entry] == ~0u) {
235 table->indexTable[entry] = findGlyphIndex_FontFile(d->fontFile, ch);
265 } 236 }
266 return d->indexTable[entry]; 237 return table->indexTable[entry];
267 } 238 }
268 return stbtt_FindGlyphIndex(&d->font, ch); 239 return findGlyphIndex_FontFile(d->fontFile, ch);
269} 240}
270 241
271/*----------------------------------------------------------------------------------------------*/ 242/*----------------------------------------------------------------------------------------------*/
@@ -282,7 +253,8 @@ struct Impl_Text {
282 enum iTextFont contentFont; 253 enum iTextFont contentFont;
283 enum iTextFont headingFont; 254 enum iTextFont headingFont;
284 float contentFontSize; 255 float contentFontSize;
285 iFont fonts[max_FontId]; 256 iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */
257 int overrideFontId; /* always checked for glyphs first, regardless of which font is used */
286 SDL_Renderer * render; 258 SDL_Renderer * render;
287 SDL_Texture * cache; 259 SDL_Texture * cache;
288 iInt2 cacheSize; 260 iInt2 cacheSize;
@@ -296,12 +268,49 @@ struct Impl_Text {
296iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) 268iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render)
297 269
298static iText *activeText_; 270static iText *activeText_;
299static iBlock *userFont_; 271
272static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId) {
273#if defined (iPlatformMobile)
274 const float uiSize = fontSize_UI * 1.1f;
275#else
276 const float uiSize = fontSize_UI;
277#endif
278 const float textSize = fontSize_UI * d->contentFontSize;
279// const float monoSize = textSize * 0.71f;
280// const float smallMonoSize = monoSize * 0.8f;
281 if (spec->flags & override_FontSpecFlag && d->overrideFontId < 0) {
282 /* This is the highest priority override font. */
283 d->overrideFontId = baseId;
284 }
285 for (enum iFontStyle style = 0; style < max_FontStyle; style++) {
286 for (enum iFontSize sizeId = 0; sizeId < max_FontSize; sizeId++) {
287 init_Font(font_Text_(FONT_ID(baseId, style, sizeId)),
288 spec,
289 spec->styles[style],
290 sizeId,
291 (sizeId < contentRegular_FontSize ? uiSize : textSize) * scale_FontSize(sizeId));
292 }
293 }
294}
295
296iLocalDef iFont *font_Text_(enum iFontId id) {
297 return at_Array(&activeText_->fonts, id & mask_FontId);
298}
299
300static enum iFontId fontId_Text_(const iFont *font) {
301 return (enum iFontId) (font - (const iFont *) constData_Array(&activeText_->fonts));
302}
303
304iLocalDef enum iFontSize sizeId_Text_(const iFont *d) {
305 return fontId_Text_(d) % max_FontSize;
306}
307
308iLocalDef enum iFontStyle styleId_Text_(const iFont *d) {
309 return (fontId_Text_(d) / max_FontSize) % max_FontStyle;
310}
300 311
301static void initFonts_Text_(iText *d) { 312static void initFonts_Text_(iText *d) {
302 const float textSize = fontSize_UI * d->contentFontSize; 313#if 0
303 const float monoSize = textSize * 0.71f;
304 const float smallMonoSize = monoSize * 0.8f;
305 const iBlock *regularFont = &fontNunitoRegular_Embedded; 314 const iBlock *regularFont = &fontNunitoRegular_Embedded;
306 const iBlock *boldFont = &fontNunitoBold_Embedded; 315 const iBlock *boldFont = &fontNunitoBold_Embedded;
307 const iBlock *italicFont = &fontNunitoLightItalic_Embedded; 316 const iBlock *italicFont = &fontNunitoLightItalic_Embedded;
@@ -367,15 +376,10 @@ static void initFonts_Text_(iText *d) {
367 h12Font = &fontIosevkaTermExtended_Embedded; 376 h12Font = &fontIosevkaTermExtended_Embedded;
368 h3Font = &fontIosevkaTermExtended_Embedded; 377 h3Font = &fontIosevkaTermExtended_Embedded;
369 } 378 }
370#if defined (iPlatformMobile)
371 const float uiSize = fontSize_UI * 1.1f;
372#else
373 const float uiSize = fontSize_UI;
374#endif
375 const struct { 379 const struct {
376 const iBlock *ttf; 380 const iFontFile *fontFile;
377 int size; 381 int size; /* pixels */
378 float scaling; 382// float scaling;
379 enum iFontSize sizeId; 383 enum iFontSize sizeId;
380 /* UI sizes: 1.0, 1.125, 1.333, 1.666 */ 384 /* UI sizes: 1.0, 1.125, 1.333, 1.666 */
381 /* Content sizes: smallmono, mono, 1.0, 1.2, 1.333, 1.666, 2.0 */ 385 /* Content sizes: smallmono, mono, 1.0, 1.2, 1.333, 1.666, 2.0 */
@@ -433,8 +437,34 @@ static void initFonts_Text_(iText *d) {
433 DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f), 437 DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f),
434// DEFINE_FONT_SET(&fontScheherazadeNewRegular_Embedded, 1.0f), 438// DEFINE_FONT_SET(&fontScheherazadeNewRegular_Embedded, 1.0f),
435 }; 439 };
440#endif
441 /* The `fonts` array has precomputed scaling factors and other parameters in all sizes
442 and styles for each available font. Indices to `fonts` act as font runtime IDs. */
443 /* First the mandatory fonts. */
444 d->overrideFontId = -1;
445 resize_Array(&d->fonts, auxiliary_FontId); /* room for the built-ins */
446 iAssert(auxiliary_FontId == documentHeading_FontId + maxVariants_Fonts);
447 setupFontVariants_Text_(d, findSpec_Fonts("default"), default_FontId);
448 setupFontVariants_Text_(d, findSpec_Fonts("iosevka"), monospace_FontId);
449 setupFontVariants_Text_(d, findSpec_Fonts("default"), documentBody_FontId);
450 setupFontVariants_Text_(d, findSpec_Fonts("default"), documentHeading_FontId);
451 /* Check if there are auxiliary fonts available and set those up, too. */
452 iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) {
453 const iFontSpec *spec = s.ptr;
454 if (spec->flags & auxiliary_FontSpecFlag) {
455 const int fontId = size_Array(&d->fonts);
456 resize_Array(&d->fonts, fontId + maxVariants_Fonts);
457 setupFontVariants_Text_(d, spec, fontId);
458 }
459 }
460 /* test */ {
461 const iFont *h = font_Text_(preformatted_FontId); // FONT_ID(documentBody_FontId, regular_FontStyle, contentRegular_FontSize));
462 printf("{%s} %d sz:%d st:%d\n", cstr_String(&h->fontSpec->name), h->height, sizeId_Text_(h),
463 styleId_Text_(h));
464 }
465#if 0
436 iForIndices(i, fontData) { 466 iForIndices(i, fontData) {
437 iFont *font = &d->fonts[i]; 467 iFont *font = font_Text_(i);
438 init_Font(font, 468 init_Font(font,
439 fontData[i].ttf, 469 fontData[i].ttf,
440 fontData[i].size, 470 fontData[i].size,
@@ -442,13 +472,15 @@ static void initFonts_Text_(iText *d) {
442 fontData[i].sizeId, 472 fontData[i].sizeId,
443 fontData[i].ttf == &fontIosevkaTermExtended_Embedded); 473 fontData[i].ttf == &fontIosevkaTermExtended_Embedded);
444 } 474 }
475#endif
445 gap_Text = iRound(gap_UI * d->contentFontSize); 476 gap_Text = iRound(gap_UI * d->contentFontSize);
446} 477}
447 478
448static void deinitFonts_Text_(iText *d) { 479static void deinitFonts_Text_(iText *d) {
449 iForIndices(i, d->fonts) { 480 iForEach(Array, i, &d->fonts) {
450 deinit_Font(&d->fonts[i]); 481 deinit_Font(i.value);
451 } 482 }
483 clear_Array(&d->fonts);
452} 484}
453 485
454static int maxGlyphHeight_Text_(const iText *d) { 486static int maxGlyphHeight_Text_(const iText *d) {
@@ -490,6 +522,7 @@ static void deinitCache_Text_(iText *d) {
490 SDL_DestroyTexture(d->cache); 522 SDL_DestroyTexture(d->cache);
491} 523}
492 524
525#if 0
493void loadUserFonts_Text(void) { 526void loadUserFonts_Text(void) {
494 if (userFont_) { 527 if (userFont_) {
495 delete_Block(userFont_); 528 delete_Block(userFont_);
@@ -508,9 +541,12 @@ void loadUserFonts_Text(void) {
508 iRelease(f); 541 iRelease(f);
509 } 542 }
510} 543}
544#endif
511 545
512void init_Text(iText *d, SDL_Renderer *render) { 546void init_Text(iText *d, SDL_Renderer *render) {
513 loadUserFonts_Text(); 547 iText *oldActive = activeText_;
548 activeText_ = d;
549 init_Array(&d->fonts, sizeof(iFont));
514 d->contentFont = nunito_TextFont; 550 d->contentFont = nunito_TextFont;
515 d->headingFont = nunito_TextFont; 551 d->headingFont = nunito_TextFont;
516 d->contentFontSize = contentScale_Text_; 552 d->contentFontSize = contentScale_Text_;
@@ -526,6 +562,7 @@ void init_Text(iText *d, SDL_Renderer *render) {
526 } 562 }
527 initCache_Text_(d); 563 initCache_Text_(d);
528 initFonts_Text_(d); 564 initFonts_Text_(d);
565 activeText_ = oldActive;
529} 566}
530 567
531void deinit_Text(iText *d) { 568void deinit_Text(iText *d) {
@@ -534,6 +571,7 @@ void deinit_Text(iText *d) {
534 deinitCache_Text_(d); 571 deinitCache_Text_(d);
535 d->render = NULL; 572 d->render = NULL;
536 iRelease(d->ansiEscape); 573 iRelease(d->ansiEscape);
574 deinit_Array(&d->fonts);
537} 575}
538 576
539void setCurrent_Text(iText *d) { 577void setCurrent_Text(iText *d) {
@@ -569,8 +607,8 @@ void setContentFontSize_Text(iText *d, float fontSizeFactor) {
569 607
570static void resetCache_Text_(iText *d) { 608static void resetCache_Text_(iText *d) {
571 deinitCache_Text_(d); 609 deinitCache_Text_(d);
572 for (int i = 0; i < max_FontId; i++) { 610 iForEach(Array, i, &d->fonts) {
573 clearGlyphs_Font_(&d->fonts[i]); 611 clearGlyphs_Font_(i.value);
574 } 612 }
575 initCache_Text_(d); 613 initCache_Text_(d);
576} 614}
@@ -582,14 +620,10 @@ void resetFonts_Text(iText *d) {
582 initFonts_Text_(d); 620 initFonts_Text_(d);
583} 621}
584 622
585iLocalDef iFont *font_Text_(enum iFontId id) {
586 return &activeText_->fonts[id & mask_FontId];
587}
588
589static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { 623static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) {
590 int w, h; 624 int w, h;
591 uint8_t *bmp = stbtt_GetGlyphBitmapSubpixel( 625 uint8_t *bmp = rasterizeGlyph_FontFile(d->fontFile, d->xScale, d->yScale, xShift, glyphIndex,
592 &d->font, d->xScale, d->yScale, xShift, 0.0f, glyphIndex, &w, &h, 0, 0); 626 &w, &h);
593 SDL_Surface *surface8 = 627 SDL_Surface *surface8 =
594 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8); 628 SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8);
595 SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE); 629 SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE);
@@ -636,8 +670,8 @@ static iInt2 assignCachePos_Text_(iText *d, iInt2 size) {
636static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { 670static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
637 iRect *glRect = &glyph->rect[hoff]; 671 iRect *glRect = &glyph->rect[hoff];
638 int x0, y0, x1, y1; 672 int x0, y0, x1, y1;
639 stbtt_GetGlyphBitmapBoxSubpixel( 673 measureGlyph_FontFile(d->fontFile, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f,
640 &d->font, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f, 0.0f, &x0, &y0, &x1, &y1); 674 &x0, &y0, &x1, &y1);
641 glRect->size = init_I2(x1 - x0, y1 - y0); 675 glRect->size = init_I2(x1 - x0, y1 - y0);
642 /* Determine placement in the glyph cache texture, advancing in rows. */ 676 /* Determine placement in the glyph cache texture, advancing in rows. */
643 glRect->pos = assignCachePos_Text_(activeText_, glRect->size); 677 glRect->pos = assignCachePos_Text_(activeText_, glRect->size);
@@ -645,7 +679,7 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
645 glyph->d[hoff].y += d->vertOffset; 679 glyph->d[hoff].y += d->vertOffset;
646 if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */ 680 if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */
647 int adv; 681 int adv;
648 stbtt_GetGlyphHMetrics(&d->font, index_Glyph_(glyph), &adv, NULL); 682 stbtt_GetGlyphHMetrics(&d->fontFile->stbInfo, index_Glyph_(glyph), &adv, NULL);
649 glyph->advance = d->xScale * adv; 683 glyph->advance = d->xScale * adv;
650 } 684 }
651} 685}
@@ -654,13 +688,18 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
654 if (isVariationSelector_Char(ch)) { 688 if (isVariationSelector_Char(ch)) {
655 return d; 689 return d;
656 } 690 }
657 /* Smol Emoji overrides all other fonts. */ 691 const enum iFontStyle styleId = styleId_Text_(d);
658 if (ch != 0x20) { 692 const enum iFontSize sizeId = sizeId_Text_(d);
659 iFont *smol = font_Text_(smolEmoji_FontId + d->sizeId); 693 iFont *overrideFont = NULL;
660 if (smol != d && (*glyphIndex = glyphIndex_Font_(smol, ch)) != 0) { 694 if (ch != 0x20 && activeText_->overrideFontId >= 0) {
661 return smol; 695 /* Override font is checked first. */
696 overrideFont = font_Text_(FONT_ID(activeText_->overrideFontId, styleId, sizeId));
697 if (overrideFont != d && (*glyphIndex = glyphIndex_Font_(overrideFont, ch)) != 0) {
698 return overrideFont;
662 } 699 }
663 } 700 }
701#if 0
702 /* TODO: Put arrows in Smol Emoji. */
664 /* Manual exceptions. */ { 703 /* Manual exceptions. */ {
665 if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) { 704 if (ch >= 0x2190 && ch <= 0x2193 /* arrows */) {
666 d = font_Text_(iosevka_FontId + d->sizeId); 705 d = font_Text_(iosevka_FontId + d->sizeId);
@@ -668,9 +707,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
668 return d; 707 return d;
669 } 708 }
670 } 709 }
710#endif
711 /* The font's own version of the glyph. */
671 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { 712 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) {
672 return d; 713 return d;
673 } 714 }
715 /* As a fallback, check all other available fonts of this size. */
716 for (iFont *font = font_Text_(FONT_ID(0, styleId, sizeId));
717 font < (iFont *) end_Array(&activeText_->fonts);
718 font += maxVariants_Fonts) {
719 if (font == d || font == overrideFont) {
720 continue; /* already checked this one */
721 }
722 if ((*glyphIndex = glyphIndex_Font_(font, ch)) != 0) {
723 return font;
724 }
725 }
726#if 0
674 const int fallbacks[] = { 727 const int fallbacks[] = {
675 notoEmoji_FontId, 728 notoEmoji_FontId,
676 symbols2_FontId, 729 symbols2_FontId,
@@ -732,6 +785,7 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
732 if (d != font) { 785 if (d != font) {
733 *glyphIndex = glyphIndex_Font_(font, ch); 786 *glyphIndex = glyphIndex_Font_(font, ch);
734 } 787 }
788#endif // 0
735 if (!*glyphIndex) { 789 if (!*glyphIndex) {
736 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); 790 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr);
737 } 791 }
@@ -739,8 +793,9 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
739} 793}
740 794
741static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { 795static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) {
796 iAssert(d->table);
742 iGlyph* glyph = NULL; 797 iGlyph* glyph = NULL;
743 void * node = value_Hash(&d->glyphs, glyphIndex); 798 void * node = value_Hash(&d->table->glyphs, glyphIndex);
744 if (node) { 799 if (node) {
745 glyph = node; 800 glyph = node;
746 } 801 }
@@ -758,7 +813,7 @@ static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) {
758 and updates the glyph metrics. */ 813 and updates the glyph metrics. */
759 allocate_Font_(d, glyph, 0); 814 allocate_Font_(d, glyph, 0);
760 allocate_Font_(d, glyph, 1); 815 allocate_Font_(d, glyph, 1);
761 insert_Hash(&d->glyphs, &glyph->node); 816 insert_Hash(&d->table->glyphs, &glyph->node);
762 } 817 }
763 return glyph; 818 return glyph;
764} 819}
@@ -866,10 +921,6 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, i
866 run->logical.start = endAt; 921 run->logical.start = endAt;
867} 922}
868 923
869static enum iFontId fontId_Text_(const iFont *font) {
870 return (enum iFontId) (font - activeText_->fonts);
871}
872
873static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { 924static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) {
874 iAssert(isEmpty_Array(&d->runs)); 925 iAssert(isEmpty_Array(&d->runs));
875 size_t length = 0; 926 size_t length = 0;
@@ -997,14 +1048,14 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh
997 continue; 1048 continue;
998 } 1049 }
999 if (ch == 0x20) { 1050 if (ch == 0x20) {
1000 if (run.font->family == emojiAndSymbols_TextFont) { 1051 if (run.font->fontSpec->flags & auxiliary_FontSpecFlag) {
1001 finishRun_AttributedText_(d, &run, pos); 1052 finishRun_AttributedText_(d, &run, pos);
1002 run.font = d->font; /* never use space from the symbols font, it's too wide */ 1053 run.font = d->font; /* never use space from the symbols font, it's too wide */
1003 } 1054 }
1004 continue; 1055 continue;
1005 } 1056 }
1006 iFont *currentFont = d->font; 1057 iFont *currentFont = d->font;
1007 if (run.font->family == arabic_TextFont && isPunct_Char(ch)) { 1058 if (run.font->fontSpec->flags & arabic_FontSpecFlag && isPunct_Char(ch)) {
1008 currentFont = run.font; /* remain as Arabic for whitespace */ 1059 currentFont = run.font; /* remain as Arabic for whitespace */
1009 } 1060 }
1010 const iGlyph *glyph = glyph_Font_(currentFont, ch); 1061 const iGlyph *glyph = glyph_Font_(currentFont, ch);
@@ -1283,7 +1334,7 @@ static iBool notify_WrapText_(iWrapText *d, const char *ending, int origin, int
1283 1334
1284float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) { 1335float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) {
1285#if defined (LAGRANGE_ENABLE_KERNING) 1336#if defined (LAGRANGE_ENABLE_KERNING)
1286 if (!enableKerning_Text || d->family != nunito_TextFont) { 1337 if (!enableKerning_Text || ~d->fontSpec->flags & fixNunitoKerning_FontSpecFlag) {
1287 return 0.0f; 1338 return 0.0f;
1288 } 1339 }
1289 if (glyph1 && glyph2) { 1340 if (glyph1 && glyph2) {
@@ -1335,7 +1386,7 @@ static void deinit_GlyphBuffer_(iGlyphBuffer *d) {
1335 1386
1336static void shape_GlyphBuffer_(iGlyphBuffer *d) { 1387static void shape_GlyphBuffer_(iGlyphBuffer *d) {
1337 if (!d->glyphInfo) { 1388 if (!d->glyphInfo) {
1338 hb_shape(d->font->hbFont, d->hb, NULL, 0); 1389 hb_shape(d->font->fontFile->hbFont, d->hb, NULL, 0);
1339 d->glyphInfo = hb_buffer_get_glyph_infos(d->hb, &d->glyphCount); 1390 d->glyphInfo = hb_buffer_get_glyph_infos(d->hb, &d->glyphCount);
1340 d->glyphPos = hb_buffer_get_glyph_positions(d->hb, &d->glyphCount); 1391 d->glyphPos = hb_buffer_get_glyph_positions(d->hb, &d->glyphCount);
1341 } 1392 }
@@ -1389,7 +1440,7 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
1389 float xCursor = 0.0f; 1440 float xCursor = 0.0f;
1390 float yCursor = 0.0f; 1441 float yCursor = 0.0f;
1391 float xCursorMax = 0.0f; 1442 float xCursorMax = 0.0f;
1392 const iBool isMonospaced = d->isMonospaced; 1443 const iBool isMonospaced = isMonospaced_Font(d);
1393 iWrapText *wrap = args->wrap; 1444 iWrapText *wrap = args->wrap;
1394 iAssert(args->text.end >= args->text.start); 1445 iAssert(args->text.end >= args->text.start);
1395 /* Split the text into a number of attributed runs that specify exactly which 1446 /* Split the text into a number of attributed runs that specify exactly which
diff --git a/src/ui/text.h b/src/ui/text.h
index 1b3200d4..f76c8125 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -31,79 +31,92 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
31 31
32/* Content sizes: regular (1x) -> medium (1.2x) -> big (1.33x) -> large (1.67x) -> huge (2x) */ 32/* Content sizes: regular (1x) -> medium (1.2x) -> big (1.33x) -> large (1.67x) -> huge (2x) */
33 33
34#define FONT_ID(name, style, size) ((name) + ((style) * max_FontSize) + (size))
35
34enum iFontId { 36enum iFontId {
35 /* UI fonts: normal weight (1x, 1.125x, 1.33x, 1.67x) */ 37 default_FontId = 0, /* default is always the first font */
36 default_FontId = 0, 38 monospace_FontId = maxVariants_Fonts, /* 2nd font is always the monospace font */
37 defaultMedium_FontId, 39 documentBody_FontId = maxVariants_Fonts * 2, /* 3rd font is the body font */
38 defaultBig_FontId, 40 documentHeading_FontId = maxVariants_Fonts * 3, /* heading font */
39 defaultLarge_FontId, 41 auxiliary_FontId = maxVariants_Fonts * 4, /* the first auxiliary font (e.g., symbols) */
40 defaultTiny_FontId, 42
41 defaultSmall_FontId, 43 // defaultMedium_FontId,
44// defaultBig_FontId,
45// defaultLarge_FontId,
46// defaultTiny_FontId,
47// defaultSmall_FontId,
42 /* UI fonts: bold weight */ 48 /* UI fonts: bold weight */
43 defaultBold_FontId, 49// defaultBold_FontId,
44 defaultMediumBold_FontId, 50// defaultMediumBold_FontId,
45 defaultBigBold_FontId, 51// defaultBigBold_FontId,
46 defaultLargeBold_FontId, 52// defaultLargeBold_FontId,
47 /* content fonts */ 53 /* content fonts */
48 regular_FontId, 54// bold_FontId,
49 bold_FontId, 55// italic_FontId,
50 italic_FontId, 56// medium_FontId,
51 medium_FontId, 57// big_FontId,
52 big_FontId, 58// largeBold_FontId,
53 largeBold_FontId, 59// largeLight_FontId,
54 largeLight_FontId, 60// hugeBold_FontId,
55 hugeBold_FontId, 61// monospaceSmall_FontId,
56 monospaceSmall_FontId,
57 monospace_FontId,
58 /* extra content fonts */ 62 /* extra content fonts */
59 defaultContentRegular_FontId, /* UI font but sized to regular_FontId */ 63// defaultContentRegular_FontId, /* UI font but sized to regular_FontId */
60 defaultContentSmall_FontId, /* UI font but sized smaller */ 64// defaultContentSmall_FontId, /* UI font but sized smaller */
61 /* symbols and scripts */ 65 /* symbols and scripts */
62 userSymbols_FontId, 66// userSymbols_FontId,
63 iosevka_FontId = userSymbols_FontId + max_FontSize, 67// iosevka_FontId = userSymbols_FontId + max_FontSize,
64 symbols_FontId = iosevka_FontId + max_FontSize, 68// symbols_FontId = iosevka_FontId + max_FontSize,
65 symbols2_FontId = symbols_FontId + max_FontSize, 69// symbols2_FontId = symbols_FontId + max_FontSize,
66 smolEmoji_FontId = symbols2_FontId + max_FontSize, 70// smolEmoji_FontId = symbols2_FontId + max_FontSize,
67 notoEmoji_FontId = smolEmoji_FontId + max_FontSize, 71// notoEmoji_FontId = smolEmoji_FontId + max_FontSize,
68 japanese_FontId = notoEmoji_FontId + max_FontSize, 72// japanese_FontId = notoEmoji_FontId + max_FontSize,
69 chineseSimplified_FontId = japanese_FontId + max_FontSize, 73// chineseSimplified_FontId = japanese_FontId + max_FontSize,
70 korean_FontId = chineseSimplified_FontId + max_FontSize, 74// korean_FontId = chineseSimplified_FontId + max_FontSize,
71 arabic_FontId = korean_FontId + max_FontSize, 75// arabic_FontId = korean_FontId + max_FontSize,
72 max_FontId = arabic_FontId + max_FontSize, 76// max_FontId = arabic_FontId + max_FontSize,
73 77
74 /* Meta: */ 78 /* Meta: */
75 mask_FontId = 0xffff, 79 mask_FontId = 0x0000ffff, /* font IDs are 16-bit; see GmRun's packing */
76 alwaysVariableFlag_FontId = 0x10000, 80 alwaysVariableFlag_FontId = 0x00010000,
77 81
78 /* UI fonts: */ 82 /* UI fonts: */
79 uiLabel_FontId = default_FontId, 83 uiLabelTiny_FontId = FONT_ID(default_FontId, semiBold_FontStyle, uiTiny_FontSize),
80 uiLabelBold_FontId = defaultBold_FontId, 84 uiLabelSmall_FontId = FONT_ID(default_FontId, regular_FontStyle, uiSmall_FontSize),
81 uiLabelLarge_FontId = defaultLarge_FontId, 85 uiLabel_FontId = FONT_ID(default_FontId, regular_FontStyle, uiNormal_FontSize),
82 uiLabelLargeBold_FontId = defaultLargeBold_FontId, 86 uiLabelMedium_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize),
83 uiShortcuts_FontId = default_FontId, 87 uiLabelMediumBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiMedium_FontSize),
84 uiInput_FontId = defaultMedium_FontId, 88 uiLabelBig_FontId = FONT_ID(default_FontId, regular_FontStyle, uiBig_FontSize),
85 uiContent_FontId = defaultMedium_FontId, 89 uiLabelBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiNormal_FontSize),
86 uiContentBold_FontId = defaultMediumBold_FontId, 90 uiLabelBigBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiBig_FontSize),
87 uiContentSymbols_FontId = symbols_FontId + uiMedium_FontSize, 91 uiLabelLarge_FontId = FONT_ID(default_FontId, regular_FontStyle, uiLarge_FontSize),
92 uiLabelLargeBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiLarge_FontSize),
93 uiLabelSymbols_FontId = FONT_ID(auxiliary_FontId, regular_FontStyle, uiNormal_FontSize),
94 uiShortcuts_FontId = FONT_ID(default_FontId, regular_FontStyle, uiNormal_FontSize),
95 uiInput_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize),
96 uiContent_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize),
97 uiContentBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiMedium_FontSize),
98 uiContentSymbols_FontId = FONT_ID(auxiliary_FontId, regular_FontStyle, uiMedium_FontSize),
88 /* Document fonts: */ 99 /* Document fonts: */
89 paragraph_FontId = regular_FontId, 100 paragraph_FontId = FONT_ID(documentBody_FontId, regular_FontStyle, contentRegular_FontSize),
90 firstParagraph_FontId = medium_FontId, 101 bold_FontId = FONT_ID(documentBody_FontId, semiBold_FontStyle, contentRegular_FontSize),
91 preformatted_FontId = monospace_FontId, 102 firstParagraph_FontId = FONT_ID(documentBody_FontId, regular_FontStyle, contentMedium_FontSize),
92 preformattedSmall_FontId = monospaceSmall_FontId, 103 preformatted_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentMono_FontSize),
93 quote_FontId = italic_FontId, 104 preformattedSmall_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentMonoSmall_FontSize),
94 heading1_FontId = hugeBold_FontId, 105 quote_FontId = FONT_ID(documentBody_FontId, italic_FontStyle, contentRegular_FontSize),
95 heading2_FontId = largeBold_FontId, 106 heading1_FontId = FONT_ID(documentHeading_FontId, bold_FontStyle, contentHuge_FontSize),
96 heading3_FontId = big_FontId, 107 heading2_FontId = FONT_ID(documentHeading_FontId, bold_FontStyle, contentLarge_FontSize),
97 banner_FontId = largeLight_FontId, 108 heading3_FontId = FONT_ID(documentHeading_FontId, regular_FontStyle, contentBig_FontSize),
98 regularMonospace_FontId = iosevka_FontId + contentRegular_FontSize 109 banner_FontId = FONT_ID(documentHeading_FontId, light_FontStyle, contentLarge_FontSize),
110 regularMonospace_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentRegular_FontSize),
99}; 111};
100 112
101iLocalDef iBool isJapanese_FontId(enum iFontId id) { 113//iLocalDef iBool isJapanese_FontId(enum iFontId id) {
102 return id >= japanese_FontId && id < japanese_FontId + max_FontSize; 114// return id >= japanese_FontId && id < japanese_FontId + max_FontSize;
103} 115//}
104 116
105#define emojiVariationSelector_Char ((iChar) 0xfe0f) 117#define emojiVariationSelector_Char ((iChar) 0xfe0f)
106 118
119/* TODO: get rid of this; configure using font ID strings, check RTL from FontFile flags */
107enum iTextFont { 120enum iTextFont {
108 undefined_TextFont = -1, 121 undefined_TextFont = -1,
109 nunito_TextFont = 0, 122 nunito_TextFont = 0,
@@ -127,7 +140,7 @@ void deinit_Text (iText *);
127 140
128void setCurrent_Text (iText *); 141void setCurrent_Text (iText *);
129 142
130void loadUserFonts_Text (void); /* based on Prefs */ 143//void loadUserFonts_Text (void); /* based on Prefs */
131 144
132void setContentFont_Text (iText *, enum iTextFont font); 145void setContentFont_Text (iText *, enum iTextFont font);
133void setHeadingFont_Text (iText *, enum iTextFont font); 146void setHeadingFont_Text (iText *, enum iTextFont font);
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index ba7545fd..ded8d7f8 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -255,7 +255,7 @@ void init_UploadWidget(iUploadWidget *d) {
255 setFlags_Widget(as_Widget(d->token), expand_WidgetFlag, iTrue); 255 setFlags_Widget(as_Widget(d->token), expand_WidgetFlag, iTrue);
256 setFocus_Widget(as_Widget(d->input)); 256 setFocus_Widget(as_Widget(d->input));
257 } 257 }
258 setFont_InputWidget(d->input, iosevka_FontId); 258 setFont_InputWidget(d->input, FONT_ID(monospace_FontId, regular_FontStyle, uiNormal_FontSize));
259 setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */ 259 setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */
260 setLineLimits_InputWidget(d->input, 7, 20); 260 setLineLimits_InputWidget(d->input, 7, 20);
261 setHint_InputWidget(d->input, "${hint.upload.text}"); 261 setHint_InputWidget(d->input, "${hint.upload.text}");
diff --git a/src/ui/util.c b/src/ui/util.c
index adca6269..a4411ea5 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -873,7 +873,7 @@ static void updateMenuItemFonts_Widget_(iWidget *d) {
873 } 873 }
874 else if (isPortraitPhone) { 874 else if (isPortraitPhone) {
875 if (!isSlidePanel) { 875 if (!isSlidePanel) {
876 setFont_LabelWidget(label, isCaution ? defaultBigBold_FontId : defaultBig_FontId); 876 setFont_LabelWidget(label, isCaution ? uiLabelBigBold_FontId : uiLabelBig_FontId);
877 } 877 }
878 } 878 }
879 else { 879 else {
@@ -1626,8 +1626,8 @@ iWidget *makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions) {
1626 } 1626 }
1627 int fonts[2] = { uiLabel_FontId, uiLabelBold_FontId }; 1627 int fonts[2] = { uiLabel_FontId, uiLabelBold_FontId };
1628 if (deviceType_App() == phone_AppDeviceType) { 1628 if (deviceType_App() == phone_AppDeviceType) {
1629 fonts[0] = defaultMedium_FontId; 1629 fonts[0] = uiLabelMedium_FontId;
1630 fonts[1] = defaultMediumBold_FontId; 1630 fonts[1] = uiLabelMediumBold_FontId;
1631 } 1631 }
1632 for (size_t i = 0; i < numActions; i++) { 1632 for (size_t i = 0; i < numActions; i++) {
1633 const char *label = actions[i].label; 1633 const char *label = actions[i].label;
@@ -1694,7 +1694,7 @@ iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, con
1694 resizeToParentWidth_WidgetFlag); 1694 resizeToParentWidth_WidgetFlag);
1695 setContentPadding_InputWidget(input, 0.5f * gap_UI, 0.5f * gap_UI); 1695 setContentPadding_InputWidget(input, 0.5f * gap_UI, 0.5f * gap_UI);
1696 if (deviceType_App() == phone_AppDeviceType) { 1696 if (deviceType_App() == phone_AppDeviceType) {
1697 setFont_InputWidget(input, defaultBig_FontId); 1697 setFont_InputWidget(input, uiLabelBig_FontId);
1698 setBackgroundColor_Widget(dlg, uiBackgroundSidebar_ColorId); 1698 setBackgroundColor_Widget(dlg, uiBackgroundSidebar_ColorId);
1699 setContentPadding_InputWidget(input, gap_UI, gap_UI); 1699 setContentPadding_InputWidget(input, gap_UI, gap_UI);
1700 } 1700 }
@@ -1811,7 +1811,7 @@ iWidget *makeQuestion_Widget(const char *title, const char *msg,
1811 resizeToParentWidth_WidgetFlag | 1811 resizeToParentWidth_WidgetFlag |
1812 (first == '&' ? selected_WidgetFlag : 0)); 1812 (first == '&' ? selected_WidgetFlag : 0));
1813 if (deviceType_App() != desktop_AppDeviceType) { 1813 if (deviceType_App() != desktop_AppDeviceType) {
1814 setFont_LabelWidget(option, defaultBig_FontId); 1814 setFont_LabelWidget(option, uiLabelBig_FontId);
1815 } 1815 }
1816 } 1816 }
1817 } 1817 }
diff --git a/src/ui/window.c b/src/ui/window.c
index 686a6dd6..ea783331 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1188,7 +1188,7 @@ void draw_Window(iWindow *d) {
1188 extern int drawCount_; 1188 extern int drawCount_;
1189 drawRoot_Widget(root->widget); 1189 drawRoot_Widget(root->widget);
1190#if !defined (NDEBUG) 1190#if !defined (NDEBUG)
1191 draw_Text(defaultBold_FontId, safeRect_Root(root).pos, red_ColorId, "%d", drawCount_); 1191 draw_Text(uiLabelBold_FontId, safeRect_Root(root).pos, red_ColorId, "%d", drawCount_);
1192 drawCount_ = 0; 1192 drawCount_ = 0;
1193#endif 1193#endif
1194 } 1194 }
@@ -1287,7 +1287,7 @@ void draw_MainWindow(iMainWindow *d) {
1287 } 1287 }
1288 setCurrent_Root(NULL); 1288 setCurrent_Root(NULL);
1289#if !defined (NDEBUG) 1289#if !defined (NDEBUG)
1290 draw_Text(defaultBold_FontId, safeRect_Root(w->roots[0]).pos, red_ColorId, "%d", drawCount_); 1290 draw_Text(uiLabelBold_FontId, safeRect_Root(w->roots[0]).pos, red_ColorId, "%d", drawCount_);
1291 drawCount_ = 0; 1291 drawCount_ = 0;
1292#endif 1292#endif
1293 } 1293 }