瀏覽代碼

Seperate filter and description in FileDialog.add_filter()

FireForge 3 年之前
父節點
當前提交
af19501cc7

+ 4 - 2
doc/classes/EditorFileDialog.xml

@@ -11,9 +11,11 @@
 		<method name="add_filter">
 			<return type="void" />
 			<argument index="0" name="filter" type="String" />
+			<argument index="1" name="description" type="String" default="&quot;&quot;" />
 			<description>
-				Adds a comma-delimited file extension filter option to the [EditorFileDialog] with an optional semi-colon-delimited label.
-				For example, [code]"*.tscn, *.scn; Scenes"[/code] results in filter text "Scenes (*.tscn, *.scn)".
+				Adds a comma-delimited file name [code]filter[/code] option to the [EditorFileDialog] with an optional [code]description[/code], which restricts what files can be picked.
+				A [code]filter[/code] should be of the form [code]"filename.extension"[/code], where filename and extension can be [code]*[/code] to match any string. Filters starting with [code].[/code] (i.e. empty filenames) are not allowed.
+				For example, a [code]filter[/code] of [code]"*.tscn, *.scn"[/code] and a [code]description[/code] of [code]"Scenes"[/code] results in filter text "Scenes (*.tscn, *.scn)".
 			</description>
 		</method>
 		<method name="clear_filters">

+ 4 - 3
doc/classes/FileDialog.xml

@@ -12,10 +12,11 @@
 		<method name="add_filter">
 			<return type="void" />
 			<argument index="0" name="filter" type="String" />
+			<argument index="1" name="description" type="String" default="&quot;&quot;" />
 			<description>
-				Adds [code]filter[/code] to the list of filters, which restricts what files can be picked.
-				A [code]filter[/code] should be of the form [code]"filename.extension ; Description"[/code], where filename and extension can be [code]*[/code] to match any string. Filters starting with [code].[/code] (i.e. empty filenames) are not allowed.
-				Example filters: [code]"*.png ; PNG Images"[/code], [code]"project.godot ; Godot Project"[/code].
+				Adds a comma-delimited file name [code]filter[/code] option to the [FileDialog] with an optional [code]description[/code], which restricts what files can be picked.
+				A [code]filter[/code] should be of the form [code]"filename.extension"[/code], where filename and extension can be [code]*[/code] to match any string. Filters starting with [code].[/code] (i.e. empty filenames) are not allowed.
+				For example, a [code]filter[/code] of [code]"*.png, *.jpg"[/code] and a [code]description[/code] of [code]"Images"[/code] results in filter text "Images (*.png, *.jpg)".
 			</description>
 		</method>
 		<method name="clear_filters">

+ 1 - 1
editor/debugger/editor_debugger_tree.cpp

