summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gmdocument.c51
-rw-r--r--src/gmdocument.h5
-rw-r--r--src/ui/documentwidget.c60
-rw-r--r--src/ui/text.c4
-rw-r--r--src/ui/text.h1
5 files changed, 102 insertions, 19 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c
index cb9e2acb..e23d5836 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -86,6 +86,8 @@ struct Impl_GmDocument {
86 iPtrArray links; 86 iPtrArray links;
87 iString title; /* the first top-level title */ 87 iString title; /* the first top-level title */
88 iPtrArray images; /* persistent across layouts, references links by ID */ 88 iPtrArray images; /* persistent across layouts, references links by ID */
89 uint32_t themeSeed;
90 iChar siteIcon;
89}; 91};
90 92
91iDefineObjectConstruction(GmDocument) 93iDefineObjectConstruction(GmDocument)
@@ -299,11 +301,12 @@ static void doLayout_GmDocument_(iGmDocument *d) {
299 enum iGmLineType prevType = text_GmLineType; 301 enum iGmLineType prevType = text_GmLineType;
300 iBool isPreformat = iFalse; 302 iBool isPreformat = iFalse;
301 iBool isFirstText = iTrue; 303 iBool isFirstText = iTrue;
304 iBool addSiteBanner = iTrue;
302 int preFont = preformatted_FontId; 305 int preFont = preformatted_FontId;
303 if (d->format == plainText_GmDocumentFormat) { 306 if (d->format == plainText_GmDocumentFormat) {
304 isPreformat = iTrue; 307 isPreformat = iTrue;
305 isFirstText = iFalse; 308 isFirstText = iFalse;
306 } 309 }
307 while (nextSplit_Rangecc(&content, "\n", &line)) { 310 while (nextSplit_Rangecc(&content, "\n", &line)) {
308 iGmRun run; 311 iGmRun run;
309 run.flags = 0; 312 run.flags = 0;
@@ -345,11 +348,26 @@ static void doLayout_GmDocument_(iGmDocument *d) {
345 startsWithSc_Rangecc(&line, "```", &iCaseSensitive)) { 348 startsWithSc_Rangecc(&line, "```", &iCaseSensitive)) {
346 isPreformat = iFalse; 349 isPreformat = iFalse;
347 preAltText = iNullRange; 350 preAltText = iNullRange;
351 addSiteBanner = iFalse; /* overrides the banner */
348 continue; 352 continue;
349 } 353 }
350 run.font = preFont; 354 run.font = preFont;
351 indent = indents[type]; 355 indent = indents[type];
352 } 356 }
357 if (addSiteBanner) {
358 addSiteBanner = iFalse;
359 const iRangecc bannerText = urlHost_String(&d->url);
360 if (!isEmpty_Range(&bannerText)) {
361 iGmRun banner = { .flags = siteBanner_GmRunFlag };
362 banner.bounds = zero_Rect();
363 banner.visBounds = init_Rect(0, 0, d->size.x, lineHeight_Text(banner_FontId) * 2);
364 banner.font = banner_FontId;
365 banner.text = bannerText;
366 banner.color = gray50_ColorId;
367 pushBack_Array(&d->layout, &banner);
368 pos.y += height_Rect(banner.visBounds) + lineHeight_Text(paragraph_FontId);
369 }
370 }
353 /* Empty lines don't produce text runs. */ 371 /* Empty lines don't produce text runs. */
354 if (isEmpty_Range(&line)) { 372 if (isEmpty_Range(&line)) {
355 pos.y += lineHeight_Text(run.font); 373 pos.y += lineHeight_Text(run.font);
@@ -415,7 +433,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
415 int bigCount = 0; 433 int bigCount = 0;
416 if (type == text_GmLineType && isFirstText) { 434 if (type == text_GmLineType && isFirstText) {
417 run.font = firstParagraph_FontId; 435 run.font = firstParagraph_FontId;
418 run.color = gray88_ColorId; 436 run.color = white_ColorId;
419 bigCount = 15; /* max lines -- what if the whole document is one paragraph? */ 437 bigCount = 15; /* max lines -- what if the whole document is one paragraph? */
420 isFirstText = iFalse; 438 isFirstText = iFalse;
421 } 439 }
@@ -505,6 +523,7 @@ void init_GmDocument(iGmDocument *d) {
505 init_PtrArray(&d->links); 523 init_PtrArray(&d->links);
506 init_String(&d->title); 524 init_String(&d->title);
507 init_PtrArray(&d->images); 525 init_PtrArray(&d->images);
526 setThemeSeed_GmDocument(d, NULL);
508} 527}
509 528
510void deinit_GmDocument(iGmDocument *d) { 529void deinit_GmDocument(iGmDocument *d) {
@@ -527,6 +546,30 @@ void reset_GmDocument(iGmDocument *d) {
527 clear_Array(&d->layout); 546 clear_Array(&d->layout);
528 clear_String(&d->url); 547 clear_String(&d->url);
529 clear_String(&d->localHost); 548 clear_String(&d->localHost);
549 d->themeSeed = 0;
550}
551
552void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) {
553 static const iChar siteIcons[] = {
554 0x25ed, 0x2600, 0x2601, 0x2604, 0x2605, 0x2606, 0x265c, 0x265e, 0x2690,
555 0x2691, 0x2693, 0x2698, 0x2699, 0x26f0, 0x270e, 0x2728, 0x272a, 0x272f,
556 0x2731, 0x2738, 0x273a, 0x273e, 0x2740, 0x2742, 0x2744, 0x2748, 0x274a,
557 0x2751, 0x2756, 0x2766, 0x27bd, 0x27c1, 0x27d0, 0x2b19, 0x1f300, 0x1f303,
558 0x1f306, 0x1f308, 0x1f30a, 0x1f319, 0x1f31f, 0x1f320, 0x1f340, 0x1f4cd, 0x1f4e1,
559 0x1f657, 0x1f659, 0x1f665, 0x1f78b, 0x1f796, 0x1f79c,
560 };
561 d->themeSeed = 0;
562 d->siteIcon = 0;
563 if (seed && !isEmpty_Block(seed)) {
564 d->themeSeed = crc32_Block(seed);
565 d->siteIcon = siteIcons[(d->themeSeed >> 7) % iElemCount(siteIcons)];
566 }
567 /* Special exceptions. */ {
568 const iRangecc host = urlHost_String(&d->url);
569 if (equalCase_Rangecc(&host, "gemini.circumlunar.space")) {
570 d->siteIcon = 0x264a; /* gemini */
571 }
572 }
530} 573}
531 574
532void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) { 575void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) {
@@ -786,6 +829,10 @@ const iString *title_GmDocument(const iGmDocument *d) {
786 return &d->title; 829 return &d->title;
787} 830}
788 831
832iChar siteIcon_GmDocument(const iGmDocument *d) {
833 return d->siteIcon;
834}
835
789const char *findLoc_GmRun(const iGmRun *d, iInt2 pos) { 836const char *findLoc_GmRun(const iGmRun *d, iInt2 pos) {
790 const int x = pos.x - left_Rect(d->bounds); 837 const int x = pos.x - left_Rect(d->bounds);
791 const char *loc; 838 const char *loc;
diff --git a/src/gmdocument.h b/src/gmdocument.h
index e280bef8..b0a7df22 100644
--- a/src/gmdocument.h
+++ b/src/gmdocument.h
@@ -38,7 +38,7 @@ struct Impl_GmImageInfo {
38enum iGmRunFlags { 38enum iGmRunFlags {
39 startOfLine_GmRunFlag = iBit(1), 39 startOfLine_GmRunFlag = iBit(1),
40 endOfLine_GmRunFlag = iBit(2), 40 endOfLine_GmRunFlag = iBit(2),
41 siteBanner_GmRunlag = iBit(3), /* area reserved for the site banner */ 41 siteBanner_GmRunFlag = iBit(3), /* area reserved for the site banner */
42}; 42};
43 43
44struct Impl_GmRun { 44struct Impl_GmRun {
@@ -62,6 +62,7 @@ enum iGmDocumentFormat {
62 plainText_GmDocumentFormat, 62 plainText_GmDocumentFormat,
63}; 63};
64 64
65void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed);
65void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); 66void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format);
66void setWidth_GmDocument (iGmDocument *, int width); 67void setWidth_GmDocument (iGmDocument *, int width);
67void setUrl_GmDocument (iGmDocument *, const iString *url); 68void setUrl_GmDocument (iGmDocument *, const iString *url);
@@ -74,6 +75,7 @@ typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *);
74 75
75void render_GmDocument (const iGmDocument *, iRangei visRangeY, iGmDocumentRenderFunc render, void *); 76void render_GmDocument (const iGmDocument *, iRangei visRangeY, iGmDocumentRenderFunc render, void *);
76iInt2 size_GmDocument (const iGmDocument *); 77iInt2 size_GmDocument (const iGmDocument *);
78iInt2 sizeWithoutBanner_GmDocument (const iGmDocument *);
77 79
78iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); 80iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start);
79iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); 81iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before);
@@ -88,6 +90,7 @@ enum iColorId linkColor_GmDocument (const iGmDocument *, iGmLinkId linkId);
88const iTime * linkTime_GmDocument (const iGmDocument *, iGmLinkId linkId); 90const iTime * linkTime_GmDocument (const iGmDocument *, iGmLinkId linkId);
89iBool isMediaLink_GmDocument (const iGmDocument *, iGmLinkId linkId); 91iBool isMediaLink_GmDocument (const iGmDocument *, iGmLinkId linkId);
90const iString * title_GmDocument (const iGmDocument *); 92const iString * title_GmDocument (const iGmDocument *);
93iChar siteIcon_GmDocument (const iGmDocument *);
91 94
92SDL_Texture * imageTexture_GmDocument (const iGmDocument *, uint16_t imageId); 95SDL_Texture * imageTexture_GmDocument (const iGmDocument *, uint16_t imageId);
93void imageInfo_GmDocument (const iGmDocument *, uint16_t imageId, iGmImageInfo *info_out); 96void imageInfo_GmDocument (const iGmDocument *, uint16_t imageId, iGmImageInfo *info_out);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index a61d5b88..dbcf57eb 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -157,7 +157,7 @@ static int documentWidth_DocumentWidget_(const iDocumentWidget *d) {
157 const iWidget *w = constAs_Widget(d); 157 const iWidget *w = constAs_Widget(d);
158 const iRect bounds = bounds_Widget(w); 158 const iRect bounds = bounds_Widget(w);
159 return iMini(bounds.size.x - gap_UI * d->pageMargin * 2, 159 return iMini(bounds.size.x - gap_UI * d->pageMargin * 2,
160 fontSize_UI * 40 * d->textSizePercent / 100); 160 fontSize_UI * 38 * d->textSizePercent / 100); /* TODO: Add user preference .*/
161} 161}
162 162
163static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { 163static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) {
@@ -168,11 +168,12 @@ static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) {
168 rect.pos.x = bounds.size.x / 2 - rect.size.x / 2; 168 rect.pos.x = bounds.size.x / 2 - rect.size.x / 2;
169 rect.pos.y = top_Rect(bounds) + margin; 169 rect.pos.y = top_Rect(bounds) + margin;
170 rect.size.y = height_Rect(bounds) - 2 * margin; 170 rect.size.y = height_Rect(bounds) - 2 * margin;
171 if (size_GmDocument(d->doc).y < rect.size.y) { 171 iInt2 docSize = addY_I2(size_GmDocument(d->doc), 0 /*-lineHeight_Text(banner_FontId) * 2*/);
172 if (docSize.y < rect.size.y) {
172 /* Center vertically if short. */ 173 /* Center vertically if short. */
173 int offset = (rect.size.y - size_GmDocument(d->doc).y) / 2; 174 int offset = (rect.size.y - docSize.y) / 2;
174 rect.pos.y += offset; 175 rect.pos.y += offset;
175 rect.size.y = size_GmDocument(d->doc).y; 176 rect.size.y = docSize.y;
176 } 177 }
177 return rect; 178 return rect;
178} 179}
@@ -324,11 +325,19 @@ static void updateSource_DocumentWidget_(iDocumentWidget *d) {
324 /* TODO: Do this in the background. However, that requires a text metrics calculator 325 /* TODO: Do this in the background. However, that requires a text metrics calculator
325 that does not try to cache the glyph bitmaps. */ 326 that does not try to cache the glyph bitmaps. */
326 const enum iGmStatusCode statusCode = status_GmRequest(d->request); 327 const enum iGmStatusCode statusCode = status_GmRequest(d->request);
327 if (statusCode != input_GmStatusCode && 328 if (category_GmStatusCode(statusCode) != categoryInput_GmStatusCode) {
328 statusCode != sensitiveInput_GmStatusCode) {
329 iString str; 329 iString str;
330 /* Theming. */ {
331 if (isEmpty_String(d->titleUser)) {
332 setThemeSeed_GmDocument(d->doc,
333 collect_Block(newRange_Block(urlHost_String(d->url))));
334 }
335 else {
336 setThemeSeed_GmDocument(d->doc, &d->titleUser->chars);
337 }
338 }
330 initBlock_String(&str, body_GmRequest(d->request)); 339 initBlock_String(&str, body_GmRequest(d->request));
331 if (statusCode == success_GmStatusCode) { 340 if (category_GmStatusCode(statusCode) == categorySuccess_GmStatusCode) {
332 /* Check the MIME type. */ 341 /* Check the MIME type. */
333 const iString *mime = meta_GmRequest(d->request); 342 const iString *mime = meta_GmRequest(d->request);
334 if (startsWith_String(mime, "text/plain")) { 343 if (startsWith_String(mime, "text/plain")) {
@@ -997,14 +1006,11 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
997 } 1006 }
998 return; 1007 return;
999 } 1008 }
1000 iString text;
1001 /* TODO: making a copy is unnecessary; the text routines should accept Rangecc */
1002 initRange_String(&text, run->text);
1003 enum iColorId fg = run->color; 1009 enum iColorId fg = run->color;
1004 const iGmDocument *doc = d->widget->doc; 1010 const iGmDocument *doc = d->widget->doc;
1005 const iBool isHover = 1011 const iBool isHover =
1006 run->linkId != 0 && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId && 1012 (run->linkId != 0 && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId &&
1007 !isEmpty_Rect(run->bounds); 1013 !isEmpty_Rect(run->bounds));
1008 const iInt2 visPos = add_I2(run->visBounds.pos, origin); 1014 const iInt2 visPos = add_I2(run->visBounds.pos, origin);
1009 /* Text markers. */ 1015 /* Text markers. */
1010 fillRange_DrawContext_(d, run, teal_ColorId, d->widget->foundMark, &d->inFoundMark); 1016 fillRange_DrawContext_(d, run, teal_ColorId, d->widget->foundMark, &d->inFoundMark);
@@ -1016,8 +1022,30 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
1016 fg = linkColor_GmDocument(doc, run->linkId); 1022 fg = linkColor_GmDocument(doc, run->linkId);
1017 } 1023 }
1018 } 1024 }
1019 drawString_Text(run->font, visPos, fg, &text); 1025 if (run->flags & siteBanner_GmRunFlag) {
1020 deinit_String(&text); 1026 fillRect_Paint(
1027 &d->paint,
1028 initCorners_Rect(topLeft_Rect(d->widgetBounds),
1029 init_I2(right_Rect(bounds_Widget(constAs_Widget(d->widget))),
1030 visPos.y + height_Rect(run->visBounds))),
1031 black_ColorId);
1032 const iChar icon = siteIcon_GmDocument(doc);
1033 iString bannerText;
1034 init_String(&bannerText);
1035 format_String(&bannerText, "%lc", (int) icon);
1036 //appendRange_String(&bannerText, run->text);
1037 const iInt2 iconSize = advanceN_Text(banner_FontId, cstr_String(&bannerText), 2);
1038 iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2));
1039// draw_Text(run->font, bpos, fg, "%s", buf);
1040// bpos.x += iconSize.x;
1041 drawRange_Text(run->font, bpos, fg, range_String(&bannerText));
1042 bpos.x += iconSize.x + 3 * gap_Text;
1043 drawRange_Text(run->font, bpos, fg, run->text);
1044 deinit_String(&bannerText);
1045 }
1046 else {
1047 drawRange_Text(run->font, visPos, fg, run->text);
1048 }
1021 /* Presentation of links. */ 1049 /* Presentation of links. */
1022 if (run->linkId) { 1050 if (run->linkId) {
1023 const int metaFont = paragraph_FontId; 1051 const int metaFont = paragraph_FontId;
@@ -1124,7 +1152,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
1124 .bounds = documentBounds_DocumentWidget_(d) 1152 .bounds = documentBounds_DocumentWidget_(d)
1125 }; 1153 };
1126 init_Paint(&ctx.paint); 1154 init_Paint(&ctx.paint);
1127 fillRect_Paint(&ctx.paint, bounds, gray15_ColorId); 1155 fillRect_Paint(&ctx.paint, bounds, black_ColorId);
1128 setClip_Paint(&ctx.paint, bounds); 1156 setClip_Paint(&ctx.paint, bounds);
1129 render_GmDocument(d->doc, visibleRange_DocumentWidget_(d), drawRun_DrawContext_, &ctx); 1157 render_GmDocument(d->doc, visibleRange_DocumentWidget_(d), drawRun_DrawContext_, &ctx);
1130 clearClip_Paint(&ctx.paint); 1158 clearClip_Paint(&ctx.paint);
diff --git a/src/ui/text.c b/src/ui/text.c
index 0c4b8624..9468ea65 100644
--- a/src/ui/text.c
+++ b/src/ui/text.c
@@ -620,6 +620,10 @@ void drawString_Text(int fontId, iInt2 pos, int color, const iString *text) {
620 draw_Text_(fontId, pos, color, range_String(text)); 620 draw_Text_(fontId, pos, color, range_String(text));
621} 621}
622 622
623void drawRange_Text(int fontId, iInt2 pos, int color, iRangecc text) {
624 draw_Text_(fontId, pos, color, text);
625}
626
623void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, const char *format, ...) { 627void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, const char *format, ...) {
624 iBlock chars; 628 iBlock chars;
625 init_Block(&chars, 0); { 629 init_Block(&chars, 0); {
diff --git a/src/ui/text.h b/src/ui/text.h
index 2068d51b..713ee1e7 100644
--- a/src/ui/text.h
+++ b/src/ui/text.h
@@ -79,6 +79,7 @@ void draw_Text (int fontId, iInt2 pos, int color, const char *text,
79void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); 79void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...);
80void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); 80void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...);
81void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); 81void drawString_Text (int fontId, iInt2 pos, int color, const iString *text);
82void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text);
82 83
83SDL_Texture * glyphCache_Text (void); 84SDL_Texture * glyphCache_Text (void);
84 85