From 824da6cbec4a6a28d0fde710777b2bbf771006a0 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 7 Dec 2021 21:00:39 +0200 Subject: Document-specific palettes Manage a color palette separately for each GmDocument, and activate one of them globally whenever a document is being drawn. Palettes are cached in memory. --- src/gmdocument.c | 8 +++- src/gmutil.c | 11 +++++ src/gmutil.h | 2 + src/history.c | 50 ++++++++++++++------- src/history.h | 5 ++- src/ui/documentwidget.c | 116 ++++++++++++++++++++++++++++-------------------- 6 files changed, 125 insertions(+), 67 deletions(-) diff --git a/src/gmdocument.c b/src/gmdocument.c index 884f8c07..e3c06d49 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -1710,9 +1710,12 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { } void makePaletteGlobal_GmDocument(const iGmDocument *d) { - if (d->isPaletteValid) { - memcpy(get_Root()->tmPalette, d->palette, sizeof(d->palette)); + if (!d->isPaletteValid) { + /* Recompute the palette since it's needed now. */ + setThemeSeed_GmDocument((iGmDocument *) d, urlThemeSeed_String(&d->url)); } + iAssert(d->isPaletteValid); + memcpy(get_Root()->tmPalette, d->palette, sizeof(d->palette)); } void invalidatePalette_GmDocument(iGmDocument *d) { @@ -1885,6 +1888,7 @@ static void normalize_GmDocument(iGmDocument *d) { void setUrl_GmDocument(iGmDocument *d, const iString *url) { url = canonicalUrl_String(url); set_String(&d->url, url); + setThemeSeed_GmDocument(d, urlThemeSeed_String(url)); iUrl parts; init_Url(&parts, url); setRange_String(&d->localHost, parts.host); diff --git a/src/gmutil.c b/src/gmutil.c index 643fbea7..79462e41 100644 --- a/src/gmutil.c +++ b/src/gmutil.c @@ -253,6 +253,17 @@ iRangecc urlRoot_String(const iString *d) { return (iRangecc){ constBegin_String(d), rootEnd }; } +const iBlock *urlThemeSeed_String(const iString *url) { + if (equalCase_Rangecc(urlScheme_String(url), "file")) { + return collect_Block(new_Block(0)); + } + const iRangecc user = urlUser_String(url); + if (isEmpty_Range(&user)) { + return collect_Block(newRange_Block(urlHost_String(url))); + } + return collect_Block(newRange_Block(user)); +} + static iBool isAbsolutePath_(iRangecc path) { return isAbsolute_Path(collect_String(urlDecode_String(collect_String(newRange_String(path))))); } diff --git a/src/gmutil.h b/src/gmutil.h index 6b10b444..35a7ee0d 100644 --- a/src/gmutil.h +++ b/src/gmutil.h @@ -117,6 +117,8 @@ iRangecc urlHost_String (const iString *); uint16_t urlPort_String (const iString *); iRangecc urlUser_String (const iString *); iRangecc urlRoot_String (const iString *); +const iBlock * urlThemeSeed_String (const iString *); + const iString * absoluteUrl_String (const iString *, const iString *urlMaybeRelative); iBool isLikelyUrl_String (const iString *); iBool isKnownScheme_Rangecc (iRangecc scheme); /* any URI scheme */ diff --git a/src/history.c b/src/history.c index 7185912f..8c268ec0 100644 --- a/src/history.c +++ b/src/history.c @@ -110,6 +110,14 @@ iHistory *copy_History(const iHistory *d) { return copy; } +void lock_History(iHistory *d) { + lock_Mutex(d->mtx); +} + +void unlock_History(iHistory *d) { + unlock_Mutex(d->mtx); +} + iMemInfo memoryUsage_History(const iHistory *d) { iMemInfo mem = { 0, 0 }; iConstForEach(Array, i, &d->recent) { @@ -297,20 +305,22 @@ void add_History(iHistory *d, const iString *url) { unlock_Mutex(d->mtx); } -iBool preceding_History(iHistory *d, iRecentUrl *recent_out) { +iRecentUrl *precedingLocked_History(iHistory *d) { + /* NOTE: Manual lock and unlock are required when using this; returning an internal pointer. */ iBool ok = iFalse; - lock_Mutex(d->mtx); - if (!isEmpty_Array(&d->recent) && d->recentPos < size_Array(&d->recent) - 1) { - const iRecentUrl *recent = constAt_Array(&d->recent, size_Array(&d->recent) - 1 - - (d->recentPos + 1)); - set_String(&recent_out->url, &recent->url); - recent_out->normScrollY = recent->normScrollY; - iChangeRef(recent_out->cachedDoc, recent->cachedDoc); + //lock_Mutex(d->mtx); + const size_t lastIndex = size_Array(&d->recent) - 1; + if (!isEmpty_Array(&d->recent) && d->recentPos < lastIndex) { + return at_Array(&d->recent, lastIndex - (d->recentPos + 1)); +// set_String(&recent_out->url, &recent->url); +// recent_out->normScrollY = recent->normScrollY; +// iChangeRef(recent_out->cachedDoc, recent->cachedDoc); /* Cached response is not returned, would involve a deep copy. */ - ok = iTrue; +// ok = iTrue; } - unlock_Mutex(d->mtx); - return ok; + //unlock_Mutex(d->mtx); +// return ok; + return NULL; } #if 0 @@ -395,12 +405,20 @@ void setCachedDocument_History(iHistory *d, iGmDocument *doc, iBool openedFromSi lock_Mutex(d->mtx); iRecentUrl *item = mostRecentUrl_History(d); if (item) { - iAssert(equal_String(url_GmDocument(doc), &item->url)); - item->flags.openedFromSidebar = openedFromSidebar; - if (item->cachedDoc != doc) { - iRelease(item->cachedDoc); - item->cachedDoc = ref_Object(doc); + if (equal_String(url_GmDocument(doc), &item->url)) { + item->flags.openedFromSidebar = openedFromSidebar; + if (item->cachedDoc != doc) { + iRelease(item->cachedDoc); + item->cachedDoc = ref_Object(doc); + } + } +#if !defined (NDEBUG) + else { + printf("[History] Not updating cached document; expecting {%s} but document URL is {%s}\n", + cstr_String(&item->url), + cstr_String(url_GmDocument(doc))); } +#endif } unlock_Mutex(d->mtx); } diff --git a/src/history.h b/src/history.h index d3daae80..0c07759b 100644 --- a/src/history.h +++ b/src/history.h @@ -58,6 +58,8 @@ iDeclareTypeConstruction(History) iDeclareTypeSerialization(History) iHistory * copy_History (const iHistory *); +void lock_History (iHistory *); +void unlock_History (iHistory *); void clear_History (iHistory *); void add_History (iHistory *, const iString *url); @@ -66,8 +68,7 @@ void setCachedResponse_History (iHistory *, const iGmResponse *response void setCachedDocument_History (iHistory *, iGmDocument *doc, iBool openedFromSidebar); iBool goBack_History (iHistory *); iBool goForward_History (iHistory *); -iBool preceding_History (iHistory *d, iRecentUrl *recent_out); -//iBool following_History (iHistory *d, iRecentUrl *recent_out); +iRecentUrl *precedingLocked_History (iHistory *); /* requires manual lock/unlock! */ iRecentUrl *recentUrl_History (iHistory *, size_t pos); iRecentUrl *mostRecentUrl_History (iHistory *); iRecentUrl *findUrl_History (iHistory *, const iString *url); diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 1760be9e..1c861b8f 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -330,7 +330,7 @@ void init_DocumentWidget(iDocumentWidget *d) { } init_PersistentDocumentState(&d->mod); d->flags = 0; - d->phoneToolbar = NULL; + d->phoneToolbar = findWidget_App("toolbar"); d->footerButtons = NULL; iZap(d->certExpiry); d->certFingerprint = new_Block(0); @@ -961,7 +961,8 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { animateMedia_DocumentWidget_(d); /* Remember scroll positions of recently visited pages. */ { iRecentUrl *recent = mostRecentUrl_History(d->mod.history); - if (recent && docSize && d->state == ready_RequestState) { + if (recent && docSize && d->state == ready_RequestState && + equal_String(&recent->url, d->mod.url)) { recent->normScrollY = normScrollPos_DocumentWidget_(d); } } @@ -1162,27 +1163,14 @@ static void replaceDocument_DocumentWidget_(iDocumentWidget *d, iGmDocument *new } static void updateBanner_DocumentWidget_(iDocumentWidget *d) { - /* TODO: Set width. */ setSite_Banner(d->banner, siteText_DocumentWidget_(d), siteIcon_GmDocument(d->doc)); } static void updateTheme_DocumentWidget_(iDocumentWidget *d) { if (document_App() != d || category_GmStatusCode(d->sourceStatus) == categoryInput_GmStatusCode) { return; - } - if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) { - iBlock empty; - init_Block(&empty, 0); - setThemeSeed_GmDocument(d->doc, &empty); - deinit_Block(&empty); - } - else if (isEmpty_String(d->titleUser)) { - setThemeSeed_GmDocument(d->doc, - collect_Block(newRange_Block(urlHost_String(d->mod.url)))); - } - else { - setThemeSeed_GmDocument(d->doc, &d->titleUser->chars); - } + } +// setThemeSeed_GmDocument(d->doc, urlThemeSeed_String(d->mod.url)); /* theme palette and icon */ d->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag; updateBanner_DocumentWidget_(d); } @@ -2616,6 +2604,7 @@ static void swap_DocumentWidget_(iDocumentWidget *d, iGmDocument *doc, iAssert(isInstance_Object(doc, &Class_GmDocument)); replaceDocument_DocumentWidget_(d, doc); d->scrollY = swapBuffersWith->scrollY; + d->scrollY.widget = as_Widget(d); iSwap(iBanner *, d->banner, swapBuffersWith->banner); setOwner_Banner(d->banner, d); setOwner_Banner(swapBuffersWith->banner, swapBuffersWith); @@ -2631,6 +2620,14 @@ static iWidget *swipeParent_DocumentWidget_(iDocumentWidget *d) { return findChild_Widget(as_Widget(d)->root->widget, "doctabs"); } +static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { + url = canonicalUrl_String(url); + if (!equal_String(d->mod.url, url)) { + d->flags |= urlChanged_DocumentWidgetFlag; + set_String(d->mod.url, url); + } +} + static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { iWidget *w = as_Widget(d); /* Swipe animations are rather complex and utilize both cached GmDocument content @@ -2658,18 +2655,31 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { swipeIn->widget.rect.size = d->widget.rect.size; swipeIn->widget.offsetRef = parent_Widget(w); /* Use a cached document for the layer underneath. */ { - iRecentUrl *recent = new_RecentUrl(); - preceding_History(d->mod.history, recent); - if (recent->cachedDoc) { +// iRecentUrl *recent = new_RecentUrl(); + lock_History(d->mod.history); + iRecentUrl *recent = precedingLocked_History(d->mod.history); + if (recent && recent->cachedResponse) { + /* iChangeRef(swipeIn->doc, recent->cachedDoc); updateBanner_DocumentWidget_(swipeIn); updateScrollMax_DocumentWidget_(d); setValue_Anim(&swipeIn->scrollY.pos, pageHeight_DocumentWidget_(d) * recent->normScrollY, 0); updateVisible_DocumentWidget_(swipeIn); - swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; + swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag;*/ + setUrl_DocumentWidget_(swipeIn, &recent->url); + updateFromCachedResponse_DocumentWidget_(swipeIn, + recent->normScrollY, + recent->cachedResponse, + recent->cachedDoc); } - delete_RecentUrl(recent); + else { + setUrlAndSource_DocumentWidget(swipeIn, &recent->url, + collectNewCStr_String("text/gemini"), + collect_Block(new_Block(0))); + } +// delete_RecentUrl(recent); + unlock_History(d->mod.history); } addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos); } @@ -2677,8 +2687,7 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { if (side == 2) { /* right edge */ if (offset < -get_Window()->pixelRatio * 10) { int animSpan = 10; - if (!atLatest_History(d->mod.history) && - ~flags_Widget(w) & dragged_WidgetFlag) { + if (!atLatest_History(d->mod.history) && ~flags_Widget(w) & dragged_WidgetFlag) { animSpan = 0; postCommand_Widget(d, "navigate.forward"); setFlags_Widget(w, dragged_WidgetFlag, iTrue); @@ -2695,6 +2704,13 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { setFlags_Widget(as_Widget(target), refChildrenOffset_WidgetFlag, iTrue); as_Widget(target)->offsetRef = parent_Widget(w); destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ + /* The `d` document will now navigate forward and be replaced with a cached + copy. However, if a cached response isn't available, we'll need to show a + blank page. */ + setUrlAndSource_DocumentWidget(d, + collectNewCStr_String("about:blank"), + collectNewCStr_String("text/gemini"), + collect_Block(new_Block(0))); } if (flags_Widget(w) & dragged_WidgetFlag) { setVisualOffset_Widget(w, width_Widget(w) + @@ -2718,10 +2734,19 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { } if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 1) { iWidget *swipeParent = swipeParent_DocumentWidget_(d); - iWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); + iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); +// iDocumentWidget *swipeInDoc = (iDocumentWidget *) swipeIn; + /* "swipe.back" will soon follow. The `d` document will do the actual back navigation, + switching immediately to a cached page. However, if one is not available, we'll need + to show a blank page for a while. */ if (swipeIn) { - swipeIn->offsetRef = NULL; - destroy_Widget(swipeIn); + setUrlAndSource_DocumentWidget(d, + swipeIn->mod.url, + collectNewCStr_String("text/gemini"), + collect_Block(new_Block(0))); + //swap_DocumentWidget_(d, swipeIn->doc, swipeIn); + as_Widget(swipeIn)->offsetRef = NULL; + destroy_Widget(as_Widget(swipeIn)); } } if (equal_Command(cmd, "swipe.back")) { @@ -2824,14 +2849,16 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) else if (equal_Command(cmd, "window.mouse.exited")) { return iFalse; } - else if (equal_Command(cmd, "theme.changed") && document_App() == d) { -// invalidateTheme_History(d->mod.history); /* cached colors */ - updateTheme_DocumentWidget_(d); - updateVisible_DocumentWidget_(d); - updateTrust_DocumentWidget_(d, NULL); - d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; - invalidate_DocumentWidget_(d); - refresh_Widget(w); + else if (equal_Command(cmd, "theme.changed")) { + invalidateTheme_History(d->mod.history); /* forget cached color palettes */ + if (document_App() == d) { + updateTheme_DocumentWidget_(d); + updateVisible_DocumentWidget_(d); + updateTrust_DocumentWidget_(d, NULL); + d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; + invalidate_DocumentWidget_(d); + refresh_Widget(w); + } } else if (equal_Command(cmd, "document.layout.changed") && document_Root(get_Root()) == d) { if (argLabel_Command(cmd, "redo")) { @@ -5047,6 +5074,7 @@ static void prerender_DocumentWidget_(iAny *context) { }; // printf("%u prerendering\n", SDL_GetTicks()); if (d->visBuf->buffers[0].texture) { + makePaletteGlobal_GmDocument(d->doc); if (render_DocumentWidget_(d, &ctx, iTrue /* just fill up progressively */)) { /* Something was drawn, should check if there is still more to do. */ addTicker_App(prerender_DocumentWidget_, context); @@ -5062,10 +5090,11 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { if (width_Rect(bounds) <= 0) { return; } - /* TODO: Come up with a better palette caching system. - It should be able to recompute cached colors in `History` when the theme has changed. - Cache the theme seed in `GmDocument`? */ -// makePaletteGlobal_GmDocument(d->doc); + /* Each document has its own palette, but the drawing routines rely on a global one. + As we're now drawing a document, ensure that the right palette is in effect. + Document theme colors can be used elsewhere, too, but first a document's palette + must be made global. */ + makePaletteGlobal_GmDocument(d->doc); if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { updateTimestampBuf_DocumentWidget_(d); } @@ -5319,14 +5348,6 @@ void deserializeState_DocumentWidget(iDocumentWidget *d, iStream *ins) { updateFromHistory_DocumentWidget_(d); } -static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { - url = canonicalUrl_String(url); - if (!equal_String(d->mod.url, url)) { - d->flags |= urlChanged_DocumentWidgetFlag; - set_String(d->mod.url, url); - } -} - void setUrlFlags_DocumentWidget(iDocumentWidget *d, const iString *url, int setUrlFlags) { iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, (setUrlFlags & openedFromSidebar_DocumentWidgetSetUrlFlag) != 0); @@ -5352,6 +5373,7 @@ void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, cons set_String(&resp->meta, mime); set_Block(&resp->body, source); updateFromCachedResponse_DocumentWidget_(d, 0, resp, NULL); + updateBanner_DocumentWidget_(d); delete_GmResponse(resp); } -- cgit v1.2.3