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/toolbox_widget.hpp"
18
19#include "editor/editor.hpp"
20#include "editor/object_info.hpp"
21#include "editor/tile_selection.hpp"
22#include "editor/tool_icon.hpp"
23#include "editor/util.hpp"
24#include "gui/menu_manager.hpp"
25#include "gui/mousecursor.hpp"
26#include "supertux/colorscheme.hpp"
27#include "supertux/console.hpp"
28#include "supertux/gameconfig.hpp"
29#include "supertux/globals.hpp"
30#include "supertux/level.hpp"
31#include "supertux/menu/menu_storage.hpp"
32#include "supertux/resources.hpp"
33#include "util/gettext.hpp"
34#include "video/drawing_context.hpp"
35#include "video/renderer.hpp"
36#include "video/video_system.hpp"
37#include "video/viewport.hpp"
38
39EditorToolboxWidget::EditorToolboxWidget(Editor& editor) :
40 m_editor(editor),
41 m_tiles(new TileSelection()),
42 m_object(),
43 m_input_type(InputType::NONE),
44 m_active_tilegroup(),
45 m_active_objectgroup(-1),
46 m_object_info(new ObjectInfo()),
47 m_rubber(new ToolIcon("images/engine/editor/rubber.png")),
48 m_select_mode(new ToolIcon("images/engine/editor/select-mode0.png")),
49 m_move_mode(new ToolIcon("images/engine/editor/move-mode0.png")),
50 m_undo_mode(new ToolIcon("images/engine/editor/arrow.png")),
51 m_hovered_item(HoveredItem::NONE),
52 m_hovered_tile(-1),
53 m_tile_scrolling(TileScrolling::NONE),
54 m_using_scroll_wheel(false),
55 m_wheel_scroll_amount(0),
56 m_starting_tile(0),
57 m_dragging(false),
58 m_drag_start(0, 0),
59 m_Xpos(512)
60{
61 m_select_mode->push_mode("images/engine/editor/select-mode1.png");
62 m_select_mode->push_mode("images/engine/editor/select-mode2.png");
63 m_move_mode->push_mode("images/engine/editor/move-mode1.png");
64 m_undo_mode->push_mode("images/engine/editor/redo.png");
65 //settings_mode->push_mode("images/engine/editor/settings-mode1.png");
66}
67
68void
69EditorToolboxWidget::draw(DrawingContext& context)
70{
71 //SCREEN_WIDTH SCREEN_HEIGHT
72 context.color().draw_filled_rect(Rectf(Vector(static_cast<float>(m_Xpos), 0),
73 Vector(static_cast<float>(context.get_width()),
74 static_cast<float>(context.get_height()))),
75 Color(0.9f, 0.9f, 1.0f, 0.6f),
76 0.0f, LAYER_GUI-10);
77 if (m_dragging) {
78 context.color().draw_filled_rect(selection_draw_rect(), Color(0.2f, 0.4f, 1.0f, 0.6f),
79 0.0f, LAYER_GUI+1);
80 }
81
82 if (m_hovered_item != HoveredItem::NONE)
83 {
84 context.color().draw_filled_rect(get_item_rect(m_hovered_item),
85 Color(0.9f, 0.9f, 1.0f, 0.6f),
86 0.0f, LAYER_GUI - 5);
87 }
88
89 context.color().draw_text(Resources::normal_font, _("Tiles"),
90 Vector(static_cast<float>(context.get_width()), 5),
91 ALIGN_RIGHT, LAYER_GUI, ColorScheme::Menu::default_color);
92 context.color().draw_text(Resources::normal_font, _("Objects"),
93 Vector(static_cast<float>(context.get_width()), 37),
94 ALIGN_RIGHT, LAYER_GUI, ColorScheme::Menu::default_color);
95
96 m_rubber->draw(context);
97 m_select_mode->draw(context);
98 m_move_mode->draw(context);
99 m_undo_mode->draw(context);
100
101 draw_tilegroup(context);
102 draw_objectgroup(context);
103}
104
105void
106EditorToolboxWidget::draw_tilegroup(DrawingContext& context)
107{
108 if (m_input_type == InputType::TILE) {
109 int pos = -1;
110 for (auto& tile_ID : m_active_tilegroup->tiles) {
111 pos++;
112 if (pos < m_starting_tile) {
113 continue;
114 }
115 auto position = get_tile_coords(pos - m_starting_tile);
116 draw_tile(context.color(), *m_editor.get_tileset(), tile_ID, position, LAYER_GUI - 9);
117
118 if (g_config->developer_mode && m_active_tilegroup->developers_group)
119 {
120 // Display tile ID on top of tile:
121 context.color().draw_text(Resources::console_font, std::to_string(tile_ID),
122 position + Vector(16, 16), ALIGN_CENTER, LAYER_GUI - 9, Color::WHITE);
123 }
124 /*if (tile_ID == 0) {
125 continue;
126 }
127 const Tile* tg_tile = m_editor.get_tileset()->get(tile_ID);
128 tg_tile->draw(context.color(), get_tile_coords(pos - starting_tile), LAYER_GUI-9);*/
129 }
130 }
131}
132
133void
134EditorToolboxWidget::draw_objectgroup(DrawingContext& context)
135{
136 if (m_input_type == InputType::OBJECT) {
137 int pos = -1;
138 for (auto& icon : m_object_info->m_groups[m_active_objectgroup].get_icons()) {
139 pos++;
140 if (pos < m_starting_tile) {
141 continue;
142 }
143 icon.draw(context, get_tile_coords(pos - m_starting_tile));
144 }
145 }
146}
147
148void
149EditorToolboxWidget::update(float dt_sec)
150{
151 switch (m_tile_scrolling)
152 {
153 case TileScrolling::UP:
154 {
155 if (m_starting_tile > 0)
156 {
157 if (m_using_scroll_wheel)
158 {
159 m_starting_tile -= 4 * m_wheel_scroll_amount;
160 if (m_starting_tile < 0)
161 {
162 m_starting_tile = 0;
163 }
164 m_tile_scrolling = TileScrolling::NONE;
165 }
166 else
167 {
168 m_starting_tile -= 4;
169 }
170 }
171 }
172 break;
173
174 case TileScrolling::DOWN:
175 {
176 int size;
177 if (m_input_type == InputType::OBJECT) {
178 size = static_cast<int>(m_object_info->m_groups[m_active_objectgroup].get_icons().size());
179 } else {
180 if (m_active_tilegroup == nullptr)
181 {
182 return;
183 }
184 size = static_cast<int>(m_active_tilegroup->tiles.size());
185 }
186 if (m_starting_tile < size-5) {
187 if (m_using_scroll_wheel)
188 {
189 m_starting_tile -= 4 * m_wheel_scroll_amount;
190 if (m_starting_tile > size - 4)
191 {
192 m_starting_tile = size - 4;
193 }
194 m_tile_scrolling = TileScrolling::NONE;
195 }
196 else
197 {
198 m_starting_tile += 4;
199 }
200 }
201 }
202 break;
203
204 default:
205 break;
206 }
207}
208
209Rectf
210EditorToolboxWidget::normalize_selection() const
211{
212 Vector drag_start_ = m_drag_start;
213 Vector drag_end = Vector(static_cast<float>(m_hovered_tile % 4),
214 static_cast<float>(m_hovered_tile / 4)); // NOLINT
215 if (drag_start_.x > drag_end.x) {
216 std::swap(drag_start_.x, drag_end.x);
217 }
218 if (drag_start_.y > drag_end.y) {
219 std::swap(drag_start_.y, drag_end.y);
220 }
221 return Rectf(drag_start_, drag_end);
222}
223
224Rectf
225EditorToolboxWidget::selection_draw_rect() const
226{
227 Rectf select = normalize_selection();
228 select.set_p2(select.p2() + Vector(1, 1));
229 select.set_p1((select.p1() * 32.0f) + Vector(static_cast<float>(m_Xpos), static_cast<float>(m_Ypos)));
230 select.set_p2((select.p2() * 32.0f) + Vector(static_cast<float>(m_Xpos), static_cast<float>(m_Ypos)));
231 return select;
232}
233
234void
235EditorToolboxWidget::update_selection()
236{
237 Rectf select = normalize_selection();
238 m_tiles->m_tiles.clear();
239 m_tiles->m_width = static_cast<int>(select.get_width() + 1);
240 m_tiles->m_height = static_cast<int>(select.get_height() + 1);
241
242 int size = static_cast<int>(m_active_tilegroup->tiles.size());
243 for (int y = static_cast<int>(select.get_top()); y <= static_cast<int>(select.get_bottom()); y++) {
244 for (int x = static_cast<int>(select.get_left()); x <= static_cast<int>(select.get_right()); x++) {
245 int tile_pos = y*4 + x + m_starting_tile;
246 if (tile_pos < size && tile_pos >= 0) {
247 m_tiles->m_tiles.push_back(m_active_tilegroup->tiles[tile_pos]);
248 } else {
249 m_tiles->m_tiles.push_back(0);
250 }
251 }
252 }
253}
254
255bool
256EditorToolboxWidget::on_mouse_button_up(const SDL_MouseButtonEvent& button)
257{
258 m_dragging = false;
259 return false;
260}
261
262bool
263EditorToolboxWidget::on_mouse_button_down(const SDL_MouseButtonEvent& button)
264{
265 if (button.button == SDL_BUTTON_LEFT)
266 {
267 switch (m_hovered_item)
268 {
269 case HoveredItem::TILEGROUP:
270 if (m_editor.get_tileset()->get_tilegroups().size() > 1)
271 {
272 m_editor.disable_keyboard();
273 MenuManager::instance().push_menu(MenuStorage::EDITOR_TILEGROUP_MENU);
274 }
275 else
276 {
277 m_active_tilegroup.reset(new Tilegroup(m_editor.get_tileset()->get_tilegroups()[0]));
278 m_input_type = EditorToolboxWidget::InputType::TILE;
279 m_starting_tile = 0;
280 update_mouse_icon();
281 }
282 return true;
283
284 case HoveredItem::OBJECTS:
285 if ((m_editor.get_level()->is_worldmap() && m_object_info->get_num_worldmap_groups() > 1) ||
286 (!m_editor.get_level()->is_worldmap() && m_object_info->get_num_level_groups() > 1))
287 {
288 m_editor.disable_keyboard();
289 MenuManager::instance().push_menu(MenuStorage::EDITOR_OBJECTGROUP_MENU);
290 }
291 else
292 {
293 if (m_editor.get_level()->is_worldmap())
294 {
295 m_active_objectgroup = m_object_info->get_first_worldmap_group_index();
296 }
297 else
298 {
299 m_active_objectgroup = 0;
300 }
301 m_input_type = EditorToolboxWidget::InputType::OBJECT;
302 m_starting_tile = 0;
303 update_mouse_icon();
304 }
305 return true;
306
307 case HoveredItem::TILE:
308 switch (m_input_type)
309 {
310 case InputType::TILE:
311 {
312 m_dragging = true;
313 m_drag_start = Vector(static_cast<float>(m_hovered_tile % 4),
314 static_cast<float>(m_hovered_tile / 4)); // NOLINT
315 int size = static_cast<int>(m_active_tilegroup->tiles.size());
316 int tile_pos = m_hovered_tile + m_starting_tile;
317 if (tile_pos < size && tile_pos >= 0) {
318 m_tiles->set_tile(m_active_tilegroup->tiles[tile_pos]);
319 } else {
320 m_tiles->set_tile(0);
321 }
322 }
323 break;
324
325 case InputType::OBJECT:
326 {
327 int size = static_cast<int>(m_object_info->m_groups[m_active_objectgroup].get_icons().size());
328 if (m_hovered_tile < size && m_hovered_tile >= 0) {
329 m_object = m_object_info->m_groups[m_active_objectgroup].get_icons()[m_hovered_tile + m_starting_tile].get_object_class();
330 }
331 update_mouse_icon();
332 }
333 break;
334
335 default:
336 break;
337 }
338 return true;
339
340 case HoveredItem::TOOL:
341 switch (m_hovered_tile)
342 {
343 case 0:
344 m_tiles->set_tile(0);
345 m_object = "";
346 update_mouse_icon();
347 break;
348
349 case 1:
350 m_select_mode->next_mode();
351 update_mouse_icon();
352 break;
353
354 case 2:
355 m_move_mode->next_mode();
356 update_mouse_icon();
357 break;
358
359 case 3:
360 m_object = "#move";
361 update_mouse_icon();
362 break;
363
364 default:
365 break;
366 }
367 return true;
368
369 case HoveredItem::NONE:
370 return false;
371
372 default:
373 return false;
374 }
375 }
376 else
377 {
378 return false;
379 }
380}
381
382bool
383EditorToolboxWidget::on_mouse_motion(const SDL_MouseMotionEvent& motion)
384{
385 Vector mouse_pos = VideoSystem::current()->get_viewport().to_logical(motion.x, motion.y);
386 float x = mouse_pos.x - static_cast<float>(m_Xpos);
387 float y = mouse_pos.y - static_cast<float>(m_Ypos);
388
389 if (x < 0) {
390 m_hovered_item = HoveredItem::NONE;
391 m_tile_scrolling = TileScrolling::NONE;
392 return false;
393 }
394
395 if (y < 0) {
396 if (y < -64) {
397 m_hovered_item = HoveredItem::TILEGROUP;
398 } else if (y < -32) {
399 m_hovered_item = HoveredItem::OBJECTS;
400 } else {
401 m_hovered_item = HoveredItem::TOOL;
402 m_hovered_tile = get_tool_pos(mouse_pos);
403 }
404 m_tile_scrolling = TileScrolling::NONE;
405 return false;
406 } else {
407 m_hovered_item = HoveredItem::TILE;
408 m_hovered_tile = get_tile_pos(mouse_pos);
409 if (m_dragging && m_input_type == InputType::TILE) {
410 update_selection();
411 }
412 }
413
414 if (y < 16) {
415 m_tile_scrolling = TileScrolling::UP;
416 m_using_scroll_wheel = false;
417 } else if (y > static_cast<float>(SCREEN_HEIGHT - 16 - m_Ypos)) {
418 m_tile_scrolling = TileScrolling::DOWN;
419 m_using_scroll_wheel = false;
420 } else {
421 m_tile_scrolling = TileScrolling::NONE;
422 }
423
424 return false;
425}
426
427bool
428EditorToolboxWidget::on_mouse_wheel(const SDL_MouseWheelEvent& wheel)
429{
430 if (m_hovered_item != HoveredItem::NONE)
431 {
432 if (wheel.y > 0) {
433 m_tile_scrolling = TileScrolling::UP;
434 }
435 else {
436 m_tile_scrolling = TileScrolling::DOWN;
437 }
438 m_using_scroll_wheel = true;
439 m_wheel_scroll_amount = wheel.y;
440 }
441 return false;
442}
443
444void
445EditorToolboxWidget::resize()
446{
447 m_Xpos = SCREEN_WIDTH - 128;
448 m_rubber->m_pos = Vector(static_cast<float>(m_Xpos) , 64.0f);
449 m_select_mode->m_pos = Vector(static_cast<float>(m_Xpos) + 32.0f, 64.0f);
450 m_move_mode->m_pos = Vector(static_cast<float>(m_Xpos) + 64.0f, 64.0f);
451 m_undo_mode->m_pos = Vector(static_cast<float>(m_Xpos) + 96.0f, 64.0f);
452}
453
454void
455EditorToolboxWidget::setup()
456{
457 resize();
458 m_tiles->set_tile(0);
459}
460
461void
462EditorToolboxWidget::update_mouse_icon()
463{
464 switch (m_input_type) {
465 case InputType::NONE:
466 MouseCursor::current()->set_icon(nullptr);
467 break;
468 case InputType::OBJECT:
469 if (m_object.empty()) {
470 MouseCursor::current()->set_icon(m_rubber->get_current_surface());
471 } else {
472 MouseCursor::current()->set_icon(m_move_mode->get_current_surface());
473 }
474 break;
475 case InputType::TILE:
476 MouseCursor::current()->set_icon(m_select_mode->get_current_surface());
477 break;
478 default:
479 break;
480 }
481}
482
483Vector
484EditorToolboxWidget::get_tile_coords(const int pos) const
485{
486 int x = pos%4;
487 int y = pos/4;
488 return Vector(static_cast<float>(x * 32 + m_Xpos),
489 static_cast<float>(y * 32 + m_Ypos));
490}
491
492int
493EditorToolboxWidget::get_tile_pos(const Vector& coords) const
494{
495 int x = static_cast<int>((coords.x - static_cast<float>(m_Xpos)) / 32.0f);
496 int y = static_cast<int>((coords.y - static_cast<float>(m_Ypos)) / 32.0f);
497 return y*4 + x;
498}
499
500Vector
501EditorToolboxWidget::get_tool_coords(const int pos) const
502{
503 int x = pos%4;
504 int y = pos/4;
505 return Vector(static_cast<float>(x * 32 + m_Xpos),
506 static_cast<float>(y * 32 + 64));
507}
508
509int
510EditorToolboxWidget::get_tool_pos(const Vector& coords) const
511{
512 int x = static_cast<int>((coords.x - static_cast<float>(m_Xpos)) / 32.0f);
513 int y = static_cast<int>((coords.y - 64.0f) / 32.0f);
514 return y*4 + x;
515}
516
517Rectf
518EditorToolboxWidget::get_item_rect(const HoveredItem& item) const
519{
520 switch (item)
521 {
522 case HoveredItem::TILEGROUP: return Rectf(Vector(static_cast<float>(m_Xpos), 0.0f), Vector(static_cast<float>(SCREEN_WIDTH), 32.0f));
523 case HoveredItem::OBJECTS: return Rectf(Vector(static_cast<float>(m_Xpos), 32.0f), Vector(static_cast<float>(SCREEN_WIDTH), 64.0f));
524 case HoveredItem::TILE:
525 {
526 auto coords = get_tile_coords(m_hovered_tile);
527 return Rectf(coords, coords + Vector(32, 32));
528 }
529 case HoveredItem::TOOL:
530 {
531 auto coords = get_tool_coords(m_hovered_tile);
532 return Rectf(coords, coords + Vector(32, 32));
533 }
534 case HoveredItem::NONE:
535 default:
536 return Rectf();
537 }
538}
539
540int
541EditorToolboxWidget::get_tileselect_select_mode() const
542{
543 return m_select_mode->get_mode();
544}
545
546int
547EditorToolboxWidget::get_tileselect_move_mode() const
548{
549 return m_move_mode->get_mode();
550}
551
552void
553EditorToolboxWidget::select_tilegroup(int id)
554{
555 m_active_tilegroup.reset(new Tilegroup(m_editor.get_tileset()->get_tilegroups()[id]));
556 m_input_type = EditorToolboxWidget::InputType::TILE;
557 m_starting_tile = 0;
558 update_mouse_icon();
559}
560
561void
562EditorToolboxWidget::select_objectgroup(int id)
563{
564 m_active_objectgroup = id;
565 m_input_type = EditorToolboxWidget::InputType::OBJECT;
566 m_starting_tile = 0;
567 update_mouse_icon();
568}
569
570/* EOF */
571