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