Browse Source

Merge pull request #54822 from KoBeWi/sortuces

Rémi Verschelde 3 years ago
parent
commit
9df9dc77a3

+ 58 - 10
editor/plugins/tiles/tile_map_editor.cpp

@@ -124,6 +124,10 @@ void TileMapEditorTilesPlugin::_tab_changed() {
 void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
 	// Update the sources.
 	int old_current = sources_list->get_current();
+	int old_source = -1;
+	if (old_current > -1) {
+		old_source = sources_list->get_item_metadata(old_current);
+	}
 	sources_list->clear();
 
 	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
@@ -136,9 +140,12 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
 		return;
 	}
 
-	for (int i = 0; i < tile_set->get_source_count(); i++) {
-		int source_id = tile_set->get_source_id(i);
+	if (!tile_set->has_source(old_source)) {
+		old_source = -1;
+	}
 
+	List<int> source_ids = TilesEditorPlugin::get_singleton()->get_sorted_sources(tile_set);
+	for (const int &source_id : source_ids) {
 		TileSetSource *source = *tile_set->get_source(source_id);
 
 		Ref<Texture2D> texture;
@@ -157,7 +164,7 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
 				if (texture.is_valid()) {
 					item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id);
 				} else {
-					item_text = vformat("No Texture Atlas Source (ID: %d)", source_id);
+					item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
 				}
 			}
 		}
@@ -180,20 +187,25 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
 		}
 
 		sources_list->add_item(item_text, texture);
-		sources_list->set_item_metadata(i, source_id);
+		sources_list->set_item_metadata(sources_list->get_item_count() - 1, source_id);
 	}
 
 	if (sources_list->get_item_count() > 0) {
-		if (old_current > 0) {
-			// Keep the current selected item if needed.
-			sources_list->set_current(CLAMP(old_current, 0, sources_list->get_item_count() - 1));
+		if (old_source >= 0) {
+			for (int i = 0; i < sources_list->get_item_count(); i++) {
+				if ((int)sources_list->get_item_metadata(i) == old_source) {
+					sources_list->set_current(i);
+					sources_list->ensure_current_is_visible();
+					break;
+				}
+			}
 		} else {
 			sources_list->set_current(0);
 		}
 		sources_list->emit_signal(SNAME("item_selected"), sources_list->get_current());
 	}
 
-	// Synchronize
+	// Synchronize the lists.
 	TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
 }
 
@@ -439,6 +451,7 @@ void TileMapEditorTilesPlugin::_scenes_list_nothing_selected() {
 }
 
 void TileMapEditorTilesPlugin::_update_theme() {
+	source_sort_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
 	select_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons")));
 	paint_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
 	line_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")));
@@ -1950,6 +1963,14 @@ void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer
 	tile_map_layer = p_tile_map_layer;
 }
 
+void TileMapEditorTilesPlugin::_set_source_sort(int p_sort) {
+	for (int i = 0; i != TilesEditorPlugin::SOURCE_SORT_MAX; i++) {
+		source_sort_button->get_popup()->set_item_checked(i, (i == (int)p_sort));
+	}
+	TilesEditorPlugin::get_singleton()->set_sorting_option(p_sort);
+	_update_tile_set_sources_list();
+}
+
 void TileMapEditorTilesPlugin::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileMapEditorTilesPlugin::_scene_thumbnail_done);
 	ClassDB::bind_method(D_METHOD("_set_tile_map_selection", "selection"), &TileMapEditorTilesPlugin::_set_tile_map_selection);
@@ -2106,17 +2127,44 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
 	atlas_sources_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	tiles_bottom_panel->add_child(atlas_sources_split_container);
 
+	VBoxContainer *split_container_left_side = memnew(VBoxContainer);
+	split_container_left_side->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	split_container_left_side->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+	split_container_left_side->set_stretch_ratio(0.25);
+	split_container_left_side->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
+	atlas_sources_split_container->add_child(split_container_left_side);
+
+	HBoxContainer *sources_bottom_actions = memnew(HBoxContainer);
+	sources_bottom_actions->set_alignment(HBoxContainer::ALIGNMENT_END);
+
+	source_sort_button = memnew(MenuButton);
+	source_sort_button->set_flat(true);
+	source_sort_button->set_tooltip(TTR("Sort sources"));
+
+	PopupMenu *p = source_sort_button->get_popup();
+	p->connect("id_pressed", callable_mp(this, &TileMapEditorTilesPlugin::_set_source_sort));
+	p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorPlugin::SOURCE_SORT_ID);
+	p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorPlugin::SOURCE_SORT_ID_REVERSE);
+	p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorPlugin::SOURCE_SORT_NAME);
+	p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorPlugin::SOURCE_SORT_NAME_REVERSE);
+	p->set_item_checked(TilesEditorPlugin::SOURCE_SORT_ID, true);
+	sources_bottom_actions->add_child(source_sort_button);
+
 	sources_list = memnew(ItemList);
 	sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
 	sources_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	sources_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	sources_list->set_stretch_ratio(0.25);
 	sources_list->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
 	sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
 	sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
 	sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_source_display).unbind(1));
 	sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
