SDL and Buttons

So I am working on a pause menu with two buttons. I am trying to create a reusable button class that I can use later for other menus/projects.

Buttons sounded easy at first but I think I am thinking about it wrong.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Button{
    private:
        int width;
        int height;
        int id;
        string text;
    public:
        Button();
        
        int on_click();
        int get_width();
        int get_height();
};
Button::Button(int w, int h, int i, int t){
    width = w;
    height = h;
    id = i;
    text = t;
}
int Button::on_click(){
    return id;
}


I was going to have the button render itself but I think that wouldn't be good. I'm just not sure what I do and don't need in the class. A little advice would be wonderful.
Last edited on
Don't know much about SDL, but you have 4 ints as parameters for your Button constructor, but on line 18, you are trying to assign t (an int) to your text member (a string).
Last edited on
This is what I use. Let me know if there's something you don't understand.

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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
class UserInterface{
public:
	virtual ~UserInterface(){}
	//Handle events from an internal object. Return a flag indicating one or
	//more actions that need to be performed afterwards (e.g. redraw the screen).
	virtual unsigned receive(TotalTimeUpdate &) = 0;
	virtual unsigned receive(MetaDataUpdate &) = 0;
};

//A class for the UI as a whole. All GUIElements are children
//or grandchildren of an instance of this class.
class GUI;

class GUIElement : public UserInterface{
protected:
	GUI *sui;
	GUIElement *parent;
public:
	GUIElement(GUI *sui, GUIElement *parent): sui(sui), parent(parent){}
	virtual ~GUIElement(){}
	//Redraw the object.
	virtual void update(){}
	//Handle SDL events. Return a flag. The GUIElement implementations are
	//trivial: they do nothing and return 0.
    virtual unsigned handle_event(const SDL_Event &);
	virtual unsigned receive(TotalTimeUpdate &);
	virtual unsigned receive(MetaDataUpdate &);
	//Handle a signal from a GUI element (e.g. a button that was pressed).
	virtual void gui_signal(unsigned){}
};

class GUI : public GUIElement{
public:
	//Return values for handle_event() and receive().
	enum InputStatus{
		NOTHING = 0,
		QUIT = 1,
		REDRAW = 2,
	};
private:
	std::list<boost::shared_ptr<GUIElement> > gui_elements;
	
	unsigned handle_in_events();
public:
	void main_loop();
	unsigned receive(TotalTimeUpdate &);
	unsigned receive(MetaDataUpdate &);
};

void GUI::main_loop(){
	unsigned status;
	while (!check_flag(status = this->handle_in_events(), QUIT)){
		//Do other stuff, such as drawing.
	}
}

unsigned GUI::handle_in_events(){
	SDL_Event e;
	unsigned ret = NOTHING;
	while (SDL_PollEvent(&e)){
		//Handle input events that are considered "global" to the application.
		switch (e.type){
			case SDL_QUIT:
				return QUIT;
			case SDL_WINDOWEVENT:
				ret |= REDRAW;
				break;
			//etc.
		}
		//Notify children.
		for (auto &p : this->gui_elements)
			ret |= p->handle_event(e);
	}
	return ret;
}

class Button : public GUIElement{
protected:
	//(x, y): The position of the top-left corner.
	//(w, h): The size of the button.
	SDL_Rect bounding_box;
public:
	Button(GUI *sui, GUIElement *parent): GUIElement(sui, parent){}
	virtual ~Button(){}
	unsigned handle_event(const SDL_Event &);
	void set_bounding_box(const SDL_Rect &bb){
		this->bounding_box = bb;
	}
	virtual void on_click() = 0;
};

unsigned Button::handle_event(const SDL_Event &event){
	if (event.type != SDL_MOUSEBUTTONUP)
		return GUI::NOTHING;
	auto x = event.button.x;
	auto y = event.button.y;
	const auto &bb = this->bounding_box;
	if (x >= bb.x && x < bb.x + bb.w && y >= bb.y && y < bb.y + bb.h)
		this->on_click();
	return GUI::NOTHING;
}

class IntegerSignalButton : public Button{
protected:
	unsigned signal_value;
	//If true, the button calls gui_signal(signal_value) on the global GUI
	//object; otherwise, it calls the function on the parent.
	bool global_button;
public:
	IntegerSignalButton(GUI *sui, GUIElement *parent): Button(sui, parent), signal_value(0), global_button(0){}
	virtual ~IntegerSignalButton(){}
	void set_signal_value(unsigned signal_value){
		this->signal_value = signal_value;
	}
	void set_global_button(bool global_button){
		this->global_button = global_button;
	}
	void on_click(){
		(this->global_button ? this->sui : this->parent)->gui_signal(this->signal_value);
	}
};

class GraphicButton : public IntegerSignalButton{
protected:
	Texture graphic;
public:
	GraphicButton(GUI *sui, GUIElement *parent): IntegerSignalButton(sui, parent), graphic(){}
	void set_graphic(Texture graphic){
		this->graphic = graphic;
		this->bounding_box.w = graphic.get_rect().w;
		this->bounding_box.h = graphic.get_rect().h;
	}
	void update();
};

class TextButton : public IntegerSignalButton{
protected:
	std::wstring text;
	int wrapping_limit;
	double scale;
	void calculate_bounding_box();
public:
	TextButton(GUI *sui, GUIElement *parent): IntegerSignalButton(sui, parent), scale(1){
		this->bounding_box.w = max_possible_value(this->bounding_box.w);
		this->bounding_box.h = max_possible_value(this->bounding_box.h);
	}
	void set_text(const std::wstring &text, int max_width = INT_MAX){
		this->text = text;
		this->wrapping_limit = max_width;
	}
	void update();
};
@Ganado

Just a typo.

@helios

That is a lot more than I expected, thanks. It's going to take me a little while to understand what is going on there.

I have come across virtual before but don't really understand what it is doing and you use it quite a bit. What does it do?

I had not came across this but I found an article that explained it very well. I will be using it in the future.

I like what you did with SDL_Rect to keep the data. I didn't think about that.

auto is new to me. I see what it does and I'm not sure if I like it. It seems dangerous if used the wrong way.

You definitely gave me a lot of ideas and a place to start. If I have anymore questions I'll ask and I'll post my code after I'm done. It's not going to be as complex as yours.
Last edited on
I have come across virtual before but don't really understand what it is doing and you use it quite a bit. What does it do?
It's a fairly large subject. The whole of Simula-like object-oriented programming revolves around virtual functions. You should consult a book on OOP or any book on C++.

auto is new to me. I see what it does and I'm not sure if I like it. It seems dangerous if used the wrong way.
If you think that, you're probably not understanding it.
Beyond simple convenience as I used it above, auto is extremely useful when working with templates. Type annotations can quickly drive the generality of a template function right to the ground.
Topic archived. No new replies allowed.