diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-25 22:10:04 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-10-25 22:10:04 +0300 |
commit | b4e13bb58f644276469a69c4bd93e22381aa0002 (patch) | |
tree | 476693d897b2ac0f26b263bb871f65a2140c6637 /src | |
parent | b80c958840a3ac0f824de3ae12717963ba679b20 (diff) |
Document presentation warnings
Warn the user about missing glyphs and potentially unsupported ANSI escapes.
TODO: Site-specific setting for dismissed warning; fonts preference about missing glyph warnings.
Diffstat (limited to 'src')
-rw-r--r-- | src/gmdocument.c | 47 | ||||
-rw-r--r-- | src/gmdocument.h | 6 | ||||
-rw-r--r-- | src/gmutil.c | 8 | ||||
-rw-r--r-- | src/gmutil.h | 2 | ||||
-rw-r--r-- | src/ui/banner.c | 77 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 27 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 8 | ||||
-rw-r--r-- | src/ui/text.c | 9 | ||||
-rw-r--r-- | src/ui/text.h | 1 |
9 files changed, 156 insertions, 29 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index bd3fa788..c7d5501f 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -107,6 +107,7 @@ struct Impl_GmDocument { | |||
107 | iChar siteIcon; | 107 | iChar siteIcon; |
108 | iMedia * media; | 108 | iMedia * media; |
109 | iStringSet *openURLs; /* currently open URLs for highlighting links */ | 109 | iStringSet *openURLs; /* currently open URLs for highlighting links */ |
110 | int warnings; | ||
110 | iBool isPaletteValid; | 111 | iBool isPaletteValid; |
111 | iColor palette[tmMax_ColorId]; /* copy of the color palette */ | 112 | iColor palette[tmMax_ColorId]; /* copy of the color palette */ |
112 | }; | 113 | }; |
@@ -596,6 +597,8 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
596 | isPreformat = iTrue; | 597 | isPreformat = iTrue; |
597 | isFirstText = iFalse; | 598 | isFirstText = iFalse; |
598 | } | 599 | } |
600 | d->warnings &= ~missingGlyphs_GmDocumentWarning; | ||
601 | checkMissing_Text(); /* clear the flag */ | ||
599 | setAnsiFlags_Text(d->theme.ansiEscapes); | 602 | setAnsiFlags_Text(d->theme.ansiEscapes); |
600 | while (nextSplit_Rangecc(content, "\n", &contentLine)) { | 603 | while (nextSplit_Rangecc(content, "\n", &contentLine)) { |
601 | iRangecc line = contentLine; /* `line` will be trimmed; modifying would confuse `nextSplit_Rangecc` */ | 604 | iRangecc line = contentLine; /* `line` will be trimmed; modifying would confuse `nextSplit_Rangecc` */ |
@@ -1045,6 +1048,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
1045 | } | 1048 | } |
1046 | #endif | 1049 | #endif |
1047 | d->size.y = pos.y; | 1050 | d->size.y = pos.y; |
1051 | if (checkMissing_Text()) { | ||
1052 | d->warnings |= missingGlyphs_GmDocumentWarning; | ||
1053 | } | ||
1048 | /* Go over the preformatted blocks and mark them wide if at least one run is wide. */ { | 1054 | /* Go over the preformatted blocks and mark them wide if at least one run is wide. */ { |
1049 | /* TODO: Store the dimensions and ranges for later access. */ | 1055 | /* TODO: Store the dimensions and ranges for later access. */ |
1050 | iForEach(Array, i, &d->layout) { | 1056 | iForEach(Array, i, &d->layout) { |
@@ -1084,6 +1090,7 @@ void init_GmDocument(iGmDocument *d) { | |||
1084 | d->siteIcon = 0; | 1090 | d->siteIcon = 0; |
1085 | d->media = new_Media(); | 1091 | d->media = new_Media(); |
1086 | d->openURLs = NULL; | 1092 | d->openURLs = NULL; |
1093 | d->warnings = 0; | ||
1087 | d->isPaletteValid = iFalse; | 1094 | d->isPaletteValid = iFalse; |
1088 | iZap(d->palette); | 1095 | iZap(d->palette); |
1089 | } | 1096 | } |
@@ -1123,16 +1130,28 @@ static void setDerivedThemeColors_(enum iGmDocumentTheme theme) { | |||
1123 | mix_Color(get_Color(tmBannerTitle_ColorId), | 1130 | mix_Color(get_Color(tmBannerTitle_ColorId), |
1124 | get_Color(tmBackground_ColorId), | 1131 | get_Color(tmBackground_ColorId), |
1125 | theme == colorfulDark_GmDocumentTheme ? 0.55f : 0)); | 1132 | theme == colorfulDark_GmDocumentTheme ? 0.55f : 0)); |
1126 | const int bannerItemFg = isDark_GmDocumentTheme(currentTheme_()) ? white_ColorId : black_ColorId; | 1133 | /* Banner colors. */ |
1127 | set_Color(tmBannerItemBackground_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId), | 1134 | if (theme == highContrast_GmDocumentTheme) { |
1128 | get_Color(tmBannerTitle_ColorId), 0.1f)); | 1135 | set_Color(tmBannerItemBackground_ColorId, get_Color(tmBannerBackground_ColorId)); |
1129 | set_Color(tmBannerItemFrame_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId), | 1136 | set_Color(tmBannerItemFrame_ColorId, get_Color(tmBannerIcon_ColorId)); |
1130 | get_Color(tmBannerTitle_ColorId), 0.4f)); | 1137 | set_Color(tmBannerItemTitle_ColorId, get_Color(tmBannerTitle_ColorId)); |
1131 | set_Color(tmBannerItemText_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId), | 1138 | set_Color(tmBannerItemText_ColorId, get_Color(tmBannerTitle_ColorId)); |
1132 | get_Color(bannerItemFg), 0.75f)); | 1139 | } |
1133 | set_Color(tmBannerItemTitle_ColorId, get_Color(bannerItemFg)); | 1140 | else { |
1141 | const int bannerItemFg = isDark_GmDocumentTheme(currentTheme_()) ? white_ColorId : black_ColorId; | ||
1142 | set_Color(tmBannerItemBackground_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId), | ||
1143 | get_Color(tmBannerTitle_ColorId), 0.1f)); | ||
1144 | set_Color(tmBannerItemFrame_ColorId, mix_Color(get_Color(tmBannerBackground_ColorId), | ||
1145 | get_Color(tmBannerTitle_ColorId), 0.4f)); | ||
1146 | set_Color(tmBannerItemText_ColorId, mix_Color(get_Color(tmBannerTitle_ColorId), | ||
1147 | get_Color(bannerItemFg), 0.5f)); | ||
1148 | set_Color(tmBannerItemTitle_ColorId, get_Color(bannerItemFg)); | ||
1149 | } | ||
1150 | /* Modified backgrounds. */ | ||
1134 | set_Color(tmBackgroundAltText_ColorId, | 1151 | set_Color(tmBackgroundAltText_ColorId, |
1135 | mix_Color(get_Color(tmQuoteIcon_ColorId), get_Color(tmBackground_ColorId), 0.85f)); | 1152 | mix_Color(get_Color(tmQuoteIcon_ColorId), get_Color(tmBackground_ColorId), 0.85f)); |
1153 | set_Color(tmFrameAltText_ColorId, | ||
1154 | mix_Color(get_Color(tmQuoteIcon_ColorId), get_Color(tmBackground_ColorId), 0.4f)); | ||
1136 | set_Color(tmBackgroundOpenLink_ColorId, | 1155 | set_Color(tmBackgroundOpenLink_ColorId, |
1137 | mix_Color(get_Color(tmLinkText_ColorId), get_Color(tmBackground_ColorId), 0.90f)); | 1156 | mix_Color(get_Color(tmLinkText_ColorId), get_Color(tmBackground_ColorId), 0.90f)); |
1138 | set_Color(tmFrameOpenLink_ColorId, | 1157 | set_Color(tmFrameOpenLink_ColorId, |
@@ -2008,6 +2027,14 @@ void setSource_GmDocument(iGmDocument *d, const iString *source, int width, int | |||
2008 | /* Normalize and convert to Gemtext if needed. */ | 2027 | /* Normalize and convert to Gemtext if needed. */ |
2009 | set_String(&d->unormSource, source); | 2028 | set_String(&d->unormSource, source); |
2010 | set_String(&d->source, source); | 2029 | set_String(&d->source, source); |
2030 | /* Detect use of ANSI escapes. */ { | ||
2031 | iRegExp *ansiEsc = new_RegExp("\x1b[[()]([0-9;AB]*?)m", 0); | ||
2032 | iRegExpMatch m; | ||
2033 | init_RegExpMatch(&m); | ||
2034 | const iBool found = matchString_RegExp(ansiEsc, &d->unormSource, &m); | ||
2035 | iChangeFlags(d->warnings, ansiEscapes_GmDocumentWarning, found); | ||
2036 | iRelease(ansiEsc); | ||
2037 | } | ||
2011 | if (d->format == gemini_SourceFormat) { | 2038 | if (d->format == gemini_SourceFormat) { |
2012 | d->theme.ansiEscapes = prefs_App()->gemtextAnsiEscapes; | 2039 | d->theme.ansiEscapes = prefs_App()->gemtextAnsiEscapes; |
2013 | } | 2040 | } |
@@ -2157,6 +2184,10 @@ size_t memorySize_GmDocument(const iGmDocument *d) { | |||
2157 | memorySize_Media(d->media); | 2184 | memorySize_Media(d->media); |
2158 | } | 2185 | } |
2159 | 2186 | ||
2187 | int warnings_GmDocument(const iGmDocument *d) { | ||
2188 | return d->warnings; | ||
2189 | } | ||
2190 | |||
2160 | iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { | 2191 | iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { |
2161 | const char * src = constBegin_String(&d->source); | 2192 | const char * src = constBegin_String(&d->source); |
2162 | const size_t startPos = (start ? start - src : 0); | 2193 | const size_t startPos = (start ? start - src : 0); |
diff --git a/src/gmdocument.h b/src/gmdocument.h index e010ad43..0c24302f 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -172,6 +172,11 @@ iRangecc findLoc_GmRun (const iGmRun *, iInt2 pos); | |||
172 | iDeclareClass(GmDocument) | 172 | iDeclareClass(GmDocument) |
173 | iDeclareObjectConstruction(GmDocument) | 173 | iDeclareObjectConstruction(GmDocument) |
174 | 174 | ||
175 | enum iGmDocumentWarning { | ||
176 | ansiEscapes_GmDocumentWarning = iBit(1), | ||
177 | missingGlyphs_GmDocumentWarning = iBit(2), | ||
178 | }; | ||
179 | |||
175 | /* | 180 | /* |
176 | enum iGmDocumentBanner { | 181 | enum iGmDocumentBanner { |
177 | none_GmDocumentBanner, | 182 | none_GmDocumentBanner, |
@@ -221,6 +226,7 @@ iInt2 size_GmDocument (const iGmDocument *); | |||
221 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ | 226 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ |
222 | const iString * source_GmDocument (const iGmDocument *); | 227 | const iString * source_GmDocument (const iGmDocument *); |
223 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ | 228 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ |
229 | int warnings_GmDocument (const iGmDocument *); | ||
224 | 230 | ||
225 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); | 231 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); |
226 | iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); | 232 | iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); |
diff --git a/src/gmutil.c b/src/gmutil.c index 264e7ac8..22219221 100644 --- a/src/gmutil.c +++ b/src/gmutil.c | |||
@@ -755,6 +755,14 @@ static const struct { | |||
755 | { 0x1f645, /* no good */ | 755 | { 0x1f645, /* no good */ |
756 | "${error.certverify}", | 756 | "${error.certverify}", |
757 | "${error.certverify.msg}" } }, | 757 | "${error.certverify.msg}" } }, |
758 | { ansiEscapes_GmStatusCode, | ||
759 | { 0x1f5b3, /* old computer */ | ||
760 | "${error.ansi}", | ||
761 | "${error.ansi.msg}" } }, | ||
762 | { missingGlyphs_GmStatusCode, | ||
763 | { 0x1f520, /* ABCD */ | ||
764 | "${error.glyphs}", | ||
765 | "${error.glyphs.msg}" } }, | ||
758 | { temporaryFailure_GmStatusCode, | 766 | { temporaryFailure_GmStatusCode, |
759 | { 0x1f50c, /* electric plug */ | 767 | { 0x1f50c, /* electric plug */ |
760 | "${error.temporary}", | 768 | "${error.temporary}", |
diff --git a/src/gmutil.h b/src/gmutil.h index c65a17e6..6b10b444 100644 --- a/src/gmutil.h +++ b/src/gmutil.h | |||
@@ -45,6 +45,8 @@ enum iGmStatusCode { | |||
45 | tlsFailure_GmStatusCode, | 45 | tlsFailure_GmStatusCode, |
46 | tlsServerCertificateExpired_GmStatusCode, | 46 | tlsServerCertificateExpired_GmStatusCode, |
47 | tlsServerCertificateNotVerified_GmStatusCode, | 47 | tlsServerCertificateNotVerified_GmStatusCode, |
48 | ansiEscapes_GmStatusCode, | ||
49 | missingGlyphs_GmStatusCode, | ||
48 | 50 | ||
49 | none_GmStatusCode = 0, | 51 | none_GmStatusCode = 0, |
50 | /* general status code categories */ | 52 | /* general status code categories */ |
diff --git a/src/ui/banner.c b/src/ui/banner.c index d95c853b..e817f1bb 100644 --- a/src/ui/banner.c +++ b/src/ui/banner.c | |||
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
27 | #include "lang.h" | 27 | #include "lang.h" |
28 | #include "paint.h" | 28 | #include "paint.h" |
29 | #include "util.h" | 29 | #include "util.h" |
30 | #include "app.h" | ||
30 | 31 | ||
31 | iDeclareType(BannerItem) | 32 | iDeclareType(BannerItem) |
32 | 33 | ||
@@ -53,27 +54,33 @@ struct Impl_Banner { | |||
53 | iRect rect; | 54 | iRect rect; |
54 | iString site; | 55 | iString site; |
55 | iString icon; | 56 | iString icon; |
57 | int siteHeight; | ||
56 | iArray items; | 58 | iArray items; |
57 | iBool isClick; | 59 | iBool isClick; |
58 | }; | 60 | }; |
59 | 61 | ||
60 | iDefineTypeConstruction(Banner) | 62 | iDefineTypeConstruction(Banner) |
61 | 63 | ||
64 | #define itemGap_Banner_ (3 * gap_UI) | ||
65 | #define itemVPad_Banner_ (2 * gap_UI) | ||
66 | #define itemHPad_Banner_ (3 * gap_UI) | ||
67 | #define bottomPad_Banner_ (4 * gap_UI) | ||
68 | |||
62 | static void updateHeight_Banner_(iBanner *d) { | 69 | static void updateHeight_Banner_(iBanner *d) { |
63 | d->rect.size.y = 0; | 70 | d->rect.size.y = 0; |
64 | if (!isEmpty_String(&d->site)) { | 71 | if (!isEmpty_String(&d->site)) { |
65 | d->rect.size.y += lineHeight_Text(banner_FontId) * 2; | 72 | d->siteHeight = lineHeight_Text(banner_FontId) * 2; |
73 | d->rect.size.y += d->siteHeight; | ||
66 | } | 74 | } |
67 | const size_t numItems = size_Array(&d->items); | 75 | const size_t numItems = size_Array(&d->items); |
68 | if (numItems) { | 76 | if (numItems) { |
69 | const int outerPad = 2 * gap_UI; | ||
70 | const int innerPad = gap_UI; | 77 | const int innerPad = gap_UI; |
71 | iConstForEach(Array, i, &d->items) { | 78 | iConstForEach(Array, i, &d->items) { |
72 | const iBannerItem *item = i.value; | 79 | const iBannerItem *item = i.value; |
73 | d->rect.size.y += item->height; | 80 | d->rect.size.y += item->height; |
74 | } | 81 | } |
75 | d->rect.size.y += (numItems - 1) * innerPad; | 82 | d->rect.size.y += (numItems - 1) * itemGap_Banner_; |
76 | d->rect.size.y += outerPad; | 83 | d->rect.size.y += bottomPad_Banner_; |
77 | } | 84 | } |
78 | } | 85 | } |
79 | 86 | ||
@@ -99,9 +106,9 @@ void setOwner_Banner(iBanner *d, iDocumentWidget *owner) { | |||
99 | 106 | ||
100 | static void updateItemHeight_Banner_(const iBanner *d, iBannerItem *item) { | 107 | static void updateItemHeight_Banner_(const iBanner *d, iBannerItem *item) { |
101 | item->height = measureWrapRange_Text(uiContent_FontId, | 108 | item->height = measureWrapRange_Text(uiContent_FontId, |
102 | width_Rect(d->rect) - 6 * gap_UI, | 109 | width_Rect(d->rect) - 2 * itemHPad_Banner_, |
103 | range_String(&item->text)) | 110 | range_String(&item->text)) |
104 | .bounds.size.y + 4 * gap_UI; | 111 | .bounds.size.y + 2 * itemVPad_Banner_; |
105 | } | 112 | } |
106 | 113 | ||
107 | void setWidth_Banner(iBanner *d, int width) { | 114 | void setWidth_Banner(iBanner *d, int width) { |
@@ -186,6 +193,7 @@ void draw_Banner(const iBanner *d) { | |||
186 | return; | 193 | return; |
187 | } | 194 | } |
188 | iRect bounds = d->rect; | 195 | iRect bounds = d->rect; |
196 | /* TODO: use d->siteHeight */ | ||
189 | iInt2 pos = addY_I2(topLeft_Rect(bounds), lineHeight_Text(banner_FontId) / 2); | 197 | iInt2 pos = addY_I2(topLeft_Rect(bounds), lineHeight_Text(banner_FontId) / 2); |
190 | iPaint p; | 198 | iPaint p; |
191 | init_Paint(&p); | 199 | init_Paint(&p); |
@@ -218,16 +226,28 @@ void draw_Banner(const iBanner *d) { | |||
218 | setBaseAttributes_Text(uiContent_FontId, tmBannerItemText_ColorId); | 226 | setBaseAttributes_Text(uiContent_FontId, tmBannerItemText_ColorId); |
219 | iWrapText wt = { | 227 | iWrapText wt = { |
220 | .text = range_String(&item->text), | 228 | .text = range_String(&item->text), |
221 | .maxWidth = width_Rect(itemRect) - 6 * gap_UI, | 229 | .maxWidth = width_Rect(itemRect) - 2 * itemHPad_Banner_, |
222 | .mode = word_WrapTextMode | 230 | .mode = word_WrapTextMode |
223 | }; | 231 | }; |
224 | draw_WrapText(&wt, uiContent_FontId, add_I2(pos, init_I2(3 * gap_UI, 2 * gap_UI)), | 232 | draw_WrapText(&wt, uiContent_FontId, add_I2(pos, init_I2(itemHPad_Banner_, itemVPad_Banner_)), |
225 | tmBannerItemText_ColorId); | 233 | tmBannerItemText_ColorId); |
226 | pos.y += innerPad; | 234 | pos.y += item->height + itemGap_Banner_; |
227 | } | 235 | } |
228 | setBaseAttributes_Text(-1, -1); | 236 | setBaseAttributes_Text(-1, -1); |
229 | } | 237 | } |
230 | 238 | ||
239 | static size_t itemAtCoord_Banner_(const iBanner *d, iInt2 coord) { | ||
240 | iInt2 pos = addY_I2(topLeft_Rect(d->rect), lineHeight_Text(banner_FontId) * 2); | ||
241 | iConstForEach(Array, i, &d->items) { | ||
242 | const iBannerItem *item = i.value; | ||
243 | if (contains_Rect((iRect){ pos, init_I2(d->rect.size.x, item->height)}, coord)) { | ||
244 | return index_ArrayConstIterator(&i); | ||
245 | } | ||
246 | pos.y += itemGap_Banner_ + item->height; | ||
247 | } | ||
248 | return iInvalidPos; | ||
249 | } | ||
250 | |||
231 | iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { | 251 | iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { |
232 | iWidget *w = as_Widget(d->doc); | 252 | iWidget *w = as_Widget(d->doc); |
233 | switch (ev->type) { | 253 | switch (ev->type) { |
@@ -240,14 +260,49 @@ iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { | |||
240 | case SDL_MOUSEBUTTONUP: | 260 | case SDL_MOUSEBUTTONUP: |
241 | /* Clicking on the top/side banner navigates to site root. */ | 261 | /* Clicking on the top/side banner navigates to site root. */ |
242 | if (ev->button.button == SDL_BUTTON_LEFT) { | 262 | if (ev->button.button == SDL_BUTTON_LEFT) { |
243 | const iBool isInside = contains_Rect(d->rect, init_I2(ev->button.x, ev->button.y)); | 263 | const iInt2 coord = init_I2(ev->button.x, ev->button.y); |
264 | const iBool isInside = contains_Rect(d->rect, coord); | ||
244 | if (isInside && ev->button.state == SDL_PRESSED) { | 265 | if (isInside && ev->button.state == SDL_PRESSED) { |
245 | d->isClick = iTrue; | 266 | d->isClick = iTrue; |
246 | return iTrue; | 267 | return iTrue; |
247 | } | 268 | } |
248 | else if (ev->button.state == SDL_RELEASED) { | 269 | else if (ev->button.state == SDL_RELEASED) { |
249 | if (d->isClick && isInside) { | 270 | if (d->isClick && isInside) { |
250 | postCommand_Widget(d->doc, "navigate.root"); | 271 | const size_t index = itemAtCoord_Banner_(d, coord); |
272 | if (index == iInvalidPos) { | ||
273 | if (coord.y < top_Rect(d->rect) + d->siteHeight) { | ||
274 | postCommand_Widget(d->doc, "navigate.root"); | ||
275 | } | ||
276 | } | ||
277 | else { | ||
278 | const iBannerItem *item = constAt_Array(&d->items, index); | ||
279 | if (item->type == error_BannerType) { | ||
280 | postCommand_Widget(d->doc, "document.info"); | ||
281 | } | ||
282 | else { | ||
283 | switch (item->code) { | ||
284 | case missingGlyphs_GmStatusCode: | ||
285 | postCommandf_App("open newtab:1 url:about:fonts"); | ||
286 | break; | ||
287 | case ansiEscapes_GmStatusCode: | ||
288 | makeQuestion_Widget(uiHeading_ColorEscape "${heading.dismiss.warning}", | ||
289 | format_Lang("${dlg.dismiss.ansi}", | ||
290 | format_CStr(uiTextStrong_ColorEscape "%s" | ||
291 | restore_ColorEscape, cstr_Rangecc(urlHost_String(url_DocumentWidget(d->doc))))), | ||
292 | (iMenuItem[]){ { "${cancel}" }, | ||
293 | { uiTextAction_ColorEscape "${dlg.dismiss.warning}", | ||
294 | SDLK_RETURN, 0, | ||
295 | format_CStr("document.dismiss warning:%d", | ||
296 | ansiEscapes_GmDocumentWarning) | ||
297 | } | ||
298 | }, 2); | ||
299 | break; | ||
300 | default: | ||
301 | postCommand_Widget(d->doc, "document.info"); | ||
302 | break; | ||
303 | } | ||
304 | } | ||
305 | } | ||
251 | } | 306 | } |
252 | d->isClick = iFalse; | 307 | d->isClick = iFalse; |
253 | } | 308 | } |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 13a8dae7..755cec6a 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -934,7 +934,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
934 | updateSideOpacity_DocumentWidget_(d, iTrue); | 934 | updateSideOpacity_DocumentWidget_(d, iTrue); |
935 | animateMedia_DocumentWidget_(d); | 935 | animateMedia_DocumentWidget_(d); |
936 | setPos_Banner(d->banner, addY_I2(topLeft_Rect(documentBounds_DocumentWidget_(d)), | 936 | setPos_Banner(d->banner, addY_I2(topLeft_Rect(documentBounds_DocumentWidget_(d)), |
937 | -pos_SmoothScroll(&d->scrollY))); | 937 | -pos_SmoothScroll(&d->scrollY))); |
938 | /*init_I2(documentBounds_DocumentWidget_(d).pos.x, | 938 | /*init_I2(documentBounds_DocumentWidget_(d).pos.x, |
939 | viewPos_DocumentWidget_(d) - | 939 | viewPos_DocumentWidget_(d) - |
940 | documentTopPad_DocumentWidget_(d)));*/ | 940 | documentTopPad_DocumentWidget_(d)));*/ |
@@ -1291,7 +1291,6 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
1291 | iRelease(errorDoc); | 1291 | iRelease(errorDoc); |
1292 | clear_Banner(d->banner); | 1292 | clear_Banner(d->banner); |
1293 | add_Banner(d->banner, error_BannerType, code, meta); | 1293 | add_Banner(d->banner, error_BannerType, code, meta); |
1294 | // translate_Lang(src); | ||
1295 | d->state = ready_RequestState; | 1294 | d->state = ready_RequestState; |
1296 | setSource_DocumentWidget(d, src); | 1295 | setSource_DocumentWidget(d, src); |
1297 | updateTheme_DocumentWidget_(d); | 1296 | updateTheme_DocumentWidget_(d); |
@@ -1798,6 +1797,16 @@ static void cacheDocumentGlyphs_DocumentWidget_(const iDocumentWidget *d) { | |||
1798 | } | 1797 | } |
1799 | } | 1798 | } |
1800 | 1799 | ||
1800 | static void addBannerWarnings_DocumentWidget_(iDocumentWidget *d) { | ||
1801 | if (warnings_GmDocument(d->doc) & missingGlyphs_GmDocumentWarning) { | ||
1802 | add_Banner(d->banner, warning_BannerType, missingGlyphs_GmStatusCode, NULL); | ||
1803 | /* TODO: List one or more of the missing characters and/or their Unicode blocks? */ | ||
1804 | } | ||
1805 | if (warnings_GmDocument(d->doc) & ansiEscapes_GmDocumentWarning) { | ||
1806 | add_Banner(d->banner, warning_BannerType, ansiEscapes_GmStatusCode, NULL); | ||
1807 | } | ||
1808 | } | ||
1809 | |||
1801 | static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY, | 1810 | static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY, |
1802 | const iGmResponse *resp, iGmDocument *cachedDoc) { | 1811 | const iGmResponse *resp, iGmDocument *cachedDoc) { |
1803 | setLinkNumberMode_DocumentWidget_(d, iFalse); | 1812 | setLinkNumberMode_DocumentWidget_(d, iFalse); |
@@ -1824,6 +1833,7 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1824 | // (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); | 1833 | // (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); |
1825 | clear_Banner(d->banner); | 1834 | clear_Banner(d->banner); |
1826 | updateBanner_DocumentWidget_(d); | 1835 | updateBanner_DocumentWidget_(d); |
1836 | addBannerWarnings_DocumentWidget_(d); | ||
1827 | } | 1837 | } |
1828 | d->state = ready_RequestState; | 1838 | d->state = ready_RequestState; |
1829 | postProcessRequestContent_DocumentWidget_(d, iTrue); | 1839 | postProcessRequestContent_DocumentWidget_(d, iTrue); |
@@ -2864,10 +2874,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2864 | size_Array(items)); | 2874 | size_Array(items)); |
2865 | delete_Array(items); | 2875 | delete_Array(items); |
2866 | /* Enforce a minimum size. */ | 2876 | /* Enforce a minimum size. */ |
2867 | iWidget *sizer = new_Widget(); | 2877 | // iWidget *sizer = new_Widget(); |
2868 | setFixedSize_Widget(sizer, init_I2(gap_UI * 65, 1)); | 2878 | // setFixedSize_Widget(sizer, init_I2(gap_UI * 65, 1)); |
2869 | addChildFlags_Widget(dlg, iClob(sizer), frameless_WidgetFlag); | 2879 | // addChildFlags_Widget(dlg, iClob(sizer), frameless_WidgetFlag); |
2870 | setFlags_Widget(dlg, centerHorizontal_WidgetFlag, iFalse); | 2880 | // setFlags_Widget(dlg, centerHorizontal_WidgetFlag, iFalse); |
2871 | if (deviceType_App() != phone_AppDeviceType) { | 2881 | if (deviceType_App() != phone_AppDeviceType) { |
2872 | const iWidget *lockButton = findWidget_Root("navbar.lock"); | 2882 | const iWidget *lockButton = findWidget_Root("navbar.lock"); |
2873 | setPos_Widget(dlg, windowToLocal_Widget(dlg, bottomLeft_Rect(bounds_Widget(lockButton)))); | 2883 | setPos_Widget(dlg, windowToLocal_Widget(dlg, bottomLeft_Rect(bounds_Widget(lockButton)))); |
@@ -2989,6 +2999,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2989 | if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { | 2999 | if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { |
2990 | init_Anim(&d->scrollY.pos, d->initNormScrollY * pageHeight_DocumentWidget_(d)); /* TODO: unless user already scrolled! */ | 3000 | init_Anim(&d->scrollY.pos, d->initNormScrollY * pageHeight_DocumentWidget_(d)); /* TODO: unless user already scrolled! */ |
2991 | } | 3001 | } |
3002 | addBannerWarnings_DocumentWidget_(d); | ||
2992 | iChangeFlags(d->flags, | 3003 | iChangeFlags(d->flags, |
2993 | urlChanged_DocumentWidgetFlag | drawDownloadCounter_DocumentWidgetFlag, | 3004 | urlChanged_DocumentWidgetFlag | drawDownloadCounter_DocumentWidgetFlag, |
2994 | iFalse); | 3005 | iFalse); |
@@ -4377,7 +4388,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4377 | if (run->flags & altText_GmRunFlag) { | 4388 | if (run->flags & altText_GmRunFlag) { |
4378 | const iInt2 margin = preRunMargin_GmDocument(doc, preId_GmRun(run)); | 4389 | const iInt2 margin = preRunMargin_GmDocument(doc, preId_GmRun(run)); |
4379 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); | 4390 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); |
4380 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); | 4391 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmFrameAltText_ColorId); |
4381 | drawWrapRange_Text(run->font, | 4392 | drawWrapRange_Text(run->font, |
4382 | add_I2(visPos, margin), | 4393 | add_I2(visPos, margin), |
4383 | run->visBounds.size.x - 2 * margin.x, | 4394 | run->visBounds.size.x - 2 * margin.x, |
@@ -5053,7 +5064,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
5053 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); | 5064 | SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); |
5054 | } | 5065 | } |
5055 | fillRect_Paint(&ctx.paint, altRect, tmBackgroundAltText_ColorId); | 5066 | fillRect_Paint(&ctx.paint, altRect, tmBackgroundAltText_ColorId); |
5056 | drawRect_Paint(&ctx.paint, altRect, tmQuoteIcon_ColorId); | 5067 | drawRect_Paint(&ctx.paint, altRect, tmFrameAltText_ColorId); |
5057 | setOpacity_Text(altTextOpacity); | 5068 | setOpacity_Text(altTextOpacity); |
5058 | drawWrapRange_Text(altFont, addX_I2(pos, margin), wrap, | 5069 | drawWrapRange_Text(altFont, addX_I2(pos, margin), wrap, |
5059 | tmQuote_ColorId, meta->altText); | 5070 | tmQuote_ColorId, meta->altText); |
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index fbcd24b9..daca05d1 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -380,8 +380,12 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
380 | } | 380 | } |
381 | if (d->flags.wrap) { | 381 | if (d->flags.wrap) { |
382 | const iRect cont = contentBounds_LabelWidget_(d); | 382 | const iRect cont = contentBounds_LabelWidget_(d); |
383 | drawWrapRange_Text( | 383 | iWrapText wt = { |
384 | d->font, topLeft_Rect(cont), width_Rect(cont), fg, range_String(&d->label)); | 384 | .text = range_String(&d->label), |
385 | .maxWidth = width_Rect(cont), | ||
386 | .mode = word_WrapTextMode, | ||
387 | }; | ||
388 | draw_WrapText(&wt, d->font, topLeft_Rect(cont), fg); | ||
385 | } | 389 | } |
386 | else if (flags & alignLeft_WidgetFlag) { | 390 | else if (flags & alignLeft_WidgetFlag) { |
387 | draw_Text(d->font, add_I2(bounds.pos, addX_I2(padding_LabelWidget_(d, 0), iconPad)), | 391 | draw_Text(d->font, add_I2(bounds.pos, addX_I2(padding_LabelWidget_(d, 0), iconPad)), |
diff --git a/src/ui/text.c b/src/ui/text.c index 106c55e9..2ef54d39 100644 --- a/src/ui/text.c +++ b/src/ui/text.c | |||
@@ -264,6 +264,7 @@ struct Impl_Text { | |||
264 | int ansiFlags; | 264 | int ansiFlags; |
265 | int baseFontId; /* base attributes (for restoring via escapes) */ | 265 | int baseFontId; /* base attributes (for restoring via escapes) */ |
266 | int baseColorId; | 266 | int baseColorId; |
267 | iBool missingGlyphs; /* true if a glyph couldn't be found */ | ||
267 | }; | 268 | }; |
268 | 269 | ||
269 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) | 270 | iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render) |
@@ -797,6 +798,7 @@ iLocalDef iFont *characterFont_Font_(iFont *d, iChar ch, uint32_t *glyphIndex) { | |||
797 | } | 798 | } |
798 | #endif // 0 | 799 | #endif // 0 |
799 | if (!*glyphIndex) { | 800 | if (!*glyphIndex) { |
801 | activeText_->missingGlyphs = iTrue; | ||
800 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); | 802 | fprintf(stderr, "failed to find %08x (%lc)\n", ch, (int)ch); fflush(stderr); |
801 | } | 803 | } |
802 | return d; | 804 | return d; |
@@ -2260,6 +2262,13 @@ iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) { | |||
2260 | return tm; | 2262 | return tm; |
2261 | } | 2263 | } |
2262 | 2264 | ||
2265 | iBool checkMissing_Text(void) { | ||
2266 | iText *d = activeText_; | ||
2267 | const iBool missing = d->missingGlyphs; | ||
2268 | d->missingGlyphs = iFalse; | ||
2269 | return missing; | ||
2270 | } | ||
2271 | |||
2263 | SDL_Texture *glyphCache_Text(void) { | 2272 | SDL_Texture *glyphCache_Text(void) { |
2264 | return activeText_->cache; | 2273 | return activeText_->cache; |
2265 | } | 2274 | } |
diff --git a/src/ui/text.h b/src/ui/text.h index 99ddb704..13a636d4 100644 --- a/src/ui/text.h +++ b/src/ui/text.h | |||
@@ -220,6 +220,7 @@ struct Impl_WrapText { | |||
220 | iTextMetrics measure_WrapText (iWrapText *, int fontId); | 220 | iTextMetrics measure_WrapText (iWrapText *, int fontId); |
221 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); | 221 | iTextMetrics draw_WrapText (iWrapText *, int fontId, iInt2 pos, int color); |
222 | 222 | ||
223 | iBool checkMissing_Text (void); /* returns the flag, and clears it */ | ||
223 | SDL_Texture * glyphCache_Text (void); | 224 | SDL_Texture * glyphCache_Text (void); |
224 | 225 | ||
225 | enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode }; | 226 | enum iTextBlockMode { quadrants_TextBlockMode, shading_TextBlockMode }; |