Split Controller::PAUSE_MENU into ESCAPE and START, as they need to act different...
[supertux.git] / src / gui / dialog.cpp
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 "control/controller.hpp"
20 #include "gui/menu_manager.hpp"
21 #include "gui/menu.hpp"
22 #include "gui/mousecursor.hpp"
23 #include "supertux/resources.hpp"
24 #include "supertux/colorscheme.hpp"
25 #include "video/drawing_context.hpp"
26 #include "video/renderer.hpp"
27 #include "video/video_system.hpp"
28
29 Dialog::Dialog() :
30   m_text(),
31   m_buttons(),
32   m_selected_button(),
33   m_cancel_button(-1),
34   m_text_size()
35 {
36 }
37
38 Dialog::~Dialog()
39 {
40 }
41
42 void
43 Dialog::set_text(const std::string& text)
44 {
45   m_text = text;
46
47   m_text_size = Sizef(Resources::normal_font->get_text_width(m_text),
48                       Resources::normal_font->get_text_height(m_text));
49
50 }
51
52 void
53 Dialog::clear_buttons()
54 {
55   m_buttons.clear();
56   m_selected_button = 0;
57   m_cancel_button = -1;
58 }
59
60 void
61 Dialog::add_default_button(const std::string& text, const std::function<void ()>& callback)
62 {
63   add_button(text, callback);
64   m_selected_button = m_buttons.size() - 1;
65 }
66
67 void
68 Dialog::add_cancel_button(const std::string& text, const std::function<void ()>& callback)
69 {
70   add_button(text, callback);
71   m_cancel_button = m_buttons.size() - 1;
72 }
73
74 void
75 Dialog::add_button(const std::string& text, const std::function<void ()>& callback)
76 {
77   m_buttons.push_back({text, callback});
78 }
79
80 int
81 Dialog::get_button_at(const Vector& mouse_pos) const
82 {
83   Rectf bg_rect(Vector(SCREEN_WIDTH/2 - m_text_size.width/2,
84                        SCREEN_HEIGHT/2 - m_text_size.height/2),
85                 Sizef(m_text_size.width,
86                       m_text_size.height + 44));
87
88   for(int i = 0; i < static_cast<int>(m_buttons.size()); ++i)
89   {
90     float segment_width = bg_rect.get_width() / m_buttons.size();
91     float button_width = segment_width;
92     float button_height = 24.0f;
93     Vector pos(bg_rect.p1.x + segment_width/2.0f + i * segment_width,
94                bg_rect.p2.y - 12);
95     Rectf button_rect(Vector(pos.x - button_width/2, pos.y - button_height/2),
96                       Vector(pos.x + button_width/2, pos.y + button_height/2));
97     if (button_rect.contains(mouse_pos))
98     {
99       return i;
100     }
101   }
102   return -1;
103 }
104
105 void
106 Dialog::event(const SDL_Event& ev)
107 {
108   switch(ev.type) {
109     case SDL_MOUSEBUTTONDOWN:
110     if(ev.button.button == SDL_BUTTON_LEFT)
111     {
112       Vector mouse_pos = VideoSystem::current()->get_renderer().to_logical(ev.motion.x, ev.motion.y);
113       int new_button = get_button_at(mouse_pos);
114       if (new_button != -1)
115       {
116         m_selected_button = new_button;
117         on_button_click(m_selected_button);
118       }
119     }
120     break;
121
122     case SDL_MOUSEMOTION:
123     {
124       Vector mouse_pos = VideoSystem::current()->get_renderer().to_logical(ev.motion.x, ev.motion.y);
125       int new_button = get_button_at(mouse_pos);
126       if (new_button != -1)
127       {
128         m_selected_button = new_button;
129         if(MouseCursor::current())
130           MouseCursor::current()->set_state(MC_LINK);
131       }
132       else
133       {
134         if(MouseCursor::current())
135           MouseCursor::current()->set_state(MC_NORMAL);
136       }
137     }
138     break;
139
140     default:
141       break;
142   }
143 }
144
145 void
146 Dialog::process_input(const Controller& controller)
147 {
148   if (controller.pressed(Controller::LEFT))
149   {
150     m_selected_button -= 1;
151     m_selected_button = std::max(m_selected_button, 0);
152   }
153
154   if (controller.pressed(Controller::RIGHT))
155   {
156     m_selected_button += 1;
157     m_selected_button = std::min(m_selected_button, static_cast<int>(m_buttons.size()) - 1);
158   }
159
160   if (controller.pressed(Controller::ACTION) ||
161       controller.pressed(Controller::MENU_SELECT))
162   {
163     on_button_click(m_selected_button);
164   }
165
166   if (m_cancel_button != -1 &&
167       (controller.pressed(Controller::ESCAPE) ||
168        controller.pressed(Controller::MENU_BACK)))
169   {
170     on_button_click(m_cancel_button);
171   }
172 }
173
174 void
175 Dialog::draw(DrawingContext& ctx)
176 {
177   Rectf bg_rect(Vector(SCREEN_WIDTH/2 - m_text_size.width/2,
178                        SCREEN_HEIGHT/2 - m_text_size.height/2),
179                 Sizef(m_text_size.width,
180                       m_text_size.height + 44));
181
182   // draw background rect
183   ctx.draw_filled_rect(bg_rect.grown(12.0f),
184                        Color(0.2f, 0.3f, 0.4f, 0.8f),
185                        16.0f,
186                        LAYER_GUI-10);
187
188   ctx.draw_filled_rect(bg_rect.grown(8.0f),
189                        Color(0.6f, 0.7f, 0.8f, 0.5f),
190                        16.0f,
191                        LAYER_GUI-10);
192
193   // draw text
194   ctx.draw_text(Resources::normal_font, m_text,
195                 Vector(bg_rect.p1.x + bg_rect.get_width()/2.0f,
196                        bg_rect.p1.y),
197                 ALIGN_CENTER, LAYER_GUI);
198
199   // draw HL line
200   ctx.draw_filled_rect(Vector(bg_rect.p1.x, bg_rect.p2.y - 35),
201                        Vector(bg_rect.get_width(), 4),
202                        Color(0.6f, 0.7f, 1.0f, 1.0f), LAYER_GUI);
203   ctx.draw_filled_rect(Vector(bg_rect.p1.x, bg_rect.p2.y - 35),
204                        Vector(bg_rect.get_width(), 2),
205                        Color(1.0f, 1.0f, 1.0f, 1.0f), LAYER_GUI);
206
207   // draw buttons
208   for(int i = 0; i < static_cast<int>(m_buttons.size()); ++i)
209   {
210     float segment_width = bg_rect.get_width() / m_buttons.size();
211     float button_width = segment_width;
212     float button_height = 24.0f;
213     Vector pos(bg_rect.p1.x + segment_width/2.0f + i * segment_width,
214                bg_rect.p2.y - 12);
215
216     if (i == m_selected_button)
217     {
218       float blink = (sinf(real_time * M_PI * 1.0f)/2.0f + 0.5f) * 0.5f + 0.25f;
219       ctx.draw_filled_rect(Rectf(Vector(pos.x - button_width/2, pos.y - button_height/2),
220                                  Vector(pos.x + button_width/2, pos.y + button_height/2)).grown(2.0f),
221                            Color(1.0f, 1.0f, 1.0f, blink),
222                            14.0f,
223                            LAYER_GUI-10);
224       ctx.draw_filled_rect(Rectf(Vector(pos.x - button_width/2, pos.y - button_height/2),
225                                  Vector(pos.x + button_width/2, pos.y + button_height/2)),
226                            Color(1.0f, 1.0f, 1.0f, 0.5f),
227                            12.0f,
228                            LAYER_GUI-10);
229     }
230
231     ctx.draw_text(Resources::normal_font, m_buttons[i].text,
232                   Vector(pos.x, pos.y - int(Resources::normal_font->get_height()/2)),
233                   ALIGN_CENTER, LAYER_GUI,
234                   i == m_selected_button ? ColorScheme::Menu::active_color : ColorScheme::Menu::default_color);
235   }
236 }
237
238 void
239 Dialog::on_button_click(int button) const
240 {
241   if (m_buttons[button].callback)
242   {
243     m_buttons[button].callback();
244   }
245   MenuManager::instance().set_dialog({});
246 }
247
248 /* EOF */