1/**************************************************************************/
2/* import_dock.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
31#include "import_dock.h"
32
33#include "core/config/project_settings.h"
34#include "editor/editor_node.h"
35#include "editor/editor_resource_preview.h"
36#include "editor/editor_scale.h"
37#include "editor/editor_settings.h"
38#include "editor/editor_string_names.h"
39#include "editor/editor_undo_redo_manager.h"
40
41class ImportDockParameters : public Object {
42 GDCLASS(ImportDockParameters, Object);
43
44public:
45 HashMap<StringName, Variant> values;
46 List<PropertyInfo> properties;
47 Ref<ResourceImporter> importer;
48 Vector<String> paths;
49 HashSet<StringName> checked;
50 bool checking;
51 String base_options_path;
52
53 bool _set(const StringName &p_name, const Variant &p_value) {
54 if (values.has(p_name)) {
55 values[p_name] = p_value;
56 if (checking) {
57 checked.insert(p_name);
58 notify_property_list_changed();
59 }
60 return true;
61 }
62
63 return false;
64 }
65
66 bool _get(const StringName &p_name, Variant &r_ret) const {
67 if (values.has(p_name)) {
68 r_ret = values[p_name];
69 return true;
70 }
71
72 return false;
73 }
74 void _get_property_list(List<PropertyInfo> *p_list) const {
75 for (const PropertyInfo &E : properties) {
76 if (!importer->get_option_visibility(base_options_path, E.name, values)) {
77 continue;
78 }
79 PropertyInfo pi = E;
80 if (checking) {
81 pi.usage |= PROPERTY_USAGE_CHECKABLE;
82 if (checked.has(E.name)) {
83 pi.usage |= PROPERTY_USAGE_CHECKED;
84 }
85 }
86 p_list->push_back(pi);
87 }
88 }
89
90 void update() {
91 notify_property_list_changed();
92 }
93
94 ImportDockParameters() {
95 checking = false;
96 }
97};
98
99ImportDock *ImportDock::singleton = nullptr;
100
101void ImportDock::set_edit_path(const String &p_path) {
102 Ref<ConfigFile> config;
103 config.instantiate();
104 Error err = config->load(p_path + ".import");
105 if (err != OK) {
106 clear();
107 return;
108 }
109
110 String importer_name = config->get_value("remap", "importer");
111
112 params->importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
113
114 params->paths.clear();
115 params->paths.push_back(p_path);
116 params->base_options_path = p_path;
117
118 _update_options(p_path, config);
119
120 List<Ref<ResourceImporter>> importers;
121 ResourceFormatImporter::get_singleton()->get_importers_for_extension(p_path.get_extension(), &importers);
122 List<Pair<String, String>> importer_names;
123
124 for (const Ref<ResourceImporter> &E : importers) {
125 importer_names.push_back(Pair<String, String>(E->get_visible_name(), E->get_importer_name()));
126 }
127
128 importer_names.sort_custom<PairSort<String, String>>();
129
130 import_as->clear();
131
132 for (const Pair<String, String> &E : importer_names) {
133 import_as->add_item(E.first);
134 import_as->set_item_metadata(-1, E.second);
135 if (E.second == importer_name) {
136 import_as->select(import_as->get_item_count() - 1);
137 }
138 }
139
140 _add_keep_import_option(importer_name);
141
142 import->set_disabled(false);
143 _set_dirty(false);
144 import_as->set_disabled(false);
145 preset->set_disabled(false);
146 content->show();
147 select_a_resource->hide();
148
149 imported->set_text(p_path.get_file());
150}
151
152void ImportDock::_add_keep_import_option(const String &p_importer_name) {
153 import_as->add_separator();
154 import_as->add_item(TTR("Keep File (No Import)"));
155 import_as->set_item_metadata(-1, "keep");
156 if (p_importer_name == "keep") {
157 import_as->select(import_as->get_item_count() - 1);
158 }
159}
160
161void ImportDock::_update_options(const String &p_path, const Ref<ConfigFile> &p_config) {
162 // Set the importer class to fetch the correct class in the XML class reference.
163 // This allows tooltips to display when hovering properties.
164 if (params->importer != nullptr) {
165 // Null check to avoid crashing if the "Keep File (No Import)" mode is selected.
166 import_opts->set_object_class(params->importer->get_class_name());
167 }
168
169 List<ResourceImporter::ImportOption> options;
170
171 if (params->importer.is_valid()) {
172 params->importer->get_import_options(p_path, &options);
173 }
174
175 params->properties.clear();
176 params->values.clear();
177 params->checking = params->paths.size() > 1;
178 params->checked.clear();
179 params->base_options_path = p_path;
180
181 for (const ResourceImporter::ImportOption &E : options) {
182 params->properties.push_back(E.option);
183 if (p_config.is_valid() && p_config->has_section_key("params", E.option.name)) {
184 params->values[E.option.name] = p_config->get_value("params", E.option.name);
185 } else {
186 params->values[E.option.name] = E.default_value;
187 }
188 }
189
190 params->update();
191 _update_preset_menu();
192
193 if (params->importer.is_valid() && params->paths.size() == 1 && params->importer->has_advanced_options()) {
194 advanced->show();
195 advanced_spacer->show();
196 } else {
197 advanced->hide();
198 advanced_spacer->hide();
199 }
200}
201
202void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) {
203 clear();
204
205 // Use the value that is repeated the most.
206 HashMap<String, Dictionary> value_frequency;
207 HashSet<String> extensions;
208
209 for (int i = 0; i < p_paths.size(); i++) {
210 Ref<ConfigFile> config;
211 config.instantiate();
212 extensions.insert(p_paths[i].get_extension());
213 Error err = config->load(p_paths[i] + ".import");
214 ERR_CONTINUE(err != OK);
215
216 if (i == 0) {
217 params->importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(config->get_value("remap", "importer"));
218 if (params->importer.is_null()) {
219 clear();
220 return;
221 }
222 }
223
224 if (!config->has_section("params")) {
225 continue;
226 }
227
228 List<String> keys;
229 config->get_section_keys("params", &keys);
230
231 for (const String &E : keys) {
232 if (!value_frequency.has(E)) {
233 value_frequency[E] = Dictionary();
234 }
235
236 Variant value = config->get_value("params", E);
237
238 if (value_frequency[E].has(value)) {
239 value_frequency[E][value] = int(value_frequency[E][value]) + 1;
240 } else {
241 value_frequency[E][value] = 1;
242 }
243 }
244 }
245
246 ERR_FAIL_COND(params->importer.is_null());
247
248 String base_path;
249 if (extensions.size() == 1 && p_paths.size() > 0) {
250 base_path = p_paths[0];
251 }
252 List<ResourceImporter::ImportOption> options;
253 params->importer->get_import_options(base_path, &options);
254
255 params->properties.clear();
256 params->values.clear();
257 params->checking = true;
258 params->checked.clear();
259 params->base_options_path = base_path;
260
261 for (const ResourceImporter::ImportOption &E : options) {
262 params->properties.push_back(E.option);
263
264 if (value_frequency.has(E.option.name)) {
265 Dictionary d = value_frequency[E.option.name];
266 int freq = 0;
267 List<Variant> v;
268 d.get_key_list(&v);
269 Variant value;
270 for (const Variant &F : v) {
271 int f = d[F];
272 if (f > freq) {
273 value = F;
274 }
275 }
276
277 params->values[E.option.name] = value;
278 } else {
279 params->values[E.option.name] = E.default_value;
280 }
281 }
282
283 params->update();
284
285 List<Ref<ResourceImporter>> importers;
286 ResourceFormatImporter::get_singleton()->get_importers_for_extension(p_paths[0].get_extension(), &importers);
287 List<Pair<String, String>> importer_names;
288
289 for (const Ref<ResourceImporter> &E : importers) {
290 importer_names.push_back(Pair<String, String>(E->get_visible_name(), E->get_importer_name()));
291 }
292
293 importer_names.sort_custom<PairSort<String, String>>();
294
295 import_as->clear();
296
297 for (const Pair<String, String> &E : importer_names) {
298 import_as->add_item(E.first);
299 import_as->set_item_metadata(-1, E.second);
300 if (E.second == params->importer->get_importer_name()) {
301 import_as->select(import_as->get_item_count() - 1);
302 }
303 }
304
305 _add_keep_import_option(params->importer->get_importer_name());
306
307 _update_preset_menu();
308
309 params->paths = p_paths;
310 import->set_disabled(false);
311 _set_dirty(false);
312 import_as->set_disabled(false);
313 preset->set_disabled(false);
314 content->show();
315 select_a_resource->hide();
316
317 imported->set_text(vformat(TTR("%d Files"), p_paths.size()));
318
319 if (params->paths.size() == 1 && params->importer->has_advanced_options()) {
320 advanced->show();
321 advanced_spacer->show();
322 } else {
323 advanced->hide();
324 advanced_spacer->hide();
325 }
326}
327
328void ImportDock::reimport_resources(const Vector<String> &p_paths) {
329 switch (p_paths.size()) {
330 case 0:
331 ERR_FAIL_MSG("You need to select files to reimport them.");
332 case 1:
333 set_edit_path(p_paths[0]);
334 break;
335 default:
336 set_edit_multiple_paths(p_paths);
337 break;
338 }
339
340 _reimport_attempt();
341}
342
343void ImportDock::_update_preset_menu() {
344 preset->get_popup()->clear();
345
346 if (params->importer.is_null()) {
347 preset->get_popup()->add_item(TTR("Default"));
348 preset->hide();
349 return;
350 }
351 preset->show();
352
353 if (params->importer->get_preset_count() == 0) {
354 preset->get_popup()->add_item(TTR("Default"));
355 } else {
356 for (int i = 0; i < params->importer->get_preset_count(); i++) {
357 preset->get_popup()->add_item(params->importer->get_preset_name(i));
358 }
359 }
360
361 preset->get_popup()->add_separator();
362 preset->get_popup()->add_item(vformat(TTR("Set as Default for '%s'"), params->importer->get_visible_name()), ITEM_SET_AS_DEFAULT);
363 if (ProjectSettings::get_singleton()->has_setting("importer_defaults/" + params->importer->get_importer_name())) {
364 preset->get_popup()->add_item(TTR("Load Default"), ITEM_LOAD_DEFAULT);
365 preset->get_popup()->add_separator();
366 preset->get_popup()->add_item(vformat(TTR("Clear Default for '%s'"), params->importer->get_visible_name()), ITEM_CLEAR_DEFAULT);
367 }
368}
369
370void ImportDock::_importer_selected(int i_idx) {
371 String name = import_as->get_selected_metadata();
372 if (name == "keep") {
373 params->importer.unref();
374 _update_options(params->base_options_path, Ref<ConfigFile>());
375 } else {
376 Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(name);
377 ERR_FAIL_COND(importer.is_null());
378
379 params->importer = importer;
380 Ref<ConfigFile> config;
381 if (params->paths.size()) {
382 String path = params->paths[0];
383 config.instantiate();
384 Error err = config->load(path + ".import");
385 if (err != OK) {
386 config.unref();
387 }
388 }
389 _update_options(params->base_options_path, config);
390 }
391}
392
393void ImportDock::_preset_selected(int p_idx) {
394 int item_id = preset->get_popup()->get_item_id(p_idx);
395
396 switch (item_id) {
397 case ITEM_SET_AS_DEFAULT: {
398 Dictionary d;
399
400 for (const PropertyInfo &E : params->properties) {
401 d[E.name] = params->values[E.name];
402 }
403
404 ProjectSettings::get_singleton()->set("importer_defaults/" + params->importer->get_importer_name(), d);
405 ProjectSettings::get_singleton()->save();
406 _update_preset_menu();
407 } break;
408 case ITEM_LOAD_DEFAULT: {
409 ERR_FAIL_COND(!ProjectSettings::get_singleton()->has_setting("importer_defaults/" + params->importer->get_importer_name()));
410
411 Dictionary d = GLOBAL_GET("importer_defaults/" + params->importer->get_importer_name());
412 List<Variant> v;
413 d.get_key_list(&v);
414
415 if (params->checking) {
416 params->checked.clear();
417 }
418 for (const Variant &E : v) {
419 params->values[E] = d[E];
420 if (params->checking) {
421 params->checked.insert(E);
422 }
423 }
424 params->update();
425 } break;
426 case ITEM_CLEAR_DEFAULT: {
427 ProjectSettings::get_singleton()->set("importer_defaults/" + params->importer->get_importer_name(), Variant());
428 ProjectSettings::get_singleton()->save();
429 _update_preset_menu();
430 } break;
431 default: {
432 List<ResourceImporter::ImportOption> options;
433
434 params->importer->get_import_options(params->base_options_path, &options, p_idx);
435
436 if (params->checking) {
437 params->checked.clear();
438 }
439 for (const ResourceImporter::ImportOption &E : options) {
440 params->values[E.option.name] = E.default_value;
441 if (params->checking) {
442 params->checked.insert(E.option.name);
443 }
444 }
445 params->update();
446 } break;
447 }
448}
449
450void ImportDock::clear() {
451 imported->set_text("");
452 import->set_disabled(true);
453 import_as->clear();
454 import_as->set_disabled(true);
455 preset->set_disabled(true);
456 params->values.clear();
457 params->properties.clear();
458 params->update();
459 preset->get_popup()->clear();
460 content->hide();
461 select_a_resource->show();
462}
463
464static bool _find_owners(EditorFileSystemDirectory *efsd, const String &p_path) {
465 if (!efsd) {
466 return false;
467 }
468
469 for (int i = 0; i < efsd->get_subdir_count(); i++) {
470 if (_find_owners(efsd->get_subdir(i), p_path)) {
471 return true;
472 }
473 }
474
475 for (int i = 0; i < efsd->get_file_count(); i++) {
476 Vector<String> deps = efsd->get_file_deps(i);
477 if (deps.has(p_path)) {
478 return true;
479 }
480 }
481
482 return false;
483}
484
485void ImportDock::_reimport_attempt() {
486 bool used_in_resources = false;
487
488 String importer_name;
489 if (params->importer.is_valid()) {
490 importer_name = params->importer->get_importer_name();
491 } else {
492 importer_name = "keep";
493 }
494 for (int i = 0; i < params->paths.size(); i++) {
495 Ref<ConfigFile> config;
496 config.instantiate();
497 Error err = config->load(params->paths[i] + ".import");
498 ERR_CONTINUE(err != OK);
499
500 String imported_with = config->get_value("remap", "importer");
501 if (imported_with != importer_name) {
502 need_cleanup.push_back(params->paths[i]);
503 if (_find_owners(EditorFileSystem::get_singleton()->get_filesystem(), params->paths[i])) {
504 used_in_resources = true;
505 }
506 }
507 }
508
509 if (!need_cleanup.is_empty() || used_in_resources) {
510 cleanup_warning->set_visible(!need_cleanup.is_empty());
511 label_warning->set_visible(used_in_resources);
512 reimport_confirm->popup_centered();
513 return;
514 }
515
516 _reimport();
517}
518
519void ImportDock::_reimport_and_cleanup() {
520 HashMap<String, Ref<Resource>> old_resources;
521
522 for (const String &path : need_cleanup) {
523 Ref<Resource> res = ResourceLoader::load(path);
524 res->set_path("");
525 res->set_meta(SNAME("_skip_save_"), true);
526 old_resources[path] = res;
527 }
528
529 EditorResourcePreview::get_singleton()->stop(); // Don't try to re-create previews after import.
530 _reimport();
531
532 if (need_cleanup.is_empty()) {
533 return;
534 }
535
536 // After changing resource type we need to make sure that all old instances are unloaded or replaced.
537 EditorNode::get_singleton()->push_item(nullptr);
538 EditorUndoRedoManager::get_singleton()->clear_history();
539
540 List<Ref<Resource>> external_resources;
541 ResourceCache::get_cached_resources(&external_resources);
542
543 for (const String &path : need_cleanup) {
544 Ref<Resource> old_res = old_resources[path];
545 Ref<Resource> new_res = ResourceLoader::load(path);
546
547 for (int i = 0; i < EditorNode::get_editor_data().get_edited_scene_count(); i++) {
548 Node *edited_scene_root = EditorNode::get_editor_data().get_edited_scene_root(i);
549 if (likely(edited_scene_root)) {
550 _replace_resource_in_object(edited_scene_root, old_res, new_res);
551 }
552 }
553 for (Ref<Resource> res : external_resources) {
554 _replace_resource_in_object(res.ptr(), old_res, new_res);
555 }
556 }
557 need_cleanup.clear();
558}
559
560void ImportDock::_advanced_options() {
561 if (params->paths.size() == 1 && params->importer.is_valid()) {
562 params->importer->show_advanced_options(params->paths[0]);
563 }
564}
565void ImportDock::_reimport() {
566 for (int i = 0; i < params->paths.size(); i++) {
567 Ref<ConfigFile> config;
568 config.instantiate();
569 Error err = config->load(params->paths[i] + ".import");
570 ERR_CONTINUE(err != OK);
571
572 if (params->importer.is_valid()) {
573 String importer_name = params->importer->get_importer_name();
574
575 if (params->checking && config->get_value("remap", "importer") == params->importer->get_importer_name()) {
576 //update only what is edited (checkboxes) if the importer is the same
577 for (const PropertyInfo &E : params->properties) {
578 if (params->checked.has(E.name)) {
579 config->set_value("params", E.name, params->values[E.name]);
580 }
581 }
582 } else {
583 //override entirely
584 config->set_value("remap", "importer", importer_name);
585 if (config->has_section("params")) {
586 config->erase_section("params");
587 }
588
589 for (const PropertyInfo &E : params->properties) {
590 config->set_value("params", E.name, params->values[E.name]);
591 }
592 }
593
594 //handle group file
595 Ref<ResourceImporter> importer = ResourceFormatImporter::get_singleton()->get_importer_by_name(importer_name);
596 ERR_CONTINUE(!importer.is_valid());
597 String group_file_property = importer->get_option_group_file();
598 if (!group_file_property.is_empty()) {
599 //can import from a group (as in, atlas)
600 ERR_CONTINUE(!params->values.has(group_file_property));
601 String group_file = params->values[group_file_property];
602 config->set_value("remap", "group_file", group_file);
603 } else {
604 config->set_value("remap", "group_file", Variant()); //clear group file if unused
605 }
606
607 } else {
608 //set to no import
609 config->clear();
610 config->set_value("remap", "importer", "keep");
611 }
612
613 config->save(params->paths[i] + ".import");
614 }
615
616 EditorFileSystem::get_singleton()->reimport_files(params->paths);
617 EditorFileSystem::get_singleton()->emit_signal(SNAME("filesystem_changed")); //it changed, so force emitting the signal
618
619 _set_dirty(false);
620}
621
622void ImportDock::_replace_resource_in_object(Object *p_object, const Ref<Resource> &old_resource, const Ref<Resource> &new_resource) {
623 ERR_FAIL_NULL(p_object);
624
625 List<PropertyInfo> props;
626 p_object->get_property_list(&props);
627
628 for (const PropertyInfo &p : props) {
629 if (p.type != Variant::OBJECT || p.hint != PROPERTY_HINT_RESOURCE_TYPE) {
630 continue;
631 }
632
633 Ref<Resource> res = p_object->get(p.name);
634 if (res.is_null()) {
635 continue;
636 }
637
638 if (res == old_resource) {
639 p_object->set(p.name, new_resource);
640 } else {
641 _replace_resource_in_object(res.ptr(), old_resource, new_resource);
642 }
643 }
644
645 Node *n = Object::cast_to<Node>(p_object);
646 if (n) {
647 for (int i = 0; i < n->get_child_count(); i++) {
648 _replace_resource_in_object(n->get_child(i), old_resource, new_resource);
649 }
650 }
651}
652
653void ImportDock::_notification(int p_what) {
654 switch (p_what) {
655 case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
656 imported->add_theme_style_override("normal", get_theme_stylebox(SNAME("normal"), SNAME("LineEdit")));
657 } break;
658
659 case NOTIFICATION_ENTER_TREE: {
660 import_opts->edit(params);
661 label_warning->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
662 } break;
663 }
664}
665
666void ImportDock::_property_edited(const StringName &p_prop) {
667 _set_dirty(true);
668}
669
670void ImportDock::_set_dirty(bool p_dirty) {
671 if (p_dirty) {
672 // Add a dirty marker to notify the user that they should reimport the selected resource to see changes.
673 import->set_text(TTR("Reimport") + " (*)");
674 import->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
675 import->set_tooltip_text(TTR("You have pending changes that haven't been applied yet. Click Reimport to apply changes made to the import options.\nSelecting another resource in the FileSystem dock without clicking Reimport first will discard changes made in the Import dock."));
676 } else {
677 // Remove the dirty marker on the Reimport button.
678 import->set_text(TTR("Reimport"));
679 import->remove_theme_color_override("font_color");
680 import->set_tooltip_text("");
681 }
682}
683
684void ImportDock::_property_toggled(const StringName &p_prop, bool p_checked) {
685 if (p_checked) {
686 params->checked.insert(p_prop);
687 } else {
688 params->checked.erase(p_prop);
689 }
690}
691
692void ImportDock::_bind_methods() {
693 ClassDB::bind_method(D_METHOD("_reimport"), &ImportDock::_reimport);
694}
695
696void ImportDock::initialize_import_options() const {
697 ERR_FAIL_COND(!import_opts || !params);
698
699 import_opts->edit(params);
700}
701
702ImportDock::ImportDock() {
703 singleton = this;
704 set_name("Import");
705
706 content = memnew(VBoxContainer);
707 content->set_v_size_flags(SIZE_EXPAND_FILL);
708 add_child(content);
709 content->hide();
710
711 imported = memnew(Label);
712 imported->add_theme_style_override("normal", EditorNode::get_singleton()->get_editor_theme()->get_stylebox(SNAME("normal"), SNAME("LineEdit")));
713 imported->set_clip_text(true);
714 content->add_child(imported);
715 HBoxContainer *hb = memnew(HBoxContainer);
716 content->add_margin_child(TTR("Import As:"), hb);
717 import_as = memnew(OptionButton);
718 import_as->set_disabled(true);
719 import_as->set_fit_to_longest_item(false);
720 import_as->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);
721 import_as->set_h_size_flags(SIZE_EXPAND_FILL);
722 import_as->connect("item_selected", callable_mp(this, &ImportDock::_importer_selected));
723 hb->add_child(import_as);
724 import_as->set_h_size_flags(SIZE_EXPAND_FILL);
725 preset = memnew(MenuButton);
726 preset->set_text(TTR("Preset"));
727 preset->set_disabled(true);
728 preset->get_popup()->connect("index_pressed", callable_mp(this, &ImportDock::_preset_selected));
729 hb->add_child(preset);
730
731 import_opts = memnew(EditorInspector);
732 content->add_child(import_opts);
733 import_opts->set_v_size_flags(SIZE_EXPAND_FILL);
734 import_opts->connect("property_edited", callable_mp(this, &ImportDock::_property_edited));
735 import_opts->connect("property_toggled", callable_mp(this, &ImportDock::_property_toggled));
736 // Make it possible to display tooltips stored in the XML class reference.
737 // The object name is set when the importer changes in `_update_options()`.
738 import_opts->set_use_doc_hints(true);
739
740 hb = memnew(HBoxContainer);
741 content->add_child(hb);
742 import = memnew(Button);
743 import->set_text(TTR("Reimport"));
744 import->set_disabled(true);
745 import->connect("pressed", callable_mp(this, &ImportDock::_reimport_attempt));
746 if (!DisplayServer::get_singleton()->get_swap_cancel_ok()) {
747 advanced_spacer = hb->add_spacer();
748 advanced = memnew(Button);
749 advanced->set_text(TTR("Advanced..."));
750 hb->add_child(advanced);
751 }
752 hb->add_spacer();
753 hb->add_child(import);
754 hb->add_spacer();
755
756 if (DisplayServer::get_singleton()->get_swap_cancel_ok()) {
757 advanced = memnew(Button);
758 advanced->set_text(TTR("Advanced..."));
759 hb->add_child(advanced);
760 advanced_spacer = hb->add_spacer();
761 }
762
763 advanced->hide();
764 advanced_spacer->hide();
765 advanced->connect("pressed", callable_mp(this, &ImportDock::_advanced_options));
766
767 reimport_confirm = memnew(ConfirmationDialog);
768 content->add_child(reimport_confirm);
769 reimport_confirm->connect("confirmed", callable_mp(this, &ImportDock::_reimport_and_cleanup));
770
771 VBoxContainer *vbc_confirm = memnew(VBoxContainer());
772 cleanup_warning = memnew(Label(TTR("The imported resource is currently loaded. All instances will be replaced and undo history will be cleared.")));
773 vbc_confirm->add_child(cleanup_warning);
774 label_warning = memnew(Label(TTR("WARNING: Assets exist that use this resource. They may stop loading properly after changing type.")));
775 vbc_confirm->add_child(label_warning);
776 reimport_confirm->add_child(vbc_confirm);
777
778 params = memnew(ImportDockParameters);
779
780 select_a_resource = memnew(Label);
781 select_a_resource->set_text(TTR("Select a resource file in the filesystem or in the inspector to adjust import settings."));
782 select_a_resource->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
783 select_a_resource->set_custom_minimum_size(Size2(100 * EDSCALE, 0));
784 select_a_resource->set_v_size_flags(SIZE_EXPAND_FILL);
785 select_a_resource->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
786 select_a_resource->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
787 add_child(select_a_resource);
788}
789
790ImportDock::~ImportDock() {
791 singleton = nullptr;
792 memdelete(params);
793}
794