diff options
Diffstat (limited to 'editor')
64 files changed, 1446 insertions, 637 deletions
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 3997469e95..f65825e395 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1253,14 +1253,14 @@ void AnimationTrackEdit::_notification(int p_what) { float offset = animation->track_get_key_time(track, i) - timeline->get_value(); if (editor->is_key_selected(track, i) && editor->is_moving_selection()) { - offset += editor->get_moving_selection_offset(); + offset = editor->snap_time(offset + editor->get_moving_selection_offset()); } offset = offset * scale + limit; if (i < animation->track_get_key_count(track) - 1) { float offset_n = animation->track_get_key_time(track, i + 1) - timeline->get_value(); if (editor->is_key_selected(track, i + 1) && editor->is_moving_selection()) { - offset_n += editor->get_moving_selection_offset(); + offset_n = editor->snap_time(offset_n + editor->get_moving_selection_offset()); } offset_n = offset_n * scale + limit; @@ -1685,15 +1685,10 @@ void AnimationTrackEdit::_zoom_changed() { } void AnimationTrackEdit::_path_entered(const String &p_text) { - - *block_animation_update_ptr = true; undo_redo->create_action("Change Track Path"); undo_redo->add_do_method(animation.ptr(), "track_set_path", track, p_text); undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track)); undo_redo->commit_action(); - *block_animation_update_ptr = false; - update(); - path->hide(); } String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { @@ -3192,7 +3187,8 @@ int AnimationTrackEditor::_confirm_insert(InsertData p_id, int p_last_track, boo case Animation::TYPE_ANIMATION: { value = p_id.value; } break; - default: {} + default: { + } } undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_id.track_idx, time, value); @@ -3881,9 +3877,7 @@ void AnimationTrackEditor::_move_selection_begin() { void AnimationTrackEditor::_move_selection(float p_offset) { moving_selection_offset = p_offset; - if (snap->is_pressed() && step->get_value() != 0) { - moving_selection_offset = Math::stepify(moving_selection_offset, step->get_value()); - } + for (int i = 0; i < track_edits.size(); i++) { track_edits[i]->update(); } @@ -4003,7 +3997,7 @@ void AnimationTrackEditor::_move_selection_commit() { // 2- remove overlapped keys for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { - float newtime = E->get().pos + motion; + float newtime = snap_time(E->get().pos + motion); int idx = animation->track_find_key(E->key().track, newtime, true); if (idx == -1) continue; @@ -4027,7 +4021,7 @@ void AnimationTrackEditor::_move_selection_commit() { // 3-move the keys (re insert them) for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { - float newpos = E->get().pos + motion; + float newpos = snap_time(E->get().pos + motion); /* if (newpos<0) continue; //no add at the beginning @@ -4038,7 +4032,7 @@ void AnimationTrackEditor::_move_selection_commit() { // 4-(undo) remove inserted keys for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { - float newpos = E->get().pos + motion; + float newpos = snap_time(E->get().pos + motion); /* if (newpos<0) continue; //no remove what no inserted @@ -4074,7 +4068,7 @@ void AnimationTrackEditor::_move_selection_commit() { for (Map<SelectedKey, KeyInfo>::Element *E = selection.back(); E; E = E->prev()) { float oldpos = E->get().pos; - float newpos = oldpos + motion; + float newpos = snap_time(oldpos + motion); //if (newpos>=0) undo_redo->add_do_method(this, "_select_at_anim", animation, E->key().track, newpos); undo_redo->add_undo_method(this, "_select_at_anim", animation, E->key().track, oldpos); @@ -4351,7 +4345,8 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { case Animation::TYPE_METHOD: text += " (Methods)"; break; case Animation::TYPE_BEZIER: text += " (Bezier)"; break; case Animation::TYPE_AUDIO: text += " (Audio)"; break; - default: {}; + default: { + }; } TreeItem *it = track_copy_select->create_item(troot); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index aeb304d3b9..3136b0f012 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1060,7 +1060,7 @@ void CodeTextEditor::delete_lines() { text_editor->end_complex_operation(); } -void CodeTextEditor::code_lines_down() { +void CodeTextEditor::clone_lines_down() { int from_line = text_editor->cursor_get_line(); int to_line = text_editor->cursor_get_line(); int column = text_editor->cursor_get_column(); @@ -1072,22 +1072,21 @@ void CodeTextEditor::code_lines_down() { } int next_line = to_line + 1; - if (to_line >= text_editor->get_line_count() - 1) { - text_editor->set_line(to_line, text_editor->get_line(to_line) + "\n"); - } - + bool caret_at_start = text_editor->cursor_get_line() == from_line; text_editor->begin_complex_operation(); for (int i = from_line; i <= to_line; i++) { - text_editor->unfold_line(i); - if (i >= text_editor->get_line_count() - 1) { - text_editor->set_line(i, text_editor->get_line(i) + "\n"); - } - String line_clone = text_editor->get_line(i); - text_editor->insert_at(line_clone, next_line); + text_editor->set_line(next_line - 1, text_editor->get_line(next_line - 1) + "\n"); + text_editor->set_line(next_line, text_editor->get_line(i)); next_line++; } + if (caret_at_start) { + text_editor->cursor_set_line(to_line + 1); + } else { + text_editor->cursor_set_line(next_line - 1); + } + text_editor->cursor_set_column(column); if (text_editor->is_selection_active()) { text_editor->select(to_line + 1, text_editor->get_selection_from_column(), next_line - 1, text_editor->get_selection_to_column()); diff --git a/editor/code_editor.h b/editor/code_editor.h index ee47eff9a8..2f9403843e 100644 --- a/editor/code_editor.h +++ b/editor/code_editor.h @@ -203,7 +203,7 @@ public: void move_lines_up(); void move_lines_down(); void delete_lines(); - void code_lines_down(); + void clone_lines_down(); void goto_line(int p_line); void goto_line_selection(int p_line, int p_begin, int p_end); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index c4516c1f17..926fa37040 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -118,8 +118,10 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode) { if (enable_rl) { search_options->add_constant_override("draw_relationship_lines", 1); search_options->add_color_override("relationship_line_color", rl_color); + search_options->add_constant_override("draw_guides", 0); } else { search_options->add_constant_override("draw_relationship_lines", 0); + search_options->add_constant_override("draw_guides", 1); } is_replace_mode = p_replace_mode; diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index d64b02a605..99b6955160 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -557,8 +557,9 @@ DependencyRemoveDialog::DependencyRemoveDialog() { ////////////// -void DependencyErrorDialog::show(const String &p_for_file, const Vector<String> &report) { +void DependencyErrorDialog::show(Mode p_mode, const String &p_for_file, const Vector<String> &report) { + mode = p_mode; for_file = p_for_file; set_title(TTR("Error loading:") + " " + p_for_file.get_file()); files->clear(); @@ -584,7 +585,14 @@ void DependencyErrorDialog::show(const String &p_for_file, const Vector<String> void DependencyErrorDialog::ok_pressed() { - EditorNode::get_singleton()->load_scene(for_file, true); + switch (mode) { + case MODE_SCENE: + EditorNode::get_singleton()->load_scene(for_file, true); + break; + case MODE_RESOURCE: + EditorNode::get_singleton()->load_resource(for_file, true); + break; + } } void DependencyErrorDialog::custom_action(const String &) { @@ -599,7 +607,7 @@ DependencyErrorDialog::DependencyErrorDialog() { files = memnew(Tree); files->set_hide_root(true); - vb->add_margin_child(TTR("Scene failed to load due to missing dependencies:"), files, true); + vb->add_margin_child(TTR("Load failed due to missing dependencies:"), files, true); files->set_v_size_flags(SIZE_EXPAND_FILL); files->set_custom_minimum_size(Size2(1, 200)); get_ok()->set_text(TTR("Open Anyway")); diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h index 4f268de748..e46df4c837 100644 --- a/editor/dependency_editor.h +++ b/editor/dependency_editor.h @@ -134,7 +134,15 @@ public: class DependencyErrorDialog : public ConfirmationDialog { GDCLASS(DependencyErrorDialog, ConfirmationDialog); +public: + enum Mode { + MODE_SCENE, + MODE_RESOURCE, + }; + +private: String for_file; + Mode mode; Button *fdep; Label *text; Tree *files; @@ -142,7 +150,7 @@ class DependencyErrorDialog : public ConfirmationDialog { void custom_action(const String &); public: - void show(const String &p_for_file, const Vector<String> &report); + void show(Mode p_mode, const String &p_for_file, const Vector<String> &report); DependencyErrorDialog(); }; diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index 2f2840192a..14abaa835c 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -209,7 +209,6 @@ EditorAbout::EditorAbout() { TreeItem *tpl_ti_lc = _tpl_tree->create_item(root); tpl_ti_lc->set_text(0, TTR("Licenses")); tpl_ti_lc->set_selectable(0, false); - int read_idx = 0; String long_text = ""; for (int component_index = 0; component_index < COPYRIGHT_INFO_COUNT; component_index++) { @@ -234,7 +233,6 @@ EditorAbout::EditorAbout() { String license = "\n License: " + String(part.license) + "\n"; text += license; long_text += license + "\n"; - read_idx++; } ti->set_metadata(0, text); } diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 9420452da1..bd6ce797b5 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -946,16 +946,18 @@ void EditorData::script_class_save_icon_paths() { void EditorData::script_class_load_icon_paths() { script_class_clear_icon_paths(); - Dictionary d = ProjectSettings::get_singleton()->get("_global_script_class_icons"); - List<Variant> keys; - d.get_key_list(&keys); + if (ProjectSettings::get_singleton()->has_setting("_global_script_class_icons")) { + Dictionary d = ProjectSettings::get_singleton()->get("_global_script_class_icons"); + List<Variant> keys; + d.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - String name = E->get().operator String(); - _script_class_icon_paths[name] = d[name]; + for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + String name = E->get().operator String(); + _script_class_icon_paths[name] = d[name]; - String path = ScriptServer::get_global_class_path(name); - script_class_set_name(path, name); + String path = ScriptServer::get_global_class_path(name); + script_class_set_name(path, name); + } } } diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp index 1a6188862f..209a006a06 100644 --- a/editor/editor_export.cpp +++ b/editor/editor_export.cpp @@ -144,6 +144,17 @@ String EditorExportPreset::get_include_filter() const { return include_filter; } +void EditorExportPreset::set_export_path(const String &p_path) { + + export_path = p_path; + EditorExport::singleton->save_presets(); +} + +String EditorExportPreset::get_export_path() const { + + return export_path; +} + void EditorExportPreset::set_exclude_filter(const String &p_exclude) { exclude_filter = p_exclude; @@ -213,6 +224,7 @@ String EditorExportPreset::get_custom_features() const { EditorExportPreset::EditorExportPreset() { + export_path = ""; export_filter = EXPORT_ALL_RESOURCES; runnable = false; } @@ -1034,6 +1046,7 @@ void EditorExport::_save() { } config->set_value(section, "include_filter", preset->get_include_filter()); config->set_value(section, "exclude_filter", preset->get_exclude_filter()); + config->set_value(section, "export_path", preset->get_export_path()); config->set_value(section, "patch_list", preset->get_patches()); String option_section = "preset." + itos(i) + ".options"; @@ -1189,6 +1202,7 @@ void EditorExport::load_config() { preset->set_include_filter(config->get_value(section, "include_filter")); preset->set_exclude_filter(config->get_value(section, "exclude_filter")); + preset->set_export_path(config->get_value(section, "export_path", "")); Vector<String> patch_list = config->get_value(section, "patch_list"); @@ -1337,18 +1351,21 @@ bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset, return valid; } -String EditorExportPlatformPC::get_binary_extension(const Ref<EditorExportPreset> &p_preset) const { +List<String> EditorExportPlatformPC::get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const { + List<String> list; for (Map<String, String>::Element *E = extensions.front(); E; E = E->next()) { if (p_preset->get(E->key())) { - return extensions[E->key()]; + list.push_back(extensions[E->key()]); + return list; } } if (extensions.has("default")) { - return extensions["default"]; + list.push_back(extensions["default"]); + return list; } - return ""; + return list; } Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) { diff --git a/editor/editor_export.h b/editor/editor_export.h index b4ee5b89e7..380b33cd17 100644 --- a/editor/editor_export.h +++ b/editor/editor_export.h @@ -57,6 +57,7 @@ private: ExportFilter export_filter; String include_filter; String exclude_filter; + String export_path; String exporter; Set<String> selected_files; @@ -114,6 +115,9 @@ public: void set_custom_features(const String &p_custom_features); String get_custom_features() const; + void set_export_path(const String &p_path); + String get_export_path() const; + const List<PropertyInfo> &get_properties() const { return properties; } EditorExportPreset(); @@ -241,7 +245,7 @@ public: virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0; - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const = 0; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const = 0; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) = 0; virtual Error export_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); virtual Error export_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); @@ -388,7 +392,7 @@ public: virtual Ref<Texture> get_logo() const; virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const; - virtual String get_binary_extension(const Ref<EditorExportPreset> &p_preset) const; + virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const; virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0); void set_extension(const String &p_extension, const String &p_feature_key = "default"); diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 438d7ea306..b39f910182 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -580,7 +580,7 @@ void EditorFileDialog::_item_list_item_rmb_selected(int p_item, const Vector2 &p if (single_item_selected) { item_menu->add_separator(); Dictionary item_meta = item_list->get_item_metadata(p_item); - String item_text = item_meta["dir"] ? TTR("Open In File Manager") : TTR("Show In File Manager"); + String item_text = item_meta["dir"] ? TTR("Open in File Manager") : TTR("Show in File Manager"); item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), item_text, ITEM_MENU_SHOW_IN_EXPLORER); } @@ -605,7 +605,7 @@ void EditorFileDialog::_item_list_rmb_clicked(const Vector2 &p_pos) { } item_menu->add_icon_item(get_icon("Reload", "EditorIcons"), TTR("Refresh"), ITEM_MENU_REFRESH, KEY_F5); item_menu->add_separator(); - item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Open In File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); + item_menu->add_icon_item(get_icon("Filesystem", "EditorIcons"), TTR("Open in File Manager"), ITEM_MENU_SHOW_IN_EXPLORER); item_menu->set_position(item_list->get_global_position() + p_pos); item_menu->popup(); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index d73bf86f64..a99c9656ba 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1305,11 +1305,6 @@ void EditorFileSystem::_save_late_updated_files() { } } -void EditorFileSystem::_resource_saved(const String &p_path) { - - EditorFileSystem::get_singleton()->update_file(p_path); -} - Vector<String> EditorFileSystem::_get_dependencies(const String &p_path) { List<String> deps; @@ -1772,7 +1767,6 @@ EditorFileSystem::EditorFileSystem() { abort_scan = false; scanning_changes = false; scanning_changes_done = false; - ResourceSaver::set_save_callback(_resource_saved); DirAccess *da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (da->change_dir("res://.import") != OK) { diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 47077425a1..f6eef2a152 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -204,8 +204,6 @@ class EditorFileSystem : public Node { bool _update_scan_actions(); - static void _resource_saved(const String &p_path); - void _update_extensions(); void _reimport_file(const String &p_file); diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp new file mode 100644 index 0000000000..011fe20564 --- /dev/null +++ b/editor/editor_folding.cpp @@ -0,0 +1,175 @@ +#include "editor_folding.h" + +#include "core/os/file_access.h" +#include "editor_settings.h" + +PoolVector<String> EditorFolding::_get_unfolds(const Object *p_object) { + + PoolVector<String> sections; + sections.resize(p_object->editor_get_section_folding().size()); + if (sections.size()) { + PoolVector<String>::Write w = sections.write(); + int idx = 0; + for (const Set<String>::Element *E = p_object->editor_get_section_folding().front(); E; E = E->next()) { + w[idx++] = E->get(); + } + } + + return sections; +} + +void EditorFolding::save_resource_folding(const RES &p_resource, const String &p_path) { + Ref<ConfigFile> config; + config.instance(); + PoolVector<String> unfolds = _get_unfolds(p_resource.ptr()); + config->set_value("folding", "sections_unfolded", unfolds); + + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + config->save(file); +} + +void EditorFolding::_set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds) { + + int uc = p_unfolds.size(); + PoolVector<String>::Read r = p_unfolds.read(); + p_object->editor_clear_section_folding(); + for (int i = 0; i < uc; i++) { + p_object->editor_set_section_unfold(r[i], true); + } +} + +void EditorFolding::load_resource_folding(RES p_resource, const String &p_path) { + + Ref<ConfigFile> config; + config.instance(); + + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + + if (config->load(file) != OK) { + return; + } + + PoolVector<String> unfolds; + + if (config->has_section_key("folding", "sections_unfolded")) { + unfolds = config->get_value("folding", "sections_unfolded"); + } + _set_unfolds(p_resource.ptr(), unfolds); +} + +void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources) { + if (p_root != p_node) { + if (!p_node->get_owner()) { + return; //not owned, bye + } + if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node)) { + return; + } + } + + PoolVector<String> unfolds = _get_unfolds(p_node); + + if (unfolds.size()) { + p_folds.push_back(p_root->get_path_to(p_node)); + p_folds.push_back(unfolds); + } + + List<PropertyInfo> plist; + p_node->get_property_list(&plist); + for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { + if (E->get().type == Variant::OBJECT) { + RES res = p_node->get(E->get().name); + if (res.is_valid() && !resources.has(res) && res->get_path() != String() && !res->get_path().is_resource_file()) { + + PoolVector<String> res_unfolds = _get_unfolds(res.ptr()); + resource_folds.push_back(res->get_path()); + resource_folds.push_back(res_unfolds); + resources.insert(res); + } + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + _fill_folds(p_root, p_node->get_child(i), p_folds, resource_folds, resources); + } +} +void EditorFolding::save_scene_folding(const Node *p_scene, const String &p_path) { + + Ref<ConfigFile> config; + config.instance(); + + Array unfolds, res_unfolds; + Set<RES> resources; + _fill_folds(p_scene, p_scene, unfolds, res_unfolds, resources); + + config->set_value("folding", "node_unfolds", unfolds); + config->set_value("folding", "resource_unfolds", res_unfolds); + + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + config->save(file); +} +void EditorFolding::load_scene_folding(Node *p_scene, const String &p_path) { + + Ref<ConfigFile> config; + config.instance(); + + String path = EditorSettings::get_singleton()->get_project_settings_dir(); + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + + if (config->load(file) != OK) { + return; + } + + Array unfolds; + if (config->has_section_key("folding", "node_unfolds")) { + unfolds = config->get_value("folding", "node_unfolds"); + } + Array res_unfolds; + if (config->has_section_key("folding", "resource_unfolds")) { + res_unfolds = config->get_value("folding", "resource_unfolds"); + } + + ERR_FAIL_COND(unfolds.size() & 1); + ERR_FAIL_COND(res_unfolds.size() & 1); + + for (int i = 0; i < unfolds.size(); i += 2) { + NodePath path = unfolds[i]; + PoolVector<String> un = unfolds[i + 1]; + Node *node = p_scene->get_node(path); + if (!node) { + continue; + } + _set_unfolds(node, un); + } + + for (int i = 0; i < res_unfolds.size(); i += 2) { + String path = res_unfolds[i]; + RES res; + if (ResourceCache::has(path)) { + res = RES(ResourceCache::get(path)); + } + if (res.is_null()) { + continue; + } + + PoolVector<String> unfolds = res_unfolds[i + 1]; + _set_unfolds(res.ptr(), unfolds); + } +} + +bool EditorFolding::has_folding_data(const String &p_path) { + + String file = p_path.get_file() + "-folding-" + p_path.md5_text() + ".cfg"; + file = EditorSettings::get_singleton()->get_project_settings_dir().plus_file(file); + return FileAccess::exists(file); +} + +EditorFolding::EditorFolding() { +} diff --git a/editor/editor_folding.h b/editor/editor_folding.h new file mode 100644 index 0000000000..cfd4b5466d --- /dev/null +++ b/editor/editor_folding.h @@ -0,0 +1,25 @@ +#ifndef EDITOR_FOLDING_H +#define EDITOR_FOLDING_H + +#include "scene/main/node.h" + +class EditorFolding { + + PoolVector<String> _get_unfolds(const Object *p_object); + void _set_unfolds(Object *p_object, const PoolVector<String> &p_unfolds); + + void _fill_folds(const Node *p_root, const Node *p_node, Array &p_folds, Array &resource_folds, Set<RES> &resources); + +public: + void save_resource_folding(const RES &p_resource, const String &p_path); + void load_resource_folding(RES p_resource, const String &p_path); + + void save_scene_folding(const Node *p_scene, const String &p_path); + void load_scene_folding(Node *p_scene, const String &p_path); + + bool has_folding_data(const String &p_path); + + EditorFolding(); +}; + +#endif // EDITOR_FOLDING_H diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 80dd5aa114..de1f856608 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -36,7 +36,7 @@ #include "editor_node.h" #include "editor_settings.h" -#define CONTRIBUTE_URL "http://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html" +#define CONTRIBUTE_URL "https://docs.godotengine.org/en/latest/community/contributing/updating_the_class_reference.html" #define CONTRIBUTE2_URL "https://github.com/godotengine/godot-docs" #define REQUEST_URL "https://github.com/godotengine/godot-docs/issues/new" @@ -252,6 +252,8 @@ void EditorHelpSearch::_notification(int p_what) { connect("confirmed", this, "_confirmed"); _update_search(); + } else if (p_what == NOTIFICATION_EXIT_TREE) { + disconnect("confirmed", this, "_confirmed"); } else if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { if (is_visible_in_tree()) { @@ -397,8 +399,10 @@ void EditorHelpIndex::_notification(int p_what) { if (enable_rl) { class_list->add_constant_override("draw_relationship_lines", 1); class_list->add_color_override("relationship_line_color", rl_color); + class_list->add_constant_override("draw_guides", 0); } else { class_list->add_constant_override("draw_relationship_lines", 0); + class_list->add_constant_override("draw_guides", 1); } } } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 3ecaa2b136..a564a2a113 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -374,14 +374,24 @@ bool EditorProperty::_get_instanced_node_original_property(const StringName &p_p node = node->get_owner(); } + if (!found) { + //if not found, try default class value + Variant attempt = ClassDB::class_get_default_property_value(object->get_class_name(), property); + if (attempt.get_type() != Variant::NIL) { + found = true; + value = attempt; + } + } + return found; } -bool EditorProperty::_is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage) { +bool EditorProperty::_is_property_different(const Variant &p_current, const Variant &p_orig) { // this is a pretty difficult function, because a property may not be saved but may have // the flag to not save if one or if zero + //make sure there is an actual state { Node *node = Object::cast_to<Node>(object); if (!node) @@ -415,15 +425,6 @@ bool EditorProperty::_is_property_different(const Variant &p_current, const Vari return false; //pointless to check if we are not comparing against anything. } - if (p_orig.get_type() == Variant::NIL) { - // not found (was not saved) - // check if it was not saved due to being zero or one - if (p_current.is_zero() && property_usage & PROPERTY_USAGE_STORE_IF_NONZERO) - return false; - if (p_current.is_one() && property_usage & PROPERTY_USAGE_STORE_IF_NONONE) - return false; - } - if (p_current.get_type() == Variant::REAL && p_orig.get_type() == Variant::REAL) { float a = p_current; float b = p_orig; @@ -434,23 +435,6 @@ bool EditorProperty::_is_property_different(const Variant &p_current, const Vari return bool(Variant::evaluate(Variant::OP_NOT_EQUAL, p_current, p_orig)); } -bool EditorProperty::_is_instanced_node_with_original_property_different() { - - bool mbi = _might_be_in_instance(); - if (mbi) { - Variant vorig; - int usage = property_usage & (PROPERTY_USAGE_STORE_IF_NONONE | PROPERTY_USAGE_STORE_IF_NONZERO); - if (_get_instanced_node_original_property(property, vorig) || usage) { - Variant v = object->get(property); - - if (_is_property_different(v, vorig, usage)) { - return true; - } - } - } - return false; -} - void EditorProperty::update_reload_status() { if (property == StringName()) @@ -458,8 +442,22 @@ void EditorProperty::update_reload_status() { bool has_reload = false; - if (_is_instanced_node_with_original_property_different()) { - has_reload = true; + if (_might_be_in_instance()) { + //check for difference including instantiation + Variant vorig; + if (_get_instanced_node_original_property(property, vorig)) { + Variant v = object->get(property); + + if (_is_property_different(v, vorig)) { + has_reload = true; + } + } + } else { + //check for difference against default class value instead + Variant default_value = ClassDB::class_get_default_property_value(object->get_class_name(), property); + if (default_value != Variant() && default_value != object->get(property)) { + has_reload = true; + } } if (object->call("property_can_revert", property).operator bool()) { @@ -628,7 +626,7 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { } if (keying_rect.has_point(mb->get_position())) { - emit_signal("property_keyed", property); + emit_signal("property_keyed", property, use_keying_next()); if (use_keying_next()) { call_deferred("emit_signal", "property_changed", property, object->get(property).operator int64_t() + 1); @@ -651,6 +649,7 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { Variant rev = object->call("property_get_revert", property); emit_signal("property_changed", property, rev); update_property(); + return; } if (!object->get_script().is_null()) { @@ -659,8 +658,16 @@ void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) { if (scr->get_property_default_value(property, orig_value)) { emit_signal("property_changed", property, orig_value); update_property(); + return; } } + + Variant default_value = ClassDB::class_get_default_property_value(object->get_class_name(), property); + if (default_value != Variant()) { + emit_signal("property_changed", property, default_value); + update_property(); + return; + } } if (check_rect.has_point(mb->get_position())) { checked = !checked; @@ -1082,10 +1089,8 @@ void EditorInspectorSection::_notification(int p_what) { Color color = get_color("font_color", "Tree"); draw_string(font, Point2(hs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width); - int ofs = 0; if (arrow.is_valid()) { draw_texture(arrow, Point2(get_size().width - arrow->get_width(), (h - arrow->get_height()) / 2).floor()); - ofs += hs + arrow->get_width(); } } } @@ -1148,6 +1153,11 @@ void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + Ref<Font> font = get_font("font", "Tree"); + if (mb->get_position().y > font->get_height()) { //clicked outside + return; + } + _test_unfold(); bool unfold = !object->editor_is_section_unfolded(section); @@ -1370,6 +1380,28 @@ void EditorInspector::update_tree() { // TreeItem *current_category = NULL; + bool unfold_if_edited = false; + + if (use_folding && auto_unfold_edited && get_tree()->get_edited_scene_root()) { + String path; + Node *node = Object::cast_to<Node>(object); + if (node) { + path = get_tree()->get_edited_scene_root()->get_filename(); + } + Resource *res = Object::cast_to<Resource>(object); + if (res) { + if (res->get_path().is_resource_file()) { + path = res->get_path(); + } else if (res->get_path().begins_with("res://")) { //internal resource + path = get_tree()->get_edited_scene_root()->get_filename(); + } + } + + if (!EditorNode::get_singleton()->get_editor_folding().has_folding_data(path)) { + unfold_if_edited = true; + } + } + String filter = search_box ? search_box->get_text() : ""; String group; String group_base; @@ -1380,6 +1412,8 @@ void EditorInspector::update_tree() { object->get_property_list(&plist, true); HashMap<String, VBoxContainer *> item_path; + Map<VBoxContainer *, EditorInspectorSection *> section_map; + item_path[""] = main_vbox; Color sscolor = get_color("prop_subsection", "Editor"); @@ -1542,7 +1576,9 @@ void EditorInspector::update_tree() { c.a /= level; section->setup(acc_path, path_name, object, c, use_folding); - item_path[acc_path] = section->get_vbox(); + VBoxContainer *vb = section->get_vbox(); + item_path[acc_path] = vb; + section_map[vb] = section; } current_vbox = item_path[acc_path]; level = (MIN(level + 1, 4)); @@ -1685,6 +1721,13 @@ void EditorInspector::update_tree() { if (current_selected && ep->property == current_selected) { ep->select(current_focusable); } + + if (unfold_if_edited && ep->can_revert_to_default()) { + //if edited and there is a parent section, unfold it. + if (current_vbox && section_map.has(current_vbox)) { + section_map[current_vbox]->unfold(); + } + } } } @@ -1992,20 +2035,20 @@ void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array changing--; } -void EditorInspector::_property_keyed(const String &p_path) { +void EditorInspector::_property_keyed(const String &p_path, bool p_advance) { if (!object) return; - emit_signal("property_keyed", p_path, object->get(p_path), true); //second param is deprecated + emit_signal("property_keyed", p_path, object->get(p_path), p_advance); //second param is deprecated } -void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value) { +void EditorInspector::_property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance) { if (!object) return; - emit_signal("property_keyed", p_path, p_value, false); //second param is deprecated + emit_signal("property_keyed", p_path, p_value, p_advance); //second param is deprecated } void EditorInspector::_property_checked(const String &p_path, bool p_checked) { @@ -2181,6 +2224,10 @@ String EditorInspector::get_object_class() const { return object_class; } +void EditorInspector::set_auto_unfold_edited(bool p_enable) { + auto_unfold_edited = p_enable; +} + void EditorInspector::_bind_methods() { ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed, DEFVAL(false)); @@ -2205,6 +2252,7 @@ void EditorInspector::_bind_methods() { ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop"))); ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property"))); + ADD_SIGNAL(MethodInfo("property_toggled", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::BOOL, "checked"))); ADD_SIGNAL(MethodInfo("restart_requested")); } @@ -2236,6 +2284,7 @@ EditorInspector::EditorInspector() { set_process(true); property_focusable = -1; use_sub_inspector_bg = false; + auto_unfold_edited = false; get_v_scrollbar()->connect("value_changed", this, "_vscroll_changed"); update_scroll_request = -1; diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index dccbdb9a73..00841178bd 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -71,8 +71,7 @@ private: bool draw_top_bg; bool _might_be_in_instance(); - bool _is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage); - bool _is_instanced_node_with_original_property_different(); + bool _is_property_different(const Variant &p_current, const Variant &p_orig); bool _get_instanced_node_original_property(const StringName &p_prop, Variant &value); void _focusable_focused(int p_index); @@ -152,6 +151,8 @@ public: void set_draw_top_bg(bool p_draw) { draw_top_bg = p_draw; } + bool can_revert_to_default() const { return can_revert; } + EditorProperty(); }; @@ -271,6 +272,7 @@ class EditorInspector : public ScrollContainer { bool read_only; bool keying; bool use_sub_inspector_bg; + bool auto_unfold_edited; float refresh_countdown; bool update_tree_pending; @@ -293,8 +295,8 @@ class EditorInspector : public ScrollContainer { void _property_changed(const String &p_path, const Variant &p_value, bool changing = false); void _property_changed_update_all(const String &p_path, const Variant &p_value); void _multiple_properties_changed(Vector<String> p_paths, Array p_values); - void _property_keyed(const String &p_path); - void _property_keyed_with_value(const String &p_path, const Variant &p_value); + void _property_keyed(const String &p_path, bool p_advance); + void _property_keyed_with_value(const String &p_path, const Variant &p_value, bool p_advance); void _property_checked(const String &p_path, bool p_checked); @@ -365,6 +367,7 @@ public: String get_object_class() const; void set_use_sub_inspector_bg(bool p_enable); + void set_auto_unfold_edited(bool p_enable); EditorInspector(); }; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 3899fcf83f..dbbf5d08b8 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -574,13 +574,29 @@ void EditorNode::_editor_select_prev() { _editor_select(editor); } -Error EditorNode::load_resource(const String &p_scene) { +Error EditorNode::load_resource(const String &p_resource, bool p_ignore_broken_deps) { - RES res = ResourceLoader::load(p_scene); + dependency_errors.clear(); + + Error err; + RES res = ResourceLoader::load(p_resource, "", false, &err); ERR_FAIL_COND_V(!res.is_valid(), ERR_CANT_OPEN); - inspector_dock->edit_resource(res); + if (!p_ignore_broken_deps && dependency_errors.has(p_resource)) { + + //current_option = -1; + Vector<String> errors; + for (Set<String>::Element *E = dependency_errors[p_resource].front(); E; E = E->next()) { + + errors.push_back(E->get()); + } + dependency_error->show(DependencyErrorDialog::MODE_RESOURCE, p_resource, errors); + dependency_errors.erase(p_resource); + + return ERR_FILE_MISSING_DEPENDENCIES; + } + inspector_dock->edit_resource(res); return OK; } @@ -1061,6 +1077,9 @@ void EditorNode::_save_scene(String p_file, int idx) { set_current_version(editor_data.get_undo_redo().get_version()); else editor_data.set_edited_scene_version(0, idx); + + editor_folding.save_scene_folding(scene, p_file); + _update_title(); _update_scene_tabs(); } else { @@ -2251,7 +2270,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { emit_signal("request_help_search", ""); } break; case HELP_DOCS: { - OS::get_singleton()->shell_open("http://docs.godotengine.org/"); + OS::get_singleton()->shell_open("https://docs.godotengine.org/"); } break; case HELP_QA: { OS::get_singleton()->shell_open("https://godotengine.org/qa/"); @@ -2842,7 +2861,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b errors.push_back(E->get()); } - dependency_error->show(lpath, errors); + dependency_error->show(DependencyErrorDialog::MODE_SCENE, lpath, errors); opening_prev = false; if (prev != -1) { @@ -2907,6 +2926,8 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b _update_scene_tabs(); _add_to_recent_scenes(lpath); + editor_folding.load_scene_folding(new_scene, lpath); + prev_scene->set_disabled(previous_scenes.size() == 0); opening_prev = false; @@ -3129,34 +3150,33 @@ Ref<Texture> EditorNode::get_object_icon(const Object *p_object, const String &p script = p_object; } - StringName name; - String icon_path; if (script.is_valid()) { - name = EditorNode::get_editor_data().script_class_get_name(script->get_path()); - icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name); - name = script->get_instance_base_type(); + StringName name = EditorNode::get_editor_data().script_class_get_name(script->get_path()); + String icon_path = EditorNode::get_editor_data().script_class_get_icon_path(name); + if (icon_path.length()) + return ResourceLoader::load(icon_path); + + // should probably be deprecated in 4.x + StringName base = script->get_instance_base_type(); + if (base != StringName()) { + const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types(); + for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) { + const Vector<EditorData::CustomType> &ct = E->value(); + for (int i = 0; i < ct.size(); ++i) { + if (ct[i].name == base && ct[i].icon.is_valid()) { + return ct[i].icon; + } + } + } + } } - if (gui_base->has_icon(p_object->get_class(), "EditorIcons")) - return gui_base->get_icon(p_object->get_class(), "EditorIcons"); - - if (icon_path.length()) - return ResourceLoader::load(icon_path); - + // should probably be deprecated in 4.x if (p_object->has_meta("_editor_icon")) return p_object->get_meta("_editor_icon"); - if (name != StringName()) { - const Map<String, Vector<EditorData::CustomType> > &p_map = EditorNode::get_editor_data().get_custom_types(); - for (const Map<String, Vector<EditorData::CustomType> >::Element *E = p_map.front(); E; E = E->next()) { - const Vector<EditorData::CustomType> &ct = E->value(); - for (int i = 0; i < ct.size(); ++i) { - if (ct[i].name == name && ct[i].icon.is_valid()) { - return ct[i].icon; - } - } - } - } + if (gui_base->has_icon(p_object->get_class(), "EditorIcons")) + return gui_base->get_icon(p_object->get_class(), "EditorIcons"); if (p_fallback.length()) return gui_base->get_icon(p_fallback, "EditorIcons"); @@ -3930,7 +3950,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/save_all_scenes"), FILE_SAVE_ALL_SCENES); if (scene_tabs->get_hovered_tab() >= 0) { scene_tabs_context_menu->add_separator(); - scene_tabs_context_menu->add_item(TTR("Show in filesystem"), FILE_SHOW_IN_FILESYSTEM); + scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM); scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE); scene_tabs_context_menu->add_item(TTR("Close Tab"), FILE_CLOSE); } @@ -4544,6 +4564,19 @@ void EditorNode::_video_driver_selected(int p_which) { _update_video_driver_color(); } +void EditorNode::_resource_saved(RES p_resource, const String &p_path) { + if (EditorFileSystem::get_singleton()) { + EditorFileSystem::get_singleton()->update_file(p_path); + } + + singleton->editor_folding.save_resource_folding(p_resource, p_path); +} + +void EditorNode::_resource_loaded(RES p_resource, const String &p_path) { + + singleton->editor_folding.load_resource_folding(p_resource, p_path); +} + void EditorNode::_bind_methods() { ClassDB::bind_method("_menu_option", &EditorNode::_menu_option); @@ -4844,6 +4877,7 @@ EditorNode::EditorNode() { EDITOR_DEF_RST("interface/scene_tabs/show_thumbnail_on_hover", true); EDITOR_DEF_RST("interface/inspector/capitalize_properties", true); EDITOR_DEF_RST("interface/inspector/disable_folding", false); + EDITOR_DEF_RST("interface/inspector/auto_unfold_edited", true); EDITOR_DEF("interface/inspector/horizontal_vector2_editing", false); EDITOR_DEF("interface/inspector/horizontal_vector_types_editing", true); EDITOR_DEF("interface/inspector/open_resources_in_current_inspector", true); @@ -4972,7 +5006,7 @@ EditorNode::EditorNode() { dock_vb->add_child(dock_hb); dock_select = memnew(Control); - dock_select->set_custom_minimum_size(Size2(64, 0) * EDSCALE); + dock_select->set_custom_minimum_size(Size2(128, 64) * EDSCALE); dock_select->connect("gui_input", this, "_dock_select_input"); dock_select->connect("draw", this, "_dock_select_draw"); dock_select->connect("mouse_exited", this, "_dock_popup_exit"); @@ -4983,7 +5017,7 @@ EditorNode::EditorNode() { dock_select_rect_over = -1; dock_popup_selected = -1; for (int i = 0; i < DOCK_SLOT_MAX; i++) { - dock_slot[i]->set_custom_minimum_size(Size2(0, 64) * EDSCALE); + dock_slot[i]->set_custom_minimum_size(Size2(170, 0) * EDSCALE); dock_slot[i]->set_v_size_flags(Control::SIZE_EXPAND_FILL); dock_slot[i]->set_popup(dock_select_popup); dock_slot[i]->connect("pre_popup_pressed", this, "_dock_pre_popup", varray(i)); @@ -5160,7 +5194,7 @@ EditorNode::EditorNode() { p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/save_scene", TTR("Save Scene"), KEY_MASK_CMD + KEY_S), FILE_SAVE_SCENE); p->add_shortcut(ED_SHORTCUT("editor/save_scene_as", TTR("Save Scene As..."), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_AS_SCENE); - p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save all Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES); + p->add_shortcut(ED_SHORTCUT("editor/save_all_scenes", TTR("Save All Scenes"), KEY_MASK_ALT + KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_S), FILE_SAVE_ALL_SCENES); p->add_separator(); p->add_shortcut(ED_SHORTCUT("editor/close_scene", TTR("Close Scene"), KEY_MASK_SHIFT + KEY_MASK_CMD + KEY_W), FILE_CLOSE); p->add_separator(); @@ -5251,10 +5285,13 @@ EditorNode::EditorNode() { p->add_check_item(TTR("Visible Navigation"), RUN_DEBUG_NAVIGATION); p->set_item_tooltip(p->get_item_count() - 1, TTR("Navigation meshes and polygons will be visible on the running game if this option is turned on.")); p->add_separator(); + //those are now on by default, since they are harmless p->add_check_item(TTR("Sync Scene Changes"), RUN_LIVE_DEBUG); p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any changes made to the scene in the editor will be replicated in the running game.\nWhen used remotely on a device, this is more efficient with network filesystem.")); + p->set_item_checked(p->get_item_count() - 1, true); p->add_check_item(TTR("Sync Script Changes"), RUN_RELOAD_SCRIPTS); p->set_item_tooltip(p->get_item_count() - 1, TTR("When this option is turned on, any script that is saved will be reloaded on the running game.\nWhen used remotely on a device, this is more efficient with network filesystem.")); + p->set_item_checked(p->get_item_count() - 1, true); p->connect("id_pressed", this, "_menu_option"); menu_hb->add_spacer(); @@ -5499,8 +5536,8 @@ EditorNode::EditorNode() { right_r_vsplit->hide(); // Add some offsets to left_r and main hsplits to make LEFT_R and RIGHT_L docks wider than minsize - left_r_hsplit->set_split_offset(100 * EDSCALE); - main_hsplit->set_split_offset(-100 * EDSCALE); + left_r_hsplit->set_split_offset(70 * EDSCALE); + main_hsplit->set_split_offset(-70 * EDSCALE); // Define corresponding default layout @@ -5515,8 +5552,8 @@ EditorNode::EditorNode() { for (int i = 0; i < vsplits.size(); i++) default_layout->set_value(docks_section, "dock_split_" + itos(i + 1), 0); default_layout->set_value(docks_section, "dock_hsplit_1", 0); - default_layout->set_value(docks_section, "dock_hsplit_2", 40 * EDSCALE); - default_layout->set_value(docks_section, "dock_hsplit_3", -40 * EDSCALE); + default_layout->set_value(docks_section, "dock_hsplit_2", 70 * EDSCALE); + default_layout->set_value(docks_section, "dock_hsplit_3", -70 * EDSCALE); default_layout->set_value(docks_section, "dock_hsplit_4", 0); _update_layouts_menu(); @@ -5829,6 +5866,9 @@ EditorNode::EditorNode() { print_handler.userdata = this; add_print_handler(&print_handler); + ResourceSaver::set_save_callback(_resource_saved); + ResourceLoader::set_load_callback(_resource_loaded); + #ifdef OSX_ENABLED ED_SHORTCUT("editor/editor_2d", TTR("Open 2D Editor"), KEY_MASK_ALT | KEY_1); ED_SHORTCUT("editor/editor_3d", TTR("Open 3D Editor"), KEY_MASK_ALT | KEY_2); @@ -5857,6 +5897,7 @@ EditorNode::~EditorNode() { memdelete(editor_plugins_force_input_forwarding); memdelete(file_server); memdelete(progress_hb); + EditorSettings::destroy(); } diff --git a/editor/editor_node.h b/editor/editor_node.h index 0096748ed1..b828a4d7d5 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -38,6 +38,7 @@ #include "editor/editor_about.h" #include "editor/editor_data.h" #include "editor/editor_export.h" +#include "editor/editor_folding.h" #include "editor/editor_inspector.h" #include "editor/editor_log.h" #include "editor/editor_name_dialog.h" @@ -385,6 +386,7 @@ private: EditorSelection *editor_selection; ProjectExportDialog *project_export; EditorResourcePreview *resource_preview; + EditorFolding editor_folding; EditorFileServer *file_server; @@ -600,6 +602,9 @@ private: PrintHandlerList print_handler; static void _print_handler(void *p_this, const String &p_string, bool p_error); + static void _resource_saved(RES p_resource, const String &p_path); + static void _resource_loaded(RES p_resource, const String &p_path); + protected: void _notification(int p_what); static void _bind_methods(); @@ -682,7 +687,7 @@ public: void fix_dependencies(const String &p_for_file); void clear_scene() { _cleanup_scene(); } Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false); - Error load_resource(const String &p_scene); + Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false); bool is_scene_open(const String &p_path); @@ -690,6 +695,7 @@ public: void set_current_scene(int p_idx); static EditorData &get_editor_data() { return singleton->editor_data; } + static EditorFolding &get_editor_folding() { return singleton->editor_folding; } EditorHistory *get_editor_history() { return &editor_history; } static VSplitContainer *get_top_split() { return singleton->top_split; } diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index dd3a8aa307..86b2db877e 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -475,7 +475,6 @@ void EditorPlugin::set_force_draw_over_forwarding_enabled() { } void EditorPlugin::notify_scene_changed(const Node *scn_root) { - if (scn_root == NULL) return; emit_signal("scene_changed", scn_root); } diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 5148c12160..3730807243 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1802,8 +1802,13 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) { NodePath path = p_path; Node *base_node = Object::cast_to<Node>(get_edited_object()); - if (base_node == NULL && get_edited_object()->has_method("get_root_path")) { - base_node = get_edited_object()->call("get_root_path"); + if (base_node == NULL) { + if (Object::cast_to<Resource>(get_edited_object())) { + Node *to_node = get_node(p_path); + path = get_tree()->get_edited_scene_root()->get_path_to(to_node); + } else if (get_edited_object()->has_method("get_root_path")) { + base_node = get_edited_object()->call("get_root_path"); + } } if (base_node) { // for AnimationTrackKeyEdit path = base_node->get_path().rel_path_to(p_path); @@ -2064,8 +2069,22 @@ void EditorPropertyResource::_menu_option(int p_which) { if (intype == "ViewportTexture") { + Resource *r = Object::cast_to<Resource>(get_edited_object()); + if (r && r->get_path().is_resource_file()) { + EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on resources saved as a file.\nResource needs to belong to a scene.")); + return; + } + + if (r && !r->is_local_to_scene()) { + EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on this resource because it's not set as local to scene.\nPlease switch on the 'local to scene' property on it (and all resources containing it up to a node).")); + return; + } + if (!scene_tree) { scene_tree = memnew(SceneTreeDialog); + Vector<StringName> valid_types; + valid_types.push_back("Viewport"); + scene_tree->get_scene_tree()->set_valid_types(valid_types); scene_tree->get_scene_tree()->set_show_enabled_subscene(true); add_child(scene_tree); scene_tree->connect("selected", this, "_viewport_selected"); @@ -2126,7 +2145,8 @@ void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<T } } -void EditorPropertyResource::_update_menu() { +void EditorPropertyResource::_update_menu_items() { + //////////////////// UPDATE MENU ////////////////////////// RES res = get_edited_object()->get(get_edited_property()); @@ -2216,7 +2236,7 @@ void EditorPropertyResource::_update_menu() { RES r = res; if (r.is_valid() && r->get_path().is_resource_file()) { menu->add_separator(); - menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); + menu->add_item(TTR("Show in FileSystem"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); } } else { } @@ -2268,6 +2288,11 @@ void EditorPropertyResource::_update_menu() { menu->add_icon_item(icon, vformat(TTR("Convert To %s"), what), CONVERT_BASE_ID + i); } } +} + +void EditorPropertyResource::_update_menu() { + + _update_menu_items(); Rect2 gt = edit->get_global_rect(); menu->set_as_minsize(); @@ -2292,6 +2317,20 @@ void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) { emit_signal("object_id_selected", get_edited_property(), p_id); } +void EditorPropertyResource::_button_input(const Ref<InputEvent> &p_event) { + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { + _update_menu_items(); + Vector2 pos = mb->get_global_position(); + //pos = assign->get_global_transform().xform(pos); + menu->set_as_minsize(); + menu->set_global_position(pos); + menu->popup(); + } + } +} + void EditorPropertyResource::_open_editor_pressed() { RES res = get_edited_object()->get(get_edited_property()); if (res.is_valid()) { @@ -2579,6 +2618,7 @@ void EditorPropertyResource::_bind_methods() { ClassDB::bind_method(D_METHOD("drop_data_fw"), &EditorPropertyResource::drop_data_fw); ClassDB::bind_method(D_METHOD("_button_draw"), &EditorPropertyResource::_button_draw); ClassDB::bind_method(D_METHOD("_open_editor_pressed"), &EditorPropertyResource::_open_editor_pressed); + ClassDB::bind_method(D_METHOD("_button_input"), &EditorPropertyResource::_button_input); } EditorPropertyResource::EditorPropertyResource() { @@ -2605,6 +2645,7 @@ EditorPropertyResource::EditorPropertyResource() { preview->set_margin(MARGIN_BOTTOM, -1); preview->set_margin(MARGIN_RIGHT, -1); assign->add_child(preview); + assign->connect("gui_input", this, "_button_input"); menu = memnew(PopupMenu); add_child(menu); @@ -2613,6 +2654,7 @@ EditorPropertyResource::EditorPropertyResource() { menu->connect("id_pressed", this, "_menu_option"); edit->connect("pressed", this, "_update_menu"); hbc->add_child(edit); + edit->connect("gui_input", this, "_button_input"); file = NULL; scene_tree = NULL; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 35d8f4d306..541abb1f22 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -552,6 +552,8 @@ class EditorPropertyResource : public EditorProperty { void _resource_selected(); void _viewport_selected(const NodePath &p_path); + void _update_menu_items(); + void _update_menu(); void _sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool); @@ -564,6 +566,7 @@ class EditorPropertyResource : public EditorProperty { bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + void _button_input(const Ref<InputEvent> &p_event); void _open_editor_pressed(); protected: diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 6eaa2560db..c0b441bbd5 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -410,7 +410,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/completion/add_type_hints", false); _initial_set("docks/scene_tree/start_create_dialog_fully_expanded", false); - _initial_set("docks/scene_tree/draw_relationship_lines", false); + _initial_set("docks/scene_tree/draw_relationship_lines", true); _initial_set("docks/scene_tree/relationship_line_color", Color::html("464646")); _initial_set("editors/grid_map/pick_distance", 5000.0); @@ -433,7 +433,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { // navigation _initial_set("editors/3d/navigation/navigation_scheme", 0); - _initial_set("editors/3d/navigation/invert_y-axis", false); + _initial_set("editors/3d/navigation/invert_y_axis", false); hints["editors/3d/navigation/navigation_scheme"] = PropertyInfo(Variant::INT, "editors/3d/navigation/navigation_scheme", PROPERTY_HINT_ENUM, "Godot,Maya,Modo"); _initial_set("editors/3d/navigation/zoom_style", 0); hints["editors/3d/navigation/zoom_style"] = PropertyInfo(Variant::INT, "editors/3d/navigation/zoom_style", PROPERTY_HINT_ENUM, "Vertical, Horizontal"); diff --git a/editor/fileserver/editor_file_server.cpp b/editor/fileserver/editor_file_server.cpp index 27fe716855..306f047860 100644 --- a/editor/fileserver/editor_file_server.cpp +++ b/editor/fileserver/editor_file_server.cpp @@ -150,13 +150,13 @@ void EditorFileServer::_subthread_start(void *s) { s.parse_utf8(fileutf8.ptr()); if (cmd == FileAccessNetwork::COMMAND_FILE_EXISTS) { - DEBUG_PRINT("FILE EXISTS: " + s); + print_verbose("FILE EXISTS: " + s); } if (cmd == FileAccessNetwork::COMMAND_GET_MODTIME) { - DEBUG_PRINT("MOD TIME: " + s); + print_verbose("MOD TIME: " + s); } if (cmd == FileAccessNetwork::COMMAND_OPEN_FILE) { - DEBUG_PRINT("OPEN: " + s); + print_verbose("OPEN: " + s); } if (!s.begins_with("res://")) { @@ -243,7 +243,7 @@ void EditorFileServer::_subthread_start(void *s) { int read = cd->files[id]->get_buffer(buf.ptrw(), blocklen); ERR_CONTINUE(read < 0); - DEBUG_PRINT("GET BLOCK - offset: " + itos(offset) + ", blocklen: " + itos(blocklen)); + print_verbose("GET BLOCK - offset: " + itos(offset) + ", blocklen: " + itos(blocklen)); //not found, continue encode_uint32(id, buf4); @@ -259,7 +259,7 @@ void EditorFileServer::_subthread_start(void *s) { } break; case FileAccessNetwork::COMMAND_CLOSE: { - DEBUG_PRINT("CLOSED"); + print_verbose("CLOSED"); ERR_CONTINUE(!cd->files.has(id)); memdelete(cd->files[id]); cd->files.erase(id); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index ef960f74ea..828e608fa4 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -227,12 +227,7 @@ void FileSystemDock::_update_tree(const Vector<String> p_uncollapsed_paths, bool void FileSystemDock::_update_display_mode(bool p_force) { // Compute the new display mode - DisplayMode new_display_mode; - if (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) { - new_display_mode = file_list_view ? DISPLAY_MODE_FILE_LIST_ONLY : DISPLAY_MODE_TREE_ONLY; - } else { - new_display_mode = DISPLAY_MODE_SPLIT; - } + DisplayMode new_display_mode = (display_mode_setting == DISPLAY_MODE_SETTING_TREE_ONLY) ? DISPLAY_MODE_TREE_ONLY : DISPLAY_MODE_SPLIT; if (p_force || new_display_mode != display_mode || old_display_mode_setting != display_mode_setting) { display_mode = new_display_mode; @@ -252,19 +247,9 @@ void FileSystemDock::_update_display_mode(bool p_force) { file_list_vb->hide(); break; - case DISPLAY_MODE_FILE_LIST_ONLY: - tree->hide(); - tree_search_box->hide(); - button_tree->show(); - - file_list_vb->show(); - _update_file_list(true); - break; - case DISPLAY_MODE_SPLIT: tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); - button_tree->hide(); tree->ensure_cursor_is_visible(); tree_search_box->hide(); _update_tree(_compute_uncollapsed_paths()); @@ -295,7 +280,6 @@ void FileSystemDock::_notification(int p_what) { String ei = "EditorIcons"; button_reload->set_icon(get_icon("Reload", ei)); button_toggle_display_mode->set_icon(get_icon("Panels2", ei)); - button_tree->set_icon(get_icon("Filesystem", ei)); _update_file_list_display_mode_button(); button_file_list_display_mode->connect("pressed", this, "_change_file_display"); @@ -312,7 +296,6 @@ void FileSystemDock::_notification(int p_what) { file_list_popup->connect("id_pressed", this, "_file_list_rmb_option"); tree_popup->connect("id_pressed", this, "_tree_rmb_option"); - button_tree->connect("pressed", this, "_go_to_tree", varray(), CONNECT_DEFERRED); current_path->connect("text_entered", this, "navigate_to_path"); display_mode_setting = DisplayModeSetting(int(EditorSettings::get_singleton()->get("docks/filesystem/display_mode"))); @@ -353,7 +336,7 @@ void FileSystemDock::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - if (tree->is_visible_in_tree()) { + if (is_visible_in_tree()) { _update_display_mode(true); } } break; @@ -362,7 +345,6 @@ void FileSystemDock::_notification(int p_what) { String ei = "EditorIcons"; button_reload->set_icon(get_icon("Reload", ei)); button_toggle_display_mode->set_icon(get_icon("Panels2", ei)); - button_tree->set_icon(get_icon("Filesystem", ei)); button_hist_next->set_icon(get_icon("Forward", ei)); button_hist_prev->set_icon(get_icon("Back", ei)); if (button_file_list_display_mode->is_pressed()) { @@ -484,20 +466,9 @@ void FileSystemDock::navigate_to_path(const String &p_path) { _set_current_path_text(path); _push_to_history(); + _update_tree(_compute_uncollapsed_paths()); if (display_mode == DISPLAY_MODE_SPLIT) { - if (path.ends_with("/") || path == "Favorites") { - _go_to_file_list(); - } - _update_tree(_compute_uncollapsed_paths()); _update_file_list(false); - } else if (display_mode == DISPLAY_MODE_TREE_ONLY) { - if (path.ends_with("/") || path == "Favorites") { - _go_to_file_list(); - } else { - _update_tree(_compute_uncollapsed_paths()); - } - } else { // DISPLAY_MODE_FILE_LIST_ONLY - _update_file_list(true); } String file_name = p_path.get_file(); @@ -711,7 +682,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { _search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128); } else { - if ((display_mode == DISPLAY_MODE_FILE_LIST_ONLY || display_mode == DISPLAY_MODE_TREE_ONLY) || always_show_folders) { + if (display_mode == DISPLAY_MODE_TREE_ONLY || always_show_folders) { // Display folders in the list if (directory != "res://") { @@ -839,35 +810,16 @@ void FileSystemDock::_tree_activate_file() { TreeItem *selected = tree->get_selected(); if (selected) { call_deferred("_select_file", selected->get_metadata(0)); - } -} -void FileSystemDock::_file_list_activate_file(int p_idx) { - _select_file(files->get_item_metadata(p_idx)); -} - -void FileSystemDock::_go_to_file_list() { - - if (display_mode == DISPLAY_MODE_TREE_ONLY) { - - file_list_view = true; - _update_display_mode(); - } else { - TreeItem *selected = tree->get_selected(); - if (selected) { + if (path.ends_with("/") || path == "Favorites") { bool collapsed = selected->is_collapsed(); selected->set_collapsed(!collapsed); } - _update_file_list(false); } } -void FileSystemDock::_go_to_tree() { - - file_list_view = false; - tree->grab_focus(); - _update_display_mode(); - tree->ensure_cursor_is_visible(); +void FileSystemDock::_file_list_activate_file(int p_idx) { + _select_file(files->get_item_metadata(p_idx)); } void FileSystemDock::_preview_invalidated(const String &p_path) { @@ -1651,7 +1603,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> p_selected) if (!fpath.ends_with("/")) { fpath = fpath.get_base_dir(); } - make_script_dialog_text->config("Node", fpath + "new_script.gd", false); + make_script_dialog_text->config("Node", fpath.plus_file("new_script.gd"), false); make_script_dialog_text->popup_centered(Size2(300, 300) * EDSCALE); } break; @@ -1704,9 +1656,6 @@ void FileSystemDock::_search_changed(const String &p_text, const Control *p_from tree_search_box->set_text(searched_string); switch (display_mode) { - case DISPLAY_MODE_FILE_LIST_ONLY: { - _update_file_list(false); - } break; case DISPLAY_MODE_TREE_ONLY: { _update_tree(searched_string.length() == 0 ? uncollapsed_paths_before_search : Vector<String>()); } break; @@ -1735,12 +1684,6 @@ void FileSystemDock::fix_dependencies(const String &p_for_file) { void FileSystemDock::focus_on_filter() { - if (display_mode == DISPLAY_MODE_FILE_LIST_ONLY && tree->is_visible()) { - // Tree mode, switch to files list with search box - tree->hide(); - file_list_vb->show(); - } - file_list_search_box->grab_focus(); } @@ -2025,7 +1968,7 @@ void FileSystemDock::_get_drag_target_folder(String &target, bool &target_favori return; } -void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths) { +void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options) { // Add options for files and folders ERR_FAIL_COND(p_paths.empty()) @@ -2112,12 +2055,14 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str if (p_paths.size() == 1) { p_popup->add_separator(); - p_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); - p_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); - p_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); + if (p_display_path_dependent_options) { + p_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); + p_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); + p_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); + } String fpath = p_paths[0]; - String item_text = fpath.ends_with("/") ? TTR("Open In File Manager") : TTR("Show In File Manager"); + String item_text = fpath.ends_with("/") ? TTR("Open in File Manager") : TTR("Show in File Manager"); p_popup->add_item(item_text, FILE_SHOW_IN_EXPLORER); } } @@ -2128,8 +2073,8 @@ void FileSystemDock::_tree_rmb_select(const Vector2 &p_pos) { if (paths.size() == 1) { if (paths[0].ends_with("/")) { - tree_popup->add_item(TTR("Expand all"), FOLDER_EXPAND_ALL); - tree_popup->add_item(TTR("Collapse all"), FOLDER_COLLAPSE_ALL); + tree_popup->add_item(TTR("Expand All"), FOLDER_EXPAND_ALL); + tree_popup->add_item(TTR("Collapse All"), FOLDER_COLLAPSE_ALL); tree_popup->add_separator(); } } @@ -2161,7 +2106,7 @@ void FileSystemDock::_file_list_rmb_select(int p_item, const Vector2 &p_pos) { if (!paths.empty()) { file_list_popup->clear(); file_list_popup->set_size(Size2(1, 1)); - _file_and_folders_fill_popup(file_list_popup, paths); + _file_and_folders_fill_popup(file_list_popup, paths, searched_string.length() == 0); file_list_popup->set_position(files->get_global_position() + p_pos); file_list_popup->popup(); } @@ -2169,13 +2114,16 @@ void FileSystemDock::_file_list_rmb_select(int p_item, const Vector2 &p_pos) { void FileSystemDock::_file_list_rmb_pressed(const Vector2 &p_pos) { // Right click on empty space for file list + if (searched_string.length() > 0) + return; + file_list_popup->clear(); file_list_popup->set_size(Size2(1, 1)); file_list_popup->add_item(TTR("New Folder..."), FILE_NEW_FOLDER); file_list_popup->add_item(TTR("New Script..."), FILE_NEW_SCRIPT); file_list_popup->add_item(TTR("New Resource..."), FILE_NEW_RESOURCE); - file_list_popup->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER); + file_list_popup->add_item(TTR("Show in File Manager"), FILE_SHOW_IN_EXPLORER); file_list_popup->set_position(files->get_global_position() + p_pos); file_list_popup->popup(); } @@ -2330,7 +2278,6 @@ void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_file_list_activate_file"), &FileSystemDock::_file_list_activate_file); ClassDB::bind_method(D_METHOD("_tree_activate_file"), &FileSystemDock::_tree_activate_file); ClassDB::bind_method(D_METHOD("_select_file"), &FileSystemDock::_select_file); - ClassDB::bind_method(D_METHOD("_go_to_tree"), &FileSystemDock::_go_to_tree); ClassDB::bind_method(D_METHOD("navigate_to_path"), &FileSystemDock::navigate_to_path); ClassDB::bind_method(D_METHOD("_change_file_display"), &FileSystemDock::_change_file_display); ClassDB::bind_method(D_METHOD("_fw_history"), &FileSystemDock::_fw_history); @@ -2426,7 +2373,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { /* button_open = memnew( Button ); button_open->set_flat(true); - button_open->connect("pressed",this,"_go_to_file_list"); + button_open->connect("pressed",this,"_tree_toggle_collapsed"); toolbar_hbc->add_child(button_open); button_open->hide(); button_open->set_focus_mode(FOCUS_NONE); @@ -2476,11 +2423,6 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { path_hb = memnew(HBoxContainer); file_list_vb->add_child(path_hb); - button_tree = memnew(ToolButton); - button_tree->set_tooltip(TTR("Enter tree-view.")); - button_tree->hide(); - path_hb->add_child(button_tree); - file_list_search_box = memnew(LineEdit); file_list_search_box->set_h_size_flags(SIZE_EXPAND_FILL); file_list_search_box->set_placeholder(TTR("Search files")); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 2aa79b1ddd..df6fa5f9d2 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -73,7 +73,6 @@ private: enum DisplayMode { DISPLAY_MODE_TREE_ONLY, - DISPLAY_MODE_FILE_LIST_ONLY, DISPLAY_MODE_SPLIT, }; @@ -109,7 +108,6 @@ private: Button *button_toggle_display_mode; Button *button_reload; - Button *button_tree; Button *button_file_list_display_mode; Button *button_hist_next; Button *button_hist_prev; @@ -127,7 +125,6 @@ private: DisplayMode display_mode; DisplayModeSetting display_mode_setting; DisplayModeSetting old_display_mode_setting; - bool file_list_view; PopupMenu *file_list_popup; PopupMenu *tree_popup; @@ -193,8 +190,7 @@ private: void _change_file_display(); void _fs_changed(); - void _go_to_tree(); - void _go_to_file_list(); + void _tree_toggle_collapsed(); void _select_file(const String p_path); void _tree_activate_file(); @@ -237,7 +233,7 @@ private: void _search_changed(const String &p_text, const Control *p_from); - void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths); + void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options = true); void _tree_rmb_select(const Vector2 &p_pos); void _file_list_rmb_select(int p_item, const Vector2 &p_pos); void _file_list_rmb_pressed(const Vector2 &p_pos); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 0ccaa95fb7..705bb1d9c5 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "find_in_files.h" + #include "core/os/dir_access.h" #include "core/os/os.h" #include "editor_node.h" @@ -89,8 +90,6 @@ static bool find_next(const String &line, String pattern, int from, bool match_c //-------------------------------------------------------------------------------- FindInFiles::FindInFiles() { _root_prefix = ROOT_PREFIX; - _extension_filter.insert("gd"); - _extension_filter.insert("cs"); _searching = false; _whole_words = true; _match_case = true; @@ -301,8 +300,7 @@ const char *FindInFilesDialog::SIGNAL_REPLACE_REQUESTED = "replace_requested"; FindInFilesDialog::FindInFilesDialog() { - set_custom_minimum_size(Size2(400, 190) * EDSCALE); - set_resizable(true); + set_custom_minimum_size(Size2(500 * EDSCALE, 0)); set_title(TTR("Find in Files")); VBoxContainer *vbc = memnew(VBoxContainer); @@ -317,7 +315,7 @@ FindInFilesDialog::FindInFilesDialog() { vbc->add_child(gc); Label *find_label = memnew(Label); - find_label->set_text(TTR("Find: ")); + find_label->set_text(TTR("Find:")); gc->add_child(find_label); _search_text_line_edit = memnew(LineEdit); @@ -326,10 +324,7 @@ FindInFilesDialog::FindInFilesDialog() { _search_text_line_edit->connect("text_entered", this, "_on_search_text_entered"); gc->add_child(_search_text_line_edit); - { - Control *placeholder = memnew(Control); - gc->add_child(placeholder); - } + gc->add_child(memnew(Control)); // Space to mantain the grid aligned. { HBoxContainer *hbc = memnew(HBoxContainer); @@ -346,7 +341,7 @@ FindInFilesDialog::FindInFilesDialog() { } Label *folder_label = memnew(Label); - folder_label->set_text(TTR("Folder: ")); + folder_label->set_text(TTR("Folder:")); gc->add_child(folder_label); { @@ -374,7 +369,7 @@ FindInFilesDialog::FindInFilesDialog() { } Label *filter_label = memnew(Label); - filter_label->set_text(TTR("Filter: ")); + filter_label->set_text(TTR("Filters:")); gc->add_child(filter_label); { @@ -382,7 +377,8 @@ FindInFilesDialog::FindInFilesDialog() { Vector<String> exts; exts.push_back("gd"); - exts.push_back("cs"); + if (Engine::get_singleton()->has_singleton("GodotSharp")) + exts.push_back("cs"); for (int i = 0; i < exts.size(); ++i) { CheckBox *cb = memnew(CheckBox); @@ -395,39 +391,14 @@ FindInFilesDialog::FindInFilesDialog() { gc->add_child(hbc); } - { - Control *placeholder = memnew(Control); - placeholder->set_custom_minimum_size(Size2(0, EDSCALE * 16)); - vbc->add_child(placeholder); - } - - { - HBoxContainer *hbc = memnew(HBoxContainer); - hbc->set_alignment(HBoxContainer::ALIGN_CENTER); - - _find_button = add_button(TTR("Find..."), false, "find"); - _find_button->set_disabled(true); - - { - Control *placeholder = memnew(Control); - placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0)); - hbc->add_child(placeholder); - } - - _replace_button = add_button(TTR("Replace..."), false, "replace"); - _replace_button->set_disabled(true); + _find_button = add_button(TTR("Find..."), false, "find"); + _find_button->set_disabled(true); - { - Control *placeholder = memnew(Control); - placeholder->set_custom_minimum_size(Size2(EDSCALE * 16, 0)); - hbc->add_child(placeholder); - } - - Button *cancel_button = get_ok(); - cancel_button->set_text(TTR("Cancel")); + _replace_button = add_button(TTR("Replace..."), false, "replace"); + _replace_button->set_disabled(true); - vbc->add_child(hbc); - } + Button *cancel_button = get_ok(); + cancel_button->set_text(TTR("Cancel")); } void FindInFilesDialog::set_search_text(String text) { diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 5eb1a42f9f..8e91a88adb 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -235,7 +235,7 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String if (p_streamable) format |= StreamTexture::FORMAT_BIT_STREAM; - if (p_mipmaps || p_compress_mode == COMPRESS_VIDEO_RAM) //VRAM always uses mipmaps + if (p_mipmaps) format |= StreamTexture::FORMAT_BIT_HAS_MIPMAPS; //mipmaps bit if (p_detect_3d) format |= StreamTexture::FORMAT_BIT_DETECT_3D; @@ -310,7 +310,9 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String case COMPRESS_VIDEO_RAM: { Ref<Image> image = p_image->duplicate(); - image->generate_mipmaps(p_force_normal); + if (p_mipmaps) { + image->generate_mipmaps(p_force_normal); + } if (p_force_rgbe && image->get_format() >= Image::FORMAT_R8 && image->get_format() <= Image::FORMAT_RGBE9995) { image->convert(Image::FORMAT_RGBE9995); diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 31eb193461..2e68609a56 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -38,11 +38,17 @@ public: List<PropertyInfo> properties; Ref<ResourceImporter> importer; Vector<String> paths; + Set<StringName> checked; + bool checking; bool _set(const StringName &p_name, const Variant &p_value) { if (values.has(p_name)) { values[p_name] = p_value; + if (checking) { + checked.insert(p_name); + _change_notify(String(p_name).utf8().get_data()); + } return true; } @@ -63,13 +69,24 @@ public: for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { if (!importer->get_option_visibility(E->get().name, values)) continue; - p_list->push_back(E->get()); + PropertyInfo pi = E->get(); + if (checking) { + pi.usage |= PROPERTY_USAGE_CHECKABLE; + if (checked.has(E->get().name)) { + pi.usage |= PROPERTY_USAGE_CHECKED; + } + } + p_list->push_back(pi); } } void update() { _change_notify(); } + + ImportDockParameters() { + checking = false; + } }; void ImportDock::set_edit_path(const String &p_path) { @@ -125,6 +142,8 @@ void ImportDock::_update_options(const Ref<ConfigFile> &p_config) { params->properties.clear(); params->values.clear(); + params->checking = false; + params->checked.clear(); for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { @@ -205,6 +224,8 @@ void ImportDock::set_edit_multiple_paths(const Vector<String> &p_paths) { params->properties.clear(); params->values.clear(); + params->checking = true; + params->checked.clear(); for (List<ResourceImporter::ImportOption>::Element *E = options.front(); E; E = E->next()) { @@ -360,11 +381,21 @@ void ImportDock::_reimport() { Error err = config->load(params->paths[i] + ".import"); ERR_CONTINUE(err != OK); - config->set_value("remap", "importer", params->importer->get_importer_name()); - config->erase_section("params"); + if (params->checking) { + //update only what edited (checkboxes) + for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { + if (params->checked.has(E->get().name)) { + config->set_value("params", E->get().name, params->values[E->get().name]); + } + } + } else { + //override entirely + config->set_value("remap", "importer", params->importer->get_importer_name()); + config->erase_section("params"); - for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { - config->set_value("params", E->get().name, params->values[E->get().name]); + for (List<PropertyInfo>::Element *E = params->properties.front(); E; E = E->next()) { + config->set_value("params", E->get().name, params->values[E->get().name]); + } } config->save(params->paths[i] + ".import"); @@ -388,11 +419,20 @@ void ImportDock::_notification(int p_what) { } break; } } + +void ImportDock::_property_toggled(const StringName &p_prop, bool p_checked) { + if (p_checked) { + params->checked.insert(p_prop); + } else { + params->checked.erase(p_prop); + } +} void ImportDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_reimport"), &ImportDock::_reimport); ClassDB::bind_method(D_METHOD("_preset_selected"), &ImportDock::_preset_selected); ClassDB::bind_method(D_METHOD("_importer_selected"), &ImportDock::_importer_selected); + ClassDB::bind_method(D_METHOD("_property_toggled"), &ImportDock::_property_toggled); } void ImportDock::initialize_import_options() const { @@ -423,6 +463,7 @@ ImportDock::ImportDock() { import_opts = memnew(EditorInspector); add_child(import_opts); import_opts->set_v_size_flags(SIZE_EXPAND_FILL); + import_opts->connect("property_toggled", this, "_property_toggled"); hb = memnew(HBoxContainer); add_child(hb); diff --git a/editor/import_dock.h b/editor/import_dock.h index 41c7298d9a..632fd39700 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -60,6 +60,7 @@ class ImportDock : public VBoxContainer { void _importer_selected(int i_idx); void _update_options(const Ref<ConfigFile> &p_config = Ref<ConfigFile>()); + void _property_toggled(const StringName &p_prop, bool p_checked); void _reimport(); enum { diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 750fca2852..2a8885c0a4 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -593,6 +593,7 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) { inspector->set_undo_redo(&editor_data->get_undo_redo()); inspector->set_use_filter(true); // TODO: check me + inspector->set_auto_unfold_edited(bool(EDITOR_GET("interface/inspector/auto_unfold_edited"))); inspector->connect("resource_selected", this, "_resource_selected"); inspector->connect("property_keyed", this, "_property_keyed"); diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp index be4e752d55..420d8ad3cf 100644 --- a/editor/multi_node_edit.cpp +++ b/editor/multi_node_edit.cpp @@ -52,7 +52,7 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value, UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("MultiNode Set") + " " + String(name)); + ur->create_action(TTR("MultiNode Set") + " " + String(name), UndoRedo::MERGE_ENDS); for (const List<NodePath>::Element *E = nodes.front(); E; E = E->next()) { if (!es->has_node(E->get())) diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 71a5d73b2f..16423decc4 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -229,6 +229,18 @@ void AbstractPolygon2DEditor::_wip_changed() { } } +void AbstractPolygon2DEditor::_wip_cancel() { + + wip.clear(); + wip_active = false; + + edited_point = PosVertex(); + hover_point = Vertex(); + selected_point = Vertex(); + + canvas_item_editor->update_viewport(); +} + void AbstractPolygon2DEditor::_wip_close() { if (!wip_active) return; @@ -429,7 +441,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) } } } else if (mb->get_button_index() == BUTTON_RIGHT && mb->is_pressed() && wip_active) { - _wip_close(); + _wip_cancel(); } } } @@ -510,6 +522,8 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) } else if (wip_active && k->get_scancode() == KEY_ENTER) { _wip_close(); + } else if (wip_active && k->get_scancode() == KEY_ESCAPE) { + _wip_cancel(); } } diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 00634ba5b8..c03670f254 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -103,6 +103,7 @@ protected: virtual void _menu_option(int p_option); void _wip_changed(); void _wip_close(); + void _wip_cancel(); bool _delete_point(const Vector2 &p_gpoint); void _notification(int p_what); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 205458fb1d..eb3c432ee7 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -692,7 +692,9 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { Ref<Animation> anim = player->get_animation(an->get_animation()); if (anim.is_valid()) { E->get()->set_max(anim->get_length()); - E->get()->set_value(an->get_playback_time()); + //StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + E->get().input_node; + StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E->key()) + "/time"; + E->get()->set_value(AnimationTreeEditor::get_singleton()->get_tree()->get(time_path)); } } } @@ -833,8 +835,6 @@ void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { if (p_node.is_valid()) { blend_tree = p_node; - } else { - blend_tree.unref(); } if (blend_tree.is_null()) { diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 31dd20e453..ee2283a035 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -179,8 +179,21 @@ void CanvasItemEditor::_snap_if_closer_float(float p_value, float p_target_snap, } } -bool CanvasItemEditor::_is_node_editable(const Node *p_node) { - return (!(p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_")) && !(ClassDB::is_parent_class(p_node->get_parent()->get_class_name(), "Container") && ClassDB::is_parent_class(p_node->get_class_name(), "Control"))); +bool CanvasItemEditor::_is_node_locked(const Node *p_node) { + return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); +} + +bool CanvasItemEditor::_is_node_movable(const Node *p_node, bool p_popup_warning) { + if (_is_node_locked(p_node)) { + return false; + } + if (Object::cast_to<Control>(p_node) && Object::cast_to<Container>(p_node->get_parent())) { + if (p_popup_warning) { + _popup_warning_temporarily(warning_child_of_container, 3.0); + } + return false; + } + return true; } void CanvasItemEditor::_snap_if_closer_point(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation, float p_radius) { @@ -415,7 +428,7 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c } } - if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !_is_node_editable(canvas_item))) { + if (canvas_item && canvas_item->is_visible_in_tree() && (include_locked_nodes || !_is_node_locked(canvas_item))) { Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform(); Rect2 rect = canvas_item->_edit_get_rect(); if (r_first) { @@ -437,7 +450,7 @@ Rect2 CanvasItemEditor::_get_encompassing_rect(const Node *p_node) { return rect; } -void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { +void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform, const Transform2D &p_canvas_xform) { if (!p_node) return; if (Object::cast_to<Viewport>(p_node)) @@ -449,16 +462,14 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no for (int i = p_node->get_child_count() - 1; i >= 0; i--) { if (canvas_item) { if (!canvas_item->is_set_as_toplevel()) { - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_parent_xform * canvas_item->get_transform(), p_canvas_xform); } else { - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, canvas_item->get_transform(), p_canvas_xform); + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, canvas_item->get_transform(), p_canvas_xform); } } else { CanvasLayer *cl = Object::cast_to<CanvasLayer>(p_node); - _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, p_limit, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); + _find_canvas_items_at_pos(p_pos, p_node->get_child(i), r_items, Transform2D(), cl ? cl->get_transform() : p_canvas_xform); } - if (p_limit != 0 && r_items.size() >= p_limit) - return; } if (canvas_item && canvas_item->is_visible_in_tree()) { @@ -478,11 +489,11 @@ void CanvasItemEditor::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_no return; } -void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit) { +void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items) { Node *scene = editor->get_edited_scene(); - _find_canvas_items_at_pos(p_pos, scene, r_items, p_limit); + _find_canvas_items_at_pos(p_pos, scene, r_items); //Remove invalid results for (int i = 0; i < r_items.size(); i++) { @@ -513,7 +524,7 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel } //Remove the item if invalid - if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || !_is_node_editable(canvas_item)) { + if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || _is_node_locked(canvas_item)) { r_items.remove(i); i--; } else { @@ -614,7 +625,7 @@ void CanvasItemEditor::_find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_n bool editable = p_node == scene || p_node->get_owner() == scene || scene->is_editable_instance(p_node->get_owner()); bool lock_children = p_node->has_meta("_edit_group_") && p_node->get_meta("_edit_group_"); - bool locked = !_is_node_editable(p_node); + bool locked = _is_node_locked(p_node); if (!lock_children || !editable) { for (int i = p_node->get_child_count() - 1; i >= 0; i--) { @@ -681,7 +692,7 @@ List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retreive_lock List<CanvasItem *> selection; for (Map<Node *, Object *>::Element *E = editor_selection->get_selection().front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key()); - if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retreive_locked || _is_node_editable(canvas_item))) { + if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retreive_locked || !_is_node_locked(canvas_item))) { CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item); if (se) { selection.push_back(canvas_item); @@ -1287,18 +1298,28 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { // Start rotation if (drag_type == DRAG_NONE) { if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { - drag_selection = _get_edited_canvas_items(); - if (drag_selection.size() > 0 && ((b->get_control() && !b->get_alt() && tool == TOOL_SELECT) || tool == TOOL_ROTATE)) { - drag_type = DRAG_ROTATE; - drag_from = transform.affine_inverse().xform(b->get_position()); - CanvasItem *canvas_item = drag_selection[0]; - if (canvas_item->_edit_use_pivot()) { - drag_rotation_center = canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_pivot()); - } else { - drag_rotation_center = canvas_item->get_global_transform_with_canvas().get_origin(); + if ((b->get_control() && !b->get_alt() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) { + List<CanvasItem *> selection = _get_edited_canvas_items(); + + // Remove not movable nodes + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + if (!_is_node_movable(E->get(), true)) + selection.erase(E); + } + + drag_selection = selection; + if (drag_selection.size() > 0) { + drag_type = DRAG_ROTATE; + drag_from = transform.affine_inverse().xform(b->get_position()); + CanvasItem *canvas_item = drag_selection[0]; + if (canvas_item->_edit_use_pivot()) { + drag_rotation_center = canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_pivot()); + } else { + drag_rotation_center = canvas_item->get_global_transform_with_canvas().get_origin(); + } + _save_canvas_item_state(drag_selection); + return true; } - _save_canvas_item_state(drag_selection); - return true; } } } @@ -1361,7 +1382,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { List<CanvasItem *> selection = _get_edited_canvas_items(); if (selection.size() == 1) { Control *control = Object::cast_to<Control>(selection[0]); - if (control && !Object::cast_to<Container>(control->get_parent())) { + if (control && _is_node_movable(control)) { Vector2 anchor_pos[4]; anchor_pos[0] = Vector2(control->get_anchor(MARGIN_LEFT), control->get_anchor(MARGIN_TOP)); anchor_pos[1] = Vector2(control->get_anchor(MARGIN_RIGHT), control->get_anchor(MARGIN_TOP)); @@ -1480,7 +1501,7 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { List<CanvasItem *> selection = _get_edited_canvas_items(); if (selection.size() == 1) { CanvasItem *canvas_item = selection[0]; - if (canvas_item->_edit_use_rect()) { + if (canvas_item->_edit_use_rect() && _is_node_movable(canvas_item)) { Rect2 rect = canvas_item->_edit_get_rect(); Transform2D xform = transform * canvas_item->get_global_transform_with_canvas(); @@ -1648,27 +1669,30 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { if (selection.size() == 1) { CanvasItem *canvas_item = selection[0]; - Transform2D xform = transform * canvas_item->get_global_transform_with_canvas(); - Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); - Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + if (_is_node_movable(canvas_item)) { + + Transform2D xform = transform * canvas_item->get_global_transform_with_canvas(); + Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; - drag_type = DRAG_SCALE_BOTH; + drag_type = DRAG_SCALE_BOTH; - Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); - Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { - drag_type = DRAG_SCALE_X; - } - Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { - drag_type = DRAG_SCALE_Y; - } + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); + Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { + drag_type = DRAG_SCALE_X; + } + Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { + drag_type = DRAG_SCALE_Y; + } - drag_from = transform.affine_inverse().xform(b->get_position()); - drag_selection = List<CanvasItem *>(); - drag_selection.push_back(canvas_item); - _save_canvas_item_state(drag_selection); - return true; + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = List<CanvasItem *>(); + drag_selection.push_back(canvas_item); + _save_canvas_item_state(drag_selection); + return true; + } } } } @@ -1696,13 +1720,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_SCALE_BOTH) { Size2 scale_factor = drag_to_local / drag_from_local; if (uniform) { - if (ABS(offset.x) > ABS(offset.y)) { - scale.x *= scale_factor.x; - scale.y = scale.x * ratio; - } else { - scale.y *= scale_factor.y; - scale.x = scale.y / ratio; - } + scale *= (scale_factor.x + scale_factor.y) / 2.0; } else { scale *= scale_factor; } @@ -1753,12 +1771,22 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_NONE) { //Start moving the nodes if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && b->is_pressed()) { - List<CanvasItem *> selection = _get_edited_canvas_items(); - if (((b->get_alt() && !b->get_control()) || tool == TOOL_MOVE) && selection.size() > 0) { - drag_type = DRAG_MOVE; - drag_from = transform.affine_inverse().xform(b->get_position()); - drag_selection = selection; - _save_canvas_item_state(drag_selection); + if ((b->get_alt() && !b->get_control()) || tool == TOOL_MOVE) { + List<CanvasItem *> selection = _get_edited_canvas_items(); + + // Remove not movable nodes + for (int i = 0; i < selection.size(); i++) { + if (!_is_node_movable(selection[i], true)) { + selection.erase(selection[i]); + } + } + + if (selection.size() > 0) { + drag_type = DRAG_MOVE; + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = selection; + _save_canvas_item_state(drag_selection); + } return true; } } @@ -2008,7 +2036,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { Vector<_SelectResult> selection; // Retrieve the items - _get_canvas_items_at_pos(click, selection, editor_selection->get_selection().empty() ? 1 : 0); + _get_canvas_items_at_pos(click, selection); // Retrieve the bones _get_bones_at_pos(click, selection); @@ -2036,10 +2064,19 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Drag the node(s) if requested List<CanvasItem *> selection = _get_edited_canvas_items(); - drag_type = DRAG_MOVE; - drag_selection = selection; - drag_from = click; - _save_canvas_item_state(drag_selection); + // Remove not movable nodes + for (int i = 0; i < selection.size(); i++) { + if (!_is_node_movable(selection[i], true)) { + selection.erase(selection[i]); + } + } + + if (selection.size() > 0) { + drag_type = DRAG_MOVE; + drag_selection = selection; + drag_from = click; + _save_canvas_item_state(drag_selection); + } } // Select the item return true; @@ -2172,6 +2209,8 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { //printf("Zoom or pan\n"); } else if ((accepted = _gui_input_select(p_event))) { //printf("Selection\n"); + } else { + //printf("Not accepted\n"); } if (accepted) @@ -2702,12 +2741,12 @@ void CanvasItemEditor::_draw_selection() { // Draw control-related helpers Control *control = Object::cast_to<Control>(canvas_item); - if (control) { + if (control && _is_node_movable(control)) { _draw_control_helpers(control); } // Draw the resize handles - if (tool == TOOL_SELECT && canvas_item->_edit_use_rect()) { + if (tool == TOOL_SELECT && canvas_item->_edit_use_rect() && _is_node_movable(canvas_item)) { Rect2 rect = canvas_item->_edit_get_rect(); Vector2 endpoints[4] = { xform.xform(rect.position), @@ -2735,40 +2774,41 @@ void CanvasItemEditor::_draw_selection() { bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CONTROL); bool is_alt = Input::get_singleton()->is_key_pressed(KEY_ALT); if ((is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { - - Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); - Transform2D simple_xform = viewport->get_transform() * unscaled_transform; - - Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); - bool uniform = Input::get_singleton()->is_key_pressed(KEY_SHIFT); - Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom; - - if (drag_type == DRAG_SCALE_X) { - scale_factor.x += offset.x; - if (uniform) { - scale_factor.y += offset.x; - } - } else if (drag_type == DRAG_SCALE_Y) { - scale_factor.y -= offset.y; - if (uniform) { - scale_factor.x -= offset.y; + if (_is_node_movable(canvas_item)) { + Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); + bool uniform = Input::get_singleton()->is_key_pressed(KEY_SHIFT); + Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom; + + if (drag_type == DRAG_SCALE_X) { + scale_factor.x += offset.x; + if (uniform) { + scale_factor.y += offset.x; + } + } else if (drag_type == DRAG_SCALE_Y) { + scale_factor.y -= offset.y; + if (uniform) { + scale_factor.x -= offset.y; + } } - } - //scale_factor *= zoom; + //scale_factor *= zoom; - viewport->draw_set_transform_matrix(simple_xform); - Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - Color x_axis_color(1.0, 0.4, 0.4, 0.6); - viewport->draw_rect(x_handle_rect, x_axis_color); - viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), x_axis_color); + viewport->draw_set_transform_matrix(simple_xform); + Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + Color x_axis_color(1.0, 0.4, 0.4, 0.6); + viewport->draw_rect(x_handle_rect, x_axis_color); + viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), x_axis_color); - Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - Color y_axis_color(0.4, 1.0, 0.4, 0.6); - viewport->draw_rect(y_handle_rect, y_axis_color); - viewport->draw_line(Point2(), Point2(0, -scale_factor.y * EDSCALE), y_axis_color); + Rect2 y_handle_rect = Rect2(-5 * EDSCALE, -(scale_factor.y + 10) * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + Color y_axis_color(0.4, 1.0, 0.4, 0.6); + viewport->draw_rect(y_handle_rect, y_axis_color); + viewport->draw_line(Point2(), Point2(0, -scale_factor.y * EDSCALE), y_axis_color); - viewport->draw_set_transform_matrix(viewport->get_transform()); + viewport->draw_set_transform_matrix(viewport->get_transform()); + } } } } @@ -2944,7 +2984,7 @@ void CanvasItemEditor::_draw_invisible_nodes_positions(Node *p_node, const Trans _draw_invisible_nodes_positions(p_node->get_child(i), parent_xform, canvas_xform); } - if (canvas_item && !canvas_item->_edit_use_rect() && (!editor_selection->is_selected(canvas_item) || !_is_node_editable(canvas_item))) { + if (canvas_item && !canvas_item->_edit_use_rect() && (!editor_selection->is_selected(canvas_item) || _is_node_locked(canvas_item))) { Transform2D xform = transform * canvas_xform * parent_xform; // Draw the node's position @@ -3133,6 +3173,8 @@ void CanvasItemEditor::_draw_viewport() { group_button->set_disabled(selection.empty()); ungroup_button->set_visible(all_group); + info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10); + _draw_grid(); _draw_selection(); _draw_axis(); @@ -3361,11 +3403,14 @@ void CanvasItemEditor::_notification(int p_what) { void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { - drag_type = DRAG_NONE; + Array selection = editor_selection->get_selected_nodes(); + if (selection.size() != 1 || (Node *)selection[0] != p_canvas_item) { + drag_type = DRAG_NONE; - // Clear the selection - editor_selection->clear(); //_clear_canvas_items(); - editor_selection->add_node(p_canvas_item); + // Clear the selection + editor_selection->clear(); //_clear_canvas_items(); + editor_selection->add_node(p_canvas_item); + } } void CanvasItemEditor::_queue_update_bone_list() { @@ -3497,6 +3542,35 @@ void CanvasItemEditor::_update_scrollbars() { updating_scroll = false; } +void CanvasItemEditor::_popup_warning_depop(Control *p_control) { + ERR_FAIL_COND(!popup_temporarily_timers.has(p_control)); + + Timer *timer = popup_temporarily_timers[p_control]; + p_control->hide(); + remove_child(timer); + popup_temporarily_timers.erase(p_control); + memdelete(timer); + info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10); +} + +void CanvasItemEditor::_popup_warning_temporarily(Control *p_control, const float p_duration) { + Timer *timer; + if (!popup_temporarily_timers.has(p_control)) { + timer = memnew(Timer); + timer->connect("timeout", this, "_popup_warning_depop", varray(p_control)); + timer->set_one_shot(true); + add_child(timer); + + popup_temporarily_timers[p_control] = timer; + } else { + timer = popup_temporarily_timers[p_control]; + } + + timer->start(p_duration); + p_control->show(); + info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10); +} + void CanvasItemEditor::_update_scroll(float) { if (updating_scroll) @@ -4203,7 +4277,7 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed); ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed); - + ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop); ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state); @@ -4389,7 +4463,22 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { viewport->update(); } +void CanvasItemEditor::add_control_to_info_overlay(Control *p_control) { + ERR_FAIL_COND(!p_control); + + p_control->set_h_size_flags(p_control->get_h_size_flags() & ~Control::SIZE_EXPAND_FILL); + info_overlay->add_child(p_control); + info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10); +} + +void CanvasItemEditor::remove_control_from_info_overlay(Control *p_control) { + + info_overlay->remove_child(p_control); + info_overlay->set_margin(MARGIN_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10); +} + void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) { + ERR_FAIL_COND(!p_control); hb->add_child(p_control); } @@ -4415,6 +4504,46 @@ void CanvasItemEditor::focus_selection() { CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { + key_pos = true; + key_rot = true; + key_scale = false; + + show_grid = false; + show_origin = true; + show_viewport = true; + show_helpers = false; + show_rulers = true; + show_guides = true; + show_edit_locks = true; + zoom = 1; + view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH); + previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen + grid_offset = Point2(); + grid_step = Point2(10, 10); + grid_step_multiplier = 0; + snap_rotation_offset = 0; + snap_rotation_step = 15 / (180 / Math_PI); + snap_active = false; + snap_node_parent = true; + snap_node_anchors = true; + snap_node_sides = true; + snap_node_center = true; + snap_other_nodes = true; + snap_grid = true; + snap_guides = true; + snap_rotation = false; + snap_pixel = false; + + skeleton_show_bones = true; + + drag_type = DRAG_NONE; + drag_from = Vector2(); + drag_to = Vector2(); + dragged_guide_pos = Point2(); + dragged_guide_index = -1; + + bone_last_frame = 0; + bone_list_dirty = false; tool = TOOL_SELECT; undo_redo = p_editor->get_undo_redo(); @@ -4458,6 +4587,28 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { viewport->connect("draw", this, "_draw_viewport"); viewport->connect("gui_input", this, "_gui_input_viewport"); + info_overlay = memnew(VBoxContainer); + info_overlay->set_anchors_and_margins_preset(Control::PRESET_BOTTOM_LEFT); + info_overlay->set_margin(MARGIN_LEFT, 10); + info_overlay->set_margin(MARGIN_BOTTOM, -15); + info_overlay->set_v_grow_direction(Control::GROW_DIRECTION_BEGIN); + info_overlay->add_constant_override("separation", 10); + viewport_scrollable->add_child(info_overlay); + + Theme *info_overlay_theme = memnew(Theme); + info_overlay_theme->copy_default_theme(); + info_overlay->set_theme(info_overlay_theme); + + StyleBoxFlat *info_overlay_label_stylebox = memnew(StyleBoxFlat); + info_overlay_label_stylebox->set_bg_color(Color(0.0, 0.0, 0.0, 0.2)); + info_overlay_label_stylebox->set_expand_margin_size_all(4); + info_overlay_theme->set_stylebox("normal", "Label", info_overlay_label_stylebox); + + warning_child_of_container = memnew(Label); + warning_child_of_container->hide(); + warning_child_of_container->set_text("Warning: Children of a container get their position and size determined only by their parent"); + add_control_to_info_overlay(warning_child_of_container); + h_scroll = memnew(HScrollBar); viewport->add_child(h_scroll); h_scroll->connect("value_changed", this, "_update_scroll"); @@ -4715,49 +4866,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY); divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE); - key_pos = true; - key_rot = true; - key_scale = false; - - show_grid = false; - show_origin = true; - show_viewport = true; - show_helpers = false; - show_rulers = true; - show_guides = true; - show_edit_locks = true; - zoom = 1; - view_offset = Point2(-150 - RULER_WIDTH, -95 - RULER_WIDTH); - previous_update_view_offset = view_offset; // Moves the view a little bit to the left so that (0,0) is visible. The values a relative to a 16/10 screen - grid_offset = Point2(); - grid_step = Point2(10, 10); - grid_step_multiplier = 0; - snap_rotation_offset = 0; - snap_rotation_step = 15 / (180 / Math_PI); - snap_active = false; - snap_node_parent = true; - snap_node_anchors = true; - snap_node_sides = true; - snap_node_center = true; - snap_other_nodes = true; - snap_grid = true; - snap_guides = true; - snap_rotation = false; - snap_pixel = false; - skeleton_show_bones = true; skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true); singleton = this; set_process_unhandled_key_input(true); - drag_type = DRAG_NONE; - drag_from = Vector2(); - drag_to = Vector2(); - dragged_guide_pos = Point2(); - dragged_guide_index = -1; - - bone_last_frame = 0; - // Update the menus' checkboxes call_deferred("set_state", get_state()); } @@ -4957,19 +5070,13 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & editor_data->get_undo_redo().add_do_property(child, "polygon", list); } - // locate at preview position - Point2 pos = Point2(0, 0); - if (parent && parent->has_method("get_global_position")) { - pos = parent->call("get_global_position"); - } - Transform2D trans = canvas->get_canvas_transform(); - Point2 target_position = (p_point - trans.get_origin()) / trans.get_scale().x - pos; - if (default_type == "Polygon2D" || default_type == "TouchScreenButton" || default_type == "TextureRect" || default_type == "NinePatchRect") { - target_position -= texture_size / 2; - } + // Compute the global position + Transform2D xform = canvas_item_editor->get_canvas_transform(); + Point2 target_position = xform.affine_inverse().xform(p_point); + // there's nothing to be used as source position so snapping will work as absolute if enabled - target_position = canvas->snap_point(target_position); - editor_data->get_undo_redo().add_do_method(child, "set_position", target_position); + target_position = canvas_item_editor->snap_point(target_position); + editor_data->get_undo_redo().add_do_method(child, "set_global_position", target_position); } bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { @@ -5004,8 +5111,8 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons CanvasItem *parent_ci = Object::cast_to<CanvasItem>(parent); if (parent_ci) { - Vector2 target_pos = canvas->get_canvas_transform().affine_inverse().xform(p_point); - target_pos = canvas->snap_point(target_pos); + Vector2 target_pos = canvas_item_editor->get_canvas_transform().affine_inverse().xform(p_point); + target_pos = canvas_item_editor->snap_point(target_pos); target_pos = parent_ci->get_global_transform_with_canvas().affine_inverse().xform(target_pos); editor_data->get_undo_redo().add_do_method(instanced_scene, "set_position", target_pos); } @@ -5125,7 +5232,7 @@ bool CanvasItemEditorViewport::can_drop_data(const Point2 &p_point, const Varian if (!preview_node->get_parent()) { // create preview only once _create_preview(files); } - Transform2D trans = canvas->get_canvas_transform(); + Transform2D trans = canvas_item_editor->get_canvas_transform(); preview_node->set_position((p_point - trans.get_origin()) / trans.get_scale().x); label->set_text(vformat(TTR("Adding %s..."), default_type)); } @@ -5220,7 +5327,7 @@ void CanvasItemEditorViewport::_bind_methods() { ClassDB::bind_method(D_METHOD("_on_mouse_exit"), &CanvasItemEditorViewport::_on_mouse_exit); } -CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas) { +CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas_item_editor) { default_type = "Sprite"; // Node2D types.push_back("Sprite"); @@ -5235,7 +5342,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte target_node = NULL; editor = p_node; editor_data = editor->get_scene_tree_dock()->get_editor_data(); - canvas = p_canvas; + canvas_item_editor = p_canvas_item_editor; preview_node = memnew(Node2D); accept = memnew(AcceptDialog); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index dc7b74112f..207e57dbe2 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -64,7 +64,10 @@ public: Dictionary undo_state; - CanvasItemEditorSelectedItem() { prev_rot = 0; } + CanvasItemEditorSelectedItem() : + prev_anchors() { + prev_rot = 0; + } }; class CanvasItemEditor : public VBoxContainer { @@ -220,6 +223,11 @@ private: ToolButton *zoom_reset; ToolButton *zoom_plus; + Map<Control *, Timer *> popup_temporarily_timers; + + Label *warning_child_of_container; + VBoxContainer *info_overlay; + Transform2D transform; bool show_grid; bool show_rulers; @@ -369,9 +377,10 @@ private: Ref<ShortCut> multiply_grid_step_shortcut; Ref<ShortCut> divide_grid_step_shortcut; - bool _is_node_editable(const Node *p_node); - void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, int p_limit = 0, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); - void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items, int p_limit = 0); + bool _is_node_locked(const Node *p_node); + bool _is_node_movable(const Node *p_node, bool p_popup_warning = false); + void _find_canvas_items_at_pos(const Point2 &p_pos, Node *p_node, Vector<_SelectResult> &r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); + void _get_canvas_items_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items); void _get_bones_at_pos(const Point2 &p_pos, Vector<_SelectResult> &r_items); void _find_canvas_items_in_rect(const Rect2 &p_rect, Node *p_node, List<CanvasItem *> *r_items, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D()); @@ -477,6 +486,9 @@ private: void _update_bone_list(); void _tree_changed(Node *); + void _popup_warning_temporarily(Control *p_control, const float p_duration); + void _popup_warning_depop(Control *p_control); + friend class CanvasItemEditorPlugin; protected: @@ -542,6 +554,9 @@ public: void add_control_to_menu_panel(Control *p_control); void remove_control_from_menu_panel(Control *p_control); + void add_control_to_info_overlay(Control *p_control); + void remove_control_from_info_overlay(Control *p_control); + HSplitContainer *get_palette_split(); VSplitContainer *get_bottom_split(); @@ -593,7 +608,7 @@ class CanvasItemEditorViewport : public Control { EditorNode *editor; EditorData *editor_data; - CanvasItemEditor *canvas; + CanvasItemEditor *canvas_item_editor; Node2D *preview_node; AcceptDialog *accept; WindowDialog *selector; @@ -627,7 +642,7 @@ public: virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; virtual void drop_data(const Point2 &p_point, const Variant &p_data); - CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas); + CanvasItemEditorViewport(EditorNode *p_node, CanvasItemEditor *p_canvas_item_editor); ~CanvasItemEditorViewport(); }; diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index 5d85a64b9c..313ba1ee6b 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -203,6 +203,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { } break; } + node->get_shape()->_change_notify(); } void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) { diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index 5dcbca2ed6..ab94258c44 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "core/io/image_loader.h" +#include "scene/2d/cpu_particles_2d.h" #include "scene/gui/separator.h" #include "scene/resources/particles_material.h" @@ -82,6 +83,25 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) { emission_mask->popup_centered_minsize(); } break; + case MENU_OPTION_CONVERT_TO_CPU_PARTICLES: { + + UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + + 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_pause_mode(particles->get_pause_mode()); + + undo_redo->create_action("Replace Particles by CPUParticles"); + undo_redo->add_do_method(particles, "replace_by", cpu_particles); + undo_redo->add_undo_method(cpu_particles, "replace_by", particles); + undo_redo->add_do_reference(cpu_particles); + undo_redo->add_undo_reference(particles); + undo_redo->commit_action(); + + } break; } } @@ -355,6 +375,8 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { menu->get_popup()->add_separator(); menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); + menu->get_popup()->add_separator(); + menu->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); menu->set_text(TTR("Particles")); toolbar->add_child(menu); diff --git a/editor/plugins/particles_2d_editor_plugin.h b/editor/plugins/particles_2d_editor_plugin.h index 71ca8ef499..eaa96d84e9 100644 --- a/editor/plugins/particles_2d_editor_plugin.h +++ b/editor/plugins/particles_2d_editor_plugin.h @@ -46,7 +46,8 @@ class Particles2DEditorPlugin : public EditorPlugin { MENU_GENERATE_VISIBILITY_RECT, MENU_LOAD_EMISSION_MASK, - MENU_CLEAR_EMISSION_MASK + MENU_CLEAR_EMISSION_MASK, + MENU_OPTION_CONVERT_TO_CPU_PARTICLES }; enum EmissionMode { diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 6b41946918..f2dfae7a9f 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -453,6 +453,7 @@ void ParticlesEditor::_bind_methods() { ParticlesEditor::ParticlesEditor() { + node = NULL; particles_editor_hb = memnew(HBoxContainer); SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); options = memnew(MenuButton); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 07a7e7952a..085660b79e 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -210,6 +210,9 @@ void ScriptEditorQuickOpen::_notification(int p_what) { search_box->set_right_icon(get_icon("Search", "EditorIcons")); search_box->set_clear_button_enabled(true); } break; + case NOTIFICATION_EXIT_TREE: { + disconnect("confirmed", this, "_confirmed"); + } break; } } @@ -978,7 +981,7 @@ void ScriptEditor::_menu_option(int p_option) { } break; case SEARCH_WEBSITE: { - OS::get_singleton()->shell_open("http://docs.godotengine.org/"); + OS::get_singleton()->shell_open("https://docs.godotengine.org/"); } break; case WINDOW_NEXT: { @@ -1363,7 +1366,9 @@ void ScriptEditor::_notification(int p_what) { if (is_visible()) { find_in_files_button->show(); } else { - find_in_files->hide(); + if (find_in_files->is_visible_in_tree()) { + editor->hide_bottom_panel(); + } find_in_files_button->hide(); } @@ -2806,8 +2811,7 @@ void ScriptEditor::_start_find_in_files(bool with_replace) { find_in_files->set_with_replace(with_replace); find_in_files->start_search(); - find_in_files_button->set_pressed(true); - find_in_files->show(); + editor->make_bottom_panel_item_visible(find_in_files); } void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) { @@ -2891,7 +2895,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { restoring_layout = false; waiting_update_names = false; pending_auto_reload = false; - auto_reload_running_scripts = false; + auto_reload_running_scripts = true; members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/open_scripts/show_members_overview"); help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index"); editor = p_editor; @@ -2997,7 +3001,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { file_menu->get_popup()->add_separator(); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH); - file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show In File System")), SHOW_IN_FILE_SYSTEM); + file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM); file_menu->get_popup()->add_separator(); file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV); @@ -3174,7 +3178,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { add_child(find_in_files_dialog); find_in_files = memnew(FindInFilesPanel); find_in_files_button = editor->add_bottom_panel_item(TTR("Search Results"), find_in_files); - find_in_files_button->set_tooltip(TTR("Search in files")); find_in_files->set_custom_minimum_size(Size2(0, 200) * EDSCALE); find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected"); find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files"); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 27f5910d94..c3e2aa86f0 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -773,7 +773,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_CLONE_DOWN: { - code_editor->code_lines_down(); + code_editor->clone_lines_down(); } break; case EDIT_TOGGLE_FOLD_LINE: { @@ -1583,8 +1583,8 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE); #endif ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T); - ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent To Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y); - ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent To Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_I); + ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent to Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y); + ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_I); ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD | KEY_I); #ifdef OSX_ENABLED diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 17f93b55a1..638de1b213 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -263,7 +263,7 @@ void ShaderEditor::_menu_option(int p_option) { shader_editor->delete_lines(); } break; case EDIT_CLONE_DOWN: { - shader_editor->code_lines_down(); + shader_editor->clone_lines_down(); } break; case EDIT_TOGGLE_COMMENT: { diff --git a/editor/plugins/skeleton_editor_plugin.cpp b/editor/plugins/skeleton_editor_plugin.cpp index e7d9f1b702..8b0beefb3e 100644 --- a/editor/plugins/skeleton_editor_plugin.cpp +++ b/editor/plugins/skeleton_editor_plugin.cpp @@ -151,6 +151,7 @@ void SkeletonEditor::_bind_methods() { } SkeletonEditor::SkeletonEditor() { + skeleton = NULL; options = memnew(MenuButton); SpatialEditor::get_singleton()->add_control_to_menu_panel(options); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index ab89d170da..5566a5aea5 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -1901,7 +1901,7 @@ void SpatialEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, con real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); - bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis"); + bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); if (invert_y_axis) { cursor.x_rot -= p_relative.y * radians_per_pixel; @@ -1926,7 +1926,7 @@ void SpatialEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, cons real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); - bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y-axis"); + bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". Transform prev_camera_transform = to_camera_transform(cursor); @@ -2231,8 +2231,21 @@ void SpatialEditorViewport::_notification(int p_what) { bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); info_label->set_visible(show_info); + Camera *current_camera; + + if (previewing) { + current_camera = previewing; + } else { + current_camera = camera; + } + if (show_info) { String text; + text += "X: " + rtos(current_camera->get_translation().x).pad_decimals(1) + "\n"; + text += "Y: " + rtos(current_camera->get_translation().y).pad_decimals(1) + "\n"; + text += "Z: " + rtos(current_camera->get_translation().z).pad_decimals(1) + "\n"; + text += TTR("Pitch") + ": " + itos(Math::round(current_camera->get_rotation_degrees().x)) + "\n"; + text += TTR("Yaw") + ": " + itos(Math::round(current_camera->get_rotation_degrees().y)) + "\n\n"; text += TTR("Objects Drawn") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_OBJECTS_IN_FRAME)) + "\n"; text += TTR("Material Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_MATERIAL_CHANGES_IN_FRAME)) + "\n"; text += TTR("Shader Changes") + ": " + itos(viewport->get_render_info(Viewport::RENDER_INFO_SHADER_CHANGES_IN_FRAME)) + "\n"; diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 30246147c2..40781908fd 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -548,7 +548,6 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) { } else { hide(); - //set_physics_process(false); } } @@ -816,16 +815,26 @@ SpriteFramesEditor::SpriteFramesEditor() { void SpriteFramesEditorPlugin::edit(Object *p_object) { frames_editor->set_undo_redo(&get_undo_redo()); - SpriteFrames *s = Object::cast_to<SpriteFrames>(p_object); - if (!s) - return; + + SpriteFrames *s; + AnimatedSprite *animated_sprite = Object::cast_to<AnimatedSprite>(p_object); + if (animated_sprite) { + s = *animated_sprite->get_sprite_frames(); + } else { + s = Object::cast_to<SpriteFrames>(p_object); + } frames_editor->edit(s); } bool SpriteFramesEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("SpriteFrames"); + AnimatedSprite *animated_sprite = Object::cast_to<AnimatedSprite>(p_object); + if (animated_sprite && *animated_sprite->get_sprite_frames()) { + return true; + } else { + return p_object->is_class("SpriteFrames"); + } } void SpriteFramesEditorPlugin::make_visible(bool p_visible) { @@ -833,14 +842,11 @@ void SpriteFramesEditorPlugin::make_visible(bool p_visible) { if (p_visible) { button->show(); editor->make_bottom_panel_item_visible(frames_editor); - //frames_editor->set_process(true); } else { button->hide(); if (frames_editor->is_visible_in_tree()) editor->hide_bottom_panel(); - - //frames_editor->set_process(false); } } diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 1a43b16f3e..4a8eae1ba4 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -360,7 +360,7 @@ void TextEditor::_edit_option(int p_op) { } break; case EDIT_CLONE_DOWN: { - code_editor->code_lines_down(); + code_editor->clone_lines_down(); } break; case EDIT_TOGGLE_FOLD_LINE: { diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 33e1f7c6a3..7c3e524d89 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -846,7 +846,8 @@ TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) { p->set_item_checked(0, true); p->connect("id_pressed", this, "_set_snap_mode"); hb_grid = memnew(HBoxContainer); - hb_tools->add_child(hb_grid); + //hb_tools->add_child(hb_grid); + main_vb->add_child(hb_grid); hb_grid->add_child(memnew(VSeparator)); hb_grid->add_child(memnew(Label(TTR("Offset:")))); diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index ed1fa9b217..213cd2ce1a 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "tile_map_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/math/math_funcs.h" #include "core/os/input.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" @@ -65,13 +66,11 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { - transp->set_icon(get_icon("Transpose", "EditorIcons")); - mirror_x->set_icon(get_icon("MirrorX", "EditorIcons")); - mirror_y->set_icon(get_icon("MirrorY", "EditorIcons")); - rotate_0->set_icon(get_icon("Rotate0", "EditorIcons")); - rotate_90->set_icon(get_icon("Rotate90", "EditorIcons")); - rotate_180->set_icon(get_icon("Rotate180", "EditorIcons")); - rotate_270->set_icon(get_icon("Rotate270", "EditorIcons")); + rotate_left_button->set_icon(get_icon("Rotate270", "EditorIcons")); + rotate_right_button->set_icon(get_icon("Rotate90", "EditorIcons")); + flip_horizontal_button->set_icon(get_icon("MirrorX", "EditorIcons")); + flip_vertical_button->set_icon(get_icon("MirrorY", "EditorIcons")); + clear_transform_button->set_icon(get_icon("Clear", "EditorIcons")); search_box->set_right_icon(get_icon("Search", "EditorIcons")); search_box->set_clear_button_enabled(true); @@ -301,7 +300,7 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p } node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose); - if (manual_autotile || node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE) { + if (manual_autotile || (p_value != -1 && node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE)) { if (current != -1) { node->set_cell_autotile_coord(p_pos.x, p_pos.y, position); } @@ -357,6 +356,10 @@ void TileMapEditor::_update_palette() { if (!node) return; + // Update the clear button + clear_transform_button->set_disabled(!flip_h && !flip_v && !transpose); + + // Update the palette Vector<int> selected = get_selected_tiles(); palette->clear(); manual_palette->clear(); @@ -429,9 +432,6 @@ void TileMapEditor::_update_palette() { Ref<Texture> tex = tileset->tile_get_texture(entries[i].id); if (tex.is_valid()) { - Color color = tileset->tile_get_modulate(entries[i].id); - palette->set_item_icon_modulate(palette->get_item_count() - 1, color); - Rect2 region = tileset->tile_get_region(entries[i].id); if (tileset->tile_get_tile_mode(entries[i].id) == TileSet::AUTO_TILE || tileset->tile_get_tile_mode(entries[i].id) == TileSet::ATLAS_TILE) { @@ -440,10 +440,25 @@ void TileMapEditor::_update_palette() { region.position += (region.size + Vector2(spacing, spacing)) * tileset->autotile_get_icon_coordinate(entries[i].id); } - if (!region.has_no_area()) + // Transpose and flip + palette->set_item_icon_transposed(palette->get_item_count() - 1, transpose); + if (flip_h) { + region.size.x = -region.size.x; + } + if (flip_v) { + region.size.y = -region.size.y; + } + + // Set region + if (region.size != Size2()) palette->set_item_icon_region(palette->get_item_count() - 1, region); + // Set icon palette->set_item_icon(palette->get_item_count() - 1, tex); + + // Modulation + Color color = tileset->tile_get_modulate(entries[i].id); + palette->set_item_icon_modulate(palette->get_item_count() - 1, color); } palette->set_item_metadata(palette->get_item_count() - 1, entries[i].id); @@ -519,11 +534,11 @@ void TileMapEditor::_pick_tile(const Point2 &p_pos) { selected.push_back(id); set_selected_tiles(selected); - mirror_x->set_pressed(node->is_cell_x_flipped(p_pos.x, p_pos.y)); - mirror_y->set_pressed(node->is_cell_y_flipped(p_pos.x, p_pos.y)); - transp->set_pressed(node->is_cell_transposed(p_pos.x, p_pos.y)); + flip_h = node->is_cell_x_flipped(p_pos.x, p_pos.y); + flip_v = node->is_cell_y_flipped(p_pos.x, p_pos.y); + transpose = node->is_cell_transposed(p_pos.x, p_pos.y); - _update_transform_buttons(); + _update_palette(); CanvasItemEditor::get_singleton()->update_viewport(); } @@ -1366,22 +1381,19 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } if (ED_IS_SHORTCUT("tile_map_editor/mirror_x", p_event)) { flip_h = !flip_h; - mirror_x->set_pressed(flip_h); - _update_transform_buttons(); + _update_palette(); CanvasItemEditor::get_singleton()->update_viewport(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/mirror_y", p_event)) { flip_v = !flip_v; - mirror_y->set_pressed(flip_v); - _update_transform_buttons(); + _update_palette(); CanvasItemEditor::get_singleton()->update_viewport(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/transpose", p_event)) { transpose = !transpose; - transp->set_pressed(transpose); - _update_transform_buttons(); + _update_palette(); CanvasItemEditor::get_singleton()->update_viewport(); return true; } @@ -1664,7 +1676,10 @@ void TileMapEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_canvas_mouse_enter"), &TileMapEditor::_canvas_mouse_enter); ClassDB::bind_method(D_METHOD("_canvas_mouse_exit"), &TileMapEditor::_canvas_mouse_exit); ClassDB::bind_method(D_METHOD("_tileset_settings_changed"), &TileMapEditor::_tileset_settings_changed); - ClassDB::bind_method(D_METHOD("_update_transform_buttons"), &TileMapEditor::_update_transform_buttons); + ClassDB::bind_method(D_METHOD("_rotate"), &TileMapEditor::_rotate); + ClassDB::bind_method(D_METHOD("_flip_horizontal"), &TileMapEditor::_flip_horizontal); + ClassDB::bind_method(D_METHOD("_flip_vertical"), &TileMapEditor::_flip_vertical); + ClassDB::bind_method(D_METHOD("_clear_transform"), &TileMapEditor::_clear_transform); ClassDB::bind_method(D_METHOD("_palette_selected"), &TileMapEditor::_palette_selected); ClassDB::bind_method(D_METHOD("_palette_multi_selected"), &TileMapEditor::_palette_multi_selected); @@ -1689,37 +1704,67 @@ TileMapEditor::CellOp TileMapEditor::_get_op_from_cell(const Point2i &p_pos) { return op; } -void TileMapEditor::_update_transform_buttons(Object *p_button) { - //ERR_FAIL_NULL(p_button); - ToolButton *b = Object::cast_to<ToolButton>(p_button); - //ERR_FAIL_COND(!b); - - if (b == rotate_0) { - mirror_x->set_pressed(false); - mirror_y->set_pressed(false); - transp->set_pressed(false); - } else if (b == rotate_90) { - mirror_x->set_pressed(true); - mirror_y->set_pressed(false); - transp->set_pressed(true); - } else if (b == rotate_180) { - mirror_x->set_pressed(true); - mirror_y->set_pressed(true); - transp->set_pressed(false); - } else if (b == rotate_270) { - mirror_x->set_pressed(false); - mirror_y->set_pressed(true); - transp->set_pressed(true); +void TileMapEditor::_rotate(int steps) { + const bool normal_rotation_matrix[][3] = { + { false, false, false }, + { true, true, false }, + { false, true, true }, + { true, false, true } + }; + + const bool mirrored_rotation_matrix[][3] = { + { false, true, false }, + { true, true, true }, + { false, false, true }, + { true, false, false } + }; + + if (transpose ^ flip_h ^ flip_v) { + // Odd number of flags activated = mirrored rotation + for (int i = 0; i < 4; i++) { + if (transpose == mirrored_rotation_matrix[i][0] && + flip_h == mirrored_rotation_matrix[i][1] && + flip_v == mirrored_rotation_matrix[i][2]) { + int new_id = Math::wrapi(i + steps, 0, 4); + transpose = mirrored_rotation_matrix[new_id][0]; + flip_h = mirrored_rotation_matrix[new_id][1]; + flip_v = mirrored_rotation_matrix[new_id][2]; + break; + } + } + } else { + // Even number of flags activated = normal rotation + for (int i = 0; i < 4; i++) { + if (transpose == normal_rotation_matrix[i][0] && + flip_h == normal_rotation_matrix[i][1] && + flip_v == normal_rotation_matrix[i][2]) { + int new_id = Math::wrapi(i + steps, 0, 4); + transpose = normal_rotation_matrix[new_id][0]; + flip_h = normal_rotation_matrix[new_id][1]; + flip_v = normal_rotation_matrix[new_id][2]; + break; + } + } } - flip_h = mirror_x->is_pressed(); - flip_v = mirror_y->is_pressed(); - transpose = transp->is_pressed(); + _update_palette(); +} + +void TileMapEditor::_flip_horizontal() { + flip_h = !flip_h; + _update_palette(); +} - rotate_0->set_pressed(!flip_h && !flip_v && !transpose); - rotate_90->set_pressed(flip_h && !flip_v && transpose); - rotate_180->set_pressed(flip_h && flip_v && !transpose); - rotate_270->set_pressed(!flip_h && flip_v && transpose); +void TileMapEditor::_flip_vertical() { + flip_v = !flip_v; + _update_palette(); +} + +void TileMapEditor::_clear_transform() { + transpose = false; + flip_h = false; + flip_v = false; + _update_palette(); } TileMapEditor::TileMapEditor(EditorNode *p_editor) { @@ -1752,10 +1797,8 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { ED_SHORTCUT("tile_map_editor/mirror_x", TTR("Mirror X"), KEY_A); ED_SHORTCUT("tile_map_editor/mirror_y", TTR("Mirror Y"), KEY_S); - HBoxContainer *tool_hb1 = memnew(HBoxContainer); - add_child(tool_hb1); - HBoxContainer *tool_hb2 = memnew(HBoxContainer); - add_child(tool_hb2); + HBoxContainer *tool_hb = memnew(HBoxContainer); + add_child(tool_hb); manual_button = memnew(CheckBox); manual_button->set_text("Disable Autotile"); @@ -1840,52 +1883,37 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { p->connect("id_pressed", this, "_menu_option"); toolbar->add_child(options); - - transp = memnew(ToolButton); - transp->set_toggle_mode(true); - transp->set_tooltip(TTR("Transpose") + " (" + ED_GET_SHORTCUT("tile_map_editor/transpose")->get_as_text() + ")"); - transp->set_focus_mode(FOCUS_NONE); - transp->connect("pressed", this, "_update_transform_buttons", make_binds(transp)); - tool_hb1->add_child(transp); - mirror_x = memnew(ToolButton); - mirror_x->set_toggle_mode(true); - mirror_x->set_tooltip(TTR("Mirror X") + " (" + ED_GET_SHORTCUT("tile_map_editor/mirror_x")->get_as_text() + ")"); - mirror_x->set_focus_mode(FOCUS_NONE); - mirror_x->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_x)); - tool_hb1->add_child(mirror_x); - mirror_y = memnew(ToolButton); - mirror_y->set_toggle_mode(true); - mirror_y->set_tooltip(TTR("Mirror Y") + " (" + ED_GET_SHORTCUT("tile_map_editor/mirror_y")->get_as_text() + ")"); - mirror_y->set_focus_mode(FOCUS_NONE); - mirror_y->connect("pressed", this, "_update_transform_buttons", make_binds(mirror_y)); - tool_hb1->add_child(mirror_y); - - rotate_0 = memnew(ToolButton); - rotate_0->set_toggle_mode(true); - rotate_0->set_tooltip(TTR("Rotate 0 degrees")); - rotate_0->set_focus_mode(FOCUS_NONE); - rotate_0->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_0)); - tool_hb2->add_child(rotate_0); - rotate_90 = memnew(ToolButton); - rotate_90->set_toggle_mode(true); - rotate_90->set_tooltip(TTR("Rotate 90 degrees")); - rotate_90->set_focus_mode(FOCUS_NONE); - rotate_90->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_90)); - tool_hb2->add_child(rotate_90); - rotate_180 = memnew(ToolButton); - rotate_180->set_toggle_mode(true); - rotate_180->set_tooltip(TTR("Rotate 180 degrees")); - rotate_180->set_focus_mode(FOCUS_NONE); - rotate_180->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_180)); - tool_hb2->add_child(rotate_180); - rotate_270 = memnew(ToolButton); - rotate_270->set_toggle_mode(true); - rotate_270->set_tooltip(TTR("Rotate 270 degrees")); - rotate_270->set_focus_mode(FOCUS_NONE); - rotate_270->connect("pressed", this, "_update_transform_buttons", make_binds(rotate_270)); - tool_hb2->add_child(rotate_270); - - rotate_0->set_pressed(true); + rotate_left_button = memnew(ToolButton); + rotate_left_button->set_tooltip(TTR("Rotate left")); + rotate_left_button->set_focus_mode(FOCUS_NONE); + rotate_left_button->connect("pressed", this, "_rotate", varray(-1)); + tool_hb->add_child(rotate_left_button); + + rotate_right_button = memnew(ToolButton); + rotate_right_button->set_tooltip(TTR("Rotate right")); + rotate_right_button->set_focus_mode(FOCUS_NONE); + rotate_right_button->connect("pressed", this, "_rotate", varray(1)); + tool_hb->add_child(rotate_right_button); + + flip_horizontal_button = memnew(ToolButton); + flip_horizontal_button->set_tooltip(TTR("Flip horizontally")); + flip_horizontal_button->set_focus_mode(FOCUS_NONE); + flip_horizontal_button->connect("pressed", this, "_flip_horizontal"); + tool_hb->add_child(flip_horizontal_button); + + flip_vertical_button = memnew(ToolButton); + flip_vertical_button->set_tooltip(TTR("Flip vertically")); + flip_vertical_button->set_focus_mode(FOCUS_NONE); + flip_vertical_button->connect("pressed", this, "_flip_vertical"); + tool_hb->add_child(flip_vertical_button); + + clear_transform_button = memnew(ToolButton); + clear_transform_button->set_tooltip(TTR("Clear transform")); + clear_transform_button->set_focus_mode(FOCUS_NONE); + clear_transform_button->connect("pressed", this, "_clear_transform"); + tool_hb->add_child(clear_transform_button); + + clear_transform_button->set_disabled(true); } TileMapEditor::~TileMapEditor() { diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index 3d44647a1b..68e5806ee5 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -93,13 +93,13 @@ class TileMapEditor : public VBoxContainer { Label *tile_info; MenuButton *options; - ToolButton *transp; - ToolButton *mirror_x; - ToolButton *mirror_y; - ToolButton *rotate_0; - ToolButton *rotate_90; - ToolButton *rotate_180; - ToolButton *rotate_270; + + ToolButton *flip_horizontal_button; + ToolButton *flip_vertical_button; + ToolButton *rotate_left_button; + ToolButton *rotate_right_button; + ToolButton *clear_transform_button; + CheckBox *manual_button; Tool tool; @@ -196,11 +196,15 @@ class TileMapEditor : public VBoxContainer { void _tileset_settings_changed(); void _icon_size_changed(float p_value); + void _clear_transform(); + void _flip_horizontal(); + void _flip_vertical(); + void _rotate(int steps); + protected: void _notification(int p_what); static void _bind_methods(); CellOp _get_op_from_cell(const Point2i &p_pos); - void _update_transform_buttons(Object *p_button = NULL); public: HBoxContainer *get_toolbar() const { return toolbar; } diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 9988d82fb8..d0e0b3e006 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -483,6 +483,11 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { //--------------- helper = memnew(TilesetEditorContext(this)); tile_names_opacity = 0; + + // config scale + max_scale = 10.0f; + min_scale = 0.1f; + scale_ratio = 1.2f; } TileSetEditor::~TileSetEditor() { @@ -953,7 +958,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Ref<InputEventMouseMotion> mm = p_ie; if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && !creating_shape) { if (!current_tile_region.has_point(mb->get_position())) { List<int> *tiles = new List<int>(); tileset->get_tile_list(tiles); @@ -972,6 +977,15 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } } + + // Mouse Wheel Event + const int _mouse_button_index = mb->get_button_index(); + if (_mouse_button_index == BUTTON_WHEEL_UP && mb->get_control()) { + _zoom_in(); + + } else if (_mouse_button_index == BUTTON_WHEEL_DOWN && mb->get_control()) { + _zoom_out(); + } } // Drag Middle Mouse if (mm.is_valid()) { @@ -1448,23 +1462,11 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { } } } else if (p_tool == ZOOM_OUT) { - float scale = workspace->get_scale().x; - if (scale > 0.1) { - scale /= 2; - workspace->set_scale(Vector2(scale, scale)); - workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); - workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); - } + _zoom_out(); } else if (p_tool == ZOOM_1) { - workspace->set_scale(Vector2(1, 1)); - workspace_container->set_custom_minimum_size(workspace->get_rect().size); - workspace_overlay->set_custom_minimum_size(workspace->get_rect().size); + _reset_zoom(); } else if (p_tool == ZOOM_IN) { - float scale = workspace->get_scale().x; - scale *= 2; - workspace->set_scale(Vector2(scale, scale)); - workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); - workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); + _zoom_in(); } else if (p_tool == TOOL_SELECT) { if (creating_shape) { // Cancel Creation @@ -1503,6 +1505,31 @@ void TileSetEditor::_set_snap_sep(Vector2 p_val) { workspace->update(); } +void TileSetEditor::_zoom_in() { + float scale = workspace->get_scale().x; + if (scale < max_scale) { + scale *= scale_ratio; + workspace->set_scale(Vector2(scale, scale)); + workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); + } +} +void TileSetEditor::_zoom_out() { + + float scale = workspace->get_scale().x; + if (scale > min_scale) { + scale /= scale_ratio; + workspace->set_scale(Vector2(scale, scale)); + workspace_container->set_custom_minimum_size(workspace->get_rect().size * scale); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size * scale); + } +} +void TileSetEditor::_reset_zoom() { + workspace->set_scale(Vector2(1, 1)); + workspace_container->set_custom_minimum_size(workspace->get_rect().size); + workspace_overlay->set_custom_minimum_size(workspace->get_rect().size); +} + void TileSetEditor::draw_highlight_current_tile() { if (get_current_tile() >= 0) { @@ -2084,13 +2111,24 @@ void TileSetEditor::update_texture_list() { List<int> ids; tileset->get_tile_list(&ids); + Vector<int> ids_to_remove; for (List<int>::Element *E = ids.front(); E; E = E->next()) { + // Clear tiles referencing gone textures (user has been already given the chance to fix broken deps) + if (!tileset->tile_get_texture(E->get()).is_valid()) { + ids_to_remove.push_back(E->get()); + ERR_CONTINUE(!tileset->tile_get_texture(E->get()).is_valid()); + } + if (!texture_map.has(tileset->tile_get_texture(E->get())->get_rid())) { texture_list->add_item(tileset->tile_get_texture(E->get())->get_path().get_file()); texture_map.insert(tileset->tile_get_texture(E->get())->get_rid(), tileset->tile_get_texture(E->get())); texture_list->set_item_metadata(texture_list->get_item_count() - 1, tileset->tile_get_texture(E->get())->get_rid()); } } + for (int i = 0; i < ids_to_remove.size(); i++) { + tileset->remove_tile(ids_to_remove[i]); + } + if (texture_list->get_item_count() > 0 && selected_texture.is_valid()) { texture_list->select(texture_list->find_metadata(selected_texture->get_rid())); if (texture_list->get_selected_items().size() > 0) diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 23bf68b90f..bd8a2ddb98 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -141,6 +141,10 @@ class TileSetEditor : public Control { EditMode edit_mode; int current_tile; + float max_scale; + float min_scale; + float scale_ratio; + void update_texture_list(); void update_texture_list_icon(); @@ -178,6 +182,10 @@ private: void _set_snap_off(Vector2 p_val); void _set_snap_sep(Vector2 p_val); + void _zoom_in(); + void _zoom_out(); + void _reset_zoom(); + void draw_highlight_current_tile(); void draw_highlight_subtile(Vector2 coord, const Vector<Vector2> &other_highlighted = Vector<Vector2>()); void draw_tile_subdivision(int p_id, Color p_color) const; diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index 29a780961e..4a98aa411d 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -39,7 +39,7 @@ void BackgroundProgress::_add_task(const String &p_task, const String &p_label, _THREAD_SAFE_METHOD_ ERR_FAIL_COND(tasks.has(p_task)); - Task t; + BackgroundProgress::Task t; t.hb = memnew(HBoxContainer); Label *l = memnew(Label); l->set_text(p_label + " "); @@ -112,7 +112,7 @@ void BackgroundProgress::add_task(const String &p_task, const String &p_label, i void BackgroundProgress::task_step(const String &p_task, int p_step) { //this code is weird, but it prevents deadlock. - bool no_updates; + bool no_updates = true; { _THREAD_SAFE_METHOD_ no_updates = updates.empty(); @@ -167,7 +167,7 @@ void ProgressDialog::_popup() { void ProgressDialog::add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) { ERR_FAIL_COND(tasks.has(p_task)); - Task t; + ProgressDialog::Task t; t.vb = memnew(VBoxContainer); VBoxContainer *vb2 = memnew(VBoxContainer); t.vb->add_margin_child(p_label, vb2); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 3ec49e187f..f8ba6fd4e3 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -50,6 +50,7 @@ void ProjectExportDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { + duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons")); delete_preset->set_icon(get_icon("Remove", "EditorIcons")); connect("confirmed", this, "_export_pck_zip"); custom_feature_display->get_parent_control()->add_style_override("panel", get_stylebox("bg", "Tree")); @@ -58,6 +59,7 @@ void ProjectExportDialog::_notification(int p_what) { EditorSettings::get_singleton()->set("interface/dialogs/export_bounds", get_rect()); } break; case NOTIFICATION_THEME_CHANGED: { + duplicate_preset->set_icon(get_icon("Duplicate", "EditorIcons")); delete_preset->set_icon(get_icon("Remove", "EditorIcons")); Control *panel = custom_feature_display->get_parent_control(); if (panel) @@ -163,14 +165,37 @@ void ProjectExportDialog::_update_presets() { updating = false; } +void ProjectExportDialog::_update_export_all() { + + bool can_export = EditorExport::get_singleton()->get_export_preset_count() > 0 ? true : false; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); + bool needs_templates; + String error; + if (preset->get_export_path() == "" || !preset->get_platform()->can_export(preset, error, needs_templates)) { + can_export = false; + break; + } + } + + if (can_export) { + export_all_button->set_disabled(false); + } else { + export_all_button->set_disabled(true); + } +} + void ProjectExportDialog::_edit_preset(int p_index) { if (p_index < 0 || p_index >= presets->get_item_count()) { name->set_text(""); name->set_editable(false); + export_path->set_editable(false); runnable->set_disabled(true); parameters->edit(NULL); presets->unselect_all(); + duplicate_preset->set_disabled(true); delete_preset->set_disabled(true); sections->hide(); patches->clear(); @@ -188,8 +213,11 @@ void ProjectExportDialog::_edit_preset(int p_index) { sections->show(); name->set_editable(true); + export_path->set_editable(true); + duplicate_preset->set_disabled(false); delete_preset->set_disabled(false); name->set_text(current->get_name()); + export_path->set_text(current->get_export_path()); runnable->set_disabled(false); runnable->set_pressed(current->is_runnable()); parameters->edit(current.ptr()); @@ -260,6 +288,7 @@ void ProjectExportDialog::_edit_preset(int p_index) { custom_features->set_text(current->get_custom_features()); _update_feature_list(); + _update_export_all(); updating = false; } @@ -428,6 +457,73 @@ void ProjectExportDialog::_name_changed(const String &p_string) { _update_presets(); } +void ProjectExportDialog::_export_path_changed(const String &p_string) { + + if (updating) + return; + + Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); + ERR_FAIL_COND(current.is_null()); + + current->set_export_path(p_string); + _update_presets(); +} + +void ProjectExportDialog::_duplicate_preset() { + + Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); + if (current.is_null()) + return; + + Ref<EditorExportPreset> preset = current->get_platform()->create_preset(); + ERR_FAIL_COND(!preset.is_valid()); + + String name = current->get_name() + "" + itos(1); + bool make_runnable = true; + int attempt = 2; + while (true) { + + bool valid = true; + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> p = EditorExport::get_singleton()->get_export_preset(i); + if (p->get_platform() == preset->get_platform() && p->is_runnable()) { + make_runnable = false; + } + if (p->get_name() == name) { + valid = false; + break; + } + } + + if (valid) + break; + + attempt++; + name = current->get_name() + " " + itos(attempt); + } + + preset->set_name(name); + if (make_runnable) + preset->set_runnable(make_runnable); + preset->set_export_filter(current->get_export_filter()); + preset->set_include_filter(current->get_include_filter()); + preset->set_exclude_filter(current->get_exclude_filter()); + Vector<String> list = current->get_patches(); + for (int i = 0; i < list.size(); i++) { + preset->add_patch(list[i]); + } + preset->set_custom_features(current->get_custom_features()); + + for (const List<PropertyInfo>::Element *E = current->get_properties().front(); E; E = E->next()) { + preset->set(E->get().name, current->get(E->get().name)); + } + + EditorExport::get_singleton()->add_export_preset(preset); + _update_presets(); + _edit_preset(EditorExport::get_singleton()->get_export_preset_count() - 1); +} + void ProjectExportDialog::_delete_preset() { Ref<EditorExportPreset> current = EditorExport::get_singleton()->get_export_preset(presets->get_current()); @@ -745,12 +841,19 @@ void ProjectExportDialog::_export_project() { export_project->set_access(FileDialog::ACCESS_FILESYSTEM); export_project->clear_filters(); - String extension = platform->get_binary_extension(current); - if (extension != String()) { - export_project->add_filter("*." + extension + " ; " + platform->get_name() + " Export"); - export_project->set_current_file(default_filename + "." + extension); + List<String> extension_list = platform->get_binary_extensions(current); + for (int i = 0; i < extension_list.size(); i++) { + export_project->add_filter("*." + extension_list[i] + " ; " + platform->get_name() + " Export"); + } + + if (current->get_export_path() != "") { + export_project->set_current_path(current->get_export_path()); } else { - export_project->set_current_file(default_filename); + if (extension_list.size() >= 1) { + export_project->set_current_file(default_filename + "." + extension_list[0]); + } else { + export_project->set_current_file(default_filename); + } } // Ensure that signal is connected if previous attempt left it disconnected with _validate_export_path @@ -772,6 +875,7 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { ERR_FAIL_COND(current.is_null()); Ref<EditorExportPlatform> platform = current->get_platform(); ERR_FAIL_COND(platform.is_null()); + current->set_export_path(p_path); Error err = platform->export_project(current, export_debug->is_pressed(), p_path, 0); if (err != OK) { @@ -782,6 +886,42 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { } } +void ProjectExportDialog::_export_all_dialog() { + + export_all_dialog->show(); + export_all_dialog->popup_centered_minsize(Size2(300, 80)); +} + +void ProjectExportDialog::_export_all_dialog_action(const String &p_str) { + + export_all_dialog->hide(); + + _export_all(p_str == "release" ? false : true); +} + +void ProjectExportDialog::_export_all(bool p_debug) { + + String mode = p_debug ? TTR("Debug") : TTR("Release"); + EditorProgress ep("exportall", TTR("Exporting All") + " " + mode, EditorExport::get_singleton()->get_export_preset_count()); + + for (int i = 0; i < EditorExport::get_singleton()->get_export_preset_count(); i++) { + Ref<EditorExportPreset> preset = EditorExport::get_singleton()->get_export_preset(i); + ERR_FAIL_COND(preset.is_null()); + Ref<EditorExportPlatform> platform = preset->get_platform(); + ERR_FAIL_COND(platform.is_null()); + + ep.step(preset->get_name(), i); + + Error err = platform->export_project(preset, p_debug, preset->get_export_path(), 0); + if (err != OK) { + error_dialog->set_text(TTR("Export templates for this platform are missing/corrupted:") + " " + platform->get_name()); + error_dialog->show(); + error_dialog->popup_centered_minsize(Size2(300, 80)); + ERR_PRINT("Failed to export project"); + } + } +} + void ProjectExportDialog::_bind_methods() { ClassDB::bind_method("_add_preset", &ProjectExportDialog::_add_preset); @@ -789,6 +929,7 @@ void ProjectExportDialog::_bind_methods() { ClassDB::bind_method("_update_parameters", &ProjectExportDialog::_update_parameters); ClassDB::bind_method("_runnable_pressed", &ProjectExportDialog::_runnable_pressed); ClassDB::bind_method("_name_changed", &ProjectExportDialog::_name_changed); + ClassDB::bind_method("_duplicate_preset", &ProjectExportDialog::_duplicate_preset); ClassDB::bind_method("_delete_preset", &ProjectExportDialog::_delete_preset); ClassDB::bind_method("_delete_preset_confirm", &ProjectExportDialog::_delete_preset_confirm); ClassDB::bind_method("get_drag_data_fw", &ProjectExportDialog::get_drag_data_fw); @@ -805,8 +946,12 @@ void ProjectExportDialog::_bind_methods() { ClassDB::bind_method("_export_pck_zip_selected", &ProjectExportDialog::_export_pck_zip_selected); ClassDB::bind_method("_open_export_template_manager", &ProjectExportDialog::_open_export_template_manager); ClassDB::bind_method("_validate_export_path", &ProjectExportDialog::_validate_export_path); + ClassDB::bind_method("_export_path_changed", &ProjectExportDialog::_export_path_changed); ClassDB::bind_method("_export_project", &ProjectExportDialog::_export_project); ClassDB::bind_method("_export_project_to_path", &ProjectExportDialog::_export_project_to_path); + ClassDB::bind_method("_export_all", &ProjectExportDialog::_export_all); + ClassDB::bind_method("_export_all_dialog", &ProjectExportDialog::_export_all_dialog); + ClassDB::bind_method("_export_all_dialog_action", &ProjectExportDialog::_export_all_dialog_action); ClassDB::bind_method("_custom_features_changed", &ProjectExportDialog::_custom_features_changed); ClassDB::bind_method("_tab_changed", &ProjectExportDialog::_tab_changed); } @@ -842,6 +987,9 @@ ProjectExportDialog::ProjectExportDialog() { presets->set_drag_forwarding(this); mc->add_child(presets); presets->connect("item_selected", this, "_edit_preset"); + duplicate_preset = memnew(ToolButton); + preset_hb->add_child(duplicate_preset); + duplicate_preset->connect("pressed", this, "_duplicate_preset"); delete_preset = memnew(ToolButton); preset_hb->add_child(delete_preset); delete_preset->connect("pressed", this, "_delete_preset"); @@ -858,6 +1006,10 @@ ProjectExportDialog::ProjectExportDialog() { runnable->connect("pressed", this, "_runnable_pressed"); settings_vb->add_child(runnable); + export_path = memnew(LineEdit); + settings_vb->add_margin_child(TTR("Export Path:"), export_path); + export_path->connect("text_changed", this, "_export_path_changed"); + sections = memnew(TabContainer); sections->set_tab_align(TabContainer::ALIGN_LEFT); settings_vb->add_child(sections); @@ -948,7 +1100,9 @@ ProjectExportDialog::ProjectExportDialog() { //disable by default name->set_editable(false); + export_path->set_editable(false); runnable->set_disabled(true); + duplicate_preset->set_disabled(true); delete_preset->set_disabled(true); sections->hide(); parameters->edit(NULL); @@ -967,6 +1121,19 @@ ProjectExportDialog::ProjectExportDialog() { // Disable initially before we select a valid preset export_button->set_disabled(true); + export_all_dialog = memnew(ConfirmationDialog); + add_child(export_all_dialog); + export_all_dialog->set_title("Export All"); + export_all_dialog->set_text(TTR("Export mode?")); + export_all_dialog->get_ok()->hide(); + export_all_dialog->add_button(TTR("Debug"), true, "debug"); + export_all_dialog->add_button(TTR("Release"), true, "release"); + export_all_dialog->connect("custom_action", this, "_export_all_dialog_action"); + + export_all_button = add_button(TTR("Export All"), !OS::get_singleton()->get_swap_ok_cancel(), "export"); + export_all_button->connect("pressed", this, "_export_all_dialog"); + export_all_button->set_disabled(true); + export_pck_zip = memnew(FileDialog); export_pck_zip->add_filter("*.zip ; ZIP File"); export_pck_zip->add_filter("*.pck ; Godot Game Pack"); @@ -1030,6 +1197,8 @@ ProjectExportDialog::ProjectExportDialog() { default_filename = "UnnamedProject"; } } + + _update_export_all(); } ProjectExportDialog::~ProjectExportDialog() { diff --git a/editor/project_export.h b/editor/project_export.h index 552c6d7faf..7009968138 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -61,10 +61,12 @@ private: TabContainer *sections; MenuButton *add_preset; + Button *duplicate_preset; Button *delete_preset; ItemList *presets; LineEdit *name; + LineEdit *export_path; EditorInspector *parameters; CheckButton *runnable; @@ -91,6 +93,8 @@ private: ConfirmationDialog *patch_erase; Button *export_button; + Button *export_all_button; + AcceptDialog *export_all_dialog; LineEdit *custom_features; RichTextLabel *custom_feature_display; @@ -106,10 +110,13 @@ private: void _runnable_pressed(); void _update_parameters(const String &p_edited_property); void _name_changed(const String &p_string); + void _export_path_changed(const String &p_string); void _add_preset(int p_platform); void _edit_preset(int p_index); + void _duplicate_preset(); void _delete_preset(); void _delete_preset_confirm(); + void _update_export_all(); void _update_presets(); @@ -139,6 +146,9 @@ private: void _validate_export_path(const String &p_path); void _export_project(); void _export_project_to_path(const String &p_path); + void _export_all_dialog(); + void _export_all_dialog_action(const String &p_str); + void _export_all(bool p_debug); void _update_feature_list(); void _custom_features_changed(const String &p_text); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 83de5a646a..8c906e5f0b 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -1312,7 +1312,7 @@ void ProjectManager::_load_recent_projects() { show->set_modulate(Color(1, 1, 1, 0.5)); path_hb->add_child(show); show->connect("pressed", this, "_show_project", varray(path)); - show->set_tooltip(TTR("Show In File Manager")); + show->set_tooltip(TTR("Show in File Manager")); Label *fpath = memnew(Label(path)); fpath->set_name("path"); diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 9ef6e4332c..8da75b7b3f 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -968,7 +968,7 @@ bool CustomPropertyEditor::edit(Object *p_owner, const String &p_name, Variant:: RES r = v; if (r.is_valid() && r->get_path().is_resource_file()) { menu->add_separator(); - menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); + menu->add_item(TTR("Show in FileSystem"), OBJ_MENU_SHOW_IN_FILE_SYSTEM); } } else { } diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp index 9042bdc7c1..a8c97be936 100644 --- a/editor/property_selector.cpp +++ b/editor/property_selector.cpp @@ -394,6 +394,8 @@ void PropertySelector::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { connect("confirmed", this, "_confirmed"); + } else if (p_what == NOTIFICATION_EXIT_TREE) { + disconnect("confirmed", this, "_confirmed"); } } diff --git a/editor/quick_open.cpp b/editor/quick_open.cpp index e48a0022e8..8dacc3c142 100644 --- a/editor/quick_open.cpp +++ b/editor/quick_open.cpp @@ -261,6 +261,8 @@ void EditorQuickOpen::_notification(int p_what) { search_box->set_right_icon(get_icon("Search", "EditorIcons")); search_box->set_clear_button_enabled(true); + } else if (p_what == NOTIFICATION_EXIT_TREE) { + disconnect("confirmed", this, "_confirmed"); } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index f908641bab..fe438236c9 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -358,15 +358,12 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { ScriptLanguage *l = ScriptServer::get_language(i); if (l->get_type() == existing->get_class()) { String name = l->get_global_class_name(existing->get_path()); - if (ScriptServer::is_global_class(name)) { - if (EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { - inherits = editor->get_editor_data().script_class_get_base(name); - } else if (l->can_inherit_from_file()) { - inherits = "\"" + existing->get_path() + "\""; - } - } else { + if (ScriptServer::is_global_class(name) && EDITOR_GET("interface/editors/derive_script_globals_by_name").operator bool()) { + inherits = name; + } else if (l->can_inherit_from_file()) { inherits = "\"" + existing->get_path() + "\""; } + break; } } } @@ -1503,6 +1500,7 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { editor_data->get_undo_redo().commit_action(); editor->push_item(p_script.operator->()); + _update_script_button(); } void SceneTreeDock::_toggle_editable_children() { @@ -1618,17 +1616,17 @@ void SceneTreeDock::_delete_confirm() { void SceneTreeDock::_update_script_button() { if (EditorNode::get_singleton()->get_editor_selection()->get_selection().size() == 1) { - button_create_script->show(); Node *n = EditorNode::get_singleton()->get_editor_selection()->get_selected_node_list()[0]; if (n->get_script().is_null()) { - button_create_script->set_icon(get_icon("ScriptCreate", "EditorIcons")); - button_create_script->set_tooltip(TTR("Attach a new or existing script for the selected node.")); + button_create_script->show(); + button_clear_script->hide(); } else { - button_create_script->set_icon(get_icon("ScriptExtend", "EditorIcons")); - button_create_script->set_tooltip(TTR("Extend the selected node's script with a new or existing script.")); + button_create_script->hide(); + button_clear_script->show(); } } else { - button_create_script->hide(); + button_create_script->show(); + button_clear_script->hide(); } } @@ -2073,9 +2071,12 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { existing_script = selected->get_script(); } - menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + if (!existing_script.is_valid()) { + menu->add_icon_shortcut(get_icon("ScriptCreate", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + } if (selection.size() > 1 || existing_script.is_valid()) { menu->add_icon_shortcut(get_icon("ScriptRemove", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/clear_script"), TOOL_CLEAR_SCRIPT); + menu->add_icon_shortcut(get_icon("ScriptExtend", "EditorIcons"), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_ATTACH_SCRIPT); } menu->add_separator(); @@ -2339,6 +2340,7 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel ED_SHORTCUT("scene_tree/instance_scene", TTR("Instance Child Scene")); ED_SHORTCUT("scene_tree/change_node_type", TTR("Change Type")); ED_SHORTCUT("scene_tree/attach_script", TTR("Attach Script")); + ED_SHORTCUT("scene_tree/extend_script", TTR("Extend Script")); ED_SHORTCUT("scene_tree/clear_script", TTR("Clear Script")); ED_SHORTCUT("scene_tree/move_up", TTR("Move Up"), KEY_MASK_CMD | KEY_UP); ED_SHORTCUT("scene_tree/move_down", TTR("Move Down"), KEY_MASK_CMD | KEY_DOWN); @@ -2370,10 +2372,12 @@ SceneTreeDock::SceneTreeDock(EditorNode *p_editor, Node *p_scene_root, EditorSel filter->set_h_size_flags(SIZE_EXPAND_FILL); filter->set_placeholder(TTR("Filter nodes")); filter_hbc->add_child(filter); + filter->add_constant_override("minimum_spaces", 0); filter->connect("text_changed", this, "_filter_changed"); tb = memnew(ToolButton); tb->connect("pressed", this, "_tool_selected", make_binds(TOOL_ATTACH_SCRIPT, false)); + tb->set_tooltip(TTR("Attach a new or existing script for the selected node.")); tb->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script")); filter_hbc->add_child(tb); tb->hide(); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index 848e4def6d..95f0c4870e 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -665,6 +665,13 @@ void SceneTreeEditor::_renamed() { Node *n = get_node(np); ERR_FAIL_COND(!n); + // Empty node names are not allowed, so resets it to previous text and show warning + if (which->get_text(0).strip_edges().empty()) { + which->set_text(0, n->get_name()); + EditorNode::get_singleton()->show_warning(TTR("No name provided")); + return; + } + String new_name = which->get_text(0); if (!Node::_validate_node_name(new_name)) { @@ -973,8 +980,11 @@ void SceneTreeEditor::_editor_settings_changed() { if (enable_rl) { tree->add_constant_override("draw_relationship_lines", 1); tree->add_color_override("relationship_line_color", rl_color); - } else + tree->add_constant_override("draw_guides", 0); + } else { tree->add_constant_override("draw_relationship_lines", 0); + tree->add_constant_override("draw_guides", 1); + } } void SceneTreeEditor::_bind_methods() { diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp index a36a844710..faa561ad54 100644 --- a/editor/script_editor_debugger.cpp +++ b/editor/script_editor_debugger.cpp @@ -123,8 +123,8 @@ protected: if (!prop_values.has(p_name) || String(p_name).begins_with("Constants/")) return false; - emit_signal("value_edited", p_name, p_value); prop_values[p_name] = p_value; + emit_signal("value_edited", p_name, p_value); return true; } @@ -467,7 +467,6 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da String type = p_data[1]; Array properties = p_data[2]; - bool is_new_object = false; if (remote_objects.has(id)) { debugObj = remote_objects[id]; } else { @@ -475,10 +474,14 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da debugObj->remote_object_id = id; debugObj->type_name = type; remote_objects[id] = debugObj; - is_new_object = true; debugObj->connect("value_edited", this, "_scene_tree_property_value_edited"); } + int old_prop_size = debugObj->prop_list.size(); + + debugObj->prop_list.clear(); + int new_props_added = 0; + Set<String> changed; for (int i = 0; i < properties.size(); i++) { Array prop = properties[i]; @@ -511,18 +514,34 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } } - if (is_new_object) { - //don't update.. it's the same, instead refresh - debugObj->prop_list.push_back(pinfo); - } + //always add the property, since props may have been added or removed + debugObj->prop_list.push_back(pinfo); - debugObj->prop_values[pinfo.name] = var; + if (!debugObj->prop_values.has(pinfo.name)) { + new_props_added++; + debugObj->prop_values[pinfo.name] = var; + } else { + + if (bool(Variant::evaluate(Variant::OP_NOT_EQUAL, debugObj->prop_values[pinfo.name], var))) { + debugObj->prop_values[pinfo.name] = var; + changed.insert(pinfo.name); + } + } } if (editor->get_editor_history()->get_current() != debugObj->get_instance_id()) { editor->push_item(debugObj, ""); } else { - debugObj->update(); + + if (old_prop_size == debugObj->prop_list.size() && new_props_added == 0) { + //only some may have changed, if so, then update those, if exist + for (Set<String>::Element *E = changed.front(); E; E = E->next()) { + EditorNode::get_singleton()->get_inspector()->update_property(E->get()); + } + } else { + //full update, because props were added or removed + debugObj->update(); + } } } else if (p_msg == "message:video_mem") { @@ -1022,8 +1041,11 @@ void ScriptEditorDebugger::_notification(int p_what) { if (enable_rl) { inspect_scene_tree->add_constant_override("draw_relationship_lines", 1); inspect_scene_tree->add_color_override("relationship_line_color", rl_color); - } else + inspect_scene_tree->add_constant_override("draw_guides", 0); + } else { inspect_scene_tree->add_constant_override("draw_relationship_lines", 0); + inspect_scene_tree->add_constant_override("draw_guides", 1); + } } break; case NOTIFICATION_PROCESS: { @@ -1272,7 +1294,7 @@ void ScriptEditorDebugger::stop() { breaked = false; server->stop(); - + _clear_remote_objects(); ppeer->set_stream_peer(Ref<StreamPeer>()); if (connection.is_valid()) { @@ -2234,7 +2256,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) { p_editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this); p_editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this); - live_debug = false; + live_debug = true; last_path_id = false; error_count = 0; warning_count = 0; |