-	sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list));
-	atlas_sources_split_container->add_child(sources_list);
+	sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list, source_sort_button));
+	sources_list->add_user_signal(MethodInfo("sort_request"));
+	sources_list->connect("sort_request", callable_mp(this, &TileMapEditorTilesPlugin::_update_tile_set_sources_list));
+	split_container_left_side->add_child(sources_list);
+	split_container_left_side->add_child(sources_bottom_actions);
 
 	// Tile atlas source.
 	tile_atlas_view = memnew(TileAtlasView);

+ 2 - 0
editor/plugins/tiles/tile_map_editor.h

@@ -145,6 +145,7 @@ private:
 	Label *invalid_source_label;
 
 	ItemList *sources_list;
+	MenuButton *source_sort_button;
 
 	Ref<Texture2D> missing_atlas_texture_icon;
 	void _update_tile_set_sources_list();
@@ -170,6 +171,7 @@ private:
 	void _tile_alternatives_control_gui_input(const Ref<InputEvent> &p_event);
 
 	void _update_atlas_view();
+	void _set_source_sort(int p_sort);
 
 	// Scenes collection sources.
 	ItemList *scene_tiles_list;

+ 41 - 9
editor/plugins/tiles/tile_set_editor.cpp

@@ -137,9 +137,8 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
 	sources_list->clear();
 
 	// Update the atlas sources.
-	for (int i = 0; i < tile_set->get_source_count(); i++) {
-		int source_id = tile_set->get_source_id(i);
-
+	List<int> source_ids = TilesEditorPlugin::get_singleton()->get_sorted_sources(tile_set);
+	for (const int &source_id : source_ids) {
 		TileSetSource *source = *tile_set->get_source(source_id);
 
 		Ref<Texture2D> texture;
@@ -156,9 +155,9 @@ 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("%s (ID:%d)", texture->get_path().get_file(), source_id);
+					item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id);
 				} else {
-					item_text = vformat(TTR("No Texture Atlas Source (ID:%d)"), source_id);
+					item_text = vformat(TTR("No Texture Atlas Source (ID: %d)"), source_id);
 				}
 			}
 		}
@@ -168,20 +167,20 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
 		if (scene_collection_source) {
 			texture = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
 			if (item_text.is_empty()) {
-				item_text = vformat(TTR("Scene Collection Source (ID:%d)"), source_id);
+				item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
 			}
 		}
 
 		// Use default if not valid.
 		if (item_text.is_empty()) {
-			item_text = vformat(TTR("Unknown Type Source (ID:%d)"), source_id);
+			item_text = vformat(TTR("Unknown Type Source (ID: %d)"), source_id);
 		}
 		if (!texture.is_valid()) {
 			texture = missing_texture_texture;
 		}
 
 		sources_list->add_item(item_text, texture);
-		sources_list->set_item_metadata(i, source_id);
+		sources_list->set_item_metadata(sources_list->get_item_count() - 1, source_id);
 	}
 
 	// Set again the current selected item if needed.
