From 67750111c420fcf97e31289bb4770cb7f69c426e Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Wed, 29 Jul 2020 11:00:54 +0300 Subject: DocumentWidget: Improved link presentation --- src/gmdocument.c | 12 ++++++-- src/gmdocument.h | 22 +++++++++----- src/ui/documentwidget.c | 78 ++++++++++++++++++++++++++++++++++--------------- 3 files changed, 78 insertions(+), 34 deletions(-) diff --git a/src/gmdocument.c b/src/gmdocument.c index 15d81603..f7016ba4 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -245,7 +245,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { header2_FontId, header3_FontId, regular_FontId, - }; + }; static const int colors[max_GmLineType] = { gray75_ColorId, gray75_ColorId, @@ -255,7 +255,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { white_ColorId, white_ColorId, white_ColorId, - }; + }; static const int indents[max_GmLineType] = { 5, 10, 5, 10, 0, 0, 0, 5 }; @@ -290,6 +290,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { } while (nextSplit_Rangecc(&content, "\n", &line)) { iGmRun run; + run.flags = 0; run.color = white_ColorId; run.linkId = 0; run.imageId = 0; @@ -402,7 +403,9 @@ static void doLayout_GmDocument_(iGmDocument *d) { isFirstText = iFalse; } iRangecc runLine = line; - /* Create one or more runs for this line. */ + /* Create one or more text runs for this line. */ + run.flags |= startOfLine_GmRunFlag; + iAssert(!isEmpty_Range(&runLine)); /* must have something at this point */ while (!isEmpty_Range(&runLine)) { /* Little bit of breathing space between wrapped lines. */ if ((type == text_GmLineType || type == quote_GmLineType || @@ -427,10 +430,13 @@ static void doLayout_GmDocument_(iGmDocument *d) { contPos = runLine.end; } pushBack_Array(&d->layout, &run); + run.flags &= ~startOfLine_GmRunFlag; runLine.start = contPos; trimStart_Rangecc(&runLine); pos.y += lineHeight_Text(run.font); } + /* Flag the end of line, too. */ + ((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag; /* Image content. */ if (type == link_GmLineType) { const size_t imgIndex = findLinkImage_GmDocument_(d, run.linkId); diff --git a/src/gmdocument.h b/src/gmdocument.h index 584efb5a..c353c733 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h @@ -12,14 +12,14 @@ iDeclareType(GmRun) typedef uint16_t iGmLinkId; enum iGmLinkFlags { - userFriendly_GmLinkFlag = 0x1, - remote_GmLinkFlag = 0x2, - http_GmLinkFlag = 0x4, - gopher_GmLinkFlag = 0x8, - file_GmLinkFlag = 0x10, - imageFileExtension_GmLinkFlag = 0x20, - audioFileExtension_GmLinkFlag = 0x40, - content_GmLinkFlag = 0x80, /* content visible below */ + userFriendly_GmLinkFlag = iBit(1), + remote_GmLinkFlag = iBit(2), + http_GmLinkFlag = iBit(3), + gopher_GmLinkFlag = iBit(4), + file_GmLinkFlag = iBit(5), + imageFileExtension_GmLinkFlag = iBit(6), + audioFileExtension_GmLinkFlag = iBit(7), + content_GmLinkFlag = iBit(8), /* content visible below */ }; iDeclareType(GmImageInfo) @@ -30,10 +30,16 @@ struct Impl_GmImageInfo { const char *mime; }; +enum iGmRunFlags { + startOfLine_GmRunFlag = iBit(1), + endOfLine_GmRunFlag = iBit(2), +}; + struct Impl_GmRun { iRangecc text; uint8_t font; uint8_t color; + uint8_t flags; iRect bounds; /* used for hit testing, may extend to edges */ iRect visBounds; /* actual visual bounds */ iGmLinkId linkId; /* zero for non-links */ diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 98d9eca5..e908f090 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -843,7 +843,8 @@ iDeclareType(DrawContext) struct Impl_DrawContext { const iDocumentWidget *widget; - iRect bounds; + iRect widgetBounds; + iRect bounds; /* document area */ iPaint paint; iBool inSelectMark; iBool inFoundMark; @@ -895,8 +896,24 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { initRange_String(&text, run->text); enum iColorId fg = run->color; const iGmDocument *doc = d->widget->doc; + const iBool isHover = + run->linkId != 0 && d->widget->hoverLink && run->linkId == d->widget->hoverLink->linkId && + !isEmpty_Rect(run->bounds); + const iInt2 visPos = add_I2(run->visBounds.pos, origin); + /* Text markers. */ + fillRange_DrawContext_(d, run, teal_ColorId, d->widget->foundMark, &d->inFoundMark); + fillRange_DrawContext_(d, run, brown_ColorId, d->widget->selectMark, &d->inSelectMark); + if (run->linkId) { + fg = linkColor_GmDocument(doc, run->linkId); + if (isHover && ~linkFlags_GmDocument(doc, run->linkId) & content_GmLinkFlag) { + fg = white_ColorId; + } + } + drawString_Text(run->font, visPos, fg, &text); + deinit_String(&text); + /* Presentation of links. */ if (run->linkId) { - /* TODO: Visualize an ongoing media request. */ + /* TODO: Show status of an ongoing media request. */ const int flags = linkFlags_GmDocument(doc, run->linkId); if (flags & content_GmLinkFlag) { fg = linkColor_GmDocument(doc, run->linkId); @@ -909,7 +926,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { info.mime, info.size.x, info.size.y, info.numBytes / 1.0e6f); if (findMediaRequest_DocumentWidget_(d->widget, run->linkId)) { appendFormat_String( - &text, " %s\u2715", run == d->widget->hoverLink ? white_ColorEscape : ""); + &text, " %s\u2715", isHover ? white_ColorEscape : ""); } drawAlign_Text(default_FontId, add_I2(topRight_Rect(run->bounds), origin), @@ -919,10 +936,10 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { deinit_String(&text); } } - else if (run == d->widget->hoverLink) { - const iGmLinkId linkId = d->widget->hoverLink->linkId; - const iString * url = linkUrl_GmDocument(doc, linkId); - const int flags = linkFlags_GmDocument(doc, linkId); + else if (isHover) { + const iGmLinkId linkId = d->widget->hoverLink->linkId; + const iString * url = linkUrl_GmDocument(doc, linkId); + const int flags = linkFlags_GmDocument(doc, linkId); iUrl parts; init_Url(&parts, url); const iString *host = collect_String(newRange_String(parts.host)); @@ -931,27 +948,37 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { const iBool showImage = (flags & imageFileExtension_GmLinkFlag) != 0; const iBool showAudio = (flags & audioFileExtension_GmLinkFlag) != 0; iRect linkRect = moved_Rect(run->visBounds, origin); - if (flags & (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag) || showHost) { + if (run->flags & endOfLine_GmRunFlag && + (flags & (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag) || + showHost)) { + iString str; + init_String(&str); + format_String(&str, " \u2014%s%s%s\r%c%s", + showHost ? " " : "", + showHost ? cstr_String(host) : "", + showHost && (showImage || showAudio) ? " \u2014" : "", + showImage || showAudio ? '0' + fg : ('0' + fg - 1), + showImage ? " View Image \U0001f5bc" + : showAudio ? " Play Audio \U0001f3b5" : ""); + const iInt2 textSize = measure_Text(default_FontId, cstr_String(&str)); + int tx = topRight_Rect(linkRect).x; + const char *msg = cstr_String(&str); + if (tx + textSize.x > right_Rect(d->widgetBounds)) { + tx = right_Rect(d->widgetBounds) - textSize.x; + fillRect_Paint(&d->paint, (iRect){ init_I2(tx, top_Rect(linkRect)), textSize }, + black_ColorId); + msg += 4; /* skip the space and dash */ + } drawAlign_Text(default_FontId, - topRight_Rect(linkRect), + init_I2(tx, top_Rect(linkRect)), fg - 1, left_Alignment, - " \u2014%s%s%s\r%c%s", - showHost ? " " : "", - showHost ? cstr_String(host) : "", - showHost && (showImage || showAudio) ? " \u2014" : "", - showImage || showAudio ? '0' + fg : ('0' + fg - 1), - showImage ? " View Image \U0001f5bc" - : showAudio ? " Play Audio \U0001f3b5" : ""); + "%s", + msg); + deinit_String(&str); } } } - const iInt2 visPos = add_I2(run->visBounds.pos, origin); - /* Text markers. */ - fillRange_DrawContext_(d, run, teal_ColorId, d->widget->foundMark, &d->inFoundMark); - fillRange_DrawContext_(d, run, brown_ColorId, d->widget->selectMark, &d->inSelectMark); - drawString_Text(run->font, visPos, fg, &text); - deinit_String(&text); // drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId); // drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, red_ColorId); @@ -961,7 +988,12 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { const iWidget *w = constAs_Widget(d); const iRect bounds = bounds_Widget(w); draw_Widget(w); - iDrawContext ctx = { .widget = d, .bounds = documentBounds_DocumentWidget_(d) }; + iDrawContext ctx = { + .widget = d, + .widgetBounds = /* omit scrollbar width */ + adjusted_Rect(bounds, zero_I2(), init_I2(-constAs_Widget(d->scroll)->rect.size.x, 0)), + .bounds = documentBounds_DocumentWidget_(d) + }; init_Paint(&ctx.paint); fillRect_Paint(&ctx.paint, bounds, gray15_ColorId); setClip_Paint(&ctx.paint, bounds); -- cgit v1.2.3