diff options
-rw-r--r-- | src/gmdocument.c | 51 | ||||
-rw-r--r-- | src/gmdocument.h | 5 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 60 | ||||
-rw-r--r-- | src/ui/text.c | 4 | ||||
-rw-r--r-- | src/ui/text.h | 1 |
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 | ||
91 | iDefineObjectConstruction(GmDocument) | 93 | iDefineObjectConstruction(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 | ||
510 | void deinit_GmDocument(iGmDocument *d) { | 529 | void 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 | |||
552 | void 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 | ||
532 | void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) { | 575 | void 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 | ||
832 | iChar siteIcon_GmDocument(const iGmDocument *d) { | ||
833 | return d->siteIcon; | ||
834 | } | ||
835 | |||
789 | const char *findLoc_GmRun(const iGmRun *d, iInt2 pos) { | 836 | const 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 { | |||
38 | enum iGmRunFlags { | 38 | enum 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 | ||
44 | struct Impl_GmRun { | 44 | struct Impl_GmRun { |
@@ -62,6 +62,7 @@ enum iGmDocumentFormat { | |||
62 | plainText_GmDocumentFormat, | 62 | plainText_GmDocumentFormat, |
63 | }; | 63 | }; |
64 | 64 | ||
65 | void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); | ||
65 | void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); | 66 | void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); |
66 | void setWidth_GmDocument (iGmDocument *, int width); | 67 | void setWidth_GmDocument (iGmDocument *, int width); |
67 | void setUrl_GmDocument (iGmDocument *, const iString *url); | 68 | void setUrl_GmDocument (iGmDocument *, const iString *url); |
@@ -74,6 +75,7 @@ typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); | |||
74 | 75 | ||
75 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, iGmDocumentRenderFunc render, void *); | 76 | void render_GmDocument (const iGmDocument *, iRangei visRangeY, iGmDocumentRenderFunc render, void *); |
76 | iInt2 size_GmDocument (const iGmDocument *); | 77 | iInt2 size_GmDocument (const iGmDocument *); |
78 | iInt2 sizeWithoutBanner_GmDocument (const iGmDocument *); | ||
77 | 79 | ||
78 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); | 80 | iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); |
79 | iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); | 81 | iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); |
@@ -88,6 +90,7 @@ enum iColorId linkColor_GmDocument (const iGmDocument *, iGmLinkId linkId); | |||
88 | const iTime * linkTime_GmDocument (const iGmDocument *, iGmLinkId linkId); | 90 | const iTime * linkTime_GmDocument (const iGmDocument *, iGmLinkId linkId); |
89 | iBool isMediaLink_GmDocument (const iGmDocument *, iGmLinkId linkId); | 91 | iBool isMediaLink_GmDocument (const iGmDocument *, iGmLinkId linkId); |
90 | const iString * title_GmDocument (const iGmDocument *); | 92 | const iString * title_GmDocument (const iGmDocument *); |
93 | iChar siteIcon_GmDocument (const iGmDocument *); | ||
91 | 94 | ||
92 | SDL_Texture * imageTexture_GmDocument (const iGmDocument *, uint16_t imageId); | 95 | SDL_Texture * imageTexture_GmDocument (const iGmDocument *, uint16_t imageId); |
93 | void imageInfo_GmDocument (const iGmDocument *, uint16_t imageId, iGmImageInfo *info_out); | 96 | void 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 | ||
163 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | 163 | static 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 | ||
623 | void drawRange_Text(int fontId, iInt2 pos, int color, iRangecc text) { | ||
624 | draw_Text_(fontId, pos, color, text); | ||
625 | } | ||
626 | |||
623 | void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, const char *format, ...) { | 627 | void 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, | |||
79 | void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); | 79 | void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); |
80 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); | 80 | void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); |
81 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); | 81 | void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); |
82 | void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); | ||
82 | 83 | ||
83 | SDL_Texture * glyphCache_Text (void); | 84 | SDL_Texture * glyphCache_Text (void); |
84 | 85 | ||