1 //  SuperTux
2 //  Copyright (C) 2015 Hume2 <teratux.mail@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 "editor/layers_widget.hpp"
18 
19 #include "editor/editor.hpp"
20 #include "editor/layer_icon.hpp"
21 #include "editor/object_menu.hpp"
22 #include "editor/tip.hpp"
23 #include "gui/menu_manager.hpp"
24 #include "math/vector.hpp"
25 #include "object/camera.hpp"
26 #include "object/path_gameobject.hpp"
27 #include "object/tilemap.hpp"
28 #include "supertux/colorscheme.hpp"
29 #include "supertux/menu/menu_storage.hpp"
30 #include "supertux/moving_object.hpp"
31 #include "supertux/resources.hpp"
32 #include "supertux/sector.hpp"
33 #include "video/drawing_context.hpp"
34 #include "video/renderer.hpp"
35 #include "video/video_system.hpp"
36 #include "video/viewport.hpp"
37 
EditorLayersWidget(Editor & editor)38 EditorLayersWidget::EditorLayersWidget(Editor& editor) :
39   m_editor(editor),
40   m_layer_icons(),
41   m_selected_tilemap(),
42   m_Ypos(448),
43   m_Width(512),
44   m_scroll(0),
45   m_scroll_speed(0),
46   m_sector_text(),
47   m_sector_text_width(0),
48   m_hovered_item(HoveredItem::NONE),
49   m_hovered_layer(-1),
50   m_object_tip(),
51   m_has_mouse_focus(false)
52 {
53 }
54 
55 void
draw(DrawingContext & context)56 EditorLayersWidget::draw(DrawingContext& context)
57 {
58 
59   if (m_object_tip) {
60     auto position = get_layer_coords(m_hovered_layer);
61     m_object_tip->draw_up(context, position);
62   }
63 
64   context.color().draw_filled_rect(Rectf(Vector(0, static_cast<float>(m_Ypos)),
65                                          Vector(static_cast<float>(m_Width), static_cast<float>(SCREEN_HEIGHT))),
66                                      Color(0.9f, 0.9f, 1.0f, 0.6f),
67                                      0.0f,
68                                      LAYER_GUI-10);
69 
70   Rectf target_rect = Rectf(0, 0, 0, 0);
71   bool draw_rect = true;
72 
73   switch (m_hovered_item)
74   {
75     case HoveredItem::SPAWNPOINTS:
76       target_rect = Rectf(Vector(0, static_cast<float>(m_Ypos)),
77                           Vector(static_cast<float>(m_Xpos), static_cast<float>(SCREEN_HEIGHT)));
78       break;
79 
80     case HoveredItem::SECTOR:
81       target_rect = Rectf(Vector(static_cast<float>(m_Xpos), static_cast<float>(m_Ypos)),
82                           Vector(static_cast<float>(m_sector_text_width + m_Xpos), static_cast<float>(SCREEN_HEIGHT)));
83       break;
84 
85     case HoveredItem::LAYERS:
86       {
87         Vector coords = get_layer_coords(m_hovered_layer);
88         target_rect = Rectf(coords, coords + Vector(32, 32));
89       }
90       break;
91 
92     default:
93       draw_rect = false;
94       break;
95   }
96 
97   if (draw_rect)
98   {
99     context.color().draw_filled_rect(target_rect, Color(0.9f, 0.9f, 1.0f, 0.6f), 0.0f,
100                                        LAYER_GUI-5);
101   }
102 
103   if (!m_editor.is_level_loaded()) {
104     return;
105   }
106 
107   context.color().draw_text(Resources::normal_font, m_sector_text,
108                             Vector(35.0f, static_cast<float>(m_Ypos) + 5.0f),
109                             ALIGN_LEFT, LAYER_GUI, ColorScheme::Menu::default_color);
110 
111   int pos = 0;
112   for (const auto& layer_icon : m_layer_icons) {
113     if (layer_icon->is_valid()) {
114       if (pos * 35 >= m_scroll) {
115         layer_icon->draw(context, get_layer_coords(pos));
116       } else if ((pos + 1) * 35 >= m_scroll) {
117         layer_icon->draw(context, get_layer_coords(pos), 35 - (m_scroll - pos * 35));
118       }
119     }
120     pos++;
121   }
122 }
123 
124 void
update(float dt_sec)125 EditorLayersWidget::update(float dt_sec)
126 {
127   auto it = m_layer_icons.begin();
128   while (it != m_layer_icons.end())
129   {
130     auto layer_icon = (*it).get();
131     if (!layer_icon->is_valid())
132       it = m_layer_icons.erase(it);
133     else
134       ++it;
135   }
136 
137   if(m_scroll_speed < 0 && m_scroll > 0)
138   {
139     m_scroll -= 5;
140   }
141   else if (m_scroll_speed > 0 && m_scroll < (static_cast<int>(m_layer_icons.size()) - 1) * 35)
142   {
143     m_scroll += 5;
144   }
145 }
146 
147 bool
on_mouse_button_up(const SDL_MouseButtonEvent & button)148 EditorLayersWidget::on_mouse_button_up(const SDL_MouseButtonEvent& button)
149 {
150   return false;
151 }
152 
153 bool
on_mouse_button_down(const SDL_MouseButtonEvent & button)154 EditorLayersWidget::on_mouse_button_down(const SDL_MouseButtonEvent& button)
155 {
156   if (button.button == SDL_BUTTON_LEFT)
157   {
158     switch (m_hovered_item)
159     {
160       case HoveredItem::SECTOR:
161         m_editor.disable_keyboard();
162         MenuManager::instance().set_menu(MenuStorage::EDITOR_SECTORS_MENU);
163         return true;
164 
165       case HoveredItem::LAYERS:
166         if (m_hovered_layer >= m_layer_icons.size())
167         {
168           return false;
169         }
170         else
171         {
172           if (m_layer_icons[m_hovered_layer]->is_tilemap()) {
173             if (m_selected_tilemap) {
174               m_selected_tilemap->m_editor_active = false;
175             }
176             m_selected_tilemap = static_cast<TileMap*>(m_layer_icons[m_hovered_layer]->get_layer());
177             m_selected_tilemap->m_editor_active = true;
178             m_editor.edit_path(m_selected_tilemap->get_path_gameobject(), m_selected_tilemap);
179           } else {
180             auto cam = dynamic_cast<Camera*>(m_layer_icons[m_hovered_layer]->get_layer());
181             if (cam) {
182               m_editor.edit_path(cam->get_path_gameobject(), cam);
183             }
184           }
185           return true;
186         }
187 
188       default:
189         return false;
190     }
191   }
192   else if (button.button == SDL_BUTTON_RIGHT)
193   {
194     if (m_hovered_item == HoveredItem::LAYERS && m_hovered_layer < m_layer_icons.size()) {
195       auto om = std::make_unique<ObjectMenu>(m_editor, m_layer_icons[m_hovered_layer]->get_layer());
196       m_editor.m_deactivate_request = true;
197       MenuManager::instance().push_menu(std::move(om));
198       return true;
199     } else {
200       return false;
201     }
202   }
203   else
204   {
205     return false;
206   }
207 }
208 
209 bool
on_mouse_motion(const SDL_MouseMotionEvent & motion)210 EditorLayersWidget::on_mouse_motion(const SDL_MouseMotionEvent& motion)
211 {
212   Vector mouse_pos = VideoSystem::current()->get_viewport().to_logical(motion.x, motion.y);
213   float x = mouse_pos.x - static_cast<float>(m_Xpos);
214   float y = mouse_pos.y - static_cast<float>(m_Ypos);
215   if (y < 0 || x > static_cast<float>(m_Width)) {
216     m_hovered_item = HoveredItem::NONE;
217     m_object_tip = nullptr;
218     m_has_mouse_focus = false;
219     m_scroll_speed = 0;
220     return false;
221   }
222 
223   m_has_mouse_focus = true;
224 
225   if (x < 0) {
226     m_hovered_item = HoveredItem::SPAWNPOINTS;
227     m_object_tip = nullptr;
228     return true;
229   } else {
230     if (x <= static_cast<float>(m_sector_text_width)) {
231       m_hovered_item = HoveredItem::SECTOR;
232       m_object_tip = nullptr;
233     } else {
234       // Scrolling
235       if (x < static_cast<float>(m_sector_text_width + 32)) {
236         m_scroll_speed = -1;
237       } else if (x > static_cast<float>(SCREEN_WIDTH - 160)) { // 160 = 128 + 32
238         m_scroll_speed = 1;
239       } else {
240         m_scroll_speed = 0;
241       }
242       unsigned int new_hovered_layer = get_layer_pos(mouse_pos);
243       if (m_hovered_layer != new_hovered_layer || m_hovered_item != HoveredItem::LAYERS) {
244         m_hovered_layer = new_hovered_layer;
245         update_tip();
246       }
247       m_hovered_item = HoveredItem::LAYERS;
248     }
249   }
250 
251   return true;
252 }
253 
254 
255 bool
on_mouse_wheel(const SDL_MouseWheelEvent & wheel)256 EditorLayersWidget::on_mouse_wheel(const SDL_MouseWheelEvent& wheel)
257 {
258   if (m_has_mouse_focus)
259   {
260     if((wheel.x < 0 || wheel.y < 0) && !(wheel.x > 0 || wheel.y > 0))
261     {
262       if (m_scroll >= 16)
263       {
264         m_scroll -= 16;
265       }
266       else
267       {
268         m_scroll = 0;
269       }
270     }
271     else if ((wheel.x > 0 || wheel.y > 0) && !(wheel.x < 0 || wheel.y < 0))
272     {
273       if (m_scroll < (static_cast<int>(m_layer_icons.size()) - 1) * 35)
274       {
275         m_scroll += 16;
276       }
277       else
278       {
279         m_scroll = (static_cast<int>(m_layer_icons.size()) - 1) * 35;
280       }
281 
282     }
283   }
284   return false;
285 }
286 
287 bool
has_mouse_focus() const288 EditorLayersWidget::has_mouse_focus() const
289 {
290   return m_has_mouse_focus;
291 }
292 
293 void
resize()294 EditorLayersWidget::resize()
295 {
296   m_Ypos = SCREEN_HEIGHT - 32;
297   m_Width = SCREEN_WIDTH - 128;
298 }
299 
300 void
setup()301 EditorLayersWidget::setup()
302 {
303   resize();
304 }
305 
306 void
refresh()307 EditorLayersWidget::refresh()
308 {
309   m_selected_tilemap = nullptr;
310   m_layer_icons.clear();
311 
312   bool tsel = false;
313   for (auto& i : m_editor.get_sector()->get_objects())
314   {
315     auto* go = i.get();
316     auto* mo = dynamic_cast<MovingObject*>(go);
317     if (!mo && go->has_settings()) {
318       if (!dynamic_cast<PathGameObject*>(go)) {
319         add_layer(go);
320       }
321 
322       auto tm = dynamic_cast<TileMap*>(go);
323       if (tm) {
324         if ( !tm->is_solid() || tsel ) {
325           tm->m_editor_active = false;
326         } else {
327           m_selected_tilemap = tm;
328           tm->m_editor_active = true;
329           tsel = true;
330         }
331       }
332     }
333   }
334 
335   sort_layers();
336   refresh_sector_text();
337 }
338 
339 void
refresh_sector_text()340 EditorLayersWidget::refresh_sector_text()
341 {
342   m_sector_text = _("Sector") + ": " + m_editor.get_sector()->get_name();
343   m_sector_text_width  = int(Resources::normal_font->get_text_width(m_sector_text)) + 6;
344 }
345 
346 void
sort_layers()347 EditorLayersWidget::sort_layers()
348 {
349   std::sort(m_layer_icons.begin(), m_layer_icons.end(),
350             [](const std::unique_ptr<LayerIcon>& lhs, const std::unique_ptr<LayerIcon>& rhs) {
351               return lhs->get_zpos() < rhs->get_zpos();
352             });
353 }
354 
355 void
add_layer(GameObject * layer)356 EditorLayersWidget::add_layer(GameObject* layer)
357 {
358   auto icon = std::make_unique<LayerIcon>(layer);
359   int z_pos = icon->get_zpos();
360 
361   // The icon is inserted to the correct position.
362   for (auto i = m_layer_icons.begin(); i != m_layer_icons.end(); ++i) {
363     const auto& li = i->get();
364     if (li->get_zpos() < z_pos) {
365       m_layer_icons.insert(i, move(icon));
366       return;
367     }
368   }
369 
370   m_layer_icons.push_back(move(icon));
371 }
372 
373 void
update_tip()374 EditorLayersWidget::update_tip()
375 {
376   if ( m_hovered_layer >= m_layer_icons.size() ) {
377     m_object_tip = nullptr;
378     return;
379   }
380   m_object_tip = std::make_unique<Tip>(*m_layer_icons[m_hovered_layer]->get_layer());
381 }
382 
383 Vector
get_layer_coords(const int pos) const384 EditorLayersWidget::get_layer_coords(const int pos) const
385 {
386   return Vector(static_cast<float>(pos * 35 + m_Xpos + m_sector_text_width - m_scroll),
387                 static_cast<float>(m_Ypos));
388 }
389 
390 int
get_layer_pos(const Vector & coords) const391 EditorLayersWidget::get_layer_pos(const Vector& coords) const
392 {
393   return static_cast<int>((coords.x - static_cast<float>(m_Xpos - m_scroll) - static_cast<float>(m_sector_text_width)) / 35.0f);
394 }
395 
396 /* EOF */
397