@@ -189,6 +188,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
 		for (int i = 0; i < sources_list->get_item_count(); i++) {
 			if ((int)sources_list->get_item_metadata(i) == to_select) {
 				sources_list->set_current(i);
+				sources_list->ensure_current_is_visible();
 				if (old_selected != to_select) {
 					sources_list->emit_signal(SNAME("item_selected"), sources_list->get_current());
 				}
@@ -312,12 +312,29 @@ void TileSetEditor::_sources_advanced_menu_id_pressed(int p_id_pressed) {
 	}
 }
 
+void TileSetEditor::_set_source_sort(int p_sort) {
+	TilesEditorPlugin::get_singleton()->set_sorting_option(p_sort);
+	for (int i = 0; i != TilesEditorPlugin::SOURCE_SORT_MAX; i++) {
+		source_sort_button->get_popup()->set_item_checked(i, (i == (int)p_sort));
+	}
+
+	int old_selected = TileSet::INVALID_SOURCE;
+	if (sources_list->get_current() >= 0) {
+		int source_id = sources_list->get_item_metadata(sources_list->get_current());
+		if (tile_set->has_source(source_id)) {
+			old_selected = source_id;
+		}
+	}
+	_update_sources_list(old_selected);
+}
+
 void TileSetEditor::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_ENTER_TREE:
 		case NOTIFICATION_THEME_CHANGED:
 			sources_delete_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
 			sources_add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
+			source_sort_button->set_icon(get_theme_icon(SNAME("Sort"), SNAME("EditorIcons")));
 			sources_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
 			missing_texture_texture = get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons"));
 			break;
@@ -670,13 +687,27 @@ TileSetEditor::TileSetEditor() {
 	split_container_left_side->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
 	split_container->add_child(split_container_left_side);
 
+	source_sort_button = memnew(MenuButton);
+	source_sort_button->set_flat(true);
+	source_sort_button->set_tooltip(TTR("Sort sources"));
+
+	PopupMenu *p = source_sort_button->get_popup();
+	p->connect("id_pressed", callable_mp(this, &TileSetEditor::_set_source_sort));
+	p->add_radio_check_item(TTR("Sort by ID (Ascending)"), TilesEditorPlugin::SOURCE_SORT_ID);
+	p->add_radio_check_item(TTR("Sort by ID (Descending)"), TilesEditorPlugin::SOURCE_SORT_ID_REVERSE);
+	p->add_radio_check_item(TTR("Sort by Name (Ascending)"), TilesEditorPlugin::SOURCE_SORT_NAME);
+	p->add_radio_check_item(TTR("Sort by Name (Descending)"), TilesEditorPlugin::SOURCE_SORT_NAME_REVERSE);
+	p->set_item_checked(TilesEditorPlugin::SOURCE_SORT_ID, true);
+
 	sources_list = memnew(ItemList);
 	sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
 	sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
 	sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
 	sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
 	sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
-	sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list));
+	sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list, source_sort_button));
+	sources_list->add_user_signal(MethodInfo("sort_request"));
+	sources_list->connect("sort_request", callable_mp(this, &TileSetEditor::_update_sources_list), varray(-1));
 	sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
 	sources_list->set_drag_forwarding(this);
 	split_container_left_side->add_child(sources_list);
@@ -704,6 +735,7 @@ TileSetEditor::TileSetEditor() {
 	sources_advanced_menu_button->get_popup()->add_item(TTR("Manage Tile Proxies"));
 	sources_advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetEditor::_sources_advanced_menu_id_pressed));
 	sources_bottom_actions->add_child(sources_advanced_menu_button);
+	sources_bottom_actions->add_child(source_sort_button);
 
 	atlas_merging_dialog = memnew(AtlasMergingDialog);
 	add_child(atlas_merging_dialog);

+ 2 - 0
editor/plugins/tiles/tile_set_editor.h

@@ -67,6 +67,7 @@ private:
 	// Sources management.
 	Button *sources_delete_button;
 	MenuButton *sources_add_button;
+	MenuButton *source_sort_button;
 	MenuButton *sources_advanced_menu_button;
 	ItemList *sources_list;
 	Ref<Texture2D> missing_texture_texture;
@@ -74,6 +75,7 @@ private:
 	void _source_delete_pressed();
 	void _source_add_id_pressed(int p_id_pressed);
 	void _sources_advanced_menu_id_pressed(int p_id_pressed);
+	void _set_source_sort(int p_sort);
 
 	AtlasMergingDialog *atlas_merging_dialog;
 	TileProxiesManagerDialog *tile_proxies_manager_dialog;

+ 97 - 2
editor/plugins/tiles/tiles_editor_plugin.cpp

@@ -218,15 +218,29 @@ void TilesEditorPlugin::set_sources_lists_current(int p_current) {
 	atlas_sources_lists_current = p_current;
 }
 
-void TilesEditorPlugin::synchronize_sources_list(Object *p_current) {
-	ItemList *item_list = Object::cast_to<ItemList>(p_current);
+void TilesEditorPlugin::synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button) {
+	ItemList *item_list = Object::cast_to<ItemList>(p_current_list);
+	MenuButton *sorting_button = Object::cast_to<MenuButton>(p_current_sort_button);
 	ERR_FAIL_COND(!item_list);
+	ERR_FAIL_COND(!sorting_button);
+
+	if (sorting_button->is_visible_in_tree()) {
+		for (int i = 0; i != SOURCE_SORT_MAX; i++) {
+			sorting_button->get_popup()->set_item_checked(i, (i == (int)source_sort));
+		}
+	}
 
 	if (item_list->is_visible_in_tree()) {
 		if (atlas_sources_lists_current < 0 || atlas_sources_lists_current >= item_list->get_item_count()) {
 			item_list->deselect_all();
 		} else {
+			// Make sure the selection is not overwritten after sorting.
+			int atlas_sources_lists_current_mem = atlas_sources_lists_current;
+			item_list->emit_signal(SNAME("sort_request"));
+			atlas_sources_lists_current = atlas_sources_lists_current_mem;
+
 			item_list->set_current(atlas_sources_lists_current);
+			item_list->ensure_current_is_visible();
 			item_list->emit_signal(SNAME("item_selected"), atlas_sources_lists_current);
 		}
 	}
@@ -246,6 +260,87 @@ void TilesEditorPlugin::synchronize_atlas_view(Object *p_current) {
 	}
 }
 
