summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-06-14 15:36:48 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-06-14 15:36:48 +0300
commitfe238b29132b43f07aa88cf6bb60bec1ff0d3f8d (patch)
tree34d08228270960a73f5586ae907a12d7adb7b393 /src
parent203426a88b2d94ed56f7d3f8f8eb18a7457cab11 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/app.c7
-rw-r--r--src/defs.h3
-rw-r--r--src/gmdocument.c18
-rw-r--r--src/gmdocument.h2
-rw-r--r--src/gmutil.c4
-rw-r--r--src/history.c31
-rw-r--r--src/history.h6
-rw-r--r--src/ui/documentwidget.c103
-rw-r--r--src/ui/documentwidget.h8
-rw-r--r--src/ui/sidebarwidget.c14
-rw-r--r--src/ui/text.c4
-rw-r--r--src/ui/touch.c49
-rw-r--r--src/ui/widget.c55
-rw-r--r--src/ui/widget.h9
14 files changed, 207 insertions, 106 deletions
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) {
2244 } 2244 }
2245 else if (equal_Command(cmd, "open")) { 2245 else if (equal_Command(cmd, "open")) {
2246 iString *url = collectNewCStr_String(suffixPtr_Command(cmd, "url")); 2246 iString *url = collectNewCStr_String(suffixPtr_Command(cmd, "url"));
2247 const iBool noProxy = argLabel_Command(cmd, "noproxy"); 2247 const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0;
2248 const iBool fromSidebar = argLabel_Command(cmd, "fromsidebar") != 0;
2248 iUrl parts; 2249 iUrl parts;
2249 init_Url(&parts, url); 2250 init_Url(&parts, url);
2250 if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") || 2251 if (argLabel_Command(cmd, "default") || equalCase_Rangecc(parts.scheme, "mailto") ||
@@ -2295,7 +2296,9 @@ iBool handleCommand_App(const char *cmd) {
2295 else { 2296 else {
2296 urlEncodePath_String(url); 2297 urlEncodePath_String(url);
2297 } 2298 }
2298 setUrlFromCache_DocumentWidget(doc, url, isHistory); 2299 setUrlFlags_DocumentWidget(doc, url,
2300 (isHistory ? useCachedContentIfAvailable_DocumentWidgetSetUrlFlag : 0) |
2301 (fromSidebar ? openedFromSidebar_DocumentWidgetSetUrlFlag : 0));
2299 /* Optionally, jump to a text in the document. This will only work if the document 2302 /* Optionally, jump to a text in the document. This will only work if the document
2300 is already available, e.g., it's from "about:" or restored from cache. */ 2303 is already available, e.g., it's from "about:" or restored from cache. */
2301 const iRangecc gotoHeading = range_Command(cmd, "gotoheading"); 2304 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 {
35 addedResponseTimestamps_FileVersion = 1, 35 addedResponseTimestamps_FileVersion = 1,
36 multipleRoots_FileVersion = 2, 36 multipleRoots_FileVersion = 2,
37 serializedSidebarState_FileVersion = 3, 37 serializedSidebarState_FileVersion = 3,
38 addedRecentUrlFlags_FileVersion = 4,
38 /* meta */ 39 /* meta */
39 idents_FileVersion = 1, /* version used by GmCerts/idents.lgr */ 40 idents_FileVersion = 1, /* version used by GmCerts/idents.lgr */
40 latest_FileVersion = 3, 41 latest_FileVersion = 4,
41}; 42};
42 43
43/* Icons */ 44/* 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 {
92 iChar siteIcon; 92 iChar siteIcon;
93 iMedia * media; 93 iMedia * media;
94 iStringSet *openURLs; /* currently open URLs for highlighting links */ 94 iStringSet *openURLs; /* currently open URLs for highlighting links */
95 iBool isPaletteValid;
96 iColor palette[tmMax_ColorId]; /* copy of the color palette */
95}; 97};
96 98
97iDefineObjectConstruction(GmDocument) 99iDefineObjectConstruction(GmDocument)
@@ -889,6 +891,8 @@ void init_GmDocument(iGmDocument *d) {
889 d->siteIcon = 0; 891 d->siteIcon = 0;
890 d->media = new_Media(); 892 d->media = new_Media();
891 d->openURLs = NULL; 893 d->openURLs = NULL;
894 d->isPaletteValid = iFalse;
895 iZap(d->palette);
892} 896}
893 897
894void deinit_GmDocument(iGmDocument *d) { 898void deinit_GmDocument(iGmDocument *d) {
@@ -1401,6 +1405,20 @@ void setThemeSeed_GmDocument(iGmDocument *d, const iBlock *seed) {
1401 } 1405 }
1402 printf("---\n"); 1406 printf("---\n");
1403#endif 1407#endif
1408 /* Color functions operate on the global palette for convenience, but we may need to switch
1409 palettes on the fly if more than one GmDocument is being displayed simultaneously. */
1410 memcpy(d->palette, get_Root()->tmPalette, sizeof(d->palette));
1411 d->isPaletteValid = iTrue;
1412}
1413
1414void makePaletteGlobal_GmDocument(const iGmDocument *d) {
1415 if (d->isPaletteValid) {
1416 memcpy(get_Root()->tmPalette, d->palette, sizeof(d->palette));
1417 }
1418}
1419
1420void invalidatePalette_GmDocument(iGmDocument *d) {
1421 d->isPaletteValid = iFalse;
1404} 1422}
1405 1423
1406void setFormat_GmDocument(iGmDocument *d, enum iSourceFormat format) { 1424void 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);
170void setSource_GmDocument (iGmDocument *, const iString *source, int width, 170void setSource_GmDocument (iGmDocument *, const iString *source, int width,
171 enum iGmDocumentUpdate updateType); 171 enum iGmDocumentUpdate updateType);
172void foldPre_GmDocument (iGmDocument *, uint16_t preId); 172void foldPre_GmDocument (iGmDocument *, uint16_t preId);
173void invalidatePalette_GmDocument(iGmDocument *);
174void makePaletteGlobal_GmDocument(const iGmDocument *); /* copies document colors to the global palette */
173 175
174//void reset_GmDocument (iGmDocument *); /* free images */ 176//void reset_GmDocument (iGmDocument *); /* free images */
175 177
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) {
556 iString *head = newRange_String( 556 iString *head = newRange_String(
557 (iRangecc){ constBegin_String(url) + fragPos + 1, constEnd_String(url) }); 557 (iRangecc){ constBegin_String(url) + fragPos + 1, constEnd_String(url) });
558 format_String(cmd, 558 format_String(cmd,
559 "open newtab:%d gotourlheading:%s url:%s", 559 "open fromsidebar:1 newtab:%d gotourlheading:%s url:%s",
560 newTab, 560 newTab,
561 cstr_String(head), 561 cstr_String(head),
562 cstr_Rangecc((iRangecc){ constBegin_String(url), 562 cstr_Rangecc((iRangecc){ constBegin_String(url),
@@ -564,7 +564,7 @@ const iString *feedEntryOpenCommand_String(const iString *url, int newTab) {
564 delete_String(head); 564 delete_String(head);
565 } 565 }
566 else { 566 else {
567 format_String(cmd, "open newtab:%d url:%s", newTab, cstr_String(url)); 567 format_String(cmd, "open fromsidebar:1 newtab:%d url:%s", newTab, cstr_String(url));
568 } 568 }
569 return cmd; 569 return cmd;
570 } 570 }
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) {
37 d->normScrollY = 0; 37 d->normScrollY = 0;
38 d->cachedResponse = NULL; 38 d->cachedResponse = NULL;
39 d->cachedDoc = NULL; 39 d->cachedDoc = NULL;
40 d->flags.openedFromSidebar = iFalse;
40} 41}
41 42
42void deinit_RecentUrl(iRecentUrl *d) { 43void deinit_RecentUrl(iRecentUrl *d) {
@@ -53,6 +54,7 @@ iRecentUrl *copy_RecentUrl(const iRecentUrl *d) {
53 copy->normScrollY = d->normScrollY; 54 copy->normScrollY = d->normScrollY;
54 copy->cachedResponse = d->cachedResponse ? copy_GmResponse(d->cachedResponse) : NULL; 55 copy->cachedResponse = d->cachedResponse ? copy_GmResponse(d->cachedResponse) : NULL;
55 copy->cachedDoc = ref_Object(d->cachedDoc); 56 copy->cachedDoc = ref_Object(d->cachedDoc);
57 copy->flags = d->flags;
56 return copy; 58 return copy;
57} 59}
58 60
@@ -171,6 +173,7 @@ void serialize_History(const iHistory *d, iStream *outs) {
171 const iRecentUrl *item = i.value; 173 const iRecentUrl *item = i.value;
172 serialize_String(&item->url, outs); 174 serialize_String(&item->url, outs);
173 write32_Stream(outs, item->normScrollY * 1.0e6f); 175 write32_Stream(outs, item->normScrollY * 1.0e6f);
176 writeU16_Stream(outs, item->flags.openedFromSidebar ? iBit(1) : 0);
174 if (item->cachedResponse) { 177 if (item->cachedResponse) {
175 write8_Stream(outs, 1); 178 write8_Stream(outs, 1);
176 serialize_GmResponse(item->cachedResponse, outs); 179 serialize_GmResponse(item->cachedResponse, outs);
@@ -192,6 +195,12 @@ void deserialize_History(iHistory *d, iStream *ins) {
192 init_RecentUrl(&item); 195 init_RecentUrl(&item);
193 deserialize_String(&item.url, ins); 196 deserialize_String(&item.url, ins);
194 item.normScrollY = (float) read32_Stream(ins) / 1.0e6f; 197 item.normScrollY = (float) read32_Stream(ins) / 1.0e6f;
198 if (version_Stream(ins) >= addedRecentUrlFlags_FileVersion) {
199 uint16_t flags = readU16_Stream(ins);
200 if (flags & iBit(1)) {
201 item.flags.openedFromSidebar = iTrue;
202 }
203 }
195 if (read8_Stream(ins)) { 204 if (read8_Stream(ins)) {
196 item.cachedResponse = new_GmResponse(); 205 item.cachedResponse = new_GmResponse();
197 deserialize_GmResponse(item.cachedResponse, ins); 206 deserialize_GmResponse(item.cachedResponse, ins);
@@ -378,12 +387,15 @@ void setCachedResponse_History(iHistory *d, const iGmResponse *response) {
378 unlock_Mutex(d->mtx); 387 unlock_Mutex(d->mtx);
379} 388}
380 389
381void setCachedDocument_History(iHistory *d, iGmDocument *doc) { 390void setCachedDocument_History(iHistory *d, iGmDocument *doc, iBool openedFromSidebar) {
382 lock_Mutex(d->mtx); 391 lock_Mutex(d->mtx);
383 iRecentUrl *item = mostRecentUrl_History(d); 392 iRecentUrl *item = mostRecentUrl_History(d);
384 if (item && item->cachedDoc != doc) { 393 if (item) {
385 iRelease(item->cachedDoc); 394 item->flags.openedFromSidebar = openedFromSidebar;
386 item->cachedDoc = ref_Object(doc); 395 if (item->cachedDoc != doc) {
396 iRelease(item->cachedDoc);
397 item->cachedDoc = ref_Object(doc);
398 }
387 } 399 }
388 unlock_Mutex(d->mtx); 400 unlock_Mutex(d->mtx);
389} 401}
@@ -487,6 +499,17 @@ size_t pruneLeastImportantMemory_History(iHistory *d) {
487 return delta; 499 return delta;
488} 500}
489 501
502void invalidateTheme_History(iHistory *d) {
503 lock_Mutex(d->mtx);
504 iForEach(Array, i, &d->recent) {
505 iRecentUrl *r = i.value;
506 if (r->cachedDoc) {
507 invalidatePalette_GmDocument(r->cachedDoc);
508 }
509 }
510 unlock_Mutex(d->mtx);
511}
512
490const iStringArray *searchContents_History(const iHistory *d, const iRegExp *pattern) { 513const iStringArray *searchContents_History(const iHistory *d, const iRegExp *pattern) {
491 iStringArray *urls = iClob(new_StringArray()); 514 iStringArray *urls = iClob(new_StringArray());
492 lock_Mutex(d->mtx); 515 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 {
39 float normScrollY; /* normalized to document height */ 39 float normScrollY; /* normalized to document height */
40 iGmResponse *cachedResponse; /* kept in memory for quicker back navigation */ 40 iGmResponse *cachedResponse; /* kept in memory for quicker back navigation */
41 iGmDocument *cachedDoc; /* cached copy of the presentation: layout and media (not serialized) */ 41 iGmDocument *cachedDoc; /* cached copy of the presentation: layout and media (not serialized) */
42 struct {
43 uint8_t openedFromSidebar : 1;
44 } flags;
42}; 45};
43 46
44iDeclareType(MemInfo) 47iDeclareType(MemInfo)
@@ -60,7 +63,7 @@ void clear_History (iHistory *);
60void add_History (iHistory *, const iString *url); 63void add_History (iHistory *, const iString *url);
61void replace_History (iHistory *, const iString *url); 64void replace_History (iHistory *, const iString *url);
62void setCachedResponse_History (iHistory *, const iGmResponse *response); 65void setCachedResponse_History (iHistory *, const iGmResponse *response);
63void setCachedDocument_History (iHistory *, iGmDocument *doc); 66void setCachedDocument_History (iHistory *, iGmDocument *doc, iBool openedFromSidebar);
64iBool goBack_History (iHistory *); 67iBool goBack_History (iHistory *);
65iBool goForward_History (iHistory *); 68iBool goForward_History (iHistory *);
66iBool preceding_History (iHistory *d, iRecentUrl *recent_out); 69iBool preceding_History (iHistory *d, iRecentUrl *recent_out);
@@ -72,6 +75,7 @@ iRecentUrl *findUrl_History (iHistory *, const iString *url);
72void clearCache_History (iHistory *); 75void clearCache_History (iHistory *);
73size_t pruneLeastImportant_History (iHistory *); 76size_t pruneLeastImportant_History (iHistory *);
74size_t pruneLeastImportantMemory_History (iHistory *); 77size_t pruneLeastImportantMemory_History (iHistory *);
78void invalidateTheme_History (iHistory *); /* theme has changed, cached contents need updating */
75 79
76iBool atLatest_History (const iHistory *); 80iBool atLatest_History (const iHistory *);
77iBool atOldest_History (const iHistory *); 81iBool 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) {
130 130
131void serialize_PersistentDocumentState(const iPersistentDocumentState *d, iStream *outs) { 131void serialize_PersistentDocumentState(const iPersistentDocumentState *d, iStream *outs) {
132 serialize_String(d->url, outs); 132 serialize_String(d->url, outs);
133 writeU16_Stream(outs, d->reloadInterval & 7); 133 uint16_t params = d->reloadInterval & 7;
134 writeU16_Stream(outs, params);
134 serialize_History(d->history, outs); 135 serialize_History(d->history, outs);
135} 136}
136 137
@@ -223,6 +224,7 @@ enum iDocumentWidgetFlag {
223 movingSelectMarkEnd_DocumentWidgetFlag = iBit(11), 224 movingSelectMarkEnd_DocumentWidgetFlag = iBit(11),
224 otherRootByDefault_DocumentWidgetFlag = iBit(12), /* links open to other root by default */ 225 otherRootByDefault_DocumentWidgetFlag = iBit(12), /* links open to other root by default */
225 urlChanged_DocumentWidgetFlag = iBit(13), 226 urlChanged_DocumentWidgetFlag = iBit(13),
227 openedFromSidebar_DocumentWidgetFlag = iBit(14),
226}; 228};
227 229
228enum iDocumentLinkOrdinalMode { 230enum iDocumentLinkOrdinalMode {
@@ -1606,7 +1608,8 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n
1606 format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached")); 1608 format_String(&d->sourceHeader, cstr_Lang("pageinfo.header.cached"));
1607 set_Block(&d->sourceContent, &resp->body); 1609 set_Block(&d->sourceContent, &resp->body);
1608 updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); 1610 updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue);
1609 setCachedDocument_History(d->mod.history, d->doc); 1611 setCachedDocument_History(d->mod.history, d->doc,
1612 (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0);
1610 } 1613 }
1611 d->state = ready_RequestState; 1614 d->state = ready_RequestState;
1612 postProcessRequestContent_DocumentWidget_(d, iTrue); 1615 postProcessRequestContent_DocumentWidget_(d, iTrue);
@@ -1626,6 +1629,9 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n
1626static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { 1629static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) {
1627 const iRecentUrl *recent = findUrl_History(d->mod.history, withSpacesEncoded_String(d->mod.url)); 1630 const iRecentUrl *recent = findUrl_History(d->mod.history, withSpacesEncoded_String(d->mod.url));
1628 if (recent && recent->cachedResponse) { 1631 if (recent && recent->cachedResponse) {
1632 iChangeFlags(d->flags,
1633 openedFromSidebar_DocumentWidgetFlag,
1634 recent->flags.openedFromSidebar);
1629 updateFromCachedResponse_DocumentWidget_( 1635 updateFromCachedResponse_DocumentWidget_(
1630 d, recent->normScrollY, recent->cachedResponse, recent->cachedDoc); 1636 d, recent->normScrollY, recent->cachedResponse, recent->cachedDoc);
1631 return iTrue; 1637 return iTrue;
@@ -2282,6 +2288,9 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2282 /* The temporary "swipeIn" will display the previous page until the finger is lifted. */ 2288 /* The temporary "swipeIn" will display the previous page until the finger is lifted. */
2283 iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein"); 2289 iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein");
2284 if (!swipeIn) { 2290 if (!swipeIn) {
2291 const iBool sidebarSwipe = (isPortraitPhone_App() &&
2292 d->flags & openedFromSidebar_DocumentWidgetFlag &&
2293 !isVisible_Widget(findWidget_App("sidebar")));
2285 swipeIn = new_DocumentWidget(); 2294 swipeIn = new_DocumentWidget();
2286 setId_Widget(as_Widget(swipeIn), "swipein"); 2295 setId_Widget(as_Widget(swipeIn), "swipein");
2287 setFlags_Widget(as_Widget(swipeIn), 2296 setFlags_Widget(as_Widget(swipeIn),
@@ -2290,16 +2299,18 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2290 swipeIn->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos)); 2299 swipeIn->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos));
2291 swipeIn->widget.rect.size = d->widget.rect.size; 2300 swipeIn->widget.rect.size = d->widget.rect.size;
2292 swipeIn->widget.offsetRef = parent_Widget(w); 2301 swipeIn->widget.offsetRef = parent_Widget(w);
2293 iRecentUrl *recent = new_RecentUrl(); 2302 if (!sidebarSwipe) {
2294 preceding_History(d->mod.history, recent); 2303 iRecentUrl *recent = new_RecentUrl();
2295 if (recent->cachedDoc) { 2304 preceding_History(d->mod.history, recent);
2296 iChangeRef(swipeIn->doc, recent->cachedDoc); 2305 if (recent->cachedDoc) {
2297 updateScrollMax_DocumentWidget_(d); 2306 iChangeRef(swipeIn->doc, recent->cachedDoc);
2298 setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0); 2307 updateScrollMax_DocumentWidget_(d);
2299 updateVisible_DocumentWidget_(swipeIn); 2308 setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0);
2300 swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag; 2309 updateVisible_DocumentWidget_(swipeIn);
2310 swipeIn->drawBufs->flags |= updateTimestampBuf_DrawBufsFlag | updateSideBuf_DrawBufsFlag;
2311 }
2312 delete_RecentUrl(recent);
2301 } 2313 }
2302 delete_RecentUrl(recent);
2303 addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos); 2314 addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos);
2304 } 2315 }
2305 } 2316 }
@@ -2326,7 +2337,9 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2326 destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */ 2337 destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */
2327 } 2338 }
2328 if (flags_Widget(w) & dragged_WidgetFlag) { 2339 if (flags_Widget(w) & dragged_WidgetFlag) {
2329 setVisualOffset_Widget(w, width_Widget(w) + offset, animSpan, 0); 2340 setVisualOffset_Widget(w, width_Widget(w) +
2341 width_Widget(d) * offset / size_Root(w->root).x,
2342 animSpan, 0);
2330 } 2343 }
2331 else { 2344 else {
2332 setVisualOffset_Widget(w, offset / 4, animSpan, 0); 2345 setVisualOffset_Widget(w, offset / 4, animSpan, 0);
@@ -2426,6 +2439,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2426 return iFalse; 2439 return iFalse;
2427 } 2440 }
2428 else if (equal_Command(cmd, "theme.changed") && document_App() == d) { 2441 else if (equal_Command(cmd, "theme.changed") && document_App() == d) {
2442// invalidateTheme_History(d->mod.history); /* cached colors */
2429 updateTheme_DocumentWidget_(d); 2443 updateTheme_DocumentWidget_(d);
2430 updateVisible_DocumentWidget_(d); 2444 updateVisible_DocumentWidget_(d);
2431 updateTrust_DocumentWidget_(d, NULL); 2445 updateTrust_DocumentWidget_(d, NULL);
@@ -2691,7 +2705,8 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2691 (startsWithCase_String(meta_GmRequest(d->request), "text/") || 2705 (startsWithCase_String(meta_GmRequest(d->request), "text/") ||
2692 !cmp_String(&d->sourceMime, mimeType_Gempub))) { 2706 !cmp_String(&d->sourceMime, mimeType_Gempub))) {
2693 setCachedResponse_History(d->mod.history, lockResponse_GmRequest(d->request)); 2707 setCachedResponse_History(d->mod.history, lockResponse_GmRequest(d->request));
2694 setCachedDocument_History(d->mod.history, d->doc); /* keeps a ref */ 2708 setCachedDocument_History(d->mod.history, d->doc, /* keeps a ref */
2709 (d->flags & openedFromSidebar_DocumentWidgetFlag) != 0);
2695 unlockResponse_GmRequest(d->request); 2710 unlockResponse_GmRequest(d->request);
2696 } 2711 }
2697 } 2712 }
@@ -2821,6 +2836,15 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2821 return iTrue; 2836 return iTrue;
2822 } 2837 }
2823 else if (equal_Command(cmd, "navigate.back") && document_App() == d) { 2838 else if (equal_Command(cmd, "navigate.back") && document_App() == d) {
2839 if (isPortraitPhone_App()) {
2840 if (d->flags & openedFromSidebar_DocumentWidgetFlag &&
2841 !isVisible_Widget(findWidget_App("sidebar"))) {
2842 postCommand_App("sidebar.toggle");
2843 showToolbars_Root(get_Root(), iTrue);
2844 return iTrue;
2845 }
2846 d->flags &= ~openedFromSidebar_DocumentWidgetFlag;
2847 }
2824 if (d->request) { 2848 if (d->request) {
2825 postCommandf_Root(w->root, 2849 postCommandf_Root(w->root,
2826 "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url)); 2850 "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
3499 pushBack_Array( 3523 pushBack_Array(
3500 &items, 3524 &items,
3501 &(iMenuItem){ delete_Icon " " uiTextCaution_ColorEscape 3525 &(iMenuItem){ delete_Icon " " uiTextCaution_ColorEscape
3502 " ${link.file.delete}", 3526 "${link.file.delete}",
3503 0, 3527 0,
3504 0, 3528 0,
3505 format_CStr("!file.delete confirm:1 path:%s", 3529 format_CStr("!file.delete confirm:1 path:%s",
@@ -4366,7 +4390,7 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
4366 iDrawBufs * dbuf = d->drawBufs; 4390 iDrawBufs * dbuf = d->drawBufs;
4367 iPaint p; 4391 iPaint p;
4368 init_Paint(&p); 4392 init_Paint(&p);
4369 setClip_Paint(&p, bounds); 4393 setClip_Paint(&p, boundsWithoutVisualOffset_Widget(w));
4370 /* Side icon and current heading. */ 4394 /* Side icon and current heading. */
4371 if (prefs_App()->sideIcon && opacity > 0 && dbuf->sideIconBuf) { 4395 if (prefs_App()->sideIcon && opacity > 0 && dbuf->sideIconBuf) {
4372 const iInt2 texSize = size_SDLTexture(dbuf->sideIconBuf); 4396 const iInt2 texSize = size_SDLTexture(dbuf->sideIconBuf);
@@ -4616,12 +4640,17 @@ static void prerender_DocumentWidget_(iAny *context) {
4616} 4640}
4617 4641
4618static void draw_DocumentWidget_(const iDocumentWidget *d) { 4642static void draw_DocumentWidget_(const iDocumentWidget *d) {
4619 const iWidget *w = constAs_Widget(d); 4643 const iWidget *w = constAs_Widget(d);
4620 const iRect bounds = bounds_Widget(w); 4644 const iRect bounds = bounds_Widget(w);
4645 const iRect boundsWithoutVisOff = boundsWithoutVisualOffset_Widget(w);
4646 const iRect clipBounds = intersect_Rect(bounds, boundsWithoutVisOff);
4621 if (width_Rect(bounds) <= 0) { 4647 if (width_Rect(bounds) <= 0) {
4622 return; 4648 return;
4623 } 4649 }
4624// draw_Widget(w); 4650 /* TODO: Come up with a better palette caching system.
4651 It should be able to recompute cached colors in `History` when the theme has changed.
4652 Cache the theme seed in `GmDocument`? */
4653// makePaletteGlobal_GmDocument(d->doc);
4625 if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) { 4654 if (d->drawBufs->flags & updateTimestampBuf_DrawBufsFlag) {
4626 updateTimestampBuf_DocumentWidget_(d); 4655 updateTimestampBuf_DocumentWidget_(d);
4627 } 4656 }
@@ -4638,7 +4667,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
4638 }; 4667 };
4639 init_Paint(&ctx.paint); 4668 init_Paint(&ctx.paint);
4640 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); 4669 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */);
4641 setClip_Paint(&ctx.paint, bounds); 4670 setClip_Paint(&ctx.paint, clipBounds);
4642 int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); 4671 int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY);
4643 draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); 4672 draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds));
4644 /* Text markers. */ 4673 /* Text markers. */
@@ -4673,7 +4702,6 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
4673 } 4702 }
4674 } 4703 }
4675 drawMedia_DocumentWidget_(d, &ctx.paint); 4704 drawMedia_DocumentWidget_(d, &ctx.paint);
4676 unsetClip_Paint(&ctx.paint);
4677 /* Fill the top and bottom, in case the document is short. */ 4705 /* Fill the top and bottom, in case the document is short. */
4678 if (yTop > top_Rect(bounds)) { 4706 if (yTop > top_Rect(bounds)) {
4679 fillRect_Paint(&ctx.paint, 4707 fillRect_Paint(&ctx.paint,
@@ -4687,6 +4715,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
4687 init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom), 4715 init_Rect(bounds.pos.x, yBottom, bounds.size.x, bottom_Rect(bounds) - yBottom),
4688 tmBackground_ColorId); 4716 tmBackground_ColorId);
4689 } 4717 }
4718 unsetClip_Paint(&ctx.paint);
4690 drawSideElements_DocumentWidget_(d); 4719 drawSideElements_DocumentWidget_(d);
4691 if (prefs_App()->hoverLink && d->hoverLink) { 4720 if (prefs_App()->hoverLink && d->hoverLink) {
4692 const int font = uiLabel_FontId; 4721 const int font = uiLabel_FontId;
@@ -4748,6 +4777,23 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
4748 drawCentered_Text(uiLabelBold_FontId, rect, iFalse, uiBackground_ColorId, "%zu bytes selected", 4777 drawCentered_Text(uiLabelBold_FontId, rect, iFalse, uiBackground_ColorId, "%zu bytes selected",
4749 size_Range(&mark)); 4778 size_Range(&mark));
4750 } 4779 }
4780 if (w->offsetRef) {
4781 const int offX = visualOffsetByReference_Widget(w);
4782 if (offX) {
4783 setClip_Paint(&ctx.paint, clipBounds);
4784 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
4785 ctx.paint.alpha = iAbs(offX) / (float) get_Window()->size.x * 300;
4786 fillRect_Paint(&ctx.paint, bounds, backgroundFadeColor_Widget());
4787 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
4788 unsetClip_Paint(&ctx.paint);
4789 }
4790 else {
4791 /* TODO: Should have a better place to do this; drawing is supposed to be immutable. */
4792 iWidget *mut = iConstCast(iWidget *, w);
4793 mut->offsetRef = NULL;
4794 mut->flags &= ~refChildrenOffset_WidgetFlag;
4795 }
4796 }
4751} 4797}
4752 4798
4753/*----------------------------------------------------------------------------------------------*/ 4799/*----------------------------------------------------------------------------------------------*/
@@ -4814,10 +4860,13 @@ static void setUrl_DocumentWidget_(iDocumentWidget *d, const iString *url) {
4814 if (!equal_String(d->mod.url, url)) { 4860 if (!equal_String(d->mod.url, url)) {
4815 d->flags |= urlChanged_DocumentWidgetFlag; 4861 d->flags |= urlChanged_DocumentWidgetFlag;
4816 set_String(d->mod.url, url); 4862 set_String(d->mod.url, url);
4817} 4863 }
4818} 4864}
4819 4865
4820void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBool isFromCache) { 4866void setUrlFlags_DocumentWidget(iDocumentWidget *d, const iString *url, int setUrlFlags) {
4867 iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag,
4868 (setUrlFlags & openedFromSidebar_DocumentWidgetSetUrlFlag) != 0);
4869 const iBool isFromCache = (setUrlFlags & useCachedContentIfAvailable_DocumentWidgetSetUrlFlag) != 0;
4821 setLinkNumberMode_DocumentWidget_(d, iFalse); 4870 setLinkNumberMode_DocumentWidget_(d, iFalse);
4822 setUrl_DocumentWidget_(d, urlFragmentStripped_String(url)); 4871 setUrl_DocumentWidget_(d, urlFragmentStripped_String(url));
4823 /* See if there a username in the URL. */ 4872 /* See if there a username in the URL. */
@@ -4829,6 +4878,7 @@ void setUrlFromCache_DocumentWidget(iDocumentWidget *d, const iString *url, iBoo
4829 4878
4830void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, const iString *mime, 4879void setUrlAndSource_DocumentWidget(iDocumentWidget *d, const iString *url, const iString *mime,
4831 const iBlock *source) { 4880 const iBlock *source) {
4881 d->flags &= ~openedFromSidebar_DocumentWidgetFlag;
4832 setLinkNumberMode_DocumentWidget_(d, iFalse); 4882 setLinkNumberMode_DocumentWidget_(d, iFalse);
4833 setUrl_DocumentWidget_(d, url); 4883 setUrl_DocumentWidget_(d, url);
4834 parseUser_DocumentWidget_(d); 4884 parseUser_DocumentWidget_(d);
@@ -4846,12 +4896,12 @@ iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) {
4846 delete_History(d->mod.history); 4896 delete_History(d->mod.history);
4847 d->initNormScrollY = normScrollPos_DocumentWidget_(d); 4897 d->initNormScrollY = normScrollPos_DocumentWidget_(d);
4848 d->mod.history = copy_History(orig->mod.history); 4898 d->mod.history = copy_History(orig->mod.history);
4849 setUrlFromCache_DocumentWidget(d, orig->mod.url, iTrue); 4899 setUrlFlags_DocumentWidget(d, orig->mod.url, useCachedContentIfAvailable_DocumentWidgetSetUrlFlag);
4850 return d; 4900 return d;
4851} 4901}
4852 4902
4853void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { 4903void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) {
4854 setUrlFromCache_DocumentWidget(d, url, iFalse); 4904 setUrlFlags_DocumentWidget(d, url, 0);
4855} 4905}
4856 4906
4857void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) { 4907void setInitialScroll_DocumentWidget(iDocumentWidget *d, float normScrollY) {
@@ -4862,6 +4912,11 @@ void setRedirectCount_DocumentWidget(iDocumentWidget *d, int count) {
4862 d->redirectCount = count; 4912 d->redirectCount = count;
4863} 4913}
4864 4914
4915void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *d, iBool fromSidebar) {
4916 iChangeFlags(d->flags, openedFromSidebar_DocumentWidgetFlag, fromSidebar);
4917 setCachedDocument_History(d->mod.history, d->doc, fromSidebar);
4918}
4919
4865iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) { 4920iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) {
4866 return d->request != NULL; 4921 return d->request != NULL;
4867} 4922}
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 *);
45const iString * feedTitle_DocumentWidget (const iDocumentWidget *); 45const iString * feedTitle_DocumentWidget (const iDocumentWidget *);
46int documentWidth_DocumentWidget (const iDocumentWidget *); 46int documentWidth_DocumentWidget (const iDocumentWidget *);
47 47
48enum iDocumentWidgetSetUrlFlags {
49 useCachedContentIfAvailable_DocumentWidgetSetUrlFlag = iBit(1),
50 openedFromSidebar_DocumentWidgetSetUrlFlag = iBit(2),
51};
52
48void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); 53void setUrl_DocumentWidget (iDocumentWidget *, const iString *url);
49void setUrlFromCache_DocumentWidget (iDocumentWidget *, const iString *url, iBool isFromCache); 54void setUrlFlags_DocumentWidget (iDocumentWidget *, const iString *url, int setUrlFlags);
50void setUrlAndSource_DocumentWidget (iDocumentWidget *, const iString *url, const iString *mime, const iBlock *source); 55void setUrlAndSource_DocumentWidget (iDocumentWidget *, const iString *url, const iString *mime, const iBlock *source);
51void setInitialScroll_DocumentWidget (iDocumentWidget *, float normScrollY); /* set after content received */ 56void setInitialScroll_DocumentWidget (iDocumentWidget *, float normScrollY); /* set after content received */
52void setRedirectCount_DocumentWidget (iDocumentWidget *, int count); 57void setRedirectCount_DocumentWidget (iDocumentWidget *, int count);
53void setSource_DocumentWidget (iDocumentWidget *, const iString *sourceText); 58void setSource_DocumentWidget (iDocumentWidget *, const iString *sourceText);
59void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *, iBool fromSidebar);
54 60
55void updateSize_DocumentWidget (iDocumentWidget *); 61void 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
773 const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->id); 773 const iGmHeading *head = constAt_Array(headings_GmDocument(doc), item->id);
774 postCommandf_App("document.goto loc:%p", head->text.start); 774 postCommandf_App("document.goto loc:%p", head->text.start);
775 dismissPortraitPhoneSidebars_Root(as_Widget(d)->root); 775 dismissPortraitPhoneSidebars_Root(as_Widget(d)->root);
776 setOpenedFromSidebar_DocumentWidget(document_App(), iTrue);
776 break; 777 break;
777 } 778 }
778 case feeds_SidebarMode: { 779 case feeds_SidebarMode: {
@@ -783,7 +784,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si
783 case bookmarks_SidebarMode: 784 case bookmarks_SidebarMode:
784 case history_SidebarMode: { 785 case history_SidebarMode: {
785 if (!isEmpty_String(&item->url)) { 786 if (!isEmpty_String(&item->url)) {
786 postCommandf_Root(get_Root(), "open newtab:%d url:%s", 787 postCommandf_Root(get_Root(), "open fromsidebar:1 newtab:%d url:%s",
787 openTabMode_Sym(modState_Keys()), 788 openTabMode_Sym(modState_Keys()),
788 cstr_String(&item->url)); 789 cstr_String(&item->url));
789 } 790 }
@@ -944,13 +945,16 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *
944 visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect); 945 visX = left_Rect(bounds_Widget(w)) - left_Rect(w->root->widget->rect);
945 } 946 }
946 setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w)); 947 setFlags_Widget(w, hidden_WidgetFlag, isVisible_Widget(w));
948 /* Safe area inset for mobile. */
949 const int safePad = (d->side == left_SidebarSide ? left_Rect(safeRect_Root(w->root)) : 0);
947 if (isVisible_Widget(w)) { 950 if (isVisible_Widget(w)) {
948 setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse); 951 setFlags_Widget(w, keepOnTop_WidgetFlag, iFalse);
949 w->rect.size.x = d->widthAsGaps * gap_UI; 952 w->rect.size.x = d->widthAsGaps * gap_UI;
950 invalidate_ListWidget(d->list); 953 invalidate_ListWidget(d->list);
951 if (isAnimated) { 954 if (isAnimated) {
952 setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); 955 setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue);
953 setVisualOffset_Widget(w, (d->side == left_SidebarSide ? -1 : 1) * w->rect.size.x, 0, 0); 956 setVisualOffset_Widget(
957 w, (d->side == left_SidebarSide ? -1 : 1) * (w->rect.size.x + safePad), 0, 0);
954 setVisualOffset_Widget(w, 0, 300, easeOut_AnimFlag | softer_AnimFlag); 958 setVisualOffset_Widget(w, 0, 300, easeOut_AnimFlag | softer_AnimFlag);
955 } 959 }
956 } 960 }
@@ -958,11 +962,13 @@ static iBool handleSidebarCommand_SidebarWidget_(iSidebarWidget *d, const char *
958 setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue); 962 setFlags_Widget(w, horizontalOffset_WidgetFlag, iTrue);
959 if (d->side == right_SidebarSide) { 963 if (d->side == right_SidebarSide) {
960 setVisualOffset_Widget(w, visX, 0, 0); 964 setVisualOffset_Widget(w, visX, 0, 0);
961 setVisualOffset_Widget(w, visX + w->rect.size.x, 300, easeOut_AnimFlag | softer_AnimFlag); 965 setVisualOffset_Widget(
966 w, visX + w->rect.size.x + safePad, 300, easeOut_AnimFlag | softer_AnimFlag);
962 } 967 }
963 else { 968 else {
964 setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue); 969 setFlags_Widget(w, keepOnTop_WidgetFlag, iTrue);
965 setVisualOffset_Widget(w, -w->rect.size.x, 300, easeOut_AnimFlag | softer_AnimFlag); 970 setVisualOffset_Widget(
971 w, -w->rect.size.x - safePad, 300, easeOut_AnimFlag | softer_AnimFlag);
966 } 972 }
967 } 973 }
968 arrange_Widget(w->parent); 974 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) {
389 d->cacheRowAllocStep = iMax(2, textSize / 6); 389 d->cacheRowAllocStep = iMax(2, textSize / 6);
390 /* Allocate initial (empty) rows. These will be assigned actual locations in the cache 390 /* Allocate initial (empty) rows. These will be assigned actual locations in the cache
391 once at least one glyph is stored. */ 391 once at least one glyph is stored. */
392 for (int h = d->cacheRowAllocStep; h <= 2 * textSize + d->cacheRowAllocStep; h += d->cacheRowAllocStep) { 392 for (int h = d->cacheRowAllocStep;
393 h <= 2.5 * textSize + d->cacheRowAllocStep;
394 h += d->cacheRowAllocStep) {
393 pushBack_Array(&d->cacheRows, &(iCacheRow){ .height = 0 }); 395 pushBack_Array(&d->cacheRows, &(iCacheRow){ .height = 0 });
394 } 396 }
395 d->cacheBottom = 0; 397 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 {
65 iBool isLeftDown; 65 iBool isLeftDown;
66 iBool isTouchDrag; 66 iBool isTouchDrag;
67 iBool isTapAndHold; 67 iBool isTapAndHold;
68 iBool didPostEdgeMove;
68 iBool didBeginOnTouchDrag; 69 iBool didBeginOnTouchDrag;
69 int pinchId; 70 int pinchId;
70 enum iTouchEdge edge; 71 enum iTouchEdge edge;
@@ -391,6 +392,12 @@ static void checkNewPinch_TouchState_(iTouchState *d, iTouch *newTouch) {
391 pinch.touchIds[1] = other->id; 392 pinch.touchIds[1] = other->id;
392 newTouch->pinchId = other->pinchId = pinch.id; 393 newTouch->pinchId = other->pinchId = pinch.id;
393 clearWidgetMomentum_TouchState_(d, affinity); 394 clearWidgetMomentum_TouchState_(d, affinity);
395 if (other->edge && other->didPostEdgeMove) {
396 postCommandf_App("edgeswipe.ended abort:1 side:%d id:%llu", other->edge, other->id);
397 other->didPostEdgeMove = iFalse;
398 }
399 other->edge = none_TouchEdge;
400 newTouch->edge = none_TouchEdge;
394 /* Remember current positions to determine pinch amount. */ 401 /* Remember current positions to determine pinch amount. */
395 newTouch->startPos = newTouch->pos[0]; 402 newTouch->startPos = newTouch->pos[0];
396 other->startPos = other->pos[0]; 403 other->startPos = other->pos[0];
@@ -476,7 +483,6 @@ iBool processEvent_Touch(const SDL_Event *ev) {
476 } 483 }
477 else if (x > rootSize.x - edgeWidth) { 484 else if (x > rootSize.x - edgeWidth) {
478 edge = right_TouchEdge; 485 edge = right_TouchEdge;
479// puts("DOWN on right edge");
480 } 486 }
481 iWidget *aff = hitChild_Window(window, init_I2(iRound(x), iRound(y_F3(pos)))); 487 iWidget *aff = hitChild_Window(window, init_I2(iRound(x), iRound(y_F3(pos))));
482#if 0 488#if 0
@@ -523,6 +529,7 @@ iBool processEvent_Touch(const SDL_Event *ev) {
523 (int) (x_F3(pos) - x_F3(touch->startPos)), 529 (int) (x_F3(pos) - x_F3(touch->startPos)),
524 touch->edge, 530 touch->edge,
525 touch->id); 531 touch->id);
532 touch->didPostEdgeMove = iTrue;
526 return iTrue; 533 return iTrue;
527 } 534 }
528 if (touch && touch->affinity) { 535 if (touch && touch->affinity) {
@@ -595,39 +602,6 @@ iBool processEvent_Touch(const SDL_Event *ev) {
595 } 602 }
596 } 603 }
597 iAssert(touch->edge == none_TouchEdge); 604 iAssert(touch->edge == none_TouchEdge);
598#if 0
599 /* Edge swipe aborted? */
600 if (touch->edge == left_TouchEdge) {
601 if (fing->dx < 0 && x_F3(touch->pos[0]) < tapRadiusPt_ * window->pixelRatio) {
602 touch->edge = none_TouchEdge;
603 if (touch->edgeDragging) {
604 setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse);
605 setVisualOffset_Widget(touch->edgeDragging, 0, 200, easeOut_AnimFlag);
606 touch->edgeDragging = NULL;
607 }
608 }
609 else if (touch->edgeDragging) {
610 setVisualOffset_Widget(touch->edgeDragging, x_F3(pos) - x_F3(touch->startPos), 10, 0);
611 }
612 }
613 if (touch->edge == right_TouchEdge) {
614 if (fing->dx > 0 && x_F3(touch->pos[0]) > window->size.x - tapRadiusPt_ * window->pixelRatio) {
615 puts("touch->edge==right returned to right edge, aborted");
616 touch->edge = none_TouchEdge;
617 if (touch->edgeDragging) {
618 setFlags_Widget(touch->edgeDragging, dragged_WidgetFlag, iFalse);
619 setVisualOffset_Widget(touch->edgeDragging, 0, 200, easeOut_AnimFlag);
620 touch->edgeDragging = NULL;
621 }
622 }
623 else if (touch->edgeDragging) {
624 setVisualOffset_Widget(touch->edgeDragging, x_F3(pos) - x_F3(touch->startPos), 10, 0);
625 }
626 }
627 if (touch->edge) {
628 pixels.y = 0;
629 }
630#endif
631 if (touch->axis == x_TouchAxis) { 605 if (touch->axis == x_TouchAxis) {
632 pixels.y = 0; 606 pixels.y = 0;
633 } 607 }
@@ -671,7 +645,12 @@ iBool processEvent_Touch(const SDL_Event *ev) {
671 } 645 }
672#endif 646#endif
673 if (touch->edge && !isStationary_Touch_(touch)) { 647 if (touch->edge && !isStationary_Touch_(touch)) {
674 postCommandf_App("edgeswipe.ended side:%d id:%llu", touch->edge, touch->id); 648 const iFloat3 gesture = gestureVector_Touch_(touch);
649 const float pixel = window->pixelRatio;
650 const int moveDir = x_F3(gesture) < -pixel ? -1 : x_F3(gesture) > pixel ? +1 : 0;
651 const int didAbort = (touch->edge == left_TouchEdge && moveDir < 0) ||
652 (touch->edge == right_TouchEdge && moveDir > 0);
653 postCommandf_App("edgeswipe.ended abort:%d side:%d id:%llu", didAbort, touch->edge, touch->id);
675 remove_ArrayIterator(&i); 654 remove_ArrayIterator(&i);
676 continue; 655 continue;
677 } 656 }
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) {
760} 760}
761 761
762iBool isBeingVisuallyOffsetByReference_Widget(const iWidget *d) { 762iBool isBeingVisuallyOffsetByReference_Widget(const iWidget *d) {
763 if (d->flags & refChildrenOffset_WidgetFlag) { 763 return visualOffsetByReference_Widget(d) != 0;
764}
765
766int visualOffsetByReference_Widget(const iWidget *d) {
767 if (d->offsetRef && d->flags & refChildrenOffset_WidgetFlag) {
768 int offX = 0;
764 iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) { 769 iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) {
765 const iWidget *child = i.object; 770 const iWidget *child = i.object;
766 if (child == d) continue; 771 if (child == d) continue;
767 if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { 772 if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) {
768 return iTrue; 773// const float factor = width_Widget(d) / (float) size_Root(d->root).x;
774 const int invOff = width_Widget(d) - iRound(value_Anim(&child->visualOffset));
775 offX -= invOff / 4;
769 } 776 }
770 } 777 }
778 return offX;
771 } 779 }
772 return iFalse; 780 return 0;
773} 781}
774 782
775static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { 783static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) {
@@ -786,14 +794,7 @@ static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) {
786 pos->y -= value_Anim(d->animOffsetRef); 794 pos->y -= value_Anim(d->animOffsetRef);
787 } 795 }
788 if (d->flags & refChildrenOffset_WidgetFlag) { 796 if (d->flags & refChildrenOffset_WidgetFlag) {
789 iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) { 797 pos->x += visualOffsetByReference_Widget(d);
790 const iWidget *child = i.object;
791 if (child == d) continue;
792 if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) {
793 const int invOff = size_Root(d->root).x - iRound(value_Anim(&child->visualOffset));
794 pos->x -= invOff / 4;
795 }
796 }
797 } 798 }
798} 799}
799 800
@@ -1081,7 +1082,9 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
1081 if (~d->flags & dragged_WidgetFlag) { 1082 if (~d->flags & dragged_WidgetFlag) {
1082 setFlags_Widget(d, dragged_WidgetFlag, iTrue); 1083 setFlags_Widget(d, dragged_WidgetFlag, iTrue);
1083 } 1084 }
1084 setVisualOffset_Widget(d, arg_Command(command_UserEvent(ev)), 10, 0); 1085 setVisualOffset_Widget(d, arg_Command(command_UserEvent(ev)) *
1086 width_Widget(d) / size_Root(d->root).x,
1087 10, 0);
1085 return iTrue; 1088 return iTrue;
1086 } 1089 }
1087 } 1090 }
@@ -1129,6 +1132,17 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
1129 return iFalse; 1132 return iFalse;
1130} 1133}
1131 1134
1135int backgroundFadeColor_Widget(void) {
1136 switch (colorTheme_App()) {
1137 case light_ColorTheme:
1138 return gray25_ColorId;
1139 case pureWhite_ColorTheme:
1140 return gray50_ColorId;
1141 default:
1142 return black_ColorId;
1143 }
1144}
1145
1132void drawBackground_Widget(const iWidget *d) { 1146void drawBackground_Widget(const iWidget *d) {
1133 if (d->flags & noBackground_WidgetFlag) { 1147 if (d->flags & noBackground_WidgetFlag) {
1134 return; 1148 return;
@@ -1151,8 +1165,7 @@ void drawBackground_Widget(const iWidget *d) {
1151 drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30); 1165 drawSoftShadow_Paint(&p, bounds_Widget(d), 12 * gap_UI, black_ColorId, 30);
1152 } 1166 }
1153 const iBool isFaded = fadeBackground && 1167 const iBool isFaded = fadeBackground &&
1154 ~d->flags & noFadeBackground_WidgetFlag;/* && 1168 ~d->flags & noFadeBackground_WidgetFlag;
1155 ~d->flags & destroyPending_WidgetFlag;*/
1156 if (isFaded) { 1169 if (isFaded) {
1157 iPaint p; 1170 iPaint p;
1158 init_Paint(&p); 1171 init_Paint(&p);
@@ -1163,19 +1176,7 @@ void drawBackground_Widget(const iWidget *d) {
1163 p.alpha *= (area > 0 ? visibleArea / area : 0.0f); 1176 p.alpha *= (area > 0 ? visibleArea / area : 0.0f);
1164 } 1177 }
1165 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); 1178 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND);
1166 int fadeColor; 1179 fillRect_Paint(&p, rect_Root(d->root), backgroundFadeColor_Widget());
1167 switch (colorTheme_App()) {
1168 default:
1169 fadeColor = black_ColorId;
1170 break;
1171 case light_ColorTheme:
1172 fadeColor = gray25_ColorId;
1173 break;
1174 case pureWhite_ColorTheme:
1175 fadeColor = gray50_ColorId;
1176 break;
1177 }
1178 fillRect_Paint(&p, rect_Root(d->root), fadeColor);
1179 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); 1180 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
1180 } 1181 }
1181 if (d->bgColor >= 0 || d->frameColor >= 0) { 1182 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 *);
242iBool isUnderKeyRoot_Widget (const iAnyObject *); 242iBool isUnderKeyRoot_Widget (const iAnyObject *);
243iBool isCommand_Widget (const iWidget *d, const SDL_Event *ev, const char *cmd); 243iBool isCommand_Widget (const iWidget *d, const SDL_Event *ev, const char *cmd);
244iBool hasParent_Widget (const iWidget *d, const iWidget *someParent); 244iBool hasParent_Widget (const iWidget *d, const iWidget *someParent);
245iBool isAffectedByVisualOffset_Widget 245iBool isAffectedByVisualOffset_Widget (const iWidget *);
246 (const iWidget *); 246iBool isBeingVisuallyOffsetByReference_Widget (const iWidget *);
247iBool isBeingVisuallyOffsetByReference_Widget 247int visualOffsetByReference_Widget (const iWidget *);
248 (const iWidget *);
249void setId_Widget (iWidget *, const char *id); 248void setId_Widget (iWidget *, const char *id);
250void setFlags_Widget (iWidget *, int64_t flags, iBool set); 249void setFlags_Widget (iWidget *, int64_t flags, iBool set);
251void setPos_Widget (iWidget *, iInt2 pos); 250void setPos_Widget (iWidget *, iInt2 pos);
@@ -278,6 +277,8 @@ void refresh_Widget (const iAnyObject *);
278 277
279iBool equalWidget_Command (const char *cmd, const iWidget *widget, const char *checkCommand); 278iBool equalWidget_Command (const char *cmd, const iWidget *widget, const char *checkCommand);
280 279
280int backgroundFadeColor_Widget (void);
281
281void setFocus_Widget (iWidget *); 282void setFocus_Widget (iWidget *);
282iWidget * focus_Widget (void); 283iWidget * focus_Widget (void);
283void setHover_Widget (iWidget *); 284void setHover_Widget (iWidget *);