1 | // SuperTux |
2 | // Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com> |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | #include "gui/dialog.hpp" |
18 | |
19 | #include <algorithm> |
20 | |
21 | #include "control/controller.hpp" |
22 | #include "gui/mousecursor.hpp" |
23 | #include "math/util.hpp" |
24 | #include "supertux/colorscheme.hpp" |
25 | #include "supertux/globals.hpp" |
26 | #include "supertux/resources.hpp" |
27 | #include "video/drawing_context.hpp" |
28 | #include "video/renderer.hpp" |
29 | #include "video/video_system.hpp" |
30 | #include "video/viewport.hpp" |
31 | |
32 | Dialog::Dialog(bool passive) : |
33 | m_text(), |
34 | m_buttons(), |
35 | m_selected_button(), |
36 | m_cancel_button(-1), |
37 | m_passive(passive), |
38 | m_text_size() |
39 | { |
40 | } |
41 | |
42 | Dialog::~Dialog() |
43 | { |
44 | } |
45 | |
46 | void |
47 | Dialog::set_text(const std::string& text) |
48 | { |
49 | m_text = text; |
50 | |
51 | m_text_size = Sizef(Resources::normal_font->get_text_width(m_text), |
52 | Resources::normal_font->get_text_height(m_text)); |
53 | |
54 | } |
55 | |
56 | void |
57 | Dialog::clear_buttons() |
58 | { |
59 | m_buttons.clear(); |
60 | m_selected_button = 0; |
61 | m_cancel_button = -1; |
62 | } |
63 | |
64 | void |
65 | Dialog::add_default_button(const std::string& text, const std::function<void ()>& callback) |
66 | { |
67 | add_button(text, callback); |
68 | m_selected_button = static_cast<int>(m_buttons.size()) - 1; |
69 | } |
70 | |
71 | void |
72 | Dialog::add_cancel_button(const std::string& text, const std::function<void ()>& callback) |
73 | { |
74 | add_button(text, callback); |
75 | m_cancel_button = static_cast<int>(m_buttons.size() - 1); |
76 | } |
77 | |
78 | void |
79 | Dialog::add_button(const std::string& text, const std::function<void ()>& callback) |
80 | { |
81 | m_buttons.push_back({text, callback}); |
82 | } |
83 | |
84 | int |
85 | Dialog::get_button_at(const Vector& mouse_pos) const |
86 | { |
87 | Rectf bg_rect(Vector(static_cast<float>(SCREEN_WIDTH) / 2.0f - m_text_size.width / 2.0f, |
88 | static_cast<float>(SCREEN_HEIGHT) / 2.0f - m_text_size.height / 2.0f), |
89 | Sizef(m_text_size.width, |
90 | m_text_size.height + 44)); |
91 | |
92 | for (int i = 0; i < static_cast<int>(m_buttons.size()); ++i) |
93 | { |
94 | float segment_width = bg_rect.get_width() / static_cast<float>(m_buttons.size()); |
95 | float button_width = segment_width; |
96 | float button_height = 24.0f; |
97 | Vector pos(bg_rect.get_left() + segment_width/2.0f + static_cast<float>(i) * segment_width, |
98 | bg_rect.get_bottom() - 12); |
99 | Rectf button_rect(Vector(pos.x - button_width/2, pos.y - button_height/2), |
100 | Vector(pos.x + button_width/2, pos.y + button_height/2)); |
101 | if (button_rect.contains(mouse_pos)) |
102 | { |
103 | return i; |
104 | } |
105 | } |
106 | return -1; |
107 | } |
108 | |
109 | void |
110 | Dialog::event(const SDL_Event& ev) |
111 | { |
112 | if (m_passive) // Passive dialogs don't accept events |
113 | return; |
114 | |
115 | switch (ev.type) { |
116 | case SDL_MOUSEBUTTONDOWN: |
117 | if (ev.button.button == SDL_BUTTON_LEFT) |
118 | { |
119 | Vector mouse_pos = VideoSystem::current()->get_viewport().to_logical(ev.motion.x, ev.motion.y); |
120 | int new_button = get_button_at(mouse_pos); |
121 | if (new_button != -1) |
122 | { |
123 | m_selected_button = new_button; |
124 | on_button_click(m_selected_button); |
125 | } |
126 | } |
127 | break; |
128 | |
129 | case SDL_MOUSEMOTION: |
130 | { |
131 | Vector mouse_pos = VideoSystem::current()->get_viewport().to_logical(ev.motion.x, ev.motion.y); |
132 | int new_button = get_button_at(mouse_pos); |
133 | if (new_button != -1) |
134 | { |
135 | m_selected_button = new_button; |
136 | if (MouseCursor::current()) |
137 | MouseCursor::current()->set_state(MouseCursorState::LINK); |
138 | } |
139 | else |
140 | { |
141 | if (MouseCursor::current()) |
142 | MouseCursor::current()->set_state(MouseCursorState::NORMAL); |
143 | } |
144 | } |
145 | break; |
146 | |
147 | default: |
148 | break; |
149 | } |
150 | } |
151 | |
152 | void |
153 | Dialog::process_input(const Controller& controller) |
154 | { |
155 | if (m_passive) // Passive dialogs don't accept events |
156 | return; |
157 | |
158 | if (controller.pressed(Control::LEFT)) |
159 | { |
160 | m_selected_button -= 1; |
161 | m_selected_button = std::max(m_selected_button, 0); |
162 | } |
163 | |
164 | if (controller.pressed(Control::RIGHT)) |
165 | { |
166 | m_selected_button += 1; |
167 | m_selected_button = std::min(m_selected_button, static_cast<int>(m_buttons.size()) - 1); |
168 | } |
169 | |
170 | if (controller.pressed(Control::ACTION) || |
171 | controller.pressed(Control::MENU_SELECT)) |
172 | { |
173 | on_button_click(m_selected_button); |
174 | } |
175 | |
176 | if (m_cancel_button != -1 && |
177 | (controller.pressed(Control::ESCAPE) || |
178 | controller.pressed(Control::MENU_BACK))) |
179 | { |
180 | on_button_click(m_cancel_button); |
181 | } |
182 | } |
183 | |
184 | void |
185 | Dialog::draw(DrawingContext& context) |
186 | { |
187 | Rectf bg_rect(Vector(static_cast<float>(m_passive ? |
188 | (static_cast<float>(context.get_width()) - m_text_size.width - 20.0f) : |
189 | static_cast<float>(context.get_width()) / 2.0f - m_text_size.width / 2.0f), |
190 | static_cast<float>(m_passive ? |
191 | (static_cast<float>(context.get_height()) - m_text_size.height - 65.0f) : |
192 | (static_cast<float>(context.get_height()) / 2.0f - m_text_size.height / 2.0f))), |
193 | Sizef(m_text_size.width, |
194 | m_text_size.height + 44)); |
195 | |
196 | // draw background rect |
197 | context.color().draw_filled_rect(bg_rect.grown(12.0f), |
198 | Color(0.2f, 0.3f, 0.4f, m_passive ? 0.3f : 0.8f), |
199 | 16.0f, |
200 | LAYER_GUI-10); |
201 | |
202 | context.color().draw_filled_rect(bg_rect.grown(8.0f), |
203 | Color(0.6f, 0.7f, 0.8f, m_passive ? 0.2f : 0.5f), |
204 | 16.0f, |
205 | LAYER_GUI-10); |
206 | |
207 | // draw text |
208 | context.color().draw_text(Resources::normal_font, m_text, |
209 | Vector(bg_rect.get_left() + bg_rect.get_width()/2.0f, |
210 | bg_rect.get_top()), |
211 | ALIGN_CENTER, LAYER_GUI); |
212 | if (m_passive) |
213 | return; |
214 | |
215 | // draw HL line |
216 | context.color().draw_filled_rect(Rectf(Vector(bg_rect.get_left(), bg_rect.get_bottom() - 35), |
217 | Sizef(bg_rect.get_width(), 4)), |
218 | Color(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI); |
219 | context.color().draw_filled_rect(Rectf(Vector(bg_rect.get_left(), bg_rect.get_bottom() - 35), |
220 | Sizef(bg_rect.get_width(), 2)), |
221 | Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI); |
222 | |
223 | // draw buttons |
224 | for (int i = 0; i < static_cast<int>(m_buttons.size()); ++i) |
225 | { |
226 | float segment_width = bg_rect.get_width() / static_cast<float>(m_buttons.size()); |
227 | float button_width = segment_width; |
228 | Vector pos(bg_rect.get_left() + segment_width/2.0f + static_cast<float>(i) * segment_width, |
229 | bg_rect.get_bottom() - 12); |
230 | |
231 | if (i == m_selected_button) |
232 | { |
233 | float button_height = 24.0f; |
234 | float blink = (sinf(g_real_time * math::PI * 1.0f)/2.0f + 0.5f) * 0.5f + 0.25f; |
235 | context.color().draw_filled_rect(Rectf(Vector(pos.x - button_width/2, pos.y - button_height/2), |
236 | Vector(pos.x + button_width/2, pos.y + button_height/2)).grown(2.0f), |
237 | Color(1.0f, 1.0f, 1.0f, blink), |
238 | 14.0f, |
239 | LAYER_GUI-10); |
240 | context.color().draw_filled_rect(Rectf(Vector(pos.x - button_width/2, pos.y - button_height/2), |
241 | Vector(pos.x + button_width/2, pos.y + button_height/2)), |
242 | Color(1.0f, 1.0f, 1.0f, 0.5f), |
243 | 12.0f, |
244 | LAYER_GUI-10); |
245 | } |
246 | |
247 | context.color().draw_text(Resources::normal_font, m_buttons[i].text, |
248 | Vector(pos.x, pos.y - static_cast<float>(int(Resources::normal_font->get_height() / 2))), |
249 | ALIGN_CENTER, LAYER_GUI, |
250 | i == m_selected_button ? ColorScheme::Menu::active_color : ColorScheme::Menu::default_color); |
251 | } |
252 | } |
253 | |
254 | void |
255 | Dialog::on_button_click(int button) const |
256 | { |
257 | if (m_buttons[button].callback) |
258 | { |
259 | m_buttons[button].callback(); |
260 | } |
261 | MenuManager::instance().set_dialog({}); |
262 | } |
263 | |
264 | /* EOF */ |
265 | |