Christos Christou
Christos Christou

Reputation: 127

Smooth (Inertial) Scrolling with SDL2?

I am building SDL2 applications for macOS using C++. I need some "basic" scrolling for an app (like a web browser). I achieve that using the SDL_MouseWheel event, which gives me a fully functional "windows-like" scrolling. I am using a Macbook Pro and I want to bring the trackpad's functionality in.

Simply, I am asking for a better scrolling algorithm (Macbook's trackpad scrolling, inertial scrolling)

I know about the SDL_MultiGesture event, but I don't really know how to put things together to achieve the result I want.

Upvotes: 2

Views: 2961

Answers (3)

Martijn Courteaux
Martijn Courteaux

Reputation: 68887

For some reason, @Aleski's answer doesn't work anymore. The new way to hack this feature back on is:

[[NSUserDefaults standardUserDefaults] setBool: YES
                                       forKey: @"AppleMomentumScrollSupported"];

Upvotes: 3

Aleksi Juvani
Aleksi Juvani

Reputation: 51

I ran into the same issue, and it turns out that SDL opts out of momentum scroll events by turning off the AppleMomentumScrollSupported default in the user defaults system.

You can turn this back on in your application with the following bit of Objective-C++, and your SDL_MouseWheel events will become smoothed.

NSDictionary *appDefaults = [[NSDictionary alloc] initWithObjectsAndKeys:
                            [NSNumber numberWithBool:YES], @"AppleMomentumScrollSupported",
                            nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
[appDefaults release];

Upvotes: 5

Christos Christou
Christos Christou

Reputation: 127

Okay, here's the answer to the problem.

First of all, you will have to do it manually. I also assume that you know SDL2 and C++.
*Note: I'm doing scrolling only for Y-Axis (you can do it for both if you want).

Firstly we will need some variables:

int    scrolling;               // flag (scrolling or not)
int    scroll_sensitivity = 40; // how fast we want to scroll
double scroll_Y = 0;            // current scrolling amount (on Y-Axis)
double scroll_acceleration;     // scrolling speed
double scroll_friction = 0.001; // how fast we decelerate
double scroll_prev_pos;         // previous event's position

After that you will need to handle the SDL_MultiGesture event:

case SDL_MULTIGESTURE:{
  if(event.mgesture.numFingers == 2){
    if(scrolling == 0){
      scrolling = 1;
      scroll_prev_pos = event.mgesture.y;
    } else{
      double dy = event.mgesture.y - scroll_prev_pos;
      scroll_acceleration = dy * 40;
      scroll_prev_pos = event.mgesture.y;
      scrolling = 1;
    }
  }
  break;
}

Also, we need to stop scrolling on SDL_FingerDown event:

case SDL_FINGERDOWN:{
  scrolling = 0;
  break;
}

Next, we want to update scroll_Y (put it in your "update" function):

if(scrolling){
  if(scroll_acceleration > 0) scroll_acceleration -= scroll_friction;
  if(scroll_acceleration < 0) scroll_acceleration += scroll_friction;
  if(abs(scroll_acceleration) < 0.0005) scroll_acceleration = 0;
  scroll_Y += scroll_sensitivity * scroll_acceleration;
  // Here you have to set your scrolling bounds i.e. if(scroll_Y < 0) scroll_Y = 0;
}

Finally, we want to render according to our scroll values:

SDL_Rect rect = {some_x, some_y + scroll_Y, some_w, some_h};
SDL_RenderCopy(renderer, some_texture, NULL, &rect);

This is it!
I have a fully working app with the above code, so I'm 100% sure that this works as supposed. If you have any problems (because it's not actual code, it's more like an algorithm) contact me. As I mentioned before, I assume that you already know good enough SDL and C++, so I believe that you are able to understand the implementation. Also, I know that this solution can become better, so if you have anything to add / change, just say it!

Have a nice day!

Upvotes: 6

Related Questions