summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.c161
-rw-r--r--src/fontpack.c112
-rw-r--r--src/fontpack.h16
-rw-r--r--src/gmdocument.c60
-rw-r--r--src/prefs.c41
-rw-r--r--src/prefs.h31
-rw-r--r--src/ui/documentwidget.c4
-rw-r--r--src/ui/labelwidget.c4
-rw-r--r--src/ui/root.c3
-rw-r--r--src/ui/text.c77
-rw-r--r--src/ui/text.h116
-rw-r--r--src/ui/util.c73
12 files changed, 397 insertions, 301 deletions
diff --git a/src/app.c b/src/app.c
index 3a96bd40..ae68324d 100644
--- a/src/app.c
+++ b/src/app.c
@@ -209,12 +209,16 @@ static iString *serializePrefs_App_(const iApp *d) {
209 } 209 }
210#endif 210#endif
211 } 211 }
212 appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.uiLanguage)); 212 appendFormat_String(str, "uilang id:%s\n", cstr_String(&d->prefs.strings[uiLanguage_PrefsString]));
213 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(as_Window(d->window))); 213 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(as_Window(d->window)));
214 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab); 214 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab);
215 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font); 215 appendFormat_String(str,
216 appendFormat_String(str, "font.user path:%s\n", cstr_String(&d->prefs.symbolFontPath)); 216 "font.set ui:%s heading:%s body:%s mono:%s monodoc:%s\n",
217 appendFormat_String(str, "headingfont.set arg:%d\n", d->prefs.headingFont); 217 cstr_String(&d->prefs.strings[uiFont_PrefsString]),
218 cstr_String(&d->prefs.strings[headingFont_PrefsString]),
219 cstr_String(&d->prefs.strings[bodyFont_PrefsString]),
220 cstr_String(&d->prefs.strings[monospaceFont_PrefsString]),
221 cstr_String(&d->prefs.strings[monospaceDocumentFont_PrefsString]));
218 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent); 222 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent);
219 appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling); 223 appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling);
220 appendFormat_String(str, "scrollspeed arg:%d type:%d\n", d->prefs.smoothScrollSpeed[keyboard_ScrollType], keyboard_ScrollType); 224 appendFormat_String(str, "scrollspeed arg:%d type:%d\n", d->prefs.smoothScrollSpeed[keyboard_ScrollType], keyboard_ScrollType);
@@ -247,15 +251,15 @@ static iString *serializePrefs_App_(const iApp *d) {
247 appendFormat_String(str, "doctheme.light.set arg:%d\n", d->prefs.docThemeLight); 251 appendFormat_String(str, "doctheme.light.set arg:%d\n", d->prefs.docThemeLight);
248 appendFormat_String(str, "saturation.set arg:%d\n", (int) ((d->prefs.saturation * 100) + 0.5f)); 252 appendFormat_String(str, "saturation.set arg:%d\n", (int) ((d->prefs.saturation * 100) + 0.5f));
249 appendFormat_String(str, "imagestyle.set arg:%d\n", d->prefs.imageStyle); 253 appendFormat_String(str, "imagestyle.set arg:%d\n", d->prefs.imageStyle);
250 appendFormat_String(str, "ca.file noset:1 path:%s\n", cstr_String(&d->prefs.caFile)); 254 appendFormat_String(str, "ca.file noset:1 path:%s\n", cstr_String(&d->prefs.strings[caFile_PrefsString]));
251 appendFormat_String(str, "ca.path path:%s\n", cstr_String(&d->prefs.caPath)); 255 appendFormat_String(str, "ca.path path:%s\n", cstr_String(&d->prefs.strings[caPath_PrefsString]));
252 appendFormat_String(str, "proxy.gemini address:%s\n", cstr_String(&d->prefs.geminiProxy)); 256 appendFormat_String(str, "proxy.gemini address:%s\n", cstr_String(&d->prefs.strings[geminiProxy_PrefsString]));
253 appendFormat_String(str, "proxy.gopher address:%s\n", cstr_String(&d->prefs.gopherProxy)); 257 appendFormat_String(str, "proxy.gopher address:%s\n", cstr_String(&d->prefs.strings[gopherProxy_PrefsString]));
254 appendFormat_String(str, "proxy.http address:%s\n", cstr_String(&d->prefs.httpProxy)); 258 appendFormat_String(str, "proxy.http address:%s\n", cstr_String(&d->prefs.strings[httpProxy_PrefsString]));
255#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT) 259#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT)
256 appendFormat_String(str, "downloads path:%s\n", cstr_String(&d->prefs.downloadDir)); 260 appendFormat_String(str, "downloads path:%s\n", cstr_String(&d->prefs.strings[downloadDir_PrefsString]));
257#endif 261#endif
258 appendFormat_String(str, "searchurl address:%s\n", cstr_String(&d->prefs.searchUrl)); 262 appendFormat_String(str, "searchurl address:%s\n", cstr_String(&d->prefs.strings[searchUrl_PrefsString]));
259 appendFormat_String(str, "translation.languages from:%d to:%d\n", d->prefs.langFrom, d->prefs.langTo); 263 appendFormat_String(str, "translation.languages from:%d to:%d\n", d->prefs.langFrom, d->prefs.langTo);
260 return str; 264 return str;
261} 265}
@@ -329,7 +333,7 @@ static void loadPrefs_App_(iApp *d) {
329 } 333 }
330 else if (equal_Command(cmd, "uilang")) { 334 else if (equal_Command(cmd, "uilang")) {
331 const char *id = cstr_Rangecc(range_Command(cmd, "id")); 335 const char *id = cstr_Rangecc(range_Command(cmd, "id"));
332 setCStr_String(&d->prefs.uiLanguage, id); 336 setCStr_String(&d->prefs.strings[uiLanguage_PrefsString], id);
333 setCurrent_Lang(id); 337 setCurrent_Lang(id);
334 } 338 }
335 else if (equal_Command(cmd, "ca.file") || equal_Command(cmd, "ca.path")) { 339 else if (equal_Command(cmd, "ca.file") || equal_Command(cmd, "ca.path")) {
@@ -360,7 +364,8 @@ static void loadPrefs_App_(iApp *d) {
360 } 364 }
361 if (!haveCA) { 365 if (!haveCA) {
362 /* Default CA setup. */ 366 /* Default CA setup. */
363 setCACertificates_TlsRequest(&d->prefs.caFile, &d->prefs.caPath); 367 setCACertificates_TlsRequest(&d->prefs.strings[caFile_PrefsString],
368 &d->prefs.strings[caPath_PrefsString]);
364 } 369 }
365#if !defined (LAGRANGE_ENABLE_CUSTOM_FRAME) 370#if !defined (LAGRANGE_ENABLE_CUSTOM_FRAME)
366 d->prefs.customFrame = iFalse; 371 d->prefs.customFrame = iFalse;
@@ -787,7 +792,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
787#endif 792#endif
788 init_Prefs(&d->prefs); 793 init_Prefs(&d->prefs);
789 init_SiteSpec(dataDir_App_()); 794 init_SiteSpec(dataDir_App_());
790 setCStr_String(&d->prefs.downloadDir, downloadDir_App_()); 795 setCStr_String(&d->prefs.strings[downloadDir_PrefsString], downloadDir_App_());
791 set_Atomic(&d->pendingRefresh, iFalse); 796 set_Atomic(&d->pendingRefresh, iFalse);
792 d->isRunning = iFalse; 797 d->isRunning = iFalse;
793 d->window = NULL; 798 d->window = NULL;
@@ -918,7 +923,7 @@ const iString *dataDir_App(void) {
918} 923}
919 924
920const iString *downloadDir_App(void) { 925const iString *downloadDir_App(void) {
921 return collect_String(cleaned_Path(&app_.prefs.downloadDir)); 926 return collect_String(cleaned_Path(&app_.prefs.strings[downloadDir_PrefsString]));
922} 927}
923 928
924const iString *downloadPathForUrl_App(const iString *url, const iString *mime) { 929const iString *downloadPathForUrl_App(const iString *url, const iString *mime) {
@@ -1517,13 +1522,13 @@ const iString *schemeProxy_App(iRangecc scheme) {
1517 iApp *d = &app_; 1522 iApp *d = &app_;
1518 const iString *proxy = NULL; 1523 const iString *proxy = NULL;
1519 if (equalCase_Rangecc(scheme, "gemini")) { 1524 if (equalCase_Rangecc(scheme, "gemini")) {
1520 proxy = &d->prefs.geminiProxy; 1525 proxy = &d->prefs.strings[geminiProxy_PrefsString];
1521 } 1526 }
1522 else if (equalCase_Rangecc(scheme, "gopher")) { 1527 else if (equalCase_Rangecc(scheme, "gopher")) {
1523 proxy = &d->prefs.gopherProxy; 1528 proxy = &d->prefs.strings[gopherProxy_PrefsString];
1524 } 1529 }
1525 else if (equalCase_Rangecc(scheme, "http") || equalCase_Rangecc(scheme, "https")) { 1530 else if (equalCase_Rangecc(scheme, "http") || equalCase_Rangecc(scheme, "https")) {
1526 proxy = &d->prefs.httpProxy; 1531 proxy = &d->prefs.strings[httpProxy_PrefsString];
1527 } 1532 }
1528 return isEmpty_String(proxy) ? NULL : proxy; 1533 return isEmpty_String(proxy) ? NULL : proxy;
1529} 1534}
@@ -1727,9 +1732,9 @@ static void updateColorThemeButton_(iLabelWidget *button, int theme) {
1727 updateDropdownSelection_LabelWidget(button, format_CStr(".set arg:%d", theme)); 1732 updateDropdownSelection_LabelWidget(button, format_CStr(".set arg:%d", theme));
1728} 1733}
1729 1734
1730static void updateFontButton_(iLabelWidget *button, int font) { 1735static void updateFontButton_(iLabelWidget *button, const iString *fontId) {
1731 if (!button) return; 1736 if (!button || isEmpty_String(fontId)) return;
1732 updateDropdownSelection_LabelWidget(button, format_CStr(".set arg:%d", font)); 1737 updateDropdownSelection_LabelWidget(button, format_CStr(":%s", cstr_String(fontId)));
1733} 1738}
1734 1739
1735static void updateImageStyleButton_(iLabelWidget *button, int style) { 1740static void updateImageStyleButton_(iLabelWidget *button, int style) {
@@ -1823,11 +1828,11 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
1823 return iFalse; 1828 return iFalse;
1824 } 1829 }
1825 else if (equal_Command(cmd, "font.set")) { 1830 else if (equal_Command(cmd, "font.set")) {
1826 updateFontButton_(findChild_Widget(d, "prefs.font"), arg_Command(cmd)); 1831 updateFontButton_(findChild_Widget(d, "prefs.font.ui"), string_Command(cmd, "ui"));
1827 return iFalse; 1832 updateFontButton_(findChild_Widget(d, "prefs.font.heading"), string_Command(cmd, "heading"));
1828 } 1833 updateFontButton_(findChild_Widget(d, "prefs.font.body"), string_Command(cmd, "body"));
1829 else if (equal_Command(cmd, "headingfont.set")) { 1834 updateFontButton_(findChild_Widget(d, "prefs.font.mono"), string_Command(cmd, "mono"));
1830 updateFontButton_(findChild_Widget(d, "prefs.headingfont"), arg_Command(cmd)); 1835 updateFontButton_(findChild_Widget(d, "prefs.font.monodoc"), string_Command(cmd, "monodoc"));
1831 return iFalse; 1836 return iFalse;
1832 } 1837 }
1833 else if (startsWith_CStr(cmd, "input.ended id:prefs.linespacing")) { 1838 else if (startsWith_CStr(cmd, "input.ended id:prefs.linespacing")) {
@@ -2040,11 +2045,12 @@ iBool willUseProxy_App(const iRangecc scheme) {
2040 2045
2041const iString *searchQueryUrl_App(const iString *queryStringUnescaped) { 2046const iString *searchQueryUrl_App(const iString *queryStringUnescaped) {
2042 iApp *d = &app_; 2047 iApp *d = &app_;
2043 if (isEmpty_String(&d->prefs.searchUrl)) { 2048 if (isEmpty_String(&d->prefs.strings[searchUrl_PrefsString])) {
2044 return collectNew_String(); 2049 return collectNew_String();
2045 } 2050 }
2046 const iString *escaped = urlEncode_String(queryStringUnescaped); 2051 const iString *escaped = urlEncode_String(queryStringUnescaped);
2047 return collectNewFormat_String("%s?%s", cstr_String(&d->prefs.searchUrl), cstr_String(escaped)); 2052 return collectNewFormat_String(
2053 "%s?%s", cstr_String(&d->prefs.strings[searchUrl_PrefsString]), cstr_String(escaped));
2048} 2054}
2049 2055
2050static void resetFonts_App_(iApp *d) { 2056static void resetFonts_App_(iApp *d) {
@@ -2074,9 +2080,10 @@ iBool handleCommand_App(const char *cmd) {
2074 } 2080 }
2075 else if (equal_Command(cmd, "uilang")) { 2081 else if (equal_Command(cmd, "uilang")) {
2076 const iString *lang = string_Command(cmd, "id"); 2082 const iString *lang = string_Command(cmd, "id");
2077 if (!equal_String(lang, &d->prefs.uiLanguage)) { 2083 iString *val = &d->prefs.strings[uiLanguage_PrefsString];
2078 set_String(&d->prefs.uiLanguage, lang); 2084 if (!equal_String(lang, val)) {
2079 setCurrent_Lang(cstr_String(&d->prefs.uiLanguage)); 2085 set_String(val, lang);
2086 setCurrent_Lang(cstr_String(val));
2080 postCommand_App("lang.changed"); 2087 postCommand_App("lang.changed");
2081 } 2088 }
2082 return iTrue; 2089 return iTrue;
@@ -2148,14 +2155,38 @@ iBool handleCommand_App(const char *cmd) {
2148 if (!isFrozen) { 2155 if (!isFrozen) {
2149 setFreezeDraw_MainWindow(get_MainWindow(), iTrue); 2156 setFreezeDraw_MainWindow(get_MainWindow(), iTrue);
2150 } 2157 }
2151 d->prefs.font = arg_Command(cmd); 2158 struct {
2152 setContentFont_Text(text_Window(d->window), d->prefs.font); 2159 const char *label;
2160 enum iPrefsString ps;
2161 int fontId;
2162 } params[] = {
2163 { "ui", uiFont_PrefsString, default_FontId },
2164 { "mono", monospaceFont_PrefsString, monospace_FontId },
2165 { "heading", headingFont_PrefsString, documentHeading_FontId },
2166 { "body", bodyFont_PrefsString, documentBody_FontId },
2167 { "monodoc", monospaceDocumentFont_PrefsString, documentMonospace_FontId },
2168 };
2169 iBool wasChanged = iFalse;
2170 iForIndices(i, params) {
2171 if (hasLabel_Command(cmd, params[i].label)) {
2172 iString *ps = &d->prefs.strings[params[i].ps];
2173 const iString *newFont = string_Command(cmd, params[i].label);
2174 if (!equal_String(ps, newFont)) {
2175 set_String(ps, newFont);
2176 wasChanged = iTrue;
2177 }
2178 }
2179 }
2180 if (wasChanged) {
2181 resetFonts_Text(text_Window(get_MainWindow()));
2182 }
2153 if (!isFrozen) { 2183 if (!isFrozen) {
2154 postCommand_App("font.changed"); 2184 postCommand_App("font.changed");
2155 postCommand_App("window.unfreeze"); 2185 postCommand_App("window.unfreeze");
2156 } 2186 }
2157 return iTrue; 2187 return iTrue;
2158 } 2188 }
2189#if 0
2159 else if (equal_Command(cmd, "headingfont.set")) { 2190 else if (equal_Command(cmd, "headingfont.set")) {
2160 if (!isFrozen) { 2191 if (!isFrozen) {
2161 setFreezeDraw_MainWindow(get_MainWindow(), iTrue); 2192 setFreezeDraw_MainWindow(get_MainWindow(), iTrue);
@@ -2168,12 +2199,13 @@ iBool handleCommand_App(const char *cmd) {
2168 } 2199 }
2169 return iTrue; 2200 return iTrue;
2170 } 2201 }
2202#endif
2171 else if (equal_Command(cmd, "zoom.set")) { 2203 else if (equal_Command(cmd, "zoom.set")) {
2172 if (!isFrozen) { 2204 if (!isFrozen) {
2173 setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */ 2205 setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */
2174 } 2206 }
2175 d->prefs.zoomPercent = arg_Command(cmd); 2207 d->prefs.zoomPercent = arg_Command(cmd);
2176 setContentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); 2208 setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f);
2177 if (!isFrozen) { 2209 if (!isFrozen) {
2178 postCommand_App("font.changed"); 2210 postCommand_App("font.changed");
2179 postCommand_App("window.unfreeze"); 2211 postCommand_App("window.unfreeze");
@@ -2189,7 +2221,7 @@ iBool handleCommand_App(const char *cmd) {
2189 delta /= 2; 2221 delta /= 2;
2190 } 2222 }
2191 d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200); 2223 d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200);
2192 setContentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f); 2224 setDocumentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f);
2193 if (!isFrozen) { 2225 if (!isFrozen) {
2194 postCommand_App("font.changed"); 2226 postCommand_App("font.changed");
2195 postCommand_App("window.unfreeze"); 2227 postCommand_App("window.unfreeze");
@@ -2388,7 +2420,7 @@ iBool handleCommand_App(const char *cmd) {
2388 return iTrue; 2420 return iTrue;
2389 } 2421 }
2390 else if (equal_Command(cmd, "searchurl")) { 2422 else if (equal_Command(cmd, "searchurl")) {
2391 iString *url = &d->prefs.searchUrl; 2423 iString *url = &d->prefs.strings[searchUrl_PrefsString];
2392 setCStr_String(url, suffixPtr_Command(cmd, "address")); 2424 setCStr_String(url, suffixPtr_Command(cmd, "address"));
2393 if (startsWith_String(url, "//")) { 2425 if (startsWith_String(url, "//")) {
2394 prependCStr_String(url, "gemini:"); 2426 prependCStr_String(url, "gemini:");
@@ -2399,20 +2431,20 @@ iBool handleCommand_App(const char *cmd) {
2399 return iTrue; 2431 return iTrue;
2400 } 2432 }
2401 else if (equal_Command(cmd, "proxy.gemini")) { 2433 else if (equal_Command(cmd, "proxy.gemini")) {
2402 setCStr_String(&d->prefs.geminiProxy, suffixPtr_Command(cmd, "address")); 2434 setCStr_String(&d->prefs.strings[geminiProxy_PrefsString], suffixPtr_Command(cmd, "address"));
2403 return iTrue; 2435 return iTrue;
2404 } 2436 }
2405 else if (equal_Command(cmd, "proxy.gopher")) { 2437 else if (equal_Command(cmd, "proxy.gopher")) {
2406 setCStr_String(&d->prefs.gopherProxy, suffixPtr_Command(cmd, "address")); 2438 setCStr_String(&d->prefs.strings[gopherProxy_PrefsString], suffixPtr_Command(cmd, "address"));
2407 return iTrue; 2439 return iTrue;
2408 } 2440 }
2409 else if (equal_Command(cmd, "proxy.http")) { 2441 else if (equal_Command(cmd, "proxy.http")) {
2410 setCStr_String(&d->prefs.httpProxy, suffixPtr_Command(cmd, "address")); 2442 setCStr_String(&d->prefs.strings[httpProxy_PrefsString], suffixPtr_Command(cmd, "address"));
2411 return iTrue; 2443 return iTrue;
2412 } 2444 }
2413#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT) 2445#if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT)
2414 else if (equal_Command(cmd, "downloads")) { 2446 else if (equal_Command(cmd, "downloads")) {
2415 setCStr_String(&d->prefs.downloadDir, suffixPtr_Command(cmd, "path")); 2447 setCStr_String(&d->prefs.strings[downloadDir_PrefsString], suffixPtr_Command(cmd, "path"));
2416 return iTrue; 2448 return iTrue;
2417 } 2449 }
2418#endif 2450#endif
@@ -2421,16 +2453,16 @@ iBool handleCommand_App(const char *cmd) {
2421 return iTrue; 2453 return iTrue;
2422 } 2454 }
2423 else if (equal_Command(cmd, "ca.file")) { 2455 else if (equal_Command(cmd, "ca.file")) {
2424 setCStr_String(&d->prefs.caFile, suffixPtr_Command(cmd, "path")); 2456 setCStr_String(&d->prefs.strings[caFile_PrefsString], suffixPtr_Command(cmd, "path"));
2425 if (!argLabel_Command(cmd, "noset")) { 2457 if (!argLabel_Command(cmd, "noset")) {
2426 setCACertificates_TlsRequest(&d->prefs.caFile, &d->prefs.caPath); 2458 setCACertificates_TlsRequest(&d->prefs.strings[caFile_PrefsString], &d->prefs.strings[caPath_PrefsString]);
2427 } 2459 }
2428 return iTrue; 2460 return iTrue;
2429 } 2461 }
2430 else if (equal_Command(cmd, "ca.path")) { 2462 else if (equal_Command(cmd, "ca.path")) {
2431 setCStr_String(&d->prefs.caPath, suffixPtr_Command(cmd, "path")); 2463 setCStr_String(&d->prefs.strings[caPath_PrefsString], suffixPtr_Command(cmd, "path"));
2432 if (!argLabel_Command(cmd, "noset")) { 2464 if (!argLabel_Command(cmd, "noset")) {
2433 setCACertificates_TlsRequest(&d->prefs.caFile, &d->prefs.caPath); 2465 setCACertificates_TlsRequest(&d->prefs.strings[caFile_PrefsString], &d->prefs.strings[caPath_PrefsString]);
2434 } 2466 }
2435 return iTrue; 2467 return iTrue;
2436 } 2468 }
@@ -2465,7 +2497,7 @@ iBool handleCommand_App(const char *cmd) {
2465 return iTrue; 2497 return iTrue;
2466 } 2498 }
2467 if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") || 2499 if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") ||
2468 ((noProxy || isEmpty_String(&d->prefs.httpProxy)) && 2500 ((noProxy || isEmpty_String(&d->prefs.strings[httpProxy_PrefsString])) &&
2469 (equalCase_Rangecc(parts.scheme, "http") || 2501 (equalCase_Rangecc(parts.scheme, "http") ||
2470 equalCase_Rangecc(parts.scheme, "https")))) { 2502 equalCase_Rangecc(parts.scheme, "https")))) {
2471 openInDefaultBrowser_App(url); 2503 openInDefaultBrowser_App(url);
@@ -2653,7 +2685,7 @@ iBool handleCommand_App(const char *cmd) {
2653 else if (equal_Command(cmd, "preferences")) { 2685 else if (equal_Command(cmd, "preferences")) {
2654 iWidget *dlg = makePreferences_Widget(); 2686 iWidget *dlg = makePreferences_Widget();
2655 updatePrefsThemeButtons_(dlg); 2687 updatePrefsThemeButtons_(dlg);
2656 setText_InputWidget(findChild_Widget(dlg, "prefs.downloads"), &d->prefs.downloadDir); 2688 setText_InputWidget(findChild_Widget(dlg, "prefs.downloads"), &d->prefs.strings[downloadDir_PrefsString]);
2657 setToggle_Widget(findChild_Widget(dlg, "prefs.hoverlink"), d->prefs.hoverLink); 2689 setToggle_Widget(findChild_Widget(dlg, "prefs.hoverlink"), d->prefs.hoverLink);
2658 setToggle_Widget(findChild_Widget(dlg, "prefs.smoothscroll"), d->prefs.smoothScrolling); 2690 setToggle_Widget(findChild_Widget(dlg, "prefs.smoothscroll"), d->prefs.smoothScrolling);
2659 setToggle_Widget(findChild_Widget(dlg, "prefs.imageloadscroll"), d->prefs.loadImageInsteadOfScrolling); 2691 setToggle_Widget(findChild_Widget(dlg, "prefs.imageloadscroll"), d->prefs.loadImageInsteadOfScrolling);
@@ -2662,24 +2694,24 @@ iBool handleCommand_App(const char *cmd) {
2662 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); 2694 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme);
2663 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); 2695 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame);
2664 setToggle_Widget(findChild_Widget(dlg, "prefs.animate"), d->prefs.uiAnimations); 2696 setToggle_Widget(findChild_Widget(dlg, "prefs.animate"), d->prefs.uiAnimations);
2665 setText_InputWidget(findChild_Widget(dlg, "prefs.userfont"), &d->prefs.symbolFontPath); 2697// setText_InputWidget(findChild_Widget(dlg, "prefs.userfont"), &d->prefs.symbolFontPath);
2666 updatePrefsPinSplitButtons_(dlg, d->prefs.pinSplit); 2698 updatePrefsPinSplitButtons_(dlg, d->prefs.pinSplit);
2667 updateScrollSpeedButtons_(dlg, mouse_ScrollType, d->prefs.smoothScrollSpeed[mouse_ScrollType]); 2699 updateScrollSpeedButtons_(dlg, mouse_ScrollType, d->prefs.smoothScrollSpeed[mouse_ScrollType]);
2668 updateScrollSpeedButtons_(dlg, keyboard_ScrollType, d->prefs.smoothScrollSpeed[keyboard_ScrollType]); 2700 updateScrollSpeedButtons_(dlg, keyboard_ScrollType, d->prefs.smoothScrollSpeed[keyboard_ScrollType]);
2669 updateDropdownSelection_LabelWidget(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.uiLanguage)); 2701 updateDropdownSelection_LabelWidget(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.strings[uiLanguage_PrefsString]));
2670 updateDropdownSelection_LabelWidget( 2702 updateDropdownSelection_LabelWidget(
2671 findChild_Widget(dlg, "prefs.returnkey"), 2703 findChild_Widget(dlg, "prefs.returnkey"),
2672 format_CStr("returnkey.set arg:%d", d->prefs.returnKey)); 2704 format_CStr("returnkey.set arg:%d", d->prefs.returnKey));
2673 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); 2705 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize);
2674 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"), 2706 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"),
2675 collectNewFormat_String("%g", uiScale_Window(as_Window(d->window)))); 2707 collectNewFormat_String("%g", uiScale_Window(as_Window(d->window))));
2676 setFlags_Widget(findChild_Widget(dlg, format_CStr("prefs.font.%d", d->prefs.font)), 2708// setFlags_Widget(findChild_Widget(dlg, format_CStr("prefs.font.%d", d->prefs.font)),
2677 selected_WidgetFlag, 2709// selected_WidgetFlag,
2678 iTrue); 2710// iTrue);
2679 setFlags_Widget( 2711// setFlags_Widget(
2680 findChild_Widget(dlg, format_CStr("prefs.headingfont.%d", d->prefs.headingFont)), 2712// findChild_Widget(dlg, format_CStr("prefs.headingfont.%d", d->prefs.headingFont)),
2681 selected_WidgetFlag, 2713// selected_WidgetFlag,
2682 iTrue); 2714// iTrue);
2683 setFlags_Widget(findChild_Widget(dlg, "prefs.mono.gemini"), 2715 setFlags_Widget(findChild_Widget(dlg, "prefs.mono.gemini"),
2684 selected_WidgetFlag, 2716 selected_WidgetFlag,
2685 d->prefs.monospaceGemini); 2717 d->prefs.monospaceGemini);
@@ -2710,8 +2742,11 @@ iBool handleCommand_App(const char *cmd) {
2710 updateColorThemeButton_(findChild_Widget(dlg, "prefs.doctheme.dark"), d->prefs.docThemeDark); 2742 updateColorThemeButton_(findChild_Widget(dlg, "prefs.doctheme.dark"), d->prefs.docThemeDark);
2711 updateColorThemeButton_(findChild_Widget(dlg, "prefs.doctheme.light"), d->prefs.docThemeLight); 2743 updateColorThemeButton_(findChild_Widget(dlg, "prefs.doctheme.light"), d->prefs.docThemeLight);
2712 updateImageStyleButton_(findChild_Widget(dlg, "prefs.imagestyle"), d->prefs.imageStyle); 2744 updateImageStyleButton_(findChild_Widget(dlg, "prefs.imagestyle"), d->prefs.imageStyle);
2713 updateFontButton_(findChild_Widget(dlg, "prefs.font"), d->prefs.font); 2745 updateFontButton_(findChild_Widget(dlg, "prefs.font.ui"), &d->prefs.strings[uiFont_PrefsString]);
2714 updateFontButton_(findChild_Widget(dlg, "prefs.headingfont"), d->prefs.headingFont); 2746 updateFontButton_(findChild_Widget(dlg, "prefs.font.heading"), &d->prefs.strings[headingFont_PrefsString]);
2747 updateFontButton_(findChild_Widget(dlg, "prefs.font.body"), &d->prefs.strings[bodyFont_PrefsString]);
2748 updateFontButton_(findChild_Widget(dlg, "prefs.font.mono"), &d->prefs.strings[monospaceFont_PrefsString]);
2749 updateFontButton_(findChild_Widget(dlg, "prefs.font.monodoc"), &d->prefs.strings[monospaceDocumentFont_PrefsString]);
2715 setFlags_Widget( 2750 setFlags_Widget(
2716 findChild_Widget( 2751 findChild_Widget(
2717 dlg, format_CStr("prefs.saturation.%d", (int) (d->prefs.saturation * 3.99f))), 2752 dlg, format_CStr("prefs.saturation.%d", (int) (d->prefs.saturation * 3.99f))),
@@ -2722,12 +2757,12 @@ iBool handleCommand_App(const char *cmd) {
2722 setText_InputWidget(findChild_Widget(dlg, "prefs.memorysize"), 2757 setText_InputWidget(findChild_Widget(dlg, "prefs.memorysize"),
2723 collectNewFormat_String("%d", d->prefs.maxMemorySize)); 2758 collectNewFormat_String("%d", d->prefs.maxMemorySize));
2724 setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); 2759 setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs);
2725 setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.searchUrl); 2760 setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.strings[searchUrl_PrefsString]);
2726 setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.caFile); 2761 setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.strings[caFile_PrefsString]);
2727 setText_InputWidget(findChild_Widget(dlg, "prefs.ca.path"), &d->prefs.caPath); 2762 setText_InputWidget(findChild_Widget(dlg, "prefs.ca.path"), &d->prefs.strings[caPath_PrefsString]);
2728 setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gemini"), &d->prefs.geminiProxy); 2763 setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gemini"), &d->prefs.strings[geminiProxy_PrefsString]);
2729 setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.gopherProxy); 2764 setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.strings[gopherProxy_PrefsString]);
2730 setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), &d->prefs.httpProxy); 2765 setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), &d->prefs.strings[httpProxy_PrefsString]);
2731 iWidget *tabs = findChild_Widget(dlg, "prefs.tabs"); 2766 iWidget *tabs = findChild_Widget(dlg, "prefs.tabs");
2732 if (tabs) { 2767 if (tabs) {
2733 showTabPage_Widget(tabs, tabPage_Widget(tabs, d->prefs.dialogTab)); 2768 showTabPage_Widget(tabs, tabPage_Widget(tabs, d->prefs.dialogTab));
diff --git a/src/fontpack.c b/src/fontpack.c
index a1f3b829..3b42e848 100644
--- a/src/fontpack.c
+++ b/src/fontpack.c
@@ -32,6 +32,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32 32
33#include "embedded.h" 33#include "embedded.h"
34 34
35/* TODO: Clean up and/or reorder this file, it's a bit unorganized. */
36
35float scale_FontSize(enum iFontSize size) { 37float scale_FontSize(enum iFontSize size) {
36 static const float sizes[max_FontSize] = { 38 static const float sizes[max_FontSize] = {
37 1.000, /* UI sizes */ 39 1.000, /* UI sizes */
@@ -46,8 +48,7 @@ float scale_FontSize(enum iFontSize size) {
46 1.666, 48 1.666,
47 2.000, 49 2.000,
48 0.568, 50 0.568,
49 0.710, 51 0.710, /* calibration: fits the Lagrange title screen with Normal line width */
50 0.800,
51 }; 52 };
52 if (size < 0 || size >= max_FontSize) { 53 if (size < 0 || size >= max_FontSize) {
53 return 1.0f; 54 return 1.0f;
@@ -66,9 +67,30 @@ struct Impl_Fonts {
66 67
67static iFonts fonts_; 68static iFonts fonts_;
68 69
70static void unloadFiles_Fonts_(iFonts *d) {
71 /* TODO: Mark all files in font packs as not resident. */
72 iForEach(PtrArray, i, &d->files) {
73 delete_FontFile(i.ptr);
74 }
75 clear_PtrArray(&d->files);
76}
77
78static iFontFile *findFile_Fonts_(iFonts *d, const iString *id) {
79 iForEach(PtrArray, i, &d->files) {
80 iFontFile *ff = i.ptr;
81 if (equal_String(&ff->id, id)) {
82 return ff;
83 }
84 }
85 return NULL;
86}
87
88/*----------------------------------------------------------------------------------------------*/
89
69iDefineTypeConstruction(FontFile) 90iDefineTypeConstruction(FontFile)
70 91
71void init_FontFile(iFontFile *d) { 92void init_FontFile(iFontFile *d) {
93 init_String(&d->id);
72 d->style = regular_FontStyle; 94 d->style = regular_FontStyle;
73 init_Block(&d->sourceData, 0); 95 init_Block(&d->sourceData, 0);
74 iZap(d->stbInfo); 96 iZap(d->stbInfo);
@@ -111,6 +133,7 @@ static void unload_FontFile_(iFontFile *d) {
111void deinit_FontFile(iFontFile *d) { 133void deinit_FontFile(iFontFile *d) {
112 unload_FontFile_(d); 134 unload_FontFile_(d);
113 deinit_Block(&d->sourceData); 135 deinit_Block(&d->sourceData);
136 deinit_String(&d->id);
114} 137}
115 138
116float scaleForPixelHeight_FontFile(const iFontFile *d, int pixelHeight) { 139float scaleForPixelHeight_FontFile(const iFontFile *d, int pixelHeight) {
@@ -140,8 +163,11 @@ void init_FontSpec(iFontSpec *d) {
140 init_String(&d->name); 163 init_String(&d->name);
141 d->flags = 0; 164 d->flags = 0;
142 d->priority = 0; 165 d->priority = 0;
143 d->scaling = 1.0f; 166 for (int i = 0; i < 2; ++i) {
144 d->vertOffset = 1.0f; 167 d->heightScale[i] = 1.0f;
168 d->glyphScale[i] = 1.0f;
169 d->vertOffsetScale[i] = 1.0f;
170 }
145 iZap(d->styles); 171 iZap(d->styles);
146} 172}
147 173
@@ -154,12 +180,12 @@ void deinit_FontSpec(iFontSpec *d) {
154 180
155iDeclareType(FontPack) 181iDeclareType(FontPack)
156iDeclareTypeConstruction(FontPack) 182iDeclareTypeConstruction(FontPack)
157 183
158struct Impl_FontPack { 184struct Impl_FontPack {
159 const iArchive *archive; /* opened ZIP archive */ 185 const iArchive *archive; /* opened ZIP archive */
160 iArray fonts; /* array of FontSpecs */ 186 iArray fonts; /* array of FontSpecs */
161 iString * loadPath; 187 iString * loadPath;
162 iFontSpec *loadSpec; 188 iFontSpec * loadSpec;
163}; 189};
164 190
165void init_FontPack(iFontPack *d) { 191void init_FontPack(iFontPack *d) {
@@ -238,11 +264,27 @@ void handleIniKeyValue_FontPack_(void *context, const iString *table, const iStr
238 else if (!cmp_String(key, "priority") && value->type == int64_TomlType) { 264 else if (!cmp_String(key, "priority") && value->type == int64_TomlType) {
239 d->loadSpec->priority = (int) value->value.int64; 265 d->loadSpec->priority = (int) value->value.int64;
240 } 266 }
241 else if (!cmp_String(key, "scaling")) { 267 else if (!cmp_String(key, "height")) {
242 d->loadSpec->scaling = (float) number_TomlValue(value); 268 d->loadSpec->heightScale[0] = d->loadSpec->heightScale[1] = (float) number_TomlValue(value);
269 }
270 else if (!cmp_String(key, "glyphscale")) {
271 d->loadSpec->glyphScale[0] = d->loadSpec->glyphScale[1] = (float) number_TomlValue(value);
243 } 272 }
244 else if (!cmp_String(key, "voffset")) { 273 else if (!cmp_String(key, "voffset")) {
245 d->loadSpec->vertOffset = (float) number_TomlValue(value); 274 d->loadSpec->vertOffsetScale[0] = d->loadSpec->vertOffsetScale[1] =
275 (float) number_TomlValue(value);
276 }
277 else if (startsWith_String(key, "ui.") || startsWith_String(key, "doc.")) {
278 const int scope = startsWith_String(key, "ui.") ? 0 : 1;
279 if (endsWith_String(key, ".height")) {
280 d->loadSpec->heightScale[scope] = (float) number_TomlValue(value);
281 }
282 if (endsWith_String(key, ".glyphscale")) {
283 d->loadSpec->glyphScale[scope] = (float) number_TomlValue(value);
284 }
285 else if (endsWith_String(key, ".voffset")) {
286 d->loadSpec->vertOffsetScale[scope] = (float) number_TomlValue(value);
287 }
246 } 288 }
247 else if (!cmp_String(key, "override") && value->type == boolean_TomlType) { 289 else if (!cmp_String(key, "override") && value->type == boolean_TomlType) {
248 iChangeFlags(d->loadSpec->flags, override_FontSpecFlag, value->value.boolean); 290 iChangeFlags(d->loadSpec->flags, override_FontSpecFlag, value->value.boolean);
@@ -265,14 +307,20 @@ void handleIniKeyValue_FontPack_(void *context, const iString *table, const iStr
265 iForIndices(i, styles) { 307 iForIndices(i, styles) {
266 if (!cmp_String(key, styles[i]) && !d->loadSpec->styles[i]) { 308 if (!cmp_String(key, styles[i]) && !d->loadSpec->styles[i]) {
267 iFontFile *ff = NULL; 309 iFontFile *ff = NULL;
268 iBlock *data = readFile_FontPack_(d, value->value.string); 310 iString *fontFileId = concat_Path(d->loadPath, value->value.string);
269 if (data) { 311 if (!(ff = findFile_Fonts_(&fonts_, fontFileId))) {
270 ff = new_FontFile(); 312 iBlock *data = readFile_FontPack_(d, value->value.string);
271 load_FontFile_(ff, data); 313 if (data) {
272 pushBack_PtrArray(&fonts_.files, ff); /* centralized ownership */ 314 ff = new_FontFile();
273 d->loadSpec->styles[i] = ff; 315 set_String(&ff->id, fontFileId);
274 delete_Block(data); 316 load_FontFile_(ff, data);
317 pushBack_PtrArray(&fonts_.files, ff); /* centralized ownership */
318 delete_Block(data);
319// printf("[FontPack] loaded file: %s\n", cstr_String(fontFileId));
320 }
275 } 321 }
322 d->loadSpec->styles[i] = ff;
323 delete_String(fontFileId);
276 break; 324 break;
277 } 325 }
278 } 326 }
@@ -290,7 +338,6 @@ static iBool load_FontPack_(iFontPack *d, const iString *ini) {
290 setHandlers_TomlParser(toml, handleIniTable_FontPack_, handleIniKeyValue_FontPack_, d); 338 setHandlers_TomlParser(toml, handleIniTable_FontPack_, handleIniKeyValue_FontPack_, d);
291 if (parse_TomlParser(toml, ini)) { 339 if (parse_TomlParser(toml, ini)) {
292 ok = iTrue; 340 ok = iTrue;
293 // fprintf(stderr, "[FontPack] error parsing %s\n", cstr_String(iniPath));
294 } 341 }
295 iAssert(d->loadSpec == NULL); 342 iAssert(d->loadSpec == NULL);
296// d->loadPath = NULL; 343// d->loadPath = NULL;
@@ -331,7 +378,7 @@ iBool loadArchive_FontPack(iFontPack *d, const iArchive *zip) {
331 initBlock_String(&ini, iniData); 378 initBlock_String(&ini, iniData);
332 if (load_FontPack_(d, &ini)) { 379 if (load_FontPack_(d, &ini)) {
333 ok = iTrue; 380 ok = iTrue;
334 } 381 }
335 deinit_String(&ini); 382 deinit_String(&ini);
336 } 383 }
337 return ok; 384 return ok;
@@ -339,14 +386,6 @@ iBool loadArchive_FontPack(iFontPack *d, const iArchive *zip) {
339 386
340/*----------------------------------------------------------------------------------------------*/ 387/*----------------------------------------------------------------------------------------------*/
341 388
342static void unloadFiles_Fonts_(iFonts *d) {
343 /* TODO: Mark all files in font packs as not resident. */
344 iForEach(PtrArray, i, &d->files) {
345 delete_FontFile(i.ptr);
346 }
347 clear_PtrArray(&d->files);
348}
349
350static void unloadFonts_Fonts_(iFonts *d) { 389static void unloadFonts_Fonts_(iFonts *d) {
351 iForEach(PtrArray, i, &d->packs) { 390 iForEach(PtrArray, i, &d->packs) {
352 iFontPack *pack = i.ptr; 391 iFontPack *pack = i.ptr;
@@ -360,6 +399,11 @@ static int cmpPriority_FontSpecPtr_(const void *a, const void *b) {
360 return -iCmp((*p1)->priority, (*p2)->priority); /* highest priority first */ 399 return -iCmp((*p1)->priority, (*p2)->priority); /* highest priority first */
361} 400}
362 401
402static int cmpName_FontSpecPtr_(const void *a, const void *b) {
403 const iFontSpec **p1 = (const iFontSpec **) a, **p2 = (const iFontSpec **) b;
404 return cmpStringCase_String(&(*p1)->name, &(*p2)->name);
405}
406
363static void sortSpecs_Fonts_(iFonts *d) { 407static void sortSpecs_Fonts_(iFonts *d) {
364 clear_PtrArray(&d->specOrder); 408 clear_PtrArray(&d->specOrder);
365 iConstForEach(PtrArray, p, &d->packs) { 409 iConstForEach(PtrArray, p, &d->packs) {
@@ -410,6 +454,18 @@ const iFontSpec *findSpec_Fonts(const char *fontId) {
410 return NULL; 454 return NULL;
411} 455}
412 456
457const iPtrArray *listSpecs_Fonts(iBool (*filterFunc)(const iFontSpec *)) {
458 iFonts *d = &fonts_;
459 iPtrArray *list = collectNew_PtrArray();
460 iConstForEach(PtrArray, i, &d->specOrder) {
461 if (filterFunc == NULL || filterFunc(i.ptr)) {
462 pushBack_PtrArray(list, i.ptr);
463 }
464 }
465 sort_Array(list, cmpName_FontSpecPtr_);
466 return list;
467}
468
413const iPtrArray *listSpecsByPriority_Fonts(void) { 469const iPtrArray *listSpecsByPriority_Fonts(void) {
414 return &fonts_.specOrder; 470 return &fonts_.specOrder;
415} 471}
diff --git a/src/fontpack.h b/src/fontpack.h
index c5aa993b..e59154e3 100644
--- a/src/fontpack.h
+++ b/src/fontpack.h
@@ -50,9 +50,8 @@ enum iFontSize {
50 contentBig_FontSize, 50 contentBig_FontSize,
51 contentLarge_FontSize, 51 contentLarge_FontSize,
52 contentHuge_FontSize, 52 contentHuge_FontSize,
53 contentMonoSmall_FontSize, 53 contentTiny_FontSize,
54 contentMono_FontSize, 54 contentSmall_FontSize, /* e.g., preformatted block scaled smaller to fit */
55 contentSmall_FontSize,
56 max_FontSize 55 max_FontSize
57}; 56};
58 57
@@ -84,6 +83,7 @@ iDeclareType(FontFile)
84iDeclareTypeConstruction(FontFile) 83iDeclareTypeConstruction(FontFile)
85 84
86struct Impl_FontFile { 85struct Impl_FontFile {
86 iString id; /* for detecting when the same file is used in many places */
87 enum iFontStyle style; 87 enum iFontStyle style;
88 iBlock sourceData; 88 iBlock sourceData;
89 stbtt_fontinfo stbInfo; 89 stbtt_fontinfo stbInfo;
@@ -112,13 +112,19 @@ struct Impl_FontSpec {
112 iString name; /* human-readable label */ 112 iString name; /* human-readable label */
113 int flags; 113 int flags;
114 int priority; 114 int priority;
115 float scaling; 115 float heightScale[2]; /* overall height scaling; ui, document */
116 float vertOffset; 116 float glyphScale[2]; /* ui, document */
117 float vertOffsetScale[2]; /* ui, document */
117 const iFontFile *styles[max_FontStyle]; 118 const iFontFile *styles[max_FontStyle];
118}; 119};
120
121iLocalDef int scaleType_FontSpec(enum iFontSize sizeId) {
122 return sizeId / contentRegular_FontSize;
123}
119 124
120void init_Fonts (const char *userDir); 125void init_Fonts (const char *userDir);
121void deinit_Fonts (void); 126void deinit_Fonts (void);
122 127
123const iFontSpec * findSpec_Fonts (const char *fontId); 128const iFontSpec * findSpec_Fonts (const char *fontId);
129const iPtrArray * listSpecs_Fonts (iBool (*filterFunc)(const iFontSpec *));
124const iPtrArray * listSpecsByPriority_Fonts (void); 130const iPtrArray * listSpecsByPriority_Fonts (void);
diff --git a/src/gmdocument.c b/src/gmdocument.c
index ce9fdec8..c98b0bb8 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -468,22 +468,23 @@ static void doLayout_GmDocument_(iGmDocument *d) {
468 const iBool isDarkBg = isDark_GmDocumentTheme( 468 const iBool isDarkBg = isDark_GmDocumentTheme(
469 isDark_ColorTheme(colorTheme_App()) ? prefs->docThemeDark : prefs->docThemeLight); 469 isDark_ColorTheme(colorTheme_App()) ? prefs->docThemeDark : prefs->docThemeLight);
470 /* TODO: Collect these parameters into a GmTheme. */ 470 /* TODO: Collect these parameters into a GmTheme. */
471 const enum iFontId headingFont = isMono ? documentMonospace_FontId : documentHeading_FontId;
472 const enum iFontId bodyFont = isMono ? documentMonospace_FontId : documentBody_FontId;
471 const int fonts[max_GmLineType] = { 473 const int fonts[max_GmLineType] = {
472 isMono ? regularMonospace_FontId : paragraph_FontId, 474 FONT_ID(bodyFont, regular_FontStyle, contentRegular_FontSize), /* text */
473 isMono ? regularMonospace_FontId : paragraph_FontId, /* bullet */ 475 FONT_ID(bodyFont, regular_FontStyle, contentRegular_FontSize), /* bullet */
474 preformatted_FontId, 476 preformatted_FontId, /* pre */
475 isMono ? regularMonospace_FontId : quote_FontId, 477 isMono ? monospaceParagraph_FontId : quote_FontId, /* quote */
476 heading1_FontId, 478 FONT_ID(headingFont, bold_FontStyle, contentHuge_FontSize), /* h1 */
477 heading2_FontId, 479 FONT_ID(headingFont, bold_FontStyle, contentLarge_FontSize), /* h2 */
478 heading3_FontId, 480 FONT_ID(headingFont, regular_FontStyle, contentBig_FontSize), /* h3 */
479 isMono ? regularMonospace_FontId 481 FONT_ID(bodyFont,
480 : ((isDarkBg && prefs->boldLinkDark) || (!isDarkBg && prefs->boldLinkLight)) 482 ((isDarkBg && prefs->boldLinkDark) || (!isDarkBg && prefs->boldLinkLight))
481 ? bold_FontId 483 ? semiBold_FontStyle
482 : paragraph_FontId, 484 : regular_FontStyle,
483 }; 485 contentRegular_FontSize) /* link */
484 float indents[max_GmLineType] = {
485 5, 10, 5, isNarrow ? 5 : 10, 0, 0, 0, 5
486 }; 486 };
487 float indents[max_GmLineType] = { 5, 10, 5, isNarrow ? 5 : 10, 0, 0, 0, 5 };
487 if (isExtremelyNarrow) { 488 if (isExtremelyNarrow) {
488 /* Further reduce the margins. */ 489 /* Further reduce the margins. */
489 indents[text_GmLineType] -= 5; 490 indents[text_GmLineType] -= 5;
@@ -562,10 +563,13 @@ static void doLayout_GmDocument_(iGmDocument *d) {
562 iGmPreMeta meta = { .bounds = line }; 563 iGmPreMeta meta = { .bounds = line };
563 meta.pixelRect.size = measurePreformattedBlock_GmDocument_( 564 meta.pixelRect.size = measurePreformattedBlock_GmDocument_(
564 d, line.start, preFont, &meta.contents, &meta.bounds.end); 565 d, line.start, preFont, &meta.contents, &meta.bounds.end);
565 if (meta.pixelRect.size.x > 566 const float oversizeRatio =
566 d->size.x - (enableIndents ? indents[preformatted_GmLineType] : 0) * gap_Text) { 567 meta.pixelRect.size.x /
567 preFont = preformattedSmall_FontId; 568 (float) (d->size.x -
568 meta.pixelRect.size = measureRange_Text(preFont, meta.contents).bounds.size; 569 (enableIndents ? indents[preformatted_GmLineType] : 0) * gap_Text);
570 if (oversizeRatio > 1.0f) {
571 preFont--; /* one notch smaller in the font size */
572 meta.pixelRect.size = measureRange_Text(preFont, meta.contents).bounds.size;
569 } 573 }
570 trimLine_Rangecc(&line, type, isNormalized); 574 trimLine_Rangecc(&line, type, isNormalized);
571 meta.altText = line; /* without the ``` */ 575 meta.altText = line; /* without the ``` */
@@ -611,7 +615,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
611 continue; 615 continue;
612 } 616 }
613 run.preId = preId; 617 run.preId = preId;
614 run.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont); 618 run.font = (d->format == plainText_SourceFormat ? plainText_FontId : preFont);
615 indent = indents[type]; 619 indent = indents[type];
616 } 620 }
617 if (addSiteBanner) { 621 if (addSiteBanner) {
@@ -719,11 +723,13 @@ static void doLayout_GmDocument_(iGmDocument *d) {
719 if (type == bullet_GmLineType) { 723 if (type == bullet_GmLineType) {
720 /* TODO: Literata bullet is broken? */ 724 /* TODO: Literata bullet is broken? */
721 iGmRun bulRun = run; 725 iGmRun bulRun = run;
726#if 0
722 if (prefs->font == literata_TextFont) { 727 if (prefs->font == literata_TextFont) {
723 /* Something wrong this the glyph in Literata, looks cropped. */ 728 /* Something wrong this the glyph in Literata, looks cropped. */
724 bulRun.font = FONT_ID(default_FontId, regular_FontStyle, 729 bulRun.font = FONT_ID(default_FontId, regular_FontStyle,
725 contentRegular_FontSize); 730 contentRegular_FontSize);
726 } 731 }
732#endif
727 bulRun.color = tmQuote_ColorId; 733 bulRun.color = tmQuote_ColorId;
728 bulRun.visBounds.pos = addX_I2(pos, (indents[text_GmLineType] - 0.55f) * gap_Text); 734 bulRun.visBounds.pos = addX_I2(pos, (indents[text_GmLineType] - 0.55f) * gap_Text);
729 bulRun.visBounds.size = 735 bulRun.visBounds.size =
@@ -778,7 +784,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
778 } 784 }
779 /* TODO: List bullets needs the same centering logic. */ 785 /* TODO: List bullets needs the same centering logic. */
780 /* Special exception for the tiny bullet operator. */ 786 /* Special exception for the tiny bullet operator. */
781 icon.font = equal_Rangecc(link->labelIcon, "\u2219") ? regularMonospace_FontId 787 icon.font = equal_Rangecc(link->labelIcon, "\u2219") ? preformatted_FontId
782 : paragraph_FontId; 788 : paragraph_FontId;
783 alignDecoration_GmRun_(&icon, iFalse); 789 alignDecoration_GmRun_(&icon, iFalse);
784 icon.color = linkColor_GmDocument(d, run.linkId, icon_GmLinkPart); 790 icon.color = linkColor_GmDocument(d, run.linkId, icon_GmLinkPart);
@@ -882,9 +888,9 @@ static void doLayout_GmDocument_(iGmDocument *d) {
882 break; 888 break;
883 } 889 }
884 clear_RunTypesetter_(&rts); 890 clear_RunTypesetter_(&rts);
885 rts.pos = pos; 891 rts.pos = pos;
886 rts.run.font = rts.fonts[text_GmLineType]; 892 rts.run.font = rts.fonts[text_GmLineType];
887 rts.run.color = colors[text_GmLineType]; 893 rts.run.color = colors[text_GmLineType];
888 isLedeParagraph = iFalse; 894 isLedeParagraph = iFalse;
889 } 895 }
890 pos = rts.pos; 896 pos = rts.pos;
@@ -925,7 +931,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
925 run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2; 931 run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2;
926 run.bounds.size.y = run.visBounds.size.y; 932 run.bounds.size.y = run.visBounds.size.y;
927 } 933 }
928 run.text = iNullRange; 934 run.text = iNullRange;
929 run.font = 0; 935 run.font = 0;
930 run.color = 0; 936 run.color = 0;
931 run.mediaType = image_GmRunMediaType; 937 run.mediaType = image_GmRunMediaType;
@@ -944,9 +950,9 @@ static void doLayout_GmDocument_(iGmDocument *d) {
944 run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI; 950 run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI;
945 run.visBounds = run.bounds; 951 run.visBounds = run.bounds;
946 run.text = iNullRange; 952 run.text = iNullRange;
947 run.color = 0; 953 run.color = 0;
948 run.mediaType = audio_GmRunMediaType; 954 run.mediaType = audio_GmRunMediaType;
949 run.mediaId = audioId; 955 run.mediaId = audioId;
950 pushBack_Array(&d->layout, &run); 956 pushBack_Array(&d->layout, &run);
951 pos.y += run.bounds.size.y + margin; 957 pos.y += run.bounds.size.y + margin;
952 } 958 }
diff --git a/src/prefs.c b/src/prefs.c
index 088cc7bc..4d079402 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -25,6 +25,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25#include <the_Foundation/fileinfo.h> 25#include <the_Foundation/fileinfo.h>
26 26
27void init_Prefs(iPrefs *d) { 27void init_Prefs(iPrefs *d) {
28 iForIndices(i, d->strings) {
29 init_String(&d->strings[i]);
30 }
28 d->dialogTab = 0; 31 d->dialogTab = 0;
29 d->langFrom = 3; /* fr */ 32 d->langFrom = 3; /* fr */
30 d->langTo = 2; /* en */ 33 d->langTo = 2; /* en */
@@ -50,8 +53,11 @@ void init_Prefs(iPrefs *d) {
50 d->decodeUserVisibleURLs = iTrue; 53 d->decodeUserVisibleURLs = iTrue;
51 d->maxCacheSize = 10; 54 d->maxCacheSize = 10;
52 d->maxMemorySize = 200; 55 d->maxMemorySize = 200;
53 d->font = nunito_TextFont; 56 setCStr_String(&d->strings[uiFont_PrefsString], "default");
54 d->headingFont = nunito_TextFont; 57 setCStr_String(&d->strings[headingFont_PrefsString], "default");
58 setCStr_String(&d->strings[bodyFont_PrefsString], "default");
59 setCStr_String(&d->strings[monospaceFont_PrefsString], "iosevka");
60 setCStr_String(&d->strings[monospaceDocumentFont_PrefsString], "iosevka-body");
55 d->monospaceGemini = iFalse; 61 d->monospaceGemini = iFalse;
56 d->monospaceGopher = iFalse; 62 d->monospaceGopher = iFalse;
57 d->boldLinkDark = iTrue; 63 d->boldLinkDark = iTrue;
@@ -66,37 +72,18 @@ void init_Prefs(iPrefs *d) {
66 d->docThemeDark = colorfulDark_GmDocumentTheme; 72 d->docThemeDark = colorfulDark_GmDocumentTheme;
67 d->docThemeLight = white_GmDocumentTheme; 73 d->docThemeLight = white_GmDocumentTheme;
68 d->saturation = 1.0f; 74 d->saturation = 1.0f;
69 initCStr_String(&d->uiLanguage, "en"); 75 setCStr_String(&d->strings[uiLanguage_PrefsString], "en");
70 init_String(&d->caFile);
71 init_String(&d->caPath);
72 init_String(&d->geminiProxy);
73 init_String(&d->gopherProxy);
74 init_String(&d->httpProxy);
75 init_String(&d->downloadDir);
76 init_String(&d->searchUrl);
77 init_String(&d->symbolFontPath);
78 /* TODO: Add some platform-specific common locations? */ 76 /* TODO: Add some platform-specific common locations? */
79 if (fileExistsCStr_FileInfo("/etc/ssl/cert.pem")) { /* macOS */ 77 if (fileExistsCStr_FileInfo("/etc/ssl/cert.pem")) { /* macOS */
80 setCStr_String(&d->caFile, "/etc/ssl/cert.pem"); 78 setCStr_String(&d->strings[caFile_PrefsString], "/etc/ssl/cert.pem");
81 } 79 }
82 if (fileExistsCStr_FileInfo("/etc/ssl/certs")) { 80 if (fileExistsCStr_FileInfo("/etc/ssl/certs")) {
83 setCStr_String(&d->caPath, "/etc/ssl/certs"); 81 setCStr_String(&d->strings[caPath_PrefsString], "/etc/ssl/certs");
84 } 82 }
85 /*
86#if defined (iPlatformAppleDesktop)
87 setCStr_String(&d->symbolFontPath, "/System/Library/Fonts/Apple Symbols.ttf");
88#endif
89 */
90} 83}
91 84
92void deinit_Prefs(iPrefs *d) { 85void deinit_Prefs(iPrefs *d) {
93 deinit_String(&d->symbolFontPath); 86 iForIndices(i, d->strings) {
94 deinit_String(&d->searchUrl); 87 deinit_String(&d->strings[i]);
95 deinit_String(&d->geminiProxy); 88 }
96 deinit_String(&d->gopherProxy);
97 deinit_String(&d->httpProxy);
98 deinit_String(&d->downloadDir);
99 deinit_String(&d->caPath);
100 deinit_String(&d->caFile);
101 deinit_String(&d->uiLanguage);
102} 89}
diff --git a/src/prefs.h b/src/prefs.h
index 37aeb260..1ba3a2ab 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -31,17 +31,36 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
31/* User preferences */ 31/* User preferences */
32 32
33iDeclareType(Prefs) 33iDeclareType(Prefs)
34
35enum iPrefsString {
36 uiLanguage_PrefsString,
37 downloadDir_PrefsString,
38 searchUrl_PrefsString,
39 /* Network */
40 caFile_PrefsString,
41 caPath_PrefsString,
42 geminiProxy_PrefsString,
43 gopherProxy_PrefsString,
44 httpProxy_PrefsString,
45 /* Style */
46 uiFont_PrefsString,
47 headingFont_PrefsString,
48 bodyFont_PrefsString,
49 monospaceFont_PrefsString,
50 monospaceDocumentFont_PrefsString,
51 max_PrefsString
52};
34 53
35/* TODO: Refactor at least the boolean values into an array for easier manipulation. 54/* TODO: Refactor at least the boolean values into an array for easier manipulation.
36 Then they can be (de)serialized as a group. Need to use a systematic command naming 55 Then they can be (de)serialized as a group. Need to use a systematic command naming
37 convention for notifications. */ 56 convention for notifications. */
38struct Impl_Prefs { 57struct Impl_Prefs {
58 iString strings[max_PrefsString];
39 /* UI state */ 59 /* UI state */
40 int dialogTab; 60 int dialogTab;
41 int langFrom; 61 int langFrom;
42 int langTo; 62 int langTo;
43 /* Window */ 63 /* Window */
44 iString uiLanguage;
45 iBool useSystemTheme; 64 iBool useSystemTheme;
46 enum iColorTheme theme; 65 enum iColorTheme theme;
47 enum iColorAccent accent; 66 enum iColorAccent accent;
@@ -55,27 +74,17 @@ struct Impl_Prefs {
55 int pinSplit; /* 0: no pinning, 1: left doc, 2: right doc */ 74 int pinSplit; /* 0: no pinning, 1: left doc, 2: right doc */
56 /* Behavior */ 75 /* Behavior */
57 int returnKey; 76 int returnKey;
58 iString downloadDir;
59 iBool hoverLink; 77 iBool hoverLink;
60 iBool smoothScrolling; 78 iBool smoothScrolling;
61 int smoothScrollSpeed[max_ScrollType]; 79 int smoothScrollSpeed[max_ScrollType];
62 iBool loadImageInsteadOfScrolling; 80 iBool loadImageInsteadOfScrolling;
63 iBool collapsePreOnLoad; 81 iBool collapsePreOnLoad;
64 iString searchUrl;
65 iBool openArchiveIndexPages; 82 iBool openArchiveIndexPages;
66 /* Network */ 83 /* Network */
67 iString caFile;
68 iString caPath;
69 iBool decodeUserVisibleURLs; 84 iBool decodeUserVisibleURLs;
70 int maxCacheSize; /* MB */ 85 int maxCacheSize; /* MB */
71 int maxMemorySize; /* MB */ 86 int maxMemorySize; /* MB */
72 iString geminiProxy;
73 iString gopherProxy;
74 iString httpProxy;
75 /* Style */ 87 /* Style */
76 iString symbolFontPath;
77 enum iTextFont font;
78 enum iTextFont headingFont;
79 iBool monospaceGemini; 88 iBool monospaceGemini;
80 iBool monospaceGopher; 89 iBool monospaceGopher;
81 iBool boldLinkDark; 90 iBool boldLinkDark;
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 2ba2fa12..45a8cf2d 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -496,7 +496,7 @@ static int documentWidth_DocumentWidget_(const iDocumentWidget *d) {
496 -1.0f, 10.0f); /* adapt to width */ 496 -1.0f, 10.0f); /* adapt to width */
497 //printf("%f\n", adjust); fflush(stdout); 497 //printf("%f\n", adjust); fflush(stdout);
498 return iMini(iMax(minWidth, bounds.size.x - gap_UI * (d->pageMargin + adjust) * 2), 498 return iMini(iMax(minWidth, bounds.size.x - gap_UI * (d->pageMargin + adjust) * 2),
499 fontSize_UI * emRatio_Text(paragraph_FontId) * /* dependent on avg. glyph width */ 499 fontSize_UI * //emRatio_Text(paragraph_FontId) * /* dependent on avg. glyph width */
500 prefs->lineWidth * prefs->zoomPercent / 100); 500 prefs->lineWidth * prefs->zoomPercent / 100);
501} 501}
502 502
@@ -3441,7 +3441,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3441 return iTrue; 3441 return iTrue;
3442 } 3442 }
3443 break; 3443 break;
3444#if 1 3444#if !defined (NDEBUG)
3445 case SDLK_KP_1: 3445 case SDLK_KP_1:
3446 case '`': { 3446 case '`': {
3447 iBlock *seed = new_Block(64); 3447 iBlock *seed = new_Block(64);
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c
index cfc81863..9713e1f2 100644
--- a/src/ui/labelwidget.c
+++ b/src/ui/labelwidget.c
@@ -384,7 +384,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) {
384 } 384 }
385 else if (flags & alignLeft_WidgetFlag) { 385 else if (flags & alignLeft_WidgetFlag) {
386 draw_Text(d->font, add_I2(bounds.pos, addX_I2(padding_LabelWidget_(d, 0), iconPad)), 386 draw_Text(d->font, add_I2(bounds.pos, addX_I2(padding_LabelWidget_(d, 0), iconPad)),
387 fg, cstr_String(&d->label)); 387 fg, "%s", cstr_String(&d->label));
388 if ((flags & drawKey_WidgetFlag) && d->key) { 388 if ((flags & drawKey_WidgetFlag) && d->key) {
389 iString str; 389 iString str;
390 init_String(&str); 390 init_String(&str);
@@ -399,6 +399,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) {
399 : colorEscape != none_ColorId ? colorEscape 399 : colorEscape != none_ColorId ? colorEscape
400 : uiTextShortcut_ColorId,*/ 400 : uiTextShortcut_ColorId,*/
401 right_Alignment, 401 right_Alignment,
402 "%s",
402 cstr_String(&str)); 403 cstr_String(&str));
403 deinit_String(&str); 404 deinit_String(&str);
404 } 405 }
@@ -409,6 +410,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) {
409 add_I2(topRight_Rect(bounds), negX_I2(padding_LabelWidget_(d, 1))), 410 add_I2(topRight_Rect(bounds), negX_I2(padding_LabelWidget_(d, 1))),
410 fg, 411 fg,
411 right_Alignment, 412 right_Alignment,
413 "%s",
412 cstr_String(&d->label)); 414 cstr_String(&d->label));
413 } 415 }
414 else { 416 else {
diff --git a/src/ui/root.c b/src/ui/root.c
index a2e6062f..f6d6f11d 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -566,7 +566,8 @@ static iBool willPerformSearchQuery_(const iString *userInput) {
566 if (isEmpty_String(clean)) { 566 if (isEmpty_String(clean)) {
567 return iFalse; 567 return iFalse;
568 } 568 }
569 return !isEmpty_String(&prefs_App()->searchUrl) && !isLikelyUrl_String(userInput); 569 return !isEmpty_String(&prefs_App()->strings[searchUrl_PrefsString]) &&
570 !isLikelyUrl_String(userInput);
570} 571}
571 572
572static void updateUrlInputContentPadding_(iWidget *navBar) { 573static void updateUrlInputContentPadding_(iWidget *navBar) {
diff --git a/src/ui/text.c b/src/ui/text.c
index 8adfa019..14b4e305 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -171,7 +171,8 @@ iLocalDef iBool isMonospaced_Font(const iFont *d) {
171static iFont *font_Text_(enum iFontId id); 171static iFont *font_Text_(enum iFontId id);
172 172
173static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *fontFile, 173static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *fontFile,
174 enum iFontSize sizeId, int height) { 174 enum iFontSize sizeId, float height) {
175 const int scaleType = scaleType_FontSpec(sizeId);
175 d->fontSpec = fontSpec; 176 d->fontSpec = fontSpec;
176 d->fontFile = fontFile; 177 d->fontFile = fontFile;
177 /* TODO: Nunito kerning fixes need to be a font parameter of its own. */ 178 /* TODO: Nunito kerning fixes need to be a font parameter of its own. */
@@ -197,15 +198,9 @@ static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *font
197 d->family = emojiAndSymbols_TextFont; 198 d->family = emojiAndSymbols_TextFont;
198 } 199 }
199#endif 200#endif
200// d->isMonospaced = (fontSpec->flags & monospace_FontSpecFlag) != 0; 201 d->height = (int) (height * fontSpec->heightScale[scaleType]);
201 d->height = height; 202 const float glyphScale = fontSpec->glyphScale[scaleType];
202 //iZap(d->font); 203 d->xScale = d->yScale = scaleForPixelHeight_FontFile(fontFile, d->height) * glyphScale;
203// stbtt_InitFont(&d->font, constData_Block(data), 0);
204// int ascent, descent, emAdv;
205// stbtt_GetFontVMetrics(&d->font, &ascent, &descent, NULL);
206// stbtt_GetCodepointHMetrics(&d->font, 'M', &emAdv, NULL);
207 const float scale = fontSpec->scaling;
208 d->xScale = d->yScale = scaleForPixelHeight_FontFile(fontFile, height) * scale;
209 if (isMonospaced_Font(d)) { 204 if (isMonospaced_Font(d)) {
210 /* It is important that monospaced fonts align 1:1 with the pixel grid so that 205 /* It is important that monospaced fonts align 1:1 with the pixel grid so that
211 box-drawing characters don't have partially occupied edge pixels, leading to seams 206 box-drawing characters don't have partially occupied edge pixels, leading to seams
@@ -217,8 +212,9 @@ static void init_Font(iFont *d, const iFontSpec *fontSpec, const iFontFile *font
217 } 212 }
218 d->emAdvance = fontFile->emAdvance * d->xScale; 213 d->emAdvance = fontFile->emAdvance * d->xScale;
219 d->baseline = fontFile->ascent * d->yScale; 214 d->baseline = fontFile->ascent * d->yScale;
220 d->vertOffset = height * (1.0f - scale) / 2 * fontSpec->vertOffset; 215 d->vertOffset = d->height * (1.0f - glyphScale) / 2 * fontSpec->vertOffsetScale[scaleType];
221 d->table = NULL; 216 d->table = NULL;
217 // printf("{%s} height:%d baseline:%d\n", cstr_String(&d->fontSpec->id), d->height, d->baseline);
222} 218}
223 219
224static void deinit_Font(iFont *d) { 220static void deinit_Font(iFont *d) {
@@ -252,8 +248,8 @@ struct Impl_CacheRow {
252}; 248};
253 249
254struct Impl_Text { 250struct Impl_Text {
255 enum iTextFont contentFont; 251// enum iTextFont contentFont;
256 enum iTextFont headingFont; 252// enum iTextFont headingFont;
257 float contentFontSize; 253 float contentFontSize;
258 iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ 254 iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */
259 int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ 255 int overrideFontId; /* always checked for glyphs first, regardless of which font is used */
@@ -290,7 +286,8 @@ static void setupFontVariants_Text_(iText *d, const iFontSpec *spec, int baseId)
290 spec, 286 spec,
291 spec->styles[style], 287 spec->styles[style],
292 sizeId, 288 sizeId,
293 (sizeId < contentRegular_FontSize ? uiSize : textSize) * scale_FontSize(sizeId)); 289 (sizeId < contentRegular_FontSize ? uiSize : textSize) *
290 scale_FontSize(sizeId));
294 } 291 }
295 } 292 }
296} 293}
@@ -311,6 +308,11 @@ iLocalDef enum iFontStyle styleId_Text_(const iFont *d) {
311 return (fontId_Text_(d) / max_FontSize) % max_FontStyle; 308 return (fontId_Text_(d) / max_FontSize) % max_FontStyle;
312} 309}
313 310
311static const iFontSpec *tryFindSpec_(enum iPrefsString ps, const char *fallback) {
312 const iFontSpec *spec = findSpec_Fonts(cstr_String(&prefs_App()->strings[ps]));
313 return spec ? spec : findSpec_Fonts(fallback);
314}
315
314static void initFonts_Text_(iText *d) { 316static void initFonts_Text_(iText *d) {
315#if 0 317#if 0
316 const iBlock *regularFont = &fontNunitoRegular_Embedded; 318 const iBlock *regularFont = &fontNunitoRegular_Embedded;
@@ -445,11 +447,11 @@ static void initFonts_Text_(iText *d) {
445 /* First the mandatory fonts. */ 447 /* First the mandatory fonts. */
446 d->overrideFontId = -1; 448 d->overrideFontId = -1;
447 resize_Array(&d->fonts, auxiliary_FontId); /* room for the built-ins */ 449 resize_Array(&d->fonts, auxiliary_FontId); /* room for the built-ins */
448 iAssert(auxiliary_FontId == documentHeading_FontId + maxVariants_Fonts); 450 setupFontVariants_Text_(d, tryFindSpec_(uiFont_PrefsString, "default"), default_FontId);
449 setupFontVariants_Text_(d, findSpec_Fonts("default"), default_FontId); 451 setupFontVariants_Text_(d, tryFindSpec_(monospaceFont_PrefsString, "iosevka"), monospace_FontId);
450 setupFontVariants_Text_(d, findSpec_Fonts("iosevka"), monospace_FontId); 452 setupFontVariants_Text_(d, tryFindSpec_(headingFont_PrefsString, "default"), documentHeading_FontId);
451 setupFontVariants_Text_(d, findSpec_Fonts("default"), documentBody_FontId); 453 setupFontVariants_Text_(d, tryFindSpec_(bodyFont_PrefsString, "default"), documentBody_FontId);
452 setupFontVariants_Text_(d, findSpec_Fonts("default"), documentHeading_FontId); 454 setupFontVariants_Text_(d, tryFindSpec_(monospaceDocumentFont_PrefsString, "iosevka-body"), documentMonospace_FontId);
453 /* Check if there are auxiliary fonts available and set those up, too. */ 455 /* Check if there are auxiliary fonts available and set those up, too. */
454 iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) { 456 iConstForEach(PtrArray, s, listSpecsByPriority_Fonts()) {
455 const iFontSpec *spec = s.ptr; 457 const iFontSpec *spec = s.ptr;
@@ -459,17 +461,6 @@ static void initFonts_Text_(iText *d) {
459 setupFontVariants_Text_(d, spec, fontId); 461 setupFontVariants_Text_(d, spec, fontId);
460 } 462 }
461 } 463 }
462#if 0
463 iForIndices(i, fontData) {
464 iFont *font = font_Text_(i);
465 init_Font(font,
466 fontData[i].ttf,
467 fontData[i].size,
468 fontData[i].scaling,
469 fontData[i].sizeId,
470 fontData[i].ttf == &fontIosevkaTermExtended_Embedded);
471 }
472#endif
473 gap_Text = iRound(gap_UI * d->contentFontSize); 464 gap_Text = iRound(gap_UI * d->contentFontSize);
474} 465}
475 466
@@ -544,8 +535,8 @@ void init_Text(iText *d, SDL_Renderer *render) {
544 iText *oldActive = activeText_; 535 iText *oldActive = activeText_;
545 activeText_ = d; 536 activeText_ = d;
546 init_Array(&d->fonts, sizeof(iFont)); 537 init_Array(&d->fonts, sizeof(iFont));
547 d->contentFont = nunito_TextFont; 538// d->contentFont = nunito_TextFont;
548 d->headingFont = nunito_TextFont; 539// d->headingFont = nunito_TextFont;
549 d->contentFontSize = contentScale_Text_; 540 d->contentFontSize = contentScale_Text_;
550 d->ansiEscape = new_RegExp("[[()]([0-9;AB]*)m", 0); 541 d->ansiEscape = new_RegExp("[[()]([0-9;AB]*)m", 0);
551 d->render = render; 542 d->render = render;
@@ -579,21 +570,15 @@ void setOpacity_Text(float opacity) {
579 SDL_SetTextureAlphaMod(activeText_->cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f); 570 SDL_SetTextureAlphaMod(activeText_->cache, iClamp(opacity, 0.0f, 1.0f) * 255 + 0.5f);
580} 571}
581 572
582void setContentFont_Text(iText *d, enum iTextFont font) { 573//void setFont_Text(iText *d, int fontId, const char *fontSpecId) {
583 if (d->contentFont != font) { 574// setupFontVariants_Text_(d, findSpec_Fonts(fontSpecId), fontId);
584 d->contentFont = font; 575// if (d->contentFont != font) {
585 resetFonts_Text(d); 576// d->contentFont = font;
586 } 577// resetFonts_Text(d);
587} 578// }
588 579//}
589void setHeadingFont_Text(iText *d, enum iTextFont font) {
590 if (d->headingFont != font) {
591 d->headingFont = font;
592 resetFonts_Text(d);
593 }
594}
595 580
596void setContentFontSize_Text(iText *d, float fontSizeFactor) { 581void setDocumentFontSize_Text(iText *d, float fontSizeFactor) {
597 fontSizeFactor *= contentScale_Text_; 582 fontSizeFactor *= contentScale_Text_;
598 iAssert(fontSizeFactor > 0); 583 iAssert(fontSizeFactor > 0);
599 if (iAbs(d->contentFontSize - fontSizeFactor) > 0.001f) { 584 if (iAbs(d->contentFontSize - fontSizeFactor) > 0.001f) {
diff --git a/src/ui/text.h b/src/ui/text.h
index af69795a..ac59e7c8 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -34,80 +34,49 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
34#define FONT_ID(name, style, size) ((name) + ((style) * max_FontSize) + (size)) 34#define FONT_ID(name, style, size) ((name) + ((style) * max_FontSize) + (size))
35 35
36enum iFontId { 36enum iFontId {
37 default_FontId = 0, /* default is always the first font */ 37 default_FontId = 0, /* default is always the first font */
38 monospace_FontId = maxVariants_Fonts, /* 2nd font is always the monospace font */ 38 monospace_FontId = maxVariants_Fonts, /* 2nd font is always the monospace font */
39 documentBody_FontId = maxVariants_Fonts * 2, /* 3rd font is the body font */ 39 documentHeading_FontId = maxVariants_Fonts * 2, /* heading font */
40 documentHeading_FontId = maxVariants_Fonts * 3, /* heading font */ 40 documentBody_FontId = maxVariants_Fonts * 3, /* body font */
41 auxiliary_FontId = maxVariants_Fonts * 4, /* the first auxiliary font (e.g., symbols) */ 41 documentMonospace_FontId = maxVariants_Fonts * 4,
42 42 auxiliary_FontId = maxVariants_Fonts * 5, /* the first auxiliary font (e.g., symbols) */
43 // defaultMedium_FontId,
44// defaultBig_FontId,
45// defaultLarge_FontId,
46// defaultTiny_FontId,
47// defaultSmall_FontId,
48 /* UI fonts: bold weight */
49// defaultBold_FontId,
50// defaultMediumBold_FontId,
51// defaultBigBold_FontId,
52// defaultLargeBold_FontId,
53 /* content fonts */
54// bold_FontId,
55// italic_FontId,
56// medium_FontId,
57// big_FontId,
58// largeBold_FontId,
59// largeLight_FontId,
60// hugeBold_FontId,
61// monospaceSmall_FontId,
62 /* extra content fonts */
63// defaultContentRegular_FontId, /* UI font but sized to regular_FontId */
64// defaultContentSmall_FontId, /* UI font but sized smaller */
65 /* symbols and scripts */
66// userSymbols_FontId,
67// iosevka_FontId = userSymbols_FontId + max_FontSize,
68// symbols_FontId = iosevka_FontId + max_FontSize,
69// symbols2_FontId = symbols_FontId + max_FontSize,
70// smolEmoji_FontId = symbols2_FontId + max_FontSize,
71// notoEmoji_FontId = smolEmoji_FontId + max_FontSize,
72// japanese_FontId = notoEmoji_FontId + max_FontSize,
73// chineseSimplified_FontId = japanese_FontId + max_FontSize,
74// korean_FontId = chineseSimplified_FontId + max_FontSize,
75// arabic_FontId = korean_FontId + max_FontSize,
76// max_FontId = arabic_FontId + max_FontSize,
77 43
78 /* Meta: */ 44 /* Meta: */
79 mask_FontId = 0x0000ffff, /* font IDs are 16-bit; see GmRun's packing */ 45 mask_FontId = 0x0000ffff, /* font IDs are 16-bit; see GmRun's packing */
80 alwaysVariableFlag_FontId = 0x00010000, 46 alwaysVariableFlag_FontId = 0x00010000,
81 47
82 /* UI fonts: */ 48 /* UI fonts: */
83 uiLabelTiny_FontId = FONT_ID(default_FontId, semiBold_FontStyle, uiTiny_FontSize), 49 uiLabelTiny_FontId = FONT_ID(default_FontId, semiBold_FontStyle, uiTiny_FontSize),
84 uiLabelSmall_FontId = FONT_ID(default_FontId, regular_FontStyle, uiSmall_FontSize), 50 uiLabelSmall_FontId = FONT_ID(default_FontId, regular_FontStyle, uiSmall_FontSize),
85 uiLabel_FontId = FONT_ID(default_FontId, regular_FontStyle, uiNormal_FontSize), 51 uiLabel_FontId = FONT_ID(default_FontId, regular_FontStyle, uiNormal_FontSize),
86 uiLabelMedium_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize), 52 uiLabelMedium_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize),
87 uiLabelMediumBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiMedium_FontSize), 53 uiLabelMediumBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiMedium_FontSize),
88 uiLabelBig_FontId = FONT_ID(default_FontId, regular_FontStyle, uiBig_FontSize), 54 uiLabelBig_FontId = FONT_ID(default_FontId, regular_FontStyle, uiBig_FontSize),
89 uiLabelBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiNormal_FontSize), 55 uiLabelBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiNormal_FontSize),
90 uiLabelBigBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiBig_FontSize), 56 uiLabelBigBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiBig_FontSize),
91 uiLabelLarge_FontId = FONT_ID(default_FontId, regular_FontStyle, uiLarge_FontSize), 57 uiLabelLarge_FontId = FONT_ID(default_FontId, regular_FontStyle, uiLarge_FontSize),
92 uiLabelLargeBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiLarge_FontSize), 58 uiLabelLargeBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiLarge_FontSize),
93 uiLabelSymbols_FontId = FONT_ID(auxiliary_FontId, regular_FontStyle, uiNormal_FontSize), 59 uiLabelSymbols_FontId = FONT_ID(auxiliary_FontId, regular_FontStyle, uiNormal_FontSize),
94 uiShortcuts_FontId = FONT_ID(default_FontId, regular_FontStyle, uiNormal_FontSize), 60 uiShortcuts_FontId = FONT_ID(default_FontId, regular_FontStyle, uiNormal_FontSize),
95 uiInput_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize), 61 uiInput_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize),
96 uiContent_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize), 62 uiContent_FontId = FONT_ID(default_FontId, regular_FontStyle, uiMedium_FontSize),
97 uiContentBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiMedium_FontSize), 63 uiContentBold_FontId = FONT_ID(default_FontId, bold_FontStyle, uiMedium_FontSize),
98 uiContentSymbols_FontId = FONT_ID(auxiliary_FontId, regular_FontStyle, uiMedium_FontSize), 64 uiContentSymbols_FontId = FONT_ID(auxiliary_FontId, regular_FontStyle, uiMedium_FontSize),
99 /* Document fonts: */ 65
100 paragraph_FontId = FONT_ID(documentBody_FontId, regular_FontStyle, contentRegular_FontSize), 66 /* Document fonts: */
101 bold_FontId = FONT_ID(documentBody_FontId, semiBold_FontStyle, contentRegular_FontSize), 67 paragraph_FontId = FONT_ID(documentBody_FontId, regular_FontStyle, contentRegular_FontSize),
102 firstParagraph_FontId = FONT_ID(documentBody_FontId, regular_FontStyle, contentMedium_FontSize), 68 bold_FontId = FONT_ID(documentBody_FontId, semiBold_FontStyle, contentRegular_FontSize),
103 preformatted_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentMono_FontSize), 69 firstParagraph_FontId = FONT_ID(documentBody_FontId, regular_FontStyle, contentMedium_FontSize),
104 preformattedSmall_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentMonoSmall_FontSize), 70 preformatted_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentSmall_FontSize),
105 quote_FontId = FONT_ID(documentBody_FontId, italic_FontStyle, contentRegular_FontSize), 71 preformattedSmall_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentTiny_FontSize),
106 heading1_FontId = FONT_ID(documentHeading_FontId, bold_FontStyle, contentHuge_FontSize), 72 quote_FontId = FONT_ID(documentBody_FontId, italic_FontStyle, contentRegular_FontSize),
107 heading2_FontId = FONT_ID(documentHeading_FontId, bold_FontStyle, contentLarge_FontSize), 73 heading1_FontId = FONT_ID(documentHeading_FontId, bold_FontStyle, contentHuge_FontSize),
108 heading3_FontId = FONT_ID(documentHeading_FontId, regular_FontStyle, contentBig_FontSize), 74 heading2_FontId = FONT_ID(documentHeading_FontId, bold_FontStyle, contentLarge_FontSize),
109 banner_FontId = FONT_ID(documentHeading_FontId, light_FontStyle, contentLarge_FontSize), 75 heading3_FontId = FONT_ID(documentHeading_FontId, regular_FontStyle, contentBig_FontSize),
110 regularMonospace_FontId = FONT_ID(monospace_FontId, regular_FontStyle, contentRegular_FontSize), 76 banner_FontId = FONT_ID(documentHeading_FontId, light_FontStyle, contentLarge_FontSize),
77 monospaceParagraph_FontId = FONT_ID(documentMonospace_FontId, regular_FontStyle, contentRegular_FontSize),
78 monospaceBold_FontId = FONT_ID(documentMonospace_FontId, semiBold_FontStyle, contentRegular_FontSize),
79 plainText_FontId = FONT_ID(documentMonospace_FontId, regular_FontStyle, contentRegular_FontSize),
111}; 80};
112 81
113//iLocalDef iBool isJapanese_FontId(enum iFontId id) { 82//iLocalDef iBool isJapanese_FontId(enum iFontId id) {
@@ -116,6 +85,7 @@ enum iFontId {
116 85
117#define emojiVariationSelector_Char ((iChar) 0xfe0f) 86#define emojiVariationSelector_Char ((iChar) 0xfe0f)
118 87
88#if 0
119/* TODO: get rid of this; configure using font ID strings, check RTL from FontFile flags */ 89/* TODO: get rid of this; configure using font ID strings, check RTL from FontFile flags */
120enum iTextFont { 90enum iTextFont {
121 undefined_TextFont = -1, 91 undefined_TextFont = -1,
@@ -129,6 +99,7 @@ enum iTextFont {
129 arabic_TextFont, 99 arabic_TextFont,
130 emojiAndSymbols_TextFont, 100 emojiAndSymbols_TextFont,
131}; 101};
102#endif
132 103
133extern int gap_Text; /* affected by content font size */ 104extern int gap_Text; /* affected by content font size */
134 105
@@ -140,11 +111,10 @@ void deinit_Text (iText *);
140 111
141void setCurrent_Text (iText *); 112void setCurrent_Text (iText *);
142 113
143//void loadUserFonts_Text (void); /* based on Prefs */ 114//void setContentFont_Text (iText *, enum iTextFont font);
144 115//void setHeadingFont_Text (iText *, enum iTextFont font);
145void setContentFont_Text (iText *, enum iTextFont font); 116//void setFont_Text (iText *, int fontId, const char *fontSpecId);
146void setHeadingFont_Text (iText *, enum iTextFont font); 117void setDocumentFontSize_Text(iText *, float fontSizeFactor); /* affects all except `default*` fonts */
147void setContentFontSize_Text (iText *, float fontSizeFactor); /* affects all except `default*` fonts */
148void resetFonts_Text (iText *); 118void resetFonts_Text (iText *);
149 119
150int lineHeight_Text (int fontId); 120int lineHeight_Text (int fontId);
diff --git a/src/ui/util.c b/src/ui/util.c
index ae72dbee..73193c7a 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1931,7 +1931,38 @@ static void addRadioButton_(iWidget *parent, const char *id, const char *label,
1931 id); 1931 id);
1932} 1932}
1933 1933
1934static iBool proportionalFonts_(const iFontSpec *spec) {
1935 return (spec->flags & monospace_FontSpecFlag) == 0 && ~spec->flags & auxiliary_FontSpecFlag;
1936}
1937
1938static iBool monospaceFonts_(const iFontSpec *spec) {
1939 return (spec->flags & monospace_FontSpecFlag) != 0 && ~spec->flags & auxiliary_FontSpecFlag;
1940}
1941
1934static const iArray *makeFontItems_(const char *id) { 1942static const iArray *makeFontItems_(const char *id) {
1943 iArray *items = collectNew_Array(sizeof(iMenuItem));
1944 if (!startsWith_CStr(id, "mono")) {
1945 iConstForEach(PtrArray, i, listSpecs_Fonts(proportionalFonts_)) {
1946 const iFontSpec *spec = i.ptr;
1947 pushBack_Array(
1948 items,
1949 &(iMenuItem){ cstr_String(&spec->name),
1950 0,
1951 0,
1952 format_CStr("!font.set %s:%s", id, cstr_String(&spec->id)) });
1953 }
1954 pushBack_Array(items, &(iMenuItem){ "---" });
1955 }
1956 iConstForEach(PtrArray, j, listSpecs_Fonts(monospaceFonts_)) {
1957 const iFontSpec *spec = j.ptr;
1958 pushBack_Array(
1959 items,
1960 &(iMenuItem){ cstr_String(&spec->name),
1961 0,
1962 0,
1963 format_CStr("!font.set %s:%s", id, cstr_String(&spec->id)) });
1964 }
1965#if 0
1935 const struct { 1966 const struct {
1936 const char * name; 1967 const char * name;
1937 enum iTextFont cfgId; 1968 enum iTextFont cfgId;
@@ -1943,7 +1974,6 @@ static const iArray *makeFontItems_(const char *id) {
1943 { "Tinos", tinos_TextFont }, 1974 { "Tinos", tinos_TextFont },
1944 { "---", -1 }, 1975 { "---", -1 },
1945 { "Iosevka", iosevka_TextFont } }; 1976 { "Iosevka", iosevka_TextFont } };
1946 iArray *items = collectNew_Array(sizeof(iMenuItem));
1947 iForIndices(i, fonts) { 1977 iForIndices(i, fonts) {
1948 pushBack_Array(items, 1978 pushBack_Array(items,
1949 &(iMenuItem){ fonts[i].name, 1979 &(iMenuItem){ fonts[i].name,
@@ -1953,17 +1983,19 @@ static const iArray *makeFontItems_(const char *id) {
1953 ? format_CStr("!%s.set arg:%d", id, fonts[i].cfgId) 1983 ? format_CStr("!%s.set arg:%d", id, fonts[i].cfgId)
1954 : NULL }); 1984 : NULL });
1955 } 1985 }
1986#endif
1956 pushBack_Array(items, &(iMenuItem){ NULL }); /* terminator */ 1987 pushBack_Array(items, &(iMenuItem){ NULL }); /* terminator */
1957 return items; 1988 return items;
1958} 1989}
1959 1990
1960static void addFontButtons_(iWidget *parent, const char *id) { 1991static void addFontButtons_(iWidget *parent, const char *id) {
1961 const iArray *items = makeFontItems_(id); 1992 const iArray *items = makeFontItems_(id);
1962 iLabelWidget *button = makeMenuButton_LabelWidget("Source Sans 3", 1993 size_t widestIndex = findWidestLabel_MenuItem(constData_Array(items), size_Array(items));
1994 iLabelWidget *button = makeMenuButton_LabelWidget(constValue_Array(items, widestIndex, iMenuItem).label,
1963 constData_Array(items), size_Array(items)); 1995 constData_Array(items), size_Array(items));
1964 setBackgroundColor_Widget(findChild_Widget(as_Widget(button), "menu"), 1996 setBackgroundColor_Widget(findChild_Widget(as_Widget(button), "menu"),
1965 uiBackgroundMenu_ColorId); 1997 uiBackgroundMenu_ColorId);
1966 setId_Widget(as_Widget(button), format_CStr("prefs.%s", id)); 1998 setId_Widget(as_Widget(button), format_CStr("prefs.font.%s", id));
1967 addChildFlags_Widget(parent, iClob(button), alignLeft_WidgetFlag); 1999 addChildFlags_Widget(parent, iClob(button), alignLeft_WidgetFlag);
1968} 2000}
1969 2001
@@ -2170,11 +2202,11 @@ iWidget *makePreferences_Widget(void) {
2170 { NULL } 2202 { NULL }
2171 }; 2203 };
2172 const iMenuItem lineWidthItems[] = { 2204 const iMenuItem lineWidthItems[] = {
2173 { "button id:prefs.linewidth.50 text:\u20132", 0, 0, "linewidth.set arg:50" }, 2205 { "button id:prefs.linewidth.30 text:\u20132", 0, 0, "linewidth.set arg:30" },
2174 { "button id:prefs.linewidth.58 text:\u20131", 0, 0, "linewidth.set arg:58" }, 2206 { "button id:prefs.linewidth.34 text:\u20131", 0, 0, "linewidth.set arg:34" },
2175 { "button id:prefs.linewidth.66 label:prefs.linewidth.normal", 0, 0, "linewidth.set arg:66" }, 2207 { "button id:prefs.linewidth.38 label:prefs.linewidth.normal", 0, 0, "linewidth.set arg:38" },
2176 { "button id:prefs.linewidth.76 text:+1", 0, 0, "linewidth.set arg:76" }, 2208 { "button id:prefs.linewidth.43 text:+1", 0, 0, "linewidth.set arg:43" },
2177 { "button id:prefs.linewidth.86 text:+2", 0, 0, "linewidth.set arg:86" }, 2209 { "button id:prefs.linewidth.48 text:+2", 0, 0, "linewidth.set arg:48" },
2178 { "button id:prefs.linewidth.1000 label:prefs.linewidth.fill", 0, 0, "linewidth.set arg:1000" }, 2210 { "button id:prefs.linewidth.1000 label:prefs.linewidth.fill", 0, 0, "linewidth.set arg:1000" },
2179 { NULL } 2211 { NULL }
2180 }; 2212 };
@@ -2491,10 +2523,14 @@ iWidget *makePreferences_Widget(void) {
2491 /* Fonts. */ { 2523 /* Fonts. */ {
2492 setId_Widget(appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.fonts}", '4', &headings, &values), "prefs.page.fonts"); 2524 setId_Widget(appendTwoColumnTabPage_Widget(tabs, "${heading.prefs.fonts}", '4', &headings, &values), "prefs.page.fonts");
2493 /* Fonts. */ { 2525 /* Fonts. */ {
2494 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}"))); 2526 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font.ui}")));
2495 addFontButtons_(values, "headingfont"); 2527 addFontButtons_(values, "ui");
2496 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font}"))); 2528 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font.heading}")));
2497 addFontButtons_(values, "font"); 2529 addFontButtons_(values, "heading");
2530 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font.body}")));
2531 addFontButtons_(values, "body");
2532 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font.mono}")));
2533 addFontButtons_(values, "mono");
2498 addDialogPadding_(headings, values); 2534 addDialogPadding_(headings, values);
2499 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.mono}"))); 2535 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.mono}")));
2500 iWidget *mono = new_Widget(); { 2536 iWidget *mono = new_Widget(); {
@@ -2511,6 +2547,9 @@ iWidget *makePreferences_Widget(void) {
2511 updateSize_LabelWidget((iLabelWidget *) tog); 2547 updateSize_LabelWidget((iLabelWidget *) tog);
2512 } 2548 }
2513 addChildFlags_Widget(values, iClob(mono), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 2549 addChildFlags_Widget(values, iClob(mono), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
2550 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font.monodoc}")));
2551 addFontButtons_(values, "monodoc");
2552 addDialogPadding_(headings, values);
2514 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.boldlink}"))); 2553 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.boldlink}")));
2515 iWidget *boldLink = new_Widget(); { 2554 iWidget *boldLink = new_Widget(); {
2516 /* TODO: Add a utility function for this type of toggles? (also for above) */ 2555 /* TODO: Add a utility function for this type of toggles? (also for above) */
@@ -2528,11 +2567,11 @@ iWidget *makePreferences_Widget(void) {
2528 } 2567 }
2529 addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 2568 addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
2530 addDialogPadding_(headings, values); 2569 addDialogPadding_(headings, values);
2531 /* Custom font. */ { 2570// /* Custom font. */ {
2532 iInputWidget *customFont = new_InputWidget(0); 2571// iInputWidget *customFont = new_InputWidget(0);
2533 setHint_InputWidget(customFont, "${hint.prefs.userfont}"); 2572// setHint_InputWidget(customFont, "${hint.prefs.userfont}");
2534 addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(customFont)); 2573// addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(customFont));
2535 } 2574// }
2536 } 2575 }
2537 } 2576 }
2538 /* Style. */ { 2577 /* Style. */ {