diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-06 12:16:43 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-06 12:16:43 +0300 |
commit | 61a3dc017067be43472dadb7909094aa04d1fe9d (patch) | |
tree | 18b87895489844b4e516f79cd1588038f1d49494 /src/ui | |
parent | f6a54d5375aab9c41af3f7c8a5e8fcbd1e0c9287 (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.h | 2 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 49 | ||||
-rw-r--r-- | src/ui/mediaui.c | 4 | ||||
-rw-r--r-- | src/ui/mobile.c | 8 | ||||
-rw-r--r-- | src/ui/root.c | 13 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 18 | ||||
-rw-r--r-- | src/ui/text.c | 337 | ||||
-rw-r--r-- | src/ui/text.h | 131 | ||||
-rw-r--r-- | src/ui/uploadwidget.c | 2 | ||||
-rw-r--r-- | src/ui/util.c | 10 | ||||
-rw-r--r-- | src/ui/window.c | 4 |
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) { | |||
1619 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { | 1620 | static 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 | ||
50 | static enum iFontId labelFont_(void) { | 50 | static 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 | ||
54 | static enum iFontId labelBoldFont_(void) { | 54 | static 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 | ||
58 | static void updatePanelSheetMetrics_(iWidget *sheet) { | 58 | static 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 | ||
124 | static iGlyph *glyph_Font_(iFont *d, iChar ch); | 124 | static iGlyph *glyph_Font_(iFont *d, iChar ch); |
125 | 125 | ||
126 | struct Impl_Font { | 126 | iDeclareType(GlyphTable) |
127 | iBlock * data; | 127 | |
128 | enum iTextFont family; | 128 | struct 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 | ||
147 | static iFont *font_Text_(enum iFontId id); | 135 | static void clearGlyphs_Font_(iGlyphTable *d) { |
148 | 136 | iForEach(Hash, i, &d->glyphs) { | |
149 | #if 0 | 137 | delete_Glyph((iGlyph *) i.value); |
150 | static 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 | ||
157 | static void init_Font(iFont *d, const iBlock *data, int height, float scale, | 142 | static 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 | |||
147 | static void deinit_GlyphTable(iGlyphTable *d) { | ||
148 | clearGlyphs_Font_(d); | ||
149 | deinit_Hash(&d->glyphs); | ||
150 | } | ||
151 | |||
152 | iDefineTypeConstruction(GlyphTable) | ||
153 | |||
154 | struct 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 | |||
165 | iLocalDef iBool isMonospaced_Font(const iFont *d) { | ||
166 | return (d->fontSpec->flags & monospace_FontSpecFlag) != 0; | ||
167 | } | ||
168 | |||
169 | static iFont *font_Text_(enum iFontId id); | ||
170 | |||
171 | static 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 | |||
238 | static 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 | ||
245 | static void deinit_Font(iFont *d) { | 222 | static 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 | ||
259 | static uint32_t glyphIndex_Font_(iFont *d, iChar ch) { | 226 | static 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 { | |||
296 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 268 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) |
297 | 269 | ||
298 | static iText *activeText_; | 270 | static iText *activeText_; |
299 | static iBlock *userFont_; | 271 | |
272 | static 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 | |||
296 | iLocalDef iFont *font_Text_(enum iFontId id) { | ||
297 | return at_Array(&activeText_->fonts, id & mask_FontId); | ||
298 | } | ||
299 | |||
300 | static enum iFontId fontId_Text_(const iFont *font) { | ||
301 | return (enum iFontId) (font - (const iFont *) constData_Array(&activeText_->fonts)); | ||
302 | } | ||
303 | |||
304 | iLocalDef enum iFontSize sizeId_Text_(const iFont *d) { | ||
305 | return fontId_Text_(d) % max_FontSize; | ||
306 | } | ||
307 | |||
308 | iLocalDef enum iFontStyle styleId_Text_(const iFont *d) { | ||
309 | return (fontId_Text_(d) / max_FontSize) % max_FontStyle; | ||
310 | } | ||
300 | 311 | ||
301 | static void initFonts_Text_(iText *d) { | 312 | static 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 | ||
448 | static void deinitFonts_Text_(iText *d) { | 479 | static 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 | ||
454 | static int maxGlyphHeight_Text_(const iText *d) { | 486 | static 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 | ||
493 | void loadUserFonts_Text(void) { | 526 | void 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 | ||
512 | void init_Text(iText *d, SDL_Renderer *render) { | 546 | void 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 | ||
531 | void deinit_Text(iText *d) { | 568 | void 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 | ||
539 | void setCurrent_Text(iText *d) { | 577 | void setCurrent_Text(iText *d) { |
@@ -569,8 +607,8 @@ void setContentFontSize_Text(iText *d, float fontSizeFactor) { | |||
569 | 607 | ||
570 | static void resetCache_Text_(iText *d) { | 608 | static 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 | ||
585 | iLocalDef iFont *font_Text_(enum iFontId id) { | ||
586 | return &activeText_->fonts[id & mask_FontId]; | ||
587 | } | ||
588 | |||
589 | static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) { | 623 | static 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) { | |||
636 | static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) { | 670 | static 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 | ||
741 | static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) { | 795 | static 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 | ||
869 | static enum iFontId fontId_Text_(const iFont *font) { | ||
870 | return (enum iFontId) (font - activeText_->fonts); | ||
871 | } | ||
872 | |||
873 | static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) { | 924 | static 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 | ||
1284 | float horizKern_Font_(iFont *d, uint32_t glyph1, uint32_t glyph2) { | 1335 | float 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 | ||
1336 | static void shape_GlyphBuffer_(iGlyphBuffer *d) { | 1387 | static 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 | |||
34 | enum iFontId { | 36 | enum 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 | ||
101 | iLocalDef 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 */ | ||
107 | enum iTextFont { | 120 | enum 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 | ||
128 | void setCurrent_Text (iText *); | 141 | void setCurrent_Text (iText *); |
129 | 142 | ||
130 | void loadUserFonts_Text (void); /* based on Prefs */ | 143 | //void loadUserFonts_Text (void); /* based on Prefs */ |
131 | 144 | ||
132 | void setContentFont_Text (iText *, enum iTextFont font); | 145 | void setContentFont_Text (iText *, enum iTextFont font); |
133 | void setHeadingFont_Text (iText *, enum iTextFont font); | 146 | void 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 | } |