From 9f7a9058d3593d09a8fad9cd42b59f8a15873837 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 11 May 2021 12:56:14 +0300 Subject: Cache GmDocuments in memory Navigation history keeps final GmDocuments in memory for quicker restore when navigating; no need to redo layout. Changed the color escape to Vertical Tab so Carriage Returns can be left in the source, reducing need to normalize spaces. --- src/history.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/history.h') diff --git a/src/history.h b/src/history.h index 164a61d6..1acf7049 100644 --- a/src/history.h +++ b/src/history.h @@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once +#include "gmdocument.h" #include "gmrequest.h" #include @@ -37,6 +38,7 @@ struct Impl_RecentUrl { iString url; float normScrollY; /* normalized to document height */ iGmResponse *cachedResponse; /* kept in memory for quicker back navigation */ + iGmDocument *cachedDoc; /* cached copy of the presentation: layout and media (not serialized) */ }; /*----------------------------------------------------------------------------------------------*/ @@ -51,6 +53,7 @@ void clear_History (iHistory *); void add_History (iHistory *, const iString *url); void replace_History (iHistory *, const iString *url); void setCachedResponse_History (iHistory *, const iGmResponse *response); +void setCachedDocument_History (iHistory *, iGmDocument *doc); iBool goBack_History (iHistory *); iBool goForward_History (iHistory *); iRecentUrl *recentUrl_History (iHistory *, size_t pos); -- cgit v1.2.3 From ced855c338b78e05c66d38618373728ef946ebaa Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 13 May 2021 06:59:50 +0300 Subject: Debug info shows total cache/RAM usage --- src/app.c | 18 +++++++++++++++--- src/gmdocument.c | 2 +- src/history.c | 10 ++++++++++ src/history.h | 9 ++++++++- src/ui/inputwidget.c | 8 ++++---- src/ui/inputwidget.h | 25 ++++++++++++------------- 6 files changed, 50 insertions(+), 22 deletions(-) (limited to 'src/history.h') diff --git a/src/app.c b/src/app.c index 6d172c02..a50a4553 100644 --- a/src/app.c +++ b/src/app.c @@ -882,13 +882,25 @@ const iString *debugInfo_App(void) { extern char **environ; /* The environment variables. */ iApp *d = &app_; iString *msg = collectNew_String(); + iObjectList *docs = iClob(listDocuments_App(NULL)); format_String(msg, "# Debug information\n"); + appendFormat_String(msg, "## Memory usage\n"); { + iMemInfo total = { 0, 0 }; + iForEach(ObjectList, i, docs) { + iDocumentWidget *doc = i.object; + iMemInfo usage = memoryUsage_History(history_DocumentWidget(doc)); + total.cacheSize += usage.cacheSize; + total.memorySize += usage.memorySize; + } + appendFormat_String(msg, "Total cache: %.3f MB\n", total.cacheSize / 1.0e6f); + appendFormat_String(msg, "Total memory: %.3f MB\n", total.memorySize / 1.0e6f); + } appendFormat_String(msg, "## Documents\n"); - iForEach(ObjectList, k, iClob(listDocuments_App(NULL))) { + iForEach(ObjectList, k, docs) { iDocumentWidget *doc = k.object; appendFormat_String(msg, "### Tab %d.%zu: %s\n", - constAs_Widget(doc)->root == get_Window()->roots[0] ? 0 : 1, - childIndex_Widget(constAs_Widget(doc)->parent, k.object), + constAs_Widget(doc)->root == get_Window()->roots[0] ? 1 : 2, + childIndex_Widget(constAs_Widget(doc)->parent, k.object) + 1, cstr_String(bookmarkTitle_DocumentWidget(doc))); append_String(msg, collect_String(debugInfo_History(history_DocumentWidget(doc)))); } diff --git a/src/gmdocument.c b/src/gmdocument.c index 4fc0dd5e..3daa4714 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -1505,7 +1505,7 @@ static void normalize_GmDocument(iGmDocument *d) { set_String(&d->source, collect_String(normalized)); printf("orig:%zu norm:%zu\n", size_String(&d->unormSource), size_String(&d->source)); /* normalized source has an extra newline at the end */ - iAssert(wasNormalized || equal_String(&d->unormSource, &d->source)); +// iAssert(wasNormalized || equal_String(&d->unormSource, &d->source)); } void setUrl_GmDocument(iGmDocument *d, const iString *url) { diff --git a/src/history.c b/src/history.c index 58ffa5c4..87cf28e6 100644 --- a/src/history.c +++ b/src/history.c @@ -108,6 +108,16 @@ iHistory *copy_History(const iHistory *d) { return copy; } +iMemInfo memoryUsage_History(const iHistory *d) { + iMemInfo mem = { 0, 0 }; + iConstForEach(Array, i, &d->recent) { + const iRecentUrl *item = i.value; + mem.cacheSize += cacheSize_RecentUrl(item); + mem.memorySize += memorySize_RecentUrl(item); + } + return mem; +} + iString *debugInfo_History(const iHistory *d) { iString *str = new_String(); format_String(str, diff --git a/src/history.h b/src/history.h index 1acf7049..ccc19d27 100644 --- a/src/history.h +++ b/src/history.h @@ -41,6 +41,13 @@ struct Impl_RecentUrl { iGmDocument *cachedDoc; /* cached copy of the presentation: layout and media (not serialized) */ }; +iDeclareType(MemInfo) + +struct Impl_MemInfo { + size_t cacheSize; /* number of bytes stored persistently */ + size_t memorySize; /* number of bytes stored in RAM */ +}; + /*----------------------------------------------------------------------------------------------*/ iDeclareType(History) @@ -78,4 +85,4 @@ const iGmResponse * size_t cacheSize_History (const iHistory *); iString * debugInfo_History (const iHistory *); - +iMemInfo memoryUsage_History (const iHistory *); diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 5f86f5bf..6719fb40 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c @@ -658,12 +658,12 @@ void setCursor_InputWidget(iInputWidget *d, size_t pos) { } static size_t indexForRelativeX_InputWidget_(const iInputWidget *d, int x, const iInputLine *line) { + size_t index = line->offset; if (x <= 0) { - return line->offset; + return index; } const char *endPos; tryAdvanceNoWrap_Text(d->font, range_String(&line->text), x, &endPos); - size_t index = line->offset; if (endPos == constEnd_String(&line->text)) { index += length_String(&line->text); } @@ -691,10 +691,10 @@ static iBool moveCursorByLine_InputWidget_(iInputWidget *d, int dir) { } if (newCursor != iInvalidPos) { /* Clamp it to the current line. */ - newCursor = iMax(newCursor, line->offset); newCursor = iMin(newCursor, line->offset + length_String(&line->text) - /* last line is allowed to go to the cursorMax */ ((const void *) line < constAt_Array(&d->lines, numLines - 1) ? 1 : 0)); + newCursor = iMax(newCursor, line->offset); setCursor_InputWidget(d, newCursor); return iTrue; } @@ -1311,7 +1311,7 @@ static void draw_InputWidget_(const iInputWidget *d) { .x; fillRect_Paint(&p, (iRect){ addX_I2(drawPos, iMin(m1, m2)), - init_I2(iAbs(m2 - m1), lineHeight_Text(d->font)) }, + init_I2(iMax(gap_UI / 3, iAbs(m2 - m1)), lineHeight_Text(d->font)) }, uiMarked_ColorId); } } diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index cb32a29c..70553488 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h @@ -41,20 +41,20 @@ struct Impl_InputWidgetContentPadding { typedef void (*iInputWidgetValidatorFunc)(iInputWidget *, void *context); -void setHint_InputWidget (iInputWidget *, const char *hintText); -void setMode_InputWidget (iInputWidget *, enum iInputMode mode); -void setMaxLen_InputWidget (iInputWidget *, size_t maxLen); -void setText_InputWidget (iInputWidget *, const iString *text); -void setTextCStr_InputWidget (iInputWidget *, const char *cstr); -void setFont_InputWidget (iInputWidget *, int fontId); -void setCursor_InputWidget (iInputWidget *, size_t pos); +void setHint_InputWidget (iInputWidget *, const char *hintText); +void setMode_InputWidget (iInputWidget *, enum iInputMode mode); +void setMaxLen_InputWidget (iInputWidget *, size_t maxLen); +void setText_InputWidget (iInputWidget *, const iString *text); +void setTextCStr_InputWidget (iInputWidget *, const char *cstr); +void setFont_InputWidget (iInputWidget *, int fontId); +void setCursor_InputWidget (iInputWidget *, size_t pos); void setContentPadding_InputWidget (iInputWidget *, int left, int right); /* only affects the text entry */ void setMaxLayoutLines_InputWidget (iInputWidget *, size_t maxLayoutLines); void setValidator_InputWidget (iInputWidget *, iInputWidgetValidatorFunc validator, void *context); void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); -void begin_InputWidget (iInputWidget *); -void end_InputWidget (iInputWidget *, iBool accept); -void selectAll_InputWidget (iInputWidget *); +void begin_InputWidget (iInputWidget *); +void end_InputWidget (iInputWidget *, iBool accept); +void selectAll_InputWidget (iInputWidget *); void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); void setSensitiveContent_InputWidget (iInputWidget *, iBool isSensitive); @@ -62,9 +62,8 @@ void setUrlContent_InputWidget (iInputWidget *, iBool isUrl); void setNotifyEdits_InputWidget (iInputWidget *, iBool notifyEdits); void setEatEscape_InputWidget (iInputWidget *, iBool eatEscape); -const iString * text_InputWidget (const iInputWidget *); -iInputWidgetContentPadding - contentPadding_InputWidget (const iInputWidget *); +iInputWidgetContentPadding contentPadding_InputWidget (const iInputWidget *); +const iString * text_InputWidget (const iInputWidget *); iLocalDef iInputWidget *newHint_InputWidget(size_t maxLen, const char *hint) { iInputWidget *d = new_InputWidget(maxLen); -- cgit v1.2.3 From b7f40b587087ce4d594ef10af509a5ab92f20466 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 10 Jun 2021 12:36:42 +0300 Subject: Preferences: Memory size limit Memory used for RAM storage of media along with navigation history so it can be restored instantly. --- po/en.po | 3 +++ res/lang/de.bin | Bin 20768 -> 20798 bytes res/lang/en.bin | Bin 19473 -> 19503 bytes res/lang/es.bin | Bin 21847 -> 21877 bytes res/lang/fi.bin | Bin 21970 -> 22000 bytes res/lang/fr.bin | Bin 22380 -> 22410 bytes res/lang/ia.bin | Bin 21678 -> 21708 bytes res/lang/ie.bin | Bin 21236 -> 21266 bytes res/lang/pl.bin | Bin 22484 -> 22514 bytes res/lang/ru.bin | Bin 32715 -> 32745 bytes res/lang/sr.bin | Bin 32120 -> 32150 bytes res/lang/tok.bin | Bin 19861 -> 19891 bytes res/lang/zh_Hans.bin | Bin 18637 -> 18667 bytes res/lang/zh_Hant.bin | Bin 18822 -> 18852 bytes src/app.c | 41 ++++++++++++++++++++++++++++++++++++++++- src/app.h | 1 + src/history.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/history.h | 7 +++++-- src/prefs.c | 1 + src/prefs.h | 1 + src/ui/root.c | 1 + src/ui/util.c | 11 +++++++++++ 22 files changed, 110 insertions(+), 4 deletions(-) (limited to 'src/history.h') diff --git a/po/en.po b/po/en.po index bd7e97bf..ae1392e8 100644 --- a/po/en.po +++ b/po/en.po @@ -1264,6 +1264,9 @@ msgstr "Decode URLs:" msgid "prefs.cachesize" msgstr "Cache size:" +msgid "prefs.memorysize" +msgstr "Memory size:" + msgid "prefs.ca.file" msgstr "CA file:" diff --git a/res/lang/de.bin b/res/lang/de.bin index 95d172cc..ece02848 100644 Binary files a/res/lang/de.bin and b/res/lang/de.bin differ diff --git a/res/lang/en.bin b/res/lang/en.bin index d5cb1c2c..06781bb4 100644 Binary files a/res/lang/en.bin and b/res/lang/en.bin differ diff --git a/res/lang/es.bin b/res/lang/es.bin index 4b567393..b47e6fae 100644 Binary files a/res/lang/es.bin and b/res/lang/es.bin differ diff --git a/res/lang/fi.bin b/res/lang/fi.bin index d1ab756b..95b2108c 100644 Binary files a/res/lang/fi.bin and b/res/lang/fi.bin differ diff --git a/res/lang/fr.bin b/res/lang/fr.bin index 5561640c..365a6d8e 100644 Binary files a/res/lang/fr.bin and b/res/lang/fr.bin differ diff --git a/res/lang/ia.bin b/res/lang/ia.bin index da272056..b037a7ae 100644 Binary files a/res/lang/ia.bin and b/res/lang/ia.bin differ diff --git a/res/lang/ie.bin b/res/lang/ie.bin index 3d77d90e..6c70c65a 100644 Binary files a/res/lang/ie.bin and b/res/lang/ie.bin differ diff --git a/res/lang/pl.bin b/res/lang/pl.bin index c09c6bef..b902baad 100644 Binary files a/res/lang/pl.bin and b/res/lang/pl.bin differ diff --git a/res/lang/ru.bin b/res/lang/ru.bin index e344e992..74e2e20f 100644 Binary files a/res/lang/ru.bin and b/res/lang/ru.bin differ diff --git a/res/lang/sr.bin b/res/lang/sr.bin index 6a860e79..576b4b9c 100644 Binary files a/res/lang/sr.bin and b/res/lang/sr.bin differ diff --git a/res/lang/tok.bin b/res/lang/tok.bin index 27bfdb49..97ef6273 100644 Binary files a/res/lang/tok.bin and b/res/lang/tok.bin differ diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin index 077ba1e3..bb2c6571 100644 Binary files a/res/lang/zh_Hans.bin and b/res/lang/zh_Hans.bin differ diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin index 4d9a984b..36fc5eb2 100644 Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin differ diff --git a/src/app.c b/src/app.c index 1ea33dd4..e2e16252 100644 --- a/src/app.c +++ b/src/app.c @@ -210,6 +210,7 @@ static iString *serializePrefs_App_(const iApp *d) { appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling); appendFormat_String(str, "imageloadscroll arg:%d\n", d->prefs.loadImageInsteadOfScrolling); appendFormat_String(str, "cachesize.set arg:%d\n", d->prefs.maxCacheSize); + appendFormat_String(str, "memorysize.set arg:%d\n", d->prefs.maxMemorySize); appendFormat_String(str, "decodeurls arg:%d\n", d->prefs.decodeUserVisibleURLs); appendFormat_String(str, "linewidth.set arg:%d\n", d->prefs.lineWidth); /* TODO: Set up an array of booleans in Prefs and do these in a loop. */ @@ -991,6 +992,33 @@ void trimCache_App(void) { iRelease(docs); } +void trimMemory_App(void) { + iApp *d = &app_; + size_t memorySize = 0; + const size_t limit = d->prefs.maxMemorySize * 1000000; + iObjectList *docs = listDocuments_App(NULL); + iForEach(ObjectList, i, docs) { + memorySize += memorySize_History(history_DocumentWidget(i.object)); + } + init_ObjectListIterator(&i, docs); + iBool wasPruned = iFalse; + while (memorySize > limit) { + iDocumentWidget *doc = i.object; + const size_t pruned = pruneLeastImportantMemory_History(history_DocumentWidget(doc)); + if (pruned) { + memorySize -= pruned; + wasPruned = iTrue; + } + next_ObjectListIterator(&i); + if (!i.value) { + if (!wasPruned) break; + wasPruned = iFalse; + init_ObjectListIterator(&i, docs); + } + } + iRelease(docs); +} + iLocalDef iBool isWaitingAllowed_App_(iApp *d) { if (!isEmpty_Periodic(&d->periodic)) { return iFalse; @@ -1577,7 +1605,9 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { postCommandf_App("searchurl address:%s", cstrText_InputWidget(findChild_Widget(d, "prefs.searchurl"))); postCommandf_App("cachesize.set arg:%d", - toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); + toInt_String(text_InputWidget(findChild_Widget(d, "prefs.cachesize")))); + postCommandf_App("memorysize.set arg:%d", + toInt_String(text_InputWidget(findChild_Widget(d, "prefs.memorysize")))); postCommandf_App("ca.file path:%s", cstrText_InputWidget(findChild_Widget(d, "prefs.ca.file"))); postCommandf_App("ca.path path:%s", @@ -2139,6 +2169,13 @@ iBool handleCommand_App(const char *cmd) { } return iTrue; } + else if (equal_Command(cmd, "memorysize.set")) { + d->prefs.maxMemorySize = arg_Command(cmd); + if (d->prefs.maxMemorySize <= 0) { + d->prefs.maxMemorySize = 0; + } + return iTrue; + } else if (equal_Command(cmd, "searchurl")) { iString *url = &d->prefs.searchUrl; setCStr_String(url, suffixPtr_Command(cmd, "address")); @@ -2417,6 +2454,8 @@ iBool handleCommand_App(const char *cmd) { iTrue); setText_InputWidget(findChild_Widget(dlg, "prefs.cachesize"), collectNewFormat_String("%d", d->prefs.maxCacheSize)); + setText_InputWidget(findChild_Widget(dlg, "prefs.memorysize"), + collectNewFormat_String("%d", d->prefs.maxMemorySize)); setToggle_Widget(findChild_Widget(dlg, "prefs.decodeurls"), d->prefs.decodeUserVisibleURLs); setText_InputWidget(findChild_Widget(dlg, "prefs.searchurl"), &d->prefs.searchUrl); setText_InputWidget(findChild_Widget(dlg, "prefs.ca.file"), &d->prefs.caFile); diff --git a/src/app.h b/src/app.h index 918cd396..2dd5198d 100644 --- a/src/app.h +++ b/src/app.h @@ -96,6 +96,7 @@ iObjectList * listDocuments_App (const iRoot *rootOrNull); /* NULL for a iStringSet * listOpenURLs_App (void); /* all tabs */ iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew); void trimCache_App (void); +void trimMemory_App (void); iDocumentWidget * document_Root (iRoot *); diff --git a/src/history.c b/src/history.c index fdd0ff55..740830e9 100644 --- a/src/history.c +++ b/src/history.c @@ -366,6 +366,17 @@ size_t cacheSize_History(const iHistory *d) { return cached; } +size_t memorySize_History(const iHistory *d) { + size_t bytes = 0; + lock_Mutex(d->mtx); + iConstForEach(Array, i, &d->recent) { + const iRecentUrl *url = i.value; + bytes += memorySize_RecentUrl(url); + } + unlock_Mutex(d->mtx); + return bytes; +} + void clearCache_History(iHistory *d) { lock_Mutex(d->mtx); iForEach(Array, i, &d->recent) { @@ -374,6 +385,7 @@ void clearCache_History(iHistory *d) { delete_GmResponse(url->cachedResponse); url->cachedResponse = NULL; } + iReleasePtr(&url->cachedDoc); /* release all cached documents and media as well */ } unlock_Mutex(d->mtx); } @@ -387,7 +399,7 @@ size_t pruneLeastImportant_History(iHistory *d) { lock_Mutex(d->mtx); iConstForEach(Array, i, &d->recent) { const iRecentUrl *url = i.value; - if (url->cachedResponse || url->cachedDoc) { + if (url->cachedResponse) { const double urlScore = cacheSize_RecentUrl(url) * pow(secondsSince_Time(&now, &url->cachedResponse->when) / 60.0, 1.25); @@ -408,6 +420,40 @@ size_t pruneLeastImportant_History(iHistory *d) { return delta; } +size_t pruneLeastImportantMemory_History(iHistory *d) { + size_t delta = 0; + size_t chosen = iInvalidPos; + double score = 0.0f; + iTime now; + initCurrent_Time(&now); + lock_Mutex(d->mtx); + iConstForEach(Array, i, &d->recent) { + const iRecentUrl *url = i.value; + if (d->recentPos == size_Array(&d->recent) - index_ArrayConstIterator(&i) - 1) { + continue; /* Not the current navigation position. */ + } + if (url->cachedDoc) { + const double urlScore = + memorySize_RecentUrl(url) * + (url->cachedResponse + ? pow(secondsSince_Time(&now, &url->cachedResponse->when) / 60.0, 1.25) + : 1.0); + if (urlScore > score) { + chosen = index_ArrayConstIterator(&i); + score = urlScore; + } + } + } + if (chosen != iInvalidPos) { + iRecentUrl *url = at_Array(&d->recent, chosen); + const size_t before = memorySize_RecentUrl(url); + iReleasePtr(&url->cachedDoc); + delta = before - memorySize_RecentUrl(url); + } + unlock_Mutex(d->mtx); + return delta; +} + const iStringArray *searchContents_History(const iHistory *d, const iRegExp *pattern) { iStringArray *urls = iClob(new_StringArray()); lock_Mutex(d->mtx); diff --git a/src/history.h b/src/history.h index ccc19d27..eb35b1df 100644 --- a/src/history.h +++ b/src/history.h @@ -66,8 +66,10 @@ iBool goForward_History (iHistory *); iRecentUrl *recentUrl_History (iHistory *, size_t pos); iRecentUrl *mostRecentUrl_History (iHistory *); iRecentUrl *findUrl_History (iHistory *, const iString *url); -void clearCache_History (iHistory *); -size_t pruneLeastImportant_History (iHistory *); + +void clearCache_History (iHistory *); +size_t pruneLeastImportant_History (iHistory *); +size_t pruneLeastImportantMemory_History (iHistory *); iBool atLatest_History (const iHistory *); iBool atOldest_History (const iHistory *); @@ -83,6 +85,7 @@ const iRecentUrl * const iGmResponse * cachedResponse_History (const iHistory *); size_t cacheSize_History (const iHistory *); +size_t memorySize_History (const iHistory *); iString * debugInfo_History (const iHistory *); iMemInfo memoryUsage_History (const iHistory *); diff --git a/src/prefs.c b/src/prefs.c index 385dee78..f1842e9a 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -46,6 +46,7 @@ void init_Prefs(iPrefs *d) { d->openArchiveIndexPages = iTrue; d->decodeUserVisibleURLs = iTrue; d->maxCacheSize = 10; + d->maxMemorySize = 200; d->font = nunito_TextFont; d->headingFont = nunito_TextFont; d->monospaceGemini = iFalse; diff --git a/src/prefs.h b/src/prefs.h index 7185c8f9..655ec949 100644 --- a/src/prefs.h +++ b/src/prefs.h @@ -66,6 +66,7 @@ struct Impl_Prefs { iString caPath; iBool decodeUserVisibleURLs; int maxCacheSize; /* MB */ + int maxMemorySize; /* MB */ iString geminiProxy; iString gopherProxy; iString httpProxy; diff --git a/src/ui/root.c b/src/ui/root.c index 23a7d670..52bc7364 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -698,6 +698,7 @@ static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { iInputWidget *url = findWidget_Root("url"); const iString *urlStr = collect_String(suffix_Command(cmd, "url")); trimCache_App(); + trimMemory_App(); visitUrl_Visited(visited_App(), withSpacesEncoded_String(urlStr), 0); /* TODO: internal URI normalization */ postCommand_App("visited.changed"); /* sidebar will update */ setText_InputWidget(url, urlStr); diff --git a/src/ui/util.c b/src/ui/util.c index 6eef544b..6c6e62a5 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -1834,6 +1834,17 @@ iWidget *makePreferences_Widget(void) { resizeToParentHeight_WidgetFlag); setContentPadding_InputWidget(cache, 0, width_Widget(unit) - 4 * gap_UI); } + /* Memory size. */ { + iInputWidget *mem = new_InputWidget(4); + setSelectAllOnFocus_InputWidget(mem, iTrue); + addPrefsInputWithHeading_(headings, values, "prefs.memorysize", iClob(mem)); + iWidget *unit = + addChildFlags_Widget(as_Widget(mem), + iClob(new_LabelWidget("${mb}", NULL)), + frameless_WidgetFlag | moveToParentRightEdge_WidgetFlag | + resizeToParentHeight_WidgetFlag); + setContentPadding_InputWidget(mem, 0, width_Widget(unit) - 4 * gap_UI); + } makeTwoColumnHeading_("${heading.prefs.certs}", headings, values); addPrefsInputWithHeading_(headings, values, "prefs.ca.file", iClob(new_InputWidget(0))); addPrefsInputWithHeading_(headings, values, "prefs.ca.path", iClob(new_InputWidget(0))); -- cgit v1.2.3 From eb04ac7a1921b7c7f534a7a63cffc4c7a35c0900 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 13 Jun 2021 15:28:58 +0300 Subject: Mobile: Working on swipe navigation Swipes back and forward are now working, although there are some glitches remaining. Most notably, when swiping back the previous document does not appear until the finger is released. --- src/history.c | 30 ++++++++++ src/history.h | 2 + src/ui/documentwidget.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++- src/ui/mobile.c | 2 +- src/ui/root.c | 13 +++-- src/ui/touch.c | 6 +- src/ui/widget.c | 15 ++++- src/ui/widget.h | 2 + 8 files changed, 202 insertions(+), 12 deletions(-) (limited to 'src/history.h') diff --git a/src/history.c b/src/history.c index 740830e9..f6ad4771 100644 --- a/src/history.c +++ b/src/history.c @@ -284,6 +284,36 @@ void add_History(iHistory *d, const iString *url ){ unlock_Mutex(d->mtx); } +iBool preceding_History(iHistory *d, iRecentUrl *recent_out) { + 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, d->recentPos + 1); + set_String(&recent_out->url, &recent->url); + recent_out->normScrollY = recent->normScrollY; + recent_out->cachedDoc = ref_Object(recent->cachedDoc); + /* Cached response is not returned, would involve a deep copy. */ + ok = iTrue; + } + unlock_Mutex(d->mtx); + return ok; +} + +iBool following_History(iHistory *d, iRecentUrl *recent_out) { + iBool ok = iFalse; + lock_Mutex(d->mtx); + if (!isEmpty_Array(&d->recent) && d->recentPos > 0) { + const iRecentUrl *recent = constAt_Array(&d->recent, d->recentPos - 1); + set_String(&recent_out->url, &recent->url); + recent_out->normScrollY = recent->normScrollY; + recent_out->cachedDoc = ref_Object(recent->cachedDoc); + /* Cached response is not returned, would involve a deep copy. */ + ok = iTrue; + } + unlock_Mutex(d->mtx); + return ok; +} + iBool goBack_History(iHistory *d) { lock_Mutex(d->mtx); if (!isEmpty_Array(&d->recent) && d->recentPos < size_Array(&d->recent) - 1) { diff --git a/src/history.h b/src/history.h index eb35b1df..9c900b82 100644 --- a/src/history.h +++ b/src/history.h @@ -63,6 +63,8 @@ void setCachedResponse_History (iHistory *, const iGmResponse *response void setCachedDocument_History (iHistory *, iGmDocument *doc); 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 *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 936c3cb7..70201d7b 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -315,6 +315,10 @@ void init_DocumentWidget(iDocumentWidget *d) { init_Widget(w); setId_Widget(w, format_CStr("document%03d", ++docEnum_)); setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue); + if (deviceType_App() != desktop_AppDeviceType) { + setFlags_Widget(w, leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag | + horizontalOffset_WidgetFlag, iTrue); + } init_PersistentDocumentState(&d->mod); d->flags = 0; d->phoneToolbar = NULL; @@ -977,6 +981,9 @@ static void updateTimestampBuf_DocumentWidget_(const iDocumentWidget *d) { } static void invalidate_DocumentWidget_(iDocumentWidget *d) { + if (flags_Widget(as_Widget(d)) & destroyPending_WidgetFlag) { + return; + } invalidate_VisBuf(d->visBuf); clear_PtrSet(d->invalidRuns); } @@ -2237,6 +2244,133 @@ static iBool handlePinch_DocumentWidget_(iDocumentWidget *d, const char *cmd) { return iTrue; } +static void swap_DocumentWidget_(iDocumentWidget *d, iGmDocument *doc, + iDocumentWidget *swapBuffersWith) { + if (doc) { + iAssert(isInstance_Object(doc, &Class_GmDocument)); + iGmDocument *copy = ref_Object(doc); + iRelease(d->doc); + d->doc = copy; + d->scrollY = swapBuffersWith->scrollY; + updateVisible_DocumentWidget_(d); + iSwap(iVisBuf *, d->visBuf, swapBuffersWith->visBuf); + iSwap(iVisBufMeta *, d->visBufMeta, swapBuffersWith->visBufMeta); + iSwap(iDrawBufs *, d->drawBufs, swapBuffersWith->drawBufs); + invalidate_DocumentWidget_(swapBuffersWith); + } +} + +static iWidget *swipeParent_DocumentWidget_(iDocumentWidget *d) { + return findChild_Widget(as_Widget(d)->root->widget, "doctabs"); +} + +static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { + iWidget *w = as_Widget(d); + if (equal_Command(cmd, "edgeswipe.moved")) { + as_Widget(d)->offsetRef = NULL; + const int side = argLabel_Command(cmd, "side"); + const int offset = arg_Command(cmd); + if (side == 1) { + if (atOldest_History(d->mod.history)) { + return iTrue; + } + iWidget *swipeParent = swipeParent_DocumentWidget_(d); + /* The temporary "swipeIn" will display the previous page until the finger is lifted. */ + iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); + if (!swipeIn) { + swipeIn = new_DocumentWidget(); + setId_Widget(as_Widget(swipeIn), "swipein"); + setFlags_Widget(as_Widget(swipeIn), + disabled_WidgetFlag | refChildrenOffset_WidgetFlag | + fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); + swipeIn->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); + swipeIn->widget.rect.size = d->widget.rect.size; + swipeIn->widget.offsetRef = w; + iRecentUrl *recent = new_RecentUrl(); + preceding_History(d->mod.history, recent); + if (recent->cachedDoc) { + iChangeRef(swipeIn->doc, recent->cachedDoc); + updateScrollMax_DocumentWidget_(d); + setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0); + updateVisible_DocumentWidget_(swipeIn); + } + delete_RecentUrl(recent); + addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos); + } + } + if (side == 2) { + if (offset < -get_Window()->pixelRatio * 10) { + if (!atLatest_History(d->mod.history) && + ~flags_Widget(w) & dragged_WidgetFlag) { + postCommand_Widget(d, "navigate.forward"); + setFlags_Widget(w, dragged_WidgetFlag, iTrue); + /* Set up the swipe dummy. */ + iWidget *swipeParent = swipeParent_DocumentWidget_(d); + iDocumentWidget *target = new_DocumentWidget(); + setId_Widget(as_Widget(target), "swipeout"); + /* The target takes the old document and jumps on top. */ + target->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); + target->widget.rect.size = d->widget.rect.size; + setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); + swap_DocumentWidget_(target, d->doc, d); + addChildPos_Widget(swipeParent, iClob(target), front_WidgetAddPos); + 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 */ + } + if (flags_Widget(w) & dragged_WidgetFlag) { + setVisualOffset_Widget(w, width_Widget(w) + offset, 10, 0); + } + else { + setVisualOffset_Widget(w, offset / 4, 10, 0); + } + } + return iTrue; + } + } + if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 2) { + if (argLabel_Command(cmd, "abort") && flags_Widget(w) & dragged_WidgetFlag) { + postCommand_Widget(d, "navigate.back"); + } + setFlags_Widget(w, dragged_WidgetFlag, iFalse); + setVisualOffset_Widget(w, 0, 100, 0); + return iTrue; + } + if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 1) { + iWidget *swipeParent = swipeParent_DocumentWidget_(d); + iWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); + if (swipeIn) { + swipeIn->offsetRef = NULL; + destroy_Widget(swipeIn); + } + } + if (equal_Command(cmd, "swipe.back")) { + if (atOldest_History(d->mod.history)) { + setVisualOffset_Widget(w, 0, 100, 0); + return iTrue; + } + iWidget *swipeParent = swipeParent_DocumentWidget_(d); + iDocumentWidget *target = new_DocumentWidget(); + setId_Widget(as_Widget(target), "swipeout"); + /* The target takes the old document and jumps on top. */ + target->widget.rect.pos = windowToInner_Widget(swipeParent, innerToWindow_Widget(w, zero_I2())); + /* Note: `innerToWindow_Widget` does not apply visual offset. */ + target->widget.rect.size = w->rect.size; + setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); + swap_DocumentWidget_(target, d->doc, d); + addChildPos_Widget(swipeParent, iClob(target), back_WidgetAddPos); + setFlags_Widget(as_Widget(d), refChildrenOffset_WidgetFlag, iTrue); + as_Widget(d)->offsetRef = swipeParent; + setVisualOffset_Widget(as_Widget(target), value_Anim(&w->visualOffset), 0, 0); + setVisualOffset_Widget(as_Widget(target), width_Widget(target), 150, 0); + destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ + setVisualOffset_Widget(w, 0, 0, 0); + postCommand_Widget(d, "navigate.back"); + return iTrue; + } + return iFalse; +} + static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { iWidget *w = as_Widget(d); if (equal_Command(cmd, "document.openurls.changed")) { @@ -2842,7 +2976,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) uiHeading_ColorEscape "${heading.import.bookmarks}", formatCStrs_Lang("dlg.import.found.n", count), (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, - { format_CStr(cstrCount_Lang("dlg.import.add.n", count), + { format_CStr(cstrCount_Lang("dlg.import.add.n", (int) count), uiTextAction_ColorEscape, count), 0, @@ -2901,6 +3035,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) else if (startsWith_CStr(cmd, "pinch.") && document_Command(cmd) == d) { return handlePinch_DocumentWidget_(d, cmd); } + else if ((startsWith_CStr(cmd, "edgeswipe.") || startsWith_CStr(cmd, "swipe.")) && + document_App() == d) { + return handleSwipe_DocumentWidget_(d, cmd); + } return iFalse; } @@ -4306,7 +4444,8 @@ static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx, /* Swap buffers around to have room available both before and after the visible region. */ allocVisBuffer_DocumentWidget_(d); reposition_VisBuf(visBuf, vis); - /* Redraw the invalid ranges. */ { + /* Redraw the invalid ranges. */ + if (~flags_Widget(constAs_Widget(d)) & destroyPending_WidgetFlag) { iPaint *p = &ctx->paint; init_Paint(p); iForIndices(i, visBuf->buffers) { @@ -4490,6 +4629,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { .vis = vis, .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, }; + init_Paint(&ctx.paint); render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); setClip_Paint(&ctx.paint, bounds); int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 1bf289a7..7c0c8b60 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c @@ -447,7 +447,7 @@ void finalizeSheet_Mobile(iWidget *sheet) { } addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); /* Slide top panel with detail panels. */ { - setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue); + setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue); topPanel->offsetRef = detailStack; } if (prefsTabs) { diff --git a/src/ui/root.c b/src/ui/root.c index 2d69970d..e279a048 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -271,7 +271,8 @@ void destroyPending_Root(iRoot *d) { setCurrent_Root(d); iForEach(PtrSet, i, d->pendingDestruction) { iWidget *widget = *i.value; - if (!isFinished_Anim(&widget->visualOffset)) { + if (!isFinished_Anim(&widget->visualOffset) || + isBeingVisuallyOffsetByReference_Widget(widget)) { continue; } if (widget->flags & keepOnTop_WidgetFlag) { @@ -1169,11 +1170,11 @@ void createUserInterface_Root(iRoot *d) { setId_Widget(mainStack, "stack"); addChildFlags_Widget(div, iClob(mainStack), resizeChildren_WidgetFlag | expand_WidgetFlag | unhittable_WidgetFlag); - iWidget *tabBar = makeTabs_Widget(mainStack); - setId_Widget(tabBar, "doctabs"); - setBackgroundColor_Widget(tabBar, uiBackground_ColorId); - appendTabPage_Widget(tabBar, iClob(new_DocumentWidget()), "Document", 0, 0); - iWidget *buttons = findChild_Widget(tabBar, "tabs.buttons"); + iWidget *docTabs = makeTabs_Widget(mainStack); + setId_Widget(docTabs, "doctabs"); + setBackgroundColor_Widget(docTabs, uiBackground_ColorId); + appendTabPage_Widget(docTabs, iClob(new_DocumentWidget()), "Document", 0, 0); + iWidget *buttons = findChild_Widget(docTabs, "tabs.buttons"); setFlags_Widget(buttons, collapse_WidgetFlag | hidden_WidgetFlag | drawBackgroundToHorizontalSafeArea_WidgetFlag, iTrue); if (deviceType_App() == phone_AppDeviceType) { diff --git a/src/ui/touch.c b/src/ui/touch.c index 236601e8..36bac267 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c @@ -273,8 +273,9 @@ static void update_TouchState_(void *ptr) { // touch->edge == right_TouchEdge && swipeDir < 0 ? SDL_BUTTON_X2 : 0); // setHover_Widget(NULL); postCommandf_App("edgeswipe.ended abort:1 side:%d id:%llu", touch->edge, touch->id); - touch->edge = none_TouchEdge; - remove_ArrayIterator(&i); + touch->edge = none_TouchEdge; + /* May be a regular drag along the edge so don't remove. */ + //remove_ArrayIterator(&i); } continue; } @@ -516,6 +517,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { else if (ev->type == SDL_FINGERMOTION) { iTouch *touch = find_TouchState_(d, fing->fingerId); if (touch && touch->edge) { + clear_Array(d->moms); pushPos_Touch_(touch, pos, nowTime); postCommandf_App("edgeswipe.moved arg:%d side:%d id:%llu", (int) (x_F3(pos) - x_F3(touch->startPos)), diff --git a/src/ui/widget.c b/src/ui/widget.c index f1d1dae4..3b67684b 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -759,6 +759,19 @@ void arrange_Widget(iWidget *d) { } } +iBool isBeingVisuallyOffsetByReference_Widget(const iWidget *d) { + if (d->flags & refChildrenOffset_WidgetFlag) { + iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) { + const iWidget *child = i.object; + if (child == d) continue; + if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { + return iTrue; + } + } + } + return iFalse; +} + static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { const int off = iRound(value_Anim(&d->visualOffset)); @@ -1075,12 +1088,12 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { if (d->flags & dragged_WidgetFlag && equal_Command(cmd, "edgeswipe.ended")) { if (argLabel_Command(cmd, "abort")) { setVisualOffset_Widget(d, 0, 200, easeOut_AnimFlag); - setFlags_Widget(d, dragged_WidgetFlag, iFalse); } else { postCommand_Widget( d, argLabel_Command(cmd, "side") == 1 ? "swipe.back" : "swipe.forward"); } + setFlags_Widget(d, dragged_WidgetFlag, iFalse); } if (d->commandHandler && d->commandHandler(d, ev->user.data1)) { iAssert(get_Root() == d->root); diff --git a/src/ui/widget.h b/src/ui/widget.h index 7255b975..b2310f21 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -244,6 +244,8 @@ iBool isCommand_Widget (const iWidget *d, const SDL_Event *ev, cons iBool hasParent_Widget (const iWidget *d, const iWidget *someParent); iBool isAffectedByVisualOffset_Widget (const iWidget *); +iBool isBeingVisuallyOffsetByReference_Widget + (const iWidget *); void setId_Widget (iWidget *, const char *id); void setFlags_Widget (iWidget *, int64_t flags, iBool set); void setPos_Widget (iWidget *, iInt2 pos); -- cgit v1.2.3 From 203426a88b2d94ed56f7d3f8f8eb18a7457cab11 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 13 Jun 2021 17:29:35 +0300 Subject: Mobile: Swipe navigation Swipes are working much better. Todo: - Pinch must always cancel/override edge swipes. - Use the right palette for the "swipein" placeholder. --- src/history.c | 7 +++++-- src/history.h | 2 +- src/ui/documentwidget.c | 17 ++++++++++++----- src/ui/root.c | 2 +- src/ui/widget.c | 8 ++++---- 5 files changed, 23 insertions(+), 13 deletions(-) (limited to 'src/history.h') diff --git a/src/history.c b/src/history.c index f6ad4771..d23fb290 100644 --- a/src/history.c +++ b/src/history.c @@ -288,10 +288,11 @@ iBool preceding_History(iHistory *d, iRecentUrl *recent_out) { 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, d->recentPos + 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; - recent_out->cachedDoc = ref_Object(recent->cachedDoc); + iChangeRef(recent_out->cachedDoc, recent->cachedDoc); /* Cached response is not returned, would involve a deep copy. */ ok = iTrue; } @@ -299,6 +300,7 @@ iBool preceding_History(iHistory *d, iRecentUrl *recent_out) { return ok; } +#if 0 iBool following_History(iHistory *d, iRecentUrl *recent_out) { iBool ok = iFalse; lock_Mutex(d->mtx); @@ -313,6 +315,7 @@ iBool following_History(iHistory *d, iRecentUrl *recent_out) { unlock_Mutex(d->mtx); return ok; } +#endif iBool goBack_History(iHistory *d) { lock_Mutex(d->mtx); diff --git a/src/history.h b/src/history.h index 9c900b82..838ca9ef 100644 --- a/src/history.h +++ b/src/history.h @@ -64,7 +64,7 @@ void setCachedDocument_History (iHistory *, iGmDocument *doc); iBool goBack_History (iHistory *); iBool goForward_History (iHistory *); iBool preceding_History (iHistory *d, iRecentUrl *recent_out); -iBool following_History (iHistory *d, iRecentUrl *recent_out); +//iBool following_History (iHistory *d, iRecentUrl *recent_out); 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 70201d7b..eae5d713 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -2266,11 +2266,15 @@ static iWidget *swipeParent_DocumentWidget_(iDocumentWidget *d) { 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 + and temporary DocumentWidgets. Depending on the swipe direction, this DocumentWidget + may wait until the finger is released to actually perform the navigation action. */ if (equal_Command(cmd, "edgeswipe.moved")) { + //printf("[%p] responds to edgeswipe.moved\n", d); as_Widget(d)->offsetRef = NULL; const int side = argLabel_Command(cmd, "side"); const int offset = arg_Command(cmd); - if (side == 1) { + if (side == 1) { /* left edge */ if (atOldest_History(d->mod.history)) { return iTrue; } @@ -2285,7 +2289,7 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue); swipeIn->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); swipeIn->widget.rect.size = d->widget.rect.size; - swipeIn->widget.offsetRef = w; + swipeIn->widget.offsetRef = parent_Widget(w); iRecentUrl *recent = new_RecentUrl(); preceding_History(d->mod.history, recent); if (recent->cachedDoc) { @@ -2293,15 +2297,18 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { updateScrollMax_DocumentWidget_(d); setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0); updateVisible_DocumentWidget_(swipeIn); + swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; } delete_RecentUrl(recent); addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos); } } - if (side == 2) { + 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) { + animSpan = 0; postCommand_Widget(d, "navigate.forward"); setFlags_Widget(w, dragged_WidgetFlag, iTrue); /* Set up the swipe dummy. */ @@ -2319,10 +2326,10 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ } if (flags_Widget(w) & dragged_WidgetFlag) { - setVisualOffset_Widget(w, width_Widget(w) + offset, 10, 0); + setVisualOffset_Widget(w, width_Widget(w) + offset, animSpan, 0); } else { - setVisualOffset_Widget(w, offset / 4, 10, 0); + setVisualOffset_Widget(w, offset / 4, animSpan, 0); } } return iTrue; diff --git a/src/ui/root.c b/src/ui/root.c index e279a048..0a5eb96e 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -281,7 +281,7 @@ void destroyPending_Root(iRoot *d) { if (widget->parent) { removeChild_Widget(widget->parent, widget); } - iAssert(widget->parent == NULL); + iAssert(widget->parent == NULL); iRelease(widget); remove_PtrSetIterator(&i); } diff --git a/src/ui/widget.c b/src/ui/widget.c index 3b67684b..11ec1b07 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -82,10 +82,10 @@ static void visualOffsetAnimation_Widget_(void *ptr) { void deinit_Widget(iWidget *d) { releaseChildren_Widget(d); -//#if !defined (NDEBUG) -// printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id), -// d->flags & keepOnTop_WidgetFlag ? 1 : 0); -//#endif +#if 0 && !defined (NDEBUG) + printf("widget %p (%s) deleted (on top:%d)\n", d, cstr_String(&d->id), + d->flags & keepOnTop_WidgetFlag ? 1 : 0); +#endif deinit_String(&d->id); if (d->flags & keepOnTop_WidgetFlag) { removeAll_PtrArray(onTop_Root(d->root), d); -- cgit v1.2.3 From fe238b29132b43f07aa88cf6bb60bec1ff0d3f8d Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Mon, 14 Jun 2021 15:36:48 +0300 Subject: Swiping and sidebar; various tweaks When a page is opened from the sidebar, swiping back will now reopen the sidebar. Another swipe will dismiss the sidebar and navigate back as usual. Attempted to cache theme colors in GmDocument, but there were issues with theme changes. --- src/app.c | 7 +++- src/defs.h | 3 +- src/gmdocument.c | 18 +++++++++ src/gmdocument.h | 2 + src/gmutil.c | 4 +- src/history.c | 31 +++++++++++++-- src/history.h | 6 ++- src/ui/documentwidget.c | 103 +++++++++++++++++++++++++++++++++++++----------- src/ui/documentwidget.h | 8 +++- src/ui/sidebarwidget.c | 14 +++++-- src/ui/text.c | 4 +- src/ui/touch.c | 49 +++++++---------------- src/ui/widget.c | 55 +++++++++++++------------- src/ui/widget.h | 9 +++-- 14 files changed, 207 insertions(+), 106 deletions(-) (limited to 'src/history.h') diff --git a/src/app.c b/src/app.c index c2bd3da0..4d96b5ef 100644 --- a/src/app.c +++ b/src/app.c @@ -2244,7 +2244,8 @@ iBool handleCommand_App(const char *cmd) { } else if (equal_Command(cmd, "open")) { iString *url = collectNewCStr_String(suffixPtr_Command(cmd, "url")); - const iBool noProxy = argLabel_Command(cmd, "noproxy"); + const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0; + const iBool fromSidebar = argLabel_Command(cmd, "fromsidebar") != 0; iUrl parts; init_Url(&parts, url); if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") || @@ -2295,7 +2296,9 @@ iBool handleCommand_App(const char *cmd) { else { urlEncodePath_String(url); } - setUrlFromCache_DocumentWidget(doc, url, isHistory); + setUrlFlags_DocumentWidget(doc, url, + (isHistory ? useCachedContentIfAvailable_DocumentWidgetSetUrlFlag : 0) | + (fromSidebar ? openedFromSidebar_DocumentWidgetSetUrlFlag : 0)); /* Optionally, jump to a text in the document. This will only work if the document is already available, e.g., it's from "about:" or restored from cache. */ const iRangecc gotoHeading = range_Command(cmd, "gotoheading"); diff --git a/src/defs.h b/src/defs.h index 950da394..3ea28e95 100644 --- a/src/defs.h +++ b/src/defs.h @@ -35,9 +35,10 @@ enum iFileVersion { addedResponseTimestamps_FileVersion = 1, multipleRoots_FileVersion = 2, serializedSidebarState_FileVersion = 3, + addedRecentUrlFlags_FileVersion = 4, /* meta */ idents_FileVersion = 1, /* version used by GmCerts/idents.lgr */ - latest_FileVersion = 3, + latest_FileVersion = 4, }; /* Icons */ diff --git a/src/gmdocument.c b/src/gmdocument.c index 5d02ee05..0ab09604 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -92,6 +92,8 @@ struct Impl_GmDocument { iChar siteIcon; iMedia * media; iStringSet *openURLs; /* currently open URLs for highlighting links */ + iBool isPaletteValid; + iColor palette[tmMax_ColorId]; /* copy of the color palette */ }; iDefineObjectConstruction(GmDocument) @@ -889,6 +891,8 @@ void init_GmDocument(iGmDocument *d) { d->siteIcon = 0; d->media = new_Media(); d->openURLs = NULL; + d->isPaletteValid = iFalse; + iZap(d->palette); } void deinit_GmDocument(iGmDocument *d) { @@ -1401,6 +1405,20 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { } printf("---\n"); #endif + /* Color functions operate on the global palette for convenience, but we may need to switch + palettes on the fly if more than one GmDocument is being displayed simultaneously. */ + memcpy(d->palette, get_Root()->tmPalette, sizeof(d->palette)); + d->isPaletteValid = iTrue; +} + +void makePaletteGlobal_GmDocument(const iGmDocument *d) { + if (d->isPaletteValid) { + memcpy(get_Root()->tmPalette, d->palette, sizeof(d->palette)); + } +} + +void invalidatePalette_GmDocument(iGmDocument *d) { + d->isPaletteValid = iFalse; } void setFormat_GmDocument(iGmDocument *d, enum iSourceFormat format) { diff --git a/src/gmdocument.h b/src/gmdocument.h index 943a408c..221e5c00 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h @@ -170,6 +170,8 @@ void setUrl_GmDocument (iGmDocument *, const iString *url); void setSource_GmDocument (iGmDocument *, const iString *source, int width, enum iGmDocumentUpdate updateType); void foldPre_GmDocument (iGmDocument *, uint16_t preId); +void invalidatePalette_GmDocument(iGmDocument *); +void makePaletteGlobal_GmDocument(const iGmDocument *); /* copies document colors to the global palette */ //void reset_GmDocument (iGmDocument *); /* free images */ diff --git a/src/gmutil.c b/src/gmutil.c index 3ca93901..7a1ae938 100644 --- a/src/gmutil.c +++ b/src/gmutil.c @@ -556,7 +556,7 @@ const iString *feedEntryOpenCommand_String(const iString *url, int newTab) { iString *head = newRange_String( (iRangecc){ constBegin_String(url) + fragPos + 1, constEnd_String(url) }); format_String(cmd, - "open newtab:%d gotourlheading:%s url:%s", + "open fromsidebar:1 newtab:%d gotourlheading:%s url:%s", newTab, cstr_String(head), cstr_Rangecc((iRangecc){ constBegin_String(url), @@ -564,7 +564,7 @@ const iString *feedEntryOpenCommand_String(const iString *url, int newTab) { delete_String(head); } else { - format_String(cmd, "open newtab:%d url:%s", newTab, cstr_String(url)); + format_String(cmd, "open fromsidebar:1 newtab:%d url:%s", newTab, cstr_String(url)); } return cmd; } diff --git a/src/history.c b/src/history.c index d23fb290..c592838e 100644 --- a/src/history.c +++ b/src/history.c @@ -37,6 +37,7 @@ void init_RecentUrl(iRecentUrl *d) { d->normScrollY = 0; d->cachedResponse = NULL; d->cachedDoc = NULL; + d->flags.openedFromSidebar = iFalse; } void deinit_RecentUrl(iRecentUrl *d) { @@ -53,6 +54,7 @@ iRecentUrl *copy_RecentUrl(const iRecentUrl *d) { copy->normScrollY = d->normScrollY; copy->cachedResponse = d->cachedResponse ? copy_GmResponse(d->cachedResponse) : NULL; copy->cachedDoc = ref_Object(d->cachedDoc); + copy->flags = d->flags; return copy; } @@ -171,6 +173,7 @@ void serialize_History(const iHistory *d, iStream *outs) { const iRecentUrl *item = i.value; serialize_String(&item->url, outs); write32_Stream(outs, item->normScrollY * 1.0e6f); + writeU16_Stream(outs, item->flags.openedFromSidebar ? iBit(1) : 0); if (item->cachedResponse) { write8_Stream(outs, 1); serialize_GmResponse(item->cachedResponse, outs); @@ -192,6 +195,12 @@ void deserialize_History(iHistory *d, iStream *ins) { init_RecentUrl(&item); deserialize_String(&item.url, ins); item.normScrollY = (float) read32_Stream(ins) / 1.0e6f; + if (version_Stream(ins) >= addedRecentUrlFlags_FileVersion) { + uint16_t flags = readU16_Stream(ins); + if (flags & iBit(1)) { + item.flags.openedFromSidebar = iTrue; + } + } if (read8_Stream(ins)) { item.cachedResponse = new_GmResponse(); deserialize_GmResponse(item.cachedResponse, ins); @@ -378,12 +387,15 @@ void setCachedResponse_History(iHistory *d, const iGmResponse *response) { unlock_Mutex(d->mtx); } -void setCachedDocument_History(iHistory *d, iGmDocument *doc) { +void setCachedDocument_History(iHistory *d, iGmDocument *doc, iBool openedFromSidebar) { lock_Mutex(d->mtx); iRecentUrl *item = mostRecentUrl_History(d); - if (item && item->cachedDoc != doc) { - iRelease(item->cachedDoc); - item->cachedDoc = ref_Object(doc); + if (item) { + item->flags.openedFromSidebar = openedFromSidebar; + if (item->cachedDoc != doc) { + iRelease(item->cachedDoc); + item->cachedDoc = ref_Object(doc); + } } unlock_Mutex(d->mtx); } @@ -487,6 +499,17 @@ size_t pruneLeastImportantMemory_History(iHistory *d) { return delta; } +void invalidateTheme_History(iHistory *d) { + lock_Mutex(d->mtx); + iForEach(Array, i, &d->recent) { + iRecentUrl *r = i.value; + if (r->cachedDoc) { + invalidatePalette_GmDocument(r->cachedDoc); + } + } + unlock_Mutex(d->mtx); +} + const iStringArray *searchContents_History(const iHistory *d, const iRegExp *pattern) { iStringArray *urls = iClob(new_StringArray()); lock_Mutex(d->mtx); diff --git a/src/history.h b/src/history.h index 838ca9ef..7dad72df 100644 --- a/src/history.h +++ b/src/history.h @@ -39,6 +39,9 @@ struct Impl_RecentUrl { float normScrollY; /* normalized to document height */ iGmResponse *cachedResponse; /* kept in memory for quicker back navigation */ iGmDocument *cachedDoc; /* cached copy of the presentation: layout and media (not serialized) */ + struct { + uint8_t openedFromSidebar : 1; + } flags; }; iDeclareType(MemInfo) @@ -60,7 +63,7 @@ void clear_History (iHistory *); void add_History (iHistory *, const iString *url); void replace_History (iHistory *, const iString *url); void setCachedResponse_History (iHistory *, const iGmResponse *response); -void setCachedDocument_History (iHistory *, iGmDocument *doc); +void setCachedDocument_History (iHistory *, iGmDocument *doc, iBool openedFromSidebar); iBool goBack_History (iHistory *); iBool goForward_History (iHistory *); iBool preceding_History (iHistory *d, iRecentUrl *recent_out); @@ -72,6 +75,7 @@ iRecentUrl *findUrl_History (iHistory *, const iString *url); void clearCache_History (iHistory *); size_t pruneLeastImportant_History (iHistory *); size_t pruneLeastImportantMemory_History (iHistory *); +void invalidateTheme_History (iHistory *); /* theme has changed, cached contents need updating */ iBool atLatest_History (const iHistory *); iBool atOldest_History (const iHistory *); diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index eae5d713..3fc22bdf 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -130,7 +130,8 @@ void deinit_PersistentDocumentState(iPersistentDocumentState *d) { void serialize_PersistentDocumentState(const iPersistentDocumentState *d, iStream *outs) { serialize_String(d->url, outs); - writeU16_Stream(outs, d->reloadInterval & 7); + uint16_t params = d->reloadInterval & 7; + writeU16_Stream(outs, params); serialize_History(d->history, outs); } @@ -223,6 +224,7 @@ enum iDocumentWidgetFlag { movingSelectMarkEnd_DocumentWidgetFlag = iBit(11), otherRootByDefault_DocumentWidgetFlag = iBit(12), /* links open to other root by default */ urlChanged_DocumentWidgetFlag = iBit(13), + openedFromSidebar_DocumentWidgetFlag = iBit(14), }; enum iDocumentLinkOrdinalMode { @@ -1606,7 +1608,8 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); set_Block(&d->sourceContent, &resp->body); updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); - setCachedDocument_History(d->mod.history, d->doc); + setCachedDocument_History(d->mod.history, d->doc, + (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); } d->state = ready_RequestState; postProcessRequestContent_DocumentWidget_(d, iTrue); @@ -1626,6 +1629,9 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { const iRecentUrl *recent = findUrl_History(d->mod.history, withSpacesEncoded_String(d->mod.url)); if (recent && recent->cachedResponse) { + iChangeFlags(d->flags, + openedFromSidebar_DocumentWidgetFlag, + recent->flags.openedFromSidebar); updateFromCachedResponse_DocumentWidget_( d, recent->normScrollY, recent->cachedResponse, recent->cachedDoc); return iTrue; @@ -2282,6 +2288,9 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { /* The temporary "swipeIn" will display the previous page until the finger is lifted. */ iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); if (!swipeIn) { + const iBool sidebarSwipe = (isPortraitPhone_App() && + d->flags & openedFromSidebar_DocumentWidgetFlag && + !isVisible_Widget(findWidget_App("sidebar"))); swipeIn = new_DocumentWidget(); setId_Widget(as_Widget(swipeIn), "swipein"); setFlags_Widget(as_Widget(swipeIn), @@ -2290,16 +2299,18 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { swipeIn->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); swipeIn->widget.rect.size = d->widget.rect.size; swipeIn->widget.offsetRef = parent_Widget(w); - iRecentUrl *recent = new_RecentUrl(); - preceding_History(d->mod.history, recent); - if (recent->cachedDoc) { - iChangeRef(swipeIn->doc, recent->cachedDoc); - updateScrollMax_DocumentWidget_(d); - setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0); - updateVisible_DocumentWidget_(swipeIn); - swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; + if (!sidebarSwipe) { + iRecentUrl *recent = new_RecentUrl(); + preceding_History(d->mod.history, recent); + if (recent->cachedDoc) { + iChangeRef(swipeIn->doc, recent->cachedDoc); + updateScrollMax_DocumentWidget_(d); + setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0); + updateVisible_DocumentWidget_(swipeIn); + swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; + } + delete_RecentUrl(recent); } - delete_RecentUrl(recent); addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos); } } @@ -2326,7 +2337,9 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) { destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ } if (flags_Widget(w) & dragged_WidgetFlag) { - setVisualOffset_Widget(w, width_Widget(w) + offset, animSpan, 0); + setVisualOffset_Widget(w, width_Widget(w) + + width_Widget(d) * offset / size_Root(w->root).x, + animSpan, 0); } else { setVisualOffset_Widget(w, offset / 4, animSpan, 0); @@ -2426,6 +2439,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) 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); @@ -2691,7 +2705,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) (startsWithCase_String(meta_GmRequest(d->request), "text/") || !cmp_String(&d->sourceMime, mimeType_Gempub))) { setCachedResponse_History(d->mod.history, lockResponse_GmRequest(d->request)); - setCachedDocument_History(d->mod.history, d->doc); /* keeps a ref */ + setCachedDocument_History(d->mod.history, d->doc, /* keeps a ref */ + (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); unlockResponse_GmRequest(d->request); } } @@ -2821,6 +2836,15 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) return iTrue; } else if (equal_Command(cmd, "navigate.back") && document_App() == d) { + if (isPortraitPhone_App()) { + if (d->flags & openedFromSidebar_DocumentWidgetFlag && + !isVisible_Widget(findWidget_App("sidebar"))) { + postCommand_App("sidebar.toggle"); + showToolbars_Root(get_Root(), iTrue); + return iTrue; + } + d->flags &= ~openedFromSidebar_DocumentWidgetFlag; + } if (d->request) { postCommandf_Root(w->root, "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); @@ -3499,7 +3523,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e pushBack_Array( &items, &(iMenuItem){ delete_Icon " " uiTextCaution_ColorEscape - " ${link.file.delete}", + "${link.file.delete}", 0, 0, format_CStr("!file.delete confirm:1 path:%s", @@ -4366,7 +4390,7 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { iDrawBufs * dbuf = d->drawBufs; iPaint p; init_Paint(&p); - setClip_Paint(&p, bounds); + setClip_Paint(&p, boundsWithoutVisualOffset_Widget(w)); /* Side icon and current heading. */ if (prefs_App()->sideIcon && opacity > 0 && dbuf->sideIconBuf) { const iInt2 texSize = size_SDLTexture(dbuf->sideIconBuf); @@ -4616,12 +4640,17 @@ static void prerender_DocumentWidget_(iAny *context) { } static void draw_DocumentWidget_(const iDocumentWidget *d) { - const iWidget *w = constAs_Widget(d); - const iRect bounds = bounds_Widget(w); + const iWidget *w = constAs_Widget(d); + const iRect bounds = bounds_Widget(w); + const iRect boundsWithoutVisOff = boundsWithoutVisualOffset_Widget(w); + const iRect clipBounds = intersect_Rect(bounds, boundsWithoutVisOff); if (width_Rect(bounds) <= 0) { return; } -// draw_Widget(w); + /* 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); if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { updateTimestampBuf_DocumentWidget_(d); } @@ -4638,7 +4667,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { }; init_Paint(&ctx.paint); render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); - setClip_Paint(&ctx.paint, bounds); + setClip_Paint(&ctx.paint, clipBounds); int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); /* Text markers. */ @@ -4673,7 +4702,6 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { } } drawMedia_DocumentWidget_(d, &ctx.paint); - unsetClip_Paint(&ctx.paint); /* Fill the top and bottom, in case the document is short. */ if (yTop > top_Rect(bounds)) { fillRect_Paint(&ctx.paint, @@ -4687,6 +4715,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), tmBackground_ColorId); } + unsetClip_Paint(&ctx.paint); drawSideElements_DocumentWidget_(d); if (prefs_App()->hoverLink && d->hoverLink) { const int font = uiLabel_FontId; @@ -4748,6 +4777,23 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { drawCentered_Text(uiLabelBold_FontId, rect, iFalse, uiBackground_ColorId, "%zu bytes selected", size_Range(&mark)); } + if (w->offsetRef) { + const int offX = visualOffsetByReference_Widget(w); + if (offX) { + setClip_Paint(&ctx.paint, clipBounds); + SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); + ctx.paint.alpha = iAbs(offX) / (float) get_Window()->size.x * 300; + fillRect_Paint(&ctx.paint, bounds, backgroundFadeColor_Widget()); + SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); + unsetClip_Paint(&ctx.paint); + } + else { + /* TODO: Should have a better place to do this; drawing is supposed to be immutable. */ + iWidget *mut = iConstCast(iWidget *, w); + mut->offsetRef = NULL; + mut->flags &= ~refChildrenOffset_WidgetFlag; + } + } } /*----------------------------------------------------------------------------------------------*/ @@ -4814,10 +4860,13 @@ static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) { if (!equal_String(d->mod.url, url)) { d->flags |= urlChanged_DocumentWidgetFlag; set_String(d->mod.url, url); -} + } } -void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { +void setUrlFlags_DocumentWidget(iDocumentWidget *d, const iString *url, int setUrlFlags) { + iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, + (setUrlFlags & openedFromSidebar_DocumentWidgetSetUrlFlag) != 0); + const iBool isFromCache = (setUrlFlags & useCachedContentIfAvailable_DocumentWidgetSetUrlFlag) != 0; setLinkNumberMode_DocumentWidget_(d, iFalse); setUrl_DocumentWidget_(d, urlFragmentStripped_String(url)); /* See if there a username in the URL. */ @@ -4829,6 +4878,7 @@ void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBoo void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, const iString *mime, const iBlock *source) { + d->flags &= ~openedFromSidebar_DocumentWidgetFlag; setLinkNumberMode_DocumentWidget_(d, iFalse); setUrl_DocumentWidget_(d, url); parseUser_DocumentWidget_(d); @@ -4846,12 +4896,12 @@ iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) { delete_History(d->mod.history); d->initNormScrollY = normScrollPos_DocumentWidget_(d); d->mod.history = copy_History(orig->mod.history); - setUrlFromCache_DocumentWidget(d, orig->mod.url, iTrue); + setUrlFlags_DocumentWidget(d, orig->mod.url, useCachedContentIfAvailable_DocumentWidgetSetUrlFlag); return d; } void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { - setUrlFromCache_DocumentWidget(d, url, iFalse); + setUrlFlags_DocumentWidget(d, url, 0); } void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { @@ -4862,6 +4912,11 @@ void setRedirectCount_DocumentWidget(iDocumentWidget *d, int count) { d->redirectCount = count; } +void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *d, iBool fromSidebar) { + iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, fromSidebar); + setCachedDocument_History(d->mod.history, d->doc, fromSidebar); +} + iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { return d->request != NULL; } diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h index c038f981..1921b25a 100644 --- a/src/ui/documentwidget.h +++ b/src/ui/documentwidget.h @@ -45,11 +45,17 @@ const iString * bookmarkTitle_DocumentWidget (const iDocumentWidget *); const iString * feedTitle_DocumentWidget (const iDocumentWidget *); int documentWidth_DocumentWidget (const iDocumentWidget *); +enum iDocumentWidgetSetUrlFlags { + useCachedContentIfAvailable_DocumentWidgetSetUrlFlag = iBit(1), + openedFromSidebar_DocumentWidgetSetUrlFlag = iBit(2), +}; + void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); -void setUrlFromCache_DocumentWidget (iDocumentWidget *, const iString *url, iBool isFromCache); +void setUrlFlags_DocumentWidget (iDocumentWidget *, const iString *url, int setUrlFlags); void setUrlAndSource_DocumentWidget (iDocumentWidget *, const iString *url, const iString *mime, const iBlock *source); void setInitialScroll_DocumentWidget (iDocumentWidget *, float normScrollY); /* set after content received */ void setRedirectCount_DocumentWidget (iDocumentWidget *, int count); void setSource_DocumentWidget (iDocumentWidget *, const iString *sourceText); +void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *, iBool fromSidebar); void updateSize_DocumentWidget (iDocumentWidget *); diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 4f3ea637..f7181037 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -773,6 +773,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->id); postCommandf_App("document.goto loc:%p", head->text.start); dismissPortraitPhoneSidebars_Root(as_Widget(d)->root); + setOpenedFromSidebar_DocumentWidget(document_App(), iTrue); break; } case feeds_SidebarMode: { @@ -783,7 +784,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si case bookmarks_SidebarMode: case history_SidebarMode: { if (!isEmpty_String(&item->url)) { - postCommandf_Root(get_Root(), "open newtab:%d url:%s", + postCommandf_Root(get_Root(), "open fromsidebar:1 newtab:%d url:%s", openTabMode_Sym(modState_Keys()), cstr_String(&item->url)); } @@ -944,13 +945,16 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); } setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); + /* Safe area inset for mobile. */ + const int safePad = (d->side == left_SidebarSide ? left_Rect(safeRect_Root(w->root)) : 0); if (isVisible_Widget(w)) { setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse); w->rect.size.x = d->widthAsGaps * gap_UI; invalidate_ListWidget(d->list); if (isAnimated) { setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); - setVisualOffset_Widget(w, (d->side == left_SidebarSide ? -1 : 1) * w->rect.size.x, 0, 0); + setVisualOffset_Widget( + w, (d->side == left_SidebarSide ? -1 : 1) * (w->rect.size.x + safePad), 0, 0); setVisualOffset_Widget(w, 0, 300, easeOut_AnimFlag | softer_AnimFlag); } } @@ -958,11 +962,13 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); if (d->side == right_SidebarSide) { setVisualOffset_Widget(w, visX, 0, 0); - setVisualOffset_Widget(w, visX + w->rect.size.x, 300, easeOut_AnimFlag | softer_AnimFlag); + setVisualOffset_Widget( + w, visX + w->rect.size.x + safePad, 300, easeOut_AnimFlag | softer_AnimFlag); } else { setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue); - setVisualOffset_Widget(w, -w->rect.size.x, 300, easeOut_AnimFlag | softer_AnimFlag); + setVisualOffset_Widget( + w, -w->rect.size.x - safePad, 300, easeOut_AnimFlag | softer_AnimFlag); } } arrange_Widget(w->parent); diff --git a/src/ui/text.c b/src/ui/text.c index ffe08fca..8a2a9ff8 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -389,7 +389,9 @@ static void initCache_Text_(iText *d) { d->cacheRowAllocStep = iMax(2, textSize / 6); /* Allocate initial (empty) rows. These will be assigned actual locations in the cache once at least one glyph is stored. */ - for (int h = d->cacheRowAllocStep; h <= 2 * textSize + d->cacheRowAllocStep; h += d->cacheRowAllocStep) { + for (int h = d->cacheRowAllocStep; + h <= 2.5 * textSize + d->cacheRowAllocStep; + h += d->cacheRowAllocStep) { pushBack_Array(&d->cacheRows, &(iCacheRow){ .height = 0 }); } d->cacheBottom = 0; diff --git a/src/ui/touch.c b/src/ui/touch.c index 36bac267..dac1152e 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c @@ -65,6 +65,7 @@ struct Impl_Touch { iBool isLeftDown; iBool isTouchDrag; iBool isTapAndHold; + iBool didPostEdgeMove; iBool didBeginOnTouchDrag; int pinchId; enum iTouchEdge edge; @@ -391,6 +392,12 @@ static void checkNewPinch_TouchState_(iTouchState *d, iTouch *newTouch) { pinch.touchIds[1] = other->id; newTouch->pinchId = other->pinchId = pinch.id; clearWidgetMomentum_TouchState_(d, affinity); + if (other->edge && other->didPostEdgeMove) { + postCommandf_App("edgeswipe.ended abort:1 side:%d id:%llu", other->edge, other->id); + other->didPostEdgeMove = iFalse; + } + other->edge = none_TouchEdge; + newTouch->edge = none_TouchEdge; /* Remember current positions to determine pinch amount. */ newTouch->startPos = newTouch->pos[0]; other->startPos = other->pos[0]; @@ -476,7 +483,6 @@ iBool processEvent_Touch(const SDL_Event *ev) { } else if (x > rootSize.x - edgeWidth) { edge = right_TouchEdge; -// puts("DOWN on right edge"); } iWidget *aff = hitChild_Window(window, init_I2(iRound(x), iRound(y_F3(pos)))); #if 0 @@ -523,6 +529,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { (int) (x_F3(pos) - x_F3(touch->startPos)), touch->edge, touch->id); + touch->didPostEdgeMove = iTrue; return iTrue; } if (touch && touch->affinity) { @@ -595,39 +602,6 @@ iBool processEvent_Touch(const SDL_Event *ev) { } } iAssert(touch->edge == none_TouchEdge); -#if 0 - /* Edge swipe aborted? */ - if (touch->edge == left_TouchEdge) { - if (fing->dx < 0 && x_F3(touch->pos[0]) < tapRadiusPt_ * window->pixelRatio) { - touch->edge = none_TouchEdge; - if (touch->edgeDragging) { - setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse); - setVisualOffset_Widget(touch->edgeDragging, 0, 200, easeOut_AnimFlag); - touch->edgeDragging = NULL; - } - } - else if (touch->edgeDragging) { - setVisualOffset_Widget(touch->edgeDragging, x_F3(pos) - x_F3(touch->startPos), 10, 0); - } - } - if (touch->edge == right_TouchEdge) { - if (fing->dx > 0 && x_F3(touch->pos[0]) > window->size.x - tapRadiusPt_ * window->pixelRatio) { - puts("touch->edge==right returned to right edge, aborted"); - touch->edge = none_TouchEdge; - if (touch->edgeDragging) { - setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse); - setVisualOffset_Widget(touch->edgeDragging, 0, 200, easeOut_AnimFlag); - touch->edgeDragging = NULL; - } - } - else if (touch->edgeDragging) { - setVisualOffset_Widget(touch->edgeDragging, x_F3(pos) - x_F3(touch->startPos), 10, 0); - } - } - if (touch->edge) { - pixels.y = 0; - } -#endif if (touch->axis == x_TouchAxis) { pixels.y = 0; } @@ -671,7 +645,12 @@ iBool processEvent_Touch(const SDL_Event *ev) { } #endif if (touch->edge && !isStationary_Touch_(touch)) { - postCommandf_App("edgeswipe.ended side:%d id:%llu", touch->edge, touch->id); + const iFloat3 gesture = gestureVector_Touch_(touch); + const float pixel = window->pixelRatio; + const int moveDir = x_F3(gesture) < -pixel ? -1 : x_F3(gesture) > pixel ? +1 : 0; + const int didAbort = (touch->edge == left_TouchEdge && moveDir < 0) || + (touch->edge == right_TouchEdge && moveDir > 0); + postCommandf_App("edgeswipe.ended abort:%d side:%d id:%llu", didAbort, touch->edge, touch->id); remove_ArrayIterator(&i); continue; } diff --git a/src/ui/widget.c b/src/ui/widget.c index 11ec1b07..85672c04 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -760,16 +760,24 @@ void arrange_Widget(iWidget *d) { } iBool isBeingVisuallyOffsetByReference_Widget(const iWidget *d) { - if (d->flags & refChildrenOffset_WidgetFlag) { + return visualOffsetByReference_Widget(d) != 0; +} + +int visualOffsetByReference_Widget(const iWidget *d) { + if (d->offsetRef && d->flags & refChildrenOffset_WidgetFlag) { + int offX = 0; iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) { const iWidget *child = i.object; if (child == d) continue; if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { - return iTrue; +// const float factor = width_Widget(d) / (float) size_Root(d->root).x; + const int invOff = width_Widget(d) - iRound(value_Anim(&child->visualOffset)); + offX -= invOff / 4; } } + return offX; } - return iFalse; + return 0; } static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { @@ -786,14 +794,7 @@ static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { pos->y -= value_Anim(d->animOffsetRef); } if (d->flags & refChildrenOffset_WidgetFlag) { - iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) { - const iWidget *child = i.object; - if (child == d) continue; - if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { - const int invOff = size_Root(d->root).x - iRound(value_Anim(&child->visualOffset)); - pos->x -= invOff / 4; - } - } + pos->x += visualOffsetByReference_Widget(d); } } @@ -1081,7 +1082,9 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { if (~d->flags & dragged_WidgetFlag) { setFlags_Widget(d, dragged_WidgetFlag, iTrue); } - setVisualOffset_Widget(d, arg_Command(command_UserEvent(ev)), 10, 0); + setVisualOffset_Widget(d, arg_Command(command_UserEvent(ev)) * + width_Widget(d) / size_Root(d->root).x, + 10, 0); return iTrue; } } @@ -1129,6 +1132,17 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { return iFalse; } +int backgroundFadeColor_Widget(void) { + switch (colorTheme_App()) { + case light_ColorTheme: + return gray25_ColorId; + case pureWhite_ColorTheme: + return gray50_ColorId; + default: + return black_ColorId; + } +} + void drawBackground_Widget(const iWidget *d) { if (d->flags & noBackground_WidgetFlag) { return; @@ -1151,8 +1165,7 @@ void drawBackground_Widget(const iWidget *d) { drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30); } const iBool isFaded = fadeBackground && - ~d->flags & noFadeBackground_WidgetFlag;/* && - ~d->flags & destroyPending_WidgetFlag;*/ + ~d->flags & noFadeBackground_WidgetFlag; if (isFaded) { iPaint p; init_Paint(&p); @@ -1163,19 +1176,7 @@ void drawBackground_Widget(const iWidget *d) { p.alpha *= (area > 0 ? visibleArea / area : 0.0f); } SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); - int fadeColor; - switch (colorTheme_App()) { - default: - fadeColor = black_ColorId; - break; - case light_ColorTheme: - fadeColor = gray25_ColorId; - break; - case pureWhite_ColorTheme: - fadeColor = gray50_ColorId; - break; - } - fillRect_Paint(&p, rect_Root(d->root), fadeColor); + fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget()); SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); } if (d->bgColor >= 0 || d->frameColor >= 0) { diff --git a/src/ui/widget.h b/src/ui/widget.h index b2310f21..acb8fa9d 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -242,10 +242,9 @@ iBool isSelected_Widget (const iAnyObject *); iBool isUnderKeyRoot_Widget (const iAnyObject *); iBool isCommand_Widget (const iWidget *d, const SDL_Event *ev, const char *cmd); iBool hasParent_Widget (const iWidget *d, const iWidget *someParent); -iBool isAffectedByVisualOffset_Widget - (const iWidget *); -iBool isBeingVisuallyOffsetByReference_Widget - (const iWidget *); +iBool isAffectedByVisualOffset_Widget (const iWidget *); +iBool isBeingVisuallyOffsetByReference_Widget (const iWidget *); +int visualOffsetByReference_Widget (const iWidget *); void setId_Widget (iWidget *, const char *id); void setFlags_Widget (iWidget *, int64_t flags, iBool set); void setPos_Widget (iWidget *, iInt2 pos); @@ -278,6 +277,8 @@ void refresh_Widget (const iAnyObject *); iBool equalWidget_Command (const char *cmd, const iWidget *widget, const char *checkCommand); +int backgroundFadeColor_Widget (void); + void setFocus_Widget (iWidget *); iWidget * focus_Widget (void); void setHover_Widget (iWidget *); -- cgit v1.2.3