From 2a5db5949f4880e4ba3133fff206a2696cd41a6b Mon Sep 17 00:00:00 2001
From: kobewi <kobewi4e@gmail.com>
Date: Fri, 14 Jul 2023 18:25:51 +0200
Subject: [PATCH] Rework modifying tile source ID

---
 editor/plugins/tiles/tile_map_editor.cpp      |  4 +-
 .../tiles/tile_set_atlas_source_editor.cpp    |  1 +
 editor/plugins/tiles/tile_set_editor.cpp      | 57 ++++++++++++++++++-
 editor/plugins/tiles/tile_set_editor.h        | 21 ++++++-
 ...le_set_scenes_collection_source_editor.cpp |  2 +
 5 files changed, 80 insertions(+), 5 deletions(-)

diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 9c5d55d78f4..06b4ae0d87f 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -156,7 +156,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
 
 		// Common to all type of sources.
 		if (!source->get_name().is_empty()) {
-			item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id);
+			item_text = source->get_name();
 		}
 
 		// Atlas source.
@@ -165,7 +165,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
 			texture = atlas_source->get_texture();
 			if (item_text.is_empty()) {
 				if (texture.is_valid()) {
-					item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
+					item_text = texture->get_path().get_file();
 				} else {
 					item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
 				}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 61d57e5eab5..c98d9086d18 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2529,6 +2529,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
 	atlas_source_inspector = memnew(EditorInspector);
 	atlas_source_inspector->set_v_size_flags(SIZE_EXPAND_FILL);
 	atlas_source_inspector->set_show_categories(true);
+	atlas_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
 	atlas_source_inspector->edit(atlas_source_proxy_object);
 	middle_vbox_container->add_child(atlas_source_inspector);
 
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index eb79394cc96..f620e434ab1 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -34,6 +34,7 @@
 #include "tiles_editor_plugin.h"
 
 #include "editor/editor_file_system.h"
+#include "editor/editor_inspector.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
@@ -42,6 +43,7 @@
 
 #include "scene/gui/box_container.h"
 #include "scene/gui/control.h"
+#include "scene/gui/dialogs.h"
 #include "scene/gui/tab_container.h"
 
 TileSetEditor *TileSetEditor::singleton = nullptr;
@@ -158,7 +160,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
 
 		// Common to all type of sources.
 		if (!source->get_name().is_empty()) {
-			item_text = vformat(TTR("%s (ID: %d)"), source->get_name(), source_id);
+			item_text = source->get_name();
 		}
 
 		// Atlas source.
@@ -167,7 +169,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
 			texture = atlas_source->get_texture();
 			if (item_text.is_empty()) {
 				if (texture.is_valid()) {
-					item_text = vformat(TTR("%s (ID: %d)"), texture->get_path().get_file(), source_id);
+					item_text = texture->get_path().get_file();
 				} else {
 					item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
 				}
@@ -964,3 +966,54 @@ TileSetEditor::TileSetEditor() {
 	EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element));
 	EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
 }
+
+void TileSourceInspectorPlugin::_show_id_edit_dialog(Object *p_for_source) {
+	if (!id_edit_dialog) {
+		id_edit_dialog = memnew(ConfirmationDialog);
+		TileSetEditor::get_singleton()->add_child(id_edit_dialog);
+
+		VBoxContainer *vbox = memnew(VBoxContainer);
+		id_edit_dialog->add_child(vbox);
+
+		Label *label = memnew(Label(TTR("Warning: Modifying a source ID will result in all TileMaps using that source to reference an invalid source instead. This may result in unexpected data loss. Change this ID carefully.")));
+		label->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
+		vbox->add_child(label);
+
+		id_input = memnew(SpinBox);
+		vbox->add_child(id_input);
+		id_input->set_max(INT_MAX);
+
+		id_edit_dialog->connect("confirmed", callable_mp(this, &TileSourceInspectorPlugin::_confirm_change_id));
+	}
+	edited_source = p_for_source;
+	id_input->set_value(p_for_source->get("id"));
+	id_edit_dialog->popup_centered(Vector2i(400, 0) * EDSCALE);
+	callable_mp((Control *)id_input->get_line_edit(), &Control::grab_focus).call_deferred();
+}
+
+void TileSourceInspectorPlugin::_confirm_change_id() {
+	edited_source->set("id", id_input->get_value());
+	id_label->set_text(vformat(TTR("ID: %d"), edited_source->get("id"))); // Use get(), because the provided ID might've been invalid.
+}
+
+bool TileSourceInspectorPlugin::can_handle(Object *p_object) {
+	return p_object->is_class("TileSetAtlasSourceProxyObject") || p_object->is_class("TileSetScenesCollectionProxyObject");
+}
+
+bool TileSourceInspectorPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {
+	if (p_path == "id") {
+		HBoxContainer *hbox = memnew(HBoxContainer);
+		hbox->set_alignment(BoxContainer::ALIGNMENT_CENTER);
+
+		id_label = memnew(Label(vformat(TTR("ID: %d"), p_object->get("id"))));
+		hbox->add_child(id_label);
+
+		Button *button = memnew(Button(TTR("Edit")));
+		hbox->add_child(button);
+		button->connect("pressed", callable_mp(this, &TileSourceInspectorPlugin::_show_id_edit_dialog).bind(p_object));
+
+		add_custom_control(hbox);
+		return true;
+	}
+	return false;
+}
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index 40ca1f7ed73..86cd70d19ec 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -38,9 +38,12 @@
 #include "tile_set_atlas_source_editor.h"
 #include "tile_set_scenes_collection_source_editor.h"
 
-class EditorFileDialog;
+class AcceptDialog;
+class SpinBox;
 class HBoxContainer;
 class SplitContainer;
+class EditorFileDialog;
+class EditorInspectorPlugin;
 
 class TileSetEditor : public Control {
 	GDCLASS(TileSetEditor, Control);
@@ -123,4 +126,20 @@ public:
 	TileSetEditor();
 };
 
+class TileSourceInspectorPlugin : public EditorInspectorPlugin {
+	GDCLASS(TileSourceInspectorPlugin, EditorInspectorPlugin);
+
+	AcceptDialog *id_edit_dialog = nullptr;
+	Label *id_label = nullptr;
+	SpinBox *id_input = nullptr;
+	Object *edited_source = nullptr;
+
+	void _show_id_edit_dialog(Object *p_for_source);
+	void _confirm_change_id();
+
+public:
+	virtual bool can_handle(Object *p_object) override;
+	virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide = false) override;
+};
+
 #endif // TILE_SET_EDITOR_H
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
index 5ffa7b4bd44..13270f38217 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -36,6 +36,7 @@
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/plugins/tiles/tile_set_editor.h"
 
 #include "scene/gui/button.h"
 #include "scene/gui/item_list.h"
@@ -504,6 +505,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
 
 	scenes_collection_source_inspector = memnew(EditorInspector);
 	scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
+	scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
 	scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
 	middle_vbox_container->add_child(scenes_collection_source_inspector);