summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-21 19:51:22 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-21 19:51:22 +0300
commit2acc486899b7253dbbc1656ea444c596d8af16ad (patch)
tree67a4ed03353e710c8a1164c1173f025f8f915e5c
parent2e7b41f2d20cee278514b84ccf131062a62b3fee (diff)
Mobile: Working on scroll bounce behavior
-rw-r--r--src/app.h1
-rw-r--r--src/ui/documentwidget.c54
-rw-r--r--src/ui/touch.c25
-rw-r--r--src/ui/touch.h12
-rw-r--r--src/ui/util.c23
-rw-r--r--src/ui/util.h4
6 files changed, 101 insertions, 18 deletions
diff --git a/src/app.h b/src/app.h
index 4eae7afd..9f3754cc 100644
--- a/src/app.h
+++ b/src/app.h
@@ -61,6 +61,7 @@ enum iUserEventCode {
61 sending SDL_MOUSEBUTTONDOWN would be premature: we don't know how long the tap will 61 sending SDL_MOUSEBUTTONDOWN would be premature: we don't know how long the tap will
62 take, it could turn into a tap-and-hold for example. */ 62 take, it could turn into a tap-and-hold for example. */
63 widgetTapBegins_UserEventCode = 4, 63 widgetTapBegins_UserEventCode = 4,
64 widgetTouchEnds_UserEventCode = 5, /* finger lifted, but momentum may continue */
64}; 65};
65 66
66const iString *execPath_App (void); 67const iString *execPath_App (void);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index c3728b75..87c724de 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -287,6 +287,7 @@ struct Impl_DocumentWidget {
287 iDrawBufs * drawBufs; /* dynamic state for drawing */ 287 iDrawBufs * drawBufs; /* dynamic state for drawing */
288 iTranslation * translation; 288 iTranslation * translation;
289 iWidget * phoneToolbar; 289 iWidget * phoneToolbar;
290 int overscroll;
290 int pinchZoomInitial; 291 int pinchZoomInitial;
291 int pinchZoomPosted; 292 int pinchZoomPosted;
292}; 293};
@@ -299,8 +300,9 @@ void init_DocumentWidget(iDocumentWidget *d) {
299 setId_Widget(w, "document000"); 300 setId_Widget(w, "document000");
300 setFlags_Widget(w, hover_WidgetFlag, iTrue); 301 setFlags_Widget(w, hover_WidgetFlag, iTrue);
301 init_PersistentDocumentState(&d->mod); 302 init_PersistentDocumentState(&d->mod);
302 d->flags = 0; 303 d->flags = 0;
303 d->phoneToolbar = NULL; 304 d->phoneToolbar = NULL;
305 d->overscroll = deviceType_App() != desktop_AppDeviceType ? 50 * gap_UI : 0;
304 iZap(d->certExpiry); 306 iZap(d->certExpiry);
305 d->certFingerprint = new_Block(0); 307 d->certFingerprint = new_Block(0);
306 d->certFlags = 0; 308 d->certFlags = 0;
@@ -1255,6 +1257,20 @@ static void refreshWhileScrolling_DocumentWidget_(iAny *ptr) {
1255 } 1257 }
1256} 1258}
1257 1259
1260static int overscroll_DocumentWidget_(const iDocumentWidget *d) {
1261 if (d->overscroll) {
1262 const int y = value_Anim(&d->scrollY);
1263 if (y <= 0) {
1264 return y;
1265 }
1266 const int scrollMax = scrollMax_DocumentWidget_(d);
1267 if (y >= scrollMax) {
1268 return y - scrollMax;
1269 }
1270 }
1271 return 0;
1272}
1273
1258static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int duration) { 1274static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int duration) {
1259 /* Get rid of link numbers when scrolling. */ 1275 /* Get rid of link numbers when scrolling. */
1260 if (offset && d->flags & showLinkNumbers_DocumentWidgetFlag) { 1276 if (offset && d->flags & showLinkNumbers_DocumentWidgetFlag) {
@@ -1267,19 +1283,19 @@ static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int dur
1267 showToolbars_Window(get_Window(), offset < 0); 1283 showToolbars_Window(get_Window(), offset < 0);
1268 } 1284 }
1269 } 1285 }
1286#if !defined (iPlatformMobile)
1270 if (!prefs_App()->smoothScrolling) { 1287 if (!prefs_App()->smoothScrolling) {
1271 duration = 0; /* always instant */ 1288 duration = 0; /* always instant */
1272 } 1289 }
1290#endif
1273 int destY = targetValue_Anim(&d->scrollY) + offset; 1291 int destY = targetValue_Anim(&d->scrollY) + offset;
1274 if (destY < 0) { 1292 if (destY < -2 * d->overscroll) {
1275 destY = 0; 1293 destY = -2 * d->overscroll;
1276 stopWidgetMomentum_Touch(as_Widget(d));
1277 } 1294 }
1278 const int scrollMax = scrollMax_DocumentWidget_(d); 1295 const int scrollMax = scrollMax_DocumentWidget_(d);
1279 if (scrollMax > 0) { 1296 if (scrollMax > 0) {
1280 if (destY >= scrollMax) { 1297 if (destY >= scrollMax + 2 * d->overscroll) {
1281 stopWidgetMomentum_Touch(as_Widget(d)); 1298 destY = scrollMax + 2 * d->overscroll;
1282 destY = scrollMax;
1283 } 1299 }
1284 } 1300 }
1285 else { 1301 else {
@@ -1291,6 +1307,18 @@ static void smoothScroll_DocumentWidget_(iDocumentWidget *d, int offset, int dur
1291 else { 1307 else {
1292 setValue_Anim(&d->scrollY, destY, 0); 1308 setValue_Anim(&d->scrollY, destY, 0);
1293 } 1309 }
1310 if (d->overscroll && widgetMode_Touch(as_Widget(d)) == momentum_WidgetTouchMode) {
1311 const int osDelta = overscroll_DocumentWidget_(d);
1312 if (osDelta) {
1313 const float remaining = stopWidgetMomentum_Touch(as_Widget(d));
1314 duration = iMini(1000, 50 * sqrt(remaining / gap_UI));
1315 setValue_Anim(&d->scrollY, osDelta < 0 ? 0 : scrollMax, duration);
1316 d->scrollY.flags = bounce_AnimFlag | easeOut_AnimFlag | softer_AnimFlag;
1317 printf("remaining: %f dur: %d\n", remaining, duration);
1318 d->scrollY.bounce = (osDelta < 0 ? -1 : 1) *
1319 iMini(10 * d->overscroll, remaining * remaining * 0.00005f);
1320 }
1321 }
1294 updateVisible_DocumentWidget_(d); 1322 updateVisible_DocumentWidget_(d);
1295 refresh_Widget(as_Widget(d)); 1323 refresh_Widget(as_Widget(d));
1296 if (duration > 0) { 1324 if (duration > 0) {
@@ -2594,6 +2622,14 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2594 if (isMetricsChange_UserEvent(ev)) { 2622 if (isMetricsChange_UserEvent(ev)) {
2595 updateSize_DocumentWidget(d); 2623 updateSize_DocumentWidget(d);
2596 } 2624 }
2625 else if (ev->type == SDL_USEREVENT && ev->user.code == widgetTouchEnds_UserEventCode) {
2626 const int osDelta = overscroll_DocumentWidget_(d);
2627 if (osDelta) {
2628 smoothScroll_DocumentWidget_(d, -osDelta, 100 * sqrt(iAbs(osDelta) / gap_UI));
2629 d->scrollY.flags = easeOut_AnimFlag | muchSofter_AnimFlag;
2630 }
2631 return iTrue;
2632 }
2597 else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) { 2633 else if (ev->type == SDL_USEREVENT && ev->user.code == command_UserEventCode) {
2598 if (!handleCommand_DocumentWidget_(d, command_UserEvent(ev))) { 2634 if (!handleCommand_DocumentWidget_(d, command_UserEvent(ev))) {
2599 /* Base class commands. */ 2635 /* Base class commands. */
@@ -3913,7 +3949,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
3913 }; 3949 };
3914 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */); 3950 render_DocumentWidget_(d, &ctx, iFalse /* just the mandatory parts */);
3915 setClip_Paint(&ctx.paint, bounds); 3951 setClip_Paint(&ctx.paint, bounds);
3916 const int yTop = docBounds.pos.y - value_Anim(&d->scrollY); 3952 int yTop = docBounds.pos.y - value_Anim(&d->scrollY) + overscroll_DocumentWidget_(d) * 0.667f;
3917 draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds)); 3953 draw_VisBuf(d->visBuf, init_I2(bounds.pos.x, yTop), ySpan_Rect(bounds));
3918 /* Text markers. */ 3954 /* Text markers. */
3919 const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0; 3955 const iBool isTouchSelecting = (flags_Widget(w) & touchDrag_WidgetFlag) != 0;
diff --git a/src/ui/touch.c b/src/ui/touch.c
index 834fe4f8..22f22f9b 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -686,6 +686,9 @@ iBool processEvent_Touch(const SDL_Event *ev) {
686 //dispatchMotion_Touch_(touch->startPos, 0); 686 //dispatchMotion_Touch_(touch->startPos, 0);
687 } 687 }
688 else { 688 else {
689 if (touch->affinity) {
690 dispatchNotification_Touch_(touch, widgetTouchEnds_UserEventCode);
691 }
689 dispatchButtonUp_Touch_(pos); 692 dispatchButtonUp_Touch_(pos);
690 setHover_Widget(NULL); 693 setHover_Widget(NULL);
691 } 694 }
@@ -696,14 +699,34 @@ iBool processEvent_Touch(const SDL_Event *ev) {
696 return iTrue; 699 return iTrue;
697} 700}
698 701
699void stopWidgetMomentum_Touch(iWidget *widget) { 702float stopWidgetMomentum_Touch(const iWidget *widget) {
700 iTouchState *d = touchState_(); 703 iTouchState *d = touchState_();
704 float remaining = 0.0f;
701 iForEach(Array, i, d->moms) { 705 iForEach(Array, i, d->moms) {
702 iMomentum *mom = i.value; 706 iMomentum *mom = i.value;
703 if (mom->affinity == widget) { 707 if (mom->affinity == widget) {
708 remaining = length_F3(mom->velocity);
704 remove_ArrayIterator(&i); 709 remove_ArrayIterator(&i);
705 } 710 }
706 } 711 }
712 return remaining;
713}
714
715enum iWidgetTouchMode widgetMode_Touch(const iWidget *widget) {
716 iTouchState *d = touchState_();
717 iConstForEach(Array, i, d->touches) {
718 const iTouch *touch = i.value;
719 if (touch->affinity == widget) {
720 return touch_WidgetTouchMode;
721 }
722 }
723 iConstForEach(Array, j, d->moms) {
724 const iMomentum *mom = j.value;
725 if (mom->affinity == widget) {
726 return momentum_WidgetTouchMode;
727 }
728 }
729 return none_WidgetTouchMode;
707} 730}
708 731
709void widgetDestroyed_Touch(iWidget *widget) { 732void widgetDestroyed_Touch(iWidget *widget) {
diff --git a/src/ui/touch.h b/src/ui/touch.h
index 0a24248b..1a6fb350 100644
--- a/src/ui/touch.h
+++ b/src/ui/touch.h
@@ -27,10 +27,18 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
27 27
28iDeclareType(Widget) 28iDeclareType(Widget)
29 29
30enum iWidgetTouchMode {
31 none_WidgetTouchMode,
32 touch_WidgetTouchMode,
33 momentum_WidgetTouchMode,
34};
35
30iBool processEvent_Touch (const SDL_Event *); 36iBool processEvent_Touch (const SDL_Event *);
31void update_Touch (void); 37void update_Touch (void);
32void stopWidgetMomentum_Touch(iWidget *widget); 38
33void widgetDestroyed_Touch (iWidget *widget); 39float stopWidgetMomentum_Touch (const iWidget *widget);
40enum iWidgetTouchMode widgetMode_Touch (const iWidget *widget);
41void widgetDestroyed_Touch (iWidget *widget);
34 42
35iInt2 latestPosition_Touch (void); /* valid during processing of current event */ 43iInt2 latestPosition_Touch (void); /* valid during processing of current event */
36size_t numFingers_Touch (void); 44size_t numFingers_Touch (void);
diff --git a/src/ui/util.c b/src/ui/util.c
index 8e808bd2..877ec7c1 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -246,6 +246,7 @@ iBool isFinished_Anim(const iAnim *d) {
246void init_Anim(iAnim *d, float value) { 246void init_Anim(iAnim *d, float value) {
247 d->due = d->when = SDL_GetTicks(); 247 d->due = d->when = SDL_GetTicks();
248 d->from = d->to = value; 248 d->from = d->to = value;
249 d->bounce = 0.0f;
249 d->flags = 0; 250 d->flags = 0;
250} 251}
251 252
@@ -276,20 +277,29 @@ static float valueAt_Anim_(const iAnim *d, const uint32_t now) {
276 return d->from; 277 return d->from;
277 } 278 }
278 float t = pos_Anim_(d, now); 279 float t = pos_Anim_(d, now);
279 const iBool isSoft = (d->flags & softer_AnimFlag) != 0; 280 const iBool isSoft = (d->flags & softer_AnimFlag) != 0;
281 const iBool isVerySoft = (d->flags & muchSofter_AnimFlag) != 0;
280 if ((d->flags & easeBoth_AnimFlag) == easeBoth_AnimFlag) { 282 if ((d->flags & easeBoth_AnimFlag) == easeBoth_AnimFlag) {
281 t = easeBoth_(t); 283 t = easeBoth_(t);
282 if (isSoft) t = easeBoth_(t); 284 if (isSoft) t = easeBoth_(t);
285 if (isVerySoft) t = easeBoth_(easeBoth_(t));
283 } 286 }
284 else if (d->flags & easeIn_AnimFlag) { 287 else if (d->flags & easeIn_AnimFlag) {
285 t = easeIn_(t); 288 t = easeIn_(t);
286 if (isSoft) t = easeIn_(t); 289 if (isSoft) t = easeIn_(t);
290 if (isVerySoft) t = easeIn_(easeIn_(t));
287 } 291 }
288 else if (d->flags & easeOut_AnimFlag) { 292 else if (d->flags & easeOut_AnimFlag) {
289 t = easeOut_(t); 293 t = easeOut_(t);
290 if (isSoft) t = easeOut_(t); 294 if (isSoft) t = easeOut_(t);
295 if (isVerySoft) t = easeOut_(easeOut_(t));
291 } 296 }
292 return d->from * (1.0f - t) + d->to * t; 297 float value = d->from * (1.0f - t) + d->to * t;
298 if (d->flags & bounce_AnimFlag) {
299 t = (1.0f - easeOut_(easeOut_(t))) * easeOut_(t);
300 value += d->bounce * t;
301 }
302 return value;
293} 303}
294 304
295void setValue_Anim(iAnim *d, float to, uint32_t span) { 305void setValue_Anim(iAnim *d, float to, uint32_t span) {
@@ -304,6 +314,7 @@ void setValue_Anim(iAnim *d, float to, uint32_t span) {
304 d->when = now; 314 d->when = now;
305 d->due = now + span; 315 d->due = now + span;
306 } 316 }
317 d->bounce = 0;
307} 318}
308 319
309void setValueSpeed_Anim(iAnim *d, float to, float unitsPerSecond) { 320void setValueSpeed_Anim(iAnim *d, float to, float unitsPerSecond) {
@@ -316,6 +327,7 @@ void setValueSpeed_Anim(iAnim *d, float to, float unitsPerSecond) {
316 d->to = to; 327 d->to = to;
317 d->when = now; 328 d->when = now;
318 d->due = d->when + span; 329 d->due = d->when + span;
330 d->bounce = 0;
319 } 331 }
320} 332}
321 333
@@ -333,9 +345,10 @@ void setValueEased_Anim(iAnim *d, float to, uint32_t span) {
333 d->from = valueAt_Anim_(d, now); 345 d->from = valueAt_Anim_(d, now);
334 d->flags = easeOut_AnimFlag; 346 d->flags = easeOut_AnimFlag;
335 } 347 }
336 d->to = to; 348 d->to = to;
337 d->when = now; 349 d->when = now;
338 d->due = now + span; 350 d->due = now + span;
351 d->bounce = 0;
339} 352}
340 353
341void setFlags_Anim(iAnim *d, int flags, iBool set) { 354void setFlags_Anim(iAnim *d, int flags, iBool set) {
diff --git a/src/ui/util.h b/src/ui/util.h
index d39a00fa..191b8a87 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -105,10 +105,12 @@ enum iAnimFlag {
105 easeOut_AnimFlag = iBit(3), 105 easeOut_AnimFlag = iBit(3),
106 easeBoth_AnimFlag = easeIn_AnimFlag | easeOut_AnimFlag, 106 easeBoth_AnimFlag = easeIn_AnimFlag | easeOut_AnimFlag,
107 softer_AnimFlag = iBit(4), 107 softer_AnimFlag = iBit(4),
108 muchSofter_AnimFlag = iBit(5),
109 bounce_AnimFlag = iBit(6),
108}; 110};
109 111
110struct Impl_Anim { 112struct Impl_Anim {
111 float from, to; 113 float from, to, bounce;
112 uint32_t when, due; 114 uint32_t when, due;
113 int flags; 115 int flags;
114}; 116};