summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-17 05:59:38 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-17 05:59:38 +0200
commit80e64189df25aafe461fb9fce107025fee2594dc (patch)
tree2a6f719932a4513f861b959ad90acc37b445be7d
parent16d6b6199d580d581f5bdb3c51ab92d1a973d31c (diff)
macOS: Trackpad swipe navigation
Work in progress. Something breaks down when swiping forward twice...
-rw-r--r--src/app.c22
-rw-r--r--src/macos.m103
-rw-r--r--src/ui/documentwidget.c144
-rw-r--r--src/ui/util.h19
4 files changed, 255 insertions, 33 deletions
diff --git a/src/app.c b/src/app.c
index accdd991..68b17ade 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1315,28 +1315,6 @@ void processEvents_App(enum iAppEventMode eventMode) {
1315 } 1315 }
1316 /* Scroll events may be per-pixel or mouse wheel steps. */ 1316 /* Scroll events may be per-pixel or mouse wheel steps. */
1317 if (ev.type == SDL_MOUSEWHEEL) { 1317 if (ev.type == SDL_MOUSEWHEEL) {
1318#if defined (iPlatformAppleDesktop)
1319 /* On macOS, we handle both trackpad and mouse events. We expect SDL to identify
1320 which device is sending the event. */
1321 if (ev.wheel.which == 0) {
1322 /* Trackpad with precise scrolling w/inertia (points). */
1323 setPerPixel_MouseWheelEvent(&ev.wheel, iTrue);
1324 ev.wheel.x *= -d->window->base.pixelRatio;
1325 ev.wheel.y *= d->window->base.pixelRatio;
1326 /* Only scroll on one axis at a time. */
1327 if (iAbs(ev.wheel.x) > iAbs(ev.wheel.y)) {
1328 ev.wheel.y = 0;
1329 }
1330 else {
1331 ev.wheel.x = 0;
1332 }
1333 }
1334 else {
1335 /* Disregard wheel acceleration applied by the OS. */
1336 ev.wheel.x = -ev.wheel.x;
1337 ev.wheel.y = iSign(ev.wheel.y);
1338 }
1339#endif
1340#if defined (iPlatformMsys) 1318#if defined (iPlatformMsys)
1341 ev.wheel.x = -ev.wheel.x; 1319 ev.wheel.x = -ev.wheel.x;
1342#endif 1320#endif
diff --git a/src/macos.m b/src/macos.m
index 5fd76bb9..2d082d28 100644
--- a/src/macos.m
+++ b/src/macos.m
@@ -403,6 +403,94 @@ void registerURLHandler_MacOS(void) {
403 [handler release]; 403 [handler release];
404} 404}
405 405
406#if 0
407static iBool isTracking_;
408
409static void trackSwipe_(NSEvent *event) {
410 if (isTracking_) {
411 return;
412 }
413 isTracking_ = iTrue;
414 [event trackSwipeEventWithOptions:NSEventSwipeTrackingLockDirection
415 dampenAmountThresholdMin:-1.0
416 max:1.0
417 usingHandler:^(CGFloat gestureAmount, NSEventPhase phase,
418 BOOL isComplete, BOOL *stop) {
419 printf("TRACK: amount:%f phase:%lu complete:%d\n",
420 gestureAmount, (unsigned long) phase, isComplete);
421 fflush(stdout);
422 if (isComplete) {
423 isTracking_ = iFalse;
424 }
425 }
426 ];
427}
428#endif
429
430static int swipeDir_ = 0;
431
432static iBool processScrollWheelEvent_(NSEvent *event) {
433 const iBool isPerPixel = (event.hasPreciseScrollingDeltas != 0);
434 const iBool isInertia = (event.momentumPhase & (NSEventPhaseBegan | NSEventPhaseChanged)) != 0;
435 const iBool isEnded = event.scrollingDeltaX == 0.0f && event.scrollingDeltaY == 0.0f && !isInertia;
436 const iWindow *win = &get_MainWindow()->base;
437 /* Post corresponding MOUSEWHEEL events. */
438 SDL_MouseWheelEvent e = { .type = SDL_MOUSEWHEEL };
439 e.timestamp = SDL_GetTicks();
440 e.which = isPerPixel ? 0 : 1; /* Distinction between trackpad and regular mouse. TODO: Still needed? */
441 setPerPixel_MouseWheelEvent(&e, isPerPixel);
442 if (isPerPixel) {
443 setInertia_MouseWheelEvent(&e, isInertia);
444 setScrollFinished_MouseWheelEvent(&e, isEnded);
445 e.x = -event.scrollingDeltaX * win->pixelRatio;
446 e.y = event.scrollingDeltaY * win->pixelRatio;
447 /* Only scroll on one axis at a time. */
448 if (swipeDir_ == 0) {
449 swipeDir_ = iAbs(e.x) > iAbs(e.y) ? 1 : 2;
450 }
451 if (swipeDir_ == 1) {
452 e.y = 0;
453 }
454 else if (swipeDir_ == 2) {
455 e.x = 0;
456 }
457 if (isEnded) {
458 swipeDir_ = 0;
459 }
460 }
461 else {
462 /* Disregard wheel acceleration applied by the OS. */
463 e.x = -event.scrollingDeltaX;
464 e.y = iSign(event.scrollingDeltaY);
465 }
466// printf("#### dx:%d dy:%d phase:%ld end:%d\n", e.x, e.y, (long) event.momentumPhase, isEnded); fflush(stdout);
467 SDL_PushEvent((SDL_Event *) &e);
468#if 0
469 /* On macOS, we handle both trackpad and mouse events. We expect SDL to identify
470 which device is sending the event. */
471 if (ev.wheel.which == 0) {
472 /* Trackpad with precise scrolling w/inertia (points). */
473 setPerPixel_MouseWheelEvent(&ev.wheel, iTrue);
474 ev.wheel.x *= -d->window->base.pixelRatio;
475 ev.wheel.y *= d->window->base.pixelRatio;
476 /* Only scroll on one axis at a time. */
477 if (iAbs(ev.wheel.x) > iAbs(ev.wheel.y)) {
478 ev.wheel.y = 0;
479 }
480 else {
481 ev.wheel.x = 0;
482 }
483 }
484 else {
485 /* Disregard wheel acceleration applied by the OS. */
486 ev.wheel.x = -ev.wheel.x;
487 ev.wheel.y = iSign(ev.wheel.y);
488 }
489#endif
490
491 return iTrue;
492}
493
406void setupApplication_MacOS(void) { 494void setupApplication_MacOS(void) {
407 NSApplication *app = [NSApplication sharedApplication]; 495 NSApplication *app = [NSApplication sharedApplication];
408 [app setActivationPolicy:NSApplicationActivationPolicyRegular]; 496 [app setActivationPolicy:NSApplicationActivationPolicyRegular];
@@ -424,6 +512,21 @@ void setupApplication_MacOS(void) {
424 NSMenuItem *windowCloseItem = [windowMenu itemWithTitle:@"Close"]; 512 NSMenuItem *windowCloseItem = [windowMenu itemWithTitle:@"Close"];
425 windowCloseItem.target = myDel; 513 windowCloseItem.target = myDel;
426 windowCloseItem.action = @selector(closeTab); 514 windowCloseItem.action = @selector(closeTab);
515 [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskScrollWheel
516 handler:^NSEvent*(NSEvent *event){
517// printf("event type: %lu\n", (unsigned long) event.type);
518// fflush(stdout);
519// if (event.type == NSEventTypeGesture) {
520// trackSwipe_(event);
521// printf("GESTURE phase:%lu\n", (unsigned long) event.phase);
522//fflush(stdout);
523// }
524 if (event.type == NSEventTypeScrollWheel &&
525 processScrollWheelEvent_(event)) {
526 return nil; /* was eaten */
527 }
528 return event;
529 }];
427} 530}
428 531
429void hideTitleBar_MacOS(iWindow *window) { 532void hideTitleBar_MacOS(iWindow *window) {
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 0a24f7e5..9f6cdc45 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -234,6 +234,10 @@ enum iDocumentWidgetFlag {
234 fromCache_DocumentWidgetFlag = iBit(16), /* don't write anything to cache */ 234 fromCache_DocumentWidgetFlag = iBit(16), /* don't write anything to cache */
235 animationPlaceholder_DocumentWidgetFlag = iBit(17), /* avoid slow operations */ 235 animationPlaceholder_DocumentWidgetFlag = iBit(17), /* avoid slow operations */
236 invalidationPending_DocumentWidgetFlag = iBit(18), /* invalidate as soon as convenient */ 236 invalidationPending_DocumentWidgetFlag = iBit(18), /* invalidate as soon as convenient */
237 leftWheelSwipe_DocumentWidgetFlag = iBit(19), /* swipe state flags are used on desktop */
238 rightWheelSwipe_DocumentWidgetFlag = iBit(20),
239 eitherWheelSwipe_DocumentWidgetFlag = leftWheelSwipe_DocumentWidgetFlag | rightWheelSwipe_DocumentWidgetFlag,
240// wheelSwipeFinished_DocumentWidgetFlag = iBit(21),
237}; 241};
238 242
239enum iDocumentLinkOrdinalMode { 243enum iDocumentLinkOrdinalMode {
@@ -241,6 +245,12 @@ enum iDocumentLinkOrdinalMode {
241 homeRow_DocumentLinkOrdinalMode, 245 homeRow_DocumentLinkOrdinalMode,
242}; 246};
243 247
248enum iWheelSwipeState {
249 none_WheelSwipeState,
250 direct_WheelSwipeState,
251 //inertia_WheelSwipeState,
252};
253
244struct Impl_DocumentWidget { 254struct Impl_DocumentWidget {
245 iWidget widget; 255 iWidget widget;
246 int flags; /* internal behavior, see enum iDocumentWidgetFlag */ 256 int flags; /* internal behavior, see enum iDocumentWidgetFlag */
@@ -263,6 +273,9 @@ struct Impl_DocumentWidget {
263 int pinchZoomInitial; 273 int pinchZoomInitial;
264 int pinchZoomPosted; 274 int pinchZoomPosted;
265 float swipeSpeed; /* points/sec */ 275 float swipeSpeed; /* points/sec */
276 uint32_t lastSwipeTime;
277 int wheelSwipeDistance;
278 enum iWheelSwipeState wheelSwipeState;
266 iString pendingGotoHeading; 279 iString pendingGotoHeading;
267 iString linePrecedingLink; 280 iString linePrecedingLink;
268 281
@@ -329,7 +342,12 @@ void init_DocumentWidget(iDocumentWidget *d) {
329 init_Widget(w); 342 init_Widget(w);
330 setId_Widget(w, format_CStr("document%03d", ++docEnum_)); 343 setId_Widget(w, format_CStr("document%03d", ++docEnum_));
331 setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue); 344 setFlags_Widget(w, hover_WidgetFlag | noBackground_WidgetFlag, iTrue);
332 if (deviceType_App() != desktop_AppDeviceType) { 345#if defined (iPlatformAppleDesktop)
346 iBool enableSwipeNavigation = iTrue; /* swipes on the trackpad */
347#else
348 iBool enableSwipeNavigation = (deviceType_App() != desktop_AppDeviceType);
349#endif
350 if (enableSwipeNavigation) {
333 setFlags_Widget(w, leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag | 351 setFlags_Widget(w, leftEdgeDraggable_WidgetFlag | rightEdgeDraggable_WidgetFlag |
334 horizontalOffset_WidgetFlag, iTrue); 352 horizontalOffset_WidgetFlag, iTrue);
335 } 353 }
@@ -358,6 +376,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
358 } 376 }
359 d->animWideRunId = 0; 377 d->animWideRunId = 0;
360 init_Anim(&d->animWideRunOffset, 0); 378 init_Anim(&d->animWideRunOffset, 0);
379 d->wheelSwipeState = none_WheelSwipeState;
361 d->selectMark = iNullRange; 380 d->selectMark = iNullRange;
362 d->foundMark = iNullRange; 381 d->foundMark = iNullRange;
363 d->pageMargin = 5; 382 d->pageMargin = 5;
@@ -1942,8 +1961,7 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n
1942 1961
1943static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) { 1962static iBool updateFromHistory_DocumentWidget_(iDocumentWidget *d) {
1944 const iRecentUrl *recent = constMostRecentUrl_History(d->mod.history); 1963 const iRecentUrl *recent = constMostRecentUrl_History(d->mod.history);
1945 iAssert(equalCase_String(&recent->url, d->mod.url)); 1964 if (recent && recent->cachedResponse && equalCase_String(&recent->url, d->mod.url)) {
1946 if (recent && recent->cachedResponse) {
1947 iChangeFlags(d->flags, 1965 iChangeFlags(d->flags,
1948 openedFromSidebar_DocumentWidgetFlag, 1966 openedFromSidebar_DocumentWidgetFlag,
1949 recent->flags.openedFromSidebar); 1967 recent->flags.openedFromSidebar);
@@ -2043,10 +2061,10 @@ static void scrollToHeading_DocumentWidget_(iDocumentWidget *d, const char *head
2043 } 2061 }
2044} 2062}
2045 2063
2046static void scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, int delta, 2064static iBool scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos, int delta,
2047 int duration) { 2065 int duration) {
2048 if (delta == 0) { 2066 if (delta == 0 || d->flags & eitherWheelSwipe_DocumentWidgetFlag) {
2049 return; 2067 return iFalse;
2050 } 2068 }
2051 const iInt2 docPos = documentPos_DocumentWidget_(d, mousePos); 2069 const iInt2 docPos = documentPos_DocumentWidget_(d, mousePos);
2052 iConstForEach(PtrArray, i, &d->visibleWideRuns) { 2070 iConstForEach(PtrArray, i, &d->visibleWideRuns) {
@@ -2087,9 +2105,10 @@ static void scrollWideBlock_DocumentWidget_(iDocumentWidget *d, iInt2 mousePos,
2087 d->animWideRunId = 0; 2105 d->animWideRunId = 0;
2088 init_Anim(&d->animWideRunOffset, 0); 2106 init_Anim(&d->animWideRunOffset, 0);
2089 } 2107 }
2090 break; 2108 return iTrue;
2091 } 2109 }
2092 } 2110 }
2111 return iFalse;
2093} 2112}
2094 2113
2095static void togglePreFold_DocumentWidget_(iDocumentWidget *d, uint16_t preId) { 2114static void togglePreFold_DocumentWidget_(iDocumentWidget *d, uint16_t preId) {
@@ -2691,7 +2710,7 @@ static void setupSwipeOverlay_DocumentWidget_(iDocumentWidget *d, iWidget *overl
2691 const int toPos = width_Widget(overlay); 2710 const int toPos = width_Widget(overlay);
2692 setVisualOffset_Widget(overlay, fromPos, 0, 0); 2711 setVisualOffset_Widget(overlay, fromPos, 0, 0);
2693 /* Bigger screen, faster swipes. */ 2712 /* Bigger screen, faster swipes. */
2694 const float devFactor = (deviceType_App() == tablet_AppDeviceType ? 2.0f : 1.0f); 2713 const float devFactor = (deviceType_App() == phone_AppDeviceType ? 1.0f : 2.0f);
2695 float swipe = iClamp(d->swipeSpeed, devFactor * 400, devFactor * 1000) * gap_UI; 2714 float swipe = iClamp(d->swipeSpeed, devFactor * 400, devFactor * 1000) * gap_UI;
2696 uint32_t span = ((toPos - fromPos) / swipe) * 1000; 2715 uint32_t span = ((toPos - fromPos) / swipe) * 1000;
2697// printf("from:%d to:%d swipe:%f span:%u\n", fromPos, toPos, d->swipeSpeed, span); 2716// printf("from:%d to:%d swipe:%f span:%u\n", fromPos, toPos, d->swipeSpeed, span);
@@ -2841,6 +2860,7 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2841 target->flags |= animationPlaceholder_DocumentWidgetFlag; 2860 target->flags |= animationPlaceholder_DocumentWidgetFlag;
2842 addChildPos_Widget(swipeParent, iClob(target), back_WidgetAddPos); 2861 addChildPos_Widget(swipeParent, iClob(target), back_WidgetAddPos);
2843 setId_Widget(as_Widget(target), "swipeout"); 2862 setId_Widget(as_Widget(target), "swipeout");
2863 setFlags_Widget(as_Widget(target), disabled_WidgetFlag, iTrue);
2844 swap_DocumentWidget_(target, d->doc, d); 2864 swap_DocumentWidget_(target, d->doc, d);
2845 setUrlAndSource_DocumentWidget(d, 2865 setUrlAndSource_DocumentWidget(d,
2846 swipeIn->mod.url, 2866 swipeIn->mod.url,
@@ -3870,6 +3890,105 @@ static void interactingWithLink_DocumentWidget_(iDocumentWidget *d, iGmLinkId id
3870 setRange_String(&d->linePrecedingLink, loc); 3890 setRange_String(&d->linePrecedingLink, loc);
3871} 3891}
3872 3892
3893iLocalDef int wheelSwipeSide_DocumentWidget_(const iDocumentWidget *d) {
3894 return (d->flags & rightWheelSwipe_DocumentWidgetFlag ? 2
3895 : d->flags & leftWheelSwipe_DocumentWidgetFlag ? 1
3896 : 0);
3897}
3898
3899static void finishWheelSwipe_DocumentWidget_(iDocumentWidget *d) {
3900 if (d->flags & eitherWheelSwipe_DocumentWidgetFlag &&
3901 d->wheelSwipeState == direct_WheelSwipeState) {
3902// ~d->flags & wheelSwipeFinished_DocumentWidgetFlag) {
3903// d->wheelSwipeState = inertia_WheelSwipeState;
3904 const int side = wheelSwipeSide_DocumentWidget_(d);
3905// d->flags |= wheelSwipeFinished_DocumentWidgetFlag;
3906 int abort = (side == 1 && d->swipeSpeed < 0 || side == 2 && d->swipeSpeed > 0);
3907 printf("speed:%f\n", d->swipeSpeed / gap_UI);
3908 if (iAbs(d->wheelSwipeDistance) < width_Widget(d) / 4 && iAbs(d->swipeSpeed) < 4 * gap_UI) {
3909 abort = 1;
3910 }
3911 postCommand_Widget(d, "edgeswipe.ended side:%d abort:%d", side, abort);
3912 d->flags &= ~eitherWheelSwipe_DocumentWidgetFlag;
3913 }
3914}
3915
3916static iBool handleWheelSwipe_DocumentWidget_(iDocumentWidget *d, const SDL_MouseWheelEvent *ev) {
3917 iWidget *w = as_Widget(d);
3918 if (~flags_Widget(w) & horizontalOffset_WidgetFlag) {
3919 return iFalse;
3920 }
3921 iAssert(~d->flags & animationPlaceholder_DocumentWidgetFlag);
3922// printf("STATE:%d wheel x:%d inert:%d end:%d\n", d->wheelSwipeState,
3923// ev->x, isInertia_MouseWheelEvent(ev),
3924// isScrollFinished_MouseWheelEvent(ev));
3925// fflush(stdout);
3926 switch (d->wheelSwipeState) {
3927 case none_WheelSwipeState:
3928 /* A new swipe starts. */
3929 if (!isInertia_MouseWheelEvent(ev) && !isScrollFinished_MouseWheelEvent(ev)) {
3930 int side = ev->x < 0 ? 1 : 2;
3931 d->wheelSwipeDistance = -ev->x;
3932 d->flags &= ~eitherWheelSwipe_DocumentWidgetFlag;
3933 d->flags |= (side == 1 ? leftWheelSwipe_DocumentWidgetFlag
3934 : rightWheelSwipe_DocumentWidgetFlag);
3935 // printf("swipe starts at %d, side %d\n", d->wheelSwipeDistance, side);
3936 d->wheelSwipeState = direct_WheelSwipeState;
3937 d->swipeSpeed = 0;
3938 postCommand_Widget(d, "edgeswipe.moved arg:%d side:%d", d->wheelSwipeDistance, side);
3939 return iTrue;
3940 }
3941 break;
3942 case direct_WheelSwipeState:
3943 if (isInertia_MouseWheelEvent(ev) || isScrollFinished_MouseWheelEvent(ev)) {
3944 finishWheelSwipe_DocumentWidget_(d);
3945 d->wheelSwipeState = none_WheelSwipeState;
3946 }
3947// else if (isInertia_MouseWheelEvent(ev)) {
3948// finishWheelSwipe_DocumentWidget_(d);
3949// d->wheelSwipeState = inertia_WheelSwipeState;
3950// }
3951 else {
3952 int step = -ev->x * 2;
3953 d->wheelSwipeDistance += step;
3954 /* Remember the maximum speed. */
3955 if (d->swipeSpeed < 0 && step < 0) {
3956 d->swipeSpeed = iMin(d->swipeSpeed, step);
3957 }
3958 else if (d->swipeSpeed > 0 && step > 0) {
3959 d->swipeSpeed = iMax(d->swipeSpeed, step);
3960 }
3961 else {
3962 d->swipeSpeed = step;
3963 }
3964 switch (wheelSwipeSide_DocumentWidget_(d)) {
3965 case 1:
3966 d->wheelSwipeDistance = iMax(0, d->wheelSwipeDistance);
3967 d->wheelSwipeDistance = iMin(width_Widget(d), d->wheelSwipeDistance);
3968 break;
3969 case 2:
3970 d->wheelSwipeDistance = iMin(0, d->wheelSwipeDistance);
3971 d->wheelSwipeDistance = iMax(-width_Widget(d), d->wheelSwipeDistance);
3972 break;
3973 }
3974 /* TODO: calculate speed, rememeber direction */
3975 //printf("swipe moved to %d, side %d\n", d->wheelSwipeDistance, side);
3976 postCommand_Widget(d, "edgeswipe.moved arg:%d side:%d", d->wheelSwipeDistance,
3977 wheelSwipeSide_DocumentWidget_(d));
3978 }
3979 return iTrue;
3980// case inertia_WheelSwipeState:
3981// if (isScrollFinished_MouseWheelEvent(ev)) {
3982// d->wheelSwipeState = none_WheelSwipeState;
3983// }
3984// else if (!isInertia_MouseWheelEvent(ev)) {
3985// d->wheelSwipeState = none_WheelSwipeState;
3986// }
3987// return iTrue;
3988 }
3989 return iFalse;
3990}
3991
3873static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { 3992static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) {
3874 iWidget *w = as_Widget(d); 3993 iWidget *w = as_Widget(d);
3875 if (isMetricsChange_UserEvent(ev)) { 3994 if (isMetricsChange_UserEvent(ev)) {
@@ -3967,13 +4086,20 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3967#endif 4086#endif
3968 } 4087 }
3969 } 4088 }
4089 else if (ev->type == SDL_MOUSEWHEEL && ev->wheel.y == 0 &&
4090 handleWheelSwipe_DocumentWidget_(d, &ev->wheel)) {
4091 return iTrue;
4092 }
3970 else if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) { 4093 else if (ev->type == SDL_MOUSEWHEEL && isHover_Widget(w)) {
3971 const iInt2 mouseCoord = coord_MouseWheelEvent(&ev->wheel); 4094 const iInt2 mouseCoord = coord_MouseWheelEvent(&ev->wheel);
3972 if (isPerPixel_MouseWheelEvent(&ev->wheel)) { 4095 if (isPerPixel_MouseWheelEvent(&ev->wheel)) {
3973 const iInt2 wheel = init_I2(ev->wheel.x, ev->wheel.y); 4096 const iInt2 wheel = init_I2(ev->wheel.x, ev->wheel.y);
3974 stop_Anim(&d->scrollY.pos); 4097 stop_Anim(&d->scrollY.pos);
3975 immediateScroll_DocumentWidget_(d, -wheel.y); 4098 immediateScroll_DocumentWidget_(d, -wheel.y);
3976 scrollWideBlock_DocumentWidget_(d, mouseCoord, -wheel.x, 0); 4099 if (!scrollWideBlock_DocumentWidget_(d, mouseCoord, -wheel.x, 0) &&
4100 wheel.x) {
4101 handleWheelSwipe_DocumentWidget_(d, &ev->wheel);
4102 }
3977 } 4103 }
3978 else { 4104 else {
3979 /* Traditional mouse wheel. */ 4105 /* Traditional mouse wheel. */
diff --git a/src/ui/util.h b/src/ui/util.h
index b9109aee..4fedd083 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -47,16 +47,31 @@ iLocalDef iBool isMetricsChange_UserEvent(const SDL_Event *d) {
47} 47}
48 48
49enum iMouseWheelFlag { 49enum iMouseWheelFlag {
50 perPixel_MouseWheelFlag = iBit(9), /* e.g., trackpad or finger scroll; applied to `direction` */ 50 /* Note: A future version of SDL may support per-pixel scrolling, but 2.0.x doesn't. */
51 perPixel_MouseWheelFlag = iBit(9), /* e.g., trackpad or finger scroll; applied to `direction` */
52 inertia_MouseWheelFlag = iBit(10),
53 scrollFinished_MouseWheelFlag = iBit(11),
51}; 54};
52 55
53/* Note: A future version of SDL may support per-pixel scrolling, but 2.0.x doesn't. */
54iLocalDef void setPerPixel_MouseWheelEvent(SDL_MouseWheelEvent *ev, iBool set) { 56iLocalDef void setPerPixel_MouseWheelEvent(SDL_MouseWheelEvent *ev, iBool set) {
55 iChangeFlags(ev->direction, perPixel_MouseWheelFlag, set); 57 iChangeFlags(ev->direction, perPixel_MouseWheelFlag, set);
56} 58}
59iLocalDef void setInertia_MouseWheelEvent(SDL_MouseWheelEvent *ev, iBool set) {
60 iChangeFlags(ev->direction, inertia_MouseWheelFlag, set);
61}
62iLocalDef void setScrollFinished_MouseWheelEvent(SDL_MouseWheelEvent *ev, iBool set) {
63 iChangeFlags(ev->direction, scrollFinished_MouseWheelFlag, set);
64}
65
57iLocalDef iBool isPerPixel_MouseWheelEvent(const SDL_MouseWheelEvent *ev) { 66iLocalDef iBool isPerPixel_MouseWheelEvent(const SDL_MouseWheelEvent *ev) {
58 return (ev->direction & perPixel_MouseWheelFlag) != 0; 67 return (ev->direction & perPixel_MouseWheelFlag) != 0;
59} 68}
69iLocalDef iBool isInertia_MouseWheelEvent(const SDL_MouseWheelEvent *ev) {
70 return (ev->direction & inertia_MouseWheelFlag) != 0;
71}
72iLocalDef iBool isScrollFinished_MouseWheelEvent(const SDL_MouseWheelEvent *ev) {
73 return (ev->direction & scrollFinished_MouseWheelFlag) != 0;
74}
60 75
61iInt2 coord_MouseWheelEvent (const SDL_MouseWheelEvent *); 76iInt2 coord_MouseWheelEvent (const SDL_MouseWheelEvent *);
62 77