@@ -258,7 +258,7 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) {
 			ResourceSaver::get_recognized_extensions(sd, &extensions);
 			file_dialog->clear_filters();
 			for (int i = 0; i < extensions.size(); i++) {
-				file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+				file_dialog->add_filter("*." + extensions[i], extensions[i].to_upper());
 			}
 
 			file_dialog->popup_file_dialog();

+ 1 - 1
editor/editor_audio_buses.cpp

@@ -1332,7 +1332,7 @@ EditorAudioBuses::EditorAudioBuses() {
 	List<String> ext;
 	ResourceLoader::get_recognized_extensions_for_type("AudioBusLayout", &ext);
 	for (const String &E : ext) {
-		file_dialog->add_filter(vformat("*.%s; %s", E, TTR("Audio Bus Layout")));
+		file_dialog->add_filter("*." + E, TTR("Audio Bus Layout"));
 	}
 	add_child(file_dialog);
 	file_dialog->connect("file_selected", callable_mp(this, &EditorAudioBuses::_file_dialog_callback));

+ 2 - 2
editor/editor_feature_profile.cpp

@@ -993,7 +993,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
 	import_profiles = memnew(EditorFileDialog);
 	add_child(import_profiles);
 	import_profiles->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILES);
-	import_profiles->add_filter("*.profile; " + TTR("Godot Feature Profile"));
+	import_profiles->add_filter("*.profile", TTR("Godot Feature Profile"));
 	import_profiles->connect("files_selected", callable_mp(this, &EditorFeatureProfileManager::_import_profiles));
 	import_profiles->set_title(TTR("Import Profile(s)"));
 	import_profiles->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
@@ -1001,7 +1001,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
 	export_profile = memnew(EditorFileDialog);
 	add_child(export_profile);
 	export_profile->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
-	export_profile->add_filter("*.profile; " + TTR("Godot Feature Profile"));
+	export_profile->add_filter("*.profile", TTR("Godot Feature Profile"));
 	export_profile->connect("file_selected", callable_mp(this, &EditorFeatureProfileManager::_export_profile));
 	export_profile->set_title(TTR("Export Profile"));
 	export_profile->set_access(EditorFileDialog::ACCESS_FILESYSTEM);

+ 7 - 3
editor/editor_file_dialog.cpp

@@ -976,8 +976,12 @@ void EditorFileDialog::clear_filters() {
 	invalidate();
 }
 
-void EditorFileDialog::add_filter(const String &p_filter) {
-	filters.push_back(p_filter);
+void EditorFileDialog::add_filter(const String &p_filter, const String &p_description) {
+	if (p_description.is_empty()) {
+		filters.push_back(p_filter);
+	} else {
+		filters.push_back(vformat("%s ; %s", p_filter, p_description));
+	}
 	update_filters();
 	invalidate();
 }
@@ -1481,7 +1485,7 @@ void EditorFileDialog::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_cancel_pressed"), &EditorFileDialog::_cancel_pressed);
 
 	ClassDB::bind_method(D_METHOD("clear_filters"), &EditorFileDialog::clear_filters);
-	ClassDB::bind_method(D_METHOD("add_filter", "filter"), &EditorFileDialog::add_filter);
+	ClassDB::bind_method(D_METHOD("add_filter", "filter", "description"), &EditorFileDialog::add_filter, DEFVAL(""));
 	ClassDB::bind_method(D_METHOD("get_current_dir"), &EditorFileDialog::get_current_dir);
 	ClassDB::bind_method(D_METHOD("get_current_file"), &EditorFileDialog::get_current_file);
 	ClassDB::bind_method(D_METHOD("get_current_path"), &EditorFileDialog::get_current_path);

+ 1 - 1
editor/editor_file_dialog.h

@@ -212,7 +212,7 @@ protected:
 public:
 	void popup_file_dialog();
 	void clear_filters();
-	void add_filter(const String &p_filter);
+	void add_filter(const String &p_filter, const String &p_description = "");
 
 	void set_enable_multiple_selection(bool p_enable);
 	Vector<String> get_selected_files() const;

+ 5 - 5
editor/editor_node.cpp

@@ -1259,7 +1259,7 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String
 			// This serves no purpose and confused people.
 			continue;
 		}
-		file->add_filter("*." + E + " ; " + E.to_upper());
+		file->add_filter("*." + E, E.to_upper());
 		preferred.push_back(E);
 	}
 	// Lowest priority extension.
@@ -2471,7 +2471,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
 			file->clear_filters();
 			for (int i = 0; i < extensions.size(); i++) {
-				file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+				file->add_filter("*." + extensions[i], extensions[i].to_upper());
 			}
 
 			Node *scene = editor_data.get_edited_scene_root();
@@ -2617,7 +2617,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			ResourceSaver::get_recognized_extensions(sd, &extensions);
 			file->clear_filters();
 			for (int i = 0; i < extensions.size(); i++) {
-				file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+				file->add_filter("*." + extensions[i], extensions[i].to_upper());
 			}
 
 			if (!scene->get_scene_file_path().is_empty()) {
@@ -2903,7 +2903,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);
 			file->clear_filters();
 			for (int i = 0; i < extensions.size(); i++) {
-				file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+				file->add_filter("*." + extensions[i], extensions[i].to_upper());
 			}
 
 			Node *scene = editor_data.get_edited_scene_root();
