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/gmdocument.c | 69 ++++++++++++++++++++++++++++++++++++++++------- src/gmdocument.h | 15 ++++++++--- src/history.c | 72 ++++++++++++++++++++++++++++++++++++++----------- src/history.h | 3 +++ src/macos.m | 2 +- src/media.c | 5 ++++ src/media.h | 2 ++ src/ui/color.c | 6 ++--- src/ui/color.h | 40 +++++++++++++-------------- src/ui/documentwidget.c | 52 +++++++++++++++++++++++++---------- src/ui/labelwidget.c | 2 +- src/ui/text.c | 4 +-- 12 files changed, 203 insertions(+), 69 deletions(-) (limited to 'src') diff --git a/src/gmdocument.c b/src/gmdocument.c index 466169fc..da99fd0d 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -47,7 +47,7 @@ iBool isDark_GmDocumentTheme(enum iGmDocumentTheme d) { iDeclareType(GmLink) struct Impl_GmLink { - iString url; + iString url; /* resolved */ iRangecc urlRange; /* URL in the source */ iRangecc labelRange; /* label in the source */ iRangecc labelIcon; /* special icon defined in the label text */ @@ -74,8 +74,9 @@ iDefineTypeConstruction(GmLink) struct Impl_GmDocument { iObject object; enum iGmDocumentFormat format; - iString source; - iString url; /* for resolving relative links */ + iString unormSource; /* unnormalized source */ + iString source; /* normalized source */ + iString url; /* for resolving relative links */ iString localHost; iInt2 size; iArray layout; /* contents of source, laid out in document space */ @@ -437,7 +438,10 @@ static void doLayout_GmDocument_(iGmDocument *d) { isFirstText = iFalse; } while (nextSplit_Rangecc(content, "\n", &contentLine)) { - iRangecc line = contentLine; /* `line` will be trimmed later; would confuse nextSplit */ + iRangecc line = contentLine; /* `line` will be trimmed; modifying would confuse `nextSplit_Rangecc` */ + if (*line.end == '\r') { + line.end--; /* trim CR always */ + } iGmRun run = { .color = white_ColorId }; enum iGmLineType type; float indent = 0.0f; @@ -844,10 +848,13 @@ static void doLayout_GmDocument_(iGmDocument *d) { } } } + printf("[GmDocument] layout size: %zu runs (%zu bytes)\n", + size_Array(&d->layout), size_Array(&d->layout) * sizeof(iGmRun)); } void init_GmDocument(iGmDocument *d) { d->format = gemini_GmDocumentFormat; + init_String(&d->unormSource); init_String(&d->source); init_String(&d->url); init_String(&d->localHost); @@ -878,6 +885,7 @@ void deinit_GmDocument(iGmDocument *d) { deinit_String(&d->localHost); deinit_String(&d->url); deinit_String(&d->source); + deinit_String(&d->unormSource); } iMedia *media_GmDocument(iGmDocument *d) { @@ -888,6 +896,7 @@ const iMedia *constMedia_GmDocument(const iGmDocument *d) { return d->media; } +#if 0 void reset_GmDocument(iGmDocument *d) { clear_Media(d->media); clearLinks_GmDocument_(d); @@ -896,8 +905,11 @@ void reset_GmDocument(iGmDocument *d) { clear_Array(&d->preMeta); clear_String(&d->url); clear_String(&d->localHost); + clear_String(&d->source); + clear_String(&d->unormSource); d->themeSeed = 0; } +#endif static void setDerivedThemeColors_(enum iGmDocumentTheme theme) { set_Color(tmQuoteIcon_ColorId, @@ -1418,6 +1430,8 @@ static void normalize_GmDocument(iGmDocument *d) { isPreformat = iTrue; /* Cannot be turned off. */ } const int preTabWidth = 4; /* TODO: user-configurable parameter */ + iBool wasNormalized = iFalse; + iBool hasTabs = iFalse; while (nextSplit_Rangecc(src, "\n", &line)) { if (isPreformat) { /* Replace any tab characters with spaces for visualization. */ @@ -1428,10 +1442,16 @@ static void normalize_GmDocument(iGmDocument *d) { while (numSpaces-- > 0) { appendCStrN_String(normalized, " ", 1); } + hasTabs = iTrue; + wasNormalized = iTrue; } - else if (*ch != '\r') { + else if (*ch != '\v') { appendCStrN_String(normalized, ch, 1); } + else { + hasTabs = iTrue; + wasNormalized = iTrue; + } } appendCStr_String(normalized, "\n"); if (d->format == gemini_GmDocumentFormat && @@ -1450,7 +1470,10 @@ static void normalize_GmDocument(iGmDocument *d) { int spaceCount = 0; for (const char *ch = line.start; ch != line.end; ch++) { char c = *ch; - if (c == '\r') continue; + if (c == '\v') { + wasNormalized = iTrue; + continue; + } if (isNormalizableSpace_(c)) { if (isPrevSpace) { if (++spaceCount == 8) { @@ -1459,9 +1482,13 @@ static void normalize_GmDocument(iGmDocument *d) { popBack_Block(&normalized->chars); pushBack_Block(&normalized->chars, '\t'); } + wasNormalized = iTrue; continue; /* skip repeated spaces */ } - c = ' '; + if (c != ' ') { + c = ' '; + wasNormalized = iTrue; + } isPrevSpace = iTrue; } else { @@ -1472,7 +1499,13 @@ static void normalize_GmDocument(iGmDocument *d) { } appendCStr_String(normalized, "\n"); } + printf("hasTabs: %d\n", hasTabs); + printf("wasNormalized: %d\n", wasNormalized); + fflush(stdout); 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)); } void setUrl_GmDocument(iGmDocument *d, const iString *url) { @@ -1483,8 +1516,18 @@ void setUrl_GmDocument(iGmDocument *d, const iString *url) { updateIconBasedOnUrl_GmDocument_(d); } -void setSource_GmDocument(iGmDocument *d, const iString *source, int width) { - set_String(&d->source, source); +void setSource_GmDocument(iGmDocument *d, const iString *source, int width, + enum iGmDocumentUpdate updateType) { + printf("[GmDocument] source update (%zu bytes), width:%d, final:%d\n", + size_String(source), width, updateType == final_GmDocumentUpdate); + if (size_String(source) == size_String(&d->unormSource)) { + iAssert(equal_String(source, &d->unormSource)); + printf("[GmDocument] source is unchanged!\n"); + return; /* Nothing to do. */ + } + set_String(&d->unormSource, source); + /* Normalize. */ + set_String(&d->source, &d->unormSource); if (isNormalized_GmDocument_(d)) { normalize_GmDocument(d); } @@ -1586,6 +1629,14 @@ const iString *source_GmDocument(const iGmDocument *d) { return &d->source; } +size_t memorySize_GmDocument(const iGmDocument *d) { + return size_String(&d->unormSource) + + size_String(&d->source) + + size_Array(&d->layout) * sizeof(iGmRun) + + size_Array(&d->links) * sizeof(iGmLink) + + memorySize_Media(d->media); +} + iRangecc findText_GmDocument(const iGmDocument *d, const iString *text, const char *start) { const char * src = constBegin_String(&d->source); const size_t startPos = (start ? start - src : 0); diff --git a/src/gmdocument.h b/src/gmdocument.h index 574f0acf..1e54a16a 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h @@ -125,12 +125,12 @@ enum iGmRunMediaType { struct Impl_GmRun { iRangecc text; + iRect bounds; /* used for hit testing, may extend to edges */ + iRect visBounds; /* actual visual bounds */ uint8_t font; uint8_t color; uint8_t flags; uint8_t mediaType; - iRect bounds; /* used for hit testing, may extend to edges */ - iRect visBounds; /* actual visual bounds */ uint16_t preId; /* preformatted block ID (sequential) */ iGmLinkId linkId; /* zero for non-links */ uint16_t mediaId; /* zero if not an image */ @@ -160,6 +160,11 @@ enum iGmDocumentBanner { certificateWarning_GmDocumentBanner, }; +enum iGmDocumentUpdate { + partial_GmDocumentUpdate, /* appending more content */ + final_GmDocumentUpdate, /* process all lines, including the last one if not terminated */ +}; + void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type); @@ -167,10 +172,11 @@ void setWidth_GmDocument (iGmDocument *, int width); void redoLayout_GmDocument (iGmDocument *); iBool updateOpenURLs_GmDocument(iGmDocument *); void setUrl_GmDocument (iGmDocument *, const iString *url); -void setSource_GmDocument (iGmDocument *, const iString *source, int width); +void setSource_GmDocument (iGmDocument *, const iString *source, int width, + enum iGmDocumentUpdate updateType); void foldPre_GmDocument (iGmDocument *, uint16_t preId); -void reset_GmDocument (iGmDocument *); /* free images */ +//void reset_GmDocument (iGmDocument *); /* free images */ typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); @@ -190,6 +196,7 @@ enum iGmDocumentBanner bannerType_GmDocument(const iGmDocument *); const iString * bannerText_GmDocument (const iGmDocument *); const iArray * headings_GmDocument (const iGmDocument *); /* array of GmHeadings */ const iString * source_GmDocument (const iGmDocument *); +size_t memorySize_GmDocument (const iGmDocument *); /* bytes */ iRangecc findText_GmDocument (const iGmDocument *, const iString *text, const char *start); iRangecc findTextBefore_GmDocument (const iGmDocument *, const iString *text, const char *before); diff --git a/src/history.c b/src/history.c index 9f4e415b..58ffa5c4 100644 --- a/src/history.c +++ b/src/history.c @@ -34,11 +34,13 @@ static const size_t maxStack_History_ = 50; /* back/forward navigable items */ void init_RecentUrl(iRecentUrl *d) { init_String(&d->url); - d->normScrollY = 0; + d->normScrollY = 0; d->cachedResponse = NULL; + d->cachedDoc = NULL; } void deinit_RecentUrl(iRecentUrl *d) { + iRelease(d->cachedDoc); deinit_String(&d->url); delete_GmResponse(d->cachedResponse); } @@ -48,11 +50,29 @@ iDefineTypeConstruction(RecentUrl) iRecentUrl *copy_RecentUrl(const iRecentUrl *d) { iRecentUrl *copy = new_RecentUrl(); set_String(©->url, &d->url); - copy->normScrollY = d->normScrollY; + copy->normScrollY = d->normScrollY; copy->cachedResponse = d->cachedResponse ? copy_GmResponse(d->cachedResponse) : NULL; + copy->cachedDoc = ref_Object(d->cachedDoc); return copy; } +size_t cacheSize_RecentUrl(const iRecentUrl *d) { + size_t size = 0; + if (d->cachedResponse) { + size += size_String(&d->cachedResponse->meta); + size += size_Block(&d->cachedResponse->body); + } + return size; +} + +size_t memorySize_RecentUrl(const iRecentUrl *d) { + size_t size = cacheSize_RecentUrl(d); + if (d->cachedDoc) { + size += memorySize_GmDocument(d->cachedDoc); + } + return size; +} + /*----------------------------------------------------------------------------------------------*/ struct Impl_History { @@ -92,20 +112,31 @@ iString *debugInfo_History(const iHistory *d) { iString *str = new_String(); format_String(str, "```\n" - "Idx | Size | SP%% | URL\n" - "----+---------+-----+-----\n"); - size_t totalSize = 0; + "Idx | Cache | Memory | SP%% | URL\n" + "----+---------+----------+-----+-----\n"); + size_t totalCache = 0; + size_t totalMemory = 0; iConstForEach(Array, i, &d->recent) { const iRecentUrl *item = i.value; appendFormat_String( str, " %2zu | ", size_Array(&d->recent) - index_ArrayConstIterator(&i) - 1); - if (item->cachedResponse) { - appendFormat_String(str, "%7zu", size_Block(&item->cachedResponse->body)); - totalSize += size_Block(&item->cachedResponse->body); + const size_t cacheSize = cacheSize_RecentUrl(item); + const size_t memSize = memorySize_RecentUrl(item); + if (cacheSize) { + appendFormat_String(str, "%7zu", cacheSize); + totalCache += cacheSize; } else { appendFormat_String(str, " --"); } + appendCStr_String(str, " | "); + if (memSize) { + appendFormat_String(str, "%8zu", memSize); + totalMemory += memSize; + } + else { + appendFormat_String(str, " --"); + } appendFormat_String(str, " | %3d | %s\n", iRound(100.0f * item->normScrollY), @@ -114,8 +145,10 @@ iString *debugInfo_History(const iHistory *d) { appendFormat_String(str, "\n```\n"); appendFormat_String(str, "Total cached data: %.3f MB\n" + "Total memory usage: %.3f MB\n" "Navigation position: %zu\n\n", - totalSize / 1.0e6f, + totalCache / 1.0e6f, + totalMemory / 1.0e6f, d->recentPos); return str; } @@ -301,14 +334,22 @@ void setCachedResponse_History(iHistory *d, const iGmResponse *response) { unlock_Mutex(d->mtx); } +void setCachedDocument_History(iHistory *d, iGmDocument *doc) { + lock_Mutex(d->mtx); + iRecentUrl *item = mostRecentUrl_History(d); + if (item && item->cachedDoc != doc) { + iRelease(item->cachedDoc); + item->cachedDoc = ref_Object(doc); + } + unlock_Mutex(d->mtx); +} + size_t cacheSize_History(const iHistory *d) { size_t cached = 0; lock_Mutex(d->mtx); iConstForEach(Array, i, &d->recent) { const iRecentUrl *url = i.value; - if (url->cachedResponse) { - cached += size_Block(&url->cachedResponse->body); - } + cached += cacheSize_RecentUrl(url); } unlock_Mutex(d->mtx); return cached; @@ -335,9 +376,9 @@ size_t pruneLeastImportant_History(iHistory *d) { lock_Mutex(d->mtx); iConstForEach(Array, i, &d->recent) { const iRecentUrl *url = i.value; - if (url->cachedResponse) { + if (url->cachedResponse || url->cachedDoc) { const double urlScore = - size_Block(&url->cachedResponse->body) * + cacheSize_RecentUrl(url) * pow(secondsSince_Time(&now, &url->cachedResponse->when) / 60.0, 1.25); if (urlScore > score) { chosen = index_ArrayConstIterator(&i); @@ -347,9 +388,10 @@ size_t pruneLeastImportant_History(iHistory *d) { } if (chosen != iInvalidPos) { iRecentUrl *url = at_Array(&d->recent, chosen); - delta = size_Block(&url->cachedResponse->body); + delta = cacheSize_RecentUrl(url); delete_GmResponse(url->cachedResponse); url->cachedResponse = NULL; + iReleasePtr(&url->cachedDoc); } unlock_Mutex(d->mtx); return delta; 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); diff --git a/src/macos.m b/src/macos.m index 61507b6f..709a97f7 100644 --- a/src/macos.m +++ b/src/macos.m @@ -479,7 +479,7 @@ void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem * [menu setAutoenablesItems:NO]; for (size_t i = 0; i < count; ++i) { const char *label = translateCStr_Lang(items[i].label); - if (label[0] == '\r') { + if (label[0] == '\v') { /* Skip the formatting escape. */ label += 2; } diff --git a/src/media.c b/src/media.c index 1313b7da..2ec2109d 100644 --- a/src/media.c +++ b/src/media.c @@ -261,6 +261,11 @@ void clear_Media(iMedia *d) { clear_PtrArray(&d->downloads); } +size_t memorySize_Media(const iMedia *d) { + /* TODO: Calculate the actual memory use. */ + return 0; +} + iBool setDownloadUrl_Media(iMedia *d, iGmLinkId linkId, const iString *url) { iGmDownload *dl = NULL; iMediaId existing = findLinkDownload_Media(d, linkId); diff --git a/src/media.h b/src/media.h index 7cc941d0..c6973b79 100644 --- a/src/media.h +++ b/src/media.h @@ -50,6 +50,8 @@ void clear_Media (iMedia *); iBool setDownloadUrl_Media (iMedia *, uint16_t linkId, const iString *url); iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); +size_t memorySize_Media (const iMedia *); + iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmMediaInfo *info_out); iInt2 imageSize_Media (const iMedia *, iMediaId imageId); diff --git a/src/ui/color.c b/src/ui/color.c index 6cbbdf28..0a177a8a 100644 --- a/src/ui/color.c +++ b/src/ui/color.c @@ -468,12 +468,12 @@ const char *escape_Color(int color) { return esc[color]; } /* TODO: Conflict with format strings! "%" (37) may be used as the color value. */ - /* Double-\r is used for range extension. */ + /* Double-\v is used for range extension. */ if (color + asciiBase_ColorEscape > 127) { iAssert(color - asciiExtended_ColorEscape + asciiBase_ColorEscape <= 127); - return format_CStr("\r\r%c", color - asciiExtended_ColorEscape + asciiBase_ColorEscape); + return format_CStr("\v\v%c", color - asciiExtended_ColorEscape + asciiBase_ColorEscape); } - return format_CStr("\r%c", color + asciiBase_ColorEscape); + return format_CStr("\v%c", color + asciiBase_ColorEscape); } iHSLColor setSat_HSLColor(iHSLColor d, float sat) { diff --git a/src/ui/color.h b/src/ui/color.h index d2fa3c00..aafc1794 100644 --- a/src/ui/color.h +++ b/src/ui/color.h @@ -187,26 +187,26 @@ iLocalDef iBool isRegularText_ColorId(enum iColorId d) { #define asciiBase_ColorEscape 33 #define asciiExtended_ColorEscape (128 - asciiBase_ColorEscape) -#define restore_ColorEscape "\r\x24" /* ASCII Cancel */ -#define black_ColorEscape "\r!" -#define gray25_ColorEscape "\r\"" -#define gray50_ColorEscape "\r#" -#define gray75_ColorEscape "\r$" -#define white_ColorEscape "\r%" -#define brown_ColorEscape "\r&" -#define orange_ColorEscape "\r'" -#define teal_ColorEscape "\r(" -#define cyan_ColorEscape "\r)" -#define yellow_ColorEscape "\r*" -#define red_ColorEscape "\r+" -#define magenta_ColorEscape "\r," -#define blue_ColorEscape "\r-" -#define green_ColorEscape "\r." -#define uiText_ColorEscape "\r4" -#define uiTextAction_ColorEscape "\r<" -#define uiTextCaution_ColorEscape "\r=" -#define uiTextStrong_ColorEscape "\r:" -#define uiHeading_ColorEscape "\rR" +#define restore_ColorEscape "\v\x24" /* ASCII Cancel */ +#define black_ColorEscape "\v!" +#define gray25_ColorEscape "\v\"" +#define gray50_ColorEscape "\v#" +#define gray75_ColorEscape "\v$" +#define white_ColorEscape "\v%" +#define brown_ColorEscape "\v&" +#define orange_ColorEscape "\v'" +#define teal_ColorEscape "\v(" +#define cyan_ColorEscape "\v)" +#define yellow_ColorEscape "\v*" +#define red_ColorEscape "\v+" +#define magenta_ColorEscape "\v," +#define blue_ColorEscape "\v-" +#define green_ColorEscape "\v." +#define uiText_ColorEscape "\v4" +#define uiTextAction_ColorEscape "\v<" +#define uiTextCaution_ColorEscape "\v=" +#define uiTextStrong_ColorEscape "\v:" +#define uiHeading_ColorEscape "\vR" iDeclareType(Color) iDeclareType(HSLColor) diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 0314757f..048f8ce4 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -975,13 +975,11 @@ iBool isPinned_DocumentWidget_(const iDocumentWidget *d) { static void showOrHidePinningIndicator_DocumentWidget_(iDocumentWidget *d) { iWidget *w = as_Widget(d); - showCollapsed_Widget(findChild_Widget(root_Widget(as_Widget(d)), "document.pinned"), + showCollapsed_Widget(findChild_Widget(root_Widget(w), "document.pinned"), isPinned_DocumentWidget_(d)); } -void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { - setUrl_GmDocument(d->doc, d->mod.url); - setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); +static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { documentRunsInvalidated_DocumentWidget_(d); updateWindowTitle_DocumentWidget_(d); updateVisible_DocumentWidget_(d); @@ -997,7 +995,23 @@ void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { d->flags |= otherRootByDefault_DocumentWidgetFlag; } } - showOrHidePinningIndicator_DocumentWidget_(d); + showOrHidePinningIndicator_DocumentWidget_(d); +} + +void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { + setUrl_GmDocument(d->doc, d->mod.url); + setSource_GmDocument(d->doc, + source, + documentWidth_DocumentWidget_(d), + isFinished_GmRequest(d->request) ? final_GmDocumentUpdate + : partial_GmDocumentUpdate); + documentWasChanged_DocumentWidget_(d); +} + +static void replaceDocument_DocumentWidget_(iDocumentWidget *d, iGmDocument *newDoc) { + iRelease(d->doc); + d->doc = ref_Object(newDoc); + documentWasChanged_DocumentWidget_(d); } static void updateTheme_DocumentWidget_(iDocumentWidget *d) { @@ -1063,13 +1077,13 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); setFormat_GmDocument(d->doc, gemini_GmDocumentFormat); translate_Lang(src); + d->state = ready_RequestState; setSource_DocumentWidget(d, src); updateTheme_DocumentWidget_(d); reset_SmoothScroll(&d->scrollY); init_Anim(&d->sideOpacity, 0); init_Anim(&d->altTextOpacity, 0); resetWideRuns_DocumentWidget_(d); - d->state = ready_RequestState; } static void updateFetchProgress_DocumentWidget_(iDocumentWidget *d) { @@ -1174,7 +1188,9 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool } } -static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse *response, +static void updateDocument_DocumentWidget_(iDocumentWidget *d, + const iGmResponse *response, + iGmDocument *cachedDoc, const iBool isInitialUpdate) { if (d->state == ready_RequestState) { return; @@ -1247,6 +1263,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse docFormat = gemini_GmDocumentFormat; setRange_String(&d->sourceMime, param); const iGmLinkId imgLinkId = 1; /* there's only the one link */ + /* TODO: Do the image loading in `postProcessRequestContent_DocumentWidget_()` */ if ((isAudio && isInitialUpdate) || (!isAudio && isRequestFinished)) { const char *linkTitle = startsWith_String(mimeStr, "image/") ? "Image" : "Audio"; @@ -1300,7 +1317,10 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse collect_String(decode_Block(&str.chars, cstr_Rangecc(charset)))); } } - if (setSource) { + if (cachedDoc) { + replaceDocument_DocumentWidget_(d, cachedDoc); + } + else if (setSource) { setSource_DocumentWidget(d, &str); } deinit_String(&str); @@ -1386,7 +1406,8 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { clear_ObjectList(d->media); delete_Gempub(d->sourceGempub); d->sourceGempub = NULL; - reset_GmDocument(d->doc); + iRelease(d->doc); + d->doc = new_GmDocument(); resetWideRuns_DocumentWidget_(d); d->state = fetching_RequestState; /* Do the fetch. */ { @@ -1397,10 +1418,11 @@ static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { d->sourceStatus = success_GmStatusCode; format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); set_Block(&d->sourceContent, &resp->body); - updateDocument_DocumentWidget_(d, resp, iTrue); - postProcessRequestContent_DocumentWidget_(d, iTrue); + updateDocument_DocumentWidget_(d, resp, recent->cachedDoc, iTrue); + setCachedDocument_History(d->mod.history, d->doc); } d->state = ready_RequestState; + postProcessRequestContent_DocumentWidget_(d, iTrue); init_Anim(&d->altTextOpacity, 0); reset_SmoothScroll(&d->scrollY); init_Anim(&d->scrollY.pos, d->initNormScrollY * size_GmDocument(d->doc).y); @@ -1587,11 +1609,12 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { } case categorySuccess_GmStatusCode: reset_SmoothScroll(&d->scrollY); - reset_GmDocument(d->doc); /* new content incoming */ + iRelease(d->doc); /* new content incoming */ + d->doc = new_GmDocument(); delete_Gempub(d->sourceGempub); d->sourceGempub = NULL; resetWideRuns_DocumentWidget_(d); - updateDocument_DocumentWidget_(d, resp, iTrue); + updateDocument_DocumentWidget_(d, resp, NULL, iTrue); break; case categoryRedirect_GmStatusCode: if (isEmpty_String(&resp->meta)) { @@ -1642,7 +1665,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { switch (category_GmStatusCode(statusCode)) { case categorySuccess_GmStatusCode: /* More content available. */ - updateDocument_DocumentWidget_(d, resp, iFalse); + updateDocument_DocumentWidget_(d, resp, NULL, iFalse); break; default: break; @@ -2258,6 +2281,7 @@ 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 */ unlockResponse_GmRequest(d->request); } } diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 5d0f0041..a7aa6391 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c @@ -196,7 +196,7 @@ static void getColors_LabelWidget_(const iLabelWidget *d, int *bg, int *fg, int } } int colorEscape = none_ColorId; - if (startsWith_String(&d->label, "\r")) { + if (startsWith_String(&d->label, "\v")) { colorEscape = cstr_String(&d->label)[1] - asciiBase_ColorEscape; /* TODO: can be two bytes long */ } if (isHover_Widget(w)) { diff --git a/src/ui/text.c b/src/ui/text.c index 9532e35a..8cf4464e 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -946,10 +946,10 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { prevCh = 0; continue; } - if (ch == '\r') { /* color change */ + if (ch == '\v') { /* color change */ iChar esc = nextChar_(&chPos, args->text.end); int colorNum = args->color; - if (esc == '\r') { /* Extended range. */ + if (esc == '\v') { /* Extended range. */ esc = nextChar_(&chPos, args->text.end) + asciiExtended_ColorEscape; colorNum = esc - asciiBase_ColorEscape; } -- 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') 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 5d517c2f790a38d7fe3c3cc59a1b39fd49e20280 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 18 May 2021 06:57:18 +0300 Subject: SidebarWidget: Don't animate at launch --- src/app.c | 4 ++-- src/ui/sidebarwidget.c | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/app.c b/src/app.c index 27ce93ce..9697ee2b 100644 --- a/src/app.c +++ b/src/app.c @@ -420,8 +420,8 @@ static iBool loadState_App_(iApp *d) { if (deviceType_App() != phone_AppDeviceType) { setWidth_SidebarWidget(sidebar, widths[0]); setWidth_SidebarWidget(sidebar2, widths[1]); - if (flags & 1) postCommand_Root(root, "sidebar.toggle"); - if (flags & 2) postCommand_Root(root, "sidebar2.toggle"); + if (flags & 1) postCommand_Root(root, "sidebar.toggle noanim:1"); + if (flags & 2) postCommand_Root(root, "sidebar2.toggle noanim:1"); } } } diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 436eb250..cb6dab65 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -878,7 +878,8 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * if (arg_Command(cmd) && isVisible_Widget(w)) { return iTrue; } - const iBool isAnimated = (deviceType_App() != phone_AppDeviceType); + const iBool isAnimated = argLabel_Command(cmd, "noanim") == 0 && + (deviceType_App() != phone_AppDeviceType); int visX = 0; if (isVisible_Widget(w)) { visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); @@ -895,6 +896,7 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * } } else if (isAnimated) { + 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); -- cgit v1.2.3 From 71971bf8b9dc4bcdfca62249c35c43be46494e89 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 18 May 2021 08:53:28 +0300 Subject: Root: Updating size of navbar buttons/indicators Not all UI elements were appropriate sized when switching between normal and tight layout. --- src/ui/root.c | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/ui/root.c b/src/ui/root.c index 6cf3f424..8c98bdd3 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -583,20 +583,22 @@ static void updateNavBarSize_(iWidget *navBar) { /* Button sizing. */ if (isNarrow ^ ((flags_Widget(navBar) & tight_WidgetFlag) != 0)) { setFlags_Widget(navBar, tight_WidgetFlag, isNarrow); - iForEach(ObjectList, i, navBar->children) { - iWidget *child = as_Widget(i.object); - setFlags_Widget(child, tight_WidgetFlag, isNarrow); - if (isInstance_Object(i.object, &Class_LabelWidget)) { - iLabelWidget *label = i.object; - updateSize_LabelWidget(label); + iObjectList *lists[] = { + children_Widget(navBar), + children_Widget(findChild_Widget(navBar, "url")), + children_Widget(findChild_Widget(navBar, "url.buttons")), + }; + iForIndices(k, lists) { + iForEach(ObjectList, i, lists[k]) { + iWidget *child = as_Widget(i.object); + setFlags_Widget(child, tight_WidgetFlag, isNarrow); + if (isInstance_Object(i.object, &Class_LabelWidget)) { + iLabelWidget *label = i.object; + updateSize_LabelWidget(label); + } } } updateUrlInputContentPadding_(navBar); - /* Note that InputWidget uses the `tight` flag to adjust its inner padding. */ -// const int embedButtonWidth = width_Widget(findChild_Widget(navBar, "navbar.lock")); -// setContentPadding_InputWidget(findChild_Widget(navBar, "url"), -// embedButtonWidth * 0.75f, -// embedButtonWidth * 0.75f); } if (isPhone) { static const char *buttons[] = { "navbar.back", "navbar.forward", "navbar.sidebar", @@ -620,7 +622,8 @@ static void updateNavBarSize_(iWidget *navBar) { urlBar->rect.size.x = iMini(navBarAvailableSpace_(navBar), 167 * gap_UI); arrange_Widget(navBar); } - refresh_Widget(navBar); + updateMetrics_Root(navBar->root); /* tight flags changed; need to resize URL bar contents */ +// refresh_Widget(navBar); postCommand_Widget(navBar, "layout.changed id:navbar"); } @@ -1086,7 +1089,9 @@ void createUserInterface_Root(iRoot *d) { setNoAutoMinHeight_LabelWidget(pin, iTrue); addChildFlags_Widget(rightEmbed, iClob(pin), - collapse_WidgetFlag | hidden_WidgetFlag | tight_WidgetFlag | frameless_WidgetFlag); + collapse_WidgetFlag | hidden_WidgetFlag | tight_WidgetFlag | + frameless_WidgetFlag); + updateSize_LabelWidget(pin); } iWidget *urlButtons = new_Widget(); setId_Widget(urlButtons, "url.buttons"); -- cgit v1.2.3 From fd8d2634b3349ef3b0fbea10514bb6b247166712 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 18 May 2021 08:53:44 +0300 Subject: LabelWidget: Respect `noAutoMinHeight` --- src/ui/labelwidget.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index a940b0cb..c20be28a 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c @@ -404,7 +404,9 @@ void updateSize_LabelWidget(iLabelWidget *d) { iWidget *w = as_Widget(d); const int64_t flags = flags_Widget(w); const iInt2 size = defaultSize_LabelWidget(d); - w->minSize.y = size.y; /* vertically text must remain visible */ + if (!d->flags.noAutoMinHeight) { + w->minSize.y = size.y; /* vertically text must remain visible */ + } /* Wrapped text implies that width must be defined by arrangement. */ if (!(flags & (fixedWidth_WidgetFlag | wrapText_WidgetFlag))) { w->rect.size.x = size.x; -- cgit v1.2.3 From 8ac8290e282a8ce645abc6fd394e53f1de50ae99 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 18 May 2021 12:31:42 +0300 Subject: DocumentWidget: Run drawing vs. backgrounds Erasing the background of every run should no longer be necessary (with progressive rendering), unless the run can change appearance. --- src/ui/documentwidget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 5dcdace6..541489c1 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -3706,8 +3706,8 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { // &d->paint, addY_I2(bottomLeft_Rect(wideRect), -1), width_Rect(wideRect), frame); // } } - else { - /* Normal background for other runs. */ + else if (run->linkId) { + /* Normal background for runs that may change appearance. */ fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackground_ColorId); } } -- cgit v1.2.3 From ca13d25e1e3dc1b2b7ea865866e9e4a5785f26d1 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 18 May 2021 12:33:10 +0300 Subject: App: Attempt to fix app restore issue Cached content like font glyphs are (were?) not being restored when returning to foreground. --- src/app.c | 3 +++ src/ui/window.c | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/app.c b/src/app.c index cc172139..26670a65 100644 --- a/src/app.c +++ b/src/app.c @@ -1019,6 +1019,9 @@ void processEvents_App(enum iAppEventMode eventMode) { case SDL_APP_LOWMEMORY: clearCache_App_(); break; + case SDL_APP_WILLENTERFOREGROUND: + invalidate_Window(d->window); + break; case SDL_APP_DIDENTERFOREGROUND: gotEvents = iTrue; d->warmupFrames = 5; diff --git a/src/ui/window.c b/src/ui/window.c index abdc363d..6b9956ea 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -566,14 +566,18 @@ iRoot *otherRoot_Window(const iWindow *d, iRoot *root) { return root == d->roots[0] && d->roots[1] ? d->roots[1] : d->roots[0]; } -void invalidate_Window(iWindow *d) { - if (d && !d->isInvalidated) { +static void invalidate_Window_(iWindow *d, iBool forced) { + if (d && (!d->isInvalidated || forced)) { d->isInvalidated = iTrue; resetFonts_Text(); postCommand_App("theme.changed auto:1"); /* forces UI invalidation */ } } +void invalidate_Window(iWindow *d) { + invalidate_Window_(d, iFalse); +} + static iBool isNormalPlacement_Window_(const iWindow *d) { if (d->isDrawFrozen) return iFalse; #if defined (iPlatformApple) @@ -741,14 +745,14 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { return iTrue; case SDL_WINDOWEVENT_RESTORED: updateSize_Window_(d, iTrue); - invalidate_Window(d); + invalidate_Window_(d, iTrue); d->isMinimized = iFalse; postRefresh_App(); return iTrue; case SDL_WINDOWEVENT_MINIMIZED: d->isMinimized = iTrue; return iTrue; -#endif +#endif /* defined (iPlatformDesktop) */ case SDL_WINDOWEVENT_LEAVE: unhover_Widget(); d->isMouseInside = iFalse; @@ -772,7 +776,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) { d->isExposed = iTrue; #if defined (iPlatformMobile) /* Returned to foreground, may have lost buffered content. */ - invalidate_Window(d); + invalidate_Window_(d, iTrue); postCommand_App("window.unfreeze"); #endif return iFalse; -- cgit v1.2.3 From 462e5085392eb78610e9b991ed295f0168a079bb Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 18 May 2021 12:34:29 +0300 Subject: Mobile: Tablet tweaks Heights of InputWidget vs. LabelWidget. The default sizes should be equal, e.g., so the navbar elements align properly. Don't set the input focus automatically when opening a dialog, since the keyboard may cover much of the UI. --- src/app.c | 6 ++++-- src/ui/inputwidget.c | 12 +++++++----- src/ui/labelwidget.c | 2 +- src/ui/sidebarwidget.c | 8 ++++++-- 4 files changed, 18 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/app.c b/src/app.c index 26670a65..73d8f803 100644 --- a/src/app.c +++ b/src/app.c @@ -2164,7 +2164,7 @@ iBool handleCommand_App(const char *cmd) { iWidget *tabs = findWidget_App("doctabs"); #if defined (iPlatformAppleMobile) /* Can't close the last on mobile. */ - if (tabCount_Widget(tabs) == 1) { + if (tabCount_Widget(tabs) == 1 && numRoots_Window(get_Window()) == 1) { postCommand_App("navigate.home"); return iTrue; } @@ -2342,7 +2342,9 @@ iBool handleCommand_App(const char *cmd) { bookmarkTitle_DocumentWidget(doc), siteIcon_GmDocument(document_DocumentWidget(doc))); } - postCommand_App("focus.set id:bmed.title"); + if (deviceType_App() == desktop_AppDeviceType) { + postCommand_App("focus.set id:bmed.title"); + } return iTrue; } else if (equal_Command(cmd, "feeds.subscribe")) { diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 784fabdd..05cd1cc3 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c @@ -148,6 +148,8 @@ iLocalDef iInt2 padding_(void) { return init_I2(gap_UI / 2, gap_UI / 2); } +#define extraPaddingHeight_ (1.25f * gap_UI) + static iRect contentBounds_InputWidget_(const iInputWidget *d) { const iWidget *w = constAs_Widget(d); // const iRect widgetBounds = bounds_Widget(w); @@ -157,7 +159,7 @@ static iRect contentBounds_InputWidget_(const iInputWidget *d) { shrink_Rect(&bounds, init_I2(gap_UI * (flags_Widget(w) & tight_WidgetFlag ? 1 : 2), 0)); bounds.pos.y += padding_().y / 2; if (flags_Widget(w) & extraPadding_WidgetFlag) { - bounds.pos.y += gap_UI; + bounds.pos.y += extraPaddingHeight_ / 2; } return bounds; } @@ -205,7 +207,7 @@ static void updateSizeForFixedLength_InputWidget_(iInputWidget *d) { /* Set a fixed size based on maximum possible width of the text. */ iBlock *content = new_Block(d->maxLen); fill_Block(content, 'M'); - int extraHeight = (flags_Widget(as_Widget(d)) & extraPadding_WidgetFlag ? 2 * gap_UI : 0); + int extraHeight = (flags_Widget(as_Widget(d)) & extraPadding_WidgetFlag ? extraPaddingHeight_ : 0); setFixedSize_Widget( as_Widget(d), add_I2(measure_Text(d->font, cstr_Block(content)), @@ -299,7 +301,7 @@ static int contentHeight_InputWidget_(const iInputWidget *d, iBool forLayout) { if (forLayout) { numLines = iMin(numLines, d->maxLayoutLines); } - return numLines * lineHeight_Text(d->font); + return (int) numLines * lineHeight_Text(d->font); } static void updateMetrics_InputWidget_(iInputWidget *d) { @@ -308,7 +310,7 @@ static void updateMetrics_InputWidget_(iInputWidget *d) { /* Caller must arrange the width, but the height is fixed. */ w->rect.size.y = contentHeight_InputWidget_(d, iTrue) + 3.0f * padding_().y; /* TODO: Why 3x? */ if (flags_Widget(w) & extraPadding_WidgetFlag) { - w->rect.size.y += 2 * gap_UI; + w->rect.size.y += extraPaddingHeight_; } invalidateBuffered_InputWidget_(d); postCommand_Widget(d, "input.resized"); @@ -960,7 +962,7 @@ static iRect bounds_InputWidget_(const iInputWidget *d) { } bounds.size.y = contentHeight_InputWidget_(d, iFalse) + 3 * padding_().y; if (w->flags & extraPadding_WidgetFlag) { - bounds.size.y += 2 * gap_UI; + bounds.size.y += extraPaddingHeight_; } return bounds; } diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index c20be28a..ed023961 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c @@ -66,7 +66,7 @@ static iInt2 padding_LabelWidget_(const iLabelWidget *d, int corner) { #if defined (iPlatformAppleMobile) return add_I2(widgetPad, init_I2(flags & tight_WidgetFlag ? 2 * gap_UI : (4 * gap_UI), - (flags & extraPadding_WidgetFlag ? 1.5f : 1) * 3 * gap_UI / 2)); + (flags & extraPadding_WidgetFlag ? 1.5f : 1.0f) * 3 * gap_UI / 2)); #else return add_I2(widgetPad, init_I2(flags & tight_WidgetFlag ? 3 * gap_UI / 2 : (3 * gap_UI), diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index cb6dab65..86410d11 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -1142,7 +1142,9 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) } if (isCommand_Widget(w, ev, "feed.entry.bookmark")) { makeBookmarkCreation_Widget(&item->url, &item->label, item->icon); - postCommand_App("focus.set id:bmed.title"); + if (deviceType_App() == desktop_AppDeviceType) { + postCommand_App("focus.set id:bmed.title"); + } return iTrue; } iBookmark *feedBookmark = get_Bookmarks(bookmarks_App(), item->id); @@ -1290,7 +1292,9 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) &item->url, collect_String(newRange_String(urlHost_String(&item->url))), 0x1f310 /* globe */); - postCommand_App("focus.set id:bmed.title"); + if (deviceType_App() == desktop_AppDeviceType) { + postCommand_App("focus.set id:bmed.title"); + } } } else if (equal_Command(cmd, "history.clear")) { -- cgit v1.2.3 From b8c07107fa332651cf2281fe8d2f90dfc122b588 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Wed, 19 May 2021 14:20:58 +0300 Subject: Cleanup --- src/ui/widget.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/ui/widget.c b/src/ui/widget.c index 64f586ce..d31f7577 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -323,7 +323,6 @@ static iBool setWidth_Widget_(iWidget *d, int width) { d->rect.size.x = width; TRACE(d, "width has changed to %d", width); if (class_Widget(d)->sizeChanged) { - const int oldHeight = d->rect.size.y; class_Widget(d)->sizeChanged(d); } return iTrue; -- cgit v1.2.3 From 5f9685010addd4a0f04c13f889856084381dd1c6 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 20 May 2021 14:57:09 +0300 Subject: Added GmTypesetter --- CMakeLists.txt | 2 ++ src/defs.h | 6 ++++++ src/gmdocument.c | 29 +++++++++++++++-------------- src/gmdocument.h | 9 ++------- src/gmtypesetter.c | 25 +++++++++++++++++++++++++ src/gmtypesetter.h | 41 +++++++++++++++++++++++++++++++++++++++++ src/ui/documentwidget.c | 14 +++++++------- 7 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 src/gmtypesetter.c create mode 100644 src/gmtypesetter.h (limited to 'src') diff --git a/CMakeLists.txt b/CMakeLists.txt index a1037b0c..edee3d85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,8 @@ set (SOURCES src/gmdocument.h src/gmrequest.c src/gmrequest.h + src/gmtypesetter.c + src/gmtypesetter.h src/gmutil.c src/gmutil.h src/gopher.c diff --git a/src/defs.h b/src/defs.h index 71719f7a..0da404bf 100644 --- a/src/defs.h +++ b/src/defs.h @@ -24,6 +24,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "lang.h" +enum iSourceFormat { + undefined_SourceFormat = -1, + gemini_SourceFormat = 0, + plainText_SourceFormat, +}; + enum iFileVersion { initial_FileVersion = 0, addedResponseTimestamps_FileVersion = 1, diff --git a/src/gmdocument.c b/src/gmdocument.c index 67adb9cc..f8d41172 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -21,6 +21,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gmdocument.h" +#include "gmtypesetter.h" #include "gmutil.h" #include "lang.h" #include "ui/color.h" @@ -73,7 +74,7 @@ iDefineTypeConstruction(GmLink) struct Impl_GmDocument { iObject object; - enum iGmDocumentFormat format; + enum iSourceFormat format; iString unormSource; /* unnormalized source */ iString source; /* normalized source */ iString url; /* for resolving relative links */ @@ -95,7 +96,7 @@ struct Impl_GmDocument { iDefineObjectConstruction(GmDocument) static enum iGmLineType lineType_GmDocument_(const iGmDocument *d, const iRangecc line) { - if (d->format == plainText_GmDocumentFormat) { + if (d->format == plainText_SourceFormat) { return text_GmLineType; } return lineType_Rangecc(line); @@ -305,7 +306,7 @@ static void linkContentWasLaidOut_GmDocument_(iGmDocument *d, const iGmMediaInfo static iBool isNormalized_GmDocument_(const iGmDocument *d) { const iPrefs *prefs = prefs_App(); - if (d->format == plainText_GmDocumentFormat) { + if (d->format == plainText_SourceFormat) { return iTrue; /* tabs are always normalized in plain text */ } if (startsWithCase_String(&d->url, "gemini:") && prefs->monospaceGemini) { @@ -433,7 +434,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { enum iGmLineType prevType = text_GmLineType; enum iGmLineType prevNonBlankType = text_GmLineType; iBool followsBlank = iFalse; - if (d->format == plainText_GmDocumentFormat) { + if (d->format == plainText_SourceFormat) { isPreformat = iTrue; isFirstText = iFalse; } @@ -502,14 +503,14 @@ static void doLayout_GmDocument_(iGmDocument *d) { if (contentLine.start == content.start) { prevType = type; } - if (d->format == gemini_GmDocumentFormat && + if (d->format == gemini_SourceFormat && startsWithSc_Rangecc(line, "```", &iCaseSensitive)) { isPreformat = iFalse; addSiteBanner = iFalse; /* overrides the banner */ continue; } run.preId = preId; - run.font = (d->format == plainText_GmDocumentFormat ? regularMonospace_FontId : preFont); + run.font = (d->format == plainText_SourceFormat ? regularMonospace_FontId : preFont); indent = indents[type]; } if (addSiteBanner) { @@ -582,7 +583,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { } } /* Folded blocks are represented by a single run with the alt text. */ - if (isPreformat && d->format != plainText_GmDocumentFormat) { + if (isPreformat && d->format != plainText_SourceFormat) { const iGmPreMeta *meta = constAt_Array(&d->preMeta, preId - 1); if (meta->flags & folded_GmPreMetaFlag) { const iBool isBlank = isEmpty_Range(&meta->altText); @@ -678,7 +679,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { pushBack_Array(&d->layout, &icon); } run.color = colors[type]; - if (d->format == plainText_GmDocumentFormat) { + if (d->format == plainText_SourceFormat) { run.color = colors[text_GmLineType]; } /* Special formatting for the first paragraph (e.g., subtitle, introduction, or lede). */ @@ -707,8 +708,8 @@ static void doLayout_GmDocument_(iGmDocument *d) { type == quote_GmLineType ? 4 : 0); } const iBool isWordWrapped = - (d->format == plainText_GmDocumentFormat ? prefs->plainTextWrap : !isPreformat); - if (isPreformat && d->format != plainText_GmDocumentFormat) { + (d->format == plainText_SourceFormat ? prefs->plainTextWrap : !isPreformat); + if (isPreformat && d->format != plainText_SourceFormat) { /* Remember the top left coordinates of the block (first line of block). */ iGmPreMeta *meta = at_Array(&d->preMeta, preId - 1); if (~meta->flags & topLeft_GmPreMetaFlag) { @@ -866,7 +867,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { } void init_GmDocument(iGmDocument *d) { - d->format = gemini_GmDocumentFormat; + d->format = gemini_SourceFormat; init_String(&d->unormSource); init_String(&d->source); init_String(&d->url); @@ -1397,7 +1398,7 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) { #endif } -void setFormat_GmDocument(iGmDocument *d, enum iGmDocumentFormat format) { +void setFormat_GmDocument(iGmDocument *d, enum iSourceFormat format) { d->format = format; } @@ -1439,7 +1440,7 @@ static void normalize_GmDocument(iGmDocument *d) { iRangecc src = range_String(&d->source); iRangecc line = iNullRange; iBool isPreformat = iFalse; - if (d->format == plainText_GmDocumentFormat) { + if (d->format == plainText_SourceFormat) { isPreformat = iTrue; /* Cannot be turned off. */ } const int preTabWidth = 4; /* TODO: user-configurable parameter */ @@ -1467,7 +1468,7 @@ static void normalize_GmDocument(iGmDocument *d) { } } appendCStr_String(normalized, "\n"); - if (d->format == gemini_GmDocumentFormat && + if (d->format == gemini_SourceFormat && lineType_GmDocument_(d, line) == preformatted_GmLineType) { isPreformat = iFalse; } diff --git a/src/gmdocument.h b/src/gmdocument.h index 1e54a16a..5137bb28 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h @@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once +#include "defs.h" #include "gmutil.h" #include "media.h" @@ -148,12 +149,6 @@ iRangecc findLoc_GmRun (const iGmRun *, iInt2 pos); iDeclareClass(GmDocument) iDeclareObjectConstruction(GmDocument) -enum iGmDocumentFormat { - undefined_GmDocumentFormat = -1, - gemini_GmDocumentFormat = 0, - plainText_GmDocumentFormat, -}; - enum iGmDocumentBanner { none_GmDocumentBanner, siteDomain_GmDocumentBanner, @@ -166,7 +161,7 @@ enum iGmDocumentUpdate { }; void setThemeSeed_GmDocument (iGmDocument *, const iBlock *seed); -void setFormat_GmDocument (iGmDocument *, enum iGmDocumentFormat format); +void setFormat_GmDocument (iGmDocument *, enum iSourceFormat format); void setBanner_GmDocument (iGmDocument *, enum iGmDocumentBanner type); void setWidth_GmDocument (iGmDocument *, int width); void redoLayout_GmDocument (iGmDocument *); diff --git a/src/gmtypesetter.c b/src/gmtypesetter.c new file mode 100644 index 00000000..29a1bd93 --- /dev/null +++ b/src/gmtypesetter.c @@ -0,0 +1,25 @@ +/* Copyright 2021 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "gmtypesetter.h" +#include "gmdocument.h" + diff --git a/src/gmtypesetter.h b/src/gmtypesetter.h new file mode 100644 index 00000000..aba351dd --- /dev/null +++ b/src/gmtypesetter.h @@ -0,0 +1,41 @@ +/* Copyright 2021 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#pragma once + +#include "defs.h" + +#include +#include +#include + +/* GmTypesetter has two jobs: it normalizes incoming source text, and typesets it as a + sequence of GmRuns. New data can be appended progressively. */ + +iDeclareType(GmTypesetter) +iDeclareTypeConstruction(GmTypesetter) + +void reset_GmTypesetter (iGmTypesetter *, enum iSourceFormat format); +void setWidth_GmTypesetter (iGmTypesetter *, int width); +void addInput_GmTypesetter (iGmTypesetter *, const iString *source); +iBool getRuns_GmTypesetter (iGmTypesetter *, iArray *runs_out); /* returns false when no output generated */ +void skip_GmTypesetter (iGmTypesetter *, int ySkip); diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 9169ea06..94337f8d 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -1076,7 +1076,7 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode } } setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); - setFormat_GmDocument(d->doc, gemini_GmDocumentFormat); + setFormat_GmDocument(d->doc, gemini_SourceFormat); translate_Lang(src); d->state = ready_RequestState; setSource_DocumentWidget(d, src); @@ -1214,7 +1214,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, if (isSuccess_GmStatusCode(statusCode)) { /* Check the MIME type. */ iRangecc charset = range_CStr("utf-8"); - enum iGmDocumentFormat docFormat = undefined_GmDocumentFormat; + enum iSourceFormat docFormat = undefined_SourceFormat; const iString *mimeStr = collect_String(lower_String(&response->meta)); /* for convenience */ set_String(&d->sourceMime, mimeStr); iRangecc mime = range_String(mimeStr); @@ -1223,18 +1223,18 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, iRangecc param = seg; trim_Rangecc(¶m); if (equal_Rangecc(param, "text/gemini")) { - docFormat = gemini_GmDocumentFormat; + docFormat = gemini_SourceFormat; setRange_String(&d->sourceMime, param); } else if (startsWith_Rangecc(param, "text/") || equal_Rangecc(param, "application/json")) { - docFormat = plainText_GmDocumentFormat; + docFormat = plainText_SourceFormat; setRange_String(&d->sourceMime, param); } else if (equal_Rangecc(param, "application/zip") || (startsWith_Rangecc(param, "application/") && endsWithCase_Rangecc(param, "+zip"))) { - docFormat = gemini_GmDocumentFormat; + docFormat = gemini_SourceFormat; setRange_String(&d->sourceMime, param); iString *key = collectNew_String(); toString_Sym(SDLK_s, KMOD_PRIMARY, key); @@ -1261,7 +1261,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, startsWith_Rangecc(param, "audio/")) { const iBool isAudio = startsWith_Rangecc(param, "audio/"); /* Make a simple document with an image or audio player. */ - docFormat = gemini_GmDocumentFormat; + docFormat = gemini_SourceFormat; setRange_String(&d->sourceMime, param); const iGmLinkId imgLinkId = 1; /* there's only the one link */ /* TODO: Do the image loading in `postProcessRequestContent_DocumentWidget_()` */ @@ -1306,7 +1306,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, } } } - if (docFormat == undefined_GmDocumentFormat) { + if (docFormat == undefined_SourceFormat) { showErrorPage_DocumentWidget_(d, unsupportedMimeType_GmStatusCode, &response->meta); deinit_String(&str); return; -- cgit v1.2.3 From d03b4bb3b567de4501915f4236280b33dc385103 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Wed, 9 Jun 2021 17:45:10 +0300 Subject: CertImportWidget: Respond to "input.paste" The "input.paste" command is triggered via menus before CertImportWidget gets the key. --- res/about/version.gmi | 1 + src/ui/certimportwidget.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'src') diff --git a/res/about/version.gmi b/res/about/version.gmi index c48a16d0..0a51df34 100644 --- a/res/about/version.gmi +++ b/res/about/version.gmi @@ -7,6 +7,7 @@ # Release notes ## 1.5.2 +* Fixed pasting a PEM-formatted certificate and/or private key via clipboard in Import Identity. * Normalize page contents (NFC) to avoid most common issues with diacritics. ## 1.5.1 diff --git a/src/ui/certimportwidget.c b/src/ui/certimportwidget.c index e121b4d0..6e818137 100644 --- a/src/ui/certimportwidget.c +++ b/src/ui/certimportwidget.c @@ -215,6 +215,14 @@ static iBool processEvent_CertImportWidget_(iCertImportWidget *d, const SDL_Even return iTrue; } } + if (isCommand_UserEvent(ev, "input.paste")) { + if (!tryImportFromClipboard_CertImportWidget_(d)) { + makeSimpleMessage_Widget(uiTextCaution_ColorEscape "${heading.certimport.pasted}", + "${dlg.certimport.notfound}"); + } + postRefresh_App(); + return iTrue; + } if (isCommand_UserEvent(ev, "certimport.paste")) { tryImportFromClipboard_CertImportWidget_(d); return iTrue; -- cgit v1.2.3 From fdfd11b8a1a2d00c850039c8237208010236f765 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 10 Jun 2021 06:33:05 +0300 Subject: DocumentWidget: Pause audio when document changes While document media is now kept in memory even in history, it's quite strange if audio players keep playing on a page viewed in the past without a way to control the playback. --- src/media.c | 9 +++++++++ src/media.h | 1 + src/ui/documentwidget.c | 6 +++++- 3 files changed, 15 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/media.c b/src/media.c index 2ec2109d..180c28e6 100644 --- a/src/media.c +++ b/src/media.c @@ -469,6 +469,15 @@ iPlayer *audioPlayer_Media(const iMedia *d, iMediaId audioId) { return NULL; } +void pauseAllPlayers_Media(const iMedia *d, iBool setPaused) { + for (size_t i = 0; i < size_PtrArray(&d->audio); ++i) { + const iGmAudio *audio = constAt_PtrArray(&d->audio, i); + if (audio->player) { + setPaused_Player(audio->player, setPaused); + } + } +} + iBool downloadInfo_Media(const iMedia *d, iMediaId downloadId, iGmMediaInfo *info_out) { if (downloadId > 0 && downloadId <= size_PtrArray(&d->downloads)) { const iGmDownload *dl = constAt_PtrArray(&d->downloads, downloadId - 1); diff --git a/src/media.h b/src/media.h index c6973b79..f7ad6efd 100644 --- a/src/media.h +++ b/src/media.h @@ -61,6 +61,7 @@ size_t numAudio_Media (const iMedia *); iMediaId findLinkAudio_Media (const iMedia *, uint16_t linkId); iBool audioInfo_Media (const iMedia *, iMediaId audioId, iGmMediaInfo *info_out); iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId); +void pauseAllPlayers_Media(const iMedia *, iBool setPaused); iMediaId findLinkDownload_Media (const iMedia *, uint16_t linkId); iBool downloadInfo_Media (const iMedia *, iMediaId downloadId, iGmMediaInfo *info_out); diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 29e264e8..a3ac208a 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -392,6 +392,7 @@ void init_DocumentWidget(iDocumentWidget *d) { } void deinit_DocumentWidget(iDocumentWidget *d) { + pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); removeTicker_App(animate_DocumentWidget_, d); removeTicker_App(prerender_DocumentWidget_, d); remove_Periodic(periodic_App(), d); @@ -1046,6 +1047,7 @@ void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { } static void replaceDocument_DocumentWidget_(iDocumentWidget *d, iGmDocument *newDoc) { + pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); iRelease(d->doc); d->doc = ref_Object(newDoc); documentWasChanged_DocumentWidget_(d); @@ -1581,6 +1583,7 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n clear_ObjectList(d->media); delete_Gempub(d->sourceGempub); d->sourceGempub = NULL; + pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); iRelease(d->doc); destroy_Widget(d->footerButtons); d->footerButtons = NULL; @@ -1867,6 +1870,7 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) { /* Keep scroll position when reloading the same page. */ reset_SmoothScroll(&d->scrollY); } + pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); iRelease(d->doc); /* new content incoming */ d->doc = new_GmDocument(); delete_Gempub(d->sourceGempub); @@ -4673,7 +4677,7 @@ void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, cons initCurrent_Time(&resp->when); set_String(&resp->meta, mime); set_Block(&resp->body, source); - updateFromCachedResponse_DocumentWidget_(d, 0, resp); + updateFromCachedResponse_DocumentWidget_(d, 0, resp, NULL); delete_GmResponse(resp); } -- cgit v1.2.3 From 99044467586a5a37c796a7786834359dfaf30a2c Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 10 Jun 2021 07:08:30 +0300 Subject: Media: Estimate memory use In-memory images, audio, and downloads are included in RAM usage in Debug Information. --- src/audio/player.c | 7 +++++++ src/audio/player.h | 1 + src/media.c | 25 +++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/audio/player.c b/src/audio/player.c index 9e026561..a2a3955b 100644 --- a/src/audio/player.c +++ b/src/audio/player.c @@ -724,6 +724,13 @@ void updateSourceData_Player(iPlayer *d, const iString *mimeType, const iBlock * unlock_Mutex(&input->mtx); } +size_t sourceDataSize_Player(const iPlayer *d) { + lock_Mutex(&d->data->mtx); + const size_t size = size_Block(&d->data->data); + unlock_Mutex(&d->data->mtx); + return size; +} + iBool start_Player(iPlayer *d) { if (isStarted_Player(d)) { return iFalse; diff --git a/src/audio/player.h b/src/audio/player.h index 8753d811..b131838d 100644 --- a/src/audio/player.h +++ b/src/audio/player.h @@ -48,6 +48,7 @@ enum iPlayerTag { void updateSourceData_Player (iPlayer *, const iString *mimeType, const iBlock *data, enum iPlayerUpdate update); +size_t sourceDataSize_Player (const iPlayer *); iBool start_Player (iPlayer *); void stop_Player (iPlayer *); diff --git a/src/media.c b/src/media.c index 180c28e6..eb4a8311 100644 --- a/src/media.c +++ b/src/media.c @@ -24,6 +24,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gmdocument.h" #include "gmrequest.h" #include "ui/window.h" +#include "ui/paint.h" /* size_SDLTexture */ #include "audio/player.h" #include "app.h" #include "stb_image.h" @@ -262,8 +263,28 @@ void clear_Media(iMedia *d) { } size_t memorySize_Media(const iMedia *d) { - /* TODO: Calculate the actual memory use. */ - return 0; + size_t memSize = 0; + iConstForEach(PtrArray, i, &d->images) { + const iGmImage *img = i.ptr; + if (img->texture) { + const iInt2 texSize = size_SDLTexture(img->texture); + memSize += 4 * texSize.x * texSize.y; /* RGBA */ + } + else { + memSize += size_Block(&img->partialData); + } + } + iConstForEach(PtrArray, a, &d->audio) { + const iGmAudio *audio = a.ptr; + if (audio->player) { + memSize += sourceDataSize_Player(audio->player); + } + } + iConstForEach(PtrArray, n, &d->downloads) { + const iGmDownload *down = n.ptr; + memSize += down->numBytes; + } + return memSize; } iBool setDownloadUrl_Media(iMedia *d, iGmLinkId linkId, const iString *url) { -- cgit v1.2.3 From 77ecd8cb2fec2c61f37f4c5561b18fad6fe6137a Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 10 Jun 2021 12:03:10 +0300 Subject: Tight mode for Query/Feeds indicator --- po/en.po | 6 ++++++ res/lang/de.bin | Bin 20699 -> 20768 bytes res/lang/en.bin | Bin 19404 -> 19473 bytes res/lang/es.bin | Bin 21778 -> 21847 bytes res/lang/fi.bin | Bin 21901 -> 21970 bytes res/lang/fr.bin | Bin 22311 -> 22380 bytes res/lang/ia.bin | Bin 21609 -> 21678 bytes res/lang/ie.bin | Bin 21167 -> 21236 bytes res/lang/pl.bin | Bin 22415 -> 22484 bytes res/lang/ru.bin | Bin 32646 -> 32715 bytes res/lang/sr.bin | Bin 32051 -> 32120 bytes res/lang/tok.bin | Bin 19792 -> 19861 bytes res/lang/zh_Hans.bin | Bin 18568 -> 18637 bytes res/lang/zh_Hant.bin | Bin 18753 -> 18822 bytes src/app.c | 8 ++++++-- src/ui/labelwidget.c | 5 +++++ src/ui/labelwidget.h | 2 ++ src/ui/root.c | 16 +++++++++++----- src/ui/util.c | 4 +++- 19 files changed, 33 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/po/en.po b/po/en.po index 8fe6b96d..bd7e97bf 100644 --- a/po/en.po +++ b/po/en.po @@ -392,6 +392,9 @@ msgstr "Find text on page" msgid "status.query" msgstr "Search Query" +msgid "status.query.tight" +msgstr "Query" + msgid "status.feeds" msgstr "Updating Feeds" @@ -1228,6 +1231,9 @@ msgstr "On Light" msgid "prefs.userfont" msgstr "Symbol font:" +msgid "hint.prefs.userfont" +msgstr "path of a TrueType font" + msgid "prefs.linewidth" msgstr "Line width:" diff --git a/res/lang/de.bin b/res/lang/de.bin index b89c8b0f..95d172cc 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 0ac42d88..d5cb1c2c 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 8f3940f1..4b567393 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 d19dc0e8..d1ab756b 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 0b78f3b7..5561640c 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 e6590afc..da272056 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 f852ba0e..3d77d90e 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 9f42024c..c09c6bef 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 5c9e3488..e344e992 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 f4239f1f..6a860e79 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 1a6b5baf..27bfdb49 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 b155036d..077ba1e3 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 57ce2d83..4d9a984b 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 71cff954..1ea33dd4 100644 --- a/src/app.c +++ b/src/app.c @@ -2503,11 +2503,15 @@ iBool handleCommand_App(const char *cmd) { return iTrue; } else if (equal_Command(cmd, "feeds.update.started")) { - showCollapsed_Widget(findWidget_App("feeds.progress"), iTrue); + iAnyObject *prog = findWidget_Root("feeds.progress"); + const iWidget *navBar = findWidget_Root("navbar"); + updateTextAndResizeWidthCStr_LabelWidget( + prog, flags_Widget(navBar) & tight_WidgetFlag ? "\u2605" : "\u2605 ${status.feeds}"); + showCollapsed_Widget(prog, iTrue); return iFalse; } else if (equal_Command(cmd, "feeds.update.finished")) { - showCollapsed_Widget(findWidget_App("feeds.progress"), iFalse); + showCollapsed_Widget(findWidget_Root("feeds.progress"), iFalse); refreshFinished_Feeds(); postRefresh_App(); return iFalse; diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index ed023961..febaa15b 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c @@ -495,6 +495,11 @@ void updateTextCStr_LabelWidget(iLabelWidget *d, const char *text) { refresh_Widget(&d->widget); } +void updateTextAndResizeWidthCStr_LabelWidget(iLabelWidget *d, const char *text) { + updateTextCStr_LabelWidget(d, text); + d->widget.rect.size.x = defaultSize_LabelWidget(d).x; +} + void setTextCStr_LabelWidget(iLabelWidget *d, const char *text) { setCStr_String(&d->label, text); set_String(&d->srcLabel, &d->label); diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h index e38a1dc8..25cde95c 100644 --- a/src/ui/labelwidget.h +++ b/src/ui/labelwidget.h @@ -43,6 +43,8 @@ void updateSize_LabelWidget (iLabelWidget *); void updateText_LabelWidget (iLabelWidget *, const iString *text); /* not resized */ void updateTextCStr_LabelWidget (iLabelWidget *, const char *text); /* not resized */ +void updateTextAndResizeWidthCStr_LabelWidget (iLabelWidget *, const char *text); + iInt2 defaultSize_LabelWidget (const iLabelWidget *); int font_LabelWidget (const iLabelWidget *); const iString * text_LabelWidget (const iLabelWidget *); diff --git a/src/ui/root.c b/src/ui/root.c index 15548e74..23a7d670 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -551,9 +551,15 @@ static void updateUrlInputContentPadding_(iWidget *navBar) { } static void showSearchQueryIndicator_(iBool show) { + iWidget *navBar = findWidget_Root("navbar"); iWidget *indicator = findWidget_App("input.indicator.search"); + updateTextCStr_LabelWidget((iLabelWidget *) indicator, + flags_Widget(navBar) & tight_WidgetFlag + ? "${status.query.tight} " return_Icon + : "${status.query} " return_Icon); + indicator->rect.size.x = defaultSize_LabelWidget((iLabelWidget *) indicator).x; /* don't touch height */ showCollapsed_Widget(indicator, show); - updateUrlInputContentPadding_(findWidget_Root("navbar")); + updateUrlInputContentPadding_(navBar); } static int navBarAvailableSpace_(iWidget *navBar) { @@ -1048,9 +1054,9 @@ void createUserInterface_Root(iRoot *d) { resizeHeightOfChildren_WidgetFlag | moveToParentRightEdge_WidgetFlag); /* Feeds refresh indicator is inside the input field. */ { - iLabelWidget *queryInd = - new_LabelWidget(uiTextAction_ColorEscape "${status.query} " return_Icon, NULL); + iLabelWidget *queryInd = new_LabelWidget("${status.query} " return_Icon, NULL); setId_Widget(as_Widget(queryInd), "input.indicator.search"); + setTextColor_LabelWidget(queryInd, uiTextAction_ColorId); setBackgroundColor_Widget(as_Widget(queryInd), uiBackground_ColorId); setFrameColor_Widget(as_Widget(queryInd), uiTextAction_ColorId); setAlignVisually_LabelWidget(queryInd, iTrue); @@ -1060,9 +1066,9 @@ void createUserInterface_Root(iRoot *d) { collapse_WidgetFlag | hidden_WidgetFlag); } /* Feeds refresh indicator is inside the input field. */ { - iLabelWidget *fprog = new_LabelWidget(uiTextCaution_ColorEscape - "\u2605 ${status.feeds}", NULL); + iLabelWidget *fprog = new_LabelWidget("", NULL); setId_Widget(as_Widget(fprog), "feeds.progress"); + setTextColor_LabelWidget(fprog, uiTextCaution_ColorId); setBackgroundColor_Widget(as_Widget(fprog), uiBackground_ColorId); setAlignVisually_LabelWidget(fprog, iTrue); setNoAutoMinHeight_LabelWidget(fprog, iTrue); diff --git a/src/ui/util.c b/src/ui/util.c index c4fb8886..6eef544b 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -1789,7 +1789,9 @@ iWidget *makePreferences_Widget(void) { updateSize_LabelWidget((iLabelWidget *) tog); } addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); - addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(new_InputWidget(0))); + iInputWidget *customFont = new_InputWidget(0); + setHint_InputWidget(customFont, "${hint.prefs.userfont}"); + addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(customFont)); } makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}"))); -- 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') 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 4e3ac08e66b54b416b6736d5b5195da87e17bec1 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 10 Jun 2021 14:59:13 +0300 Subject: DocumentWidget: Deleting local files via link context menu --- po/en.po | 12 ++++++++++++ res/lang/de.bin | Bin 20798 -> 20949 bytes res/lang/en.bin | Bin 19503 -> 19654 bytes res/lang/es.bin | Bin 21877 -> 22028 bytes res/lang/fi.bin | Bin 22000 -> 22151 bytes res/lang/fr.bin | Bin 22410 -> 22561 bytes res/lang/ia.bin | Bin 21708 -> 21859 bytes res/lang/ie.bin | Bin 21266 -> 21417 bytes res/lang/pl.bin | Bin 22514 -> 22665 bytes res/lang/ru.bin | Bin 32745 -> 32896 bytes res/lang/sr.bin | Bin 32150 -> 32301 bytes res/lang/tok.bin | Bin 19891 -> 20042 bytes res/lang/zh_Hans.bin | Bin 18667 -> 18818 bytes res/lang/zh_Hant.bin | Bin 18852 -> 19003 bytes src/app.c | 17 +++++++++++++++++ src/ui/documentwidget.c | 14 +++++++++++++- 16 files changed, 42 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/po/en.po b/po/en.po index ae1392e8..5172977a 100644 --- a/po/en.po +++ b/po/en.po @@ -742,6 +742,18 @@ msgstr "Bookmark Link…" msgid "link.download" msgstr "Download Linked File" +msgid "link.file.delete" +msgstr "Delete File" + +msgid "heading.file.delete" +msgstr "DELETE FILE" + +msgid "dlg.file.delete.confirm" +msgstr "Are you sure you want to delete this file?" + +msgid "dlg.file.delete" +msgstr "Delete" + msgid "heading.openlink" msgstr "OPEN LINK" diff --git a/res/lang/de.bin b/res/lang/de.bin index ece02848..ad41666e 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 06781bb4..a107cec4 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 b47e6fae..5ad57bc6 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 95b2108c..5167bea1 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 365a6d8e..dfa5f097 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 b037a7ae..91e5336c 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 6c70c65a..d1cdb189 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 b902baad..b65a2b52 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 74e2e20f..d962ec59 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 576b4b9c..bcd10900 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 97ef6273..8d4963bd 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 bb2c6571..0a5bab01 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 36fc5eb2..53b05911 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 e2e16252..12c7dc7d 100644 --- a/src/app.c +++ b/src/app.c @@ -2303,6 +2303,23 @@ iBool handleCommand_App(const char *cmd) { } setCurrent_Root(oldRoot); } + else if (equal_Command(cmd, "file.delete")) { + const char *path = suffixPtr_Command(cmd, "path"); + if (argLabel_Command(cmd, "confirm")) { + makeQuestion_Widget( + uiHeading_ColorEscape "${heading.file.delete}", + format_CStr("${dlg.file.delete.confirm}\n%s", path), + (iMenuItem[]){ + { "${cancel}", 0, 0, NULL }, + { uiTextCaution_ColorEscape "${dlg.file.delete}", 0, 0, + format_CStr("!file.delete path:%s", path) } }, + 2); + } + else { + remove(path); + } + return iTrue; + } else if (equal_Command(cmd, "document.request.cancelled")) { /* TODO: How should cancelled requests be treated in the history? */ #if 0 diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index a3ac208a..20c0370a 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -3348,6 +3348,18 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e d->contextLink->linkId) }); } } + if (equalCase_Rangecc(scheme, "file")) { + /* Local files may be deleted. */ + pushBack_Array( + &items, + &(iMenuItem){ delete_Icon " " uiTextCaution_ColorEscape + " ${link.file.delete}", + 0, + 0, + format_CStr("!file.delete confirm:1 path:%s", + cstrCollect_String( + localFilePathFromUrl_String(linkUrl))) }); + } } else if (deviceType_App() == desktop_AppDeviceType) { if (!isEmpty_Range(&d->selectMark)) { @@ -4484,7 +4496,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { /* Text markers. */ const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; if (!isEmpty_Range(&d->foundMark) || !isEmpty_Range(&d->selectMark)) { - SDL_Renderer *render = renderer_Window(get_Window()); + SDL_Renderer *render = renderer_Window(get_Window()); ctx.firstMarkRect = zero_Rect(); ctx.lastMarkRect = zero_Rect(); SDL_SetRenderDrawBlendMode(render, -- cgit v1.2.3 From d3030ff846ba3673f96ca142935b2f1e1b954423 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 10 Jun 2021 15:01:41 +0300 Subject: Mobile: Horizontal sidebar animation Also fixing glitch with search input field where pressing Return would insert newline. --- src/app.c | 3 ++- src/app.h | 1 + src/ui/inputwidget.c | 14 +++++++++++--- src/ui/inputwidget.h | 1 + src/ui/root.c | 28 ++++++++++++++++------------ src/ui/sidebarwidget.c | 29 ++++++++++++++++++++--------- 6 files changed, 51 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/app.c b/src/app.c index 12c7dc7d..88d912ab 100644 --- a/src/app.c +++ b/src/app.c @@ -2562,7 +2562,8 @@ iBool handleCommand_App(const char *cmd) { iAnyObject *prog = findWidget_Root("feeds.progress"); const iWidget *navBar = findWidget_Root("navbar"); updateTextAndResizeWidthCStr_LabelWidget( - prog, flags_Widget(navBar) & tight_WidgetFlag ? "\u2605" : "\u2605 ${status.feeds}"); + prog, flags_Widget(navBar) & tight_WidgetFlag || deviceType_App() == phone_AppDeviceType ? + "\u2605" : "\u2605 ${status.feeds}"); showCollapsed_Widget(prog, iTrue); return iFalse; } diff --git a/src/app.h b/src/app.h index 2dd5198d..5d1d42e1 100644 --- a/src/app.h +++ b/src/app.h @@ -86,6 +86,7 @@ uint32_t elapsedSinceLastTicker_App (void); /* milliseconds */ iBool isLandscape_App (void); iLocalDef iBool isPortrait_App (void) { return !isLandscape_App(); } enum iAppDeviceType deviceType_App (void); +iLocalDef iBool isPortraitPhone_App (void) { return isPortrait_App() && deviceType_App() == phone_AppDeviceType; } iGmCerts * certs_App (void); iVisited * visited_App (void); iBookmarks * bookmarks_App (void); diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index cf128017..9571a97e 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c @@ -80,6 +80,7 @@ enum iInputWidgetFlag { markWords_InputWidgetFlag = iBit(8), needUpdateBuffer_InputWidgetFlag = iBit(9), enterKeyEnabled_InputWidgetFlag = iBit(10), + enterKeyInsertsLineFeed_InputWidgetFlag = iBit(11), }; /*----------------------------------------------------------------------------------------------*/ @@ -347,8 +348,11 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) { d->lastCursor = 0; d->cursorLine = 0; d->lastUpdateWidth = 0; - d->verticalMoveX = -1; /* TODO: Use this. */ - d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag; + d->verticalMoveX = -1; /* TODO: Use this. */ + d->inFlags = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag; + if (deviceType_App() != desktop_AppDeviceType) { + d->inFlags |= enterKeyInsertsLineFeed_InputWidgetFlag; + } iZap(d->mark); setMaxLen_InputWidget(d, maxLen); d->maxLayoutLines = iInvalidSize; @@ -464,6 +468,10 @@ void setValidator_InputWidget(iInputWidget *d, iInputWidgetValidatorFunc validat d->validatorContext = context; } +void setEnterInsertsLF_InputWidget(iInputWidget *d, iBool enterInsertsLF) { + iChangeFlags(d->inFlags, enterKeyInsertsLineFeed_InputWidgetFlag, enterInsertsLF); +} + void setEnterKeyEnabled_InputWidget(iInputWidget *d, iBool enterKeyEnabled) { iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled); } @@ -1166,7 +1174,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) { case SDLK_KP_ENTER: if (mods == KMOD_SHIFT || (d->maxLen == 0 && ~d->inFlags & isUrl_InputWidgetFlag && - deviceType_App() != desktop_AppDeviceType)) { + d->inFlags & enterKeyInsertsLineFeed_InputWidgetFlag)) { pushUndo_InputWidget_(d); deleteMarked_InputWidget_(d); insertChar_InputWidget_(d, '\n'); diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h index f8c5bf1e..c70d9ad6 100644 --- a/src/ui/inputwidget.h +++ b/src/ui/inputwidget.h @@ -51,6 +51,7 @@ 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 setEnterInsertsLF_InputWidget (iInputWidget *, iBool enterInsertsLF); void setEnterKeyEnabled_InputWidget (iInputWidget *, iBool enterKeyEnabled); void begin_InputWidget (iInputWidget *); void end_InputWidget (iInputWidget *, iBool accept); diff --git a/src/ui/root.c b/src/ui/root.c index 52bc7364..91e19e46 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -834,20 +834,20 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) { const int viewHeight = size_Root(get_Root()).y; if (arg_Command(cmd) >= 0) { postCommandf_App("sidebar.mode arg:%d show:1", arg_Command(cmd)); - if (!isVisible) { - setVisualOffset_Widget(sidebar, viewHeight, 0, 0); - setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag); - } +// if (!isVisible) { +// setVisualOffset_Widget(sidebar, viewHeight, 0, 0); +// setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag); +// } } else { postCommandf_App("sidebar.toggle"); - if (isVisible) { - setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag); - } - else { - setVisualOffset_Widget(sidebar, viewHeight, 0, 0); - setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag); - } +// if (isVisible) { +// setVisualOffset_Widget(sidebar, height_Widget(sidebar), 250, easeIn_AnimFlag); +// } +// else { +// setVisualOffset_Widget(sidebar, viewHeight, 0, 0); +// setVisualOffset_Widget(sidebar, 0, 400, easeOut_AnimFlag | softer_AnimFlag); +// } } return iTrue; } @@ -855,7 +855,10 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) { /* TODO: Clean this up. */ iWidget *sidebar = findWidget_App("sidebar"); iWidget *sidebar2 = findWidget_App("sidebar2"); - dismissSidebar_(sidebar, "toolbar.view"); + //dismissSidebar_(sidebar, "toolbar.view"); + if (isVisible_Widget(sidebar)) { + postCommandf_App("sidebar.toggle"); + } const iBool isVisible = isVisible_Widget(sidebar2); // setFlags_Widget(findChild_Widget(toolBar, "toolbar.ident"), noBackground_WidgetFlag, // isVisible); @@ -1225,6 +1228,7 @@ void createUserInterface_Root(iRoot *d) { setHint_InputWidget(input, "${hint.findtext}"); setSelectAllOnFocus_InputWidget(input, iTrue); setEatEscape_InputWidget(input, iFalse); /* unfocus and close with one keypress */ + setEnterInsertsLF_InputWidget(input, iFalse); setId_Widget(addChildFlags_Widget(searchBar, iClob(input), expand_WidgetFlag), "find.input"); addChild_Widget(searchBar, iClob(newIcon_LabelWidget(" \u2b9f ", 'g', KMOD_PRIMARY, "find.next"))); diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 27646b22..2e1e138e 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -678,8 +678,8 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { iClob(buttons), arrangeHorizontal_WidgetFlag | resizeWidthOfChildren_WidgetFlag | - arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag | - drawBackgroundToHorizontalSafeArea_WidgetFlag); + arrangeHeight_WidgetFlag | resizeToParentWidth_WidgetFlag); // | +// drawBackgroundToHorizontalSafeArea_WidgetFlag); setBackgroundColor_Widget(buttons, uiBackgroundSidebar_ColorId); } else { @@ -700,13 +700,13 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { setPadding_Widget(as_Widget(d->list), 0, gap_UI, 0, gap_UI); addChildFlags_Widget(listAndActions, iClob(d->list), - expand_WidgetFlag | drawBackgroundToHorizontalSafeArea_WidgetFlag); + expand_WidgetFlag); // | drawBackgroundToHorizontalSafeArea_WidgetFlag); setId_Widget(addChildPosFlags_Widget(listAndActions, iClob(d->actions = new_Widget()), isPhone ? front_WidgetAddPos : back_WidgetAddPos, arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag | - resizeWidthOfChildren_WidgetFlag | - drawBackgroundToHorizontalSafeArea_WidgetFlag), + resizeWidthOfChildren_WidgetFlag), // | +// drawBackgroundToHorizontalSafeArea_WidgetFlag), "actions"); setBackgroundColor_Widget(d->actions, uiBackgroundSidebar_ColorId); d->contextItem = NULL; @@ -938,7 +938,7 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * } const iBool isAnimated = prefs_App()->uiAnimations && argLabel_Command(cmd, "noanim") == 0 && - (deviceType_App() != phone_AppDeviceType); + (d->side == left_SideBarSide || deviceType_App() != phone_AppDeviceType); int visX = 0; if (isVisible_Widget(w)) { visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); @@ -989,6 +989,15 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) if (isVisible_Widget(w)) { postCommand_Widget(w, "sidebar.toggle"); } + setFlags_Widget(findChild_Widget(w, "buttons"), + drawBackgroundToHorizontalSafeArea_WidgetFlag, + isLandscape_App()); + setFlags_Widget(findChild_Widget(w, "actions"), + drawBackgroundToHorizontalSafeArea_WidgetFlag, + isLandscape_App()); + setFlags_Widget(as_Widget(d->list), + drawBackgroundToHorizontalSafeArea_WidgetFlag, + isLandscape_App()); return iFalse; } } @@ -1528,9 +1537,11 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { const iRect bounds = bounds_Widget(w); iPaint p; init_Paint(&p); - if (flags_Widget(w) & visualOffset_WidgetFlag && - flags_Widget(w) & horizontalOffset_WidgetFlag && isVisible_Widget(w)) { - fillRect_Paint(&p, boundsWithoutVisualOffset_Widget(w), tmBackground_ColorId); + if (deviceType_App() != phone_AppDeviceType) { + if (flags_Widget(w) & visualOffset_WidgetFlag && + flags_Widget(w) & horizontalOffset_WidgetFlag && isVisible_Widget(w)) { + fillRect_Paint(&p, boundsWithoutVisualOffset_Widget(w), tmBackground_ColorId); + } } draw_Widget(w); if (isVisible_Widget(w)) { -- cgit v1.2.3 From 6015ee90776ef748fb83769d01735ff0f5ff42d3 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 11 Jun 2021 12:38:01 +0300 Subject: Extend list of allowed ciphers Ciphers used in the wild. --- src/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/main.c b/src/main.c index 5c15d8f7..b1850283 100644 --- a/src/main.c +++ b/src/main.c @@ -64,7 +64,8 @@ int main(int argc, char **argv) { "ECDHE-ECDSA-AES128-GCM-SHA256:" "ECDHE-RSA-AES256-GCM-SHA384:" "ECDHE-RSA-CHACHA20-POLY1305:" - "ECDHE-RSA-AES128-GCM-SHA256"); + "ECDHE-RSA-AES128-GCM-SHA256:" + "DHE-RSA-AES256-GCM-SHA384"); SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1"); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { -- cgit v1.2.3 From 703d2b41fa299a9f8e24a5f6064c3624c3dedbbb Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 11 Jun 2021 12:38:37 +0300 Subject: Mobile: Minor tweaks --- src/gmdocument.c | 5 +++-- src/ui/documentwidget.c | 1 + src/ui/mobile.c | 2 ++ src/ui/root.c | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/gmdocument.c b/src/gmdocument.c index 56953255..4f4546f2 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -256,9 +256,10 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li if ((len = decodeBytes_MultibyteChar(desc.start, desc.end, &icon)) > 0) { if (desc.start + len < desc.end && (isPictograph_Char(icon) || isEmoji_Char(icon) || + icon == 0x2139 /* info */ || icon == 0x2191 /* up arrow */ || - icon == 0x2a2f /* close X */ || - icon == 0x2022 /* bullet */) && + icon == 0x2022 /* bullet */ || + icon == 0x2a2f /* close X */) && !isFitzpatrickType_Char(icon)) { link->flags |= iconFromLabel_GmLinkFlag; link->labelIcon = (iRangecc){ desc.start, desc.start + len }; diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 20c0370a..936c3cb7 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -2374,6 +2374,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) msg, "%s\n", formatCStrs_Lang("num.bytes.n", size_Block(&d->sourceContent))); } } + /* TODO: On mobile, omit the CA status. */ appendFormat_String( msg, "\n%s${pageinfo.cert.status}\n" diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 263fc141..580cc355 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c @@ -201,6 +201,8 @@ static iBool isTwoColumnPage_(iWidget *d) { static iBool isOmittedPref_(const iString *id) { static const char *omittedPrefs[] = { + "prefs.userfont", + "prefs.animate", "prefs.smoothscroll", "prefs.imageloadscroll", "prefs.pinsplit", diff --git a/src/ui/root.c b/src/ui/root.c index 91e19e46..52b6829c 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -1126,7 +1126,7 @@ void createUserInterface_Root(iRoot *d) { setId_Widget(as_Widget(pageMenuButton), "pagemenubutton"); setFont_LabelWidget(pageMenuButton, uiContentBold_FontId); setAlignVisually_LabelWidget(pageMenuButton, iTrue); - addChildFlags_Widget(urlButtons, iClob(pageMenuButton), embedFlags); + addChildFlags_Widget(urlButtons, iClob(pageMenuButton), embedFlags | tight_WidgetFlag); updateSize_LabelWidget(pageMenuButton); } /* Reload button. */ { -- cgit v1.2.3 From 31f7eafd9c6897cdf0ee7d6eeaade9dcc65cb006 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 11 Jun 2021 12:39:28 +0300 Subject: iOS: Opening a file via document picker Use a native file picker to open files. Declare support for .gmi/.gemini files so they can be opened via Files and share sheets. --- po/en.po | 3 + res/iOSBundleInfo.plist.in | 246 +++++++++++++++++++++++++-------------------- res/lang/de.bin | Bin 20949 -> 20976 bytes res/lang/en.bin | Bin 19654 -> 19681 bytes res/lang/es.bin | Bin 22028 -> 22055 bytes res/lang/fi.bin | Bin 22151 -> 22178 bytes res/lang/fr.bin | Bin 22561 -> 22588 bytes res/lang/ia.bin | Bin 21859 -> 21886 bytes res/lang/ie.bin | Bin 21417 -> 21444 bytes res/lang/pl.bin | Bin 22665 -> 22692 bytes res/lang/ru.bin | Bin 32896 -> 32923 bytes res/lang/sr.bin | Bin 32301 -> 32328 bytes res/lang/tok.bin | Bin 20042 -> 20069 bytes res/lang/zh_Hans.bin | Bin 18818 -> 18845 bytes res/lang/zh_Hant.bin | Bin 19003 -> 19030 bytes src/app.c | 13 +++ src/ios.h | 1 + src/ios.m | 26 ++++- src/ui/root.c | 2 + 19 files changed, 182 insertions(+), 109 deletions(-) (limited to 'src') diff --git a/po/en.po b/po/en.po index 5172977a..29eadd94 100644 --- a/po/en.po +++ b/po/en.po @@ -468,6 +468,9 @@ msgstr "Open in New Tab" msgid "menu.opentab.background" msgstr "Open in Background Tab" +msgid "menu.openfile" +msgstr "Open File…" + msgid "menu.edit" msgstr "Edit…" diff --git a/res/iOSBundleInfo.plist.in b/res/iOSBundleInfo.plist.in index 1cf0a303..f47df7cd 100644 --- a/res/iOSBundleInfo.plist.in +++ b/res/iOSBundleInfo.plist.in @@ -1,116 +1,148 @@ - - CFBundleDevelopmentRegion - English - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleGetInfoString - ${MACOSX_BUNDLE_INFO_STRING} - CFBundleIconName - ${MACOSX_BUNDLE_ICON_FILE} - CFBundleIdentifier - ${MACOSX_BUNDLE_GUI_IDENTIFIER} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${MACOSX_BUNDLE_BUNDLE_NAME} - CFBundlePackageType - APPL - CFBundleVersion - ${MACOSX_BUNDLE_BUNDLE_VERSION} - CFBundleShortVersionString - ${MACOSX_BUNDLE_SHORT_VERSION_STRING} - CFBundleSignature - ???? - CSResourcesFileMapped - - NSHumanReadableCopyright - ${MACOSX_BUNDLE_COPYRIGHT} - NSHighResolutionCapable - - NSRequiresAquaSystemAppearance - - NSSupportsAutomaticGraphicsSwitching - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UILaunchStoryboardName - LaunchScreen + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconName + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CSResourcesFileMapped + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSHighResolutionCapable + + NSRequiresAquaSystemAppearance + + NSSupportsAutomaticGraphicsSwitching + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UILaunchStoryboardName + LaunchScreen UIBackgroundModes - audio + audio ITSAppUsesNonExemptEncryption - LSSupportsOpeningDocumentsInPlace - - CFBundleDocumentTypes - - - CFBundleTypeExtensions - - gmi - gemini - - CFBundleTypeIconFile - text-gemini.icns - CFBundleTypeName - Gemini Text File - CFBundleTypeRole - Viewer - LSHandlerRank - Default - LSTypeIsPackage - - - - CFBundleTypeExtensions - - gpub - - CFBundleTypeIconFile - gempub.icns - CFBundleTypeName - Gempub Book - CFBundleTypeRole - Viewer - LSHandlerRank - Default - LSTypeIsPackage - - - - CFBundleURLTypes - - - CFBundleURLName - Gemini - CFBundleURLSchemes - - gemini - - - - CFBundleURLName - Gopher - CFBundleURLSchemes - - gopher - - - - + LSSupportsOpeningDocumentsInPlace + + CFBundleDocumentTypes + + + LSItemContentTypes + + fi.skyjake.lagrange.gemini + public.plain-text + + + + CFBundleTypeExtensions + + gmi + gemini + + CFBundleTypeIconFile + text-gemini.icns + CFBundleTypeName + Gemini Text File + CFBundleTypeRole + Viewer + LSHandlerRank + Default + LSTypeIsPackage + + + + CFBundleTypeExtensions + + gpub + + CFBundleTypeIconFile + gempub.icns + CFBundleTypeName + Gempub Book + CFBundleTypeRole + Viewer + LSHandlerRank + Default + LSTypeIsPackage + + + + CFBundleURLTypes + + + CFBundleURLName + Gemini + CFBundleURLSchemes + + gemini + + + + CFBundleURLName + Gopher + CFBundleURLSchemes + + gopher + + + + UTExportedTypeDeclarations + + + UTTypeIdentifier + fi.skyjake.lagrange.gemini + UTTypeReferenceURL + https://gemini.circumlunar.space/docs/specification.gmi + UTTypeDescription + Gemini Text Document + UTTypeConformsTo + + public.plain-text + + UTTypeTagSpecification + + public.filename-extension + + gmi + gemini + + public.mime-type + text/gemini + + + + diff --git a/res/lang/de.bin b/res/lang/de.bin index ad41666e..1738f4a7 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 a107cec4..54bb4867 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 5ad57bc6..5e0ec4d0 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 5167bea1..a000d152 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 dfa5f097..db440d19 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 91e5336c..c0fb91d9 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 d1cdb189..96f5f2cb 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 b65a2b52..99b6f1c4 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 d962ec59..aec21b35 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 bcd10900..ac4223ac 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 8d4963bd..760b3044 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 0a5bab01..cc8543b2 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 53b05911..2ef3b8a9 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 88d912ab..73908a09 100644 --- a/src/app.c +++ b/src/app.c @@ -2303,6 +2303,19 @@ iBool handleCommand_App(const char *cmd) { } setCurrent_Root(oldRoot); } + else if (equal_Command(cmd, "file.open")) { + const char *path = suffixPtr_Command(cmd, "path"); + if (path) { + postCommandf_App("open temp:%d url:%s", + argLabel_Command(cmd, "temp"), + makeFileUrl_CStr(path)); + return iTrue; + } +#if defined (iPlatformAppleMobile) + pickFileForOpening_iOS(); +#endif + return iTrue; + } else if (equal_Command(cmd, "file.delete")) { const char *path = suffixPtr_Command(cmd, "path"); if (argLabel_Command(cmd, "confirm")) { diff --git a/src/ios.h b/src/ios.h index 578c85fe..64c0fad1 100644 --- a/src/ios.h +++ b/src/ios.h @@ -35,6 +35,7 @@ void setupWindow_iOS (iWindow *window); iBool processEvent_iOS (const SDL_Event *); void playHapticEffect_iOS (enum iHapticEffect effect); void exportDownloadedFile_iOS(const iString *path); +void pickFileForOpening_iOS (void); iBool isPhone_iOS (void); void safeAreaInsets_iOS (float *left, float *top, float *right, float *bottom); diff --git a/src/ios.m b/src/ios.m index a1654df2..e7288677 100644 --- a/src/ios.m +++ b/src/ios.m @@ -181,11 +181,22 @@ static AppState *appState_; - (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentsAtURLs:(NSArray *)urls { - [self removeSavedFile]; + if (fileBeingSaved) { + [self removeSavedFile]; + } + else { + /* A file is being opened. */ + NSURL *url = [urls firstObject]; + iString *path = localFilePathFromUrl_String(collectNewCStr_String([[url absoluteString] + UTF8String])); + postCommandf_App("file.open temp:1 path:%s", cstrCollect_String(path)); + } } - (void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller { - [self removeSavedFile]; + if (fileBeingSaved) { + [self removeSavedFile]; + } } -(void)keyboardOnScreen:(NSNotification *)notification { @@ -335,6 +346,17 @@ void exportDownloadedFile_iOS(const iString *path) { [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil]; } +void pickFileForOpening_iOS(void) { + UIDocumentPickerViewController *picker = [[UIDocumentPickerViewController alloc] + initWithDocumentTypes:@[@"fi.skyjake.lagrange.gemini", + @"public.text", + @"public.image", + @"public.audio"] + inMode:UIDocumentPickerModeImport]; + picker.delegate = appState_; + [viewController_(get_Window()) presentViewController:picker animated:YES completion:nil]; +} + /*----------------------------------------------------------------------------------------------*/ enum iAVFAudioPlayerState { diff --git a/src/ui/root.c b/src/ui/root.c index 52b6829c..0ab969bf 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -90,6 +90,7 @@ static const iMenuItem navMenuItems_[] = { #if defined (iPlatformAppleMobile) /* Tablet menu. */ static const iMenuItem tabletNavMenuItems_[] = { + { folder_Icon " ${menu.openfile}", SDLK_o, KMOD_PRIMARY, "file.open" }, { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, { close_Icon " ${menu.closetab}", 'w', KMOD_PRIMARY, "tabs.close" }, { "---", 0, 0, NULL }, @@ -110,6 +111,7 @@ static const iMenuItem tabletNavMenuItems_[] = { /* Phone menu. */ static const iMenuItem phoneNavMenuItems_[] = { + { folder_Icon " ${menu.openfile}", SDLK_o, KMOD_PRIMARY, "file.open" }, { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" }, { close_Icon " ${menu.closetab}", 'w', KMOD_PRIMARY, "tabs.close" }, { "---", 0, 0, NULL }, -- cgit v1.2.3 From a1fdf46087de8e1719ec80ac1ca145dacfc8e640 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 11 Jun 2021 14:24:48 +0300 Subject: iOS: Audio remote control, Now Playing info Update the basic Now Playing info about the currently playing music, and respond to remote control commands. Seems to work with MP3 but not other audio formats, probably because those are played via custom decoders. There must be some API for updating the playback status manually. --- Depends-iOS.cmake | 1 + src/app.c | 7 +++++ src/audio/player.c | 14 +++++++++ src/audio/player.h | 2 ++ src/ios.h | 3 ++ src/ios.m | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 112 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/Depends-iOS.cmake b/Depends-iOS.cmake index c3747769..e0584c92 100644 --- a/Depends-iOS.cmake +++ b/Depends-iOS.cmake @@ -15,6 +15,7 @@ set (SDL2_LDFLAGS "-framework Foundation" "-framework Foundation" "-framework GameController" + "-framework MediaPlayer" "-framework Metal" "-framework OpenGLES" "-framework QuartzCore" diff --git a/src/app.c b/src/app.c index 73908a09..c2bd3da0 100644 --- a/src/app.c +++ b/src/app.c @@ -1089,6 +1089,13 @@ void processEvents_App(enum iAppEventMode eventMode) { postRefresh_App(); break; case SDL_APP_WILLENTERBACKGROUND: +#if defined (iPlatformAppleMobile) + updateNowPlayingInfo_iOS(); +#endif + setFreezeDraw_Window(d->window, iTrue); + savePrefs_App_(d); + saveState_App_(d); + break; case SDL_APP_TERMINATING: setFreezeDraw_Window(d->window, iTrue); savePrefs_App_(d); diff --git a/src/audio/player.c b/src/audio/player.c index a2a3955b..94bcd065 100644 --- a/src/audio/player.c +++ b/src/audio/player.c @@ -455,6 +455,8 @@ struct Impl_Player { iAVFAudioPlayer * avfPlayer; /* iOS */ }; +static iPlayer *activePlayer_; + iDefineTypeConstruction(Player) static size_t sampleSize_Player_(const iPlayer *d) { @@ -655,8 +657,14 @@ void deinit_Player(iPlayer *d) { #if defined (iPlatformAppleMobile) if (d->avfPlayer) { delete_AVFAudioPlayer(d->avfPlayer); + if (activePlayer_ == d) { + clearNowPlayingInfo_iOS(); + } } #endif + if (activePlayer_ == d) { + activePlayer_ = NULL; + } } iBool isStarted_Player(const iPlayer *d) { @@ -739,6 +747,7 @@ iBool start_Player(iPlayer *d) { if (d->avfPlayer) { play_AVFAudioPlayer(d->avfPlayer); setNotIdle_Player(d); + activePlayer_ = d; return iTrue; } #endif @@ -756,6 +765,7 @@ iBool start_Player(iPlayer *d) { d->decoder->gain = d->volume; SDL_PauseAudioDevice(d->device, SDL_FALSE); setNotIdle_Player(d); + activePlayer_ = d; return iTrue; } @@ -889,3 +899,7 @@ iString *metadataLabel_Player(const iPlayer *d) { } return meta; } + +iPlayer *active_Player(void) { + return activePlayer_; +} diff --git a/src/audio/player.h b/src/audio/player.h index b131838d..ca307dc4 100644 --- a/src/audio/player.h +++ b/src/audio/player.h @@ -68,3 +68,5 @@ float streamProgress_Player (const iPlayer *); /* normalized 0...1 */ uint32_t idleTimeMs_Player (const iPlayer *); iString * metadataLabel_Player (const iPlayer *); + +iPlayer * active_Player (void); diff --git a/src/ios.h b/src/ios.h index 64c0fad1..a8039c1c 100644 --- a/src/ios.h +++ b/src/ios.h @@ -56,3 +56,6 @@ double currentTime_AVFAudioPlayer (const iAVFAudioPlayer *); double duration_AVFAudioPlayer (const iAVFAudioPlayer *); iBool isStarted_AVFAudioPlayer (const iAVFAudioPlayer *); iBool isPaused_AVFAudioPlayer (const iAVFAudioPlayer *); + +void clearNowPlayingInfo_iOS (void); +void updateNowPlayingInfo_iOS (void); diff --git a/src/ios.m b/src/ios.m index e7288677..b50f4ecb 100644 --- a/src/ios.m +++ b/src/ios.m @@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "ios.h" #include "app.h" +#include "audio/player.h" #include "ui/command.h" #include "ui/window.h" @@ -32,9 +33,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include -#import -#import #import +#import +#import +#import static iBool isSystemDarkMode_ = iFalse; static iBool isPhone_ = iFalse; @@ -241,6 +243,55 @@ void setupApplication_iOS(void) { name:UIKeyboardWillHideNotification object:nil]; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; + /* Media player remote controls. */ + MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter]; + [[commandCenter pauseCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { + iPlayer *player = active_Player(); + if (player) { + setPaused_Player(player, iTrue); + return MPRemoteCommandHandlerStatusSuccess; + } + return MPRemoteCommandHandlerStatusCommandFailed; + }]; + [[commandCenter playCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { + iPlayer *player = active_Player(); + if (player) { + if (isPaused_Player(player)) { + setPaused_Player(player, iFalse); + } + else { + start_Player(player); + } + return MPRemoteCommandHandlerStatusSuccess; + } + return MPRemoteCommandHandlerStatusCommandFailed; + }]; + [[commandCenter stopCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { + iPlayer *player = active_Player(); + if (player) { + stop_Player(player); + return MPRemoteCommandHandlerStatusSuccess; + } + return MPRemoteCommandHandlerStatusCommandFailed; + }]; + [[commandCenter togglePlayPauseCommand] addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) { + iPlayer *player = active_Player(); + if (player) { + setPaused_Player(player, !isPaused_Player(player)); + return MPRemoteCommandHandlerStatusSuccess; + } + return MPRemoteCommandHandlerStatusCommandFailed; + }]; + [[commandCenter nextTrackCommand] setEnabled:NO]; + [[commandCenter previousTrackCommand] setEnabled:NO]; + [[commandCenter changeRepeatModeCommand] setEnabled:NO]; + [[commandCenter changeShuffleModeCommand] setEnabled:NO]; + [[commandCenter changePlaybackRateCommand] setEnabled:NO]; + [[commandCenter seekForwardCommand] setEnabled:NO]; + [[commandCenter seekBackwardCommand] setEnabled:NO]; + [[commandCenter skipForwardCommand] setEnabled:NO]; + [[commandCenter skipBackwardCommand] setEnabled:NO]; + [[commandCenter changePlaybackPositionCommand] setEnabled:NO]; } static iBool isDarkMode_(iWindow *window) { @@ -335,6 +386,38 @@ iBool processEvent_iOS(const SDL_Event *ev) { return iFalse; /* allow normal processing */ } +void updateNowPlayingInfo_iOS(void) { + const iPlayer *player = active_Player(); + if (!player) { + clearNowPlayingInfo_iOS(); + return; + } + NSMutableDictionary *info = [[NSMutableDictionary alloc] init]; + [info setObject:[NSNumber numberWithDouble:time_Player(player)] + forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; + [info setObject:[NSNumber numberWithInt:MPNowPlayingInfoMediaTypeAudio] + forKey:MPNowPlayingInfoPropertyMediaType]; + [info setObject:[NSNumber numberWithDouble:duration_Player(player)] + forKey:MPMediaItemPropertyPlaybackDuration]; + const iString *title = tag_Player(player, title_PlayerTag); + const iString *artist = tag_Player(player, artist_PlayerTag); + if (isEmpty_String(title)) { + title = collectNewCStr_String("Audio"); /* TODO: Use link label or URL file name */ + } + if (isEmpty_String(artist)) { + artist = collectNewCStr_String("Lagrange"); /* TODO: Use domain or base URL */ + } + [info setObject:[NSString stringWithUTF8String:cstr_String(title)] + forKey:MPMediaItemPropertyTitle]; + [info setObject:[NSString stringWithUTF8String:cstr_String(artist)] + forKey:MPMediaItemPropertyArtist]; + [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:info]; +} + +void clearNowPlayingInfo_iOS(void) { + [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:nil]; +} + void exportDownloadedFile_iOS(const iString *path) { NSURL *url = [NSURL fileURLWithPath:[[NSString alloc] initWithCString:cstr_String(path) encoding:NSUTF8StringEncoding]]; -- cgit v1.2.3 From d5b8ef63d8c969b8e21129793534161bfb8e9fc0 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sat, 12 Jun 2021 20:01:32 +0300 Subject: Cleanup --- src/defs.h | 4 ++++ src/ui/mediaui.c | 2 +- src/ui/root.c | 10 +++------- src/ui/window.c | 8 ++------ 4 files changed, 10 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/defs.h b/src/defs.h index ff68e7e5..950da394 100644 --- a/src/defs.h +++ b/src/defs.h @@ -42,6 +42,7 @@ enum iFileVersion { /* Icons */ +#define menu_Icon "\U0001d362" #define rightArrowhead_Icon "\u27a4" #define leftArrowhead_Icon "\u27a4" #define warning_Icon "\u26a0" @@ -110,6 +111,9 @@ enum iFileVersion { # define shiftReturn_Icon shift_Icon " " return_Icon #endif +#if defined (iPlatformAppleDesktop) +# define iHaveNativeMenus +#endif /* UI labels that depend on the platform */ diff --git a/src/ui/mediaui.c b/src/ui/mediaui.c index bc417fc3..fa09b214 100644 --- a/src/ui/mediaui.c +++ b/src/ui/mediaui.c @@ -118,7 +118,7 @@ void draw_PlayerUI(iPlayerUI *d, iPaint *p) { isPaused_Player(d->player) ? "\U0001f782" : "\u23f8", uiContent_FontId); drawPlayerButton_(p, d->rewindRect, "\u23ee", uiContent_FontId); - drawPlayerButton_(p, d->menuRect, "\U0001d362", uiContent_FontId); + drawPlayerButton_(p, d->menuRect, menu_Icon, uiContent_FontId); if (!isAdjusting) { drawPlayerButton_( p, d->volumeRect, volumeChar_(volume_Player(d->player)), uiContentSymbols_FontId); diff --git a/src/ui/root.c b/src/ui/root.c index 0ab969bf..76f4c0d7 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -52,10 +52,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -#if defined (iPlatformAppleDesktop) -# define iHaveNativeMenus -#endif - #if defined (iPlatformPcDesktop) /* TODO: Submenus wouldn't hurt here. */ static const iMenuItem navMenuItems_[] = { @@ -1158,11 +1154,11 @@ void createUserInterface_Root(iRoot *d) { #if !defined (iHaveNativeMenus) # if defined (iPlatformAppleMobile) iLabelWidget *navMenu = - makeMenuButton_LabelWidget("\U0001d362", isPhone ? phoneNavMenuItems_ : tabletNavMenuItems_, + makeMenuButton_LabelWidget(menu_Icon, isPhone ? phoneNavMenuItems_ : tabletNavMenuItems_, isPhone ? iElemCount(phoneNavMenuItems_) : iElemCount(tabletNavMenuItems_)); # else iLabelWidget *navMenu = - makeMenuButton_LabelWidget("\U0001d362", navMenuItems_, iElemCount(navMenuItems_)); + makeMenuButton_LabelWidget(menu_Icon, navMenuItems_, iElemCount(navMenuItems_)); # endif setAlignVisually_LabelWidget(navMenu, iTrue); setId_Widget(addChildFlags_Widget(navBar, iClob(navMenu), collapse_WidgetFlag), "navbar.menu"); @@ -1267,7 +1263,7 @@ void createUserInterface_Root(iRoot *d) { iClob(newLargeIcon_LabelWidget(book_Icon, "toolbar.showview arg:-1")), frameless_WidgetFlag | commandOnClick_WidgetFlag), "toolbar.view"); - iLabelWidget *menuButton = makeMenuButton_LabelWidget("\U0001d362", phoneNavMenuItems_, + iLabelWidget *menuButton = makeMenuButton_LabelWidget(menu_Icon, phoneNavMenuItems_, iElemCount(phoneNavMenuItems_)); setFont_LabelWidget(menuButton, uiLabelLarge_FontId); setId_Widget(as_Widget(menuButton), "toolbar.navmenu"); diff --git a/src/ui/window.c b/src/ui/window.c index 96a22fee..f71d8102 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -69,10 +69,6 @@ iDefineTypeConstructionArgs(Window, (iRect rect), rect) /* TODO: Define menus per platform. */ -#if defined (iPlatformAppleDesktop) -# define iHaveNativeMenus -#endif - #if defined (iHaveNativeMenus) /* Using native menus. */ static const iMenuItem fileMenuItems_[] = { @@ -202,7 +198,7 @@ static void windowSizeChanged_Window_(iWindow *d) { } static void setupUserInterface_Window(iWindow *d) { -#if defined (iPlatformAppleDesktop) +#if defined (iHaveNativeMenus) insertMacMenus_(); #endif /* One root is created by default. */ @@ -913,7 +909,7 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) { } } if (isCommand_UserEvent(&event, "lang.changed")) { -#if defined (iPlatformAppleDesktop) +#if defined (iHaveNativeMenus) /* Retranslate the menus. */ removeMacMenus_(); insertMacMenus_(); -- cgit v1.2.3 From 7a27cf99aecfb3112af1d61b908d62d1808f43e5 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sat, 12 Jun 2021 20:02:32 +0300 Subject: Touch: Revising edge swiping events Edge swiping now has its own set of commands that are posted, allowing widgets to respond more flexibly. --- src/ui/mobile.c | 3 +-- src/ui/touch.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++-------- src/ui/util.c | 1 + src/ui/widget.c | 30 +++++++++++++++++++--- 4 files changed, 96 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 580cc355..7794d686 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c @@ -150,8 +150,7 @@ static iBool topPanelHandler_(iWidget *topPanel, const char *cmd) { setFlags_Widget(button, selected_WidgetFlag, iTrue); return iTrue; } - if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd) && - argLabel_Command(cmd, "button") == SDL_BUTTON_X1) { + if (equal_Command(cmd, "swipe.back")) { postCommand_App("panel.close"); return iTrue; } diff --git a/src/ui/touch.c b/src/ui/touch.c index 74a22baf..c7c5a410 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c @@ -40,6 +40,10 @@ iDeclareType(TouchState) #define numHistory_Touch_ 5 #define lastIndex_Touch_ (numHistory_Touch_ - 1) +static const uint32_t longPressSpanMs_ = 500; +static const uint32_t shortPressSpanMs_ = 250; +static const int tapRadiusPt_ = 10; + enum iTouchEdge { none_TouchEdge, left_TouchEdge, @@ -55,7 +59,7 @@ enum iTouchAxis { struct Impl_Touch { SDL_FingerID id; iWidget *affinity; /* widget on which the touch started */ - iWidget *edgeDragging; +// iWidget *edgeDragging; iBool hasMoved; iBool isTapBegun; iBool isLeftDown; @@ -132,9 +136,6 @@ static iTouch *find_TouchState_(iTouchState *d, SDL_FingerID id) { return NULL; } -static const uint32_t longPressSpanMs_ = 500; -static const int tapRadiusPt_ = 10; - iLocalDef float distance_Touch_(const iTouch *d) { return length_F3(sub_F3(d->pos[0], d->startPos)); } @@ -246,6 +247,11 @@ iLocalDef double accurateTicks_(void) { return 1000.0 * (double) count / (double) freq; } +static iFloat3 gestureVector_Touch_(const iTouch *d) { + const size_t lastIndex = iMin(d->posCount - 1, lastIndex_Touch_); + return sub_F3(d->pos[0], d->pos[lastIndex]); +} + static void update_TouchState_(void *ptr) { iTouchState *d = ptr; /* Check for long presses to simulate right clicks. */ @@ -255,6 +261,22 @@ static void update_TouchState_(void *ptr) { if (touch->pinchId || touch->isTouchDrag) { continue; } + if (touch->edge) { + const iFloat3 pos = touch->pos[0]; + /* Cancel the swipe if the finger doesn't move or moves mostly vertically. */ + const iFloat3 gestureVector = gestureVector_Touch_(touch); + if (fabsf(2 * x_F3(gestureVector)) < fabsf(y_F3(gestureVector)) || + (isStationary_Touch_(touch) && nowTime - touch->startTime > shortPressSpanMs_)) { + //const int swipeDir = x_F3(gestureVector) > 0 ? +1 : -1; + //dispatchClick_Touch_(touch, +// touch->edge == left_TouchEdge && swipeDir > 0 ? SDL_BUTTON_X1 : +// 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; + } + else continue; + } /* Holding a touch will reset previous momentum for this widget. */ if (isStationary_Touch_(touch)) { const int elapsed = nowTime - touch->startTime; @@ -450,8 +472,10 @@ 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 if (edge == left_TouchEdge) { dragging = findSlidePanel_Widget_(aff); if (dragging) { @@ -460,6 +484,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { setFlags_Widget(dragging, dragged_WidgetFlag, iTrue); } } +#endif /* TODO: We must retain a reference to the affinity widget, or otherwise it might be destroyed during the gesture. */ // printf("aff:[%p] %s:'%s'\n", aff, aff ? class_Widget(aff)->name : "-", @@ -469,7 +494,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { iTouch newTouch = { .id = fing->fingerId, .affinity = aff, - .edgeDragging = dragging, +// .edgeDragging = dragging, .didBeginOnTouchDrag = (flags_Widget(aff) & touchDrag_WidgetFlag) != 0, .edge = edge, .startTime = nowTime, @@ -487,6 +512,14 @@ iBool processEvent_Touch(const SDL_Event *ev) { } else if (ev->type == SDL_FINGERMOTION) { iTouch *touch = find_TouchState_(d, fing->fingerId); + if (touch && touch->edge) { + pushPos_Touch_(touch, pos, nowTime); + postCommandf_App("edgeswipe.moved arg:%d side:%d id:%llu", + (int) (x_F3(pos) - x_F3(touch->startPos)), + touch->edge, + touch->id); + return iTrue; + } if (touch && touch->affinity) { if (touch->isTouchDrag) { dispatchMotion_Touch_(pos, SDL_BUTTON_LMASK); @@ -556,6 +589,8 @@ iBool processEvent_Touch(const SDL_Event *ev) { touch->axis = y_TouchAxis; } } + 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) { @@ -570,22 +605,35 @@ iBool processEvent_Touch(const SDL_Event *ev) { setVisualOffset_Widget(touch->edgeDragging, x_F3(pos) - x_F3(touch->startPos), 10, 0); } } - if (touch->edge == right_TouchEdge && fing->dx > 0) { - touch->edge = none_TouchEdge; + 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; } if (touch->axis == y_TouchAxis) { pixels.x = 0; } -// printf("%p (%s) py: %i wy: %f acc: %f\n", +// printf("%p (%s) py: %i wy: %f acc: %f edge: %d\n", // touch->affinity, // class_Widget(touch->affinity)->name, -// pixels.y, y_F3(amount), y_F3(touch->accum)); +// pixels.y, y_F3(amount), y_F3(touch->accum), +// touch->edge); if (pixels.x || pixels.y) { setFocus_Widget(NULL); dispatchMotion_Touch_(touch->pos[0], 0); @@ -612,9 +660,16 @@ iBool processEvent_Touch(const SDL_Event *ev) { endPinch_TouchState_(d, touch->pinchId); break; } +#if 0 if (touch->edgeDragging) { setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse); } +#endif + if (touch->edge && !isStationary_Touch_(touch)) { + postCommandf_App("edgeswipe.ended side:%d id:%llu", touch->edge, touch->id); + remove_ArrayIterator(&i); + continue; + } if (flags_Widget(touch->affinity) & touchDrag_WidgetFlag) { if (!touch->isLeftDown && !touch->isTapAndHold) { /* This will be a click on a touchDrag widget. */ @@ -638,6 +693,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { const uint32_t duration = nowTime - touch->startTime; const iFloat3 gestureVector = sub_F3(pos, touch->pos[lastIndex]); iFloat3 velocity = zero_F3(); +#if 0 if (touch->edge && fabsf(2 * x_F3(gestureVector)) > fabsf(y_F3(gestureVector)) && !isStationary_Touch_(touch)) { const int swipeDir = x_F3(gestureVector) > 0 ? +1 : -1; @@ -646,7 +702,9 @@ iBool processEvent_Touch(const SDL_Event *ev) { touch->edge == right_TouchEdge && swipeDir < 0 ? SDL_BUTTON_X2 : 0); setHover_Widget(NULL); } - else { + else +#endif + { const uint32_t elapsed = fing->timestamp - touch->posTime[lastIndex]; const float minVelocity = 400.0f; if (elapsed < 150) { diff --git a/src/ui/util.c b/src/ui/util.c index 6c6e62a5..9cc848de 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -1326,6 +1326,7 @@ static iBool messageHandler_(iWidget *msg, const char *cmd) { equal_Command(cmd, "document.request.updated") || equal_Command(cmd, "scrollbar.fade") || equal_Command(cmd, "widget.overflow") || + equal_Command(cmd, "edgeswipe.ended") || startsWith_CStr(cmd, "window."))) { setupSheetTransition_Mobile(msg, iFalse); destroy_Widget(msg); diff --git a/src/ui/widget.c b/src/ui/widget.c index 4eac7ecf..bedcb146 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -1056,10 +1056,32 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { isCommand_UserEvent(ev, "widget.overflow")) { scrollOverflow_Widget(d, 0); /* check bounds */ } - if (ev->user.code == command_UserEventCode && d->commandHandler && - d->commandHandler(d, ev->user.data1)) { - iAssert(get_Root() == d->root); - return iTrue; + if (ev->user.code == command_UserEventCode) { + const char *cmd = command_UserEvent(ev); + if (d->flags & edgeDraggable_WidgetFlag && + isVisible_Widget(d) && + ~d->flags & disabled_WidgetFlag && + equal_Command(cmd, "edgeswipe.moved")) { + if (~d->flags & dragged_WidgetFlag) { + setFlags_Widget(d, dragged_WidgetFlag, iTrue); + } + setVisualOffset_Widget(d, arg_Command(command_UserEvent(ev)), 10, 0); + return iTrue; + } + 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"); + } + } + if (d->commandHandler && d->commandHandler(d, ev->user.data1)) { + iAssert(get_Root() == d->root); + return iTrue; + } } break; } -- cgit v1.2.3 From d24775473c5e95043481457b061660caac8a1524 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 13 Jun 2021 06:52:36 +0300 Subject: Touch: Swiping away the sidebar --- src/ui/mobile.c | 4 ++-- src/ui/root.c | 4 ++-- src/ui/sidebarwidget.c | 34 ++++++++++++++++++++-------------- src/ui/sidebarwidget.h | 4 ++-- src/ui/touch.c | 5 ++++- src/ui/util.c | 2 +- src/ui/widget.c | 18 +++++++++++------- src/ui/widget.h | 4 ++-- 8 files changed, 44 insertions(+), 31 deletions(-) (limited to 'src') diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 7794d686..1bf289a7 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c @@ -120,7 +120,7 @@ static iBool mainDetailSplitHandler_(iWidget *mainDetailSplit, const char *cmd) } iForEach(ObjectList, i, children_Widget(detailStack)) { iWidget *panel = i.object; - setFlags_Widget(panel, edgeDraggable_WidgetFlag, !isSideBySide); + setFlags_Widget(panel, leftEdgeDraggable_WidgetFlag, !isSideBySide); if (isSideBySide) { setVisualOffset_Widget(panel, 0, 0, 0); } @@ -414,7 +414,7 @@ void finalizeSheet_Mobile(iWidget *sheet) { setFlags_Widget(sheet, frameless_WidgetFlag | //resizeWidthOfChildren_WidgetFlag | - edgeDraggable_WidgetFlag | + leftEdgeDraggable_WidgetFlag | commandOnClick_WidgetFlag, iTrue); iPtrArray * contents = collect_PtrArray(new_PtrArray()); /* two-column pages */ diff --git a/src/ui/root.c b/src/ui/root.c index 76f4c0d7..2d69970d 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -1185,9 +1185,9 @@ void createUserInterface_Root(iRoot *d) { } /* Sidebars. */ { iWidget *content = findChild_Widget(root, "tabs.content"); - iSidebarWidget *sidebar1 = new_SidebarWidget(left_SideBarSide); + iSidebarWidget *sidebar1 = new_SidebarWidget(left_SidebarSide); addChildPos_Widget(content, iClob(sidebar1), front_WidgetAddPos); - iSidebarWidget *sidebar2 = new_SidebarWidget(right_SideBarSide); + iSidebarWidget *sidebar2 = new_SidebarWidget(right_SidebarSide); if (deviceType_App() != phone_AppDeviceType) { addChildPos_Widget(content, iClob(sidebar2), back_WidgetAddPos); } diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 2e1e138e..4f3ea637 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -625,7 +625,7 @@ static void updateMetrics_SidebarWidget_(iSidebarWidget *d) { void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { iWidget *w = as_Widget(d); init_Widget(w); - setId_Widget(w, side == left_SideBarSide ? "sidebar" : "sidebar2"); + setId_Widget(w, side == left_SidebarSide ? "sidebar" : "sidebar2"); initCopy_String(&d->cmdPrefix, id_Widget(w)); appendChar_String(&d->cmdPrefix, '.'); setBackgroundColor_Widget(w, none_ColorId); @@ -659,7 +659,7 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { d->actions = NULL; /* On a phone, the right sidebar is used exclusively for Identities. */ const iBool isPhone = deviceType_App() == phone_AppDeviceType; - if (!isPhone || d->side == left_SideBarSide) { + if (!isPhone || d->side == left_SidebarSide) { iWidget *buttons = new_Widget(); setId_Widget(buttons, "buttons"); for (int i = 0; i < max_SidebarMode; i++) { @@ -715,24 +715,24 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { addChildFlags_Widget(content, iClob(d->blank), resizeChildren_WidgetFlag); addChildFlags_Widget(vdiv, iClob(content), expand_WidgetFlag); setMode_SidebarWidget(d, - deviceType_App() == phone_AppDeviceType && d->side == right_SideBarSide ? + deviceType_App() == phone_AppDeviceType && d->side == right_SidebarSide ? identities_SidebarMode : bookmarks_SidebarMode); d->resizer = addChildFlags_Widget(w, iClob(new_Widget()), hover_WidgetFlag | commandOnClick_WidgetFlag | fixedWidth_WidgetFlag | resizeToParentHeight_WidgetFlag | - (side == left_SideBarSide ? moveToParentRightEdge_WidgetFlag + (side == left_SidebarSide ? moveToParentRightEdge_WidgetFlag : moveToParentLeftEdge_WidgetFlag)); if (deviceType_App() == phone_AppDeviceType) { setFlags_Widget(d->resizer, hidden_WidgetFlag | disabled_WidgetFlag, iTrue); } - setId_Widget(d->resizer, side == left_SideBarSide ? "sidebar.grab" : "sidebar2.grab"); + setId_Widget(d->resizer, side == left_SidebarSide ? "sidebar.grab" : "sidebar2.grab"); setBackgroundColor_Widget(d->resizer, none_ColorId); d->menu = NULL; addAction_Widget(w, SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh"); updateMetrics_SidebarWidget_(d); - if (side == left_SideBarSide) { + if (side == left_SidebarSide) { postCommand_App("~sidebar.update"); /* unread count */ } } @@ -799,7 +799,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si updateContextMenu_SidebarWidget_(d); arrange_Widget(d->menu); openMenu_Widget(d->menu, - d->side == left_SideBarSide + d->side == left_SidebarSide ? topRight_Rect(itemRect_ListWidget(d->list, itemIndex)) : addX_I2(topLeft_Rect(itemRect_ListWidget(d->list, itemIndex)), -width_Widget(d->menu))); @@ -857,7 +857,7 @@ void setWidth_SidebarWidget(iSidebarWidget *d, float widthAsGaps) { if (!isFixedWidth) { /* Even less space if the other sidebar is visible, too. */ const int otherWidth = - width_Widget(findWidget_App(d->side == left_SideBarSide ? "sidebar2" : "sidebar")); + width_Widget(findWidget_App(d->side == left_SidebarSide ? "sidebar2" : "sidebar")); width = iClamp(width, 30 * gap_UI, size_Root(w->root).x - 50 * gap_UI - otherWidth); } d->widthAsGaps = (float) width / (float) gap_UI; @@ -938,7 +938,7 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * } const iBool isAnimated = prefs_App()->uiAnimations && argLabel_Command(cmd, "noanim") == 0 && - (d->side == left_SideBarSide || deviceType_App() != phone_AppDeviceType); + (d->side == left_SidebarSide || deviceType_App() != phone_AppDeviceType); int visX = 0; if (isVisible_Widget(w)) { visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); @@ -950,13 +950,13 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * 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, 0, 0); setVisualOffset_Widget(w, 0, 300, easeOut_AnimFlag | softer_AnimFlag); } } else if (isAnimated) { setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); - if (d->side == right_SideBarSide) { + if (d->side == right_SidebarSide) { setVisualOffset_Widget(w, visX, 0, 0); setVisualOffset_Widget(w, visX + w->rect.size.x, 300, easeOut_AnimFlag | softer_AnimFlag); } @@ -984,7 +984,8 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) /* Handle commands. */ if (isResize_UserEvent(ev)) { checkModeButtonLayout_SidebarWidget_(d); - if (deviceType_App() == phone_AppDeviceType && d->side == left_SideBarSide) { + if (deviceType_App() == phone_AppDeviceType && d->side == left_SidebarSide) { + setFlags_Widget(w, rightEdgeDraggable_WidgetFlag, isPortrait_App()); /* In landscape, visibility of the toolbar is controlled separately. */ if (isVisible_Widget(w)) { postCommand_Widget(w, "sidebar.toggle"); @@ -1038,6 +1039,11 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) postCommandf_App("sidebar.mode arg:%d toggle:1", identities_SidebarMode); return iTrue; } + else if (isPortraitPhone_App() && isVisible_Widget(w) && d->side == left_SidebarSide && + equal_Command(cmd, "swipe.forward")) { + postCommand_App("sidebar.toggle"); + return iTrue; + } else if (startsWith_CStr(cmd, cstr_String(&d->cmdPrefix))) { if (handleSidebarCommand_SidebarWidget_(d, cmd + size_String(&d->cmdPrefix))) { return iTrue; @@ -1068,7 +1074,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) const int resMid = d->resizer->rect.size.x / 2; setWidth_SidebarWidget( d, - ((d->side == left_SideBarSide + ((d->side == left_SidebarSide ? inner.x : (right_Rect(rect_Root(w->root)) - coord_Command(cmd).x)) + resMid) / (float) gap_UI); @@ -1547,7 +1553,7 @@ static void draw_SidebarWidget_(const iSidebarWidget *d) { if (isVisible_Widget(w)) { drawVLine_Paint( &p, - addX_I2(d->side == left_SideBarSide ? topRight_Rect(bounds) : topLeft_Rect(bounds), -1), + addX_I2(d->side == left_SidebarSide ? topRight_Rect(bounds) : topLeft_Rect(bounds), -1), height_Rect(bounds), uiSeparator_ColorId); } diff --git a/src/ui/sidebarwidget.h b/src/ui/sidebarwidget.h index 2e418aa4..130242ab 100644 --- a/src/ui/sidebarwidget.h +++ b/src/ui/sidebarwidget.h @@ -36,8 +36,8 @@ enum iSidebarMode { const char * icon_SidebarMode (enum iSidebarMode mode); enum iSidebarSide { - left_SideBarSide, - right_SideBarSide, + left_SidebarSide, + right_SidebarSide, }; enum iFeedsMode { diff --git a/src/ui/touch.c b/src/ui/touch.c index c7c5a410..236601e8 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c @@ -274,8 +274,9 @@ static void update_TouchState_(void *ptr) { // setHover_Widget(NULL); postCommandf_App("edgeswipe.ended abort:1 side:%d id:%llu", touch->edge, touch->id); touch->edge = none_TouchEdge; + remove_ArrayIterator(&i); } - else continue; + continue; } /* Holding a touch will reset previous momentum for this widget. */ if (isStationary_Touch_(touch)) { @@ -362,6 +363,7 @@ static void update_TouchState_(void *ptr) { } } +#if 0 static iWidget *findSlidePanel_Widget_(iWidget *d) { for (iWidget *w = d; w; w = parent_Widget(w)) { if (isVisible_Widget(w) && flags_Widget(w) & edgeDraggable_WidgetFlag) { @@ -370,6 +372,7 @@ static iWidget *findSlidePanel_Widget_(iWidget *d) { } return NULL; } +#endif static void checkNewPinch_TouchState_(iTouchState *d, iTouch *newTouch) { iWidget *affinity = newTouch->affinity; diff --git a/src/ui/util.c b/src/ui/util.c index 9cc848de..f2449096 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -1317,7 +1317,7 @@ void updateValueInput_Widget(iWidget *d, const char *title, const char *prompt) static iBool messageHandler_(iWidget *msg, const char *cmd) { /* Almost any command dismisses the sheet. */ - /* TODO: Use a "notification" prefix (like `) to ignore all types of commands line this? */ + /* TODO: Add a "notification" type of user events to separate them from user actions. */ if (!(equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.player.update") || equal_Command(cmd, "bookmarks.request.finished") || diff --git a/src/ui/widget.c b/src/ui/widget.c index bedcb146..f1d1dae4 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -1058,15 +1058,19 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { } if (ev->user.code == command_UserEventCode) { const char *cmd = command_UserEvent(ev); - if (d->flags & edgeDraggable_WidgetFlag && - isVisible_Widget(d) && - ~d->flags & disabled_WidgetFlag && + if (d->flags & (leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag) && + isVisible_Widget(d) && ~d->flags & disabled_WidgetFlag && equal_Command(cmd, "edgeswipe.moved")) { - if (~d->flags & dragged_WidgetFlag) { - setFlags_Widget(d, dragged_WidgetFlag, iTrue); + /* Check the side. */ + const int side = argLabel_Command(cmd, "side"); + if ((side == 1 && d->flags & leftEdgeDraggable_WidgetFlag) || + (side == 2 && d->flags & rightEdgeDraggable_WidgetFlag)) { + if (~d->flags & dragged_WidgetFlag) { + setFlags_Widget(d, dragged_WidgetFlag, iTrue); + } + setVisualOffset_Widget(d, arg_Command(command_UserEvent(ev)), 10, 0); + return iTrue; } - setVisualOffset_Widget(d, arg_Command(command_UserEvent(ev)), 10, 0); - return iTrue; } if (d->flags & dragged_WidgetFlag && equal_Command(cmd, "edgeswipe.ended")) { if (argLabel_Command(cmd, "abort")) { diff --git a/src/ui/widget.h b/src/ui/widget.h index 8de62b7a..7255b975 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -87,7 +87,7 @@ enum iWidgetFlag { }; /* 64-bit extended flags */ -//#define wasCollapsed_WidgetFlag iBit64(32) +#define rightEdgeDraggable_WidgetFlag iBit64(32) #define disabledWhenHidden_WidgetFlag iBit64(32) #define centerHorizontal_WidgetFlag iBit64(33) #define moveToParentLeftEdge_WidgetFlag iBit64(34) @@ -118,7 +118,7 @@ enum iWidgetFlag { #define ignoreForParentWidth_WidgetFlag iBit64(59) #define noFadeBackground_WidgetFlag iBit64(60) #define destroyPending_WidgetFlag iBit64(61) /* TODO: needed? */ -#define edgeDraggable_WidgetFlag iBit64(62) +#define leftEdgeDraggable_WidgetFlag iBit64(62) #define refChildrenOffset_WidgetFlag iBit64(63) /* visual offset determined by the offset of referenced children */ enum iWidgetAddPos { -- cgit v1.2.3 From 4a29ccb82910503fe1c44fbf493caf0945714705 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 13 Jun 2021 15:27:34 +0300 Subject: GmDocument: Link icons --- src/gmdocument.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gmdocument.c b/src/gmdocument.c index 4f4546f2..5d02ee05 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -256,10 +256,12 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li if ((len = decodeBytes_MultibyteChar(desc.start, desc.end, &icon)) > 0) { if (desc.start + len < desc.end && (isPictograph_Char(icon) || isEmoji_Char(icon) || + /* TODO: Add range(s) of 0x2nnn symbols. */ icon == 0x2139 /* info */ || icon == 0x2191 /* up arrow */ || icon == 0x2022 /* bullet */ || - icon == 0x2a2f /* close X */) && + icon == 0x2a2f /* close X */ || + icon == 0x2b50) && !isFitzpatrickType_Char(icon)) { link->flags |= iconFromLabel_GmLinkFlag; link->labelIcon = (iRangecc){ desc.start, desc.start + len }; -- 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') 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') 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') 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 From acad729245e09d927f1b16de2c6619b84f88c59e Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Mon, 14 Jun 2021 21:49:16 +0300 Subject: TextBuf: Attempted workaround It is possible that calling `SDL_RenderClear` was not performing the expected clearing of the TextBuf texture due to the wrong blend mode being in effect. IssueID #271 --- src/ui/text.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ui/text.c b/src/ui/text.c index 8a2a9ff8..27be1bec 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -1506,9 +1506,10 @@ static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iB if (d->texture) { SDL_Texture *oldTarget = SDL_GetRenderTarget(render); SDL_SetRenderTarget(render, d->texture); + SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(render, 255, 255, 255, 0); + SDL_RenderClear(render); SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */ - SDL_SetRenderDrawColor(text_.render, 0, 0, 0, 0); - SDL_RenderClear(text_.render); const int fg = color | fillBackground_ColorId; iRangecc range = range_CStr(text); if (maxWidth == 0) { -- cgit v1.2.3 From 4bcb7acb840627a197f8e9ed40256fc8ac0a01a9 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Mon, 14 Jun 2021 22:12:19 +0300 Subject: Mobile: Toolbar color depends on sidebar Toolbar should reflect the colors of the current view, whether it's a page or the sidebar. --- src/app.c | 2 +- src/ios.m | 2 +- src/ui/documentwidget.c | 8 ++++++-- src/ui/root.c | 26 ++++++++++++++++++++++---- src/ui/root.h | 3 ++- src/ui/sidebarwidget.c | 1 + 6 files changed, 33 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/app.c b/src/app.c index 4d96b5ef..749d998e 100644 --- a/src/app.c +++ b/src/app.c @@ -2027,7 +2027,7 @@ iBool handleCommand_App(const char *cmd) { else if (equal_Command(cmd, "hidetoolbarscroll")) { d->prefs.hideToolbarOnScroll = arg_Command(cmd); if (!d->prefs.hideToolbarOnScroll) { - showToolbars_Root(get_Root(), iTrue); + showToolbar_Root(get_Root(), iTrue); } return iTrue; } diff --git a/src/ios.m b/src/ios.m index b50f4ecb..82a8c0c8 100644 --- a/src/ios.m +++ b/src/ios.m @@ -327,7 +327,7 @@ iBool isPhone_iOS(void) { } int displayRefreshRate_iOS(void) { - return uiWindow_(get_Window()).screen.maximumFramesPerSecond; + return (int) uiWindow_(get_Window()).screen.maximumFramesPerSecond; } void setupWindow_iOS(iWindow *window) { diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 3fc22bdf..bd624059 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -1674,7 +1674,7 @@ static void scrollBegan_DocumentWidget_(iAnyObject *any, int offset, uint32_t du if (deviceType_App() == phone_AppDeviceType) { const float normPos = normScrollPos_DocumentWidget_(d); if (prefs_App()->hideToolbarOnScroll && iAbs(offset) > 5 && normPos >= 0) { - showToolbars_Root(as_Widget(d)->root, offset < 0); + showToolbar_Root(as_Widget(d)->root, offset < 0); } } updateVisible_DocumentWidget_(d); @@ -2840,7 +2840,11 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) if (d->flags & openedFromSidebar_DocumentWidgetFlag && !isVisible_Widget(findWidget_App("sidebar"))) { postCommand_App("sidebar.toggle"); - showToolbars_Root(get_Root(), iTrue); + showToolbar_Root(get_Root(), iTrue); +#if defined (iPlatformAppleMobile) + /* TODO: Add a softer tap? */ +// playHapticEffect_iOS(tap_HapticEffect); +#endif return iTrue; } d->flags &= ~openedFromSidebar_DocumentWidgetFlag; diff --git a/src/ui/root.c b/src/ui/root.c index 0a5eb96e..6c00db0f 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -514,6 +514,24 @@ void updatePadding_Root(iRoot *d) { #endif } +void updateToolbarColors_Root(iRoot *d) { +#if defined (iPlatformMobile) + iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); + if (toolBar) { + const iBool isSidebarVisible = isVisible_Widget(findChild_Widget(d->widget, "sidebar")); + setBackgroundColor_Widget(toolBar, isSidebarVisible ? uiBackgroundSidebar_ColorId : + tmBannerBackground_ColorId); + iForEach(ObjectList, i, children_Widget(toolBar)) { + iLabelWidget *btn = i.object; + setTextColor_LabelWidget(i.object, isSidebarVisible ? uiTextDim_ColorId : + tmBannerIcon_ColorId); + } + } +#else + iUnused(d); +#endif +} + void dismissPortraitPhoneSidebars_Root(iRoot *d) { if (deviceType_App() == phone_AppDeviceType && isPortrait_App()) { iWidget *sidebar = findChild_Widget(d->widget, "sidebar"); @@ -1247,7 +1265,6 @@ void createUserInterface_Root(iRoot *d) { arrangeHeight_WidgetFlag | arrangeHorizontal_WidgetFlag | commandOnClick_WidgetFlag | drawBackgroundToBottom_WidgetFlag, iTrue); - setBackgroundColor_Widget(toolBar, tmBannerBackground_ColorId); setId_Widget(addChildFlags_Widget(toolBar, iClob(newLargeIcon_LabelWidget("\U0001f870", "navigate.back")), frameless_WidgetFlag), @@ -1269,12 +1286,13 @@ void createUserInterface_Root(iRoot *d) { setFont_LabelWidget(menuButton, uiLabelLarge_FontId); setId_Widget(as_Widget(menuButton), "toolbar.navmenu"); addChildFlags_Widget(toolBar, iClob(menuButton), frameless_WidgetFlag); +// setBackgroundColor_Widget(toolBar, tmBannerBackground_ColorId); iForEach(ObjectList, i, children_Widget(toolBar)) { iLabelWidget *btn = i.object; setFlags_Widget(i.object, noBackground_WidgetFlag, iTrue); - setTextColor_LabelWidget(i.object, tmBannerIcon_ColorId); - // setBackgroundColor_Widget(i.object, tmBannerSideTitle_ColorId); +// setTextColor_LabelWidget(i.object, tmBannerIcon_ColorId); } + updateToolbarColors_Root(d); const iMenuItem items[] = { { book_Icon " ${sidebar.bookmarks}", 0, 0, "toolbar.showview arg:0" }, { star_Icon " ${sidebar.feeds}", 0, 0, "toolbar.showview arg:1" }, @@ -1356,7 +1374,7 @@ void createUserInterface_Root(iRoot *d) { } } -void showToolbars_Root(iRoot *d, iBool show) { +void showToolbar_Root(iRoot *d, iBool show) { /* The toolbar is only used on phone portrait layout. */ if (isLandscape_App()) return; iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); diff --git a/src/ui/root.h b/src/ui/root.h index 96864a15..740e97c9 100644 --- a/src/ui/root.h +++ b/src/ui/root.h @@ -34,7 +34,8 @@ void postArrange_Root (iRoot *); void updateMetrics_Root (iRoot *); void updatePadding_Root (iRoot *); /* TODO: is part of metrics? */ void dismissPortraitPhoneSidebars_Root (iRoot *); -void showToolbars_Root (iRoot *, iBool show); +void showToolbar_Root (iRoot *, iBool show); +void updateToolbarColors_Root (iRoot *); iInt2 size_Root (const iRoot *); iRect rect_Root (const iRoot *); diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index f7181037..5b836670 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -971,6 +971,7 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char * w, -w->rect.size.x - safePad, 300, easeOut_AnimFlag | softer_AnimFlag); } } + updateToolbarColors_Root(w->root); arrange_Widget(w->parent); /* BUG: Rearranging because the arrange above didn't fully resolve the height. */ arrange_Widget(w); -- cgit v1.2.3 From e5e3b291ac0adc2eab0ec5840b7bad99517b4d59 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 06:40:49 +0300 Subject: iOS: Gentle haptic tap for sidebar-back-swipe --- src/ios.h | 1 + src/ios.m | 43 ++++++++++++++++++++++++++++++++++--------- src/ui/documentwidget.c | 3 +-- 3 files changed, 36 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/ios.h b/src/ios.h index a8039c1c..70b889bf 100644 --- a/src/ios.h +++ b/src/ios.h @@ -28,6 +28,7 @@ iDeclareType(Window) enum iHapticEffect { tap_HapticEffect, + gentleTap_HapticEffect, }; void setupApplication_iOS (void); diff --git a/src/ios.m b/src/ios.m index 82a8c0c8..b82d54a7 100644 --- a/src/ios.m +++ b/src/ios.m @@ -66,6 +66,7 @@ API_AVAILABLE(ios(13.0)) @interface HapticState : NSObject @property (nonatomic, strong) CHHapticEngine *engine; @property (nonatomic, strong) NSDictionary *tapDef; +@property (nonatomic, strong) NSDictionary *gentleTapDef; @end @implementation HapticState @@ -107,26 +108,47 @@ API_AVAILABLE(ios(13.0)) CHHapticPatternKeyEvent: @{ CHHapticPatternKeyEventType: CHHapticEventTypeHapticTransient, CHHapticPatternKeyTime: @0.0, - CHHapticPatternKeyEventDuration:@0.1 + CHHapticPatternKeyEventDuration:@0.1, + CHHapticPatternKeyEventParameters: @[ + @{ + CHHapticPatternKeyParameterID: CHHapticEventParameterIDHapticIntensity, + CHHapticPatternKeyParameterValue: @1.0 + } + ] }, }, - ], + ] + }; + self.gentleTapDef = @{ + CHHapticPatternKeyPattern: + @[ + @{ + CHHapticPatternKeyEvent: @{ + CHHapticPatternKeyEventType: CHHapticEventTypeHapticTransient, + CHHapticPatternKeyTime: @0.0, + CHHapticPatternKeyEventDuration:@0.1, + CHHapticPatternKeyEventParameters: @[ + @{ + CHHapticPatternKeyParameterID: CHHapticEventParameterIDHapticIntensity, + CHHapticPatternKeyParameterValue: @0.33 + } + ] + }, + }, + ] }; } --(void)playTapEffect { + +-(void)playHapticEffect:(NSDictionary *)def { NSError *error = nil; - CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:self.tapDef + CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:def error:&error]; // TODO: Check the error. id player = [self.engine createPlayerWithPattern:pattern error:&error]; // TODO: Check the error. [self.engine startWithCompletionHandler:^(NSError *err){ if (err == nil) { - /* Just keep it running. */ -// [self.engine notifyWhenPlayersFinished:^(NSError * _Nullable error) { -// return CHHapticEngineFinishedActionStopEngine; -// }]; NSError *startError = nil; [player startAtTime:0.0 error:&startError]; } @@ -341,7 +363,10 @@ void playHapticEffect_iOS(enum iHapticEffect effect) { HapticState *hs = (HapticState *) appState_.haptic; switch(effect) { case tap_HapticEffect: - [hs playTapEffect]; + [hs playHapticEffect:hs.tapDef]; + break; + case gentleTap_HapticEffect: + [hs playHapticEffect:hs.gentleTapDef]; break; } } diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index bd624059..0fe8220e 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -2842,8 +2842,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) postCommand_App("sidebar.toggle"); showToolbar_Root(get_Root(), iTrue); #if defined (iPlatformAppleMobile) - /* TODO: Add a softer tap? */ -// playHapticEffect_iOS(tap_HapticEffect); + playHapticEffect_iOS(gentleTap_HapticEffect); #endif return iTrue; } -- cgit v1.2.3 From d5a87c08b3ed5521a21a72c048e5cf4db68314fa Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 10:31:20 +0300 Subject: DocumentWidget: Caching error page documents --- src/gmdocument.c | 4 ++++ src/gmdocument.h | 1 + src/history.c | 1 + src/ui/documentwidget.c | 23 +++++++++++++++-------- 4 files changed, 21 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/gmdocument.c b/src/gmdocument.c index 0ab09604..aa2baa4a 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -919,6 +919,10 @@ const iMedia *constMedia_GmDocument(const iGmDocument *d) { return d->media; } +const iString *url_GmDocument(const iGmDocument *d) { + return &d->url; +} + #if 0 void reset_GmDocument(iGmDocument *d) { clear_Media(d->media); diff --git a/src/gmdocument.h b/src/gmdocument.h index 221e5c00..831459d8 100644 --- a/src/gmdocument.h +++ b/src/gmdocument.h @@ -179,6 +179,7 @@ typedef void (*iGmDocumentRenderFunc)(void *, const iGmRun *); iMedia * media_GmDocument (iGmDocument *); const iMedia * constMedia_GmDocument (const iGmDocument *); +const iString * url_GmDocument (const iGmDocument *); void render_GmDocument (const iGmDocument *, iRangei visRangeY, iGmDocumentRenderFunc render, void *); /* includes partial overlaps */ diff --git a/src/history.c b/src/history.c index c592838e..2cea393d 100644 --- a/src/history.c +++ b/src/history.c @@ -391,6 +391,7 @@ 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); diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 0fe8220e..599e7ef3 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -1042,7 +1042,10 @@ static void documentWasChanged_DocumentWidget_(iDocumentWidget *d) { d->flags |= otherRootByDefault_DocumentWidgetFlag; } } - showOrHidePinningIndicator_DocumentWidget_(d); + showOrHidePinningIndicator_DocumentWidget_(d); + setCachedDocument_History(d->mod.history, + d->doc, /* keeps a ref */ + (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); } void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { @@ -1168,8 +1171,14 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode { person_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" } }, 2); } - setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); - setFormat_GmDocument(d->doc, gemini_SourceFormat); + /* Make a new document for the error page.*/ { + iGmDocument *errorDoc = new_GmDocument(); + setUrl_GmDocument(errorDoc, d->mod.url); + setBanner_GmDocument(errorDoc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); + setFormat_GmDocument(errorDoc, gemini_SourceFormat); + replaceDocument_DocumentWidget_(d, errorDoc); + iRelease(errorDoc); + } translate_Lang(src); d->state = ready_RequestState; setSource_DocumentWidget(d, src); @@ -1608,8 +1617,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, - (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); +// setCachedDocument_History(d->mod.history, d->doc, +// (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); } d->state = ready_RequestState; postProcessRequestContent_DocumentWidget_(d, iTrue); @@ -2705,8 +2714,6 @@ 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 */ - (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0); unlockResponse_GmRequest(d->request); } } @@ -4917,7 +4924,7 @@ void setRedirectCount_DocumentWidget(iDocumentWidget *d, int count) { void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *d, iBool fromSidebar) { iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, fromSidebar); - setCachedDocument_History(d->mod.history, d->doc, fromSidebar); +// setCachedDocument_History(d->mod.history, d->doc, fromSidebar); } iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { -- cgit v1.2.3 From 8d2154465c5ec92dc9a02f4004ea6ea660467410 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 11:12:24 +0300 Subject: Mobile: Minor tweaks Slightly thicker fetch progress indicator, smaller search query indicator on the phone. --- src/ui/indicatorwidget.c | 2 +- src/ui/root.c | 3 ++- src/ui/util.c | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ui/indicatorwidget.c b/src/ui/indicatorwidget.c index 4a829ae3..bc0bd0fa 100644 --- a/src/ui/indicatorwidget.c +++ b/src/ui/indicatorwidget.c @@ -109,7 +109,7 @@ void draw_IndicatorWidget_(const iIndicatorWidget *d) { colors[0] = black_ColorId; } fillRect_Paint(&p, - (iRect){ topLeft_Rect(rect), init_I2(pos * width_Rect(rect), gap_UI / 4)}, + (iRect){ topLeft_Rect(rect), init_I2(pos * width_Rect(rect), gap_UI / 3)}, colors[isCompleted_IndicatorWidget_(d) ? 1 : 0]); } } diff --git a/src/ui/root.c b/src/ui/root.c index 6c00db0f..c3a9596f 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -571,7 +571,8 @@ static void showSearchQueryIndicator_(iBool show) { iWidget *navBar = findWidget_Root("navbar"); iWidget *indicator = findWidget_App("input.indicator.search"); updateTextCStr_LabelWidget((iLabelWidget *) indicator, - flags_Widget(navBar) & tight_WidgetFlag + (deviceType_App() == phone_AppDeviceType || + flags_Widget(navBar) & tight_WidgetFlag) ? "${status.query.tight} " return_Icon : "${status.query} " return_Icon); indicator->rect.size.x = defaultSize_LabelWidget((iLabelWidget *) indicator).x; /* don't touch height */ diff --git a/src/ui/util.c b/src/ui/util.c index f2449096..55b48b3f 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -1600,8 +1600,6 @@ iWidget *makePreferences_Widget(void) { /* General preferences. */ { appendTwoColumnPage_(tabs, "${heading.prefs.general}", '1', &headings, &values); #if defined (LAGRANGE_ENABLE_DOWNLOAD_EDIT) - //addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.downloads}"))); - //setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.downloads"); addPrefsInputWithHeading_(headings, values, "prefs.downloads", iClob(new_InputWidget(0))); #endif iInputWidget *searchUrl; -- cgit v1.2.3 From c013b8f50c102df14ecf8327ca1a3cb023247086 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 11:58:13 +0300 Subject: Preferences: New Fonts tab; reorganized Added a fonts tab and reorganized settings more logically. Some of the General items were more appropriate for the Style page. --- po/en.po | 9 ++++-- res/lang/de.bin | Bin 20976 -> 21008 bytes res/lang/en.bin | Bin 19681 -> 19705 bytes res/lang/es.bin | Bin 22055 -> 22087 bytes res/lang/fi.bin | Bin 22178 -> 22210 bytes res/lang/fr.bin | Bin 22588 -> 22620 bytes res/lang/ia.bin | Bin 21886 -> 21918 bytes res/lang/ie.bin | Bin 21444 -> 21476 bytes res/lang/pl.bin | Bin 22692 -> 22724 bytes res/lang/ru.bin | Bin 32923 -> 32955 bytes res/lang/sr.bin | Bin 32328 -> 32360 bytes res/lang/tok.bin | Bin 20069 -> 20101 bytes res/lang/zh_Hans.bin | Bin 18845 -> 18877 bytes res/lang/zh_Hant.bin | Bin 19030 -> 19062 bytes src/ui/mobile.c | 9 +++++- src/ui/util.c | 81 ++++++++++++++++++++++++++++----------------------- 16 files changed, 59 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/po/en.po b/po/en.po index 29eadd94..924f61c0 100644 --- a/po/en.po +++ b/po/en.po @@ -1058,7 +1058,7 @@ msgid "heading.prefs.colors" msgstr "Colors" msgid "heading.prefs.fonts" -msgstr "FONTS" +msgstr "Fonts" # tab button msgid "heading.prefs.general" @@ -1066,7 +1066,7 @@ msgstr "General" # tab button msgid "heading.prefs.interface" -msgstr "Interface" +msgstr "UI" # tab button msgid "heading.prefs.keys" @@ -1079,8 +1079,11 @@ msgstr "Network" msgid "heading.prefs.paragraph" msgstr "PARAGRAPH" +msgid "heading.prefs.uitheme" +msgstr "UI COLORS" + msgid "heading.prefs.pagecontent" -msgstr "PAGE CONTENT" +msgstr "PAGE COLORS" msgid "heading.prefs.proxies" msgstr "PROXIES" diff --git a/res/lang/de.bin b/res/lang/de.bin index 1738f4a7..2b2e2b3a 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 54bb4867..54f384b0 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 5e0ec4d0..a69a21c5 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 a000d152..7f431a03 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 db440d19..8040b95c 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 c0fb91d9..2f8c6b21 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 96f5f2cb..d5d88d5b 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 99b6f1c4..a886bb65 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 aec21b35..6f69ac6e 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 ac4223ac..71c1daac 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 760b3044..eb7da0c9 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 cc8543b2..73dad7a7 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 2ef3b8a9..6b2ff359 100644 Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin differ diff --git a/src/ui/mobile.c b/src/ui/mobile.c index 7c0c8b60..0ff3fe85 100644 --- a/src/ui/mobile.c +++ b/src/ui/mobile.c @@ -473,7 +473,8 @@ void finalizeSheet_Mobile(iWidget *sheet) { 0x02699, /* gear */ 0x1f4f1, /* mobile phone */ 0x1f3a8, /* palette */ - 0x1f523, + 0x1f5da, /* aA */ + 0x1f660, /* pointing bud */ 0x1f5a7, /* computer network */ }; setIcon_LabelWidget(panelButton, icons[i]); @@ -621,6 +622,12 @@ void finalizeSheet_Mobile(iWidget *sheet) { destroyPending_Root(sheet->root); /* Additional elements for preferences. */ if (isPrefs) { + addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); + /* Management. */ { + iLabelWidget *idManButton = addChildFlags_Widget(topPanel, + iClob(makePanelButton_(person_Icon " ${sidebar.identities}", "panel.open")), + chevron_WidgetFlag | borderTop_WidgetFlag); + } addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); iLabelWidget *aboutButton = addChildFlags_Widget(topPanel, iClob(makePanelButton_(planet_Icon " ${menu.about}", "panel.open")), diff --git a/src/ui/util.c b/src/ui/util.c index 55b48b3f..f094bdf9 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -1607,12 +1607,6 @@ iWidget *makePreferences_Widget(void) { setUrlContent_InputWidget(searchUrl, iTrue); addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); addChild_Widget(values, iClob(makePadding_Widget(bigGap))); - addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.collapsepreonload}"))); - addChild_Widget(values, iClob(makeToggle_Widget("prefs.collapsepreonload"))); - addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.plaintext.wrap}"))); - addChild_Widget(values, iClob(makeToggle_Widget("prefs.plaintext.wrap"))); - addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.centershort}"))); - addChild_Widget(values, iClob(makeToggle_Widget("prefs.centershort"))); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.hoverlink}"))); addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink"))); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.archive.openindex}"))); @@ -1673,26 +1667,6 @@ iWidget *makePreferences_Widget(void) { } /* User Interface. */ { appendTwoColumnPage_(tabs, "${heading.prefs.interface}", '2', &headings, &values); -#if defined (iPlatformApple) || defined (iPlatformMSys) - addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.ostheme}"))); - addChild_Widget(values, iClob(makeToggle_Widget("prefs.ostheme"))); -#endif - addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.theme}"))); - iWidget *themes = new_Widget(); - /* Themes. */ { - setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.black}", "theme.set arg:0"))), "prefs.theme.0"); - setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.dark}", "theme.set arg:1"))), "prefs.theme.1"); - setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.light}", "theme.set arg:2"))), "prefs.theme.2"); - setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.white}", "theme.set arg:3"))), "prefs.theme.3"); - } - addChildFlags_Widget(values, iClob(themes), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); - /* Accents. */ - iWidget *accent = new_Widget(); { - setId_Widget(addChild_Widget(accent, iClob(new_LabelWidget("${prefs.accent.teal}", "accent.set arg:0"))), "prefs.accent.0"); - setId_Widget(addChild_Widget(accent, iClob(new_LabelWidget("${prefs.accent.orange}", "accent.set arg:1"))), "prefs.accent.1"); - } - addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.accent}"))); - addChildFlags_Widget(values, iClob(accent), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); #if defined (LAGRANGE_ENABLE_CUSTOM_FRAME) addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.customframe}"))); addChild_Widget(values, iClob(makeToggle_Widget("prefs.customframe"))); @@ -1717,6 +1691,27 @@ iWidget *makePreferences_Widget(void) { } /* Colors. */ { appendTwoColumnPage_(tabs, "${heading.prefs.colors}", '3', &headings, &values); + makeTwoColumnHeading_("${heading.prefs.uitheme}", headings, values); +#if defined (iPlatformApple) || defined (iPlatformMSys) + addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.ostheme}"))); + addChild_Widget(values, iClob(makeToggle_Widget("prefs.ostheme"))); +#endif + addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.theme}"))); + iWidget *themes = new_Widget(); + /* Themes. */ { + setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.black}", "theme.set arg:0"))), "prefs.theme.0"); + setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.dark}", "theme.set arg:1"))), "prefs.theme.1"); + setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.light}", "theme.set arg:2"))), "prefs.theme.2"); + setId_Widget(addChild_Widget(themes, iClob(new_LabelWidget("${prefs.theme.white}", "theme.set arg:3"))), "prefs.theme.3"); + } + addChildFlags_Widget(values, iClob(themes), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); + /* Accents. */ + iWidget *accent = new_Widget(); { + setId_Widget(addChild_Widget(accent, iClob(new_LabelWidget("${prefs.accent.teal}", "accent.set arg:0"))), "prefs.accent.0"); + setId_Widget(addChild_Widget(accent, iClob(new_LabelWidget("${prefs.accent.orange}", "accent.set arg:1"))), "prefs.accent.1"); + } + addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.accent}"))); + addChildFlags_Widget(values, iClob(accent), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); makeTwoColumnHeading_("${heading.prefs.pagecontent}", headings, values); for (int i = 0; i < 2; ++i) { const iBool isDark = (i == 0); @@ -1749,14 +1744,15 @@ iWidget *makePreferences_Widget(void) { } addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); } - /* Layout. */ { - setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.style}", '4', &headings, &values), "prefs.page.style"); - makeTwoColumnHeading_("${heading.prefs.fonts}", headings, values); + /* Fonts. */ { + setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.fonts}", '4', &headings, &values), "prefs.page.fonts"); /* Fonts. */ { addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}"))); addFontButtons_(values, "headingfont"); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font}"))); addFontButtons_(values, "font"); + addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); + addChild_Widget(values, iClob(makePadding_Widget(bigGap))); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.mono}"))); iWidget *mono = new_Widget(); { iWidget *tog; @@ -1788,11 +1784,18 @@ iWidget *makePreferences_Widget(void) { updateSize_LabelWidget((iLabelWidget *) tog); } addChildFlags_Widget(values, iClob(boldLink), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); - iInputWidget *customFont = new_InputWidget(0); - setHint_InputWidget(customFont, "${hint.prefs.userfont}"); - addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(customFont)); - } - makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); + addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); + addChild_Widget(values, iClob(makePadding_Widget(bigGap))); + /* Custom font. */ { + iInputWidget *customFont = new_InputWidget(0); + setHint_InputWidget(customFont, "${hint.prefs.userfont}"); + addPrefsInputWithHeading_(headings, values, "prefs.userfont", iClob(customFont)); + } + } + } + /* Style. */ { + setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.style}", '5', &headings, &values), "prefs.page.style"); +// makeTwoColumnHeading_("${heading.prefs.paragraph}", headings, values); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.linewidth}"))); iWidget *widths = new_Widget(); /* Line widths. */ { @@ -1812,14 +1815,20 @@ iWidget *makePreferences_Widget(void) { addChildFlags_Widget(values, iClob(quote), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.biglede}"))); addChild_Widget(values, iClob(makeToggle_Widget("prefs.biglede"))); + addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.plaintext.wrap}"))); + addChild_Widget(values, iClob(makeToggle_Widget("prefs.plaintext.wrap"))); + addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.collapsepreonload}"))); + addChild_Widget(values, iClob(makeToggle_Widget("prefs.collapsepreonload"))); // makeTwoColumnHeading_("${heading.prefs.widelayout}", headings, values); addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); addChild_Widget(values, iClob(makePadding_Widget(bigGap))); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.sideicon}"))); addChild_Widget(values, iClob(makeToggle_Widget("prefs.sideicon"))); + addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.centershort}"))); + addChild_Widget(values, iClob(makeToggle_Widget("prefs.centershort"))); } /* Network. */ { - appendTwoColumnPage_(tabs, "${heading.prefs.network}", '5', &headings, &values); + appendTwoColumnPage_(tabs, "${heading.prefs.network}", '6', &headings, &values); addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.decodeurls}"))); addChild_Widget(values, iClob(makeToggle_Widget("prefs.decodeurls"))); /* Cache size. */ { @@ -1855,7 +1864,7 @@ iWidget *makePreferences_Widget(void) { /* Keybindings. */ if (deviceType_App() == desktop_AppDeviceType) { iBindingsWidget *bind = new_BindingsWidget(); - appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '6', KMOD_PRIMARY); + appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '7', KMOD_PRIMARY); } addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); updatePreferencesLayout_Widget(dlg); -- cgit v1.2.3 From 00a67e05bd6b1b2a8a8a65b3611c59c3a253e755 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 12:46:12 +0300 Subject: Mobile: Draw Identity button as outline --- src/ui/labelwidget.c | 22 ++++++++++++++-------- src/ui/labelwidget.h | 1 + src/ui/root.c | 10 ++++++---- src/ui/sidebarwidget.c | 8 +------- src/ui/text.c | 37 +++++++++++++++++++++++++++++++++++++ src/ui/text.h | 2 ++ 6 files changed, 61 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index febaa15b..09c5812b 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c @@ -44,6 +44,7 @@ struct Impl_LabelWidget { struct { uint8_t alignVisual : 1; /* align according to visible bounds, not font metrics */ uint8_t noAutoMinHeight : 1; /* minimum height is not set automatically */ + uint8_t drawAsOutline : 1; /* draw as outline, filled with background color */ } flags; }; @@ -348,14 +349,14 @@ static void draw_LabelWidget_(const iLabelWidget *d) { cstr_String(&d->label)); } else { - drawCentered_Text(d->font, - adjusted_Rect(bounds, - add_I2(zero_I2(), init_I2(iconPad, 0)), - neg_I2(zero_I2())), - d->flags.alignVisual, - fg, - "%s", - cstr_String(&d->label)); + drawCenteredOutline_Text( + d->font, + adjusted_Rect(bounds, add_I2(zero_I2(), init_I2(iconPad, 0)), neg_I2(zero_I2())), + d->flags.alignVisual, + d->flags.drawAsOutline ? fg : none_ColorId, + d->flags.drawAsOutline ? d->widget.bgColor : fg, + "%s", + cstr_String(&d->label)); } if (flags & chevron_WidgetFlag) { const iRect chRect = rect; @@ -442,6 +443,7 @@ void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) { setFlags_Widget(w, hover_WidgetFlag, d->click.button != 0); d->flags.alignVisual = iFalse; d->flags.noAutoMinHeight = iFalse; + d->flags.drawAsOutline = iFalse; updateSize_LabelWidget(d); updateKey_LabelWidget_(d); /* could be bound to another key */ } @@ -481,6 +483,10 @@ void setNoAutoMinHeight_LabelWidget(iLabelWidget *d, iBool noAutoMinHeight) { } } +void setOutline_LabelWidget(iLabelWidget *d, iBool drawAsOutline) { + d->flags.drawAsOutline = drawAsOutline; +} + void updateText_LabelWidget(iLabelWidget *d, const iString *text) { set_String(&d->label, text); set_String(&d->srcLabel, text); diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h index 25cde95c..25c873fc 100644 --- a/src/ui/labelwidget.h +++ b/src/ui/labelwidget.h @@ -31,6 +31,7 @@ iDeclareObjectConstructionArgs(LabelWidget, const char *label, const char *comma void setAlignVisually_LabelWidget(iLabelWidget *, iBool alignVisual); void setNoAutoMinHeight_LabelWidget(iLabelWidget *, iBool noAutoMinHeight); +void setOutline_LabelWidget (iLabelWidget *, iBool drawAsOutline); void setFont_LabelWidget (iLabelWidget *, int fontId); void setTextColor_LabelWidget (iLabelWidget *, int color); void setText_LabelWidget (iLabelWidget *, const iString *text); /* resizes widget */ diff --git a/src/ui/root.c b/src/ui/root.c index c3a9596f..71f51ef0 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -432,9 +432,9 @@ static void updateNavBarIdentity_(iWidget *navBar) { const iGmIdentity *ident = identityForUrl_GmCerts(certs_App(), url_DocumentWidget(document_App())); iWidget *button = findChild_Widget(navBar, "navbar.ident"); - iWidget *tool = findWidget_App("toolbar.ident"); + iLabelWidget *toolButton = findWidget_App("toolbar.ident"); setFlags_Widget(button, selected_WidgetFlag, ident != NULL); - setFlags_Widget(tool, selected_WidgetFlag, ident != NULL); + setOutline_LabelWidget(toolButton, ident == NULL); /* Update menu. */ iLabelWidget *idItem = child_Widget(findChild_Widget(button, "menu"), 0); const iString *subjectName = ident ? name_GmIdentity(ident) : NULL; @@ -519,12 +519,14 @@ void updateToolbarColors_Root(iRoot *d) { iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); if (toolBar) { const iBool isSidebarVisible = isVisible_Widget(findChild_Widget(d->widget, "sidebar")); - setBackgroundColor_Widget(toolBar, isSidebarVisible ? uiBackgroundSidebar_ColorId : - tmBannerBackground_ColorId); + const int bg = isSidebarVisible ? uiBackgroundSidebar_ColorId : + tmBannerBackground_ColorId; + setBackgroundColor_Widget(toolBar, bg); iForEach(ObjectList, i, children_Widget(toolBar)) { iLabelWidget *btn = i.object; setTextColor_LabelWidget(i.object, isSidebarVisible ? uiTextDim_ColorId : tmBannerIcon_ColorId); + setBackgroundColor_Widget(i.object, bg); /* using noBackground, but ident has outline */ } } #else diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 5b836670..b4948821 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -1806,13 +1806,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, : uiTextFramelessHover_ColorId) : uiTextDim_ColorId; if (!d->listItem.isSelected && !isUsedOnDomain) { - /* Draw an outline of the icon. */ - for (int off = 0; off < 4; ++off) { - drawRange_Text(font, - add_I2(cPos, init_I2(off % 2 == 0 ? -1 : 1, off / 2 == 0 ? -1 : 1)), - metaFg, - range_String(&icon)); - } + drawOutline_Text(font, cPos, metaFg, none_ColorId, range_String(&icon)); } drawRange_Text(font, cPos, diff --git a/src/ui/text.c b/src/ui/text.c index 27be1bec..8a70e4db 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -1315,6 +1315,18 @@ void drawRangeN_Text(int fontId, iInt2 pos, int color, iRangecc text, size_t max drawBoundedN_Text_(fontId, pos, 0, color, text, maxChars); } +void drawOutline_Text(int fontId, iInt2 pos, int outlineColor, int fillColor, iRangecc text) { + for (int off = 0; off < 4; ++off) { + drawRange_Text(fontId, + add_I2(pos, init_I2(off % 2 == 0 ? -1 : 1, off / 2 == 0 ? -1 : 1)), + outlineColor, + text); + } + if (fillColor != none_ColorId) { + drawRange_Text(fontId, pos, fillColor, text); + } +} + iInt2 advanceWrapRange_Text(int fontId, int maxWidth, iRangecc text) { iInt2 size = zero_I2(); const char *endp; @@ -1356,6 +1368,31 @@ void drawCentered_Text(int fontId, iRect rect, iBool alignVisual, int color, con deinit_Block(&chars); } +void drawCenteredOutline_Text(int fontId, iRect rect, iBool alignVisual, int outlineColor, + int fillColor, const char *format, ...) { + iBlock chars; + init_Block(&chars, 0); { + va_list args; + va_start(args, format); + vprintf_Block(&chars, format, args); + va_end(args); + } + if (outlineColor != none_ColorId) { + for (int off = 0; off < 4; ++off) { + drawCenteredRange_Text( + fontId, + moved_Rect(rect, init_I2(off % 2 == 0 ? -1 : 1, off / 2 == 0 ? -1 : 1)), + alignVisual, + outlineColor, + range_Block(&chars)); + } + } + if (fillColor != none_ColorId) { + drawCenteredRange_Text(fontId, rect, alignVisual, fillColor, range_Block(&chars)); + } + deinit_Block(&chars); +} + void drawCenteredRange_Text(int fontId, iRect rect, iBool alignVisual, int color, iRangecc text) { iRect textBounds = alignVisual ? visualBounds_Text(fontId, text) : (iRect){ zero_I2(), advanceRange_Text(fontId, text) }; diff --git a/src/ui/text.h b/src/ui/text.h index 2f2bcf3a..64a8352f 100644 --- a/src/ui/text.h +++ b/src/ui/text.h @@ -162,9 +162,11 @@ void draw_Text (int fontId, iInt2 pos, int color, const char *t void drawAlign_Text (int fontId, iInt2 pos, int color, enum iAlignment align, const char *text, ...); void drawCentered_Text (int fontId, iRect rect, iBool alignVisual, int color, const char *text, ...); void drawCenteredRange_Text (int fontId, iRect rect, iBool alignVisual, int color, iRangecc text); +void drawCenteredOutline_Text(int fontId, iRect rect, iBool alignVisual, int outlineColor, int fillColor, const char *text, ...); void drawString_Text (int fontId, iInt2 pos, int color, const iString *text); void drawRange_Text (int fontId, iInt2 pos, int color, iRangecc text); void drawRangeN_Text (int fontId, iInt2 pos, int color, iRangecc text, size_t maxLen); +void drawOutline_Text (int fontId, iInt2 pos, int outlineColor, int fillColor, iRangecc text); void drawBoundRange_Text (int fontId, iInt2 pos, int boundWidth, int color, iRangecc text); /* bound does not wrap */ int drawWrapRange_Text (int fontId, iInt2 pos, int maxWidth, int color, iRangecc text); /* returns new Y */ -- cgit v1.2.3 From 379c1100995fbc841df863027871ee91c77deedf Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 13:18:57 +0300 Subject: Mobile: Show identity name in toolbar Shuffled the widget flags a bit to make room for a new one. --- src/ui/labelwidget.c | 8 +++++++- src/ui/labelwidget.h | 1 + src/ui/root.c | 29 +++++++++++++++++++++++++++-- src/ui/text.c | 1 + src/ui/text.h | 1 + src/ui/util.c | 4 ++-- src/ui/widget.c | 5 +++++ src/ui/widget.h | 6 +++--- 8 files changed, 47 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 09c5812b..46ef5d1f 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c @@ -45,6 +45,7 @@ struct Impl_LabelWidget { uint8_t alignVisual : 1; /* align according to visible bounds, not font metrics */ uint8_t noAutoMinHeight : 1; /* minimum height is not set automatically */ uint8_t drawAsOutline : 1; /* draw as outline, filled with background color */ + uint8_t noTopFrame : 1; } flags; }; @@ -290,7 +291,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { }; drawLines_Paint(&p, points + 2, 3, frame2); drawLines_Paint( - &p, points, !isHover && flags & noTopFrame_WidgetFlag ? 2 : 3, frame); + &p, points, !isHover && flags & d->flags.noTopFrame ? 2 : 3, frame); } } setClip_Paint(&p, rect); @@ -444,6 +445,7 @@ void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) { d->flags.alignVisual = iFalse; d->flags.noAutoMinHeight = iFalse; d->flags.drawAsOutline = iFalse; + d->flags.noTopFrame = iFalse; updateSize_LabelWidget(d); updateKey_LabelWidget_(d); /* could be bound to another key */ } @@ -483,6 +485,10 @@ void setNoAutoMinHeight_LabelWidget(iLabelWidget *d, iBool noAutoMinHeight) { } } +void setNoTopFrame_LabelWidget(iLabelWidget *d, iBool noTopFrame) { + d->flags.noTopFrame = noTopFrame; +} + void setOutline_LabelWidget(iLabelWidget *d, iBool drawAsOutline) { d->flags.drawAsOutline = drawAsOutline; } diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h index 25c873fc..f6343273 100644 --- a/src/ui/labelwidget.h +++ b/src/ui/labelwidget.h @@ -31,6 +31,7 @@ iDeclareObjectConstructionArgs(LabelWidget, const char *label, const char *comma void setAlignVisually_LabelWidget(iLabelWidget *, iBool alignVisual); void setNoAutoMinHeight_LabelWidget(iLabelWidget *, iBool noAutoMinHeight); +void setNoTopFrame_LabelWidget (iLabelWidget *, iBool noTopFrame); void setOutline_LabelWidget (iLabelWidget *, iBool drawAsOutline); void setFont_LabelWidget (iLabelWidget *, int fontId); void setTextColor_LabelWidget (iLabelWidget *, int color); diff --git a/src/ui/root.c b/src/ui/root.c index 71f51ef0..9f4aeb7f 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -443,6 +443,10 @@ static void updateNavBarIdentity_(iWidget *navBar) { subjectName ? format_CStr(uiTextAction_ColorEscape "%s", cstr_String(subjectName)) : "${menu.identity.notactive}"); setFlags_Widget(as_Widget(idItem), disabled_WidgetFlag, !ident); + iLabelWidget *toolName = findWidget_App("toolbar.name"); + if (toolName) { + updateTextCStr_LabelWidget(toolName, subjectName ? cstr_String(subjectName) : ""); + } } static void updateNavDirButtons_(iWidget *navBar) { @@ -951,6 +955,19 @@ void updateMetrics_Root(iRoot *d) { updatePadding_Root(d); arrange_Widget(d->widget); updateUrlInputContentPadding_(navBar); + /* Position the toolbar identity name label manually. */ { + iLabelWidget *idName = findChild_Widget(d->widget, "toolbar.name"); + if (idName) { + const iWidget *idButton = findChild_Widget(d->widget, "toolbar.ident"); + const int font = defaultSmall_FontId; + setFont_LabelWidget(idName, font); + setPos_Widget(as_Widget(idName), + windowToLocal_Widget(as_Widget(idName), + addY_I2(bottomLeft_Rect(bounds_Widget(idButton)), -gap_UI * 2))); + setFixedSize_Widget(as_Widget(idName), init_I2(width_Widget(idButton), + lineHeight_Text(font))); + } + } postRefresh_App(); } @@ -1284,16 +1301,24 @@ void createUserInterface_Root(iRoot *d) { iClob(newLargeIcon_LabelWidget(book_Icon, "toolbar.showview arg:-1")), frameless_WidgetFlag | commandOnClick_WidgetFlag), "toolbar.view"); + setId_Widget(addChildFlags_Widget(toolBar, + iClob(new_LabelWidget("", NULL)), + frameless_WidgetFlag | + noBackground_WidgetFlag | + disabled_WidgetFlag | + fixedPosition_WidgetFlag | + fixedSize_WidgetFlag | + ignoreForParentWidth_WidgetFlag | + ignoreForParentHeight_WidgetFlag), + "toolbar.name"); iLabelWidget *menuButton = makeMenuButton_LabelWidget(menu_Icon, phoneNavMenuItems_, iElemCount(phoneNavMenuItems_)); setFont_LabelWidget(menuButton, uiLabelLarge_FontId); setId_Widget(as_Widget(menuButton), "toolbar.navmenu"); addChildFlags_Widget(toolBar, iClob(menuButton), frameless_WidgetFlag); -// setBackgroundColor_Widget(toolBar, tmBannerBackground_ColorId); iForEach(ObjectList, i, children_Widget(toolBar)) { iLabelWidget *btn = i.object; setFlags_Widget(i.object, noBackground_WidgetFlag, iTrue); -// setTextColor_LabelWidget(i.object, tmBannerIcon_ColorId); } updateToolbarColors_Root(d); const iMenuItem items[] = { diff --git a/src/ui/text.c b/src/ui/text.c index 8a70e4db..fcd944d6 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -306,6 +306,7 @@ static void initFonts_Text_(iText *d) { { &fontSourceSans3Regular_Embedded, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, { &fontSourceSans3Regular_Embedded, uiSize * 1.333f, 1.0f, uiBig_FontSize }, { &fontSourceSans3Regular_Embedded, uiSize * 1.666f, 1.0f, uiLarge_FontSize }, + { &fontSourceSans3Regular_Embedded, uiSize * 0.8f, 1.0f, uiNormal_FontSize }, /* UI fonts: bold weight */ { &fontSourceSans3Bold_Embedded, uiSize, 1.0f, uiNormal_FontSize }, { &fontSourceSans3Bold_Embedded, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, diff --git a/src/ui/text.h b/src/ui/text.h index 64a8352f..dd377247 100644 --- a/src/ui/text.h +++ b/src/ui/text.h @@ -50,6 +50,7 @@ enum iFontId { defaultMedium_FontId, defaultBig_FontId, defaultLarge_FontId, + defaultSmall_FontId, /* UI fonts: bold weight */ defaultBold_FontId, defaultMediumBold_FontId, diff --git a/src/ui/util.c b/src/ui/util.c index f094bdf9..3a9bc400 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -979,8 +979,8 @@ static void addTabPage_Widget_(iWidget *tabs, enum iWidgetAddPos addPos, iWidget iClob(newKeyMods_LabelWidget(label, key, kmods, format_CStr("tabs.switch page:%p", page))), addPos); setFlags_Widget(button, selected_WidgetFlag, isSel); - setFlags_Widget( - button, noTopFrame_WidgetFlag | commandOnClick_WidgetFlag | expand_WidgetFlag, iTrue); + setFlags_Widget(button, commandOnClick_WidgetFlag | expand_WidgetFlag, iTrue); + setNoTopFrame_LabelWidget((iLabelWidget *) button, iTrue); addChildPos_Widget(pages, page, addPos); if (tabCount_Widget(tabs) > 1) { setFlags_Widget(buttons, hidden_WidgetFlag, iFalse); diff --git a/src/ui/widget.c b/src/ui/widget.c index 85672c04..4e1c9119 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -406,6 +406,11 @@ static void boundsOfChildren_Widget_(const iWidget *d, iRect *bounds_out) { iRect childRect = child->rect; if (child->flags & ignoreForParentWidth_WidgetFlag) { childRect.size.x = 0; + childRect.pos.x = bounds_out->pos.x; + } + if (child->flags & ignoreForParentHeight_WidgetFlag) { + childRect.size.y = 0; + childRect.pos.y = bounds_out->pos.y; } if (isEmpty_Rect(*bounds_out)) { *bounds_out = childRect; diff --git a/src/ui/widget.h b/src/ui/widget.h index acb8fa9d..79565483 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -87,7 +87,7 @@ enum iWidgetFlag { }; /* 64-bit extended flags */ -#define rightEdgeDraggable_WidgetFlag iBit64(32) +#define rightEdgeDraggable_WidgetFlag iBit64(31) #define disabledWhenHidden_WidgetFlag iBit64(32) #define centerHorizontal_WidgetFlag iBit64(33) #define moveToParentLeftEdge_WidgetFlag iBit64(34) @@ -103,7 +103,7 @@ enum iWidgetFlag { #define drawBackgroundToVerticalSafeArea_WidgetFlag iBit64(44) #define visualOffset_WidgetFlag iBit64(45) #define parentCannotResize_WidgetFlag iBit64(46) -#define noTopFrame_WidgetFlag iBit64(47) +#define ignoreForParentHeight_WidgetFlag iBit64(47) #define unpadded_WidgetFlag iBit64(48) /* ignore parent's padding */ #define extraPadding_WidgetFlag iBit64(49) #define borderBottom_WidgetFlag iBit64(50) @@ -117,7 +117,7 @@ enum iWidgetFlag { #define parentCannotResizeHeight_WidgetFlag iBit64(58) #define ignoreForParentWidth_WidgetFlag iBit64(59) #define noFadeBackground_WidgetFlag iBit64(60) -#define destroyPending_WidgetFlag iBit64(61) /* TODO: needed? */ +#define destroyPending_WidgetFlag iBit64(61) #define leftEdgeDraggable_WidgetFlag iBit64(62) #define refChildrenOffset_WidgetFlag iBit64(63) /* visual offset determined by the offset of referenced children */ -- cgit v1.2.3 From 8d4eec40be5f0fb18012e69787d5c28d5866267b Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 13:42:35 +0300 Subject: Mobile: Showing identity name in toolbar Tweaked the position to work without safe area insets as well. It would make things easier to have the name as a child of the Identity button and keep the size of the button fixed. --- src/ui/root.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ui/root.c b/src/ui/root.c index 9f4aeb7f..5266978b 100644 --- a/src/ui/root.c +++ b/src/ui/root.c @@ -446,6 +446,8 @@ static void updateNavBarIdentity_(iWidget *navBar) { iLabelWidget *toolName = findWidget_App("toolbar.name"); if (toolName) { updateTextCStr_LabelWidget(toolName, subjectName ? cstr_String(subjectName) : ""); + setFont_LabelWidget(toolButton, subjectName ? defaultMedium_FontId : uiLabelLarge_FontId); + arrange_Widget(parent_Widget(toolButton)); } } @@ -958,12 +960,16 @@ void updateMetrics_Root(iRoot *d) { /* Position the toolbar identity name label manually. */ { iLabelWidget *idName = findChild_Widget(d->widget, "toolbar.name"); if (idName) { - const iWidget *idButton = findChild_Widget(d->widget, "toolbar.ident"); + const iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); + const iWidget *viewButton = findChild_Widget(d->widget, "toolbar.view"); + const iWidget *idButton = findChild_Widget(toolBar, "toolbar.ident"); const int font = defaultSmall_FontId; setFont_LabelWidget(idName, font); setPos_Widget(as_Widget(idName), windowToLocal_Widget(as_Widget(idName), - addY_I2(bottomLeft_Rect(bounds_Widget(idButton)), -gap_UI * 2))); + init_I2(left_Rect(bounds_Widget(idButton)), + bottom_Rect(bounds_Widget(viewButton)) - + lineHeight_Text(font) - gap_UI / 2))); setFixedSize_Widget(as_Widget(idName), init_I2(width_Widget(idButton), lineHeight_Text(font))); } @@ -1302,10 +1308,9 @@ void createUserInterface_Root(iRoot *d) { frameless_WidgetFlag | commandOnClick_WidgetFlag), "toolbar.view"); setId_Widget(addChildFlags_Widget(toolBar, - iClob(new_LabelWidget("", NULL)), + iClob(new_LabelWidget("", "toolbar.showident")), frameless_WidgetFlag | noBackground_WidgetFlag | - disabled_WidgetFlag | fixedPosition_WidgetFlag | fixedSize_WidgetFlag | ignoreForParentWidth_WidgetFlag | -- cgit v1.2.3 From f959fccf855af48cebf1cfae75c6938bba635e51 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Mon, 14 Jun 2021 21:49:16 +0300 Subject: TextBuf: Attempted workaround It is possible that calling `SDL_RenderClear` was not performing the expected clearing of the TextBuf texture due to the wrong blend mode being in effect. IssueID #271 --- src/ui/text.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ui/text.c b/src/ui/text.c index 55fd4254..db922715 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -1504,9 +1504,10 @@ static void initWrap_TextBuf_(iTextBuf *d, int font, int color, int maxWidth, iB if (d->texture) { SDL_Texture *oldTarget = SDL_GetRenderTarget(render); SDL_SetRenderTarget(render, d->texture); + SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); + SDL_SetRenderDrawColor(render, 255, 255, 255, 0); + SDL_RenderClear(render); SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */ - SDL_SetRenderDrawColor(text_.render, 0, 0, 0, 0); - SDL_RenderClear(text_.render); const int fg = color | fillBackground_ColorId; iRangecc range = range_CStr(text); if (maxWidth == 0) { -- cgit v1.2.3 From 0e46354f810c84827199faa3780691d772b7bd5b Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 13 Jun 2021 15:27:34 +0300 Subject: GmDocument: Link icons # Conflicts: # src/gmdocument.c --- src/gmdocument.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gmdocument.c b/src/gmdocument.c index b95f85e7..4f9f97e7 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -254,9 +254,12 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li if ((len = decodeBytes_MultibyteChar(desc.start, desc.end, &icon)) > 0) { if (desc.start + len < desc.end && (isPictograph_Char(icon) || isEmoji_Char(icon) || + /* TODO: Add range(s) of 0x2nnn symbols. */ + icon == 0x2139 /* info */ || icon == 0x2191 /* up arrow */ || + icon == 0x2022 /* bullet */ || icon == 0x2a2f /* close X */ || - icon == 0x2022 /* bullet */) && + icon == 0x2b50) && !isFitzpatrickType_Char(icon)) { link->flags |= iconFromLabel_GmLinkFlag; link->labelIcon = (iRangecc){ desc.start, desc.start + len }; -- cgit v1.2.3 From 8b8411bb99bc7828947ebfb29d9ba49064341733 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 11 Jun 2021 12:38:01 +0300 Subject: Extend list of allowed ciphers Ciphers used in the wild. --- src/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/main.c b/src/main.c index 5c15d8f7..b1850283 100644 --- a/src/main.c +++ b/src/main.c @@ -64,7 +64,8 @@ int main(int argc, char **argv) { "ECDHE-ECDSA-AES128-GCM-SHA256:" "ECDHE-RSA-AES256-GCM-SHA384:" "ECDHE-RSA-CHACHA20-POLY1305:" - "ECDHE-RSA-AES128-GCM-SHA256"); + "ECDHE-RSA-AES128-GCM-SHA256:" + "DHE-RSA-AES256-GCM-SHA384"); SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1"); if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { -- cgit v1.2.3 From 941c92e80ab5981f7f65a6780552416b998b0425 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 17:49:36 +0300 Subject: Text: Small font is semibold for legibility --- src/ui/text.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/ui/text.c b/src/ui/text.c index fcd944d6..ae1248a7 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -306,7 +306,7 @@ static void initFonts_Text_(iText *d) { { &fontSourceSans3Regular_Embedded, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, { &fontSourceSans3Regular_Embedded, uiSize * 1.333f, 1.0f, uiBig_FontSize }, { &fontSourceSans3Regular_Embedded, uiSize * 1.666f, 1.0f, uiLarge_FontSize }, - { &fontSourceSans3Regular_Embedded, uiSize * 0.8f, 1.0f, uiNormal_FontSize }, + { &fontSourceSans3Semibold_Embedded, uiSize * 0.8f, 1.0f, uiNormal_FontSize }, /* UI fonts: bold weight */ { &fontSourceSans3Bold_Embedded, uiSize, 1.0f, uiNormal_FontSize }, { &fontSourceSans3Bold_Embedded, uiSize * 1.125f, 1.0f, uiMedium_FontSize }, -- cgit v1.2.3 From a8fa5c21f0bc81f3f144ee29e16deab3f91296d4 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Tue, 15 Jun 2021 17:51:04 +0300 Subject: Fixed visual artifact during sidebar animation The sidebars are not supposed to have borders or background fades. --- src/ui/labelwidget.c | 26 +++++++++++++++++++------- src/ui/labelwidget.h | 2 ++ src/ui/sidebarwidget.c | 9 ++++++--- src/ui/util.c | 5 +++-- src/ui/widget.c | 2 +- src/ui/widget.h | 2 +- 6 files changed, 32 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index 46ef5d1f..b68ab793 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c @@ -46,6 +46,7 @@ struct Impl_LabelWidget { uint8_t noAutoMinHeight : 1; /* minimum height is not set automatically */ uint8_t drawAsOutline : 1; /* draw as outline, filled with background color */ uint8_t noTopFrame : 1; + uint8_t wrap : 1; } flags; }; @@ -317,7 +318,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { cstr_String(&str)); deinit_String(&str); } - if (flags & wrapText_WidgetFlag) { + if (d->flags.wrap) { const iRect inner = adjusted_Rect(innerBounds_Widget(w), init_I2(iconPad, 0), zero_I2()); const int wrap = inner.size.x; drawWrapRange_Text(d->font, topLeft_Rect(inner), wrap, fg, range_String(&d->label)); @@ -372,7 +373,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { static void sizeChanged_LabelWidget_(iLabelWidget *d) { iWidget *w = as_Widget(d); - if (flags_Widget(w) & wrapText_WidgetFlag) { + if (d->flags.wrap) { if (flags_Widget(w) & fixedHeight_WidgetFlag) { /* Calculate a new height based on the wrapping. */ w->rect.size.y = advanceWrapRange_Text( @@ -410,7 +411,7 @@ void updateSize_LabelWidget(iLabelWidget *d) { w->minSize.y = size.y; /* vertically text must remain visible */ } /* Wrapped text implies that width must be defined by arrangement. */ - if (!(flags & (fixedWidth_WidgetFlag | wrapText_WidgetFlag))) { + if (~flags & fixedWidth_WidgetFlag && !d->flags.wrap) { w->rect.size.x = size.x; } if (~flags & fixedHeight_WidgetFlag) { @@ -442,10 +443,11 @@ void init_LabelWidget(iLabelWidget *d, const char *label, const char *cmd) { d->kmods = 0; init_Click(&d->click, d, !isEmpty_String(&d->command) ? SDL_BUTTON_LEFT : 0); setFlags_Widget(w, hover_WidgetFlag, d->click.button != 0); - d->flags.alignVisual = iFalse; + d->flags.alignVisual = iFalse; d->flags.noAutoMinHeight = iFalse; - d->flags.drawAsOutline = iFalse; - d->flags.noTopFrame = iFalse; + d->flags.drawAsOutline = iFalse; + d->flags.noTopFrame = iFalse; + d->flags.wrap = iFalse; updateSize_LabelWidget(d); updateKey_LabelWidget_(d); /* could be bound to another key */ } @@ -489,8 +491,14 @@ void setNoTopFrame_LabelWidget(iLabelWidget *d, iBool noTopFrame) { d->flags.noTopFrame = noTopFrame; } +void setWrap_LabelWidget(iLabelWidget *d, iBool wrap) { + d->flags.wrap = wrap; +} + void setOutline_LabelWidget(iLabelWidget *d, iBool drawAsOutline) { - d->flags.drawAsOutline = drawAsOutline; + if (d) { + d->flags.drawAsOutline = drawAsOutline; + } } void updateText_LabelWidget(iLabelWidget *d, const iString *text) { @@ -554,6 +562,10 @@ iChar icon_LabelWidget(const iLabelWidget *d) { return d->icon; } +iBool isWrapped_LabelWidget(const iLabelWidget *d) { + return d->flags.wrap; +} + const iString *text_LabelWidget(const iLabelWidget *d) { if (!d) return collectNew_String(); return &d->label; diff --git a/src/ui/labelwidget.h b/src/ui/labelwidget.h index f6343273..b8b6fd87 100644 --- a/src/ui/labelwidget.h +++ b/src/ui/labelwidget.h @@ -32,6 +32,7 @@ iDeclareObjectConstructionArgs(LabelWidget, const char *label, const char *comma void setAlignVisually_LabelWidget(iLabelWidget *, iBool alignVisual); void setNoAutoMinHeight_LabelWidget(iLabelWidget *, iBool noAutoMinHeight); void setNoTopFrame_LabelWidget (iLabelWidget *, iBool noTopFrame); +void setWrap_LabelWidget (iLabelWidget *, iBool wrap); void setOutline_LabelWidget (iLabelWidget *, iBool drawAsOutline); void setFont_LabelWidget (iLabelWidget *, int fontId); void setTextColor_LabelWidget (iLabelWidget *, int color); @@ -53,6 +54,7 @@ const iString * text_LabelWidget (const iLabelWidget *); const iString * sourceText_LabelWidget (const iLabelWidget *); /* untranslated */ const iString * command_LabelWidget (const iLabelWidget *); iChar icon_LabelWidget (const iLabelWidget *); +iBool isWrapped_LabelWidget (const iLabelWidget *); iLabelWidget *newKeyMods_LabelWidget(const char *label, int key, int kmods, const char *command); iLabelWidget *newColor_LabelWidget (const char *text, int color); diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index b4948821..c0a22e99 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -523,15 +523,17 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { addChild_Widget(div, iClob(makePadding_Widget(gap_UI))); addChild_Widget(div, iClob(new_LabelWidget("${menu.identity.import}", "ident.import"))); addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); /* pad */ + iLabelWidget *linkLabel; setBackgroundColor_Widget( addChildFlags_Widget( div, - iClob(new_LabelWidget(format_CStr(cstr_Lang("ident.gotohelp"), + iClob(linkLabel = new_LabelWidget(format_CStr(cstr_Lang("ident.gotohelp"), uiTextStrong_ColorEscape, restore_ColorEscape), "!open newtab:1 gotoheading:1.6 url:about:help")), - frameless_WidgetFlag | fixedHeight_WidgetFlag | wrapText_WidgetFlag), + frameless_WidgetFlag | fixedHeight_WidgetFlag), uiBackgroundSidebar_ColorId); + setWrap_LabelWidget(linkLabel, iTrue); addChild_Widget(d->blank, iClob(div)); } // arrange_Widget(d->blank); @@ -631,7 +633,8 @@ void init_SidebarWidget(iSidebarWidget *d, enum iSidebarSide side) { setBackgroundColor_Widget(w, none_ColorId); setFlags_Widget(w, collapse_WidgetFlag | hidden_WidgetFlag | arrangeHorizontal_WidgetFlag | - resizeWidthOfChildren_WidgetFlag, + resizeWidthOfChildren_WidgetFlag | noFadeBackground_WidgetFlag | + noShadowBorder_WidgetFlag, iTrue); iZap(d->modeScroll); d->side = side; diff --git a/src/ui/util.c b/src/ui/util.c index 3a9bc400..e0b05a44 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -710,7 +710,8 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { menu, iClob(newKeyMods_LabelWidget(labelText, item->key, item->kmods, item->command)), noBackground_WidgetFlag | frameless_WidgetFlag | alignLeft_WidgetFlag | - drawKey_WidgetFlag | (isInfo ? wrapText_WidgetFlag : 0) | itemFlags); + drawKey_WidgetFlag | itemFlags); + setWrap_LabelWidget(label, isInfo); haveIcons |= checkIcon_LabelWidget(label); updateSize_LabelWidget(label); /* drawKey was set */ if (isInfo) { @@ -776,7 +777,7 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) { if (isInstance_Object(i.object, &Class_LabelWidget)) { iLabelWidget *label = i.object; const iBool isCaution = startsWith_String(text_LabelWidget(label), uiTextCaution_ColorEscape); - if (flags_Widget(as_Widget(label)) & wrapText_WidgetFlag) { + if (isWrapped_LabelWidget(label)) { continue; } if (deviceType_App() == desktop_AppDeviceType) { diff --git a/src/ui/widget.c b/src/ui/widget.c index 4e1c9119..992f115d 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -1164,7 +1164,7 @@ void drawBackground_Widget(const iWidget *d) { shadowBorder = iFalse; } } - if (shadowBorder) { + if (shadowBorder && ~d->flags & noShadowBorder_WidgetFlag) { iPaint p; init_Paint(&p); drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30); diff --git a/src/ui/widget.h b/src/ui/widget.h index 79565483..41784b99 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -92,7 +92,7 @@ enum iWidgetFlag { #define centerHorizontal_WidgetFlag iBit64(33) #define moveToParentLeftEdge_WidgetFlag iBit64(34) #define moveToParentRightEdge_WidgetFlag iBit64(35) -#define wrapText_WidgetFlag iBit64(36) +#define noShadowBorder_WidgetFlag iBit64(36) #define borderTop_WidgetFlag iBit64(37) #define overflowScrollable_WidgetFlag iBit64(38) #define focusRoot_WidgetFlag iBit64(39) -- cgit v1.2.3 From 626cf01c7102b89f0ff352603f6b89f2aa9425c0 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Wed, 16 Jun 2021 18:35:29 +0300 Subject: DocumentWidget: Spaces in image URL On a generated image page, the image link URL was not escaped so any spaces would cause it to be broken. These content links also should not use the background highlighting meant for links open in other tabs. --- src/ui/documentwidget.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 6184a75a..5480523c 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -1432,7 +1432,9 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, const iGmResponse linkTitle = baseName_Path(collect_String(newRange_String(parts.path))).start; } - format_String(&str, "=> %s %s\n", cstr_String(d->mod.url), linkTitle); + format_String(&str, "=> %s %s\n", + cstr_String(withSpacesEncoded_String(d->mod.url)), + linkTitle); setData_Media(media_GmDocument(d->doc), imgLinkId, mimeStr, @@ -3870,7 +3872,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { } #endif /* Fill the background. */ { - if (run->linkId && linkFlags & isOpen_GmLinkFlag) { + if (run->linkId && linkFlags & isOpen_GmLinkFlag && ~linkFlags & content_GmLinkFlag) { /* Open links get a highlighted background. */ int bg = tmBackgroundOpenLink_ColorId; const int frame = tmFrameOpenLink_ColorId; -- cgit v1.2.3 From 4c57a05442c86b8bb79df58e0718810031956b90 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 17 Jun 2021 07:09:20 +0300 Subject: Cleanup --- src/ui/inputwidget.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'src') diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 32fb5ccb..39da1c3b 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c @@ -1357,13 +1357,7 @@ static void draw_InputWidget_(const iInputWidget *d) { } iPaint p; init_Paint(&p); - /* `lines` is already up to date and ready for drawing. */ - /* TODO: If empty, draw the hint. */ -// iString *text = visText_InputWidget_(d); -// if (isWhite_(text) && !isEmpty_String(&d->hint)) { -// set_String(text, &d->hint); -// isHint = iTrue; -// } + /* `lines` is already up to date and ready for drawing. */ fillRect_Paint( &p, bounds, isFocused ? uiInputBackgroundFocused_ColorId : uiInputBackground_ColorId); drawRectThickness_Paint(&p, @@ -1374,7 +1368,6 @@ static void draw_InputWidget_(const iInputWidget *d) { setClip_Paint(&p, adjusted_Rect(bounds, init_I2(d->leftPadding, 0), init_I2(-d->rightPadding, w->flags & extraPadding_WidgetFlag ? -gap_UI / 2 : 0))); const iRect contentBounds = contentBounds_InputWidget_(d); -// const iInt2 textOrigin = textOrigin_InputWidget_(d); //, cstr_String(text)); iInt2 drawPos = topLeft_Rect(contentBounds); const int fg = isHint ? uiAnnotation_ColorId : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId @@ -1418,19 +1411,6 @@ static void draw_InputWidget_(const iInputWidget *d) { drawPos.y += lineHeight_Text(d->font); } } -// if (d->buffered && !isFocused && !isHint) { -// /* Most input widgets will use this, since only one is focused at a time. */ -// draw_TextBuf(d->buffered, textOrigin, white_ColorId); -// } -// else { -// draw_Text(d->font, -// textOrigin, -// isHint ? uiAnnotation_ColorId -// : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId -// : uiInputText_ColorId, -// "%s", -// cstr_String(text)); -// } unsetClip_Paint(&p); /* Cursor blinking. */ if (isFocused && d->cursorVis) { @@ -1474,7 +1454,6 @@ static void draw_InputWidget_(const iInputWidget *d) { deinit_String(&cur); } } -// delete_String(text); drawChildren_Widget(w); } -- cgit v1.2.3 From b4699418195cd7d6057aeef256ed2fc824f3862d Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Wed, 16 Jun 2021 18:35:29 +0300 Subject: DocumentWidget: Spaces in image URL On a generated image page, the image link URL was not escaped so any spaces would cause it to be broken. These content links also should not use the background highlighting meant for links open in other tabs. --- src/ui/documentwidget.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 599e7ef3..7f8d70a3 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -1469,7 +1469,9 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, linkTitle = baseName_Path(collect_String(newRange_String(parts.path))).start; } - format_String(&str, "=> %s %s\n", cstr_String(d->mod.url), linkTitle); + format_String(&str, "=> %s %s\n", + cstr_String(withSpacesEncoded_String(d->mod.url)), + linkTitle); setData_Media(media_GmDocument(d->doc), imgLinkId, mimeStr, @@ -4092,7 +4094,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { } #endif /* Fill the background. */ { - if (run->linkId && linkFlags & isOpen_GmLinkFlag) { + if (run->linkId && linkFlags & isOpen_GmLinkFlag && ~linkFlags & content_GmLinkFlag) { /* Open links get a highlighted background. */ int bg = tmBackgroundOpenLink_ColorId; const int frame = tmFrameOpenLink_ColorId; -- cgit v1.2.3 From 1455c26b793be67f5c47a7f7dbf06c9c263d8e2a Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 17 Jun 2021 07:09:20 +0300 Subject: Cleanup --- src/ui/inputwidget.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'src') diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 9571a97e..b108ee17 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c @@ -1365,13 +1365,7 @@ static void draw_InputWidget_(const iInputWidget *d) { } iPaint p; init_Paint(&p); - /* `lines` is already up to date and ready for drawing. */ - /* TODO: If empty, draw the hint. */ -// iString *text = visText_InputWidget_(d); -// if (isWhite_(text) && !isEmpty_String(&d->hint)) { -// set_String(text, &d->hint); -// isHint = iTrue; -// } + /* `lines` is already up to date and ready for drawing. */ fillRect_Paint( &p, bounds, isFocused ? uiInputBackgroundFocused_ColorId : uiInputBackground_ColorId); drawRectThickness_Paint(&p, @@ -1382,7 +1376,6 @@ static void draw_InputWidget_(const iInputWidget *d) { setClip_Paint(&p, adjusted_Rect(bounds, init_I2(d->leftPadding, 0), init_I2(-d->rightPadding, w->flags & extraPadding_WidgetFlag ? -gap_UI / 2 : 0))); const iRect contentBounds = contentBounds_InputWidget_(d); -// const iInt2 textOrigin = textOrigin_InputWidget_(d); //, cstr_String(text)); iInt2 drawPos = topLeft_Rect(contentBounds); const int fg = isHint ? uiAnnotation_ColorId : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId @@ -1426,19 +1419,6 @@ static void draw_InputWidget_(const iInputWidget *d) { drawPos.y += lineHeight_Text(d->font); } } -// if (d->buffered && !isFocused && !isHint) { -// /* Most input widgets will use this, since only one is focused at a time. */ -// draw_TextBuf(d->buffered, textOrigin, white_ColorId); -// } -// else { -// draw_Text(d->font, -// textOrigin, -// isHint ? uiAnnotation_ColorId -// : isFocused && !isEmpty_Array(&d->text) ? uiInputTextFocused_ColorId -// : uiInputText_ColorId, -// "%s", -// cstr_String(text)); -// } unsetClip_Paint(&p); /* Cursor blinking. */ if (isFocused && d->cursorVis) { @@ -1482,7 +1462,6 @@ static void draw_InputWidget_(const iInputWidget *d) { deinit_String(&cur); } } -// delete_String(text); drawChildren_Widget(w); } -- cgit v1.2.3 From aac0bc8a49d06fffcaca7a1fd3c35de035ac3e7c Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 17 Jun 2021 11:06:47 +0300 Subject: Color: Load palettes from a config file IssueID #263 --- src/app.c | 1 + src/ui/color.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/ui/color.h | 2 ++ 3 files changed, 81 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/app.c b/src/app.c index 749d998e..3a1ee101 100644 --- a/src/app.c +++ b/src/app.c @@ -761,6 +761,7 @@ static void init_App_(iApp *d, int argc, char **argv) { setupApplication_iOS(); #endif init_Keys(); + loadPalette_Color(dataDir_App_()); setThemePalette_Color(d->prefs.theme); /* default UI colors */ loadPrefs_App_(d); load_Keys(dataDir_App_()); diff --git a/src/ui/color.c b/src/ui/color.c index 6c51bc06..05ec1f6f 100644 --- a/src/ui/color.c +++ b/src/ui/color.c @@ -24,11 +24,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "root.h" #include "app.h" +#include +#include #include static const iColor transparent_; -static const iColor darkPalette_[] = { +static iColor darkPalette_[] = { { 0, 0, 0, 255 }, { 40, 40, 40, 255 }, { 80, 80, 80, 255 }, @@ -47,7 +49,7 @@ static const iColor darkPalette_[] = { { 0, 200, 0, 255 }, }; -static const iColor lightPalette_[] = { +static iColor lightPalette_[] = { { 0, 0, 0, 255 }, { 75, 75, 75, 255 }, { 150, 150, 150, 255 }, @@ -804,3 +806,77 @@ iColor ansiForeground_Color(iRangecc escapeSequence, int fallback) { } return clr; } + +iBool loadPalette_Color(const char *path) { + iBool wasLoaded = iFalse; + iFile *f = newCStr_File(concatPath_CStr(path, "palette.txt")); + if (open_File(f, text_FileMode | readOnly_FileMode)) { + iColor *dstPal = darkPalette_; + iRangecc srcLine = iNullRange; + const iBlock *src = collect_Block(readAll_File(f)); + while (nextSplit_Rangecc(range_Block(src), "\n", &srcLine)) { + iRangecc line = srcLine; + trim_Rangecc(&line); + if (isEmpty_Range(&line)) { + continue; + } + if (*line.start == '#') { + /* Control directive. */ + line.start++; + trim_Rangecc(&line); + if (equalCase_Rangecc(line, "dark")) { + dstPal = darkPalette_; + } + else if (equalCase_Rangecc(line, "light")) { + dstPal = lightPalette_; + } + continue; + } + static const struct { + const char *label; + int paletteIndex; + } colors_[] = { + { "black:", 0 }, { "gray25:", 1 }, { "gray50:", 2 }, { "gray75:", 3 }, + { "white:", 4 }, { "brown:", 5 }, { "orange:", 6 }, { "teal:", 7 }, + { "cyan:", 8 }, { "yellow:", 9 }, { "red:", 10 }, { "magenta:", 11 }, + { "blue:", 12 }, { "green:", 13 }, + }; + iForIndices(i, colors_) { + if (startsWithCase_Rangecc(line, colors_[i].label)) { + iColor *dst = &dstPal[colors_[i].paletteIndex]; + line.start += strlen(colors_[i].label); + trim_Rangecc(&line); + if (!isEmpty_Range(&line)) { + if (*line.start == '#') { + /* Hexadecimal color. */ + line.start++; + if (size_Range(&line) == 6) { + iBlock *vals = hexDecode_Rangecc(line); + iAssert(size_Block(vals) == 3); + const uint8_t *rgb = constData_Block(vals); + *dst = (iColor){ rgb[0], rgb[1], rgb[2], 255 }; + delete_Block(vals); + } + else { + fprintf(stderr, "[Color] invalid custom color: %s\n", + cstr_Rangecc(line)); + } + } + else { + unsigned int red = 0, green = 0, blue = 0; + sscanf(line.start, "%u %u %u", &red, &green, &blue); + if (red > 255 || green > 255 || blue > 255) { + fprintf(stderr, "[Color] RGB value(s) out of range: %s\n", + cstr_Rangecc(line)); + } + *dst = (iColor){ red, green, blue, 255 }; + } + } + } + } + } + wasLoaded = iTrue; + } + iRelease(f); + return wasLoaded; +} diff --git a/src/ui/color.h b/src/ui/color.h index aafc1794..37ec49eb 100644 --- a/src/ui/color.h +++ b/src/ui/color.h @@ -244,7 +244,9 @@ iLocalDef void setHsl_Color(int color, iHSLColor hsl) { set_Color(color, rgb_HSLColor(hsl)); } +iBool loadPalette_Color (const char *path); void setThemePalette_Color (enum iColorTheme theme); iColor ansiForeground_Color (iRangecc escapeSequence, int fallback); const char * escape_Color (int color); + -- cgit v1.2.3 From dcf520652cd2279a1d4dd78361d589ede0c36eaf Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 24 Jun 2021 07:19:09 +0300 Subject: Text: Tune kerning on Nunito --- src/ui/text.c | 28 +++++++++++++++++++++++++--- src/ui/text.h | 3 ++- 2 files changed, 27 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/ui/text.c b/src/ui/text.c index db922715..51865654 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -116,6 +116,7 @@ iDefineTypeConstructionArgs(Glyph, (iChar ch), ch) struct Impl_Font { iBlock * data; + enum iTextFont family; stbtt_fontinfo font; float xScale, yScale; int vertOffset; /* offset due to scaling */ @@ -134,6 +135,15 @@ static void init_Font(iFont *d, const iBlock *data, int height, float scale, enum iFontSize sizeId, iBool isMonospaced) { init_Hash(&d->glyphs); d->data = NULL; + d->family = undefined_TextFont; + /* Note: We only use `family` currently for applying a kerning fix to Nunito. */ + if (data == &fontNunitoRegular_Embedded || + data == &fontNunitoBold_Embedded || + data == &fontNunitoExtraBold_Embedded || + data == &fontNunitoLightItalic_Embedded || + data == &fontNunitoExtraLight_Embedded) { + d->family = nunito_TextFont; + } d->isMonospaced = isMonospaced; d->height = height; iZap(d->font); @@ -1128,14 +1138,26 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) { const iChar next = nextChar_(&peek, args->text.end); if (enableKerning_Text && !d->manualKernOnly && next) { const uint32_t nextGlyphIndex = glyphIndex_Font_(glyph->font, next); - const int kern = stbtt_GetGlyphKernAdvance( + int kern = stbtt_GetGlyphKernAdvance( &glyph->font->font, glyph->glyphIndex, nextGlyphIndex); + /* Nunito needs some kerning fixes. */ + if (glyph->font->family == nunito_TextFont) { + if (ch == 'W' && (next == 'i' || next == 'h')) { + kern = -30; + } + else if (ch == 'T' && next == 'h') { + kern = -15; + } + else if (ch == 'V' && next == 'i') { + kern = -15; + } + } if (kern) { // printf("%lc(%u) -> %lc(%u): kern %d (%f)\n", ch, glyph->glyphIndex, next, // nextGlyphIndex, // kern, d->xScale * kern); - xpos += d->xScale * kern; - xposExtend += d->xScale * kern; + xpos += glyph->font->xScale * kern; + xposExtend += glyph->font->xScale * kern; } } } diff --git a/src/ui/text.h b/src/ui/text.h index 2f2bcf3a..768713ee 100644 --- a/src/ui/text.h +++ b/src/ui/text.h @@ -116,7 +116,8 @@ iLocalDef iBool isJapanese_FontId(enum iFontId id) { #define emojiVariationSelector_Char ((iChar) 0xfe0f) enum iTextFont { - nunito_TextFont, + undefined_TextFont = -1, + nunito_TextFont = 0, firaSans_TextFont, literata_TextFont, tinos_TextFont, -- cgit v1.2.3 From 3ca6d5ad281a1006b31f3b7031ecc4693a1b4f89 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 24 Jun 2021 07:31:25 +0300 Subject: DocumentWidget: Fixed link number alignment The link numbers were not aligned with all fonts. --- src/ui/documentwidget.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 5480523c..6dc80a07 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -3933,11 +3933,12 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); if (ordChar) { const char *circle = "\u25ef"; /* Large Circle */ + const int circleFont = defaultContentRegular_FontId; iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), - init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(run->font)) }; + init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) }; drawRange_Text( - run->font, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); - iRect circleArea = visualBounds_Text(run->font, range_CStr(circle)); + circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); + iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle)); addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); drawCentered_Text(defaultContentSmall_FontId, circleArea, -- cgit v1.2.3 From c19b0bbb4cc7d5e373058bd13bec2c9691695b82 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 24 Jun 2021 15:45:02 +0300 Subject: GmDocument: Don't collapse preformatted on Gopher pages IssueID #300 --- src/gmdocument.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/gmdocument.c b/src/gmdocument.c index 4f9f97e7..cde714e7 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -285,6 +285,12 @@ static void clearLinks_GmDocument_(iGmDocument *d) { clear_PtrArray(&d->links); } +static iBool isGopher_GmDocument_(const iGmDocument *d) { + const iRangecc scheme = urlScheme_String(&d->url); + return (equalCase_Rangecc(scheme, "gopher") || + equalCase_Rangecc(scheme, "finger")); +} + static iBool isForcedMonospace_GmDocument_(const iGmDocument *d) { const iRangecc scheme = urlScheme_String(&d->url); if (equalCase_Rangecc(scheme, "gemini")) { @@ -360,6 +366,7 @@ static void updateOpenURLs_GmDocument_(iGmDocument *d) { static void doLayout_GmDocument_(iGmDocument *d) { const iPrefs *prefs = prefs_App(); const iBool isMono = isForcedMonospace_GmDocument_(d); + const iBool isGopher = isGopher_GmDocument_(d); const iBool isNarrow = d->size.x < 90 * gap_Text; const iBool isVeryNarrow = d->size.x <= 70 * gap_Text; const iBool isExtremelyNarrow = d->size.x <= 60 * gap_Text; @@ -475,7 +482,7 @@ static void doLayout_GmDocument_(iGmDocument *d) { meta.flags = constValue_Array(oldPreMeta, preIndex, iGmPreMeta).flags & folded_GmPreMetaFlag; } - else if (prefs->collapsePreOnLoad) { + else if (prefs->collapsePreOnLoad && !isGopher) { meta.flags |= folded_GmPreMetaFlag; } pushBack_Array(&d->preMeta, &meta); -- cgit v1.2.3 From 3889d10ab44a0f4d03ae9a20b2ae93022ac0f33a Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 24 Jun 2021 22:04:03 +0300 Subject: GmDocument: Mark opened URLs as visited Set a formatting flag on opened links. --- src/gmdocument.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/gmdocument.c b/src/gmdocument.c index cde714e7..002494b6 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -1423,6 +1423,9 @@ iBool updateOpenURLs_GmDocument(iGmDocument *d) { const iBool isOpen = contains_StringSet(d->openURLs, &link->url); if (isOpen ^ ((link->flags & isOpen_GmLinkFlag) != 0)) { iChangeFlags(link->flags, isOpen_GmLinkFlag, isOpen); + if (isOpen) { + link->flags |= visited_GmLinkFlag; + } wasChanged = iTrue; } } -- cgit v1.2.3 From 2dcaf9c3f2bc5fda355d5a6492127a2dc6a88c29 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Fri, 25 Jun 2021 13:37:47 +0300 Subject: Touch: Debugging helper IssueID #298 --- src/app.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'src') diff --git a/src/app.c b/src/app.c index 663fa2e8..ce0898ba 100644 --- a/src/app.c +++ b/src/app.c @@ -59,6 +59,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include +//#define LAGRANGE_ENABLE_MOUSE_TOUCH_EMULATION 1 + #if defined (iPlatformAppleDesktop) # include "macos.h" #endif @@ -1149,6 +1151,45 @@ void processEvents_App(enum iAppEventMode eventMode) { ev.wheel.x = -ev.wheel.x; #endif } +#if defined (LAGRANGE_ENABLE_MOUSE_TOUCH_EMULATION) + /* Convert mouse events to finger events to test the touch handling. */ { + static float xPrev = 0.0f; + static float yPrev = 0.0f; + if (ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP) { + const float xf = (d->window->pixelRatio * ev.button.x) / (float) d->window->size.x; + const float yf = (d->window->pixelRatio * ev.button.y) / (float) d->window->size.y; + ev.type = (ev.type == SDL_MOUSEBUTTONDOWN ? SDL_FINGERDOWN : SDL_FINGERUP); + ev.tfinger.x = xf; + ev.tfinger.y = yf; + ev.tfinger.dx = xf - xPrev; + ev.tfinger.dy = yf - yPrev; + xPrev = xf; + yPrev = yf; + ev.tfinger.fingerId = 0x1234; + ev.tfinger.pressure = 1.0f; + ev.tfinger.timestamp = SDL_GetTicks(); + ev.tfinger.touchId = SDL_TOUCH_MOUSEID; + } + else if (ev.type == SDL_MOUSEMOTION) { + if (~ev.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT)) { + continue; /* only when pressing a button */ + } + const float xf = (d->window->pixelRatio * ev.motion.x) / (float) d->window->size.x; + const float yf = (d->window->pixelRatio * ev.motion.y) / (float) d->window->size.y; + ev.type = SDL_FINGERMOTION; + ev.tfinger.x = xf; + ev.tfinger.y = yf; + ev.tfinger.dx = xf - xPrev; + ev.tfinger.dy = yf - yPrev; + xPrev = xf; + yPrev = yf; + ev.tfinger.fingerId = 0x1234; + ev.tfinger.pressure = 1.0f; + ev.tfinger.timestamp = SDL_GetTicks(); + ev.tfinger.touchId = SDL_TOUCH_MOUSEID; + } + } +#endif iBool wasUsed = processEvent_Window(d->window, &ev); if (!wasUsed) { /* There may be a key bindings for this. */ -- cgit v1.2.3