diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 66 | ||||
-rw-r--r-- | src/app.h | 2 | ||||
-rw-r--r-- | src/fontpack.c | 120 | ||||
-rw-r--r-- | src/fontpack.h | 4 | ||||
-rw-r--r-- | src/ui/banner.c | 3 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 2 | ||||
-rw-r--r-- | src/ui/text.c | 40 | ||||
-rw-r--r-- | src/ui/text.h | 2 | ||||
-rw-r--r-- | src/ui/util.c | 48 | ||||
-rw-r--r-- | src/ui/util.h | 1 |
10 files changed, 259 insertions, 29 deletions
@@ -1365,6 +1365,10 @@ void processEvents_App(enum iAppEventMode eventMode) { | |||
1365 | dispatchCommands_Periodic(&d->periodic); | 1365 | dispatchCommands_Periodic(&d->periodic); |
1366 | continue; | 1366 | continue; |
1367 | } | 1367 | } |
1368 | if (ev.type == SDL_USEREVENT && ev.user.code == releaseObject_UserEventCode) { | ||
1369 | iRelease(ev.user.data1); | ||
1370 | continue; | ||
1371 | } | ||
1368 | if (ev.type == SDL_USEREVENT && ev.user.code == refresh_UserEventCode) { | 1372 | if (ev.type == SDL_USEREVENT && ev.user.code == refresh_UserEventCode) { |
1369 | gotRefresh = iTrue; | 1373 | gotRefresh = iTrue; |
1370 | continue; | 1374 | continue; |
@@ -2262,6 +2266,13 @@ void resetFonts_App(void) { | |||
2262 | } | 2266 | } |
2263 | } | 2267 | } |
2264 | 2268 | ||
2269 | void availableFontsChanged_App(void) { | ||
2270 | iApp *d = &app_; | ||
2271 | iConstForEach(PtrArray, win, listWindows_App_(d, collectNew_PtrArray())) { | ||
2272 | resetMissing_Text(text_Window(win.ptr)); | ||
2273 | } | ||
2274 | } | ||
2275 | |||
2265 | static void invalidateCachedDocuments_App_(void) { | 2276 | static void invalidateCachedDocuments_App_(void) { |
2266 | iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { | 2277 | iForEach(ObjectList, i, iClob(listDocuments_App(NULL))) { |
2267 | invalidateCachedLayout_History(history_DocumentWidget(i.object)); | 2278 | invalidateCachedLayout_History(history_DocumentWidget(i.object)); |
@@ -2388,24 +2399,49 @@ iBool handleCommand_App(const char *cmd) { | |||
2388 | reload_Fonts(); /* also does font cache reset, window invalidation */ | 2399 | reload_Fonts(); /* also does font cache reset, window invalidation */ |
2389 | return iTrue; | 2400 | return iTrue; |
2390 | } | 2401 | } |
2391 | #if 0 | 2402 | else if (equal_Command(cmd, "font.find")) { |
2392 | else if (equal_Command(cmd, "font.user")) { | 2403 | searchOnlineLibraryForCharacters_Fonts(string_Command(cmd, "chars")); |
2393 | const char *path = suffixPtr_Command(cmd, "path"); | 2404 | return iTrue; |
2394 | if (cmp_String(&d->prefs.symbolFontPath, path)) { | 2405 | } |
2395 | if (!isFrozen) { | 2406 | else if (equal_Command(cmd, "font.found")) { |
2396 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); | 2407 | if (hasLabel_Command(cmd, "error")) { |
2397 | } | 2408 | makeSimpleMessage_Widget("${heading.glyphfinder}", |
2398 | setCStr_String(&d->prefs.symbolFontPath, path); | 2409 | format_CStr("%d %s", |
2399 | loadUserFonts_Text(); | 2410 | argLabel_Command(cmd, "error"), |
2400 | resetFonts_App(d); | 2411 | suffixPtr_Command(cmd, "msg"))); |
2401 | if (!isFrozen) { | 2412 | return iTrue; |
2402 | postCommand_App("font.changed"); | ||
2403 | postCommand_App("window.unfreeze"); | ||
2404 | } | ||
2405 | } | 2413 | } |
2414 | iString *src = collectNew_String(); | ||
2415 | setCStr_String(src, "# ${heading.glyphfinder.results}\n\n"); | ||
2416 | iRangecc path = iNullRange; | ||
2417 | iBool isFirst = iTrue; | ||
2418 | while (nextSplit_Rangecc(range_Command(cmd, "packs"), ",", &path)) { | ||
2419 | if (isFirst) { | ||
2420 | appendCStr_String(src, "${glyphfinder.results}\n\n"); | ||
2421 | } | ||
2422 | iRangecc fpath = path; | ||
2423 | iRangecc fsize = path; | ||
2424 | fpath.end = strchr(fpath.start, ';'); | ||
2425 | fsize.start = fpath.end + 1; | ||
2426 | const uint32_t size = strtoul(fsize.start, NULL, 10); | ||
2427 | appendFormat_String(src, "=> gemini://skyjake.fi/fonts/%s %s (%.1f MB)\n", | ||
2428 | cstr_Rangecc(fpath), | ||
2429 | cstr_Rangecc(fpath), | ||
2430 | (double) size / 1.0e6); | ||
2431 | isFirst = iFalse; | ||
2432 | } | ||
2433 | if (isFirst) { | ||
2434 | appendFormat_String(src, "${glyphfinder.results.empty}\n"); | ||
2435 | } | ||
2436 | appendCStr_String(src, "\n=> about:fonts ${menu.fonts}"); | ||
2437 | iDocumentWidget *page = newTab_App(NULL, iTrue); | ||
2438 | translate_Lang(src); | ||
2439 | setUrlAndSource_DocumentWidget(page, | ||
2440 | collectNewCStr_String(""), | ||
2441 | collectNewCStr_String("text/gemini"), | ||
2442 | utf8_String(src)); | ||
2406 | return iTrue; | 2443 | return iTrue; |
2407 | } | 2444 | } |
2408 | #endif | ||
2409 | else if (equal_Command(cmd, "font.set")) { | 2445 | else if (equal_Command(cmd, "font.set")) { |
2410 | if (!isFrozen) { | 2446 | if (!isFrozen) { |
2411 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); | 2447 | setFreezeDraw_MainWindow(get_MainWindow(), iTrue); |
@@ -67,6 +67,7 @@ enum iUserEventCode { | |||
67 | take, it could turn into a tap-and-hold for example. */ | 67 | take, it could turn into a tap-and-hold for example. */ |
68 | widgetTapBegins_UserEventCode, | 68 | widgetTapBegins_UserEventCode, |
69 | widgetTouchEnds_UserEventCode, /* finger lifted, but momentum may continue */ | 69 | widgetTouchEnds_UserEventCode, /* finger lifted, but momentum may continue */ |
70 | releaseObject_UserEventCode, /* object that needs releasing in the main thread */ | ||
70 | }; | 71 | }; |
71 | 72 | ||
72 | const iString *execPath_App (void); | 73 | const iString *execPath_App (void); |
@@ -142,6 +143,7 @@ iDocumentWidget * document_Command (const char *cmd); | |||
142 | void openInDefaultBrowser_App(const iString *url); | 143 | void openInDefaultBrowser_App(const iString *url); |
143 | void revealPath_App (const iString *path); | 144 | void revealPath_App (const iString *path); |
144 | void resetFonts_App (void); | 145 | void resetFonts_App (void); |
146 | void availableFontsChanged_App(void); | ||
145 | 147 | ||
146 | iMainWindow * mainWindow_App (void); | 148 | iMainWindow * mainWindow_App (void); |
147 | void closePopups_App (void); | 149 | void closePopups_App (void); |
diff --git a/src/fontpack.c b/src/fontpack.c index a440234e..96006226 100644 --- a/src/fontpack.c +++ b/src/fontpack.c | |||
@@ -23,6 +23,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
23 | #include "fontpack.h" | 23 | #include "fontpack.h" |
24 | #include "resources.h" | 24 | #include "resources.h" |
25 | #include "ui/window.h" | 25 | #include "ui/window.h" |
26 | #include "gmrequest.h" | ||
26 | #include "app.h" | 27 | #include "app.h" |
27 | 28 | ||
28 | #include <the_Foundation/archive.h> | 29 | #include <the_Foundation/archive.h> |
@@ -748,7 +749,7 @@ const iPtrArray *listSpecsByPriority_Fonts(void) { | |||
748 | return &fonts_.specOrder; | 749 | return &fonts_.specOrder; |
749 | } | 750 | } |
750 | 751 | ||
751 | iString *infoText_FontPack(const iFontPack *d) { | 752 | iString *infoText_FontPack(const iFontPack *d, iBool isFull) { |
752 | const iFontPack *installed = pack_Fonts(cstr_String(&d->id)); | 753 | const iFontPack *installed = pack_Fonts(cstr_String(&d->id)); |
753 | const iBool isInstalled = (installed != NULL); | 754 | const iBool isInstalled = (installed != NULL); |
754 | const int installedVersion = installed ? installed->version : 0; | 755 | const int installedVersion = installed ? installed->version : 0; |
@@ -757,9 +758,17 @@ iString *infoText_FontPack(const iFontPack *d) { | |||
757 | size_t sizeInBytes = 0; | 758 | size_t sizeInBytes = 0; |
758 | iPtrSet *uniqueFiles = new_PtrSet(); | 759 | iPtrSet *uniqueFiles = new_PtrSet(); |
759 | iStringList *names = new_StringList(); | 760 | iStringList *names = new_StringList(); |
761 | size_t numNames = 0; | ||
762 | iBool isAbbreviated = iFalse; | ||
760 | iConstForEach(PtrArray, i, listSpecs_FontPack(d)) { | 763 | iConstForEach(PtrArray, i, listSpecs_FontPack(d)) { |
761 | const iFontSpec *spec = i.ptr; | 764 | const iFontSpec *spec = i.ptr; |
762 | pushBack_StringList(names, &spec->name); | 765 | numNames++; |
766 | if (isFull || size_StringList(names) < 20) { | ||
767 | pushBack_StringList(names, &spec->name); | ||
768 | } | ||
769 | else { | ||
770 | isAbbreviated = iTrue; | ||
771 | } | ||
763 | iForIndices(j, spec->styles) { | 772 | iForIndices(j, spec->styles) { |
764 | insert_PtrSet(uniqueFiles, spec->styles[j]->sourceData.i); | 773 | insert_PtrSet(uniqueFiles, spec->styles[j]->sourceData.i); |
765 | } | 774 | } |
@@ -777,11 +786,12 @@ iString *infoText_FontPack(const iFontPack *d) { | |||
777 | if (!endsWith_String(str, "(")) { | 786 | if (!endsWith_String(str, "(")) { |
778 | appendCStr_String(str, ", "); | 787 | appendCStr_String(str, ", "); |
779 | } | 788 | } |
780 | appendCStr_String(str, formatCStrs_Lang("num.fonts.n", size_StringList(names))); | 789 | appendCStr_String(str, formatCStrs_Lang("num.fonts.n", numNames)); |
781 | } | 790 | } |
782 | appendFormat_String(str, ")"); | 791 | appendFormat_String(str, ")"); |
783 | } | 792 | } |
784 | appendFormat_String(str, " \u2014 %s\n", cstrCollect_String(joinCStr_StringList(names, ", "))); | 793 | appendFormat_String(str, " \u2014 %s%s\n", cstrCollect_String(joinCStr_StringList(names, ", ")), |
794 | isAbbreviated ? ", ..." : ""); | ||
785 | if (isInstalled && installedVersion != d->version) { | 795 | if (isInstalled && installedVersion != d->version) { |
786 | appendCStr_String(str, format_Lang("${fontpack.meta.version}\n", d->version)); | 796 | appendCStr_String(str, format_Lang("${fontpack.meta.version}\n", d->version)); |
787 | } | 797 | } |
@@ -945,7 +955,7 @@ const iString *infoPage_Fonts(iRangecc query) { | |||
945 | appendFormat_String(str, "### %s\n", | 955 | appendFormat_String(str, "### %s\n", |
946 | isEmpty_String(packId) ? "fonts.ini" : | 956 | isEmpty_String(packId) ? "fonts.ini" : |
947 | cstr_String(packId)); | 957 | cstr_String(packId)); |
948 | append_String(str, collect_String(infoText_FontPack(pack))); | 958 | append_String(str, collect_String(infoText_FontPack(pack, iFalse))); |
949 | appendFormat_String(str, "=> %s ${fontpack.meta.viewfile}\n", | 959 | appendFormat_String(str, "=> %s ${fontpack.meta.viewfile}\n", |
950 | cstrCollect_String(makeFileUrl_String(&spec->sourcePath))); | 960 | cstrCollect_String(makeFileUrl_String(&spec->sourcePath))); |
951 | if (pack->isStandalone) { | 961 | if (pack->isStandalone) { |
@@ -1018,6 +1028,7 @@ void install_Fonts(const iString *packId, const iBlock *data) { | |||
1018 | iRelease(f); | 1028 | iRelease(f); |
1019 | /* Newly installed fontpacks may have a higher priority that overrides other fonts. */ | 1029 | /* Newly installed fontpacks may have a higher priority that overrides other fonts. */ |
1020 | reload_Fonts(); | 1030 | reload_Fonts(); |
1031 | availableFontsChanged_App(); | ||
1021 | } | 1032 | } |
1022 | 1033 | ||
1023 | void installFontFile_Fonts(const iString *fileName, const iBlock *data) { | 1034 | void installFontFile_Fonts(const iString *fileName, const iBlock *data) { |
@@ -1028,6 +1039,7 @@ void installFontFile_Fonts(const iString *fileName, const iBlock *data) { | |||
1028 | } | 1039 | } |
1029 | iRelease(f); | 1040 | iRelease(f); |
1030 | reload_Fonts(); | 1041 | reload_Fonts(); |
1042 | availableFontsChanged_App(); | ||
1031 | } | 1043 | } |
1032 | 1044 | ||
1033 | void enablePack_Fonts(const iString *packId, iBool enable) { | 1045 | void enablePack_Fonts(const iString *packId, iBool enable) { |
@@ -1040,6 +1052,7 @@ void enablePack_Fonts(const iString *packId, iBool enable) { | |||
1040 | } | 1052 | } |
1041 | updateActive_Fonts(); | 1053 | updateActive_Fonts(); |
1042 | resetFonts_App(); | 1054 | resetFonts_App(); |
1055 | availableFontsChanged_App(); | ||
1043 | invalidate_Window(get_MainWindow()); | 1056 | invalidate_Window(get_MainWindow()); |
1044 | } | 1057 | } |
1045 | 1058 | ||
@@ -1047,5 +1060,100 @@ void updateActive_Fonts(void) { | |||
1047 | sortSpecs_Fonts_(&fonts_); | 1060 | sortSpecs_Fonts_(&fonts_); |
1048 | } | 1061 | } |
1049 | 1062 | ||
1050 | iDefineClass(FontFile) | 1063 | static void findCharactersInCMap_(iGmRequest *d, iGmRequest *req) { |
1064 | /* Note: Called in background thread. */ | ||
1065 | iUnused(req); | ||
1066 | const iString *missingChars = userData_Object(d); | ||
1067 | if (isSuccess_GmStatusCode(status_GmRequest(d))) { | ||
1068 | iStringList *matchingPacks = new_StringList(); | ||
1069 | iChar needed[20]; | ||
1070 | iChar minChar = UINT32_MAX, maxChar = 0; | ||
1071 | size_t numNeeded = 0; | ||
1072 | iConstForEach(String, ch, missingChars) { | ||
1073 | needed[numNeeded++] = ch.value; | ||
1074 | minChar = iMin(minChar, ch.value); | ||
1075 | maxChar = iMax(maxChar, ch.value); | ||
1076 | if (numNeeded == iElemCount(needed)) { | ||
1077 | /* Shouldn't be that many. */ | ||
1078 | break; | ||
1079 | } | ||
1080 | } | ||
1081 | iBlock *data = decompressGzip_Block(body_GmRequest(d)); | ||
1082 | iRangecc line = iNullRange; | ||
1083 | while (nextSplit_Rangecc(range_Block(data), "\n", &line)) { | ||
1084 | iRangecc fontpackPath = iNullRange; | ||
1085 | for (const char *pos = line.start; pos < line.end; pos++) { | ||
1086 | if (*pos == ':') { | ||
1087 | fontpackPath.start = line.start; | ||
1088 | fontpackPath.end = pos; | ||
1089 | line.start = pos + 1; | ||
1090 | trimStart_Rangecc(&line); | ||
1091 | break; | ||
1092 | } | ||
1093 | } | ||
1094 | if (fontpackPath.start) { | ||
1095 | /* Parse the character ranges and see if any match what we need. */ | ||
1096 | const char *pos = line.start; | ||
1097 | while (pos < line.end) { | ||
1098 | char *endp; | ||
1099 | uint32_t first = strtoul(pos, &endp, 10); | ||
1100 | uint32_t last = first; | ||
1101 | if (*endp == '-') { | ||
1102 | last = strtoul(endp + 1, &endp, 10); | ||
1103 | } | ||
1104 | if (maxChar < first) { | ||
1105 | break; /* The rest are even higher. */ | ||
1106 | } | ||
1107 | if (minChar <= last) { | ||
1108 | for (size_t i = 0; i < numNeeded; i++) { | ||
1109 | if (needed[i] >= first && needed[i] <= last) { | ||
1110 | /* Got it. */ | ||
1111 | pushBackRange_StringList(matchingPacks, fontpackPath); | ||
1112 | break; | ||
1113 | } | ||
1114 | } | ||
1115 | } | ||
1116 | pos = endp + 1; | ||
1117 | } | ||
1118 | } | ||
1119 | } | ||
1120 | delete_Block(data); | ||
1121 | iString result; | ||
1122 | init_String(&result); | ||
1123 | format_String(&result, "font.found chars:%s packs:", cstr_String(missingChars)); | ||
1124 | iConstForEach(StringList, s, matchingPacks) { | ||
1125 | if (s.pos != 0) { | ||
1126 | appendCStr_String(&result, ","); | ||
1127 | } | ||
1128 | append_String(&result, s.value); | ||
1129 | } | ||
1130 | postCommandString_Root(NULL, &result); | ||
1131 | deinit_String(&result); | ||
1132 | iRelease(matchingPacks); | ||
1133 | } | ||
1134 | else { | ||
1135 | /* Report error. */ | ||
1136 | postCommandf_Root(NULL, | ||
1137 | "font.found chars:%s error:%d msg:\x1b[1m%s\x1b[0m\n%s", | ||
1138 | cstr_String(missingChars), | ||
1139 | status_GmRequest(d), | ||
1140 | cstr_String(meta_GmRequest(d)), | ||
1141 | cstr_String(url_GmRequest(d))); | ||
1142 | } | ||
1143 | // fflush(stdout); | ||
1144 | delete_String(userData_Object(d)); | ||
1145 | /* We can't delete ourselves; threads must be joined from another thread. */ | ||
1146 | SDL_PushEvent((SDL_Event *) &(SDL_UserEvent){ | ||
1147 | .type = SDL_USEREVENT, .code = releaseObject_UserEventCode, .data1 = d }); | ||
1148 | } | ||
1051 | 1149 | ||
1150 | void searchOnlineLibraryForCharacters_Fonts(const iString *chars) { | ||
1151 | /* Fetch the character map from skyjake.fi. */ | ||
1152 | iGmRequest *req = new_GmRequest(certs_App()); | ||
1153 | setUrl_GmRequest(req, collectNewCStr_String("gemini://skyjake.fi/fonts/cmap.txt.gz")); | ||
1154 | setUserData_Object(req, copy_String(chars)); | ||
1155 | iConnect(GmRequest, req, finished, req, findCharactersInCMap_); | ||
1156 | submit_GmRequest(req); | ||
1157 | } | ||
1158 | |||
1159 | iDefineClass(FontFile) | ||
diff --git a/src/fontpack.h b/src/fontpack.h index aa6f2b9f..f6d4d483 100644 --- a/src/fontpack.h +++ b/src/fontpack.h | |||
@@ -160,7 +160,7 @@ const iString * loadPath_FontPack (const iFontPack *); /* may return N | |||
160 | iBool isDisabled_FontPack (const iFontPack *); | 160 | iBool isDisabled_FontPack (const iFontPack *); |
161 | iBool isReadOnly_FontPack (const iFontPack *); | 161 | iBool isReadOnly_FontPack (const iFontPack *); |
162 | const iPtrArray * listSpecs_FontPack (const iFontPack *); | 162 | const iPtrArray * listSpecs_FontPack (const iFontPack *); |
163 | iString * infoText_FontPack (const iFontPack *); | 163 | iString * infoText_FontPack (const iFontPack *, iBool isFull); |
164 | const iArray * actions_FontPack (const iFontPack *, iBool showInstalled); | 164 | const iArray * actions_FontPack (const iFontPack *, iBool showInstalled); |
165 | 165 | ||
166 | const iString * idFromUrl_FontPack (const iString *url); | 166 | const iString * idFromUrl_FontPack (const iString *url); |
@@ -186,3 +186,5 @@ void reload_Fonts (void); | |||
186 | iLocalDef iBool isInstalled_Fonts(const char *packId) { | 186 | iLocalDef iBool isInstalled_Fonts(const char *packId) { |
187 | return pack_Fonts(packId) != NULL; | 187 | return pack_Fonts(packId) != NULL; |
188 | } | 188 | } |
189 | |||
190 | void searchOnlineLibraryForCharacters_Fonts (const iString *chars); | ||
diff --git a/src/ui/banner.c b/src/ui/banner.c index 11ae1574..79d70039 100644 --- a/src/ui/banner.c +++ b/src/ui/banner.c | |||
@@ -327,7 +327,8 @@ iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { | |||
327 | else { | 327 | else { |
328 | switch (item->code) { | 328 | switch (item->code) { |
329 | case missingGlyphs_GmStatusCode: | 329 | case missingGlyphs_GmStatusCode: |
330 | postCommandf_App("open newtab:1 url:about:fonts"); | 330 | //postCommandf_App("open newtab:1 url:about:fonts"); |
331 | makeGlyphFinder_Widget(); | ||
331 | break; | 332 | break; |
332 | case ansiEscapes_GmStatusCode: | 333 | case ansiEscapes_GmStatusCode: |
333 | makeQuestion_Widget( | 334 | makeQuestion_Widget( |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 25559890..97ecb4ba 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -2661,7 +2661,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
2661 | if (loadArchive_FontPack(fp, zip)) { | 2661 | if (loadArchive_FontPack(fp, zip)) { |
2662 | appendFormat_String(&str, "# " fontpack_Icon "%s\n%s", | 2662 | appendFormat_String(&str, "# " fontpack_Icon "%s\n%s", |
2663 | cstr_String(id_FontPack(fp).id), | 2663 | cstr_String(id_FontPack(fp).id), |
2664 | cstrCollect_String(infoText_FontPack(fp))); | 2664 | cstrCollect_String(infoText_FontPack(fp, iTrue))); |
2665 | } | 2665 | } |
2666 | appendCStr_String(&str, "\n"); | 2666 | appendCStr_String(&str, "\n"); |
2667 | appendCStr_String(&str, cstr_Lang("fontpack.help")); | 2667 | appendCStr_String(&str, cstr_Lang("fontpack.help")); |
diff --git a/src/ui/text.c b/src/ui/text.c index 200108ed..ab2af2b2 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -247,8 +247,6 @@ struct Impl_CacheRow { | |||
247 | }; | 247 | }; |
248 | 248 | ||
249 | struct Impl_Text { | 249 | struct Impl_Text { |
250 | // enum iTextFont contentFont; | ||
251 | // enum iTextFont headingFont; | ||
252 | float contentFontSize; | 250 | float contentFontSize; |
253 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ | 251 | iArray fonts; /* fonts currently selected for use (incl. all styles/sizes) */ |
254 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ | 252 | int overrideFontId; /* always checked for glyphs first, regardless of which font is used */ |
@@ -264,7 +262,8 @@ struct Impl_Text { | |||
264 | int ansiFlags; | 262 | int ansiFlags; |
265 | int baseFontId; /* base attributes (for restoring via escapes) */ | 263 | int baseFontId; /* base attributes (for restoring via escapes) */ |
266 | int baseFgColorId; | 264 | int baseFgColorId; |
267 | iBool missingGlyphs; /* true if a glyph couldn't be found */ | 265 | iBool missingGlyphs; /* true if a glyph couldn't be found */ |
266 | iChar missingChars[20]; /* rotating buffer of the latest missing characters */ | ||
268 | }; | 267 | }; |
269 | 268 | ||
270 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 269 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) |
@@ -341,6 +340,8 @@ static void initFonts_Text_(iText *d) { | |||
341 | printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); | 340 | printf("[Text] %zu font variants ready\n", size_Array(&d->fonts)); |
342 | #endif | 341 | #endif |
343 | gap_Text = iRound(gap_UI * d->contentFontSize); | 342 | gap_Text = iRound(gap_UI * d->contentFontSize); |
343 | // d->missingGlyphs = iFalse; | ||
344 | // iZap(d->missingChars); | ||
344 | } | 345 | } |
345 | 346 | ||
346 | static void deinitFonts_Text_(iText *d) { | 347 | static void deinitFonts_Text_(iText *d) { |
@@ -407,6 +408,7 @@ void init_Text(iText *d, SDL_Renderer *render) { | |||
407 | d->baseFontId = -1; | 408 | d->baseFontId = -1; |
408 | d->baseFgColorId = -1; | 409 | d->baseFgColorId = -1; |
409 | d->missingGlyphs = iFalse; | 410 | d->missingGlyphs = iFalse; |
411 | iZap(d->missingChars); | ||
410 | d->render = render; | 412 | d->render = render; |
411 | /* A grayscale palette for rasterized glyphs. */ { | 413 | /* A grayscale palette for rasterized glyphs. */ { |
412 | SDL_Color colors[256]; | 414 | SDL_Color colors[256]; |
@@ -593,8 +595,23 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
593 | } | 595 | } |
594 | } | 596 | } |
595 | if (!*glyphIndex) { | 597 | if (!*glyphIndex) { |
596 | activeText_->missingGlyphs = iTrue; | 598 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int) ch); fflush(stderr); |
597 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); | 599 | iText *tx = activeText_; |
600 | tx->missingGlyphs = iTrue; | ||
601 | /* Remember a few of the latest missing characters. */ | ||
602 | iBool gotIt = iFalse; | ||
603 | for (size_t i = 0; i < iElemCount(tx->missingChars); i++) { | ||
604 | if (tx->missingChars[i] == ch) { | ||
605 | gotIt = iTrue; | ||
606 | break; | ||
607 | } | ||
608 | } | ||
609 | if (!gotIt) { | ||
610 | memmove(tx->missingChars + 1, | ||
611 | tx->missingChars, | ||
612 | sizeof(tx->missingChars) - sizeof(tx->missingChars[0])); | ||
613 | tx->missingChars[0] = ch; | ||
614 | } | ||
598 | } | 615 | } |
599 | return d; | 616 | return d; |
600 | } | 617 | } |
@@ -2233,6 +2250,19 @@ iBool checkMissing_Text(void) { | |||
2233 | return missing; | 2250 | return missing; |
2234 | } | 2251 | } |
2235 | 2252 | ||
2253 | iChar missing_Text(size_t index) { | ||
2254 | const iText *d = activeText_; | ||
2255 | if (index >= iElemCount(d->missingChars)) { | ||
2256 | return 0; | ||
2257 | } | ||
2258 | return d->missingChars[index]; | ||
2259 | } | ||
2260 | |||
2261 | void resetMissing_Text(iText *d) { | ||
2262 | d->missingGlyphs = iFalse; | ||
2263 | iZap(d->missingChars); | ||
2264 | } | ||
2265 | |||
2236 | SDL_Texture *glyphCache_Text(void) { | 2266 | SDL_Texture *glyphCache_Text(void) { |
2237 | return activeText_->cache; | 2267 | return activeText_->cache; |
2238 | } | 2268 | } |
diff --git a/src/ui/text.h b/src/ui/text.h index b952df84..e741880d 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -227,6 +227,8 @@ struct Impl_WrapText { | |||
227 | iTextMetrics measure_WrapText (iWrapText *, int fontId); | 227 | iTextMetrics measure_WrapText (iWrapText *, int fontId); |
228 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); | 228 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); |
229 | 229 | ||
230 | iChar missing_Text (size_t index); | ||
231 | void resetMissing_Text (iText *); | ||
230 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ | 232 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ |
231 | SDL_Texture * glyphCache_Text (void); | 233 | SDL_Texture * glyphCache_Text (void); |
232 | 234 | ||
diff --git a/src/ui/util.c b/src/ui/util.c index 31907721..8e71dcec 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -3451,6 +3451,54 @@ iWidget *makeTranslation_Widget(iWidget *parent) { | |||
3451 | return dlg; | 3451 | return dlg; |
3452 | } | 3452 | } |
3453 | 3453 | ||
3454 | iWidget *makeGlyphFinder_Widget(void) { | ||
3455 | iString msg; | ||
3456 | iString command; | ||
3457 | init_String(&msg); | ||
3458 | initCStr_String(&command, "!font.find chars:"); | ||
3459 | for (size_t i = 0; ; i++) { | ||
3460 | iChar ch = missing_Text(i); | ||
3461 | if (!ch) break; | ||
3462 | appendFormat_String(&msg, " U+%04X", ch); | ||
3463 | appendChar_String(&command, ch); | ||
3464 | } | ||
3465 | iArray items; | ||
3466 | init_Array(&items, sizeof(iMenuItem)); | ||
3467 | if (!isEmpty_String(&msg)) { | ||
3468 | prependCStr_String(&msg, "${dlg.glyphfinder.missing} "); | ||
3469 | appendCStr_String(&msg, "\n\n${dlg.glyphfinder.help}"); | ||
3470 | pushBackN_Array( | ||
3471 | &items, | ||
3472 | (iMenuItem[]){ | ||
3473 | { "${menu.fonts}", 0, 0, "!open newtab:1 url:about:fonts" }, | ||
3474 | { "${dlg.glyphfinder.disable}", 0, 0, "prefs.font.warnmissing.changed arg:0" }, | ||
3475 | { "---" }, | ||
3476 | { uiTextCaution_ColorEscape magnifyingGlass_Icon " ${dlg.glyphfinder.search}", | ||
3477 | 0, | ||
3478 | 0, | ||
3479 | cstr_String(&command) }, | ||
3480 | { "${close}", 0, 0, "cancel" } }, | ||
3481 | 5); | ||
3482 | } | ||
3483 | else { | ||
3484 | setCStr_String(&msg, "${dlg.glyphfinder.help.empty}"); | ||
3485 | pushBackN_Array(&items, | ||
3486 | (iMenuItem[]){ { "${menu.reload}", 0, 0, "navigate.reload" }, | ||
3487 | { "${close}", 0, 0, "cancel" } }, | ||
3488 | 2); | ||
3489 | } | ||
3490 | iWidget *dlg = makeQuestion_Widget("${heading.glyphfinder}", cstr_String(&msg), | ||
3491 | constData_Array(&items), | ||
3492 | size_Array(&items)); | ||
3493 | arrange_Widget(dlg); | ||
3494 | deinit_Array(&items); | ||
3495 | deinit_String(&command); | ||
3496 | deinit_String(&msg); | ||
3497 | return dlg; | ||
3498 | } | ||
3499 | |||
3500 | /*----------------------------------------------------------------------------------------------*/ | ||
3501 | |||
3454 | void init_PerfTimer(iPerfTimer *d) { | 3502 | void init_PerfTimer(iPerfTimer *d) { |
3455 | d->ticks = SDL_GetPerformanceCounter(); | 3503 | d->ticks = SDL_GetPerformanceCounter(); |
3456 | } | 3504 | } |
diff --git a/src/ui/util.h b/src/ui/util.h index 98ce784c..0289d579 100644 --- a/src/ui/util.h +++ b/src/ui/util.h | |||
@@ -342,6 +342,7 @@ iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *titl | |||
342 | iWidget * makeIdentityCreation_Widget (void); | 342 | iWidget * makeIdentityCreation_Widget (void); |
343 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); | 343 | iWidget * makeFeedSettings_Widget (uint32_t bookmarkId); |
344 | iWidget * makeTranslation_Widget (iWidget *parent); | 344 | iWidget * makeTranslation_Widget (iWidget *parent); |
345 | iWidget * makeGlyphFinder_Widget (void); | ||
345 | 346 | ||
346 | const char * languageId_String (const iString *menuItemLabel); | 347 | const char * languageId_String (const iString *menuItemLabel); |
347 | int languageIndex_CStr (const char *langId); | 348 | int languageIndex_CStr (const char *langId); |