@@ -6970,7 +6970,7 @@ EditorNode::EditorNode() {
 	file_templates->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
 	file_templates->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
 	file_templates->clear_filters();
-	file_templates->add_filter("*.tpz ; " + TTR("Template Package"));
+	file_templates->add_filter("*.tpz", TTR("Template Package"));
 
 	file = memnew(EditorFileDialog);
 	gui_base->add_child(file);

+ 1 - 1
editor/editor_resource_picker.cpp

@@ -254,7 +254,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
 
 			file_dialog->clear_filters();
 			for (const String &E : valid_extensions) {
-				file_dialog->add_filter("*." + E + " ; " + E.to_upper());
+				file_dialog->add_filter("*." + E, E.to_upper());
 			}
 
 			file_dialog->popup_file_dialog();

+ 1 - 1
editor/export_template_manager.cpp

@@ -990,7 +990,7 @@ ExportTemplateManager::ExportTemplateManager() {
 	install_file_dialog->set_title(TTR("Select Template File"));
 	install_file_dialog->set_access(FileDialog::ACCESS_FILESYSTEM);
 	install_file_dialog->set_file_mode(FileDialog::FILE_MODE_OPEN_FILE);
-	install_file_dialog->add_filter("*.tpz ; " + TTR("Godot Export Templates"));
+	install_file_dialog->add_filter("*.tpz", TTR("Godot Export Templates"));
 	install_file_dialog->connect("file_selected", callable_mp(this, &ExportTemplateManager::_install_file_selected), varray(false));
 	add_child(install_file_dialog);
 

+ 2 - 2
editor/import/scene_import_settings.cpp

@@ -1382,8 +1382,8 @@ SceneImportSettings::SceneImportSettings() {
 
 	item_save_path = memnew(EditorFileDialog);
 	item_save_path->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
-	item_save_path->add_filter("*.tres; " + TTR("Text Resource"));
-	item_save_path->add_filter("*.res; " + TTR("Binary Resource"));
+	item_save_path->add_filter("*.tres", TTR("Text Resource"));
+	item_save_path->add_filter("*.res", TTR("Binary Resource"));
 	add_child(item_save_path);
 	item_save_path->connect("file_selected", callable_mp(this, &SceneImportSettings::_save_path_changed));
 

+ 2 - 2
editor/inspector_dock.cpp

@@ -219,12 +219,12 @@ void InspectorDock::_load_resource(const String &p_type) {
 
 	load_resource_dialog->clear_filters();
 	for (int i = 0; i < extensions.size(); i++) {
-		load_resource_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+		load_resource_dialog->add_filter("*." + extensions[i], extensions[i].to_upper());
 	}
 
 	const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
 	for (int i = 0; i < textfile_ext.size(); i++) {
-		load_resource_dialog->add_filter("*." + textfile_ext[i] + " ; " + textfile_ext[i].to_upper());
+		load_resource_dialog->add_filter("*." + textfile_ext[i], textfile_ext[i].to_upper());
 	}
 
 	load_resource_dialog->popup_file_dialog();

+ 1 - 1
editor/plugins/asset_library_editor_plugin.cpp

@@ -1584,7 +1584,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
 	asset_open = memnew(EditorFileDialog);
 
 	asset_open->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
-	asset_open->add_filter("*.zip ; " + TTR("Assets ZIP File"));
+	asset_open->add_filter("*.zip", TTR("Assets ZIP File"));
 	asset_open->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
 	add_child(asset_open);
 	asset_open->connect("file_selected", callable_mp(this, &EditorAssetLibrary::_asset_file_selected));

+ 1 - 1
editor/plugins/cpu_particles_2d_editor_plugin.cpp

@@ -257,7 +257,7 @@ CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() {
 	List<String> ext;
 	ImageLoader::get_recognized_extensions(&ext);
 	for (const String &E : ext) {
-		file->add_filter("*." + E + "; " + E.to_upper());
+		file->add_filter("*." + E, E.to_upper());
 	}
 	file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
 	toolbar->add_child(file);

+ 1 - 1
editor/plugins/gpu_particles_2d_editor_plugin.cpp

@@ -380,7 +380,7 @@ GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin() {
 	List<String> ext;
 	ImageLoader::get_recognized_extensions(&ext);
 	for (const String &E : ext) {
-		file->add_filter("*." + E + "; " + E.to_upper());
+		file->add_filter("*." + E, E.to_upper());
 	}
 	file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
 	toolbar->add_child(file);

+ 1 - 1
editor/plugins/lightmap_gi_editor_plugin.cpp

@@ -138,7 +138,7 @@ LightmapGIEditorPlugin::LightmapGIEditorPlugin() {
 
 	file_dialog = memnew(EditorFileDialog);
 	file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
-	file_dialog->add_filter("*.lmbake ; " + TTR("LightMap Bake"));
+	file_dialog->add_filter("*.lmbake", TTR("LightMap Bake"));
 	file_dialog->set_title(TTR("Select lightmap bake file:"));
 	file_dialog->connect("file_selected", callable_mp(this, &LightmapGIEditorPlugin::_bake_select_file));
 	bake->add_child(file_dialog);

+ 1 - 1
editor/plugins/mesh_library_editor_plugin.cpp

@@ -263,7 +263,7 @@ MeshLibraryEditor::MeshLibraryEditor() {
 	file->clear_filters();
 	file->set_title(TTR("Import Scene"));
 	for (int i = 0; i < extensions.size(); i++) {
-		file->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+		file->add_filter("*." + extensions[i], extensions[i].to_upper());
 	}
 	add_child(file);
 	file->connect("file_selected", callable_mp(this, &MeshLibraryEditor::_import_scene_cbk));

+ 1 - 1
editor/plugins/occluder_instance_3d_editor_plugin.cpp

@@ -113,7 +113,7 @@ OccluderInstance3DEditorPlugin::OccluderInstance3DEditorPlugin() {
 
 	file_dialog = memnew(EditorFileDialog);
 	file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
-	file_dialog->add_filter("*.occ ; Occluder3D");
+	file_dialog->add_filter("*.occ", "Occluder3D");
 	file_dialog->set_title(TTR("Select occluder bake file:"));
 	file_dialog->connect("file_selected", callable_mp(this, &OccluderInstance3DEditorPlugin::_bake_select_file));
 	bake->add_child(file_dialog);

+ 3 - 3
editor/plugins/script_editor_plugin.cpp

@@ -1188,7 +1188,7 @@ void ScriptEditor::_menu_option(int p_option) {
 
 			file_dialog->clear_filters();
 			for (const String &E : textfile_extensions) {
-				file_dialog->add_filter("*." + E + " ; " + E.to_upper());
+				file_dialog->add_filter("*." + E, E.to_upper());
 			}
 			file_dialog->popup_file_dialog();
 			file_dialog->set_title(TTR("New Text File..."));
@@ -1203,11 +1203,11 @@ void ScriptEditor::_menu_option(int p_option) {
 			ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
 			file_dialog->clear_filters();
 			for (int i = 0; i < extensions.size(); i++) {
-				file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+				file_dialog->add_filter("*." + extensions[i], extensions[i].to_upper());
 			}
 
 			for (const String &E : textfile_extensions) {
-				file_dialog->add_filter("*." + E + " ; " + E.to_upper());
+				file_dialog->add_filter("*." + E, E.to_upper());
 			}
 
 			file_dialog->popup_file_dialog();

+ 2 - 2
editor/plugins/theme_editor_plugin.cpp

@@ -2080,7 +2080,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito
 	List<String> ext;
 	ResourceLoader::get_recognized_extensions_for_type("Theme", &ext);
 	for (const String &E : ext) {
-		import_another_theme_dialog->add_filter(vformat("*.%s; %s", E, TTR("Theme Resource")));
+		import_another_theme_dialog->add_filter("*." + E, TTR("Theme Resource"));
 	}
 	import_another_file_hb->add_child(import_another_theme_dialog);
 	import_another_theme_dialog->connect("file_selected", callable_mp(this, &ThemeItemEditorDialog::_select_another_theme_cbk));
@@ -3663,7 +3663,7 @@ ThemeEditor::ThemeEditor() {
 	List<String> ext;
 	ResourceLoader::get_recognized_extensions_for_type("PackedScene", &ext);
 	for (const String &E : ext) {
-		preview_scene_dialog->add_filter(vformat("*.%s; %s", E, TTR("Scene")));
+		preview_scene_dialog->add_filter("*." + E, TTR("Scene"));
 	}
 	main_hs->add_child(preview_scene_dialog);
 	preview_scene_dialog->connect("file_selected", callable_mp(this, &ThemeEditor::_preview_scene_dialog_cbk));

+ 3 - 3
editor/project_export.cpp

@@ -892,7 +892,7 @@ void ProjectExportDialog::_export_project() {
 	List<String> extension_list = platform->get_binary_extensions(current);
 	for (int i = 0; i < extension_list.size(); i++) {
 		// TRANSLATORS: This is the name of a project export file format. %s will be replaced by the platform name.
-		export_project->add_filter(vformat("*.%s; %s", extension_list[i], vformat(TTR("%s Export"), platform->get_name())));
+		export_project->add_filter("*." + extension_list[i], vformat(TTR("%s Export"), platform->get_name()));
 	}
 
 	if (!current->get_export_path().is_empty()) {
@@ -1222,8 +1222,8 @@ ProjectExportDialog::ProjectExportDialog() {
 	export_all_button->set_disabled(true);
 
 	export_pck_zip = memnew(EditorFileDialog);
-	export_pck_zip->add_filter("*.zip ; " + TTR("ZIP File"));
-	export_pck_zip->add_filter("*.pck ; " + TTR("Godot Project Pack"));
+	export_pck_zip->add_filter("*.zip", TTR("ZIP File"));
+	export_pck_zip->add_filter("*.pck", TTR("Godot Project Pack"));
 	export_pck_zip->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
 	export_pck_zip->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
 	add_child(export_pck_zip);

+ 2 - 2
editor/project_manager.cpp

@@ -362,8 +362,8 @@ private:
 		if (mode == MODE_IMPORT) {
 			fdialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
 			fdialog->clear_filters();
-			fdialog->add_filter(vformat("project.godot ; %s %s", VERSION_NAME, TTR("Project")));
-			fdialog->add_filter("*.zip ; " + TTR("ZIP File"));
+			fdialog->add_filter("project.godot", vformat("%s %s", VERSION_NAME, TTR("Project")));
+			fdialog->add_filter("*.zip", TTR("ZIP File"));
 		} else {
 			fdialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);
 		}

+ 3 - 3
editor/property_editor.cpp

@@ -145,7 +145,7 @@ void CustomPropertyEditor::_menu_option(int p_which) {
 
 					file->clear_filters();
 					for (const String &E : valid_extensions) {
-						file->add_filter("*." + E + " ; " + E.to_upper());
+						file->add_filter("*." + E, E.to_upper());
 					}
 
 					file->popup_file_dialog();
@@ -1223,7 +1223,7 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
 								filter = "*." + extensions[i];
 							}
 
-							file->add_filter(filter + " ; " + extensions[i].to_upper());
+							file->add_filter(filter, extensions[i].to_upper());
 						}
 					}
 					file->popup_file_dialog();
@@ -1307,7 +1307,7 @@ void CustomPropertyEditor::_action_pressed(int p_which) {
 				ResourceLoader::get_recognized_extensions_for_type(type, &extensions);
 				file->clear_filters();
 				for (const String &E : extensions) {
-					file->add_filter("*." + E + " ; " + E.to_upper());
+					file->add_filter("*." + E, E.to_upper());
 				}
 
 				file->popup_file_dialog();

+ 1 - 1
editor/scene_tree_dock.cpp

@@ -910,7 +910,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 			ResourceSaver::get_recognized_extensions(sd, &extensions);
 			new_scene_from_dialog->clear_filters();
 			for (int i = 0; i < extensions.size(); i++) {
-				new_scene_from_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
+				new_scene_from_dialog->add_filter("*." + extensions[i], extensions[i].to_upper());
 			}
 
 			String existing;

+ 7 - 3
scene/gui/file_dialog.cpp

@@ -673,9 +673,13 @@ void FileDialog::clear_filters() {
 	invalidate();
 }
 
-void FileDialog::add_filter(const String &p_filter) {
+void FileDialog::add_filter(const String &p_filter, const String &p_description) {
 	ERR_FAIL_COND_MSG(p_filter.begins_with("."), "Filter must be \"filename.extension\", can't start with dot.");
-	filters.push_back(p_filter);
+	if (p_description.is_empty()) {
+		filters.push_back(p_filter);
+	} else {
+		filters.push_back(vformat("%s ; %s", p_filter, p_description));
+	}
 	update_filters();
 	invalidate();
 }
@@ -919,7 +923,7 @@ void FileDialog::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_cancel_pressed"), &FileDialog::_cancel_pressed);
 
 	ClassDB::bind_method(D_METHOD("clear_filters"), &FileDialog::clear_filters);
-	ClassDB::bind_method(D_METHOD("add_filter", "filter"), &FileDialog::add_filter);
+	ClassDB::bind_method(D_METHOD("add_filter", "filter", "description"), &FileDialog::add_filter, DEFVAL(""));
 	ClassDB::bind_method(D_METHOD("set_filters", "filters"), &FileDialog::set_filters);
 	ClassDB::bind_method(D_METHOD("get_filters"), &FileDialog::get_filters);
 	ClassDB::bind_method(D_METHOD("get_current_dir"), &FileDialog::get_current_dir);

+ 1 - 1
scene/gui/file_dialog.h

@@ -151,7 +151,7 @@ protected:
 public:
 	void popup_file_dialog();
 	void clear_filters();
-	void add_filter(const String &p_filter);
+	void add_filter(const String &p_filter, const String &p_description = "");
 	void set_filters(const Vector<String> &p_filters);
 	Vector<String> get_filters() const;