summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.c37
-rw-r--r--src/defs.h2
-rw-r--r--src/gmrequest.c4
-rw-r--r--src/prefs.c5
-rw-r--r--src/prefs.h1
-rw-r--r--src/ui/inputwidget.h5
-rw-r--r--src/ui/keys.c8
-rw-r--r--src/ui/root.c8
-rw-r--r--src/ui/text.c127
-rw-r--r--src/ui/text.h16
-rw-r--r--src/ui/util.c2
11 files changed, 147 insertions, 68 deletions
diff --git a/src/app.c b/src/app.c
index e22ee494..bb3afea6 100644
--- a/src/app.c
+++ b/src/app.c
@@ -204,6 +204,7 @@ static iString *serializePrefs_App_(const iApp *d) {
204 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window)); 204 appendFormat_String(str, "uiscale arg:%f\n", uiScale_Window(d->window));
205 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab); 205 appendFormat_String(str, "prefs.dialogtab arg:%d\n", d->prefs.dialogTab);
206 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font); 206 appendFormat_String(str, "font.set arg:%d\n", d->prefs.font);
207 appendFormat_String(str, "font.user path:%s\n", cstr_String(&d->prefs.symbolFontPath));
207 appendFormat_String(str, "headingfont.set arg:%d\n", d->prefs.headingFont); 208 appendFormat_String(str, "headingfont.set arg:%d\n", d->prefs.headingFont);
208 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent); 209 appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent);
209 appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling); 210 appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling);
@@ -1555,24 +1556,25 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
1555 isSelected_Widget(findChild_Widget(d, "prefs.imageloadscroll"))); 1556 isSelected_Widget(findChild_Widget(d, "prefs.imageloadscroll")));
1556 postCommandf_App("hidetoolbarscroll arg:%d", 1557 postCommandf_App("hidetoolbarscroll arg:%d",
1557 isSelected_Widget(findChild_Widget(d, "prefs.hidetoolbarscroll"))); 1558 isSelected_Widget(findChild_Widget(d, "prefs.hidetoolbarscroll")));
1558 postCommandf_App("ostheme arg:%d", 1559 postCommandf_App("ostheme arg:%d", isSelected_Widget(findChild_Widget(d, "prefs.ostheme")));
1559 isSelected_Widget(findChild_Widget(d, "prefs.ostheme"))); 1560 postCommandf_App("font.user path:%s",
1561 cstrText_InputWidget(findChild_Widget(d, "prefs.userfont")));
1560 postCommandf_App("decodeurls arg:%d", 1562 postCommandf_App("decodeurls arg:%d",
1561 isSelected_Widget(findChild_Widget(d, "prefs.decodeurls"))); 1563 isSelected_Widget(findChild_Widget(d, "prefs.decodeurls")));
1562 postCommandf_App("searchurl address:%s", 1564 postCommandf_App("searchurl address:%s",
1563 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.searchurl")))); 1565 cstrText_InputWidget(findChild_Widget(d, "prefs.searchurl")));
1564 postCommandf_App("cachesize.set arg:%d", 1566 postCommandf_App("cachesize.set arg:%d",
1565 toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); 1567 toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize"))));
1566 postCommandf_App("ca.file path:%s", 1568 postCommandf_App("ca.file path:%s",
1567 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.ca.file")))); 1569 cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file")));
1568 postCommandf_App("ca.path path:%s", 1570 postCommandf_App("ca.path path:%s",
1569 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.ca.path")))); 1571 cstrText_InputWidget(findChild_Widget(d, "prefs.ca.path")));
1570 postCommandf_App("proxy.gemini address:%s", 1572 postCommandf_App("proxy.gemini address:%s",
1571 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.gemini")))); 1573 cstrText_InputWidget(findChild_Widget(d, "prefs.proxy.gemini")));
1572 postCommandf_App("proxy.gopher address:%s", 1574 postCommandf_App("proxy.gopher address:%s",
1573 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.gopher")))); 1575 cstrText_InputWidget(findChild_Widget(d, "prefs.proxy.gopher")));
1574 postCommandf_App("proxy.http address:%s", 1576 postCommandf_App("proxy.http address:%s",
1575 cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.http")))); 1577 cstrText_InputWidget(findChild_Widget(d, "prefs.proxy.http")));
1576 const iWidget *tabs = findChild_Widget(d, "prefs.tabs"); 1578 const iWidget *tabs = findChild_Widget(d, "prefs.tabs");
1577 if (tabs) { 1579 if (tabs) {
1578 postCommandf_App("prefs.dialogtab arg:%u", 1580 postCommandf_App("prefs.dialogtab arg:%u",
@@ -1892,6 +1894,22 @@ iBool handleCommand_App(const char *cmd) {
1892 resetFonts_Text(); 1894 resetFonts_Text();
1893 return iTrue; 1895 return iTrue;
1894 } 1896 }
1897 else if (equal_Command(cmd, "font.user")) {
1898 const char *path = suffixPtr_Command(cmd, "path");
1899 if (cmp_String(&d->prefs.symbolFontPath, path)) {
1900 if (!isFrozen) {
1901 setFreezeDraw_Window(get_Window(), iTrue);
1902 }
1903 setCStr_String(&d->prefs.symbolFontPath, path);
1904 loadUserFonts_Text();
1905 resetFonts_Text();
1906 if (!isFrozen) {
1907 postCommand_App("font.changed");
1908 postCommand_App("window.unfreeze");
1909 }
1910 }
1911 return iTrue;
1912 }
1895 else if (equal_Command(cmd, "font.set")) { 1913 else if (equal_Command(cmd, "font.set")) {
1896 if (!isFrozen) { 1914 if (!isFrozen) {
1897 setFreezeDraw_Window(get_Window(), iTrue); 1915 setFreezeDraw_Window(get_Window(), iTrue);
@@ -2332,6 +2350,7 @@ iBool handleCommand_App(const char *cmd) {
2332 setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages); 2350 setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages);
2333 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); 2351 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme);
2334 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); 2352 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame);
2353 setText_InputWidget(findChild_Widget(dlg, "prefs.userfont"), &d->prefs.symbolFontPath);
2335 updatePrefsPinSplitButtons_(dlg, d->prefs.pinSplit); 2354 updatePrefsPinSplitButtons_(dlg, d->prefs.pinSplit);
2336 updateDropdownSelection_(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.uiLanguage)); 2355 updateDropdownSelection_(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.uiLanguage));
2337 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); 2356 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize);
diff --git a/src/defs.h b/src/defs.h
index 650f7e65..c19dd50e 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -62,7 +62,7 @@ enum iFileVersion {
62#define home_Icon "\U0001f3e0" 62#define home_Icon "\U0001f3e0"
63#define edit_Icon "\u270e" 63#define edit_Icon "\u270e"
64#define delete_Icon "\u232b" 64#define delete_Icon "\u232b"
65#define copy_Icon "\u2bba" 65#define copy_Icon "\u2398" //"\u2bba"
66#define check_Icon "\u2714" 66#define check_Icon "\u2714"
67#define ballotCheck_Icon "\U0001f5f9" 67#define ballotCheck_Icon "\U0001f5f9"
68#define inbox_Icon "\U0001f4e5" 68#define inbox_Icon "\U0001f4e5"
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 3dd92eec..1325c025 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -346,6 +346,7 @@ static const iBlock *replaceVariables_(const iBlock *block) {
346 if (equal_Rangecc(name, "APP_VERSION")) { 346 if (equal_Rangecc(name, "APP_VERSION")) {
347 repl = range_CStr(LAGRANGE_APP_VERSION); 347 repl = range_CStr(LAGRANGE_APP_VERSION);
348 } 348 }
349#if 0
349 else if (startsWith_Rangecc(name, "BT:")) { /* block text */ 350 else if (startsWith_Rangecc(name, "BT:")) { /* block text */
350 repl = range_String(collect_String(renderBlockChars_Text( 351 repl = range_String(collect_String(renderBlockChars_Text(
351 &fontFiraSansRegular_Embedded, 352 &fontFiraSansRegular_Embedded,
@@ -356,12 +357,13 @@ static const iBlock *replaceVariables_(const iBlock *block) {
356 } 357 }
357 else if (startsWith_Rangecc(name, "ST:")) { /* shaded text */ 358 else if (startsWith_Rangecc(name, "ST:")) { /* shaded text */
358 repl = range_String(collect_String(renderBlockChars_Text( 359 repl = range_String(collect_String(renderBlockChars_Text(
359 &fontSymbola_Embedded, 360 &fontSmolEmojiRegular_Embedded,
360 20, 361 20,
361 shading_TextBlockMode, 362 shading_TextBlockMode,
362 &(iString){ iBlockLiteral( 363 &(iString){ iBlockLiteral(
363 name.start + 3, size_Range(&name) - 3, size_Range(&name) - 3) }))); 364 name.start + 3, size_Range(&name) - 3, size_Range(&name) - 3) })));
364 } 365 }
366#endif
365 else if (equal_Rangecc(name, "ALT")) { 367 else if (equal_Rangecc(name, "ALT")) {
366#if defined (iPlatformApple) 368#if defined (iPlatformApple)
367 repl = range_CStr("\u2325"); 369 repl = range_CStr("\u2325");
diff --git a/src/prefs.c b/src/prefs.c
index 96fa3c59..b29b33b5 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -68,6 +68,7 @@ void init_Prefs(iPrefs *d) {
68 init_String(&d->httpProxy); 68 init_String(&d->httpProxy);
69 init_String(&d->downloadDir); 69 init_String(&d->downloadDir);
70 init_String(&d->searchUrl); 70 init_String(&d->searchUrl);
71 init_String(&d->symbolFontPath);
71 /* TODO: Add some platform-specific common locations? */ 72 /* TODO: Add some platform-specific common locations? */
72 if (fileExistsCStr_FileInfo("/etc/ssl/cert.pem")) { /* macOS */ 73 if (fileExistsCStr_FileInfo("/etc/ssl/cert.pem")) { /* macOS */
73 setCStr_String(&d->caFile, "/etc/ssl/cert.pem"); 74 setCStr_String(&d->caFile, "/etc/ssl/cert.pem");
@@ -75,9 +76,13 @@ void init_Prefs(iPrefs *d) {
75 if (fileExistsCStr_FileInfo("/etc/ssl/certs")) { 76 if (fileExistsCStr_FileInfo("/etc/ssl/certs")) {
76 setCStr_String(&d->caPath, "/etc/ssl/certs"); 77 setCStr_String(&d->caPath, "/etc/ssl/certs");
77 } 78 }
79#if defined (iPlatformAppleDesktop)
80 setCStr_String(&d->symbolFontPath, "/System/Library/Fonts/Apple Symbols.ttf");
81#endif
78} 82}
79 83
80void deinit_Prefs(iPrefs *d) { 84void deinit_Prefs(iPrefs *d) {
85 deinit_String(&d->symbolFontPath);
81 deinit_String(&d->searchUrl); 86 deinit_String(&d->searchUrl);
82 deinit_String(&d->geminiProxy); 87 deinit_String(&d->geminiProxy);
83 deinit_String(&d->gopherProxy); 88 deinit_String(&d->gopherProxy);
diff --git a/src/prefs.h b/src/prefs.h
index 5c01ebda..7185c8f9 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -70,6 +70,7 @@ struct Impl_Prefs {
70 iString gopherProxy; 70 iString gopherProxy;
71 iString httpProxy; 71 iString httpProxy;
72 /* Style */ 72 /* Style */
73 iString symbolFontPath;
73 enum iTextFont font; 74 enum iTextFont font;
74 enum iTextFont headingFont; 75 enum iTextFont headingFont;
75 iBool monospaceGemini; 76 iBool monospaceGemini;
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h
index cb32a29c..5c39aae0 100644
--- a/src/ui/inputwidget.h
+++ b/src/ui/inputwidget.h
@@ -63,6 +63,11 @@ void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits);
63void setEatEscape_InputWidget (iInputWidget *, iBool eatEscape); 63void setEatEscape_InputWidget (iInputWidget *, iBool eatEscape);
64 64
65const iString * text_InputWidget (const iInputWidget *); 65const iString * text_InputWidget (const iInputWidget *);
66
67iLocalDef const char *cstrText_InputWidget(const iInputWidget *d) {
68 return cstr_String(text_InputWidget(d));
69}
70
66iInputWidgetContentPadding 71iInputWidgetContentPadding
67 contentPadding_InputWidget (const iInputWidget *); 72 contentPadding_InputWidget (const iInputWidget *);
68 73
diff --git a/src/ui/keys.c b/src/ui/keys.c
index 87a5fb88..17cc0e72 100644
--- a/src/ui/keys.c
+++ b/src/ui/keys.c
@@ -311,7 +311,10 @@ static iBinding *findCommand_Keys_(iKeys *d, const char *command) {
311static void updateLookup_Keys_(iKeys *d) { 311static void updateLookup_Keys_(iKeys *d) {
312 clear_PtrSet(&d->lookup); 312 clear_PtrSet(&d->lookup);
313 iConstForEach(Array, i, &d->bindings) { 313 iConstForEach(Array, i, &d->bindings) {
314 insert_PtrSet(&d->lookup, i.value); 314 const iBinding *bind = i.value;
315 if (~bind->flags & noDirectTrigger_BindFlag) {
316 insert_PtrSet(&d->lookup, i.value);
317 }
315 } 318 }
316} 319}
317 320
@@ -442,9 +445,6 @@ iBool processEvent_Keys(const SDL_Event *ev) {
442 if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) { 445 if (ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) {
443 const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod)); 446 const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod));
444 if (bind) { 447 if (bind) {
445 if (bind->flags & noDirectTrigger_BindFlag) {
446 return iFalse;
447 }
448 if (ev->type == SDL_KEYUP) { 448 if (ev->type == SDL_KEYUP) {
449 if (bind->flags & argRelease_BindFlag) { 449 if (bind->flags & argRelease_BindFlag) {
450 postCommandf_Root(root, "%s release:1", cstr_String(&bind->command)); 450 postCommandf_Root(root, "%s release:1", cstr_String(&bind->command));
diff --git a/src/ui/root.c b/src/ui/root.c
index 7a409a75..49925856 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -156,10 +156,10 @@ static const char *pageMenuCStr_ = midEllipsis_Icon;
156/* TODO: A preference for these, maybe? */ 156/* TODO: A preference for these, maybe? */
157static const char *stopSeqCStr_[] = { 157static const char *stopSeqCStr_[] = {
158 /* Corners */ 158 /* Corners */
159 uiTextCaution_ColorEscape "\U0000230c", 159 uiTextCaution_ColorEscape "\U0000231c",
160 uiTextCaution_ColorEscape "\U0000230d", 160 uiTextCaution_ColorEscape "\U0000231d",
161 uiTextCaution_ColorEscape "\U0000230f", 161 uiTextCaution_ColorEscape "\U0000231f",
162 uiTextCaution_ColorEscape "\U0000230e", 162 uiTextCaution_ColorEscape "\U0000231e",
163#if 0 163#if 0
164 /* Rotating arrow */ 164 /* Rotating arrow */
165 uiTextCaution_ColorEscape "\U00002b62", 165 uiTextCaution_ColorEscape "\U00002b62",
diff --git a/src/ui/text.c b/src/ui/text.c
index b0c4f557..0e6a6d32 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32 32
33#include <the_Foundation/array.h> 33#include <the_Foundation/array.h>
34#include <the_Foundation/file.h> 34#include <the_Foundation/file.h>
35#include <the_Foundation/fileinfo.h>
35#include <the_Foundation/hash.h> 36#include <the_Foundation/hash.h>
36#include <the_Foundation/math.h> 37#include <the_Foundation/math.h>
37#include <the_Foundation/stringlist.h> 38#include <the_Foundation/stringlist.h>
@@ -124,10 +125,6 @@ struct Impl_Font {
124 iBool isMonospaced; 125 iBool isMonospaced;
125 iBool manualKernOnly; 126 iBool manualKernOnly;
126 enum iFontSize sizeId; /* used to look up different fonts of matching size */ 127 enum iFontSize sizeId; /* used to look up different fonts of matching size */
127// enum iFontId
128// enum iFontId japaneseFont; /* font to use for Japanese glyphs */
129// enum iFontId chineseFont; /* font to use for Simplified Chinese glyphs */
130// enum iFontId koreanFont; /* font to use for Korean glyphs */
131 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */ 128 uint32_t indexTable[128 - 32]; /* quick ASCII lookup */
132}; 129};
133 130
@@ -155,13 +152,12 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale,
155 d->xScale *= floorf(advance) / advance; 152 d->xScale *= floorf(advance) / advance;
156 } 153 }
157 } 154 }
158 d->vertOffset = height * (1.0f - scale) / 2; 155 d->baseline = ascent * d->yScale;
159 d->baseline = ascent * d->yScale; 156 d->vertOffset = height * (1.0f - scale) / 2;
160 d->sizeId = sizeId; 157 if (scale > 1.0f) {
161// d->symbolsFont = symbolsFont; 158 d->vertOffset /= 2; /* Tweak for Noto Sans Symbols */
162// d->japaneseFont = regularJapanese_FontId; 159 }
163// d->chineseFont = regularChinese_FontId; 160 d->sizeId = sizeId;
164// d->koreanFont = regularKorean_FontId;
165 memset(d->indexTable, 0xff, sizeof(d->indexTable)); 161 memset(d->indexTable, 0xff, sizeof(d->indexTable));
166} 162}
167 163
@@ -215,7 +211,8 @@ struct Impl_Text {
215 iRegExp * ansiEscape; 211 iRegExp * ansiEscape;
216}; 212};
217 213
218static iText text_; 214static iText text_;
215static iBlock *userFont_;
219 216
220static void initFonts_Text_(iText *d) { 217static void initFonts_Text_(iText *d) {
221 const float textSize = fontSize_UI * d->contentFontSize; 218 const float textSize = fontSize_UI * d->contentFontSize;
@@ -321,27 +318,31 @@ static void initFonts_Text_(iText *d) {
321 { &fontIosevkaTermExtended_Embedded, smallMonoSize, 1.0f, contentMonoSmall_FontSize }, 318 { &fontIosevkaTermExtended_Embedded, smallMonoSize, 1.0f, contentMonoSmall_FontSize },
322 { &fontIosevkaTermExtended_Embedded, monoSize, 1.0f, contentMono_FontSize }, 319 { &fontIosevkaTermExtended_Embedded, monoSize, 1.0f, contentMono_FontSize },
323 /* extra content fonts */ 320 /* extra content fonts */
324 { &fontSourceSans3Regular_Embedded, textSize, scaling, contentRegular_FontSize }, 321 { &fontSourceSans3Regular_Embedded, textSize, scaling, contentRegular_FontSize },
325 { &fontIosevkaTermExtended_Embedded, textSize, 0.866f, contentRegular_FontSize }, 322// { &fontIosevkaTermExtended_Embedded, textSize, 0.866f, contentRegular_FontSize },
326 /* symbols and scripts */ 323 /* symbols and scripts */
327#define DEFINE_FONT_SET(data) \ 324#define DEFINE_FONT_SET(data, glyphScale) \
328 { &data, uiSize, 1.0f, uiNormal_FontSize }, \ 325 { (data), uiSize, glyphScale, uiNormal_FontSize }, \
329 { &data, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, \ 326 { (data), uiSize * 1.125f, glyphScale, uiMedium_FontSize }, \
330 { &data, uiSize * 1.333f, 1.0f, uiBig_FontSize }, \ 327 { (data), uiSize * 1.333f, glyphScale, uiBig_FontSize }, \
331 { &data, uiSize * 1.666f, 1.0f, uiLarge_FontSize }, \ 328 { (data), uiSize * 1.666f, glyphScale, uiLarge_FontSize }, \
332 { &data, textSize, 1.0f, contentRegular_FontSize }, \ 329 { (data), textSize, glyphScale, contentRegular_FontSize }, \
333 { &data, textSize * 1.200f, 1.0f, contentMedium_FontSize }, \ 330 { (data), textSize * 1.200f, glyphScale, contentMedium_FontSize }, \
334 { &data, textSize * 1.333f, 1.0f, contentBig_FontSize }, \ 331 { (data), textSize * 1.333f, glyphScale, contentBig_FontSize }, \
335 { &data, textSize * 1.666f, 1.0f, contentLarge_FontSize }, \ 332 { (data), textSize * 1.666f, glyphScale, contentLarge_FontSize }, \
336 { &data, textSize * 2.000f, 1.0f, contentHuge_FontSize }, \ 333 { (data), textSize * 2.000f, glyphScale, contentHuge_FontSize }, \
337 { &data, smallMonoSize, 1.0f, contentMonoSmall_FontSize }, \ 334 { (data), smallMonoSize, glyphScale, contentMonoSmall_FontSize }, \
338 { &data, monoSize, 1.0f, contentMono_FontSize } 335 { (data), monoSize, glyphScale, contentMono_FontSize }
339 DEFINE_FONT_SET(fontSymbola_Embedded), 336 DEFINE_FONT_SET(userFont_ ? userFont_ : &fontIosevkaTermExtended_Embedded, 1.0f),
340 DEFINE_FONT_SET(fontNotoEmojiRegular_Embedded), 337 DEFINE_FONT_SET(&fontIosevkaTermExtended_Embedded, 0.866f),
341 DEFINE_FONT_SET(fontNotoSansJPRegular_Embedded), 338 DEFINE_FONT_SET(&fontNotoSansSymbolsRegular_Embedded, 1.45f),
342 DEFINE_FONT_SET(fontNotoSansSCRegular_Embedded), 339 DEFINE_FONT_SET(&fontNotoSansSymbols2Regular_Embedded, 1.45f),
343 DEFINE_FONT_SET(fontNanumGothicRegular_Embedded), /* TODO: should use Noto Sans here, too */ 340 DEFINE_FONT_SET(&fontSmolEmojiRegular_Embedded, 1.0f),
344 DEFINE_FONT_SET(fontNotoSansArabicUIRegular_Embedded), 341 DEFINE_FONT_SET(&fontNotoEmojiRegular_Embedded, 1.0f),
342 DEFINE_FONT_SET(&fontNotoSansJPRegular_Embedded, 1.0f),
343 DEFINE_FONT_SET(&fontNotoSansSCRegular_Embedded, 1.0f),
344 DEFINE_FONT_SET(&fontNanumGothicRegular_Embedded, 1.0f), /* TODO: should use Noto Sans here, too */
345 DEFINE_FONT_SET(&fontNotoSansArabicUIRegular_Embedded, 1.0f),
345 }; 346 };
346 iForIndices(i, fontData) { 347 iForIndices(i, fontData) {
347 iFont *font = &d->fonts[i]; 348 iFont *font = &d->fonts[i];
@@ -401,8 +402,28 @@ static void deinitCache_Text_(iText *d) {
401 SDL_DestroyTexture(d->cache); 402 SDL_DestroyTexture(d->cache);
402} 403}
403 404
405void loadUserFonts_Text(void) {
406 if (userFont_) {
407 delete_Block(userFont_);
408 userFont_ = NULL;
409 }
410 /* Load the system font. */
411 const iPrefs *prefs = prefs_App();
412 if (!isEmpty_String(&prefs->symbolFontPath)) {
413 iFile *f = new_File(&prefs->symbolFontPath);
414 if (open_File(f, readOnly_FileMode)) {
415 userFont_ = readAll_File(f);
416 }
417 else {
418 fprintf(stderr, "[Text] failed to open: %s\n", cstr_String(&prefs->symbolFontPath));
419 }
420 iRelease(f);
421 }
422}
423
404void init_Text(SDL_Renderer *render) { 424void init_Text(SDL_Renderer *render) {
405 iText *d = &text_; 425 iText *d = &text_;
426 loadUserFonts_Text();
406 d->contentFont = nunito_TextFont; 427 d->contentFont = nunito_TextFont;
407 d->headingFont = nunito_TextFont; 428 d->headingFont = nunito_TextFont;
408 d->contentFontSize = contentScale_Text_; 429 d->contentFontSize = contentScale_Text_;
@@ -542,14 +563,27 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {
542} 563}
543 564
544iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { 565iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
566 /* Smol Emoji overrides all other fonts. */
567 if (ch != 0x20) {
568 iFont *smol = font_Text_(smolEmoji_FontId + d->sizeId);
569 if (smol != d && (*glyphIndex = glyphIndex_Font_(smol, ch)) != 0) {
570 return smol;
571 }
572 }
545 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) { 573 if ((*glyphIndex = glyphIndex_Font_(d, ch)) != 0) {
546 return d; 574 return d;
547 } 575 }
548 /* Not defined in current font, try Noto Emoji (for selected characters). */ 576 const int fallbacks[] = {
549 if ((ch >= 0x1f300 && ch < 0x1f600) || (ch >= 0x1f680 && ch <= 0x1f6c5)) { 577 smolEmoji_FontId,
550 iFont *emoji = font_Text_(emoji_FontId + d->sizeId); 578 notoEmoji_FontId,
551 if (emoji != d && (*glyphIndex = glyphIndex_Font_(emoji, ch)) != 0) { 579 symbols2_FontId,
552 return emoji; 580 symbols_FontId
581 };
582 /* First fallback is Smol Emoji. */
583 iForIndices(i, fallbacks) {
584 iFont *fallback = font_Text_(fallbacks[i] + d->sizeId);
585 if (fallback != d && (*glyphIndex = glyphIndex_Font_(fallback, ch)) != 0) {
586 return fallback;
553 } 587 }
554 } 588 }
555 /* Try Simplified Chinese. */ 589 /* Try Simplified Chinese. */
@@ -588,13 +622,18 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) {
588 return d; 622 return d;
589 } 623 }
590#endif 624#endif
591 /* Fall back to Symbola for anything else. */ 625 /* User's symbols font. */ {
592 iFont *font = font_Text_(symbols_FontId + d->sizeId); 626 iFont *sys = font_Text_(userSymbols_FontId + d->sizeId);
593 *glyphIndex = glyphIndex_Font_(font, ch); 627 if (sys != d && (*glyphIndex = glyphIndex_Font_(sys, ch)) != 0) {
594// if (!*glyphIndex) { 628 return sys;
595// fprintf(stderr, "failed to find %08x (%lc)\n", ch, ch); fflush(stderr); 629 }
596// } 630 }
597 return font; 631// iFont *font = font_Text_(iosevka_FontId + d->sizeId);
632// *glyphIndex = glyphIndex_Font_(font, ch);
633 if (!*glyphIndex) {
634 fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr);
635 }
636 return d;
598} 637}
599 638
600static iGlyph *glyph_Font_(iFont *d, iChar ch) { 639static iGlyph *glyph_Font_(iFont *d, iChar ch) {
diff --git a/src/ui/text.h b/src/ui/text.h
index 044ddd32..1bb60e8b 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -68,11 +68,14 @@ enum iFontId {
68 monospace_FontId, 68 monospace_FontId,
69 /* extra content fonts */ 69 /* extra content fonts */
70 defaultContentSized_FontId, /* UI font but sized to regular_FontId */ 70 defaultContentSized_FontId, /* UI font but sized to regular_FontId */
71 regularMonospace_FontId,
72 /* symbols and scripts */ 71 /* symbols and scripts */
73 symbols_FontId, 72 userSymbols_FontId,
74 emoji_FontId = symbols_FontId + max_FontSize, 73 iosevka_FontId = userSymbols_FontId + max_FontSize,
75 japanese_FontId = emoji_FontId + max_FontSize, 74 symbols_FontId = iosevka_FontId + max_FontSize,
75 symbols2_FontId = symbols_FontId + max_FontSize,
76 smolEmoji_FontId = symbols2_FontId + max_FontSize,
77 notoEmoji_FontId = smolEmoji_FontId + max_FontSize,
78 japanese_FontId = notoEmoji_FontId + max_FontSize,
76 chineseSimplified_FontId = japanese_FontId + max_FontSize, 79 chineseSimplified_FontId = japanese_FontId + max_FontSize,
77 korean_FontId = chineseSimplified_FontId + max_FontSize, 80 korean_FontId = chineseSimplified_FontId + max_FontSize,
78 arabic_FontId = korean_FontId + max_FontSize, 81 arabic_FontId = korean_FontId + max_FontSize,
@@ -91,7 +94,7 @@ enum iFontId {
91 uiInput_FontId = defaultMedium_FontId, 94 uiInput_FontId = defaultMedium_FontId,
92 uiContent_FontId = defaultMedium_FontId, 95 uiContent_FontId = defaultMedium_FontId,
93 uiContentBold_FontId = defaultMediumBold_FontId, 96 uiContentBold_FontId = defaultMediumBold_FontId,
94 uiContentSymbols_FontId = symbols_FontId + uiMedium_FontSize, 97 uiContentSymbols_FontId = symbols_FontId + uiMedium_FontSize,
95 /* Document fonts: */ 98 /* Document fonts: */
96 paragraph_FontId = regular_FontId, 99 paragraph_FontId = regular_FontId,
97 firstParagraph_FontId = medium_FontId, 100 firstParagraph_FontId = medium_FontId,
@@ -102,6 +105,7 @@ enum iFontId {
102 heading2_FontId = largeBold_FontId, 105 heading2_FontId = largeBold_FontId,
103 heading3_FontId = big_FontId, 106 heading3_FontId = big_FontId,
104 banner_FontId = largeLight_FontId, 107 banner_FontId = largeLight_FontId,
108 regularMonospace_FontId = iosevka_FontId + contentRegular_FontSize
105}; 109};
106 110
107iLocalDef iBool isJapanese_FontId(enum iFontId id) { 111iLocalDef iBool isJapanese_FontId(enum iFontId id) {
@@ -124,6 +128,8 @@ extern int gap_Text; /* affected by content font size */
124void init_Text (SDL_Renderer *); 128void init_Text (SDL_Renderer *);
125void deinit_Text (void); 129void deinit_Text (void);
126 130
131void loadUserFonts_Text (void); /* based on Prefs */
132
127void setContentFont_Text (enum iTextFont font); 133void setContentFont_Text (enum iTextFont font);
128void setHeadingFont_Text (enum iTextFont font); 134void setHeadingFont_Text (enum iTextFont font);
129void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */ 135void setContentFontSize_Text (float fontSizeFactor); /* affects all except `default*` fonts */
diff --git a/src/ui/util.c b/src/ui/util.c
index 6c87ba26..977eba9d 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1525,6 +1525,7 @@ void updatePreferencesLayout_Widget(iWidget *prefs) {
1525 static const char *inputIds[] = { 1525 static const char *inputIds[] = {
1526 "prefs.searchurl", 1526 "prefs.searchurl",
1527 "prefs.downloads", 1527 "prefs.downloads",
1528 "prefs.userfont",
1528 "prefs.ca.file", 1529 "prefs.ca.file",
1529 "prefs.ca.path", 1530 "prefs.ca.path",
1530 "prefs.proxy.gemini", 1531 "prefs.proxy.gemini",
@@ -1786,6 +1787,7 @@ iWidget *makePreferences_Widget(void) {
1786 updateSize_LabelWidget((iLabelWidget *) tog); 1787 updateSize_LabelWidget((iLabelWidget *) tog);
1787 } 1788 }
1788 addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 1789 addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
1790 addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(new_InputWidget(0)));
1789 } 1791 }
1790 makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); 1792 makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values);
1791 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}"))); 1793 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}")));