=> 5f7709f0b84e74fde847cdcf02c41990ef037daa
[1mdiff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c[m [1mindex afd0070f..1870efd6 100644[m [1m--- a/src/ui/documentwidget.c[m [1m+++ b/src/ui/documentwidget.c[m [36m@@ -41,6 +41,7 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m #include "inputwidget.h"[m #include "keys.h"[m #include "labelwidget.h"[m [32m+[m[32m#include "linkinfo.h"[m #include "media.h"[m #include "paint.h"[m #include "periodic.h"[m [36m@@ -161,10 +162,10 @@[m [menum iDrawBufsFlag {[m };[m [m struct Impl_DrawBufs {[m [31m- int flags;[m [31m- SDL_Texture * sideIconBuf;[m [31m- iTextBuf * timestampBuf;[m [31m- uint32_t lastRenderTime;[m [32m+[m[32m int flags;[m [32m+[m[32m SDL_Texture *sideIconBuf;[m [32m+[m[32m iTextBuf *timestampBuf;[m [32m+[m[32m uint32_t lastRenderTime;[m };[m [m static void init_DrawBufs(iDrawBufs *d) {[m [36m@@ -234,10 +235,11 @@[m [menum iDocumentWidgetFlag {[m fromCache_DocumentWidgetFlag = iBit(16), /* don't write anything to cache */[m animationPlaceholder_DocumentWidgetFlag = iBit(17), /* avoid slow operations */[m invalidationPending_DocumentWidgetFlag = iBit(18), /* invalidate as soon as convenient */[m [31m- leftWheelSwipe_DocumentWidgetFlag = iBit(19), /* swipe state flags are used on desktop */[m [31m- rightWheelSwipe_DocumentWidgetFlag = iBit(20), [m [31m- eitherWheelSwipe_DocumentWidgetFlag = leftWheelSwipe_DocumentWidgetFlag | rightWheelSwipe_DocumentWidgetFlag,[m [31m-// wheelSwipeFinished_DocumentWidgetFlag = iBit(21),[m [32m+[m[32m leftWheelSwipe_DocumentWidgetFlag = iBit(19), /* swipe state flags are used on desktop */[m [32m+[m[32m rightWheelSwipe_DocumentWidgetFlag = iBit(20),[m[41m [m [32m+[m[32m eitherWheelSwipe_DocumentWidgetFlag = leftWheelSwipe_DocumentWidgetFlag |[m [32m+[m[32m rightWheelSwipe_DocumentWidgetFlag,[m [32m+[m[32m// wheelSwipeFinished_DocumentWidgetFlag = iBit(21),[m };[m [m enum iDocumentLinkOrdinalMode {[m [36m@@ -322,6 +324,7 @@[m [mstruct Impl_DocumentWidget {[m iVisBufMeta * visBufMeta;[m iGmRunRange renderRuns;[m iPtrSet * invalidRuns;[m [32m+[m[32m iLinkInfo * linkInfo;[m [m /* Widget structure: */ [m iScrollWidget *scroll;[m [36m@@ -413,6 +416,7 @@[m [mvoid init_DocumentWidget(iDocumentWidget *d) {[m init_String(&d->pendingGotoHeading);[m init_String(&d->linePrecedingLink);[m init_Click(&d->click, d, SDL_BUTTON_LEFT);[m [32m+[m[32m d->linkInfo = (deviceType_App() == desktop_AppDeviceType ? new_LinkInfo() : NULL);[m addChild_Widget(w, iClob(d->scroll = new_ScrollWidget()));[m d->menu = NULL; /* created when clicking */[m d->playerMenu = NULL;[m [36m@@ -457,6 +461,7 @@[m [mvoid deinit_DocumentWidget(iDocumentWidget *d) {[m delete_VisBuf(d->visBuf);[m free(d->visBufMeta);[m delete_PtrSet(d->invalidRuns);[m [32m+[m[32m delete_LinkInfo(d->linkInfo);[m iRelease(d->media);[m iRelease(d->request);[m delete_Gempub(d->sourceGempub);[m [36m@@ -737,7 +742,8 @@[m [mstatic void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse);[m static void animate_DocumentWidget_(void *ticker) {[m iDocumentWidget *d = ticker;[m refresh_Widget(d);[m [31m- if (!isFinished_Anim(&d->sideOpacity) || !isFinished_Anim(&d->altTextOpacity)) {[m [32m+[m[32m if (!isFinished_Anim(&d->sideOpacity) || !isFinished_Anim(&d->altTextOpacity) ||[m [32m+[m[32m (d->linkInfo && !isFinished_Anim(&d->linkInfo->opacity))) {[m addTicker_App(animate_DocumentWidget_, d);[m }[m }[m [36m@@ -789,6 +795,10 @@[m [mstatic void updateHover_DocumentWidget_(iDocumentWidget *d, iInt2 mouse) {[m if (d->hoverLink) {[m invalidateLink_DocumentWidget_(d, d->hoverLink->linkId);[m }[m [32m+[m[32m if (update_LinkInfo(d->linkInfo, d->doc, d->hoverLink ? d->hoverLink->linkId : 0,[m [32m+[m[32m width_Widget(w))) {[m [32m+[m[32m animate_DocumentWidget_(d);[m[41m [m [32m+[m[32m }[m refresh_Widget(w);[m }[m /* Hovering over preformatted blocks. */[m [36m@@ -4923,6 +4933,7 @@[m [mstatic void drawRun_DrawContext_(void *context, const iGmRun *run) {[m (float) bodySize_GmRequest(mr->req) / 1.0e6f);[m }[m }[m [32m+[m[32m#if 0[m else if (isHover) {[m /* TODO: Make this a dynamic overlay, not part of the VisBuf content. */[m const iGmLinkId linkId = d->widget->hoverLink->linkId;[m [36m@@ -5002,6 +5013,7 @@[m [mstatic void drawRun_DrawContext_(void *context, const iGmRun *run) {[m deinit_String(&str);[m }[m }[m [32m+[m[32m#endif[m }[m if (0) {[m drawRect_Paint(&d->paint, (iRect){ visPos, run->bounds.size }, green_ColorId);[m [36m@@ -5446,6 +5458,25 @@[m [mstatic void draw_DocumentWidget_(const iDocumentWidget *d) {[m }[m unsetClip_Paint(&ctx.paint);[m drawSideElements_DocumentWidget_(d);[m [32m+[m[32m if (deviceType_App() == desktop_AppDeviceType && prefs_App()->hoverLink && d->linkInfo) {[m [32m+[m[32m const int pad = gap_UI;[m [32m+[m[32m update_LinkInfo(d->linkInfo,[m [32m+[m[32m d->doc,[m [32m+[m[32m d->hoverLink ? d->hoverLink->linkId : 0,[m [32m+[m[32m width_Rect(bounds) - 2 * pad);[m [32m+[m[32m const iInt2 infoSize = size_LinkInfo(d->linkInfo);[m [32m+[m[32m iInt2 infoPos = add_I2(bottomLeft_Rect(bounds), init_I2(pad, -infoSize.y - pad));[m [32m+[m[32m if (d->hoverLink) {[m [32m+[m[32m const iRect runRect = runRect_DocumentWidget_(d, d->hoverLink);[m [32m+[m[32m d->linkInfo->isAltPos =[m [32m+[m[32m (bottom_Rect(runRect) >= infoPos.y - lineHeight_Text(paragraph_FontId));[m [32m+[m[32m }[m [32m+[m[32m if (d->linkInfo->isAltPos) {[m [32m+[m[32m infoPos.y = top_Rect(bounds) + pad;[m [32m+[m[32m }[m [32m+[m[32m draw_LinkInfo(d->linkInfo, infoPos);[m [32m+[m[32m }[m [32m+[m[32m#if 0[m if (prefs_App()->hoverLink && d->hoverLink) {[m const int font = uiLabel_FontId;[m const iRangecc linkUrl = range_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId));[m [36m@@ -5455,6 +5486,7 @@[m [mstatic void draw_DocumentWidget_(const iDocumentWidget *d) {[m fillRect_Paint(&ctx.paint, linkRect, tmBackground_ColorId);[m drawRange_Text(font, addX_I2(topLeft_Rect(linkRect), gap_UI), tmParagraph_ColorId, linkUrl);[m }[m [32m+[m[32m#endif[m }[m if (colorTheme_App() == pureWhite_ColorTheme) {[m drawHLine_Paint(&ctx.paint, topLeft_Rect(bounds), width_Rect(bounds), uiSeparator_ColorId);[m [1mdiff --git a/src/ui/linkinfo.c b/src/ui/linkinfo.c[m [1mindex e20c183c..72abea1a 100644[m [1m--- a/src/ui/linkinfo.c[m [1m+++ b/src/ui/linkinfo.c[m [36m@@ -21,12 +21,23 @@[m [mANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT[m SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m [m #include "linkinfo.h"[m [32m+[m[32m#include "metrics.h"[m [32m+[m[32m#include "paint.h"[m [32m+[m[32m#include "../gmcerts.h"[m [32m+[m[32m#include "../app.h"[m [m [31m-#define hPad_LinkInfo_ (2 * gap_UI)[m [31m-#define vPad_LinkInfo_ (1 * gap_UI)[m [32m+[m[32m#include[m [32m+[m [32m+[m[32miDefineTypeConstruction(LinkInfo)[m [32m+[m [32m+[m[32m#define minWidth_LinkInfo_ (40 * gap_UI)[m [32m+[m[32m#define hPad_LinkInfo_ (2 * gap_UI)[m [32m+[m[32m#define vPad_LinkInfo_ (1 * gap_UI)[m [m void init_LinkInfo(iLinkInfo *d) {[m [31m- d->buf = NULL; [m [32m+[m[32m d->buf = NULL;[m [32m+[m[32m init_Anim(&d->opacity, 0.0f);[m [32m+[m[32m d->isAltPos = iFalse;[m }[m [m void deinit_LinkInfo(iLinkInfo *d) {[m [36m@@ -37,5 +48,136 @@[m [miInt2 size_LinkInfo(const iLinkInfo *d) {[m if (!d->buf) {[m return zero_I2();[m }[m [31m- return add_I2(d->buf->size, init_I2(2 * hPad_LinkInfo_, 2 * vPad_LinkInfo_)));[m [32m+[m[32m return add_I2(d->buf->size, init_I2(2 * hPad_LinkInfo_, 2 * vPad_LinkInfo_));[m [32m+[m[32m}[m [32m+[m [32m+[m[32miBool update_LinkInfo(iLinkInfo *d, const iGmDocument *doc, iGmLinkId linkId, int maxWidth) {[m [32m+[m[32m if (!d) {[m [32m+[m[32m return iFalse;[m [32m+[m[32m }[m [32m+[m[32m if (d->linkId != linkId || d->maxWidth != maxWidth) {[m [32m+[m[32m d->linkId = linkId;[m [32m+[m[32m d->maxWidth = maxWidth;[m [32m+[m[32m invalidate_LinkInfo(d);[m [32m+[m[32m if (linkId) {[m [32m+[m[32m /* Measure and draw. */[m [32m+[m[32m if (targetValue_Anim(&d->opacity) < 1) {[m [32m+[m[32m setValue_Anim(&d->opacity, 1, 75);[m [32m+[m[32m }[m [32m+[m[32m const int avail = iMax(minWidth_LinkInfo_, maxWidth) - 2 * hPad_LinkInfo_;[m [32m+[m[32m const iString *url = linkUrl_GmDocument(doc, linkId);[m [32m+[m[32m iUrl parts;[m [32m+[m[32m init_Url(&parts, url);[m [32m+[m[32m const int flags = linkFlags_GmDocument(doc, linkId);[m [32m+[m[32m const enum iGmLinkScheme scheme = scheme_GmLinkFlag(flags);[m [32m+[m[32m const iBool showHost = (flags & humanReadable_GmLinkFlag &&[m [32m+[m[32m (!isEmpty_Range(&parts.host) ||[m [32m+[m[32m scheme == mailto_GmLinkScheme));[m [32m+[m[32m const iBool showImage = (flags & imageFileExtension_GmLinkFlag) != 0;[m [32m+[m[32m const iBool showAudio = (flags & audioFileExtension_GmLinkFlag) != 0;[m [32m+[m[32m// int fg = linkColor_GmDocument(doc, linkId, textHover_GmLinkPart);[m [32m+[m[32m iString str;[m [32m+[m[32m init_String(&str);[m [32m+[m[32m /* Identity that will be used. */[m [32m+[m[32m const iGmIdentity *ident = identityForUrl_GmCerts(certs_App(), url);[m [32m+[m[32m if (ident) {[m [32m+[m[32m appendFormat_String(&str, person_Icon " %s",[m [32m+[m[32m //escape_Color(tmBannerItemTitle_ColorId),[m [32m+[m[32m cstr_String(name_GmIdentity(ident)));[m[41m [m [32m+[m[32m } /* Show scheme and host. */[m [32m+[m[32m if ((showHost ||[m [32m+[m[32m (flags & (imageFileExtension_GmLinkFlag | audioFileExtension_GmLinkFlag))) &&[m [32m+[m[32m scheme != mailto_GmLinkScheme) {[m [32m+[m[32m if (!isEmpty_String(&str)) {[m [32m+[m[32m appendCStr_String(&str, "\n");[m [32m+[m[32m }[m [32m+[m[32m if (showHost && scheme != gemini_GmLinkScheme) {[m [32m+[m[32m append_String([m [32m+[m[32m &str, collect_String(upper_String(collectNewRange_String(parts.scheme))));[m [32m+[m[32m appendCStr_String(&str, " \u2014 ");[m [32m+[m[32m }[m [32m+[m[32m if (showHost) {[m [32m+[m[32m appendFormat_String(&str, "\x1b[1m%s\x1b[0m", cstr_Rangecc(parts.host));[m [32m+[m[32m }[m [32m+[m[32m if (showImage || showAudio) {[m [32m+[m[32m appendFormat_String([m [32m+[m[32m &str,[m [32m+[m[32m "%s%s%s",[m [32m+[m[32m // showHost ? (scheme == mailto_GmLinkScheme ? cstr_String(url)[m [32m+[m[32m // : scheme != gemini_GmLinkScheme[m [32m+[m[32m // ? format_CStr("%s \u2014 %s",[m [32m+[m[32m // cstrCollect_String(upper_String([m [32m+[m[32m // collectNewRange_String(parts.scheme))),[m [32m+[m[32m // cstr_Rangecc(parts.host))[m [32m+[m[32m // : cstr_Rangecc(parts.host))[m [32m+[m[32m // : "",[m [32m+[m[32m // showHost ? format_CStr("\x1b[1m%s\x1b[0m", cstr_Rangecc(parts.host)) : "",[m [32m+[m[32m showHost && (showImage || showAudio) ? " \u2014" : "",[m [32m+[m[32m showImage || showAudio[m [32m+[m[32m ? ""[m [32m+[m[32m : "", // escape_Color(linkColor_GmDocument(doc, linkId, domain_GmLinkPart)),[m [32m+[m[32m showImage || showAudio[m [32m+[m[32m ? format_CStr(showImage ? photo_Icon " %s " : "\U0001f3b5 %s",[m [32m+[m[32m cstr_Lang(showImage ? "link.hint.image" : "link.hint.audio"))[m [32m+[m[32m : "");[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m if (flags & visited_GmLinkFlag) {[m [32m+[m[32m iDate date;[m [32m+[m[32m init_Date(&date, linkTime_GmDocument(doc, linkId));[m [32m+[m[32m if (!isEmpty_String(&str)) {[m [32m+[m[32m appendCStr_String(&str, " \u2014 ");[m [32m+[m[32m }[m [32m+[m[32m// appendCStr_String(&str, escape_Color(tmQuoteIcon_ColorId));[m [32m+[m[32m iString *dateStr = format_Date(&date, "%b %d");[m [32m+[m[32m append_String(&str, dateStr);[m [32m+[m[32m delete_String(dateStr);[m [32m+[m[32m }[m [32m+[m[32m if (!isEmpty_String(&str)) {[m [32m+[m[32m appendCStr_String(&str, "\n");[m [32m+[m[32m }[m [32m+[m[32m appendRange_String(&str, range_String(url));[m [32m+[m[32m /* Draw the text. */[m[41m [m [32m+[m[32m iWrapText wt = { .text = range_String(&str), .maxWidth = avail, .mode = word_WrapTextMode };[m [32m+[m[32m d->buf = new_TextBuf(&wt, uiLabel_FontId, tmQuote_ColorId);[m [32m+[m[32m deinit_String(&str);[m [32m+[m[32m }[m [32m+[m[32m else {[m [32m+[m[32m if (targetValue_Anim(&d->opacity) > 0) {[m [32m+[m[32m setValue_Anim(&d->opacity, 0, 150);[m [32m+[m[32m }[m[41m [m [32m+[m[32m }[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m return iFalse;[m [32m+[m[32m}[m [32m+[m [32m+[m[32mvoid invalidate_LinkInfo(iLinkInfo *d) {[m [32m+[m[32m if (targetValue_Anim(&d->opacity) > 0) {[m [32m+[m[32m setValue_Anim(&d->opacity, 0, 150);[m [32m+[m[32m }[m[41m [m [32m+[m[41m [m [32m+[m[32m // if (d->buf) {[m [32m+[m[32m// delete_TextBuf(d->buf);[m [32m+[m[32m// d->buf = NULL;[m [32m+[m[32m// }[m [32m+[m[32m}[m [32m+[m [32m+[m[32mvoid draw_LinkInfo(const iLinkInfo *d, iInt2 topLeft) {[m [32m+[m[32m const float opacity = value_Anim(&d->opacity);[m [32m+[m[32m if (!d->buf || opacity <= 0.01f) {[m [32m+[m[32m return;[m [32m+[m[32m }[m [32m+[m[32m iPaint p;[m [32m+[m[32m init_Paint(&p);[m [32m+[m[32m iInt2 size = size_LinkInfo(d);[m [32m+[m[32m iRect rect = { topLeft, size };[m [32m+[m[32m SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);[m [32m+[m[32m p.alpha = 255 * opacity;[m [32m+[m[32m fillRect_Paint(&p, rect, tmBackgroundAltText_ColorId);[m [32m+[m[32m drawRect_Paint(&p, rect, tmFrameAltText_ColorId);[m [32m+[m[32m SDL_SetTextureAlphaMod(d->buf->texture, p.alpha);[m [32m+[m[32m draw_TextBuf(d->buf, add_I2(topLeft, init_I2(hPad_LinkInfo_, vPad_LinkInfo_)),[m [32m+[m[32m white_ColorId);[m [32m+[m[32m SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);[m }[m [1mdiff --git a/src/ui/linkinfo.h b/src/ui/linkinfo.h[m [1mindex dbedf359..a1669f95 100644[m [1m--- a/src/ui/linkinfo.h[m [1m+++ b/src/ui/linkinfo.h[m [36m@@ -23,10 +23,23 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m #pragma once[m [m #include "text.h"[m [32m+[m[32m#include "util.h"[m [32m+[m[32m#include "../gmdocument.h"[m [m iDeclareType(LinkInfo)[m iDeclareTypeConstruction(LinkInfo)[m [m struct Impl_LinkInfo {[m [32m+[m[32m iGmLinkId linkId;[m [32m+[m[32m int maxWidth;[m iTextBuf *buf;[m [32m+[m[32m iAnim opacity;[m [32m+[m[32m iBool isAltPos;[m };[m [32m+[m [32m+[m[32miBool update_LinkInfo (iLinkInfo *, const iGmDocument *doc, iGmLinkId linkId,[m [32m+[m[32m int maxWidth); /* returns true if changed */[m [32m+[m[32mvoid invalidate_LinkInfo (iLinkInfo *);[m [32m+[m [32m+[m[32miInt2 size_LinkInfo (const iLinkInfo *);[m [32m+[m[32mvoid draw_LinkInfo (const iLinkInfo *, iInt2 topLeft);[m
text/gemini; charset=utf-8
This content has been proxied by September (3851b).