tile_set_editor.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. /**************************************************************************/
  2. /* tile_set_editor.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "tile_set_editor.h"
  31. #include "tile_data_editors.h"
  32. #include "tiles_editor_plugin.h"
  33. #include "editor/editor_file_system.h"
  34. #include "editor/editor_node.h"
  35. #include "editor/editor_scale.h"
  36. #include "editor/editor_settings.h"
  37. #include "editor/editor_undo_redo_manager.h"
  38. #include "scene/gui/box_container.h"
  39. #include "scene/gui/control.h"
  40. #include "scene/gui/tab_container.h"
  41. TileSetEditor *TileSetEditor::singleton = nullptr;
  42. void TileSetEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
  43. ERR_FAIL_COND(!tile_set.is_valid());
  44. if (!_can_drop_data_fw(p_point, p_data, p_from)) {
  45. return;
  46. }
  47. if (p_from == sources_list) {
  48. // Handle dropping a texture in the list of atlas resources.
  49. int source_id = TileSet::INVALID_SOURCE;
  50. int added = 0;
  51. Dictionary d = p_data;
  52. Vector<String> files = d["files"];
  53. for (int i = 0; i < files.size(); i++) {
  54. Ref<Texture2D> resource = ResourceLoader::load(files[i]);
  55. if (resource.is_valid()) {
  56. // Retrieve the id for the next created source.
  57. source_id = tile_set->get_next_source_id();
  58. // Actually create the new source.
  59. Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
  60. atlas_source->set_texture(resource);
  61. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  62. undo_redo->create_action(TTR("Add a new atlas source"));
  63. undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
  64. undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
  65. undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
  66. undo_redo->commit_action();
  67. added += 1;
  68. }
  69. }
  70. if (added == 1) {
  71. tile_set_atlas_source_editor->init_source();
  72. }
  73. // Update the selected source (thus triggering an update).
  74. _update_sources_list(source_id);
  75. }
  76. }
  77. bool TileSetEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
  78. ERR_FAIL_COND_V(!tile_set.is_valid(), false);
  79. if (read_only) {
  80. return false;
  81. }
  82. if (p_from == sources_list) {
  83. Dictionary d = p_data;
  84. if (!d.has("type")) {
  85. return false;
  86. }
  87. // Check if we have a Texture2D.
  88. if (String(d["type"]) == "files") {
  89. Vector<String> files = d["files"];
  90. if (files.size() == 0) {
  91. return false;
  92. }
  93. for (int i = 0; i < files.size(); i++) {
  94. String file = files[i];
  95. String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
  96. if (!ClassDB::is_parent_class(ftype, "Texture2D")) {
  97. return false;
  98. }
  99. }
  100. return true;
  101. }
  102. }
  103. return false;
  104. }
  105. void TileSetEditor::_update_sources_list(int force_selected_id) {
  106. if (tile_set.is_null()) {
  107. return;
  108. }
  109. // Get the previously selected id.
  110. int old_selected = TileSet::INVALID_SOURCE;
  111. if (sources_list->get_current() >= 0) {
  112. int source_id = sources_list->get_item_metadata(sources_list->get_current());
  113. if (tile_set->has_source(source_id)) {
  114. old_selected = source_id;
  115. }
  116. }
  117. int to_select = TileSet::INVALID_SOURCE;
  118. if (force_selected_id >= 0) {
  119. to_select = force_selected_id;
  120. } else if (old_selected >= 0) {
  121. to_select = old_selected;
  122. }
  123. // Clear the list.
  124. sources_list->clear();
  125. // Update the atlas sources.
  126. List<int> source_ids = TilesEditorPlugin::get_singleton()->get_sorted_sources(tile_set);
  127. for (const int &source_id : source_ids) {
  128. TileSetSource *source = *tile_set->get_source(source_id);
  129. Ref<Texture2D> texture;
  130. String item_text;
  131. // Common to all type of sources.
  132. if (!source->get_name().is_empty()) {
  133. item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id);
  134. }
  135. // Atlas source.
  136. TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
  137. if (atlas_source) {
  138. texture = atlas_source->get_texture();
  139. if (item_text.is_empty()) {
  140. if (texture.is_valid()) {
  141. item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
  142. } else {
  143. item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
  144. }
  145. }
  146. }
  147. // Scene collection source.
  148. TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
  149. if (scene_collection_source) {
  150. texture = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
  151. if (item_text.is_empty()) {
  152. item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
  153. }
  154. }
  155. // Use default if not valid.
  156. if (item_text.is_empty()) {
  157. item_text = vformat(TTR("Unknown Type Source (ID: %d)"), source_id);
  158. }
  159. if (!texture.is_valid()) {
  160. texture = missing_texture_texture;
  161. }
  162. sources_list->add_item(item_text, texture);
  163. sources_list->set_item_metadata(-1, source_id);
  164. }
  165. // Set again the current selected item if needed.
  166. if (to_select >= 0) {
  167. for (int i = 0; i < sources_list->get_item_count(); i++) {
  168. if ((int)sources_list->get_item_metadata(i) == to_select) {
  169. sources_list->set_current(i);
  170. sources_list->ensure_current_is_visible();
  171. if (old_selected != to_select) {
  172. sources_list->emit_signal(SNAME("item_selected"), sources_list->get_current());
  173. }
  174. break;
  175. }
  176. }
  177. }
  178. // If nothing is selected, select the first entry.
  179. if (sources_list->get_current() < 0 && sources_list->get_item_count() > 0) {
  180. sources_list->set_current(0);
  181. if (old_selected != int(sources_list->get_item_metadata(0))) {
  182. sources_list->emit_signal(SNAME("item_selected"), sources_list->get_current());
  183. }
  184. }
  185. // If there is no source left, hide all editors and show the label.
  186. _source_selected(sources_list->get_current());
  187. // Synchronize the lists.
  188. TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
  189. }
  190. void TileSetEditor::_source_selected(int p_source_index) {
  191. ERR_FAIL_COND(!tile_set.is_valid());
  192. // Update the selected source.
  193. sources_delete_button->set_disabled(p_source_index < 0 || read_only);
  194. if (p_source_index >= 0) {
  195. int source_id = sources_list->get_item_metadata(p_source_index);
  196. TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(source_id));
  197. TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(*tile_set->get_source(source_id));
  198. if (atlas_source) {
  199. no_source_selected_label->hide();
  200. tile_set_atlas_source_editor->edit(*tile_set, atlas_source, source_id);
  201. tile_set_atlas_source_editor->show();
  202. tile_set_scenes_collection_source_editor->hide();
  203. } else if (scenes_collection_source) {
  204. no_source_selected_label->hide();
  205. tile_set_atlas_source_editor->hide();
  206. tile_set_scenes_collection_source_editor->edit(*tile_set, scenes_collection_source, source_id);
  207. tile_set_scenes_collection_source_editor->show();
  208. } else {
  209. no_source_selected_label->show();
  210. tile_set_atlas_source_editor->hide();
  211. tile_set_scenes_collection_source_editor->hide();
  212. }
  213. } else {
  214. no_source_selected_label->show();
  215. tile_set_atlas_source_editor->hide();
  216. tile_set_scenes_collection_source_editor->hide();
  217. }
  218. }
  219. void TileSetEditor::_source_delete_pressed() {
  220. ERR_FAIL_COND(!tile_set.is_valid());
  221. // Update the selected source.
  222. int to_delete = sources_list->get_item_metadata(sources_list->get_current());
  223. Ref<TileSetSource> source = tile_set->get_source(to_delete);
  224. // Remove the source.
  225. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  226. undo_redo->create_action(TTR("Remove source"));
  227. undo_redo->add_do_method(*tile_set, "remove_source", to_delete);
  228. undo_redo->add_undo_method(*tile_set, "add_source", source, to_delete);
  229. undo_redo->commit_action();
  230. _update_sources_list();
  231. }
  232. void TileSetEditor::_source_add_id_pressed(int p_id_pressed) {
  233. ERR_FAIL_COND(!tile_set.is_valid());
  234. switch (p_id_pressed) {
  235. case 0: {
  236. int source_id = tile_set->get_next_source_id();
  237. Ref<TileSetAtlasSource> atlas_source = memnew(TileSetAtlasSource);
  238. // Add a new source.
  239. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  240. undo_redo->create_action(TTR("Add atlas source"));
  241. undo_redo->add_do_method(*tile_set, "add_source", atlas_source, source_id);
  242. undo_redo->add_do_method(*atlas_source, "set_texture_region_size", tile_set->get_tile_size());
  243. undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
  244. undo_redo->commit_action();
  245. _update_sources_list(source_id);
  246. } break;
  247. case 1: {
  248. int source_id = tile_set->get_next_source_id();
  249. Ref<TileSetScenesCollectionSource> scene_collection_source = memnew(TileSetScenesCollectionSource);
  250. // Add a new source.
  251. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  252. undo_redo->create_action(TTR("Add atlas source"));
  253. undo_redo->add_do_method(*tile_set, "add_source", scene_collection_source, source_id);
  254. undo_redo->add_undo_method(*tile_set, "remove_source", source_id);
  255. undo_redo->commit_action();
  256. _update_sources_list(source_id);
  257. } break;
  258. default:
  259. ERR_FAIL();
  260. }
  261. }
  262. void TileSetEditor::_sources_advanced_menu_id_pressed(int p_id_pressed) {
  263. ERR_FAIL_COND(!tile_set.is_valid());
  264. switch (p_id_pressed) {
  265. case 0: {
  266. atlas_merging_dialog->update_tile_set(tile_set);
  267. atlas_merging_dialog->popup_centered_ratio(0.5);
  268. } break;
  269. case 1: {
  270. tile_proxies_manager_dialog->update_tile_set(tile_set);
  271. tile_proxies_manager_dialog->popup_centered_ratio(0.5);
  272. } break;
  273. }
  274. }
  275. void TileSetEditor::_set_source_sort(int p_sort) {
  276. TilesEditorPlugin::get_singleton()->set_sorting_option(p_sort);
  277. for (int i = 0; i != TilesEditorPlugin::SOURCE_SORT_MAX; i++) {
  278. source_sort_button->get_popup()->set_item_checked(i, (i == (int)p_sort));
  279. }
  280. int old_selected = TileSet::INVALID_SOURCE;
  281. if (sources_list->get_current() >= 0) {
  282. int source_id = sources_list->get_item_metadata(sources_list->get_current());
  283. if (tile_set->has_source(source_id)) {
  284. old_selected = source_id;
  285. }
  286. }
  287. _update_sources_list(old_selected);
  288. EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "tile_source_sort", p_sort);
  289. }
  290. void TileSetEditor::_notification(int p_what) {
  291. switch (p_what) {
  292. case NOTIFICATION_ENTER_TREE:
  293. case NOTIFICATION_THEME_CHANGED: {
  294. sources_delete_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
  295. sources_add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
  296. source_sort_button->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
  297. sources_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
  298. missing_texture_texture = get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons"));
  299. _update_sources_list();
  300. } break;
  301. case NOTIFICATION_INTERNAL_PROCESS: {
  302. if (tile_set_changed_needs_update) {
  303. if (tile_set.is_valid()) {
  304. tile_set->set_edited(true);
  305. }
  306. read_only = false;
  307. if (tile_set.is_valid()) {
  308. read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set);
  309. }
  310. _update_sources_list();
  311. _update_patterns_list();
  312. sources_add_button->set_disabled(read_only);
  313. sources_advanced_menu_button->set_disabled(read_only);
  314. source_sort_button->set_disabled(read_only);
  315. tile_set_changed_needs_update = false;
  316. }
  317. } break;
  318. }
  319. }
  320. void TileSetEditor::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) {
  321. ERR_FAIL_COND(!tile_set.is_valid());
  322. if (EditorNode::get_singleton()->is_resource_read_only(tile_set)) {
  323. return;
  324. }
  325. if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
  326. Vector<int> selected = patterns_item_list->get_selected_items();
  327. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  328. undo_redo->create_action(TTR("Remove TileSet patterns"));
  329. for (int i = 0; i < selected.size(); i++) {
  330. int pattern_index = selected[i];
  331. undo_redo->add_do_method(*tile_set, "remove_pattern", pattern_index);
  332. undo_redo->add_undo_method(*tile_set, "add_pattern", tile_set->get_pattern(pattern_index), pattern_index);
  333. }
  334. undo_redo->commit_action();
  335. patterns_item_list->accept_event();
  336. }
  337. }
  338. void TileSetEditor::_pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture) {
  339. // TODO optimize ?
  340. for (int i = 0; i < patterns_item_list->get_item_count(); i++) {
  341. if (patterns_item_list->get_item_metadata(i) == p_pattern) {
  342. patterns_item_list->set_item_icon(i, p_texture);
  343. break;
  344. }
  345. }
  346. }
  347. void TileSetEditor::_update_patterns_list() {
  348. ERR_FAIL_COND(!tile_set.is_valid());
  349. // Recreate the items.
  350. patterns_item_list->clear();
  351. for (int i = 0; i < tile_set->get_patterns_count(); i++) {
  352. int id = patterns_item_list->add_item("");
  353. patterns_item_list->set_item_metadata(id, tile_set->get_pattern(i));
  354. TilesEditorPlugin::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileSetEditor::_pattern_preview_done));
  355. }
  356. // Update the label visibility.
  357. patterns_help_label->set_visible(patterns_item_list->get_item_count() == 0);
  358. }
  359. void TileSetEditor::_tile_set_changed() {
  360. tile_set_changed_needs_update = true;
  361. }
  362. void TileSetEditor::_tab_changed(int p_tab_changed) {
  363. split_container->set_visible(p_tab_changed == 0);
  364. patterns_item_list->set_visible(p_tab_changed == 1);
  365. }
  366. void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
  367. EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
  368. ERR_FAIL_NULL(undo_redo_man);
  369. TileSet *ed_tile_set = Object::cast_to<TileSet>(p_edited);
  370. if (!ed_tile_set) {
  371. return;
  372. }
  373. Vector<String> components = String(p_array_prefix).split("/", true, 2);
  374. // Compute the array indices to save.
  375. int begin = 0;
  376. int end;
  377. if (p_array_prefix == "occlusion_layer_") {
  378. end = ed_tile_set->get_occlusion_layers_count();
  379. } else if (p_array_prefix == "physics_layer_") {
  380. end = ed_tile_set->get_physics_layers_count();
  381. } else if (p_array_prefix == "terrain_set_") {
  382. end = ed_tile_set->get_terrain_sets_count();
  383. } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
  384. int terrain_set = components[0].trim_prefix("terrain_set_").to_int();
  385. end = ed_tile_set->get_terrains_count(terrain_set);
  386. } else if (p_array_prefix == "navigation_layer_") {
  387. end = ed_tile_set->get_navigation_layers_count();
  388. } else if (p_array_prefix == "custom_data_layer_") {
  389. end = ed_tile_set->get_custom_data_layers_count();
  390. } else {
  391. ERR_FAIL_MSG("Invalid array prefix for TileSet.");
  392. }
  393. if (p_from_index < 0) {
  394. // Adding new.
  395. if (p_to_pos >= 0) {
  396. begin = p_to_pos;
  397. } else {
  398. end = 0; // Nothing to save when adding at the end.
  399. }
  400. } else if (p_to_pos < 0) {
  401. // Removing.
  402. begin = p_from_index;
  403. } else {
  404. // Moving.
  405. begin = MIN(p_from_index, p_to_pos);
  406. end = MIN(MAX(p_from_index, p_to_pos) + 1, end);
  407. }
  408. #define ADD_UNDO(obj, property) undo_redo_man->add_undo_property(obj, property, obj->get(property));
  409. // Add undo method to adding array element.
  410. if (p_array_prefix == "occlusion_layer_") {
  411. if (p_from_index < 0) {
  412. undo_redo_man->add_undo_method(ed_tile_set, "remove_occlusion_layer", p_to_pos < 0 ? ed_tile_set->get_occlusion_layers_count() : p_to_pos);
  413. }
  414. } else if (p_array_prefix == "physics_layer_") {
  415. if (p_from_index < 0) {
  416. undo_redo_man->add_undo_method(ed_tile_set, "remove_physics_layer", p_to_pos < 0 ? ed_tile_set->get_physics_layers_count() : p_to_pos);
  417. }
  418. } else if (p_array_prefix == "terrain_set_") {
  419. if (p_from_index < 0) {
  420. undo_redo_man->add_undo_method(ed_tile_set, "remove_terrain_set", p_to_pos < 0 ? ed_tile_set->get_terrain_sets_count() : p_to_pos);
  421. }
  422. } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
  423. int terrain_set = components[0].trim_prefix("terrain_set_").to_int();
  424. if (p_from_index < 0) {
  425. undo_redo_man->add_undo_method(ed_tile_set, "remove_terrain", terrain_set, p_to_pos < 0 ? ed_tile_set->get_terrains_count(terrain_set) : p_to_pos);
  426. }
  427. } else if (p_array_prefix == "navigation_layer_") {
  428. if (p_from_index < 0) {
  429. undo_redo_man->add_undo_method(ed_tile_set, "remove_navigation_layer", p_to_pos < 0 ? ed_tile_set->get_navigation_layers_count() : p_to_pos);
  430. }
  431. } else if (p_array_prefix == "custom_data_layer_") {
  432. if (p_from_index < 0) {
  433. undo_redo_man->add_undo_method(ed_tile_set, "remove_custom_data_layer", p_to_pos < 0 ? ed_tile_set->get_custom_data_layers_count() : p_to_pos);
  434. }
  435. }
  436. // Save layers' properties.
  437. List<PropertyInfo> properties;
  438. ed_tile_set->get_property_list(&properties);
  439. for (PropertyInfo pi : properties) {
  440. if (pi.name.begins_with(p_array_prefix)) {
  441. String str = pi.name.trim_prefix(p_array_prefix);
  442. int to_char_index = 0;
  443. while (to_char_index < str.length()) {
  444. if (!is_digit(str[to_char_index])) {
  445. break;
  446. }
  447. to_char_index++;
  448. }
  449. if (to_char_index > 0) {
  450. int array_index = str.left(to_char_index).to_int();
  451. if (array_index >= begin && array_index < end) {
  452. ADD_UNDO(ed_tile_set, pi.name);
  453. }
  454. }
  455. }
  456. }
  457. // Save properties for TileSetAtlasSources tile data
  458. for (int i = 0; i < ed_tile_set->get_source_count(); i++) {
  459. int source_id = ed_tile_set->get_source_id(i);
  460. Ref<TileSetAtlasSource> tas = ed_tile_set->get_source(source_id);
  461. if (tas.is_valid()) {
  462. for (int j = 0; j < tas->get_tiles_count(); j++) {
  463. Vector2i tile_id = tas->get_tile_id(j);
  464. for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
  465. int alternative_id = tas->get_alternative_tile_id(tile_id, k);
  466. TileData *tile_data = tas->get_tile_data(tile_id, alternative_id);
  467. ERR_FAIL_COND(!tile_data);
  468. // Actually saving stuff.
  469. if (p_array_prefix == "occlusion_layer_") {
  470. for (int layer_index = begin; layer_index < end; layer_index++) {
  471. ADD_UNDO(tile_data, vformat("occlusion_layer_%d/polygon", layer_index));
  472. }
  473. } else if (p_array_prefix == "physics_layer_") {
  474. for (int layer_index = begin; layer_index < end; layer_index++) {
  475. ADD_UNDO(tile_data, vformat("physics_layer_%d/polygons_count", layer_index));
  476. for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(layer_index); polygon_index++) {
  477. ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, polygon_index));
  478. ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, polygon_index));
  479. ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, polygon_index));
  480. }
  481. }
  482. } else if (p_array_prefix == "terrain_set_") {
  483. ADD_UNDO(tile_data, "terrain_set");
  484. for (int terrain_set_index = begin; terrain_set_index < end; terrain_set_index++) {
  485. for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
  486. TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
  487. if (tile_data->is_valid_terrain_peering_bit(bit)) {
  488. ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
  489. }
  490. }
  491. }
  492. } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
  493. for (int terrain_index = 0; terrain_index < TileSet::CELL_NEIGHBOR_MAX; terrain_index++) {
  494. TileSet::CellNeighbor bit = TileSet::CellNeighbor(terrain_index);
  495. if (tile_data->is_valid_terrain_peering_bit(bit)) {
  496. ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[terrain_index]));
  497. }
  498. }
  499. } else if (p_array_prefix == "navigation_layer_") {
  500. for (int layer_index = begin; layer_index < end; layer_index++) {
  501. ADD_UNDO(tile_data, vformat("navigation_layer_%d/polygon", layer_index));
  502. }
  503. } else if (p_array_prefix == "custom_data_layer_") {
  504. for (int layer_index = begin; layer_index < end; layer_index++) {
  505. ADD_UNDO(tile_data, vformat("custom_data_%d", layer_index));
  506. }
  507. }
  508. }
  509. }
  510. }
  511. }
  512. #undef ADD_UNDO
  513. // Add do method to add/remove array element.
  514. if (p_array_prefix == "occlusion_layer_") {
  515. if (p_from_index < 0) {
  516. undo_redo_man->add_do_method(ed_tile_set, "add_occlusion_layer", p_to_pos);
  517. } else if (p_to_pos < 0) {
  518. undo_redo_man->add_do_method(ed_tile_set, "remove_occlusion_layer", p_from_index);
  519. } else {
  520. undo_redo_man->add_do_method(ed_tile_set, "move_occlusion_layer", p_from_index, p_to_pos);
  521. }
  522. } else if (p_array_prefix == "physics_layer_") {
  523. if (p_from_index < 0) {
  524. undo_redo_man->add_do_method(ed_tile_set, "add_physics_layer", p_to_pos);
  525. } else if (p_to_pos < 0) {
  526. undo_redo_man->add_do_method(ed_tile_set, "remove_physics_layer", p_from_index);
  527. } else {
  528. undo_redo_man->add_do_method(ed_tile_set, "move_physics_layer", p_from_index, p_to_pos);
  529. }
  530. } else if (p_array_prefix == "terrain_set_") {
  531. if (p_from_index < 0) {
  532. undo_redo_man->add_do_method(ed_tile_set, "add_terrain_set", p_to_pos);
  533. } else if (p_to_pos < 0) {
  534. undo_redo_man->add_do_method(ed_tile_set, "remove_terrain_set", p_from_index);
  535. } else {
  536. undo_redo_man->add_do_method(ed_tile_set, "move_terrain_set", p_from_index, p_to_pos);
  537. }
  538. } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
  539. int terrain_set = components[0].trim_prefix("terrain_set_").to_int();
  540. if (p_from_index < 0) {
  541. undo_redo_man->add_do_method(ed_tile_set, "add_terrain", terrain_set, p_to_pos);
  542. } else if (p_to_pos < 0) {
  543. undo_redo_man->add_do_method(ed_tile_set, "remove_terrain", terrain_set, p_from_index);
  544. } else {
  545. undo_redo_man->add_do_method(ed_tile_set, "move_terrain", terrain_set, p_from_index, p_to_pos);
  546. }
  547. } else if (p_array_prefix == "navigation_layer_") {
  548. if (p_from_index < 0) {
  549. undo_redo_man->add_do_method(ed_tile_set, "add_navigation_layer", p_to_pos);
  550. } else if (p_to_pos < 0) {
  551. undo_redo_man->add_do_method(ed_tile_set, "remove_navigation_layer", p_from_index);
  552. } else {
  553. undo_redo_man->add_do_method(ed_tile_set, "move_navigation_layer", p_from_index, p_to_pos);
  554. }
  555. } else if (p_array_prefix == "custom_data_layer_") {
  556. if (p_from_index < 0) {
  557. undo_redo_man->add_do_method(ed_tile_set, "add_custom_data_layer", p_to_pos);
  558. } else if (p_to_pos < 0) {
  559. undo_redo_man->add_do_method(ed_tile_set, "remove_custom_data_layer", p_from_index);
  560. } else {
  561. undo_redo_man->add_do_method(ed_tile_set, "move_custom_data_layer", p_from_index, p_to_pos);
  562. }
  563. }
  564. }
  565. void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
  566. EditorUndoRedoManager *undo_redo_man = Object::cast_to<EditorUndoRedoManager>(p_undo_redo);
  567. ERR_FAIL_NULL(undo_redo_man);
  568. #define ADD_UNDO(obj, property) undo_redo_man->add_undo_property(obj, property, obj->get(property));
  569. TileSet *ed_tile_set = Object::cast_to<TileSet>(p_edited);
  570. if (ed_tile_set) {
  571. Vector<String> components = p_property.split("/", true, 3);
  572. for (int i = 0; i < ed_tile_set->get_source_count(); i++) {
  573. int source_id = ed_tile_set->get_source_id(i);
  574. Ref<TileSetAtlasSource> tas = ed_tile_set->get_source(source_id);
  575. if (tas.is_valid()) {
  576. for (int j = 0; j < tas->get_tiles_count(); j++) {
  577. Vector2i tile_id = tas->get_tile_id(j);
  578. for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
  579. int alternative_id = tas->get_alternative_tile_id(tile_id, k);
  580. TileData *tile_data = tas->get_tile_data(tile_id, alternative_id);
  581. ERR_FAIL_COND(!tile_data);
  582. if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
  583. ADD_UNDO(tile_data, "terrain_set");
  584. ADD_UNDO(tile_data, "terrain");
  585. for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
  586. TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
  587. if (tile_data->is_valid_terrain_peering_bit(bit)) {
  588. ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
  589. }
  590. }
  591. } else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int() && components[1] == "type") {
  592. int custom_data_layer = components[0].trim_prefix("custom_data_layer_").is_valid_int();
  593. ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer));
  594. }
  595. }
  596. }
  597. }
  598. }
  599. }
  600. #undef ADD_UNDO
  601. }
  602. void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
  603. bool new_read_only_state = false;
  604. if (p_tile_set.is_valid()) {
  605. new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set);
  606. }
  607. if (p_tile_set == tile_set && new_read_only_state == read_only) {
  608. return;
  609. }
  610. // Remove listener.
  611. if (tile_set.is_valid()) {
  612. tile_set->disconnect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
  613. }
  614. // Change the edited object.
  615. tile_set = p_tile_set;
  616. // Read-only status is false by default
  617. read_only = new_read_only_state;
  618. // Add the listener again and check for read-only status.
  619. if (tile_set.is_valid()) {
  620. sources_add_button->set_disabled(read_only);
  621. sources_advanced_menu_button->set_disabled(read_only);
  622. source_sort_button->set_disabled(read_only);
  623. tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
  624. if (first_edit) {
  625. first_edit = false;
  626. _set_source_sort(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "tile_source_sort", 0));
  627. } else {
  628. _update_sources_list();
  629. }
  630. _update_patterns_list();
  631. }
  632. }
  633. TileSetEditor::TileSetEditor() {
  634. singleton = this;
  635. set_process_internal(true);
  636. // TabBar.
  637. tabs_bar = memnew(TabBar);
  638. tabs_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
  639. tabs_bar->set_clip_tabs(false);
  640. tabs_bar->add_tab(TTR("Tiles"));
  641. tabs_bar->add_tab(TTR("Patterns"));
  642. tabs_bar->connect("tab_changed", callable_mp(this, &TileSetEditor::_tab_changed));
  643. tile_set_toolbar = memnew(HBoxContainer);
  644. tile_set_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
  645. tile_set_toolbar->add_child(tabs_bar);
  646. add_child(tile_set_toolbar);
  647. //// Tiles ////
  648. // Split container.
  649. split_container = memnew(HSplitContainer);
  650. split_container->set_name(TTR("Tiles"));
  651. split_container->set_h_size_flags(SIZE_EXPAND_FILL);
  652. split_container->set_v_size_flags(SIZE_EXPAND_FILL);
  653. add_child(split_container);
  654. // Sources list.
  655. VBoxContainer *split_container_left_side = memnew(VBoxContainer);
  656. split_container_left_side->set_h_size_flags(SIZE_EXPAND_FILL);
  657. split_container_left_side->set_v_size_flags(SIZE_EXPAND_FILL);
  658. split_container_left_side->set_stretch_ratio(0.25);
  659. split_container_left_side->set_custom_minimum_size(Size2(70, 0) * EDSCALE);
  660. split_container->add_child(split_container_left_side);
  661. source_sort_button = memnew(MenuButton);
  662. source_sort_button->set_flat(true);
  663. source_sort_button->set_tooltip_text(TTR("Sort Sources"));
  664. PopupMenu *p = source_sort_button->get_popup();
  665. p->connect("id_pressed", callable_mp(this, &TileSetEditor::_set_source_sort));
  666. p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorPlugin::SOURCE_SORT_ID);
  667. p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorPlugin::SOURCE_SORT_ID_REVERSE);
  668. p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorPlugin::SOURCE_SORT_NAME);
  669. p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorPlugin::SOURCE_SORT_NAME_REVERSE);
  670. p->set_item_checked(TilesEditorPlugin::SOURCE_SORT_ID, true);
  671. sources_list = memnew(ItemList);
  672. sources_list->set_fixed_icon_size(Size2(60, 60) * EDSCALE);
  673. sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
  674. sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
  675. sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
  676. sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
  677. sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list).bind(sources_list, source_sort_button));
  678. sources_list->add_user_signal(MethodInfo("sort_request"));
  679. sources_list->connect("sort_request", callable_mp(this, &TileSetEditor::_update_sources_list).bind(-1));
  680. sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
  681. SET_DRAG_FORWARDING_CDU(sources_list, TileSetEditor);
  682. split_container_left_side->add_child(sources_list);
  683. HBoxContainer *sources_bottom_actions = memnew(HBoxContainer);
  684. sources_bottom_actions->set_alignment(BoxContainer::ALIGNMENT_END);
  685. split_container_left_side->add_child(sources_bottom_actions);
  686. sources_delete_button = memnew(Button);
  687. sources_delete_button->set_flat(true);
  688. sources_delete_button->set_disabled(true);
  689. sources_delete_button->connect("pressed", callable_mp(this, &TileSetEditor::_source_delete_pressed));
  690. sources_bottom_actions->add_child(sources_delete_button);
  691. sources_add_button = memnew(MenuButton);
  692. sources_add_button->set_flat(true);
  693. sources_add_button->get_popup()->add_item(TTR("Atlas"));
  694. sources_add_button->get_popup()->add_item(TTR("Scenes Collection"));
  695. sources_add_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_source_add_id_pressed));
  696. sources_bottom_actions->add_child(sources_add_button);
  697. sources_advanced_menu_button = memnew(MenuButton);
  698. sources_advanced_menu_button->set_flat(true);
  699. sources_advanced_menu_button->get_popup()->add_item(TTR("Open Atlas Merging Tool"));
  700. sources_advanced_menu_button->get_popup()->add_item(TTR("Manage Tile Proxies"));
  701. sources_advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_sources_advanced_menu_id_pressed));
  702. sources_bottom_actions->add_child(sources_advanced_menu_button);
  703. sources_bottom_actions->add_child(source_sort_button);
  704. atlas_merging_dialog = memnew(AtlasMergingDialog);
  705. add_child(atlas_merging_dialog);
  706. tile_proxies_manager_dialog = memnew(TileProxiesManagerDialog);
  707. add_child(tile_proxies_manager_dialog);
  708. // Right side container.
  709. VBoxContainer *split_container_right_side = memnew(VBoxContainer);
  710. split_container_right_side->set_h_size_flags(SIZE_EXPAND_FILL);
  711. split_container_right_side->set_v_size_flags(SIZE_EXPAND_FILL);
  712. split_container->add_child(split_container_right_side);
  713. // No source selected.
  714. no_source_selected_label = memnew(Label);
  715. no_source_selected_label->set_text(TTR("No TileSet source selected. Select or create a TileSet source."));
  716. no_source_selected_label->set_h_size_flags(SIZE_EXPAND_FILL);
  717. no_source_selected_label->set_v_size_flags(SIZE_EXPAND_FILL);
  718. no_source_selected_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
  719. no_source_selected_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
  720. split_container_right_side->add_child(no_source_selected_label);
  721. // Atlases editor.
  722. tile_set_atlas_source_editor = memnew(TileSetAtlasSourceEditor);
  723. tile_set_atlas_source_editor->set_h_size_flags(SIZE_EXPAND_FILL);
  724. tile_set_atlas_source_editor->set_v_size_flags(SIZE_EXPAND_FILL);
  725. tile_set_atlas_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_sources_list));
  726. split_container_right_side->add_child(tile_set_atlas_source_editor);
  727. tile_set_atlas_source_editor->hide();
  728. // Scenes collection editor.
  729. tile_set_scenes_collection_source_editor = memnew(TileSetScenesCollectionSourceEditor);
  730. tile_set_scenes_collection_source_editor->set_h_size_flags(SIZE_EXPAND_FILL);
  731. tile_set_scenes_collection_source_editor->set_v_size_flags(SIZE_EXPAND_FILL);
  732. tile_set_scenes_collection_source_editor->connect("source_id_changed", callable_mp(this, &TileSetEditor::_update_sources_list));
  733. split_container_right_side->add_child(tile_set_scenes_collection_source_editor);
  734. tile_set_scenes_collection_source_editor->hide();
  735. //// Patterns ////
  736. int thumbnail_size = 64;
  737. patterns_item_list = memnew(ItemList);
  738. patterns_item_list->set_max_columns(0);
  739. patterns_item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
  740. patterns_item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
  741. patterns_item_list->set_max_text_lines(2);
  742. patterns_item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
  743. patterns_item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  744. patterns_item_list->connect("gui_input", callable_mp(this, &TileSetEditor::_patterns_item_list_gui_input));
  745. add_child(patterns_item_list);
  746. patterns_item_list->hide();
  747. patterns_help_label = memnew(Label);
  748. patterns_help_label->set_text(TTR("Add new patterns in the TileMap editing mode."));
  749. patterns_help_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
  750. patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
  751. patterns_item_list->add_child(patterns_help_label);
  752. // Registers UndoRedo inspector callback.
  753. EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element));
  754. EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
  755. }