Repository: godotengine/godot
File: editor/plugins/gpu_particles_2d_editor_plugin.cpp (C++)

Code before:
	}
}

void GPUParticles2DEditorPlugin::_file_selected(const String &p_file) {
	source_emission_file = p_file;
	emission_mask->popup_centered();
}

void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
	switch (p_idx) {
		case MENU_GENERATE_VISIBILITY_RECT: {
			// Add one second to the default generation lifetime, since the progress is updated every second.
			generate_seconds->set_value(MAX(1.0, trunc(particles->get_lifetime()) + 1.0));

			if (generate_seconds->get_value() >= 11.0 + CMP_EPSILON) {
				// Only pop up the time dialog if the particle's lifetime is long enough to warrant shortening it.
				generate_visibility_rect->popup_centered();
			} else {
				// Generate the visibility rect immediately.
				_generate_visibility_rect();
			}
		} break;
		case MENU_LOAD_EMISSION_MASK: {
			file->popup_file_dialog();

		} break;
		case MENU_CLEAR_EMISSION_MASK: {
			emission_mask->popup_centered();
		} break;
		case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: {
			CPUParticles2D *cpu_particles = memnew(CPUParticles2D);
			cpu_particles->convert_from_particles(particles);
			cpu_particles->set_name(particles->get_name());
			cpu_particles->set_transform(particles->get_transform());
			cpu_particles->set_visible(particles->is_visible());
			cpu_particles->set_process_mode(particles->get_process_mode());
			cpu_particles->set_z_index(particles->get_z_index());

			UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
			ur->create_action(TTR("Convert to CPUParticles2D"));
			ur->add_do_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", particles, cpu_particles, true, false);
			ur->add_do_reference(cpu_particles);
			ur->add_undo_method(EditorNode::get_singleton()->get_scene_tree_dock(), "replace_node", cpu_particles, particles, false, false);
			ur->add_undo_reference(particles);
			ur->commit_action();

		} break;
		case MENU_RESTART: {
			particles->restart();
		}
	}

Review comment (performance):
Doing an inheritance check by string is slow, it's much better to rely on casting, which is what you do anyway later on (btw it's usually `Object::cast_to`, not `Node::cast_to`):
```suggestion
			GPUParticles2D *selected_particle = Object::cast_to<GPUParticles2D>(P);
			if (selected_particle) {
```

Code after:
	}
}

void GPUParticles2DEditorPlugin::_file_selected(const String &p_file) {
	source_emission_file = p_file;
	emission_mask->popup_centered();
}

void GPUParticles2DEditorPlugin::_selection_changed() {
	List<Node *> selected_nodes = editor->get_editor_selection()->get_selected_node_list();

	if (selected_particles.is_empty() && selected_nodes.is_empty()) {
		return;
	}

	for (GPUParticles2D *SP : selected_particles) {
		SP->set_show_visibility_rect(false);
	}
	selected_particles.clear();

	for (Node *P : selected_nodes) {
		GPUParticles2D *selected_particle = Object::cast_to<GPUParticles2D>(P);
		if (selected_particle != nullptr) {
			selected_particle->set_show_visibility_rect(true);
			selected_particles.push_back(selected_particle);
		}
	}
}

void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
	switch (p_idx) {
		case MENU_GENERATE_VISIBILITY_RECT: {
			// Add one second to the default generation lifetime, since the progress is updated every second.
			generate_seconds->set_value(MAX(1.0, trunc(particles->get_lifetime()) + 1.0));

			if (generate_seconds->get_value() >= 11.0 + CMP_EPSILON) {
				// Only pop up the time dialog if the particle's lifetime is long enough to warrant shortening it.
				generate_visibility_rect->popup_centered();
			} else {
				// Generate the visibility rect immediately.
				_generate_visibility_rect();
			}
		} break;
		case MENU_LOAD_EMISSION_MASK: {
			file->popup_file_dialog();

		} break;
		case MENU_CLEAR_EMISSION_MASK: {
			emission_mask->popup_centered();
		} break;
		case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: {