SDL need help with clickable button

Ok so im trying to make a program and i have 1 button in it and when i click it i want it to perform the action to exit out of the program for now just to see if it works but i cant get it to work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <SDL.h>

using namespace std;

int main(int argc, char* argv[])
{
    bool running = true;
    SDL_Event event;
    Uint8 *keyStates = SDL_GetKeyState(0);

    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_Surface *screen;
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    SDL_Surface *encryptBMP;
    encryptBMP = SDL_LoadBMP("Encrypt.bmp");

    Uint32 screenColor = SDL_MapRGB(screen->format, 25, 23, 90);

    SDL_Rect offset;
    offset.x = 40;
    offset.y = 40;

    while(running)
    {
        while(SDL_PollEvent(&event))
        {
            switch(event.type)
            {
                case SDL_QUIT:
                    running = false;
                    break;
            }
        }

        if(SDL_MOUSEBUTTONDOWN == offset.x && offset.y)
        {
            SDL_Quit();
        }


        SDL_FillRect(screen, NULL, screenColor);
        SDL_BlitSurface(encryptBMP, NULL, screen, &offset);
        SDL_Flip(screen);
    }
    SDL_FreeSurface(screen);
    SDL_FreeSurface(encryptBMP);
    SDL_Quit();
}
You should handle the mouse events inside the even loop like you do with the SDL_QUIT event. event.button.x and event.button.y gives you the mouse position so you can use that to calculate if the mouse click was on the encrypt button. SDL_Quit doesn't quit the program and you shouldn't call other SDL functions after it. You already quit the program on the SDL_QUIT event so just do the same way here.
SDL_MOUSEBUTTONDOWN is just a event type state flag. The mouse XY variables are stored in event.button.x; event.button.y : http://sdl.beuc.net/sdl.wiki/SDL_MouseButtonEvent
Can i see an example using my code please?
Something like this:
1
2
3
4
5
6
7
8
9
10
11
bool XYInRect( const SDL_Rect& rect, int x, int y )
{
    return ( /* some implementation */ );
}
if ( event.type == SDL_MOUSEBUTTONDOWN ) // if the user clicked a mousebutton
{
    if ( XYInRect( rectOfButton, event.mouse.x, event.mouse.y ) ) // so if the mouse-click is on the button
    {
        // do some action
    }
}
Last edited on
Ok i got it working but it moves the button even when i dont click on it?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <SDL.h>

using namespace std;

bool XYInRect(const SDL_Rect& rect, int x, int y)
{
    return (true);
}

int main(int argc, char* argv[])
{
    bool running = true;
    SDL_Event event;
    Uint8 *keyStates = SDL_GetKeyState(0);

    SDL_Init(SDL_INIT_EVERYTHING);

    SDL_Surface *screen;
    screen = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);

    SDL_Surface *encryptBMP;
    encryptBMP = SDL_LoadBMP("Encrypt.bmp");

    Uint32 screenColor = SDL_MapRGB(screen->format, 25, 23, 90);

    SDL_Rect offset;
    offset.x = 40;
    offset.y = 40;

    while(running)
    {
        while(SDL_PollEvent(&event))
        {
            switch(event.type)
            {
                case SDL_QUIT:
                    running = false;
                    break;
            }
        }


        if (event.type == SDL_MOUSEBUTTONDOWN) // if the user clicked a mousebutton
        {
            if (XYInRect(offset, event.motion.x, event.motion.y)) // so if the mouse-click is on the button
            {
                offset.x = 70;
                offset.y = 70;
            }
        }

        SDL_FillRect(screen, NULL, screenColor);
        SDL_BlitSurface(encryptBMP, NULL, screen, &offset);
        SDL_Flip(screen);
    }
    SDL_FreeSurface(screen);
    SDL_FreeSurface(encryptBMP);
    SDL_Quit();
}
Last edited on
You should handle the event inside the loop that calls SDL_PollEvent.
Like this?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 while(SDL_PollEvent(&event))
        {
            switch(event.type)
            {
                case SDL_QUIT:
                    running = false;
                    break;
            }

            if(event.type == SDL_MOUSEBUTTONDOWN) // if the user clicked a mousebutton
            {
                if(XYInRect(offset, event.button.x, event.button.y)) // so if the mouse-click is on the button
                {
                    offset.x = 90;
                    offset.y = 90;
                }
            }
        }


it still moves even when i dont click on it.
Yes, or you can do it inside the switch like you did with SDL_QUIT.

You have not implemented XYInRect in a useful way. Fransje's intention was that it should return true if the x and y position is inside the rectangle and otherwise false. The rectangle that you pass to it should have the same position, width and height as the button. In your code you have only set the x and y position of the rectangle that you pass to XYInRect.

And by the way, you shouldn't call SDL_FreeSurface on the surface returned from SDL_SetVideoMode. SDL will handle that when you call SDL_Quit.
Last edited on
Im sorry but im still having trouble, its hard for me to grasp stuff easily can i see more code? Also he had

event.mouse.x, event.mouse.y

And my compiler says that SDL_Event has no member named mouse so i changed it to button but im not sure if thats correct.
Last edited on
I don't post code because I think you learn more by writing it yourself. The code that Fransje posted is just an example to demonstrate. You don't have to do it exactly like that.

The things that you need to do is ...
1. check if mouse button was clicked,
2. check if the click is inside the button area,
3. do whatever it is you want to do when the button is clicked.

You have already done (1) so what you should do now is to check if the click is inside the button area.
i also did 3, so #2's code should go in the bool function and return true?
Also he had

event.mouse.x, event.mouse.y


Yeah sorry, it should be button.

i also did 3, so #2's code should go in the bool function and return true?


I use this implementation:
1
2
3
4
bool XYInRect( const SDL_Rect& rect, int x, int y )
{
    return ( ( x >= rect.x && x <= rect.x + rect.w ) && ( y >= rect.y && y <= rect.y + rect.h ) );
}



My point is that it is not very important how you do it. The important thing is that you get it to work. Of course there are ways that are better than others but if you can't think of any way of doing it it's pointless to think about better ways of doing it.

You can use a function if you want. If you think it is easier to not use a function, then you don't have to use a function. You can always change to a function later if you want.

Functions are great because they allow you do split the code into smaller problems. Given some input it produces some output. It is very important to understand what the output and inputs are. I think you didn't completely understood what the first argument to the XYInRect was for, and then it is impossible to implement and use the function properly.

Note that if you decide to use the function, (2) is not just the function definition, but also the function call and the if statement.
Last edited on
ok Fransje i got it working i understand it now :), but if i make another button will i need another function?
Last edited on
In Fransje's code <= should be replaced with <, otherwise it will return true if the click is one pixel below or one pixel to the right of the rectangle.
ok but what if i make another button? i cant use SDL_MOUSEBUTTONDOWN in another case or it will say its a duplicate case? so what do i do there?
You can use SDL_MOUSEBUTTONDOWN, but now for both buttons you check if the click is inside its rect.

By the way thanks Peter87 for the bug catch.
Topic archived. No new replies allowed.