Handling Mouse Scrolling
Learn how to detect and handle mouse scroll wheel events in SDL2, including vertical and horizontal scrolling, as well as scroll wheel button events.
In this lesson, we cover how to detect and react to our user providing input through their mouse scroll wheel. Similar to other forms of input, when these interactions are detected, SDL pushes events into the event queue. Accordingly, we receive these events within our event loop, and can react to them as needed.
This lesson builds on our earlier work, where we have a Window class that initializes SDL and creates a window, and an application loop set up in our main function. We receive and handle mouse wheel events within the SDL_PollEvent loop, highlighted below:
#include <SDL.h>
#include "Window.h"
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event Event;
while(true) {
while(SDL_PollEvent(&Event)) {
// Detect and handle input events
// ...
}
GameWindow.Update();
}
SDL_Quit();
return 0;
}Mouse Wheel Events
When an event is created by the user interacting with their mouse wheel, the SDL_Event object will have a type of SDL_MOUSEWHEEL:
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_MOUSEWHEEL) {
std::cout << "Mouse wheel input detected\n";
}
}Mouse wheel input detected
Mouse wheel input detected
Mouse wheel input detectedWithin the wheel object, the y member variable is an integer that lets us know how far the wheel was scrolled. By default, positive values mean the user scrolled the wheel forward, while negative means they scrolled backward.
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_MOUSEWHEEL) {
std::cout << "Scroll amount: "
<< Event.wheel.y << '\n';
}
}Unlike button presses, mouse scrolling is not discreet. It typically takes some amount of time for the user to provide their full scroll input - that is, how far they want to move their wheel.
Even if they're performing a small scroll that may only take a fraction of a second, it is still likely to span multiple frames of our application. To handle this, a typical scroll from the user will result in several SDL_MOUSEWHEEL events, each with a relatively small y value.
The previous program output will be something like this:
Scroll amount: 1
Scroll amount: 2
Scroll amount: 1
Scroll amount: 1This implementation helps us make scroll input feel responsive and smooth. As soon as the user starts their input, we get an event we can react to immediately even though the user may still be scrolling. The full extent of the scroll (5, in the previous example) is spread across multiple frames.
Using the SDL_MouseWheelEvent Type
If we want to store or transfer an SDL_Event that has a type of SDL_MOUSEWHEEL, we can use the more descriptive SDL_MouseWheelEvent subtype. This type is slightly easier to use as it does not require us to access the intermediate wheel subobject to retrieve the information we care about:
// Event Loop
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_MOUSEWHEEL) {
HandleMouseWheel(Event)
}
}// Handler
void HandleMouseWheel(const SDL_MouseWheelEvent& E) {
int ScrollAmount { Event.y };
// ...
}On many mice, the scroll wheel is also a button that can be pressed. This interaction is handled like any other mouse button event, which we covered in the previous lesson:
Mouse Input Basics
Discover how to process mouse input, including position tracking and button presses
The scroll mouse button is typically represented by the SDL_BUTTON_MIDDLE variable, so we can detect scroll button keydown and keyup events like this:
// Event loop
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_MOUSEBUTTONDOWN ||
Event.type == SDL_MOUSEBUTTONUP) {
HandleMouseButton(Event.button);
}
}// Handler
void HandleMouseButton(SDL_MouseButtonEvent& E) {
if (E.button == SDL_BUTTON_MIDDLE) {
std::cout << "\nScroll button "
<< (E.type == SDL_MOUSEBUTTONDOWN
? "pressed" : "released");
}
}Scroll button pressed
Scroll button releasedHorizontal Scrolling
Some mice allow the user to provide horizontal scroll input, typically by tilting their mouse wheel left or right.
These actions also generate SDL_MouseWheelEvent events. The amount of horizontal scrolling is represented by the x member variable, where negative values indicate scrolling left and positive values indicate scrolling right:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
int HorizontalScrollAmount{E.x};
if (HorizontalScrollAmount != 0) {
std::cout << "Horizontal scroll detected: "
<< HorizontalScrollAmount << '\n';
}
}Horizontal scroll detected: -1
Horizontal scroll detected: 1Precise Scroll Distance
On some devices, it is possible to get more precise scroll information than the integers reported in the x and y fields of the SDL_MouseWheelEvent.
If we want to support those devices, we can access the preciseX and preciseY fields instead. These will be floating point numbers:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
std::cout << "preciseX: " << E.preciseX
<< ", preciseY: " << E.preciseY << '\n';
}preciseX: 0, preciseY: 1.023
preciseX: 0, preciseY: 0.853
preciseX: 0, preciseY: 1.911When precise scroll distance isn't supported, the preciseX and preciseY values will fall back to match the regular x and y values, albeit in float form.
Flipped Scroll Direction
On some platforms, users can invert their scroll direction. SDL detects this, and reports whether the user has inverted their scrolling in the direction field of the SDL_MouseWheelEvent. This variable will be an integer that is equal to either SDL_MOUSEWHEEL_NORMAL or SDL_MOUSEWHEEL_FLIPPED:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
if (E.direction == SDL_MOUSEWHEEL_NORMAL) {
std::cout << "Using normal direction\n";
}
if (E.direction == SDL_MOUSEWHEEL_FLIPPED) {
std::cout << "Using flipped direction\n";
}
}Using normal directionThe x, y, preciseX, and preciseY values reported by SDL have already considered the user's preferred direction so, in most cases, we can ignore this.
However, if we wanted to override the user's individual preferences and ensure scrolling works the same for everyone, we can conditionally multiply the scroll values by -1 before using them:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
int ScrollAmount{E.direction ==
SDL_MOUSEWHEEL_NORMAL ? E.y : E.y * -1};
}Mouse Position
Sometimes, we need to understand where the cursor was when a scroll event occurred. These coordinates are available through the mouseX and mouseY variables of the SDL_MouseWheelEvent:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
std::cout << "\nScroll event happened at "
<< "x = " << E.mouseX
<< ", y = " << E.mouseY;
}Scroll event happened at x = 446, y = 181Similar to SDL_MouseMotionEvents, the x and y coordinates are relative to the left edge and top edge of our window by default. If needed, we can determine the distance from the right and bottom edges by subtracting them from our window's width and height:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
int DistanceFromLeft{E.mouseX};
int DistanceFromTop{E.mouseY};
int DistanceFromRight{WindowWidth - E.mouseX};
int DistanceFromBottom{WindowHeight - E.mouseY};
}Summary
This lesson covered how to detect and handle mouse scroll wheel events in SDL2. Key points include:
- Understanding the
SDL_MouseWheelEventstructure and its member variables. - Handling vertical and horizontal scrolling.
- Detecting and responding to scroll wheel button events.
- Using precise scroll distances when supported.
- Accounting for flipped scroll directions and mouse positions during scroll events.
Managing Mouse Focus with SDL2
Learn how to track and respond to mouse focus events in SDL2, including handling multiple windows and customizing focus-related click behavior.