+void TilesEditorPlugin::set_sorting_option(int p_option) {
+	source_sort = p_option;
+}
+
+List<int> TilesEditorPlugin::get_sorted_sources(const Ref<TileSet> tile_set) const {
+	SourceNameComparator::tile_set = tile_set;
+	List<int> source_ids;
+
+	for (int i = 0; i < tile_set->get_source_count(); i++) {
+		source_ids.push_back(tile_set->get_source_id(i));
+	}
+
+	switch (source_sort) {
+		case SOURCE_SORT_ID_REVERSE:
+			// Already sorted.
+			source_ids.reverse();
+			break;
+		case SOURCE_SORT_NAME:
+			source_ids.sort_custom<SourceNameComparator>();
+			break;
+		case SOURCE_SORT_NAME_REVERSE:
+			source_ids.sort_custom<SourceNameComparator>();
+			source_ids.reverse();
+			break;
+		default: // SOURCE_SORT_ID
+			break;
+	}
+
+	SourceNameComparator::tile_set.unref();
+	return source_ids;
+}
+
+Ref<TileSet> TilesEditorPlugin::SourceNameComparator::tile_set;
+
+bool TilesEditorPlugin::SourceNameComparator::operator()(const int &p_a, const int &p_b) const {
+	String name_a;
+	String name_b;
+
+	{
+		TileSetSource *source = *tile_set->get_source(p_a);
+
+		if (!source->get_name().is_empty()) {
+			name_a = source->get_name();
+		}
+
+		TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+		if (atlas_source) {
+			Ref<Texture2D> texture = atlas_source->get_texture();
+			if (name_a.is_empty() && texture.is_valid()) {
+				name_a = texture->get_path().get_file();
+			}
+		}
+
+		if (name_a.is_empty()) {
+			name_a = itos(p_a);
+		}
+	}
+
+	{
+		TileSetSource *source = *tile_set->get_source(p_b);
+
+		if (!source->get_name().is_empty()) {
+			name_b = source->get_name();
+		}
+
+		TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
+		if (atlas_source) {
+			Ref<Texture2D> texture = atlas_source->get_texture();
+			if (name_b.is_empty() && texture.is_valid()) {
+				name_b = texture->get_path().get_file();
+			}
+		}
+
+		if (name_b.is_empty()) {
+			name_b = itos(p_b);
+		}
+	}
+
+	return NaturalNoCaseComparator()(name_a, name_b);
+}
+
 void TilesEditorPlugin::edit(Object *p_object) {
 	// Disconnect to changes.
 	TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));

+ 22 - 1
editor/plugins/tiles/tiles_editor_plugin.h

@@ -43,6 +43,15 @@ class TilesEditorPlugin : public EditorPlugin {
 
 	static TilesEditorPlugin *singleton;
 
+public:
+	enum SourceSortOption {
+		SOURCE_SORT_ID = 0,
+		SOURCE_SORT_ID_REVERSE,
+		SOURCE_SORT_NAME,
+		SOURCE_SORT_NAME_REVERSE,
+		SOURCE_SORT_MAX
+	};
+
 private:
 	EditorNode *editor_node;
 
@@ -65,6 +74,14 @@ private:
 
 	void _tile_map_changed();
 
+	// Source sorting.
+	int source_sort = SOURCE_SORT_ID;
+
+	struct SourceNameComparator {
+		static Ref<TileSet> tile_set;
+		bool operator()(const int &p_a, const int &p_b) const;
+	};
+
 	// Patterns preview generation.
 	struct QueueItem {
 		Ref<TileSet> tile_set;
@@ -97,11 +114,15 @@ public:
 
 	// To synchronize the atlas sources lists.
 	void set_sources_lists_current(int p_current);
-	void synchronize_sources_list(Object *p_current);
+	void synchronize_sources_list(Object *p_current_list, Object *p_current_sort_button);
 
 	void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
 	void synchronize_atlas_view(Object *p_current);
 
+	// Sorting.
+	void set_sorting_option(int p_option);
+	List<int> get_sorted_sources(const Ref<TileSet> tile_set) const;
+
 	virtual void edit(Object *p_object) override;
 	virtual bool handles(Object *p_object) const override;
 	virtual void make_visible(bool p_visible) override;