1 | // SuperTux |
2 | // Copyright (C) 2009 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/menu_manager.hpp" |
18 | |
19 | #include "control/input_manager.hpp" |
20 | #include "gui/dialog.hpp" |
21 | #include "gui/menu.hpp" |
22 | #include "gui/mousecursor.hpp" |
23 | #include "supertux/gameconfig.hpp" |
24 | #include "supertux/globals.hpp" |
25 | #include "supertux/menu/menu_storage.hpp" |
26 | #include "util/log.hpp" |
27 | #include "video/drawing_context.hpp" |
28 | |
29 | MenuManager* MenuManager:: = nullptr; |
30 | |
31 | MenuManager& |
32 | MenuManager::() |
33 | { |
34 | assert(s_instance); |
35 | return *s_instance; |
36 | } |
37 | |
38 | namespace { |
39 | |
40 | Rectf (const Menu& ) |
41 | { |
42 | return Rectf(menu.get_center_pos().x - menu.get_width() / 2, |
43 | menu.get_center_pos().y - menu.get_height() / 2, |
44 | menu.get_center_pos().x + menu.get_width() / 2, |
45 | menu.get_center_pos().y + menu.get_height() / 2); |
46 | } |
47 | |
48 | } // namespace |
49 | |
50 | class final |
51 | { |
52 | private: |
53 | Rectf ; |
54 | Rectf ; |
55 | |
56 | float ; |
57 | float ; |
58 | bool ; |
59 | |
60 | public: |
61 | () : |
62 | m_from_rect(), |
63 | m_to_rect(), |
64 | m_effect_progress(1.0f), |
65 | m_effect_start_time(), |
66 | m_is_active(false) |
67 | { |
68 | } |
69 | |
70 | void (const Rectf& from_rect, |
71 | const Rectf& to_rect) |
72 | { |
73 | m_from_rect = from_rect; |
74 | m_to_rect = to_rect; |
75 | |
76 | m_effect_start_time = g_real_time; |
77 | m_effect_progress = 0.0f; |
78 | |
79 | m_is_active = true; |
80 | } |
81 | |
82 | void (const Rectf& rect) |
83 | { |
84 | m_to_rect = m_from_rect = rect; |
85 | } |
86 | |
87 | void () |
88 | { |
89 | if (!g_config->transitions_enabled && m_is_active) |
90 | { |
91 | m_effect_progress = 1.0f; |
92 | m_is_active = false; |
93 | return; |
94 | } |
95 | if (m_is_active) |
96 | { |
97 | m_effect_progress = (g_real_time - m_effect_start_time) * 6.0f; |
98 | |
99 | if (m_effect_progress > 1.0f) |
100 | { |
101 | m_effect_progress = 1.0f; |
102 | m_is_active = false; |
103 | } |
104 | } |
105 | } |
106 | |
107 | void (DrawingContext& context) |
108 | { |
109 | float p = m_effect_progress; |
110 | |
111 | Rectf rect = m_to_rect; |
112 | if (m_is_active) |
113 | { |
114 | rect = Rectf((m_to_rect.get_left() * p) + (m_from_rect.get_left() * (1.0f - p)), |
115 | (m_to_rect.get_top() * p) + (m_from_rect.get_top() * (1.0f - p)), |
116 | (m_to_rect.get_right() * p) + (m_from_rect.get_right() * (1.0f - p)), |
117 | (m_to_rect.get_bottom() * p) + (m_from_rect.get_bottom() * (1.0f - p))); |
118 | } |
119 | |
120 | // draw menu background rectangles |
121 | context.color().draw_filled_rect(Rectf(rect.get_left() - 4, rect.get_top() - 10-4, |
122 | rect.get_right() + 4, rect.get_bottom() + 10 + 4), |
123 | Color(0.2f, 0.3f, 0.4f, 0.8f), |
124 | 20.0f, |
125 | LAYER_GUI-10); |
126 | |
127 | context.color().draw_filled_rect(Rectf(rect.get_left(), rect.get_top() - 10, |
128 | rect.get_right(), rect.get_bottom() + 10), |
129 | Color(0.6f, 0.7f, 0.8f, 0.5f), |
130 | 16.0f, |
131 | LAYER_GUI-10); |
132 | } |
133 | |
134 | bool () const |
135 | { |
136 | return m_is_active; |
137 | } |
138 | }; |
139 | |
140 | MenuManager::() : |
141 | m_dialog(), |
142 | m_has_next_dialog(false), |
143 | m_next_dialog(), |
144 | m_menu_stack(), |
145 | m_transition(new MenuTransition) |
146 | { |
147 | s_instance = this; |
148 | } |
149 | |
150 | MenuManager::() |
151 | { |
152 | s_instance = nullptr; |
153 | } |
154 | |
155 | void |
156 | MenuManager::() |
157 | { |
158 | for (const auto& : m_menu_stack) |
159 | { |
160 | menu->refresh(); |
161 | } |
162 | } |
163 | |
164 | void |
165 | MenuManager::(const Controller& controller) |
166 | { |
167 | if (m_dialog && !m_dialog->is_passive()) |
168 | { |
169 | m_dialog->process_input(controller); |
170 | } |
171 | else if (current_menu()) |
172 | { |
173 | current_menu()->process_input(controller); |
174 | } |
175 | } |
176 | |
177 | void |
178 | MenuManager::(const SDL_Event& ev) |
179 | { |
180 | if (!m_transition->is_active()) |
181 | { |
182 | if (m_dialog && !m_dialog->is_passive()) |
183 | { |
184 | m_dialog->event(ev); |
185 | } |
186 | else if (current_menu()) |
187 | { |
188 | // only pass events when the menu is fully visible and not in a |
189 | // transition animation |
190 | current_menu()->event(ev); |
191 | } |
192 | } |
193 | } |
194 | |
195 | void |
196 | MenuManager::(DrawingContext& context) |
197 | { |
198 | if (m_has_next_dialog) |
199 | { |
200 | m_dialog = std::move(m_next_dialog); |
201 | m_has_next_dialog = false; |
202 | } |
203 | |
204 | if (m_transition->is_active()) |
205 | { |
206 | m_transition->update(); |
207 | m_transition->draw(context); |
208 | } |
209 | else |
210 | { |
211 | if (m_dialog) |
212 | { |
213 | m_dialog->update(); |
214 | m_dialog->draw(context); |
215 | } |
216 | if (current_menu() && (!m_dialog || m_dialog->is_passive())) |
217 | { |
218 | // brute force the transition into the right shape in case the |
219 | // menu has changed sizes |
220 | m_transition->set(menu2rect(*current_menu())); |
221 | m_transition->draw(context); |
222 | |
223 | current_menu()->draw(context); |
224 | } |
225 | } |
226 | |
227 | if ((m_dialog || current_menu()) && MouseCursor::current()) |
228 | { |
229 | MouseCursor::current()->draw(context); |
230 | } |
231 | } |
232 | |
233 | void |
234 | MenuManager::(std::unique_ptr<Dialog> dialog) |
235 | { |
236 | // delay reseting m_dialog to a later point, as otherwise the Dialog |
237 | // can't unset itself without ending up with "delete this" problems |
238 | m_next_dialog = std::move(dialog); |
239 | m_has_next_dialog = true; |
240 | } |
241 | |
242 | void |
243 | MenuManager::(int id) |
244 | { |
245 | push_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id))); |
246 | } |
247 | |
248 | void |
249 | MenuManager::(int id) |
250 | { |
251 | set_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id))); |
252 | } |
253 | |
254 | void |
255 | MenuManager::(std::unique_ptr<Menu> ) |
256 | { |
257 | assert(menu); |
258 | transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(), |
259 | menu.get()); |
260 | m_menu_stack.push_back(std::move(menu)); |
261 | } |
262 | |
263 | void |
264 | MenuManager::() |
265 | { |
266 | if (m_menu_stack.empty()) |
267 | { |
268 | log_warning << "trying to pop on an empty menu_stack" << std::endl; |
269 | } |
270 | else |
271 | { |
272 | transition(m_menu_stack.back().get(), |
273 | (m_menu_stack.size() >= 2) |
274 | ? m_menu_stack[m_menu_stack.size() - 2].get() |
275 | : nullptr); |
276 | |
277 | m_menu_stack.pop_back(); |
278 | } |
279 | } |
280 | |
281 | void |
282 | MenuManager::(std::unique_ptr<Menu> ) |
283 | { |
284 | if (menu) |
285 | { |
286 | transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(), |
287 | menu.get()); |
288 | m_menu_stack.clear(); |
289 | m_menu_stack.push_back(std::move(menu)); |
290 | } |
291 | else |
292 | { |
293 | transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(), |
294 | nullptr); |
295 | m_menu_stack.clear(); |
296 | } |
297 | |
298 | // just to be sure... |
299 | InputManager::current()->reset(); |
300 | } |
301 | |
302 | void |
303 | MenuManager::() |
304 | { |
305 | transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(), |
306 | nullptr); |
307 | m_menu_stack.clear(); |
308 | } |
309 | |
310 | void |
311 | MenuManager::() |
312 | { |
313 | for (const auto& : m_menu_stack) |
314 | { |
315 | menu->on_window_resize(); |
316 | } |
317 | } |
318 | |
319 | Menu* |
320 | MenuManager::() const |
321 | { |
322 | if (m_menu_stack.empty()) |
323 | { |
324 | return nullptr; |
325 | } |
326 | else |
327 | { |
328 | return m_menu_stack.back().get(); |
329 | } |
330 | } |
331 | |
332 | void |
333 | MenuManager::(Menu* from, Menu* to) |
334 | { |
335 | if (!from && !to) |
336 | { |
337 | return; |
338 | } |
339 | else |
340 | { |
341 | Rectf from_rect; |
342 | if (from) |
343 | { |
344 | from_rect = menu2rect(*from); |
345 | } |
346 | else |
347 | { |
348 | from_rect = Rectf(to->get_center_pos(), Sizef(0, 0)); |
349 | } |
350 | |
351 | Rectf to_rect; |
352 | if (to) |
353 | { |
354 | to_rect = menu2rect(*to); |
355 | } |
356 | else |
357 | { |
358 | to_rect = Rectf(from->get_center_pos(), Sizef(0, 0)); |
359 | } |
360 | |
361 | m_transition->start(from_rect, to_rect); |
362 | } |
363 | } |
364 | |
365 | /* EOF */ |
366 | |