Lazy Foo' Productions

SDL Forums external link SDL Tutorials 🐰SDL3 Tutorials🥚 Articles OpenGL Tutorials OpenGL Forums external link
Follow BlueSky Follow Facebook Follow Twitter Follow Threads
Donate
News FAQs Contact Bugs

Mouse Events

Mouse Events screenshot

Last Updated: Oct 13th, 2024

Here we're going to make some buttons that respond to mouse input.
class LButton
{
    public:
        //Button dimensions
        static constexpr int kButtonWidth = 300;
        static constexpr int kButtonHeight = 200;

        //Initializes internal variables
        LButton();

        //Sets top left position
        void setPosition( float x, float y );

        //Handles mouse event
        void handleEvent( SDL_Event* e );
    
        //Shows button sprite
        void render();

    private:
        enum eButtonSprite
        {
            eButtonSpriteMouseOut = 0,
            eButtonSpriteMouseOverMotion = 1,
            eButtonSpriteMouseDown = 2,
            eButtonSpriteMouseUp = 3
        };

        //Top left position
        SDL_FPoint mPosition;

        //Currently used global sprite
        eButtonSprite mCurrentSprite;
};
Here is our buttons class. It has constants for the dimensions, a constructor, a function to set its position, a function to handle events, and a function to render it.

Then we have an enumeration to define the different mouse states with their corresponding sprite, an SDL_FPoint to define the position of the button, and an eButtonSprite to define the current sprite index.
//LButton Implementation
LButton::LButton():
    mPosition{ 0.f, 0.f },
    mCurrentSprite{ eButtonSpriteMouseOut }
{

}

void LButton::setPosition( float x, float y )
{
    mPosition.x = x;
    mPosition.y = y;
}
Here are the functions used to initialize and position the button.
void LButton::handleEvent( SDL_Event* e )
{
    //If mouse event happened
    if( e->type == SDL_EVENT_MOUSE_MOTION || e->type == SDL_EVENT_MOUSE_BUTTON_DOWN || e->type == SDL_EVENT_MOUSE_BUTTON_UP )
    {
        //Get mouse position
        float x = -1.f, y = -1.f;
        SDL_GetMouseState( &x, &y );

        //Check if mouse is in button
        bool inside = true;

        //Mouse is left of the button
        if( x < mPosition.x )
        {
            inside = false;
        }
        //Mouse is right of the button
        else if( x > mPosition.x + kButtonWidth )
        {
            inside = false;
        }
        //Mouse above the button
        else if( y < mPosition.y )
        {
            inside = false;
        }
        //Mouse below the button
        else if( y > mPosition.y + kButtonHeight )
        {
            inside = false;
        }
Here is where we start handling mouse events.

First we check if it's a SDL_EVENT_MOUSE_MOTION type which corresponds with an SDL_MouseMotionEvent or a SDL_EVENT_MOUSE_BUTTON_DOWN/SDL_EVENT_MOUSE_BUTTON_UP type which corresponds with an SDL_MouseButtonEvent.

If a mouse event happened, we then get the mouse position with SDL_GetMouseState. We then check if the mouse is inside the button. We do this by testing if it is left of the button, right of the button, above the button, or below the button. If it is not left/right/above/below the button, then it must be inside.
        //Mouse is outside button
        if( !inside )
        {
            mCurrentSprite = eButtonSpriteMouseOut;
        }
        //Mouse is inside button
        else
        {
            //Set mouse over sprite
            switch( e->type )
            {
                case SDL_EVENT_MOUSE_MOTION:
                mCurrentSprite = eButtonSpriteMouseOverMotion;
                break;
            
                case SDL_EVENT_MOUSE_BUTTON_DOWN:
                mCurrentSprite = eButtonSpriteMouseDown;
                break;
                
                case SDL_EVENT_MOUSE_BUTTON_UP:
                mCurrentSprite = eButtonSpriteMouseUp;
                break;
            }
        }
    }
}
If the mouse is outside the button, we set it to the mouse out sprite. If the mouse is inside the button, we set the mouse over sprite on a mouse motion, and mouse up/down sprite on mouse button up/down.
void LButton::render()
{
    //Define sprites
    SDL_FRect spriteClips[] = {
        { 0.f, 0 * kButtonHeight, kButtonWidth, kButtonHeight },
        { 0.f, 1 * kButtonHeight, kButtonWidth, kButtonHeight },
        { 0.f, 2 * kButtonHeight, kButtonWidth, kButtonHeight },
        { 0.f, 3 * kButtonHeight, kButtonWidth, kButtonHeight },
    };


    //Show current button sprite
    gButtonSpriteTexture.render( mPosition.x, mPosition.y, &spriteClips[ mCurrentSprite ] );
}
In the rendering function, we define the clip rectangles for the sprites and render the current sprite based on the input from the event handler.
            //Place buttons
            constexpr int kButtonCount = 4;
            LButton buttons[ kButtonCount ];
            buttons[ 0 ].setPosition(                                    0,                                      0 );
            buttons[ 1 ].setPosition( kScreenWidth - LButton::kButtonWidth,                                      0 );
            buttons[ 2 ].setPosition(                                    0, kScreenHeight - LButton::kButtonHeight );
            buttons[ 3 ].setPosition( kScreenWidth - LButton::kButtonWidth, kScreenHeight - LButton::kButtonHeight );
Before we enter the main loop, we define the buttons by placing them in each corner of the screen.
                //Get event data
                while( SDL_PollEvent( &e ) )
                {
                    //If event is quit type
                    if( e.type == SDL_EVENT_QUIT )
                    {
                        //End the main loop
                        quit = true;
                    }
                    
                    //Handle button events
                    for( int i = 0; i < kButtonCount; ++i )
                    {
                        buttons[ i ].handleEvent( &e );
                    }
                }
Here we're passing the event from the event loop to our buttons.
                //Fill the background
                SDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF,  0xFF );
                SDL_RenderClear( gRenderer );

                //Render buttons
                for( int i = 0; i < kButtonCount; i++ )
                {
                    buttons[ i ].render();
                }

                //Update screen
                SDL_RenderPresent( gRenderer );
            } 
And here we're rendering the buttons to the screen.