Further cleanup in the MenuManager
[supertux.git] / src / gui / menu_manager.cpp
1 //  SuperTux
2 //  Copyright (C) 2009 Ingo Ruhnke <grumbel@gmx.de>
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 "supertux/globals.hpp"
25 #include "supertux/menu/menu_storage.hpp"
26 #include "supertux/timer.hpp"
27 #include "util/log.hpp"
28 #include "video/drawing_context.hpp"
29
30 MenuManager* MenuManager::s_instance = 0;
31
32 MenuManager&
33 MenuManager::instance()
34 {
35   assert(s_instance);
36   return *s_instance;
37 }
38
39 namespace {
40
41 Rectf menu2rect(const Menu& menu)
42 {
43   return Rectf(menu.get_pos().x,
44                menu.get_pos().y,
45                menu.get_pos().x + menu.get_width(),
46                menu.get_pos().y + menu.get_height());
47 }
48
49 Rectf pos2rect(const Menu& menu)
50 {
51   return Rectf(menu.get_pos().x,
52                menu.get_pos().y,
53                menu.get_pos().x,
54                menu.get_pos().y);
55 }
56
57 } // namespace
58
59 class MenuTransition
60 {
61 private:
62   Rectf m_from_rect;
63   Rectf m_to_rect;
64
65   bool m_closing_animation;
66   float m_effect_progress;
67   float m_effect_start_time;
68   bool m_is_active;
69 public:
70   MenuTransition() :
71     m_is_active(false)
72   {
73   }
74
75   void start(const Rectf& to_rect)
76   {
77     m_to_rect = to_rect;
78   }
79
80   void start(const Rectf& from_rect,
81              const Rectf& to_rect)
82   {
83     m_from_rect = from_rect;
84     m_to_rect = to_rect;
85
86     m_effect_start_time = real_time;
87     m_effect_progress = 0.0f;
88
89     m_is_active = true;
90   }
91
92   void update()
93   {
94     m_effect_progress = (real_time - m_effect_start_time) * 6.0f;
95
96     if (m_effect_progress >= 1.0f) {
97       m_effect_progress = 1.0f;
98
99       /*      if (close)
100       {
101         MenuManager::instance().m_current = 0;
102         close = false;
103         }*/
104     }
105     else if (m_effect_progress <= 0.0f)
106     {
107       m_effect_progress = 0.0f;
108     }
109   }
110
111   void draw(DrawingContext& context)
112   {
113 #ifdef GRUMBEL
114     Vector pos = current()->get_pos();
115     float menu_width = current()->get_width();
116     float menu_height = current()->get_height();
117
118     float p = m_effect_progress;
119     if (p != 1.0f)
120     {
121       if (close)
122       {
123         menu_width *= 1.0f - p;
124         menu_height *= 1.0f - p;
125       }
126       else if (MenuManager::instance().m_previous)
127       {
128         menu_width  = (menu_width  * p) + (MenuManager::instance().m_previous->get_width()  * (1.0f - p));
129         menu_height = (menu_height * p) + (MenuManager::instance().m_previous->get_height() * (1.0f - p));
130         //std::cout << p << " " << this << " " << last_menus.back() << std::endl;
131       }
132       else
133       {
134         menu_width  *= p;
135         menu_height *= p;
136       }
137     }
138
139     // draw menu background rectangles
140     context.draw_filled_rect(Rectf(Vector(pos.x - menu_width/2-4, pos.y - menu_height/2 - 10-4),
141                                    Vector(pos.x + menu_width/2+4, pos.y - menu_height/2 + 10 + menu_height+4)),
142                              Color(0.2f, 0.3f, 0.4f, 0.8f),
143                              20.0f,
144                              LAYER_GUI-10);
145
146     context.draw_filled_rect(Rectf(Vector(pos.x - menu_width/2, pos.y - menu_height/2 - 10),
147                                    Vector(pos.x + menu_width/2, pos.y - menu_height/2 + 10 + menu_height)),
148                              Color(0.6f, 0.7f, 0.8f, 0.5f),
149                              16.0f,
150                              LAYER_GUI-10);
151 #endif
152   }
153
154   bool is_active()
155   {
156     return m_is_active;
157   }
158 };
159
160 MenuManager::MenuManager() :
161   m_menu_stack(),
162   m_transition(new MenuTransition)
163 {
164   s_instance = this;
165 }
166
167 MenuManager::~MenuManager()
168 {
169   s_instance = nullptr;
170 }
171
172 void
173 MenuManager::update()
174 {
175   if (current())
176   {
177     current()->update();
178   } 
179 }
180
181 void
182 MenuManager::event(const SDL_Event& event)
183 {
184   if (current())
185   {
186     current()->event(event);
187   }
188 }
189
190 void
191 MenuManager::draw(DrawingContext& context)
192 {
193   if (!current())
194   {
195     return;
196   }
197
198   m_transition->update();
199   m_transition->draw(context);
200
201 #ifdef GRUMBEL
202   // only pass events in non-anim states
203   if(m_effect_progress != 1.0f)
204     return;
205 #endif
206
207   current()->draw(context);
208
209   if (MouseCursor::current())
210   {
211     MouseCursor::current()->draw(context);
212   }
213 }
214
215 bool
216 MenuManager::check_menu()
217 {
218   if (current())
219   {
220     current()->check_menu();
221     return true;
222   }
223   else
224   {
225     return false;
226   }
227 }
228
229 void
230 MenuManager::push_menu(int id)
231 {
232   push_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id)));
233 }
234
235 void
236 MenuManager::push_menu(std::unique_ptr<Menu> menu)
237 {
238   //start_transition_effect();
239   m_menu_stack.push_back(std::move(menu));
240 }
241
242 void
243 MenuManager::pop_menu()
244 {
245   if (m_menu_stack.empty())
246   {
247     log_warning << "trying to pop on an empty menu_stack" << std::endl;
248   }
249   else
250   {
251     m_menu_stack.pop_back();
252     //start_transition_effect();
253   }
254 }
255
256 void
257 MenuManager::clear_menu_stack()
258 {
259   m_menu_stack.clear();
260 }
261
262 void
263 MenuManager::set_menu(int id)
264 {
265   set_menu(MenuStorage::instance().create(static_cast<MenuStorage::MenuId>(id)));
266 }
267
268 void
269 MenuManager::set_menu(std::unique_ptr<Menu> menu)
270 {
271   if (menu)
272   {
273     m_transition->start(pos2rect(*menu), menu2rect(*menu));
274     m_menu_stack.push_back(std::move(menu));
275   }
276   else
277   {
278     m_menu_stack.clear();
279   }
280
281   // just to be sure...
282   g_input_manager->reset();
283 }
284
285 void
286 MenuManager::recalc_pos()
287 {
288   for(auto i = m_menu_stack.begin(); i != m_menu_stack.end(); ++i)
289   {
290     // FIXME: This is of course not quite right, since it ignores any previous set_pos() calls
291     (*i)->set_pos(SCREEN_WIDTH/2, SCREEN_HEIGHT/2);
292   }
293 }
294
295 Menu*
296 MenuManager::current() const
297 {
298   if (m_menu_stack.empty())
299   {
300     return nullptr;
301   }
302   else
303   {
304     return m_menu_stack.back().get();
305   }
306 }
307
308 /* EOF */