Turned a lot of other global objects into Currentons
[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 void
205 MenuManager::push_menu(int id)
206 {
207   push_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id)));
208 }
209
210 void
211 MenuManager::set_menu(int id)
212 {
213   set_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id)));
214 }
215
216 void
217 MenuManager::push_menu(std::unique_ptr<Menu> menu)
218 {
219   assert(menu);
220   transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(),
221              menu.get());
222   m_menu_stack.push_back(std::move(menu));
223 }
224
225 void
226 MenuManager::pop_menu()
227 {
228   if (m_menu_stack.empty())
229   {
230     log_warning << "trying to pop on an empty menu_stack" << std::endl;
231   }
232   else
233   {
234     transition(m_menu_stack.back().get(),
235                (m_menu_stack.size() >= 2)
236                ? m_menu_stack[m_menu_stack.size() - 2].get()
237                : nullptr);
238
239     m_menu_stack.pop_back();
240   }
241 }
242
243 void
244 MenuManager::set_menu(std::unique_ptr<Menu> menu)
245 {
246   if (menu)
247   {
248     transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(),
249                menu.get());
250     m_menu_stack.clear();
251     m_menu_stack.push_back(std::move(menu));
252   }
253   else
254   {
255     transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(),
256                nullptr);
257     m_menu_stack.clear();
258   }
259
260   // just to be sure...
261   InputManager::current()->reset();
262 }
263
264 void
265 MenuManager::clear_menu_stack()
266 {
267   transition(m_menu_stack.empty() ? nullptr : m_menu_stack.back().get(),
268              nullptr);
269   m_menu_stack.clear();
270 }
271
272 void
273 MenuManager::on_window_resize()
274 {
275   for(auto i = m_menu_stack.begin(); i != m_menu_stack.end(); ++i)
276   {
277     (*i)->on_window_resize();
278   }
279 }
280
281 Menu*
282 MenuManager::current() const
283 {
284   if (m_menu_stack.empty())
285   {
286     return nullptr;
287   }
288   else
289   {
290     return m_menu_stack.back().get();
291   }
292 }
293
294 void
295 MenuManager::transition(Menu* from, Menu* to)
296 {
297   if (!from && !to)
298   {
299     return;
300   }
301   else
302   {
303     Rectf from_rect;
304     if (from)
305     {
306       from_rect = menu2rect(*from);
307     }
308     else
309     {
310       from_rect = Rectf(to->get_center_pos(), Sizef(0, 0));
311     }
312
313     Rectf to_rect;
314     if (to)
315     {
316       to_rect = menu2rect(*to);
317     }
318     else
319     {
320       to_rect = Rectf(from->get_center_pos(), Sizef(0, 0));
321     }
322
323     m_transition->start(from_rect, to_rect);
324   }
325 }
326
327 /* EOF */