summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 *);