diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gmdocument.c | 22 | ||||
-rw-r--r-- | src/gmdocument.h | 19 | ||||
-rw-r--r-- | src/ui/banner.c | 262 | ||||
-rw-r--r-- | src/ui/banner.h | 56 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 295 |
5 files changed, 490 insertions, 164 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c index b844c0f6..eac0ac62 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c | |||
@@ -97,8 +97,8 @@ struct Impl_GmDocument { | |||
97 | iBool enableCommandLinks; /* `about:command?` only allowed on selected pages */ | 97 | iBool enableCommandLinks; /* `about:command?` only allowed on selected pages */ |
98 | iArray layout; /* contents of source, laid out in document space */ | 98 | iArray layout; /* contents of source, laid out in document space */ |
99 | iPtrArray links; | 99 | iPtrArray links; |
100 | enum iGmDocumentBanner bannerType; | 100 | // enum iGmDocumentBanner bannerType; |
101 | iString bannerText; | 101 | // iString bannerText; |
102 | iString title; /* the first top-level title */ | 102 | iString title; /* the first top-level title */ |
103 | iArray headings; | 103 | iArray headings; |
104 | iArray preMeta; /* metadata about preformatted blocks */ | 104 | iArray preMeta; /* metadata about preformatted blocks */ |
@@ -573,7 +573,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
573 | const iArray *oldPreMeta = collect_Array(copy_Array(&d->preMeta)); /* remember fold states */ | 573 | const iArray *oldPreMeta = collect_Array(copy_Array(&d->preMeta)); /* remember fold states */ |
574 | clear_Array(&d->preMeta); | 574 | clear_Array(&d->preMeta); |
575 | clear_String(&d->title); | 575 | clear_String(&d->title); |
576 | clear_String(&d->bannerText); | 576 | // clear_String(&d->bannerText); |
577 | if (d->size.x <= 0 || isEmpty_String(&d->source)) { | 577 | if (d->size.x <= 0 || isEmpty_String(&d->source)) { |
578 | return; | 578 | return; |
579 | } | 579 | } |
@@ -587,7 +587,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
587 | int preFont = preformatted_FontId; | 587 | int preFont = preformatted_FontId; |
588 | uint16_t preId = 0; | 588 | uint16_t preId = 0; |
589 | iBool enableIndents = iFalse; | 589 | iBool enableIndents = iFalse; |
590 | iBool addSiteBanner = d->bannerType != none_GmDocumentBanner; | 590 | // iBool addSiteBanner = d->bannerType != none_GmDocumentBanner; |
591 | const iBool isNormalized = isNormalized_GmDocument_(d); | 591 | const iBool isNormalized = isNormalized_GmDocument_(d); |
592 | enum iGmLineType prevType = text_GmLineType; | 592 | enum iGmLineType prevType = text_GmLineType; |
593 | enum iGmLineType prevNonBlankType = text_GmLineType; | 593 | enum iGmLineType prevNonBlankType = text_GmLineType; |
@@ -669,7 +669,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
669 | if (d->format == gemini_SourceFormat && | 669 | if (d->format == gemini_SourceFormat && |
670 | startsWithSc_Rangecc(line, "```", &iCaseSensitive)) { | 670 | startsWithSc_Rangecc(line, "```", &iCaseSensitive)) { |
671 | isPreformat = iFalse; | 671 | isPreformat = iFalse; |
672 | addSiteBanner = iFalse; /* overrides the banner */ | 672 | // addSiteBanner = iFalse; /* overrides the banner */ |
673 | continue; | 673 | continue; |
674 | } | 674 | } |
675 | run.mediaType = max_MediaType; /* preformatted block */ | 675 | run.mediaType = max_MediaType; /* preformatted block */ |
@@ -677,6 +677,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
677 | run.font = (d->format == plainText_SourceFormat ? plainText_FontId : preFont); | 677 | run.font = (d->format == plainText_SourceFormat ? plainText_FontId : preFont); |
678 | indent = indents[type]; | 678 | indent = indents[type]; |
679 | } | 679 | } |
680 | #if 0 | ||
680 | if (addSiteBanner) { | 681 | if (addSiteBanner) { |
681 | addSiteBanner = iFalse; | 682 | addSiteBanner = iFalse; |
682 | const iRangecc bannerText = urlHost_String(&d->url); | 683 | const iRangecc bannerText = urlHost_String(&d->url); |
@@ -697,6 +698,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { | |||
697 | 1.5f * lineHeight_Text(paragraph_FontId) * prefs->lineSpacing; | 698 | 1.5f * lineHeight_Text(paragraph_FontId) * prefs->lineSpacing; |
698 | } | 699 | } |
699 | } | 700 | } |
701 | #endif | ||
700 | /* Empty lines don't produce text runs. */ | 702 | /* Empty lines don't produce text runs. */ |
701 | if (isEmpty_Range(&line)) { | 703 | if (isEmpty_Range(&line)) { |
702 | if (type == quote_GmLineType && !prefs->quoteIcon) { | 704 | if (type == quote_GmLineType && !prefs->quoteIcon) { |
@@ -1068,13 +1070,13 @@ void init_GmDocument(iGmDocument *d) { | |||
1068 | init_String(&d->source); | 1070 | init_String(&d->source); |
1069 | init_String(&d->url); | 1071 | init_String(&d->url); |
1070 | init_String(&d->localHost); | 1072 | init_String(&d->localHost); |
1071 | d->bannerType = siteDomain_GmDocumentBanner; | 1073 | // d->bannerType = siteDomain_GmDocumentBanner; |
1072 | d->outsideMargin = 0; | 1074 | d->outsideMargin = 0; |
1073 | d->size = zero_I2(); | 1075 | d->size = zero_I2(); |
1074 | d->enableCommandLinks = iFalse; | 1076 | d->enableCommandLinks = iFalse; |
1075 | init_Array(&d->layout, sizeof(iGmRun)); | 1077 | init_Array(&d->layout, sizeof(iGmRun)); |
1076 | init_PtrArray(&d->links); | 1078 | init_PtrArray(&d->links); |
1077 | init_String(&d->bannerText); | 1079 | // init_String(&d->bannerText); |
1078 | init_String(&d->title); | 1080 | init_String(&d->title); |
1079 | init_Array(&d->headings, sizeof(iGmHeading)); | 1081 | init_Array(&d->headings, sizeof(iGmHeading)); |
1080 | init_Array(&d->preMeta, sizeof(iGmPreMeta)); | 1082 | init_Array(&d->preMeta, sizeof(iGmPreMeta)); |
@@ -1089,7 +1091,7 @@ void init_GmDocument(iGmDocument *d) { | |||
1089 | void deinit_GmDocument(iGmDocument *d) { | 1091 | void deinit_GmDocument(iGmDocument *d) { |
1090 | iReleasePtr(&d->openURLs); | 1092 | iReleasePtr(&d->openURLs); |
1091 | delete_Media(d->media); | 1093 | delete_Media(d->media); |
1092 | deinit_String(&d->bannerText); | 1094 | // deinit_String(&d->bannerText); |
1093 | deinit_String(&d->title); | 1095 | deinit_String(&d->title); |
1094 | clearLinks_GmDocument_(d); | 1096 | clearLinks_GmDocument_(d); |
1095 | deinit_PtrArray(&d->links); | 1097 | deinit_PtrArray(&d->links); |
@@ -1625,9 +1627,11 @@ void setFormat_GmDocument(iGmDocument *d, enum iSourceFormat format) { | |||
1625 | d->format = format; | 1627 | d->format = format; |
1626 | } | 1628 | } |
1627 | 1629 | ||
1630 | #if 0 | ||
1628 | void setBanner_GmDocument(iGmDocument *d, enum iGmDocumentBanner type) { | 1631 | void setBanner_GmDocument(iGmDocument *d, enum iGmDocumentBanner type) { |
1629 | d->bannerType = type; | 1632 | d->bannerType = type; |
1630 | } | 1633 | } |
1634 | #endif | ||
1631 | 1635 | ||
1632 | void setWidth_GmDocument(iGmDocument *d, int width, int outsideMargin) { | 1636 | void setWidth_GmDocument(iGmDocument *d, int width, int outsideMargin) { |
1633 | d->size.x = width; | 1637 | d->size.x = width; |
@@ -2104,6 +2108,7 @@ iInt2 size_GmDocument(const iGmDocument *d) { | |||
2104 | return d->size; | 2108 | return d->size; |
2105 | } | 2109 | } |
2106 | 2110 | ||
2111 | #if 0 | ||
2107 | enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *d) { | 2112 | enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *d) { |
2108 | return d->bannerType; | 2113 | return d->bannerType; |
2109 | } | 2114 | } |
@@ -2126,6 +2131,7 @@ const iGmRun *siteBanner_GmDocument(const iGmDocument *d) { | |||
2126 | const iString *bannerText_GmDocument(const iGmDocument *d) { | 2131 | const iString *bannerText_GmDocument(const iGmDocument *d) { |
2127 | return &d->bannerText; | 2132 | return &d->bannerText; |
2128 | } | 2133 | } |
2134 | #endif | ||
2129 | 2135 | ||
2130 | const iArray *headings_GmDocument(const iGmDocument *d) { | 2136 | const iArray *headings_GmDocument(const iGmDocument *d) { |
2131 | return &d->headings; | 2137 | return &d->headings; |
diff --git a/src/gmdocument.h b/src/gmdocument.h index e6388fbd..e010ad43 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h | |||
@@ -120,10 +120,10 @@ enum iGmRunFlags { | |||
120 | decoration_GmRunFlag = iBit(1), /* not part of the source */ | 120 | decoration_GmRunFlag = iBit(1), /* not part of the source */ |
121 | startOfLine_GmRunFlag = iBit(2), | 121 | startOfLine_GmRunFlag = iBit(2), |
122 | endOfLine_GmRunFlag = iBit(3), | 122 | endOfLine_GmRunFlag = iBit(3), |
123 | siteBanner_GmRunFlag = iBit(4), /* area reserved for the site banner */ | 123 | // siteBanner_GmRunFlag = iBit(4), /* area reserved for the site banner */ |
124 | quoteBorder_GmRunFlag = iBit(5), | 124 | quoteBorder_GmRunFlag = iBit(5), |
125 | wide_GmRunFlag = iBit(6), /* horizontally scrollable */ | 125 | wide_GmRunFlag = iBit(6), /* horizontally scrollable */ |
126 | footer_GmRunFlag = iBit(7), | 126 | // footer_GmRunFlag = iBit(7), |
127 | altText_GmRunFlag = iBit(8), | 127 | altText_GmRunFlag = iBit(8), |
128 | }; | 128 | }; |
129 | 129 | ||
@@ -171,12 +171,13 @@ iRangecc findLoc_GmRun (const iGmRun *, iInt2 pos); | |||
171 | 171 | ||
172 | iDeclareClass(GmDocument) | 172 | iDeclareClass(GmDocument) |
173 | iDeclareObjectConstruction(GmDocument) | 173 | iDeclareObjectConstruction(GmDocument) |
174 | 174 | ||
175 | /* | ||
175 | enum iGmDocumentBanner { | 176 | enum iGmDocumentBanner { |
176 | none_GmDocumentBanner, | 177 | none_GmDocumentBanner, |
177 | siteDomain_GmDocumentBanner, | 178 | siteDomain_GmDocumentBanner, |
178 | certificateWarning_GmDocumentBanner, | 179 | certificateWarning_GmDocumentBanner, |
179 | }; | 180 | };*/ |
180 | 181 | ||
181 | enum iGmDocumentUpdate { | 182 | enum iGmDocumentUpdate { |
182 | partial_GmDocumentUpdate, /* appending more content */ | 183 | partial_GmDocumentUpdate, /* appending more content */ |
@@ -185,7 +186,7 @@ enum iGmDocumentUpdate { | |||
185 | 186 | ||
186 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); | 187 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); |
187 | void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); | 188 | void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); |
188 | void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type); | 189 | //void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type); |
189 | void setWidth_GmDocument (iGmDocument *, int width, int outsideMargin); | 190 | void setWidth_GmDocument (iGmDocument *, int width, int outsideMargin); |
190 | void redoLayout_GmDocument (iGmDocument *); | 191 | void redoLayout_GmDocument (iGmDocument *); |
191 | iBool updateOpenURLs_GmDocument(iGmDocument *); | 192 | iBool updateOpenURLs_GmDocument(iGmDocument *); |
@@ -213,10 +214,10 @@ const iGmRun * renderProgressive_GmDocument(const iGmDocument *d, const iGmRun | |||
213 | iRangei visRangeY, iGmDocumentRenderFunc render, | 214 | iRangei visRangeY, iGmDocumentRenderFunc render, |
214 | void *context); | 215 | void *context); |
215 | iInt2 size_GmDocument (const iGmDocument *); | 216 | iInt2 size_GmDocument (const iGmDocument *); |
216 | const iGmRun * siteBanner_GmDocument (const iGmDocument *); | 217 | //const iGmRun * siteBanner_GmDocument (const iGmDocument *); |
217 | iBool hasSiteBanner_GmDocument (const iGmDocument *); | 218 | //iBool hasSiteBanner_GmDocument (const iGmDocument *); |
218 | enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *); | 219 | //enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *); |
219 | const iString * bannerText_GmDocument (const iGmDocument *); | 220 | //const iString * bannerText_GmDocument (const iGmDocument *); |
220 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ | 221 | const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ |
221 | const iString * source_GmDocument (const iGmDocument *); | 222 | const iString * source_GmDocument (const iGmDocument *); |
222 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ | 223 | size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ |
diff --git a/src/ui/banner.c b/src/ui/banner.c new file mode 100644 index 00000000..5ec3c9f0 --- /dev/null +++ b/src/ui/banner.c | |||
@@ -0,0 +1,262 @@ | |||
1 | /* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #include "banner.h" | ||
24 | |||
25 | #include "command.h" | ||
26 | #include "documentwidget.h" | ||
27 | #include "paint.h" | ||
28 | #include "util.h" | ||
29 | |||
30 | iDeclareType(BannerItem) | ||
31 | |||
32 | struct Impl_BannerItem { | ||
33 | enum iBannerType type; | ||
34 | enum iGmStatusCode code; | ||
35 | iString message; | ||
36 | int height; | ||
37 | }; | ||
38 | |||
39 | static void init_BannerItem(iBannerItem *d) { | ||
40 | init_String(&d->message); | ||
41 | d->height = 0; | ||
42 | } | ||
43 | |||
44 | static void deinit_BannerItem(iBannerItem *d) { | ||
45 | deinit_String(&d->message); | ||
46 | } | ||
47 | |||
48 | /*----------------------------------------------------------------------------------------------*/ | ||
49 | |||
50 | struct Impl_Banner { | ||
51 | iDocumentWidget *doc; | ||
52 | iRect rect; | ||
53 | iString site; | ||
54 | iString icon; | ||
55 | iArray items; | ||
56 | }; | ||
57 | |||
58 | iDefineTypeConstruction(Banner) | ||
59 | |||
60 | static void updateHeight_Banner_(iBanner *d) { | ||
61 | d->rect.size.y = 0; | ||
62 | if (!isEmpty_String(&d->site)) { //} || !isEmpty_String(&d->icon)) { | ||
63 | d->rect.size.y += lineHeight_Text(banner_FontId) * 2; | ||
64 | } | ||
65 | } | ||
66 | |||
67 | void init_Banner(iBanner *d) { | ||
68 | d->doc = NULL; | ||
69 | d->rect = zero_Rect(); | ||
70 | init_String(&d->site); | ||
71 | init_String(&d->icon); | ||
72 | init_Array(&d->items, sizeof(iBannerItem)); | ||
73 | } | ||
74 | |||
75 | void deinit_Banner(iBanner *d) { | ||
76 | clear_Banner(d); | ||
77 | deinit_Array(&d->items); | ||
78 | deinit_String(&d->icon); | ||
79 | deinit_String(&d->site); | ||
80 | } | ||
81 | |||
82 | void setOwner_Banner(iBanner *d, iDocumentWidget *owner) { | ||
83 | d->doc = owner; | ||
84 | } | ||
85 | |||
86 | void setWidth_Banner(iBanner *d, int width) { | ||
87 | d->rect.size.x = width; | ||
88 | updateHeight_Banner_(d); | ||
89 | } | ||
90 | |||
91 | void setPos_Banner(iBanner *d, iInt2 pos) { | ||
92 | d->rect.pos = pos; | ||
93 | } | ||
94 | |||
95 | int height_Banner(const iBanner *d) { | ||
96 | return d->rect.size.y; | ||
97 | } | ||
98 | |||
99 | void clear_Banner(iBanner *d) { | ||
100 | iForEach(Array, i, &d->items) { | ||
101 | deinit_BannerItem(i.value); | ||
102 | } | ||
103 | clear_Array(&d->items); | ||
104 | clear_String(&d->site); | ||
105 | clear_String(&d->icon); | ||
106 | d->rect.size.y = 0; | ||
107 | } | ||
108 | |||
109 | void setSite_Banner(iBanner *d, iRangecc site, iChar icon) { | ||
110 | clear_String(&d->site); | ||
111 | clear_String(&d->icon); | ||
112 | if (icon) { | ||
113 | setRange_String(&d->site, site); | ||
114 | appendChar_String(&d->icon, icon); | ||
115 | } | ||
116 | updateHeight_Banner_(d); | ||
117 | } | ||
118 | |||
119 | void add_Banner(iBanner *d, enum iBannerType type, enum iGmStatusCode code, const iString *message) { | ||
120 | iBannerItem item; | ||
121 | init_BannerItem(&item); | ||
122 | item.type = type; | ||
123 | item.code = code; | ||
124 | set_String(&item.message, message); | ||
125 | pushBack_Array(&d->items, &item); | ||
126 | updateHeight_Banner_(d); | ||
127 | } | ||
128 | |||
129 | void remove_Banner(iBanner *d, enum iGmStatusCode code) { | ||
130 | iForEach(Array, i, &d->items) { | ||
131 | iBannerItem *item = i.value; | ||
132 | if (item->code == code) { | ||
133 | deinit_BannerItem(item); | ||
134 | remove_ArrayIterator(&i); | ||
135 | } | ||
136 | } | ||
137 | updateHeight_Banner_(d); | ||
138 | } | ||
139 | |||
140 | iBool processEvent_Banner(iBanner *d, const SDL_Event *ev) { | ||
141 | iWidget *w = as_Widget(d->doc); | ||
142 | /* on motion: */ | ||
143 | // setCursor_Window(window_Widget(w), SDL_SYSTEM_CURSOR_HAND); | ||
144 | return iFalse; | ||
145 | } | ||
146 | |||
147 | #if 0 | ||
148 | static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 visPos) { | ||
149 | const iGmDocument *doc = d->widget->doc; | ||
150 | const iChar icon = siteIcon_GmDocument(doc); | ||
151 | iString str; | ||
152 | init_String(&str); | ||
153 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | ||
154 | if (icon) { | ||
155 | appendChar_String(&str, icon); | ||
156 | const iRect iconRect = visualBounds_Text(run->font, range_String(&str)); | ||
157 | drawRange_Text( | ||
158 | run->font, | ||
159 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | ||
160 | tmBannerIcon_ColorId, | ||
161 | range_String(&str)); | ||
162 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | ||
163 | } | ||
164 | drawRange_Text(run->font, | ||
165 | bpos, | ||
166 | tmBannerTitle_ColorId, | ||
167 | bannerText_DocumentWidget_(d->widget)); | ||
168 | if (bannerType_GmDocument(doc) == certificateWarning_GmDocumentBanner) { | ||
169 | const int domainHeight = lineHeight_Text(banner_FontId) * 2; | ||
170 | iRect rect = { add_I2(visPos, init_I2(0, domainHeight)), | ||
171 | addY_I2(run->visBounds.size, -domainHeight - lineHeight_Text(uiContent_FontId)) }; | ||
172 | format_String(&str, "${heading.certwarn}"); | ||
173 | const int certFlags = d->widget->certFlags; | ||
174 | if (certFlags & timeVerified_GmCertFlag && certFlags & domainVerified_GmCertFlag) { | ||
175 | iUrl parts; | ||
176 | init_Url(&parts, d->widget->mod.url); | ||
177 | const iTime oldUntil = | ||
178 | domainValidUntil_GmCerts(certs_App(), parts.host, port_Url(&parts)); | ||
179 | iDate exp; | ||
180 | init_Date(&exp, &oldUntil); | ||
181 | iTime now; | ||
182 | initCurrent_Time(&now); | ||
183 | const int days = secondsSince_Time(&oldUntil, &now) / 3600 / 24; | ||
184 | appendCStr_String(&str, "\n"); | ||
185 | if (days <= 30) { | ||
186 | appendCStr_String(&str, | ||
187 | format_CStr(cstrCount_Lang("dlg.certwarn.mayberenewed.n", days), | ||
188 | cstrCollect_String(format_Date(&exp, "%Y-%m-%d")), | ||
189 | days)); | ||
190 | } | ||
191 | else { | ||
192 | appendCStr_String(&str, cstr_Lang("dlg.certwarn.different")); | ||
193 | } | ||
194 | } | ||
195 | else if (certFlags & domainVerified_GmCertFlag) { | ||
196 | appendCStr_String(&str, "\n"); | ||
197 | appendFormat_String(&str, cstr_Lang("dlg.certwarn.expired"), | ||
198 | cstrCollect_String(format_Date(&d->widget->certExpiry, "%Y-%m-%d"))); | ||
199 | } | ||
200 | else if (certFlags & timeVerified_GmCertFlag) { | ||
201 | appendCStr_String(&str, "\n"); | ||
202 | appendFormat_String(&str, cstr_Lang("dlg.certwarn.domain"), | ||
203 | cstr_String(d->widget->certSubject)); | ||
204 | } | ||
205 | else { | ||
206 | appendCStr_String(&str, "\n"); | ||
207 | appendCStr_String(&str, cstr_Lang("dlg.certwarn.domain.expired")); | ||
208 | } | ||
209 | const iInt2 dims = measureWrapRange_Text( | ||
210 | uiContent_FontId, width_Rect(rect) - 16 * gap_UI, range_String(&str)).bounds.size; | ||
211 | const int warnHeight = run->visBounds.size.y - domainHeight; | ||
212 | const int yOff = (lineHeight_Text(uiLabelLarge_FontId) - | ||
213 | lineHeight_Text(uiContent_FontId)) / 2; | ||
214 | const iRect bgRect = | ||
215 | init_Rect(0, visPos.y + domainHeight, d->widgetBounds.size.x, warnHeight); | ||
216 | fillRect_Paint(&d->paint, bgRect, orange_ColorId); | ||
217 | if (!isDark_ColorTheme(colorTheme_App())) { | ||
218 | drawHLine_Paint(&d->paint, | ||
219 | topLeft_Rect(bgRect), width_Rect(bgRect), tmBannerTitle_ColorId); | ||
220 | drawHLine_Paint(&d->paint, | ||
221 | bottomLeft_Rect(bgRect), width_Rect(bgRect), tmBannerTitle_ColorId); | ||
222 | } | ||
223 | const int fg = black_ColorId; | ||
224 | adjustEdges_Rect(&rect, warnHeight / 2 - dims.y / 2 - yOff, 0, 0, 0); | ||
225 | bpos = topLeft_Rect(rect); | ||
226 | draw_Text(uiLabelLarge_FontId, bpos, fg, "\u26a0"); | ||
227 | adjustEdges_Rect(&rect, 0, -8 * gap_UI, 0, 8 * gap_UI); | ||
228 | translate_Lang(&str); | ||
229 | drawWrapRange_Text(uiContent_FontId, | ||
230 | addY_I2(topLeft_Rect(rect), yOff), | ||
231 | width_Rect(rect), | ||
232 | fg, | ||
233 | range_String(&str)); | ||
234 | } | ||
235 | deinit_String(&str); | ||
236 | } | ||
237 | #endif | ||
238 | |||
239 | void draw_Banner(const iBanner *d) { | ||
240 | if (isEmpty_Banner(d)) { | ||
241 | return; | ||
242 | } | ||
243 | iRect bounds = d->rect; | ||
244 | iInt2 pos = addY_I2(topLeft_Rect(bounds), lineHeight_Text(banner_FontId) / 2); | ||
245 | iPaint p; | ||
246 | init_Paint(&p); | ||
247 | // drawRect_Paint(&p, bounds, red_ColorId); | ||
248 | /* Draw the icon. */ | ||
249 | if (!isEmpty_String(&d->icon)) { | ||
250 | const int font = banner_FontId; | ||
251 | const iRect iconRect = visualBounds_Text(font, range_String(&d->icon)); | ||
252 | drawRange_Text(font, | ||
253 | addY_I2(pos, -mid_Rect(iconRect).y + lineHeight_Text(font) / 2), | ||
254 | tmBannerIcon_ColorId, | ||
255 | range_String(&d->icon)); | ||
256 | pos.x += right_Rect(iconRect) + 3 * gap_Text; | ||
257 | } | ||
258 | /* Draw the site name. */ | ||
259 | if (!isEmpty_String(&d->site)) { | ||
260 | drawRange_Text(banner_FontId, pos, tmBannerTitle_ColorId, range_String(&d->site)); | ||
261 | } | ||
262 | } | ||
diff --git a/src/ui/banner.h b/src/ui/banner.h new file mode 100644 index 00000000..5ee8b3ae --- /dev/null +++ b/src/ui/banner.h | |||
@@ -0,0 +1,56 @@ | |||
1 | /* Copyright 2021 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
2 | |||
3 | Redistribution and use in source and binary forms, with or without | ||
4 | modification, are permitted provided that the following conditions are met: | ||
5 | |||
6 | 1. Redistributions of source code must retain the above copyright notice, this | ||
7 | list of conditions and the following disclaimer. | ||
8 | 2. Redistributions in binary form must reproduce the above copyright notice, | ||
9 | this list of conditions and the following disclaimer in the documentation | ||
10 | and/or other materials provided with the distribution. | ||
11 | |||
12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||
16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||
19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | ||
22 | |||
23 | #pragma once | ||
24 | |||
25 | #include "../gmutil.h" | ||
26 | |||
27 | #include <the_Foundation/vec2.h> | ||
28 | #include <SDL_events.h> | ||
29 | |||
30 | iDeclareType(Banner) | ||
31 | iDeclareTypeConstruction(Banner) | ||
32 | |||
33 | iDeclareType(DocumentWidget) | ||
34 | |||
35 | enum iBannerType { | ||
36 | warning_BannerType, | ||
37 | error_BannerType, | ||
38 | }; | ||
39 | |||
40 | void setOwner_Banner (iBanner *, iDocumentWidget *owner); | ||
41 | void setWidth_Banner (iBanner *, int width); | ||
42 | void setPos_Banner (iBanner *, iInt2 pos); | ||
43 | |||
44 | int height_Banner (const iBanner *); | ||
45 | |||
46 | iLocalDef iBool isEmpty_Banner(const iBanner *d) { | ||
47 | return height_Banner(d) == 0; | ||
48 | } | ||
49 | |||
50 | void clear_Banner (iBanner *); | ||
51 | void setSite_Banner (iBanner *, iRangecc site, iChar icon); | ||
52 | void add_Banner (iBanner *, enum iBannerType type, enum iGmStatusCode code, const iString *message); | ||
53 | void remove_Banner (iBanner *, enum iGmStatusCode code); | ||
54 | |||
55 | iBool processEvent_Banner (iBanner *, const SDL_Event *ev); | ||
56 | void draw_Banner (const iBanner *); | ||
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 963bae8f..17111ed0 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
27 | 27 | ||
28 | #include "app.h" | 28 | #include "app.h" |
29 | #include "audio/player.h" | 29 | #include "audio/player.h" |
30 | #include "banner.h" | ||
30 | #include "bookmarks.h" | 31 | #include "bookmarks.h" |
31 | #include "command.h" | 32 | #include "command.h" |
32 | #include "defs.h" | 33 | #include "defs.h" |
@@ -280,6 +281,7 @@ struct Impl_DocumentWidget { | |||
280 | iTime sourceTime; | 281 | iTime sourceTime; |
281 | iGempub * sourceGempub; /* NULL unless the page is Gempub content */ | 282 | iGempub * sourceGempub; /* NULL unless the page is Gempub content */ |
282 | iGmDocument * doc; | 283 | iGmDocument * doc; |
284 | iBanner * banner; | ||
283 | 285 | ||
284 | /* Rendering: */ | 286 | /* Rendering: */ |
285 | int pageMargin; | 287 | int pageMargin; |
@@ -339,6 +341,7 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
339 | d->isRequestUpdated = iFalse; | 341 | d->isRequestUpdated = iFalse; |
340 | d->media = new_ObjectList(); | 342 | d->media = new_ObjectList(); |
341 | d->doc = new_GmDocument(); | 343 | d->doc = new_GmDocument(); |
344 | d->banner = new_Banner(); | ||
342 | d->redirectCount = 0; | 345 | d->redirectCount = 0; |
343 | d->ordinalBase = 0; | 346 | d->ordinalBase = 0; |
344 | d->initNormScrollY = 0; | 347 | d->initNormScrollY = 0; |
@@ -429,6 +432,7 @@ void deinit_DocumentWidget(iDocumentWidget *d) { | |||
429 | deinit_Block(&d->sourceContent); | 432 | deinit_Block(&d->sourceContent); |
430 | deinit_String(&d->sourceMime); | 433 | deinit_String(&d->sourceMime); |
431 | deinit_String(&d->sourceHeader); | 434 | deinit_String(&d->sourceHeader); |
435 | delete_Banner(d->banner); | ||
432 | iRelease(d->doc); | 436 | iRelease(d->doc); |
433 | if (d->mediaTimer) { | 437 | if (d->mediaTimer) { |
434 | SDL_RemoveTimer(d->mediaTimer); | 438 | SDL_RemoveTimer(d->mediaTimer); |
@@ -512,35 +516,67 @@ static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | |||
512 | prefs->lineWidth * prefs->zoomPercent / 100); | 516 | prefs->lineWidth * prefs->zoomPercent / 100); |
513 | } | 517 | } |
514 | 518 | ||
519 | static int documentTopPad_DocumentWidget_(const iDocumentWidget *d) { | ||
520 | /* Amount of space between banner and top of the document. */ | ||
521 | return isEmpty_Banner(d->banner) ? 0 : lineHeight_Text(paragraph_FontId); | ||
522 | } | ||
523 | |||
524 | static int pageHeight_DocumentWidget_(const iDocumentWidget *d) { | ||
525 | return height_Banner(d->banner) + documentTopPad_DocumentWidget_(d) + size_GmDocument(d->doc).y; | ||
526 | } | ||
527 | |||
515 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | 528 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { |
516 | const iRect bounds = bounds_Widget(constAs_Widget(d)); | 529 | const iRect bounds = bounds_Widget(constAs_Widget(d)); |
517 | const int margin = gap_UI * d->pageMargin; | 530 | const int margin = gap_UI * d->pageMargin; |
518 | iRect rect; | 531 | iRect rect; |
519 | rect.size.x = documentWidth_DocumentWidget_(d); | 532 | rect.size.x = documentWidth_DocumentWidget_(d); |
520 | rect.pos.x = mid_Rect(bounds).x - rect.size.x / 2; | 533 | rect.pos.x = mid_Rect(bounds).x - rect.size.x / 2; |
521 | rect.pos.y = top_Rect(bounds); | 534 | rect.pos.y = top_Rect(bounds) + margin; |
522 | rect.size.y = height_Rect(bounds) - margin; | 535 | rect.size.y = height_Rect(bounds) - margin; |
523 | const iGmRun *banner = siteBanner_GmDocument(d->doc); | 536 | iBool wasCentered = iFalse; |
524 | if (!banner) { | ||
525 | rect.pos.y += margin; | ||
526 | rect.size.y -= margin; | ||
527 | } | ||
528 | if (d->flags & centerVertically_DocumentWidgetFlag) { | 537 | if (d->flags & centerVertically_DocumentWidgetFlag) { |
529 | const iInt2 docSize = addY_I2(size_GmDocument(d->doc), | 538 | const iInt2 docSize = addY_I2(size_GmDocument(d->doc), |
530 | iMax(height_Widget(d->footerButtons), height_Widget(d->phoneToolbar))); | 539 | iMax(height_Widget(d->footerButtons), height_Widget(d->phoneToolbar))); |
531 | if (docSize.y < rect.size.y) { | 540 | if (docSize.y < rect.size.y) { |
532 | /* Center vertically if short. There is one empty paragraph line's worth of margin | 541 | /* Center vertically if short. There is one empty paragraph line's worth of margin |
533 | between the banner and the page contents. */ | 542 | between the banner and the page contents. */ |
534 | const int bannerHeight = banner ? height_Rect(banner->visBounds) : 0; | 543 | // const int bannerHeight = 0; //banner ? height_Rect(banner->visBounds) : 0; |
535 | int offset = iMax(0, (rect.size.y + margin - docSize.y - bannerHeight - | 544 | #if 0 |
536 | lineHeight_Text(paragraph_FontId)) / 2); | 545 | int offset = iMax(0, (rect.size.y + margin - size_GmDocument(d->doc).y |
537 | rect.pos.y += offset; | 546 | //- lineHeight_Text(paragraph_FontId) |
547 | ) / 2 - height_Banner(d->banner) | ||
548 | //- | ||
549 | //documentTopPad_DocumentWidget_(d) | ||
550 | ); | ||
551 | #endif | ||
552 | int offset = iMax(0, height_Rect(bounds) / 2 | ||
553 | // - (isEmpty_Banner(d->banner) ? lineHeight_Text(paragraph_FontId) / 2 : 0) | ||
554 | - documentTopPad_DocumentWidget_(d) | ||
555 | // + lineHeight_Text(paragraph_FontId) / 2 | ||
556 | // - isEmpty_Banner(d->banner) ? | ||
557 | - height_Banner(d->banner) | ||
558 | - size_GmDocument(d->doc).y / 2 | ||
559 | ); | ||
560 | //(//documentTopPad_DocumentWidget_(d) + | ||
561 | //size_GmDocument(d->doc).y) / 2); | ||
562 | rect.pos.y = top_Rect(bounds) + offset; | ||
538 | rect.size.y = docSize.y; | 563 | rect.size.y = docSize.y; |
564 | wasCentered = iTrue; | ||
539 | } | 565 | } |
540 | } | 566 | } |
567 | if (!wasCentered && !isEmpty_Banner(d->banner)) { | ||
568 | /* The banner overtakes the top banner. */ | ||
569 | rect.pos.y -= margin; | ||
570 | rect.size.y -= margin; | ||
571 | } | ||
541 | return rect; | 572 | return rect; |
542 | } | 573 | } |
543 | 574 | ||
575 | static int viewPos_DocumentWidget_(const iDocumentWidget *d) { | ||
576 | return height_Banner(d->banner) + documentTopPad_DocumentWidget_(d) - pos_SmoothScroll(&d->scrollY); | ||
577 | } | ||
578 | |||
579 | #if 0 | ||
544 | static iRect siteBannerRect_DocumentWidget_(const iDocumentWidget *d) { | 580 | static iRect siteBannerRect_DocumentWidget_(const iDocumentWidget *d) { |
545 | const iGmRun *banner = siteBanner_GmDocument(d->doc); | 581 | const iGmRun *banner = siteBanner_GmDocument(d->doc); |
546 | if (!banner) { | 582 | if (!banner) { |
@@ -550,17 +586,23 @@ static iRect siteBannerRect_DocumentWidget_(const iDocumentWidget *d) { | |||
550 | const iInt2 origin = addY_I2(topLeft_Rect(docBounds), -pos_SmoothScroll(&d->scrollY)); | 586 | const iInt2 origin = addY_I2(topLeft_Rect(docBounds), -pos_SmoothScroll(&d->scrollY)); |
551 | return moved_Rect(banner->visBounds, origin); | 587 | return moved_Rect(banner->visBounds, origin); |
552 | } | 588 | } |
589 | #endif | ||
553 | 590 | ||
554 | static iInt2 documentPos_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { | 591 | static iInt2 documentPos_DocumentWidget_(const iDocumentWidget *d, iInt2 pos) { |
555 | return addY_I2(sub_I2(pos, topLeft_Rect(documentBounds_DocumentWidget_(d))), | 592 | return addY_I2(sub_I2(pos, topLeft_Rect(documentBounds_DocumentWidget_(d))), |
556 | pos_SmoothScroll(&d->scrollY)); | 593 | -viewPos_DocumentWidget_(d)); |
557 | } | 594 | } |
558 | 595 | ||
559 | static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { | 596 | static iRangei visibleRange_DocumentWidget_(const iDocumentWidget *d) { |
560 | const int margin = !hasSiteBanner_GmDocument(d->doc) ? gap_UI * d->pageMargin : 0; | 597 | // const int margin = -documentTopPad_DocumentWidget_(d) + |
561 | return (iRangei){ pos_SmoothScroll(&d->scrollY) - margin, | 598 | // d->pageMargin * gap_UI; |
562 | pos_SmoothScroll(&d->scrollY) + height_Rect(bounds_Widget(constAs_Widget(d))) - | 599 | //const int top = -viewPos_DocumentWidget_(d) - margin; |
563 | margin }; | 600 | int top = pos_SmoothScroll(&d->scrollY) - height_Banner(d->banner) - documentTopPad_DocumentWidget_(d); |
601 | if (isEmpty_Banner(d->banner)) { | ||
602 | /* Top padding is not collapsed. */ | ||
603 | top -= d->pageMargin * gap_UI; | ||
604 | } | ||
605 | return (iRangei){ top, top + height_Rect(bounds_Widget(constAs_Widget(d))) }; | ||
564 | } | 606 | } |
565 | 607 | ||
566 | static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { | 608 | static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { |
@@ -598,7 +640,7 @@ static const iGmRun *lastVisibleLink_DocumentWidget_(const iDocumentWidget *d) { | |||
598 | } | 640 | } |
599 | 641 | ||
600 | static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) { | 642 | static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) { |
601 | const int docSize = size_GmDocument(d->doc).y; | 643 | const int docSize = pageHeight_DocumentWidget_(d); // size_GmDocument(d->doc).y; |
602 | if (docSize) { | 644 | if (docSize) { |
603 | return pos_SmoothScroll(&d->scrollY) / (float) docSize; | 645 | return pos_SmoothScroll(&d->scrollY) / (float) docSize; |
604 | } | 646 | } |
@@ -607,15 +649,9 @@ static float normScrollPos_DocumentWidget_(const iDocumentWidget *d) { | |||
607 | 649 | ||
608 | static int scrollMax_DocumentWidget_(const iDocumentWidget *d) { | 650 | static int scrollMax_DocumentWidget_(const iDocumentWidget *d) { |
609 | const iWidget *w = constAs_Widget(d); | 651 | const iWidget *w = constAs_Widget(d); |
610 | int sm = size_GmDocument(d->doc).y - height_Rect(bounds_Widget(w)) + | 652 | int sm = pageHeight_DocumentWidget_(d) - height_Rect(bounds_Widget(w)) + |
611 | (hasSiteBanner_GmDocument(d->doc) ? 1 : 2) * d->pageMargin * gap_UI + | 653 | (isEmpty_Banner(d->banner) ? 2 : 1) * d->pageMargin * gap_UI + /* top and bottom margins */ |
612 | iMax(height_Widget(d->phoneToolbar), height_Widget(d->footerButtons)); | 654 | iMax(height_Widget(d->phoneToolbar), height_Widget(d->footerButtons)); |
613 | // sm += height_Widget(d->phoneToolbar); | ||
614 | // if (d->phoneToolbar) { | ||
615 | // sm += size_Root(w->root).y - | ||
616 | // top_Rect(boundsWithoutVisualOffset_Widget(d->phoneToolbar)); | ||
617 | // sm += height_Widget(d->phoneToolbar); | ||
618 | // } | ||
619 | return sm; | 655 | return sm; |
620 | } | 656 | } |
621 | 657 | ||
@@ -699,7 +735,8 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | |||
699 | const iGmRun * oldHoverLink = d->hoverLink; | 735 | const iGmRun * oldHoverLink = d->hoverLink; |
700 | d->hoverPre = NULL; | 736 | d->hoverPre = NULL; |
701 | d->hoverLink = NULL; | 737 | d->hoverLink = NULL; |
702 | const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), pos_SmoothScroll(&d->scrollY)); | 738 | const iInt2 hoverPos = addY_I2(sub_I2(mouse, topLeft_Rect(docBounds)), |
739 | -viewPos_DocumentWidget_(d)); | ||
703 | if (isHoverAllowed_DocumentWidget_(d)) { | 740 | if (isHoverAllowed_DocumentWidget_(d)) { |
704 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 741 | iConstForEach(PtrArray, i, &d->visibleLinks) { |
705 | const iGmRun *run = i.ptr; | 742 | const iGmRun *run = i.ptr; |
@@ -757,8 +794,9 @@ static void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) { | |||
757 | 794 | ||
758 | static void updateSideOpacity_DocumentWidget_(iDocumentWidget *d, iBool isAnimated) { | 795 | static void updateSideOpacity_DocumentWidget_(iDocumentWidget *d, iBool isAnimated) { |
759 | float opacity = 0.0f; | 796 | float opacity = 0.0f; |
760 | const iGmRun *banner = siteBanner_GmDocument(d->doc); | 797 | // const iGmRun *banner = siteBanner_GmDocument(d->doc); |
761 | if (banner && bottom_Rect(banner->visBounds) < pos_SmoothScroll(&d->scrollY)) { | 798 | if (!isEmpty_Banner(d->banner) && height_Banner(d->banner) < pos_SmoothScroll(&d->scrollY)) { |
799 | // if (banner && bottom_Rect(banner->visBounds) < pos_SmoothScroll(&d->scrollY)) { | ||
762 | opacity = 1.0f; | 800 | opacity = 1.0f; |
763 | } | 801 | } |
764 | setValue_Anim(&d->sideOpacity, opacity, isAnimated ? (opacity < 0.5f ? 100 : 200) : 0); | 802 | setValue_Anim(&d->sideOpacity, opacity, isAnimated ? (opacity < 0.5f ? 100 : 200) : 0); |
@@ -862,6 +900,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
862 | prefs_App()->centerShortDocs || startsWithCase_String(d->mod.url, "about:") || | 900 | prefs_App()->centerShortDocs || startsWithCase_String(d->mod.url, "about:") || |
863 | !isSuccess_GmStatusCode(d->sourceStatus)); | 901 | !isSuccess_GmStatusCode(d->sourceStatus)); |
864 | const iRangei visRange = visibleRange_DocumentWidget_(d); | 902 | const iRangei visRange = visibleRange_DocumentWidget_(d); |
903 | // printf("visRange: %d...%d\n", visRange.start, visRange.end); | ||
865 | const iRect bounds = bounds_Widget(as_Widget(d)); | 904 | const iRect bounds = bounds_Widget(as_Widget(d)); |
866 | const int scrollMax = updateScrollMax_DocumentWidget_(d); | 905 | const int scrollMax = updateScrollMax_DocumentWidget_(d); |
867 | /* Reposition the footer buttons as appropriate. */ | 906 | /* Reposition the footer buttons as appropriate. */ |
@@ -880,13 +919,13 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
880 | } | 919 | } |
881 | else { | 920 | else { |
882 | d->footerButtons->animOffsetRef = &d->scrollY.pos; | 921 | d->footerButtons->animOffsetRef = &d->scrollY.pos; |
883 | d->footerButtons->rect.pos.y = size_GmDocument(d->doc).y + 2 * gap_UI * d->pageMargin; | 922 | d->footerButtons->rect.pos.y = pageHeight_DocumentWidget_(d) + 2 * gap_UI * d->pageMargin; |
884 | // + height_Widget(d->phoneToolbar); | 923 | // + height_Widget(d->phoneToolbar); |
885 | } | 924 | } |
886 | } | 925 | } |
887 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax }); | 926 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax }); |
888 | const int docSize = size_GmDocument(d->doc).y + iMax(height_Widget(d->phoneToolbar), | 927 | const int docSize = pageHeight_DocumentWidget_(d) + iMax(height_Widget(d->phoneToolbar), |
889 | height_Widget(d->footerButtons)); | 928 | height_Widget(d->footerButtons)); |
890 | setThumb_ScrollWidget(d->scroll, | 929 | setThumb_ScrollWidget(d->scroll, |
891 | pos_SmoothScroll(&d->scrollY), | 930 | pos_SmoothScroll(&d->scrollY), |
892 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); | 931 | docSize > 0 ? height_Rect(bounds) * size_Range(&visRange) / docSize : 0); |
@@ -906,6 +945,11 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
906 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window(), 0)); | 945 | updateHover_DocumentWidget_(d, mouseCoord_Window(get_Window(), 0)); |
907 | updateSideOpacity_DocumentWidget_(d, iTrue); | 946 | updateSideOpacity_DocumentWidget_(d, iTrue); |
908 | animateMedia_DocumentWidget_(d); | 947 | animateMedia_DocumentWidget_(d); |
948 | setPos_Banner(d->banner, addY_I2(topLeft_Rect(documentBounds_DocumentWidget_(d)), | ||
949 | -pos_SmoothScroll(&d->scrollY))); | ||
950 | /*init_I2(documentBounds_DocumentWidget_(d).pos.x, | ||
951 | viewPos_DocumentWidget_(d) - | ||
952 | documentTopPad_DocumentWidget_(d)));*/ | ||
909 | /* Remember scroll positions of recently visited pages. */ { | 953 | /* Remember scroll positions of recently visited pages. */ { |
910 | iRecentUrl *recent = mostRecentUrl_History(d->mod.history); | 954 | iRecentUrl *recent = mostRecentUrl_History(d->mod.history); |
911 | if (recent && docSize && d->state == ready_RequestState) { | 955 | if (recent && docSize && d->state == ready_RequestState) { |
@@ -1015,18 +1059,18 @@ static void invalidate_DocumentWidget_(iDocumentWidget *d) { | |||
1015 | clear_PtrSet(d->invalidRuns); | 1059 | clear_PtrSet(d->invalidRuns); |
1016 | } | 1060 | } |
1017 | 1061 | ||
1018 | static iRangecc bannerText_DocumentWidget_(const iDocumentWidget *d) { | 1062 | static iRangecc siteText_DocumentWidget_(const iDocumentWidget *d) { |
1019 | return isEmpty_String(d->titleUser) ? range_String(bannerText_GmDocument(d->doc)) | 1063 | return isEmpty_String(d->titleUser) ? urlHost_String(d->mod.url) |
1020 | : range_String(d->titleUser); | 1064 | : range_String(d->titleUser); |
1021 | } | 1065 | } |
1022 | 1066 | ||
1023 | static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { | 1067 | static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) { |
1024 | d->foundMark = iNullRange; | 1068 | d->foundMark = iNullRange; |
1025 | d->selectMark = iNullRange; | 1069 | d->selectMark = iNullRange; |
1026 | d->hoverPre = NULL; | 1070 | d->hoverPre = NULL; |
1027 | d->hoverAltPre = NULL; | 1071 | d->hoverAltPre = NULL; |
1028 | d->hoverLink = NULL; | 1072 | d->hoverLink = NULL; |
1029 | d->contextLink = NULL; | 1073 | d->contextLink = NULL; |
1030 | iZap(d->visibleRuns); | 1074 | iZap(d->visibleRuns); |
1031 | iZap(d->renderRuns); | 1075 | iZap(d->renderRuns); |
1032 | } | 1076 | } |
@@ -1087,6 +1131,7 @@ void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { | |||
1087 | outsideMargin, | 1131 | outsideMargin, |
1088 | isFinished_GmRequest(d->request) ? final_GmDocumentUpdate | 1132 | isFinished_GmRequest(d->request) ? final_GmDocumentUpdate |
1089 | : partial_GmDocumentUpdate); | 1133 | : partial_GmDocumentUpdate); |
1134 | setWidth_Banner(d->banner, docWidth); | ||
1090 | documentWasChanged_DocumentWidget_(d); | 1135 | documentWasChanged_DocumentWidget_(d); |
1091 | } | 1136 | } |
1092 | 1137 | ||
@@ -1097,6 +1142,11 @@ static void replaceDocument_DocumentWidget_(iDocumentWidget *d, iGmDocument *new | |||
1097 | documentWasChanged_DocumentWidget_(d); | 1142 | documentWasChanged_DocumentWidget_(d); |
1098 | } | 1143 | } |
1099 | 1144 | ||
1145 | static void updateBanner_DocumentWidget_(iDocumentWidget *d) { | ||
1146 | clear_Banner(d->banner); | ||
1147 | setSite_Banner(d->banner, siteText_DocumentWidget_(d), siteIcon_GmDocument(d->doc)); | ||
1148 | } | ||
1149 | |||
1100 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { | 1150 | static void updateTheme_DocumentWidget_(iDocumentWidget *d) { |
1101 | if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) { | 1151 | if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) { |
1102 | iBlock empty; | 1152 | iBlock empty; |
@@ -1112,8 +1162,10 @@ static void updateTheme_DocumentWidget_(iDocumentWidget *d) { | |||
1112 | setThemeSeed_GmDocument(d->doc, &d->titleUser->chars); | 1162 | setThemeSeed_GmDocument(d->doc, &d->titleUser->chars); |
1113 | } | 1163 | } |
1114 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag; | 1164 | d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag; |
1165 | updateBanner_DocumentWidget_(d); | ||
1115 | } | 1166 | } |
1116 | 1167 | ||
1168 | #if 0 | ||
1117 | static enum iGmDocumentBanner bannerType_DocumentWidget_(const iDocumentWidget *d) { | 1169 | static enum iGmDocumentBanner bannerType_DocumentWidget_(const iDocumentWidget *d) { |
1118 | if (d->certFlags & available_GmCertFlag) { | 1170 | if (d->certFlags & available_GmCertFlag) { |
1119 | const int req = domainVerified_GmCertFlag | timeVerified_GmCertFlag | trusted_GmCertFlag; | 1171 | const int req = domainVerified_GmCertFlag | timeVerified_GmCertFlag | trusted_GmCertFlag; |
@@ -1123,6 +1175,7 @@ static enum iGmDocumentBanner bannerType_DocumentWidget_(const iDocumentWidget * | |||
1123 | } | 1175 | } |
1124 | return siteDomain_GmDocumentBanner; | 1176 | return siteDomain_GmDocumentBanner; |
1125 | } | 1177 | } |
1178 | #endif | ||
1126 | 1179 | ||
1127 | static void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuItem *items, size_t count) { | 1180 | static void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuItem *items, size_t count) { |
1128 | iWidget *w = as_Widget(d); | 1181 | iWidget *w = as_Widget(d); |
@@ -1160,6 +1213,8 @@ static void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuIte | |||
1160 | 1213 | ||
1161 | static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode code, | 1214 | static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode code, |
1162 | const iString *meta) { | 1215 | const iString *meta) { |
1216 | /* TODO: No such thing as an "error page". It should be an empty page with an error banner. */ | ||
1217 | #if 0 | ||
1163 | iString *src = collectNewCStr_String("# "); | 1218 | iString *src = collectNewCStr_String("# "); |
1164 | const iGmError *msg = get_GmError(code); | 1219 | const iGmError *msg = get_GmError(code); |
1165 | appendChar_String(src, msg->icon ? msg->icon : 0x2327); /* X in a box */ | 1220 | appendChar_String(src, msg->icon ? msg->icon : 0x2327); /* X in a box */ |
@@ -1242,14 +1297,16 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
1242 | /* Make a new document for the error page.*/ { | 1297 | /* Make a new document for the error page.*/ { |
1243 | iGmDocument *errorDoc = new_GmDocument(); | 1298 | iGmDocument *errorDoc = new_GmDocument(); |
1244 | setUrl_GmDocument(errorDoc, d->mod.url); | 1299 | setUrl_GmDocument(errorDoc, d->mod.url); |
1245 | setBanner_GmDocument(errorDoc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); | 1300 | // setBanner_GmDocument(errorDoc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); |
1301 | |||
1246 | setFormat_GmDocument(errorDoc, gemini_SourceFormat); | 1302 | setFormat_GmDocument(errorDoc, gemini_SourceFormat); |
1247 | replaceDocument_DocumentWidget_(d, errorDoc); | 1303 | replaceDocument_DocumentWidget_(d, errorDoc); |
1248 | iRelease(errorDoc); | 1304 | iRelease(errorDoc); |
1249 | } | 1305 | } |
1250 | translate_Lang(src); | 1306 | translate_Lang(src); |
1307 | #endif | ||
1251 | d->state = ready_RequestState; | 1308 | d->state = ready_RequestState; |
1252 | setSource_DocumentWidget(d, src); | 1309 | // setSource_DocumentWidget(d, src); |
1253 | updateTheme_DocumentWidget_(d); | 1310 | updateTheme_DocumentWidget_(d); |
1254 | reset_SmoothScroll(&d->scrollY); | 1311 | reset_SmoothScroll(&d->scrollY); |
1255 | init_Anim(&d->sideOpacity, 0); | 1312 | init_Anim(&d->sideOpacity, 0); |
@@ -1729,7 +1786,7 @@ static void updateTrust_DocumentWidget_(iDocumentWidget *d, const iGmResponse *r | |||
1729 | else { | 1786 | else { |
1730 | updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_Icon); | 1787 | updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_Icon); |
1731 | } | 1788 | } |
1732 | setBanner_GmDocument(d->doc, bannerType_DocumentWidget_(d)); | 1789 | // setBanner_GmDocument(d->doc, bannerType_DocumentWidget_(d)); |
1733 | } | 1790 | } |
1734 | 1791 | ||
1735 | static void parseUser_DocumentWidget_(iDocumentWidget *d) { | 1792 | static void parseUser_DocumentWidget_(iDocumentWidget *d) { |
@@ -1778,12 +1835,13 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n | |||
1778 | updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); | 1835 | updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); |
1779 | // setCachedDocument_History(d->mod.history, d->doc, | 1836 | // setCachedDocument_History(d->mod.history, d->doc, |
1780 | // (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); | 1837 | // (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); |
1838 | updateBanner_DocumentWidget_(d); | ||
1781 | } | 1839 | } |
1782 | d->state = ready_RequestState; | 1840 | d->state = ready_RequestState; |
1783 | postProcessRequestContent_DocumentWidget_(d, iTrue); | 1841 | postProcessRequestContent_DocumentWidget_(d, iTrue); |
1784 | init_Anim(&d->altTextOpacity, 0); | 1842 | init_Anim(&d->altTextOpacity, 0); |
1785 | reset_SmoothScroll(&d->scrollY); | 1843 | reset_SmoothScroll(&d->scrollY); |
1786 | init_Anim(&d->scrollY.pos, d->initNormScrollY * size_GmDocument(d->doc).y); | 1844 | init_Anim(&d->scrollY.pos, d->initNormScrollY * pageHeight_DocumentWidget_(d)); |
1787 | updateSideOpacity_DocumentWidget_(d, iFalse); | 1845 | updateSideOpacity_DocumentWidget_(d, iFalse); |
1788 | updateVisible_DocumentWidget_(d); | 1846 | updateVisible_DocumentWidget_(d); |
1789 | moveSpan_SmoothScroll(&d->scrollY, 0, 0); /* clamp position to new max */ | 1847 | moveSpan_SmoothScroll(&d->scrollY, 0, 0); /* clamp position to new max */ |
@@ -1870,8 +1928,11 @@ static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int dur | |||
1870 | } | 1928 | } |
1871 | 1929 | ||
1872 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool centered) { | 1930 | static void scrollTo_DocumentWidget_(iDocumentWidget *d, int documentY, iBool centered) { |
1873 | if (!hasSiteBanner_GmDocument(d->doc)) { | 1931 | if (!isEmpty_Banner(d->banner)) { |
1874 | documentY += d->pageMargin * gap_UI; | 1932 | documentY += height_Banner(d->banner) + documentTopPad_DocumentWidget_(d); |
1933 | } | ||
1934 | else { | ||
1935 | documentY += documentTopPad_DocumentWidget_(d) + d->pageMargin * gap_UI; | ||
1875 | } | 1936 | } |
1876 | init_Anim(&d->scrollY.pos, | 1937 | init_Anim(&d->scrollY.pos, |
1877 | documentY - (centered ? documentBounds_DocumentWidget_(d).size.y / 2 | 1938 | documentY - (centered ? documentBounds_DocumentWidget_(d).size.y / 2 |
@@ -2403,6 +2464,7 @@ static iBool updateDocumentWidthRetainingScrollPosition_DocumentWidget_(iDocumen | |||
2403 | voffset = visibleRange_DocumentWidget_(d).start - top_Rect(run->visBounds); | 2464 | voffset = visibleRange_DocumentWidget_(d).start - top_Rect(run->visBounds); |
2404 | } | 2465 | } |
2405 | setWidth_GmDocument(d->doc, newWidth, (width_Widget(d) - newWidth) / 2); | 2466 | setWidth_GmDocument(d->doc, newWidth, (width_Widget(d) - newWidth) / 2); |
2467 | setWidth_Banner(d->banner, newWidth); | ||
2406 | documentRunsInvalidated_DocumentWidget_(d); | 2468 | documentRunsInvalidated_DocumentWidget_(d); |
2407 | if (runLoc && !keepCenter) { | 2469 | if (runLoc && !keepCenter) { |
2408 | run = findRunAtLoc_GmDocument(d->doc, runLoc); | 2470 | run = findRunAtLoc_GmDocument(d->doc, runLoc); |
@@ -2504,7 +2566,8 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | |||
2504 | if (recent->cachedDoc) { | 2566 | if (recent->cachedDoc) { |
2505 | iChangeRef(swipeIn->doc, recent->cachedDoc); | 2567 | iChangeRef(swipeIn->doc, recent->cachedDoc); |
2506 | updateScrollMax_DocumentWidget_(d); | 2568 | updateScrollMax_DocumentWidget_(d); |
2507 | setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0); | 2569 | setValue_Anim(&swipeIn->scrollY.pos, |
2570 | pageHeight_DocumentWidget_(d) * recent->normScrollY, 0); | ||
2508 | updateVisible_DocumentWidget_(swipeIn); | 2571 | updateVisible_DocumentWidget_(swipeIn); |
2509 | swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; | 2572 | swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; |
2510 | } | 2573 | } |
@@ -2935,7 +2998,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2935 | updateFetchProgress_DocumentWidget_(d); | 2998 | updateFetchProgress_DocumentWidget_(d); |
2936 | checkResponse_DocumentWidget_(d); | 2999 | checkResponse_DocumentWidget_(d); |
2937 | if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { | 3000 | if (category_GmStatusCode(status_GmRequest(d->request)) == categorySuccess_GmStatusCode) { |
2938 | init_Anim(&d->scrollY.pos, d->initNormScrollY * size_GmDocument(d->doc).y); /* TODO: unless user already scrolled! */ | 3001 | init_Anim(&d->scrollY.pos, d->initNormScrollY * pageHeight_DocumentWidget_(d)); /* TODO: unless user already scrolled! */ |
2939 | } | 3002 | } |
2940 | iChangeFlags(d->flags, | 3003 | iChangeFlags(d->flags, |
2941 | urlChanged_DocumentWidgetFlag | drawDownloadCounter_DocumentWidgetFlag, | 3004 | urlChanged_DocumentWidgetFlag | drawDownloadCounter_DocumentWidgetFlag, |
@@ -3352,7 +3415,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
3352 | 3415 | ||
3353 | static iRect runRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run) { | 3416 | static iRect runRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run) { |
3354 | const iRect docBounds = documentBounds_DocumentWidget_(d); | 3417 | const iRect docBounds = documentBounds_DocumentWidget_(d); |
3355 | return moved_Rect(run->bounds, addY_I2(topLeft_Rect(docBounds), -pos_SmoothScroll(&d->scrollY))); | 3418 | return moved_Rect(run->bounds, addY_I2(topLeft_Rect(docBounds), viewPos_DocumentWidget_(d))); |
3356 | } | 3419 | } |
3357 | 3420 | ||
3358 | static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) { | 3421 | static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) { |
@@ -3660,9 +3723,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3660 | if (isVisible_Widget(d->menu)) { | 3723 | if (isVisible_Widget(d->menu)) { |
3661 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); | 3724 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); |
3662 | } | 3725 | } |
3726 | #if 0 | ||
3663 | else if (contains_Rect(siteBannerRect_DocumentWidget_(d), mpos)) { | 3727 | else if (contains_Rect(siteBannerRect_DocumentWidget_(d), mpos)) { |
3664 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_HAND); | 3728 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_HAND); |
3665 | } | 3729 | } |
3730 | #endif | ||
3666 | else { | 3731 | else { |
3667 | if (value_Anim(&d->altTextOpacity) < 0.833f) { | 3732 | if (value_Anim(&d->altTextOpacity) < 0.833f) { |
3668 | setValue_Anim(&d->altTextOpacity, 0, 0); /* keep it hidden while moving */ | 3733 | setValue_Anim(&d->altTextOpacity, 0, 0); /* keep it hidden while moving */ |
@@ -4126,6 +4191,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4126 | d->selectMark = iNullRange; | 4191 | d->selectMark = iNullRange; |
4127 | refresh_Widget(w); | 4192 | refresh_Widget(w); |
4128 | } | 4193 | } |
4194 | #if 0 | ||
4129 | /* Clicking on the top/side banner navigates to site root. */ | 4195 | /* Clicking on the top/side banner navigates to site root. */ |
4130 | const iRect banRect = siteBannerRect_DocumentWidget_(d); | 4196 | const iRect banRect = siteBannerRect_DocumentWidget_(d); |
4131 | if (contains_Rect(banRect, pos_Click(&d->click))) { | 4197 | if (contains_Rect(banRect, pos_Click(&d->click))) { |
@@ -4139,6 +4205,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
4139 | postCommand_Widget(d, "navigate.root"); | 4205 | postCommand_Widget(d, "navigate.root"); |
4140 | } | 4206 | } |
4141 | } | 4207 | } |
4208 | #endif | ||
4142 | } | 4209 | } |
4143 | return iTrue; | 4210 | return iTrue; |
4144 | case aborted_ClickResult: | 4211 | case aborted_ClickResult: |
@@ -4200,7 +4267,7 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4200 | } | 4267 | } |
4201 | if (~run->flags & decoration_GmRunFlag) { | 4268 | if (~run->flags & decoration_GmRunFlag) { |
4202 | const iInt2 visPos = | 4269 | const iInt2 visPos = |
4203 | add_I2(run->bounds.pos, addY_I2(d->viewPos, -pos_SmoothScroll(&d->widget->scrollY))); | 4270 | add_I2(run->bounds.pos, addY_I2(d->viewPos, viewPos_DocumentWidget_(d->widget))); |
4204 | const iRect rangeRect = { addX_I2(visPos, x), init_I2(w, height_Rect(run->bounds)) }; | 4271 | const iRect rangeRect = { addX_I2(visPos, x), init_I2(w, height_Rect(run->bounds)) }; |
4205 | if (rangeRect.size.x) { | 4272 | if (rangeRect.size.x) { |
4206 | fillRect_Paint(&d->paint, rangeRect, color); | 4273 | fillRect_Paint(&d->paint, rangeRect, color); |
@@ -4220,7 +4287,7 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4220 | (contains_Range(&url, mark.end) || url.end == mark.end)) { | 4287 | (contains_Range(&url, mark.end) || url.end == mark.end)) { |
4221 | fillRect_Paint( | 4288 | fillRect_Paint( |
4222 | &d->paint, | 4289 | &d->paint, |
4223 | moved_Rect(run->visBounds, addY_I2(d->viewPos, -pos_SmoothScroll(&d->widget->scrollY))), | 4290 | moved_Rect(run->visBounds, addY_I2(d->viewPos, viewPos_DocumentWidget_(d->widget))), |
4224 | color); | 4291 | color); |
4225 | } | 4292 | } |
4226 | } | 4293 | } |
@@ -4234,96 +4301,6 @@ static void drawMark_DrawContext_(void *context, const iGmRun *run) { | |||
4234 | } | 4301 | } |
4235 | } | 4302 | } |
4236 | 4303 | ||
4237 | static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 visPos) { | ||
4238 | const iGmDocument *doc = d->widget->doc; | ||
4239 | const iChar icon = siteIcon_GmDocument(doc); | ||
4240 | iString str; | ||
4241 | init_String(&str); | ||
4242 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | ||
4243 | if (icon) { | ||
4244 | appendChar_String(&str, icon); | ||
4245 | const iRect iconRect = visualBounds_Text(run->font, range_String(&str)); | ||
4246 | drawRange_Text( | ||
4247 | run->font, | ||
4248 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), | ||
4249 | tmBannerIcon_ColorId, | ||
4250 | range_String(&str)); | ||
4251 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | ||
4252 | } | ||
4253 | drawRange_Text(run->font, | ||
4254 | bpos, | ||
4255 | tmBannerTitle_ColorId, | ||
4256 | bannerText_DocumentWidget_(d->widget)); | ||
4257 | if (bannerType_GmDocument(doc) == certificateWarning_GmDocumentBanner) { | ||
4258 | const int domainHeight = lineHeight_Text(banner_FontId) * 2; | ||
4259 | iRect rect = { add_I2(visPos, init_I2(0, domainHeight)), | ||
4260 | addY_I2(run->visBounds.size, -domainHeight - lineHeight_Text(uiContent_FontId)) }; | ||
4261 | format_String(&str, "${heading.certwarn}"); | ||
4262 | const int certFlags = d->widget->certFlags; | ||
4263 | if (certFlags & timeVerified_GmCertFlag && certFlags & domainVerified_GmCertFlag) { | ||
4264 | iUrl parts; | ||
4265 | init_Url(&parts, d->widget->mod.url); | ||
4266 | const iTime oldUntil = | ||
4267 | domainValidUntil_GmCerts(certs_App(), parts.host, port_Url(&parts)); | ||
4268 | iDate exp; | ||
4269 | init_Date(&exp, &oldUntil); | ||
4270 | iTime now; | ||
4271 | initCurrent_Time(&now); | ||
4272 | const int days = secondsSince_Time(&oldUntil, &now) / 3600 / 24; | ||
4273 | appendCStr_String(&str, "\n"); | ||
4274 | if (days <= 30) { | ||
4275 | appendCStr_String(&str, | ||
4276 | format_CStr(cstrCount_Lang("dlg.certwarn.mayberenewed.n", days), | ||
4277 | cstrCollect_String(format_Date(&exp, "%Y-%m-%d")), | ||
4278 | days)); | ||
4279 | } | ||
4280 | else { | ||
4281 | appendCStr_String(&str, cstr_Lang("dlg.certwarn.different")); | ||
4282 | } | ||
4283 | } | ||
4284 | else if (certFlags & domainVerified_GmCertFlag) { | ||
4285 | appendCStr_String(&str, "\n"); | ||
4286 | appendFormat_String(&str, cstr_Lang("dlg.certwarn.expired"), | ||
4287 | cstrCollect_String(format_Date(&d->widget->certExpiry, "%Y-%m-%d"))); | ||
4288 | } | ||
4289 | else if (certFlags & timeVerified_GmCertFlag) { | ||
4290 | appendCStr_String(&str, "\n"); | ||
4291 | appendFormat_String(&str, cstr_Lang("dlg.certwarn.domain"), | ||
4292 | cstr_String(d->widget->certSubject)); | ||
4293 | } | ||
4294 | else { | ||
4295 | appendCStr_String(&str, "\n"); | ||
4296 | appendCStr_String(&str, cstr_Lang("dlg.certwarn.domain.expired")); | ||
4297 | } | ||
4298 | const iInt2 dims = measureWrapRange_Text( | ||
4299 | uiContent_FontId, width_Rect(rect) - 16 * gap_UI, range_String(&str)).bounds.size; | ||
4300 | const int warnHeight = run->visBounds.size.y - domainHeight; | ||
4301 | const int yOff = (lineHeight_Text(uiLabelLarge_FontId) - | ||
4302 | lineHeight_Text(uiContent_FontId)) / 2; | ||
4303 | const iRect bgRect = | ||
4304 | init_Rect(0, visPos.y + domainHeight, d->widgetBounds.size.x, warnHeight); | ||
4305 | fillRect_Paint(&d->paint, bgRect, orange_ColorId); | ||
4306 | if (!isDark_ColorTheme(colorTheme_App())) { | ||
4307 | drawHLine_Paint(&d->paint, | ||
4308 | topLeft_Rect(bgRect), width_Rect(bgRect), tmBannerTitle_ColorId); | ||
4309 | drawHLine_Paint(&d->paint, | ||
4310 | bottomLeft_Rect(bgRect), width_Rect(bgRect), tmBannerTitle_ColorId); | ||
4311 | } | ||
4312 | const int fg = black_ColorId; | ||
4313 | adjustEdges_Rect(&rect, warnHeight / 2 - dims.y / 2 - yOff, 0, 0, 0); | ||
4314 | bpos = topLeft_Rect(rect); | ||
4315 | draw_Text(uiLabelLarge_FontId, bpos, fg, "\u26a0"); | ||
4316 | adjustEdges_Rect(&rect, 0, -8 * gap_UI, 0, 8 * gap_UI); | ||
4317 | translate_Lang(&str); | ||
4318 | drawWrapRange_Text(uiContent_FontId, | ||
4319 | addY_I2(topLeft_Rect(rect), yOff), | ||
4320 | width_Rect(rect), | ||
4321 | fg, | ||
4322 | range_String(&str)); | ||
4323 | } | ||
4324 | deinit_String(&str); | ||
4325 | } | ||
4326 | |||
4327 | static void drawRun_DrawContext_(void *context, const iGmRun *run) { | 4304 | static void drawRun_DrawContext_(void *context, const iGmRun *run) { |
4328 | iDrawContext *d = context; | 4305 | iDrawContext *d = context; |
4329 | const iInt2 origin = d->viewPos; | 4306 | const iInt2 origin = d->viewPos; |
@@ -4430,6 +4407,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4430 | run->color, | 4407 | run->color, |
4431 | run->text); | 4408 | run->text); |
4432 | } | 4409 | } |
4410 | #if 0 | ||
4433 | else if (run->flags & siteBanner_GmRunFlag) { | 4411 | else if (run->flags & siteBanner_GmRunFlag) { |
4434 | /* Banner background. */ | 4412 | /* Banner background. */ |
4435 | iRect bannerBack = initCorners_Rect(topLeft_Rect(d->widgetBounds), | 4413 | iRect bannerBack = initCorners_Rect(topLeft_Rect(d->widgetBounds), |
@@ -4438,6 +4416,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4438 | fillRect_Paint(&d->paint, bannerBack, tmBannerBackground_ColorId); | 4416 | fillRect_Paint(&d->paint, bannerBack, tmBannerBackground_ColorId); |
4439 | drawBannerRun_DrawContext_(d, run, visPos); | 4417 | drawBannerRun_DrawContext_(d, run, visPos); |
4440 | } | 4418 | } |
4419 | #endif | ||
4441 | else { | 4420 | else { |
4442 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { | 4421 | if (d->showLinkNumbers && run->linkId && run->flags & decoration_GmRunFlag) { |
4443 | const size_t ord = visibleLinkOrdinal_DocumentWidget_(d->widget, run->linkId); | 4422 | const size_t ord = visibleLinkOrdinal_DocumentWidget_(d->widget, run->linkId); |
@@ -4663,8 +4642,8 @@ static void updateSideIconBuf_DocumentWidget_(const iDocumentWidget *d) { | |||
4663 | SDL_DestroyTexture(dbuf->sideIconBuf); | 4642 | SDL_DestroyTexture(dbuf->sideIconBuf); |
4664 | dbuf->sideIconBuf = NULL; | 4643 | dbuf->sideIconBuf = NULL; |
4665 | } | 4644 | } |
4666 | const iGmRun *banner = siteBanner_GmDocument(d->doc); | 4645 | // const iGmRun *banner = siteBanner_GmDocument(d->doc); |
4667 | if (!banner) { | 4646 | if (isEmpty_Banner(d->banner)) { |
4668 | return; | 4647 | return; |
4669 | } | 4648 | } |
4670 | const int margin = gap_UI * d->pageMargin; | 4649 | const int margin = gap_UI * d->pageMargin; |
@@ -4747,7 +4726,7 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
4747 | bottomLeft_Rect(bounds), | 4726 | bottomLeft_Rect(bounds), |
4748 | init_I2(margin, | 4727 | init_I2(margin, |
4749 | -margin + -dbuf->timestampBuf->size.y + | 4728 | -margin + -dbuf->timestampBuf->size.y + |
4750 | iMax(0, d->scrollY.max - pos_SmoothScroll(&d->scrollY)))), | 4729 | iMax(0, d->scrollY.max + viewPos_DocumentWidget_(d)))), |
4751 | tmQuoteIcon_ColorId); | 4730 | tmQuoteIcon_ColorId); |
4752 | } | 4731 | } |
4753 | unsetClip_Paint(&p); | 4732 | unsetClip_Paint(&p); |
@@ -4988,7 +4967,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4988 | }; | 4967 | }; |
4989 | init_Paint(&ctx.paint); | 4968 | init_Paint(&ctx.paint); |
4990 | render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); | 4969 | render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); |
4991 | int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); | 4970 | int yTop = docBounds.pos.y + viewPos_DocumentWidget_(d); |
4992 | const iBool isDocEmpty = size_GmDocument(d->doc).y == 0; | 4971 | const iBool isDocEmpty = size_GmDocument(d->doc).y == 0; |
4993 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; | 4972 | const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; |
4994 | if (!isDocEmpty) { | 4973 | if (!isDocEmpty) { |
@@ -5025,12 +5004,22 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
5025 | } | 5004 | } |
5026 | } | 5005 | } |
5027 | drawMedia_DocumentWidget_(d, &ctx.paint); | 5006 | drawMedia_DocumentWidget_(d, &ctx.paint); |
5028 | /* Fill the top and bottom, in case the document is short. */ | 5007 | /* Fill the top and bottom, in case the document is short. */ |
5029 | if (yTop > top_Rect(bounds)) { | 5008 | if (yTop > top_Rect(bounds)) { |
5030 | fillRect_Paint(&ctx.paint, | 5009 | fillRect_Paint(&ctx.paint, |
5031 | (iRect){ bounds.pos, init_I2(bounds.size.x, yTop - top_Rect(bounds)) }, | 5010 | (iRect){ bounds.pos, init_I2(bounds.size.x, yTop - top_Rect(bounds)) }, |
5032 | hasSiteBanner_GmDocument(d->doc) ? tmBannerBackground_ColorId | 5011 | !isEmpty_Banner(d->banner) ? tmBannerBackground_ColorId |
5033 | : tmBackground_ColorId); | 5012 | : tmBackground_ColorId); |
5013 | } | ||
5014 | /* Banner. */ { | ||
5015 | /* Fill the part between the banner and the top of the document. */ | ||
5016 | fillRect_Paint(&ctx.paint, | ||
5017 | (iRect){ init_I2(left_Rect(bounds), | ||
5018 | top_Rect(docBounds) + viewPos_DocumentWidget_(d) - | ||
5019 | documentTopPad_DocumentWidget_(d)), | ||
5020 | init_I2(bounds.size.x, documentTopPad_DocumentWidget_(d)) }, | ||
5021 | tmBackground_ColorId); | ||
5022 | draw_Banner(d->banner); | ||
5034 | } | 5023 | } |
5035 | const int yBottom = yTop + size_GmDocument(d->doc).y + 1; | 5024 | const int yBottom = yTop + size_GmDocument(d->doc).y + 1; |
5036 | if (yBottom < bottom_Rect(bounds)) { | 5025 | if (yBottom < bottom_Rect(bounds)) { |
@@ -5073,7 +5062,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
5073 | const int altFont = uiLabel_FontId; | 5062 | const int altFont = uiLabel_FontId; |
5074 | const int wrap = docBounds.size.x - 2 * margin; | 5063 | const int wrap = docBounds.size.x - 2 * margin; |
5075 | iInt2 pos = addY_I2(add_I2(docBounds.pos, meta->pixelRect.pos), | 5064 | iInt2 pos = addY_I2(add_I2(docBounds.pos, meta->pixelRect.pos), |
5076 | -pos_SmoothScroll(&d->scrollY)); | 5065 | viewPos_DocumentWidget_(d)); |
5077 | const iInt2 textSize = measureWrapRange_Text(altFont, wrap, meta->altText).bounds.size; | 5066 | const iInt2 textSize = measureWrapRange_Text(altFont, wrap, meta->altText).bounds.size; |
5078 | pos.y -= textSize.y + gap_UI; | 5067 | pos.y -= textSize.y + gap_UI; |
5079 | pos.y = iMax(pos.y, top_Rect(bounds)); | 5068 | pos.y = iMax(pos.y, top_Rect(bounds)); |
@@ -5276,11 +5265,23 @@ void updateSize_DocumentWidget(iDocumentWidget *d) { | |||
5276 | resetWideRuns_DocumentWidget_(d); | 5265 | resetWideRuns_DocumentWidget_(d); |
5277 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 5266 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
5278 | updateVisible_DocumentWidget_(d); | 5267 | updateVisible_DocumentWidget_(d); |
5268 | setWidth_Banner(d->banner, documentWidth_DocumentWidget(d)); | ||
5279 | invalidate_DocumentWidget_(d); | 5269 | invalidate_DocumentWidget_(d); |
5280 | arrange_Widget(d->footerButtons); | 5270 | arrange_Widget(d->footerButtons); |
5281 | } | 5271 | } |
5282 | 5272 | ||
5273 | #if 0 | ||
5274 | static void sizeChanged_DocumentWidget_(iDocumentWidget *d) { | ||
5275 | if (current_Root()) { | ||
5276 | /* TODO: This gets called more than once during a single arrange. | ||
5277 | It could be done via some sort of callback instead. */ | ||
5278 | updateVisible_DocumentWidget_(d); | ||
5279 | } | ||
5280 | } | ||
5281 | #endif | ||
5282 | |||
5283 | iBeginDefineSubclass(DocumentWidget, Widget) | 5283 | iBeginDefineSubclass(DocumentWidget, Widget) |
5284 | .processEvent = (iAny *) processEvent_DocumentWidget_, | 5284 | .processEvent = (iAny *) processEvent_DocumentWidget_, |
5285 | .draw = (iAny *) draw_DocumentWidget_, | 5285 | .draw = (iAny *) draw_DocumentWidget_, |
5286 | // .sizeChanged = (iAny *) sizeChanged_DocumentWidget_, | ||
5286 | iEndDefineSubclass(DocumentWidget) | 5287 | iEndDefineSubclass(DocumentWidget) |