summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-06-13 15:28:58 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-06-13 15:28:58 +0300
commiteb04ac7a1921b7c7f534a7a63cffc4c7a35c0900 (patch)
treea84fd4b4630f7469caa6e6a5df50fb13e12f8437 /src
parent4a29ccb82910503fe1c44fbf493caf0945714705 (diff)
Mobile: Working on swipe navigation
Swipes back and forward are now working, although there are some glitches remaining. Most notably, when swiping back the previous document does not appear until the finger is released.
Diffstat (limited to 'src')
-rw-r--r--src/history.c30
-rw-r--r--src/history.h2
-rw-r--r--src/ui/documentwidget.c144
-rw-r--r--src/ui/mobile.c2
-rw-r--r--src/ui/root.c13
-rw-r--r--src/ui/touch.c6
-rw-r--r--src/ui/widget.c15
-rw-r--r--src/ui/widget.h2
8 files changed, 202 insertions, 12 deletions
diff --git a/src/history.c b/src/history.c
index 740830e9..f6ad4771 100644
--- a/src/history.c
+++ b/src/history.c
@@ -284,6 +284,36 @@ void add_History(iHistory *d, const iString *url ){
284 unlock_Mutex(d->mtx); 284 unlock_Mutex(d->mtx);
285} 285}
286 286
287iBool preceding_History(iHistory *d, iRecentUrl *recent_out) {
288 iBool ok = iFalse;
289 lock_Mutex(d->mtx);
290 if (!isEmpty_Array(&d->recent) && d->recentPos < size_Array(&d->recent) - 1) {
291 const iRecentUrl *recent = constAt_Array(&d->recent, d->recentPos + 1);
292 set_String(&recent_out->url, &recent->url);
293 recent_out->normScrollY = recent->normScrollY;
294 recent_out->cachedDoc = ref_Object(recent->cachedDoc);
295 /* Cached response is not returned, would involve a deep copy. */
296 ok = iTrue;
297 }
298 unlock_Mutex(d->mtx);
299 return ok;
300}
301
302iBool following_History(iHistory *d, iRecentUrl *recent_out) {
303 iBool ok = iFalse;
304 lock_Mutex(d->mtx);
305 if (!isEmpty_Array(&d->recent) && d->recentPos > 0) {
306 const iRecentUrl *recent = constAt_Array(&d->recent, d->recentPos - 1);
307 set_String(&recent_out->url, &recent->url);
308 recent_out->normScrollY = recent->normScrollY;
309 recent_out->cachedDoc = ref_Object(recent->cachedDoc);
310 /* Cached response is not returned, would involve a deep copy. */
311 ok = iTrue;
312 }
313 unlock_Mutex(d->mtx);
314 return ok;
315}
316
287iBool goBack_History(iHistory *d) { 317iBool goBack_History(iHistory *d) {
288 lock_Mutex(d->mtx); 318 lock_Mutex(d->mtx);
289 if (!isEmpty_Array(&d->recent) && d->recentPos < size_Array(&d->recent) - 1) { 319 if (!isEmpty_Array(&d->recent) && d->recentPos < size_Array(&d->recent) - 1) {
diff --git a/src/history.h b/src/history.h
index eb35b1df..9c900b82 100644
--- a/src/history.h
+++ b/src/history.h
@@ -63,6 +63,8 @@ void setCachedResponse_History (iHistory *, const iGmResponse *response
63void setCachedDocument_History (iHistory *, iGmDocument *doc); 63void setCachedDocument_History (iHistory *, iGmDocument *doc);
64iBool goBack_History (iHistory *); 64iBool goBack_History (iHistory *);
65iBool goForward_History (iHistory *); 65iBool goForward_History (iHistory *);
66iBool preceding_History (iHistory *d, iRecentUrl *recent_out);
67iBool following_History (iHistory *d, iRecentUrl *recent_out);
66iRecentUrl *recentUrl_History (iHistory *, size_t pos); 68iRecentUrl *recentUrl_History (iHistory *, size_t pos);
67iRecentUrl *mostRecentUrl_History (iHistory *); 69iRecentUrl *mostRecentUrl_History (iHistory *);
68iRecentUrl *findUrl_History (iHistory *, const iString *url); 70iRecentUrl *findUrl_History (iHistory *, const iString *url);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 936c3cb7..70201d7b 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -315,6 +315,10 @@ void init_DocumentWidget(iDocumentWidget *d) {
315 init_Widget(w); 315 init_Widget(w);
316 setId_Widget(w, format_CStr("document%03d", ++docEnum_)); 316 setId_Widget(w, format_CStr("document%03d", ++docEnum_));
317 setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue); 317 setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue);
318 if (deviceType_App() != desktop_AppDeviceType) {
319 setFlags_Widget(w, leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag |
320 horizontalOffset_WidgetFlag, iTrue);
321 }
318 init_PersistentDocumentState(&d->mod); 322 init_PersistentDocumentState(&d->mod);
319 d->flags = 0; 323 d->flags = 0;
320 d->phoneToolbar = NULL; 324 d->phoneToolbar = NULL;
@@ -977,6 +981,9 @@ static void updateTimestampBuf_DocumentWidget_(const iDocumentWidget *d) {
977} 981}
978 982
979static void invalidate_DocumentWidget_(iDocumentWidget *d) { 983static void invalidate_DocumentWidget_(iDocumentWidget *d) {
984 if (flags_Widget(as_Widget(d)) & destroyPending_WidgetFlag) {
985 return;
986 }
980 invalidate_VisBuf(d->visBuf); 987 invalidate_VisBuf(d->visBuf);
981 clear_PtrSet(d->invalidRuns); 988 clear_PtrSet(d->invalidRuns);
982} 989}
@@ -2237,6 +2244,133 @@ static iBool handlePinch_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2237 return iTrue; 2244 return iTrue;
2238} 2245}
2239 2246
2247static void swap_DocumentWidget_(iDocumentWidget *d, iGmDocument *doc,
2248 iDocumentWidget *swapBuffersWith) {
2249 if (doc) {
2250 iAssert(isInstance_Object(doc, &Class_GmDocument));
2251 iGmDocument *copy = ref_Object(doc);
2252 iRelease(d->doc);
2253 d->doc = copy;
2254 d->scrollY = swapBuffersWith->scrollY;
2255 updateVisible_DocumentWidget_(d);
2256 iSwap(iVisBuf *, d->visBuf, swapBuffersWith->visBuf);
2257 iSwap(iVisBufMeta *, d->visBufMeta, swapBuffersWith->visBufMeta);
2258 iSwap(iDrawBufs *, d->drawBufs, swapBuffersWith->drawBufs);
2259 invalidate_DocumentWidget_(swapBuffersWith);
2260 }
2261}
2262
2263static iWidget *swipeParent_DocumentWidget_(iDocumentWidget *d) {
2264 return findChild_Widget(as_Widget(d)->root->widget, "doctabs");
2265}
2266
2267static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2268 iWidget *w = as_Widget(d);
2269 if (equal_Command(cmd, "edgeswipe.moved")) {
2270 as_Widget(d)->offsetRef = NULL;
2271 const int side = argLabel_Command(cmd, "side");
2272 const int offset = arg_Command(cmd);
2273 if (side == 1) {
2274 if (atOldest_History(d->mod.history)) {
2275 return iTrue;
2276 }
2277 iWidget *swipeParent = swipeParent_DocumentWidget_(d);
2278 /* The temporary "swipeIn" will display the previous page until the finger is lifted. */
2279 iDocumentWidget *swipeIn = findChild_Widget(swipeParent, "swipein");
2280 if (!swipeIn) {
2281 swipeIn = new_DocumentWidget();
2282 setId_Widget(as_Widget(swipeIn), "swipein");
2283 setFlags_Widget(as_Widget(swipeIn),
2284 disabled_WidgetFlag | refChildrenOffset_WidgetFlag |
2285 fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue);
2286 swipeIn->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos));
2287 swipeIn->widget.rect.size = d->widget.rect.size;
2288 swipeIn->widget.offsetRef = w;
2289 iRecentUrl *recent = new_RecentUrl();
2290 preceding_History(d->mod.history, recent);
2291 if (recent->cachedDoc) {
2292 iChangeRef(swipeIn->doc, recent->cachedDoc);
2293 updateScrollMax_DocumentWidget_(d);
2294 setValue_Anim(&swipeIn->scrollY.pos, size_GmDocument(swipeIn->doc).y * recent->normScrollY, 0);
2295 updateVisible_DocumentWidget_(swipeIn);
2296 }
2297 delete_RecentUrl(recent);
2298 addChildPos_Widget(swipeParent, iClob(swipeIn), front_WidgetAddPos);
2299 }
2300 }
2301 if (side == 2) {
2302 if (offset < -get_Window()->pixelRatio * 10) {
2303 if (!atLatest_History(d->mod.history) &&
2304 ~flags_Widget(w) & dragged_WidgetFlag) {
2305 postCommand_Widget(d, "navigate.forward");
2306 setFlags_Widget(w, dragged_WidgetFlag, iTrue);
2307 /* Set up the swipe dummy. */
2308 iWidget *swipeParent = swipeParent_DocumentWidget_(d);
2309 iDocumentWidget *target = new_DocumentWidget();
2310 setId_Widget(as_Widget(target), "swipeout");
2311 /* The target takes the old document and jumps on top. */
2312 target->widget.rect.pos = windowToInner_Widget(swipeParent, localToWindow_Widget(w, w->rect.pos));
2313 target->widget.rect.size = d->widget.rect.size;
2314 setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue);
2315 swap_DocumentWidget_(target, d->doc, d);
2316 addChildPos_Widget(swipeParent, iClob(target), front_WidgetAddPos);
2317 setFlags_Widget(as_Widget(target), refChildrenOffset_WidgetFlag, iTrue);
2318 as_Widget(target)->offsetRef = parent_Widget(w);
2319 destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */
2320 }
2321 if (flags_Widget(w) & dragged_WidgetFlag) {
2322 setVisualOffset_Widget(w, width_Widget(w) + offset, 10, 0);
2323 }
2324 else {
2325 setVisualOffset_Widget(w, offset / 4, 10, 0);
2326 }
2327 }
2328 return iTrue;
2329 }
2330 }
2331 if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 2) {
2332 if (argLabel_Command(cmd, "abort") && flags_Widget(w) & dragged_WidgetFlag) {
2333 postCommand_Widget(d, "navigate.back");
2334 }
2335 setFlags_Widget(w, dragged_WidgetFlag, iFalse);
2336 setVisualOffset_Widget(w, 0, 100, 0);
2337 return iTrue;
2338 }
2339 if (equal_Command(cmd, "edgeswipe.ended") && argLabel_Command(cmd, "side") == 1) {
2340 iWidget *swipeParent = swipeParent_DocumentWidget_(d);
2341 iWidget *swipeIn = findChild_Widget(swipeParent, "swipein");
2342 if (swipeIn) {
2343 swipeIn->offsetRef = NULL;
2344 destroy_Widget(swipeIn);
2345 }
2346 }
2347 if (equal_Command(cmd, "swipe.back")) {
2348 if (atOldest_History(d->mod.history)) {
2349 setVisualOffset_Widget(w, 0, 100, 0);
2350 return iTrue;
2351 }
2352 iWidget *swipeParent = swipeParent_DocumentWidget_(d);
2353 iDocumentWidget *target = new_DocumentWidget();
2354 setId_Widget(as_Widget(target), "swipeout");
2355 /* The target takes the old document and jumps on top. */
2356 target->widget.rect.pos = windowToInner_Widget(swipeParent, innerToWindow_Widget(w, zero_I2()));
2357 /* Note: `innerToWindow_Widget` does not apply visual offset. */
2358 target->widget.rect.size = w->rect.size;
2359 setFlags_Widget(as_Widget(target), fixedPosition_WidgetFlag | fixedSize_WidgetFlag, iTrue);
2360 swap_DocumentWidget_(target, d->doc, d);
2361 addChildPos_Widget(swipeParent, iClob(target), back_WidgetAddPos);
2362 setFlags_Widget(as_Widget(d), refChildrenOffset_WidgetFlag, iTrue);
2363 as_Widget(d)->offsetRef = swipeParent;
2364 setVisualOffset_Widget(as_Widget(target), value_Anim(&w->visualOffset), 0, 0);
2365 setVisualOffset_Widget(as_Widget(target), width_Widget(target), 150, 0);
2366 destroy_Widget(as_Widget(target)); /* will be actually deleted after animation finishes */
2367 setVisualOffset_Widget(w, 0, 0, 0);
2368 postCommand_Widget(d, "navigate.back");
2369 return iTrue;
2370 }
2371 return iFalse;
2372}
2373
2240static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { 2374static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2241 iWidget *w = as_Widget(d); 2375 iWidget *w = as_Widget(d);
2242 if (equal_Command(cmd, "document.openurls.changed")) { 2376 if (equal_Command(cmd, "document.openurls.changed")) {
@@ -2842,7 +2976,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2842 uiHeading_ColorEscape "${heading.import.bookmarks}", 2976 uiHeading_ColorEscape "${heading.import.bookmarks}",
2843 formatCStrs_Lang("dlg.import.found.n", count), 2977 formatCStrs_Lang("dlg.import.found.n", count),
2844 (iMenuItem[]){ { "${cancel}", 0, 0, NULL }, 2978 (iMenuItem[]){ { "${cancel}", 0, 0, NULL },
2845 { format_CStr(cstrCount_Lang("dlg.import.add.n", count), 2979 { format_CStr(cstrCount_Lang("dlg.import.add.n", (int) count),
2846 uiTextAction_ColorEscape, 2980 uiTextAction_ColorEscape,
2847 count), 2981 count),
2848 0, 2982 0,
@@ -2901,6 +3035,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2901 else if (startsWith_CStr(cmd, "pinch.") && document_Command(cmd) == d) { 3035 else if (startsWith_CStr(cmd, "pinch.") && document_Command(cmd) == d) {
2902 return handlePinch_DocumentWidget_(d, cmd); 3036 return handlePinch_DocumentWidget_(d, cmd);
2903 } 3037 }
3038 else if ((startsWith_CStr(cmd, "edgeswipe.") || startsWith_CStr(cmd, "swipe.")) &&
3039 document_App() == d) {
3040 return handleSwipe_DocumentWidget_(d, cmd);
3041 }
2904 return iFalse; 3042 return iFalse;
2905} 3043}
2906 3044
@@ -4306,7 +4444,8 @@ static iBool render_DocumentWidget_(const iDocumentWidget *d, iDrawContext *ctx,
4306 /* Swap buffers around to have room available both before and after the visible region. */ 4444 /* Swap buffers around to have room available both before and after the visible region. */
4307 allocVisBuffer_DocumentWidget_(d); 4445 allocVisBuffer_DocumentWidget_(d);
4308 reposition_VisBuf(visBuf, vis); 4446 reposition_VisBuf(visBuf, vis);
4309 /* Redraw the invalid ranges. */ { 4447 /* Redraw the invalid ranges. */
4448 if (~flags_Widget(constAs_Widget(d)) & destroyPending_WidgetFlag) {
4310 iPaint *p = &ctx->paint; 4449 iPaint *p = &ctx->paint;
4311 init_Paint(p); 4450 init_Paint(p);
4312 iForIndices(i, visBuf->buffers) { 4451 iForIndices(i, visBuf->buffers) {
@@ -4490,6 +4629,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
4490 .vis = vis, 4629 .vis = vis,
4491 .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0, 4630 .showLinkNumbers = (d->flags & showLinkNumbers_DocumentWidgetFlag) != 0,
4492 }; 4631 };
4632 init_Paint(&ctx.paint);
4493 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); 4633 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */);
4494 setClip_Paint(&ctx.paint, bounds); 4634 setClip_Paint(&ctx.paint, bounds);
4495 int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY); 4635 int yTop = docBounds.pos.y - pos_SmoothScroll(&d->scrollY);
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index 1bf289a7..7c0c8b60 100644
--- a/src/ui/mobile.c
+++ b/src/ui/mobile.c
@@ -447,7 +447,7 @@ void finalizeSheet_Mobile(iWidget *sheet) {
447 } 447 }
448 addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_())))); 448 addChild_Widget(topPanel, iClob(makePadding_Widget(lineHeight_Text(labelFont_()))));
449 /* Slide top panel with detail panels. */ { 449 /* Slide top panel with detail panels. */ {
450 setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue); 450 setFlags_Widget(topPanel, refChildrenOffset_WidgetFlag, iTrue);
451 topPanel->offsetRef = detailStack; 451 topPanel->offsetRef = detailStack;
452 } 452 }
453 if (prefsTabs) { 453 if (prefsTabs) {
diff --git a/src/ui/root.c b/src/ui/root.c
index 2d69970d..e279a048 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -271,7 +271,8 @@ void destroyPending_Root(iRoot *d) {
271 setCurrent_Root(d); 271 setCurrent_Root(d);
272 iForEach(PtrSet, i, d->pendingDestruction) { 272 iForEach(PtrSet, i, d->pendingDestruction) {
273 iWidget *widget = *i.value; 273 iWidget *widget = *i.value;
274 if (!isFinished_Anim(&widget->visualOffset)) { 274 if (!isFinished_Anim(&widget->visualOffset) ||
275 isBeingVisuallyOffsetByReference_Widget(widget)) {
275 continue; 276 continue;
276 } 277 }
277 if (widget->flags & keepOnTop_WidgetFlag) { 278 if (widget->flags & keepOnTop_WidgetFlag) {
@@ -1169,11 +1170,11 @@ void createUserInterface_Root(iRoot *d) {
1169 setId_Widget(mainStack, "stack"); 1170 setId_Widget(mainStack, "stack");
1170 addChildFlags_Widget(div, iClob(mainStack), resizeChildren_WidgetFlag | expand_WidgetFlag | 1171 addChildFlags_Widget(div, iClob(mainStack), resizeChildren_WidgetFlag | expand_WidgetFlag |
1171 unhittable_WidgetFlag); 1172 unhittable_WidgetFlag);
1172 iWidget *tabBar = makeTabs_Widget(mainStack); 1173 iWidget *docTabs = makeTabs_Widget(mainStack);
1173 setId_Widget(tabBar, "doctabs"); 1174 setId_Widget(docTabs, "doctabs");
1174 setBackgroundColor_Widget(tabBar, uiBackground_ColorId); 1175 setBackgroundColor_Widget(docTabs, uiBackground_ColorId);
1175 appendTabPage_Widget(tabBar, iClob(new_DocumentWidget()), "Document", 0, 0); 1176 appendTabPage_Widget(docTabs, iClob(new_DocumentWidget()), "Document", 0, 0);
1176 iWidget *buttons = findChild_Widget(tabBar, "tabs.buttons"); 1177 iWidget *buttons = findChild_Widget(docTabs, "tabs.buttons");
1177 setFlags_Widget(buttons, collapse_WidgetFlag | hidden_WidgetFlag | 1178 setFlags_Widget(buttons, collapse_WidgetFlag | hidden_WidgetFlag |
1178 drawBackgroundToHorizontalSafeArea_WidgetFlag, iTrue); 1179 drawBackgroundToHorizontalSafeArea_WidgetFlag, iTrue);
1179 if (deviceType_App() == phone_AppDeviceType) { 1180 if (deviceType_App() == phone_AppDeviceType) {
diff --git a/src/ui/touch.c b/src/ui/touch.c
index 236601e8..36bac267 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -273,8 +273,9 @@ static void update_TouchState_(void *ptr) {
273// touch->edge == right_TouchEdge && swipeDir < 0 ? SDL_BUTTON_X2 : 0); 273// touch->edge == right_TouchEdge && swipeDir < 0 ? SDL_BUTTON_X2 : 0);
274// setHover_Widget(NULL); 274// setHover_Widget(NULL);
275 postCommandf_App("edgeswipe.ended abort:1 side:%d id:%llu", touch->edge, touch->id); 275 postCommandf_App("edgeswipe.ended abort:1 side:%d id:%llu", touch->edge, touch->id);
276 touch->edge = none_TouchEdge; 276 touch->edge = none_TouchEdge;
277 remove_ArrayIterator(&i); 277 /* May be a regular drag along the edge so don't remove. */
278 //remove_ArrayIterator(&i);
278 } 279 }
279 continue; 280 continue;
280 } 281 }
@@ -516,6 +517,7 @@ iBool processEvent_Touch(const SDL_Event *ev) {
516 else if (ev->type == SDL_FINGERMOTION) { 517 else if (ev->type == SDL_FINGERMOTION) {
517 iTouch *touch = find_TouchState_(d, fing->fingerId); 518 iTouch *touch = find_TouchState_(d, fing->fingerId);
518 if (touch && touch->edge) { 519 if (touch && touch->edge) {
520 clear_Array(d->moms);
519 pushPos_Touch_(touch, pos, nowTime); 521 pushPos_Touch_(touch, pos, nowTime);
520 postCommandf_App("edgeswipe.moved arg:%d side:%d id:%llu", 522 postCommandf_App("edgeswipe.moved arg:%d side:%d id:%llu",
521 (int) (x_F3(pos) - x_F3(touch->startPos)), 523 (int) (x_F3(pos) - x_F3(touch->startPos)),
diff --git a/src/ui/widget.c b/src/ui/widget.c
index f1d1dae4..3b67684b 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -759,6 +759,19 @@ void arrange_Widget(iWidget *d) {
759 } 759 }
760} 760}
761 761
762iBool isBeingVisuallyOffsetByReference_Widget(const iWidget *d) {
763 if (d->flags & refChildrenOffset_WidgetFlag) {
764 iConstForEach(ObjectList, i, children_Widget(d->offsetRef)) {
765 const iWidget *child = i.object;
766 if (child == d) continue;
767 if (child->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) {
768 return iTrue;
769 }
770 }
771 }
772 return iFalse;
773}
774
762static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { 775static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) {
763 if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { 776 if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) {
764 const int off = iRound(value_Anim(&d->visualOffset)); 777 const int off = iRound(value_Anim(&d->visualOffset));
@@ -1075,12 +1088,12 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
1075 if (d->flags & dragged_WidgetFlag && equal_Command(cmd, "edgeswipe.ended")) { 1088 if (d->flags & dragged_WidgetFlag && equal_Command(cmd, "edgeswipe.ended")) {
1076 if (argLabel_Command(cmd, "abort")) { 1089 if (argLabel_Command(cmd, "abort")) {
1077 setVisualOffset_Widget(d, 0, 200, easeOut_AnimFlag); 1090 setVisualOffset_Widget(d, 0, 200, easeOut_AnimFlag);
1078 setFlags_Widget(d, dragged_WidgetFlag, iFalse);
1079 } 1091 }
1080 else { 1092 else {
1081 postCommand_Widget( 1093 postCommand_Widget(
1082 d, argLabel_Command(cmd, "side") == 1 ? "swipe.back" : "swipe.forward"); 1094 d, argLabel_Command(cmd, "side") == 1 ? "swipe.back" : "swipe.forward");
1083 } 1095 }
1096 setFlags_Widget(d, dragged_WidgetFlag, iFalse);
1084 } 1097 }
1085 if (d->commandHandler && d->commandHandler(d, ev->user.data1)) { 1098 if (d->commandHandler && d->commandHandler(d, ev->user.data1)) {
1086 iAssert(get_Root() == d->root); 1099 iAssert(get_Root() == d->root);
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 7255b975..b2310f21 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -244,6 +244,8 @@ iBool isCommand_Widget (const iWidget *d, const SDL_Event *ev, cons
244iBool hasParent_Widget (const iWidget *d, const iWidget *someParent); 244iBool hasParent_Widget (const iWidget *d, const iWidget *someParent);
245iBool isAffectedByVisualOffset_Widget 245iBool isAffectedByVisualOffset_Widget
246 (const iWidget *); 246 (const iWidget *);
247iBool isBeingVisuallyOffsetByReference_Widget
248 (const iWidget *);
247void setId_Widget (iWidget *, const char *id); 249void setId_Widget (iWidget *, const char *id);
248void setFlags_Widget (iWidget *, int64_t flags, iBool set); 250void setFlags_Widget (iWidget *, int64_t flags, iBool set);
249void setPos_Widget (iWidget *, iInt2 pos); 251void setPos_Widget (iWidget *, iInt2 pos);