diff options
Diffstat (limited to 'editor')
30 files changed, 639 insertions, 305 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 462f314471..648e950fd4 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -1061,6 +1061,9 @@ void ActionMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { action_list_search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + if (!actions_cache.is_empty()) { + update_action_list(); + } } break; } } diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 7fa7b45f85..5df413f0a7 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -60,7 +60,7 @@ public: return true; } - bool _read_only() { + bool _is_read_only() { return animation_read_only; } @@ -70,7 +70,7 @@ public: ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationTrackKeyEdit::_hide_script_from_inspector); ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationTrackKeyEdit::get_root_path); ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationTrackKeyEdit::_dont_undo_redo); - ClassDB::bind_method(D_METHOD("_read_only"), &AnimationTrackKeyEdit::_read_only); + ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationTrackKeyEdit::_is_read_only); } void _fix_node_path(Variant &value) { @@ -727,7 +727,7 @@ public: return true; } - bool _read_only() { + bool _is_read_only() { return animation_read_only; } @@ -737,7 +737,7 @@ public: ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_script_from_inspector); ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationMultiTrackKeyEdit::get_root_path); ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiTrackKeyEdit::_dont_undo_redo); - ClassDB::bind_method(D_METHOD("_read_only"), &AnimationMultiTrackKeyEdit::_read_only); + ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiTrackKeyEdit::_is_read_only); } void _fix_node_path(Variant &value, NodePath &base) { diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index 58206efc20..6c0ba55ec8 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -85,6 +85,7 @@ void EditorDebuggerRemoteObject::_bind_methods() { ClassDB::bind_method(D_METHOD("get_variant"), &EditorDebuggerRemoteObject::get_variant); ClassDB::bind_method(D_METHOD("clear"), &EditorDebuggerRemoteObject::clear); ClassDB::bind_method(D_METHOD("get_remote_object_id"), &EditorDebuggerRemoteObject::get_remote_object_id); + ClassDB::bind_method(D_METHOD("_is_read_only"), &EditorDebuggerRemoteObject::_is_read_only); ADD_SIGNAL(MethodInfo("value_edited", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value"))); } diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h index 5aac4dbf11..c595e0acaa 100644 --- a/editor/debugger/editor_debugger_inspector.h +++ b/editor/debugger/editor_debugger_inspector.h @@ -50,6 +50,7 @@ public: HashMap<StringName, Variant> prop_values; ObjectID get_remote_object_id() { return remote_object_id; }; + bool _is_read_only() { return true; }; String get_title(); Variant get_variant(const StringName &p_name); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 855f4b1366..122e6ad021 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -340,6 +340,8 @@ void EditorProperty::_notification(int p_what) { draw_string(font, Point2(ofs, v_ofs + font->get_ascent(font_size)), label, HORIZONTAL_ALIGNMENT_LEFT, text_limit, font_size, color); } + ofs = size.width; + if (keying) { Ref<Texture2D> key; @@ -349,7 +351,7 @@ void EditorProperty::_notification(int p_what) { key = get_theme_icon(SNAME("Key"), SNAME("EditorIcons")); } - ofs = size.width - key->get_width() - get_theme_constant(SNAME("hseparator"), SNAME("Tree")); + ofs -= key->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); Color color2(1, 1, 1); if (keying_hover) { @@ -373,7 +375,7 @@ void EditorProperty::_notification(int p_what) { close = get_theme_icon(SNAME("Close"), SNAME("EditorIcons")); - ofs = size.width - close->get_width() - get_theme_constant(SNAME("hseparator"), SNAME("Tree")); + ofs -= close->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); Color color2(1, 1, 1); if (delete_hover) { @@ -2628,15 +2630,28 @@ void EditorInspector::update_tree() { valid_plugins.push_back(inspector_plugins[i]); } - // Decide if properties should be drawn with the warning color (yellow). + // Decide if properties should be drawn with the warning color (yellow), + // or if the whole object should be considered read-only. bool draw_warning = false; + bool all_read_only = false; if (is_inside_tree()) { + if (object->has_method("_is_read_only")) { + all_read_only = object->call("_is_read_only"); + } + Node *nod = Object::cast_to<Node>(object); Node *es = EditorNode::get_singleton()->get_edited_scene(); if (nod && es != nod && nod->get_owner() != es) { // Draw in warning color edited nodes that are not in the currently edited scene, // as changes may be lost in the future. draw_warning = true; + } else { + if (!all_read_only) { + Resource *res = Object::cast_to<Resource>(object); + if (res) { + all_read_only = EditorNode::get_singleton()->is_resource_read_only(res); + } + } } } @@ -3179,7 +3194,6 @@ void EditorInspector::update_tree() { ep->property_usage = p.usage; //and set label? } - if (!editors[i].label.is_empty()) { ep->set_label(editors[i].label); } else { @@ -3206,7 +3220,7 @@ void EditorInspector::update_tree() { ep->set_checkable(checkable); ep->set_checked(checked); ep->set_keying(keying); - ep->set_read_only(property_read_only); + ep->set_read_only(property_read_only || all_read_only); ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); } @@ -3253,6 +3267,9 @@ void EditorInspector::update_tree() { add_md->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); add_md->connect(SNAME("pressed"), callable_mp(this, &EditorInspector::_show_add_meta_dialog)); main_vbox->add_child(add_md); + if (all_read_only) { + add_md->set_disabled(true); + } } // Get the lists of to add at the end. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 362159cb56..4aedd98bd3 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -1250,7 +1250,9 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St } void EditorNode::save_resource(const Ref<Resource> &p_resource) { - if (p_resource->get_path().is_resource_file()) { + // If the resource has been imported, ask the user to use a different path in order to save it. + String path = p_resource->get_path(); + if (path.is_resource_file() && !FileAccess::exists(path + ".import")) { save_resource_in_path(p_resource, p_resource->get_path()); } else { save_resource_as(p_resource); @@ -1260,11 +1262,18 @@ void EditorNode::save_resource(const Ref<Resource> &p_resource) { void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path) { { String path = p_resource->get_path(); - int srpos = path.find("::"); - if (srpos != -1) { - String base = path.substr(0, srpos); - if (!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base) { - show_warning(TTR("This resource can't be saved because it does not belong to the edited scene. Make it unique first.")); + if (!path.is_resource_file()) { + int srpos = path.find("::"); + if (srpos != -1) { + String base = path.substr(0, srpos); + if (!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base) { + show_warning(TTR("This resource can't be saved because it does not belong to the edited scene. Make it unique first.")); + return; + } + } + } else { + if (FileAccess::exists(path + ".import")) { + show_warning(TTR("This resource can't be saved because it was imported from another file. Make it unique first.")); return; } } @@ -2223,7 +2232,14 @@ void EditorNode::_edit_current(bool p_skip_foreign) { bool stay_in_script_editor_on_node_selected = bool(EDITOR_GET("text_editor/behavior/navigation/stay_in_script_editor_on_node_selected")); bool skip_main_plugin = false; - String editable_warning; // None by default. + String editable_info; // None by default. + bool info_is_warning = false; + + if (current_obj->has_method("_is_read_only")) { + if (current_obj->call("_is_read_only")) { + editable_info = TTR("This object is marked as read-only, so it's not editable."); + } + } if (is_resource) { Resource *current_res = Object::cast_to<Resource>(current_obj); @@ -2237,16 +2253,25 @@ void EditorNode::_edit_current(bool p_skip_foreign) { int subr_idx = current_res->get_path().find("::"); if (subr_idx != -1) { String base_path = current_res->get_path().substr(0, subr_idx); - if (FileAccess::exists(base_path + ".import")) { - editable_warning = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); + if (!base_path.is_resource_file()) { + if (FileAccess::exists(base_path + ".import")) { + if (get_edited_scene() && get_edited_scene()->get_scene_file_path() == base_path) { + info_is_warning = true; + } + editable_info = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); + } else { + if ((!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base_path) && ResourceLoader::get_resource_type(base_path) == "PackedScene") { + editable_info = TTR("This resource belongs to a scene that was instantiated or inherited.\nChanges to it must be made inside the original scene."); + } + } } else { - if ((!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base_path) && ResourceLoader::get_resource_type(base_path) == "PackedScene") { - editable_warning = TTR("This resource belongs to a scene that was instantiated or inherited.\nChanges to it won't be kept when saving the current scene."); + if (FileAccess::exists(base_path + ".import")) { + editable_info = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); } } } else if (current_res->get_path().is_resource_file()) { if (FileAccess::exists(current_res->get_path() + ".import")) { - editable_warning = TTR("This resource was imported, so it's not editable. Change its settings in the import panel and then re-import."); + editable_info = TTR("This resource was imported, so it's not editable. Change its settings in the import panel and then re-import."); } } } else if (is_node) { @@ -2270,7 +2295,8 @@ void EditorNode::_edit_current(bool p_skip_foreign) { if (get_edited_scene() && !get_edited_scene()->get_scene_file_path().is_empty()) { String source_scene = get_edited_scene()->get_scene_file_path(); if (FileAccess::exists(source_scene + ".import")) { - editable_warning = TTR("This scene was imported, so changes to it won't be kept.\nInstancing it or inheriting will allow making changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); + editable_info = TTR("This scene was imported, so changes to it won't be kept.\nInstancing it or inheriting will allow making changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); + info_is_warning = true; } } @@ -2278,7 +2304,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) { Node *selected_node = nullptr; if (current_obj->is_class("EditorDebuggerRemoteObject")) { - editable_warning = TTR("This is a remote object, so changes to it won't be kept.\nPlease read the documentation relevant to debugging to better understand this workflow."); + editable_info = TTR("This is a remote object, so it's not editable.\nPlease read the documentation relevant to debugging to better understand this workflow."); disable_folding = true; } else if (current_obj->is_class("MultiNodeEdit")) { Node *scene = get_edited_scene(); @@ -2313,7 +2339,10 @@ void EditorNode::_edit_current(bool p_skip_foreign) { InspectorDock::get_inspector_singleton()->update_tree(); } - InspectorDock::get_singleton()->set_warning(editable_warning); + InspectorDock::get_singleton()->set_info( + info_is_warning ? TTR("Changes may be lost!") : TTR("This object is read-only."), + editable_info, + info_is_warning); if (InspectorDock::get_inspector_singleton()->is_using_folding() == disable_folding) { InspectorDock::get_inspector_singleton()->set_use_folding(!disable_folding); @@ -3828,6 +3857,37 @@ void EditorNode::edit_foreign_resource(Ref<Resource> p_resource) { InspectorDock::get_singleton()->call_deferred("edit_resource", p_resource); } +bool EditorNode::is_resource_read_only(Ref<Resource> p_resource) { + ERR_FAIL_COND_V(p_resource.is_null(), false); + + String path = p_resource->get_path(); + if (!path.is_resource_file()) { + // If the resource name contains '::', that means it is a subresource embedded in another resource. + int srpos = path.find("::"); + if (srpos != -1) { + String base = path.substr(0, srpos); + // If the base resource is a packed scene, we treat it as read-only if it is not the currently edited scene. + if (ResourceLoader::get_resource_type(base) == "PackedScene") { + if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) { + return true; + } + } else { + // If a corresponding .import file exists for the base file, we assume it to be imported and should therefore treated as read-only. + if (FileAccess::exists(base + ".import")) { + return true; + } + } + } + } else { + // The resource is not a subresource, but if it has an .import file, it's imported so treat it as read only. + if (FileAccess::exists(path + ".import")) { + return true; + } + } + + return false; +} + void EditorNode::request_instance_scene(const String &p_path) { SceneTreeDock::get_singleton()->instantiate(p_path); } @@ -5837,6 +5897,7 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene); ClassDB::bind_method("open_request", &EditorNode::open_request); ClassDB::bind_method("edit_foreign_resource", &EditorNode::edit_foreign_resource); + ClassDB::bind_method("is_resource_read_only", &EditorNode::is_resource_read_only); ClassDB::bind_method("_close_messages", &EditorNode::_close_messages); ClassDB::bind_method("_show_messages", &EditorNode::_show_messages); @@ -6166,6 +6227,7 @@ EditorNode::EditorNode() { add_child(editor_export); // Exporters might need the theme. + EditorColorMap::create(); theme = create_custom_theme(); register_exporters(); diff --git a/editor/editor_node.h b/editor/editor_node.h index 7400bcd422..91c753e480 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -776,6 +776,8 @@ public: void open_request(const String &p_path); void edit_foreign_resource(Ref<Resource> p_resource); + bool is_resource_read_only(Ref<Resource> p_resource); + bool is_changing_scene() const; Control *get_main_control(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index f434df3a1e..84c4c9c877 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2613,8 +2613,47 @@ void EditorPropertyQuaternion::_set_read_only(bool p_read_only) { for (int i = 0; i < 4; i++) { spin[i]->set_read_only(p_read_only); } + for (int i = 0; i < 3; i++) { + euler[i]->set_read_only(p_read_only); + } }; +void EditorPropertyQuaternion::_edit_custom_value() { + if (edit_button->is_pressed()) { + edit_custom_bc->show(); + for (int i = 0; i < 3; i++) { + euler[i]->grab_focus(); + } + } else { + edit_custom_bc->hide(); + for (int i = 0; i < 4; i++) { + spin[i]->grab_focus(); + } + } + update_property(); +} + +void EditorPropertyQuaternion::_custom_value_changed(double val) { + if (setting) { + return; + } + + edit_euler.x = euler[0]->get_value(); + edit_euler.y = euler[1]->get_value(); + edit_euler.z = euler[2]->get_value(); + + Vector3 v; + v.x = Math::deg2rad(edit_euler.x); + v.y = Math::deg2rad(edit_euler.y); + v.z = Math::deg2rad(edit_euler.z); + + Quaternion temp_q = Quaternion(v); + spin[0]->set_value(temp_q.x); + spin[1]->set_value(temp_q.y); + spin[2]->set_value(temp_q.z); + spin[3]->set_value(temp_q.w); +} + void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) { if (setting) { return; @@ -2625,9 +2664,18 @@ void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) p.y = spin[1]->get_value(); p.z = spin[2]->get_value(); p.w = spin[3]->get_value(); + emit_changed(get_edited_property(), p, p_name); } +bool EditorPropertyQuaternion::is_grabbing_euler() { + bool is_grabbing = false; + for (int i = 0; i < 3; i++) { + is_grabbing |= euler[i]->is_grabbing(); + } + return is_grabbing; +} + void EditorPropertyQuaternion::update_property() { Quaternion val = get_edited_object()->get(get_edited_property()); setting = true; @@ -2635,9 +2683,22 @@ void EditorPropertyQuaternion::update_property() { spin[1]->set_value(val.y); spin[2]->set_value(val.z); spin[3]->set_value(val.w); + if (!is_grabbing_euler()) { + Vector3 v = val.normalized().get_euler_yxz(); + edit_euler.x = Math::rad2deg(v.x); + edit_euler.y = Math::rad2deg(v.y); + edit_euler.z = Math::rad2deg(v.z); + euler[0]->set_value(edit_euler.x); + euler[1]->set_value(edit_euler.y); + euler[2]->set_value(edit_euler.z); + } setting = false; } +void EditorPropertyQuaternion::_warning_pressed() { + warning_dialog->popup_centered(); +} + void EditorPropertyQuaternion::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -2646,6 +2707,13 @@ void EditorPropertyQuaternion::_notification(int p_what) { for (int i = 0; i < 4; i++) { spin[i]->add_theme_color_override("label_color", colors[i]); } + for (int i = 0; i < 3; i++) { + euler[i]->add_theme_color_override("label_color", colors[i]); + } + edit_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + euler_label->add_theme_color_override(SNAME("font_color"), get_theme_color(SNAME("property_color"), SNAME("Editor"))); + warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); + warning->add_theme_color_override(SNAME("font_color"), get_theme_color(SNAME("warning_color"), SNAME("Editor"))); } break; } } @@ -2653,7 +2721,7 @@ void EditorPropertyQuaternion::_notification(int p_what) { void EditorPropertyQuaternion::_bind_methods() { } -void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix) { +void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix, bool p_hide_editor) { for (int i = 0; i < 4; i++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); @@ -2665,28 +2733,50 @@ void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, // a generic way to store 4 values, so we'll still respect the suffix. spin[i]->set_suffix(p_suffix); } + + for (int i = 0; i < 3; i++) { + euler[i]->set_min(-360); + euler[i]->set_max(360); + euler[i]->set_step(0.1); + euler[i]->set_hide_slider(false); + euler[i]->set_allow_greater(true); + euler[i]->set_allow_lesser(true); + euler[i]->set_suffix(U"\u00B0"); + } + + if (p_hide_editor) { + edit_button->hide(); + } } EditorPropertyQuaternion::EditorPropertyQuaternion() { bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); - BoxContainer *bc; - + VBoxContainer *bc = memnew(VBoxContainer); + edit_custom_bc = memnew(VBoxContainer); + BoxContainer *edit_custom_layout; if (horizontal) { - bc = memnew(HBoxContainer); - add_child(bc); + default_layout = memnew(HBoxContainer); + edit_custom_layout = memnew(HBoxContainer); set_bottom_editor(bc); } else { - bc = memnew(VBoxContainer); - add_child(bc); + default_layout = memnew(VBoxContainer); + edit_custom_layout = memnew(VBoxContainer); } + edit_custom_bc->hide(); + add_child(bc); + edit_custom_bc->set_h_size_flags(SIZE_EXPAND_FILL); + default_layout->set_h_size_flags(SIZE_EXPAND_FILL); + edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL); + bc->add_child(default_layout); + bc->add_child(edit_custom_bc); static const char *desc[4] = { "x", "y", "z", "w" }; for (int i = 0; i < 4; i++) { spin[i] = memnew(EditorSpinSlider); spin[i]->set_flat(true); spin[i]->set_label(desc[i]); - bc->add_child(spin[i]); + default_layout->add_child(spin[i]); add_focusable(spin[i]); spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyQuaternion::_value_changed).bind(desc[i])); if (horizontal) { @@ -2694,6 +2784,41 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() { } } + warning = memnew(Button); + warning->set_text(TTR("Temporary Euler may be changed implicitly!")); + warning->set_clip_text(true); + warning->connect("pressed", callable_mp(this, &EditorPropertyQuaternion::_warning_pressed)); + warning_dialog = memnew(AcceptDialog); + add_child(warning_dialog); + warning_dialog->set_text(TTR("Temporary Euler will not be stored in the object with the original value. Instead, it will be stored as Quaternion with irreversible conversion.\nThis is due to the fact that the result of Euler->Quaternion can be determined uniquely, but the result of Quaternion->Euler can be multi-existent.")); + + euler_label = memnew(Label); + euler_label->set_text("Temporary Euler"); + + edit_custom_bc->add_child(warning); + edit_custom_bc->add_child(edit_custom_layout); + edit_custom_layout->add_child(euler_label); + + for (int i = 0; i < 3; i++) { + euler[i] = memnew(EditorSpinSlider); + euler[i]->set_flat(true); + euler[i]->set_label(desc[i]); + edit_custom_layout->add_child(euler[i]); + add_focusable(euler[i]); + euler[i]->connect("value_changed", callable_mp(this, &EditorPropertyQuaternion::_custom_value_changed)); + if (horizontal) { + euler[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + edit_button = memnew(Button); + edit_button->set_flat(true); + edit_button->set_toggle_mode(true); + default_layout->add_child(edit_button); + edit_button->connect("pressed", callable_mp(this, &EditorPropertyQuaternion::_edit_custom_value)); + + add_focusable(edit_button); + if (!horizontal) { set_label_reference(spin[0]); //show text and buttons around this } @@ -3653,20 +3778,24 @@ void EditorPropertyResource::_set_read_only(bool p_read_only) { resource_picker->set_editable(!p_read_only); }; -void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_edit) { +void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) { if (p_resource->is_built_in() && !p_resource->get_path().is_empty()) { String parent = p_resource->get_path().get_slice("::", 0); List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions); - if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) { - // If the resource belongs to another scene, edit it in that scene instead. - EditorNode::get_singleton()->call_deferred("edit_foreign_resource", p_resource); - return; + if (p_inspect) { + if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) { + // If the resource belongs to another (non-imported) scene, edit it in that scene instead. + if (!FileAccess::exists(parent + ".import")) { + EditorNode::get_singleton()->call_deferred("edit_foreign_resource", p_resource); + return; + } + } } } - if (!p_edit && use_sub_inspector) { + if (!p_inspect && use_sub_inspector) { bool unfold = !get_edited_object()->editor_is_section_unfolded(get_edited_property()); get_edited_object()->editor_set_section_unfold(get_edited_property(), unfold); update_property(); @@ -4357,7 +4486,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::QUATERNION: { EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step); - editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix); + editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix, p_hint == PROPERTY_HINT_HIDE_QUATERNION_EDIT); return editor; } break; case Variant::AABB: { diff --git a/editor/editor_properties.h b/editor/editor_properties.h index c1dfb5cb1e..6ac3973303 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -625,9 +625,26 @@ public: class EditorPropertyQuaternion : public EditorProperty { GDCLASS(EditorPropertyQuaternion, EditorProperty); + BoxContainer *default_layout = nullptr; EditorSpinSlider *spin[4]; bool setting = false; + + Button *warning = nullptr; + AcceptDialog *warning_dialog = nullptr; + + Label *euler_label = nullptr; + VBoxContainer *edit_custom_bc = nullptr; + EditorSpinSlider *euler[3]; + Button *edit_button = nullptr; + + Vector3 edit_euler = Vector3(); + void _value_changed(double p_val, const String &p_name); + void _edit_custom_value(); + void _custom_value_changed(double p_val); + void _warning_pressed(); + + bool is_grabbing_euler(); protected: virtual void _set_read_only(bool p_read_only) override; @@ -636,7 +653,7 @@ protected: public: virtual void update_property() override; - void setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix = String()); + void setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix = String(), bool p_hide_editor = false); EditorPropertyQuaternion(); }; @@ -835,7 +852,7 @@ class EditorPropertyResource : public EditorProperty { bool updating_theme = false; bool opened_editor = false; - void _resource_selected(const Ref<Resource> &p_resource, bool p_edit); + void _resource_selected(const Ref<Resource> &p_resource, bool p_inspect); void _resource_changed(const Ref<Resource> &p_resource); void _viewport_selected(const NodePath &p_path); diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index e5c1836205..7e42c45b01 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -81,6 +81,8 @@ void EditorResourcePicker::_update_resource() { } else if (edited_resource.is_valid()) { assign_button->set_tooltip(resource_path + TTR("Type:") + " " + edited_resource->get_class()); } + + assign_button->set_disabled(!editable && !edited_resource.is_valid()); } void EditorResourcePicker::_update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj) { @@ -171,35 +173,50 @@ void EditorResourcePicker::_update_menu_items() { edit_menu->clear(); // Add options for creating specific subtypes of the base resource type. - set_create_options(edit_menu); + if (is_editable()) { + set_create_options(edit_menu); - // Add an option to load a resource from a file using the QuickOpen dialog. - edit_menu->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Quick Load"), OBJ_MENU_QUICKLOAD); + // Add an option to load a resource from a file using the QuickOpen dialog. + edit_menu->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Quick Load"), OBJ_MENU_QUICKLOAD); - // Add an option to load a resource from a file using the regular file dialog. - edit_menu->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Load"), OBJ_MENU_LOAD); + // Add an option to load a resource from a file using the regular file dialog. + edit_menu->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Load"), OBJ_MENU_LOAD); + } // Add options for changing existing value of the resource. if (edited_resource.is_valid()) { - edit_menu->add_icon_item(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")), TTR("Edit"), OBJ_MENU_EDIT); - edit_menu->add_icon_item(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")), TTR("Clear"), OBJ_MENU_CLEAR); - edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE); - - // Check whether the resource has subresources. - List<PropertyInfo> property_list; - edited_resource->get_property_list(&property_list); - bool has_subresources = false; - for (PropertyInfo &p : property_list) { - if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script")) { - has_subresources = true; - break; - } - } - if (has_subresources) { - edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique (Recursive)"), OBJ_MENU_MAKE_UNIQUE_RECURSIVE); + // Determine if the edited resource is part of another scene (foreign) which was imported + bool is_edited_resource_foreign_import = EditorNode::get_singleton()->is_resource_read_only(edited_resource); + + // If the resource is determined to be foreign and imported, change the menu entry's description to 'inspect' rather than 'edit' + // since will only be able to view its properties in read-only mode. + if (is_edited_resource_foreign_import) { + // The 'Search' icon is a magnifying glass, which seems appropriate, but maybe a bespoke icon is preferred here. + edit_menu->add_icon_item(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")), TTR("Inspect"), OBJ_MENU_INSPECT); + } else { + edit_menu->add_icon_item(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")), TTR("Edit"), OBJ_MENU_INSPECT); } - edit_menu->add_icon_item(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")), TTR("Save"), OBJ_MENU_SAVE); + if (is_editable()) { + edit_menu->add_icon_item(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")), TTR("Clear"), OBJ_MENU_CLEAR); + edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE); + + // Check whether the resource has subresources. + List<PropertyInfo> property_list; + edited_resource->get_property_list(&property_list); + bool has_subresources = false; + for (PropertyInfo &p : property_list) { + if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script")) { + has_subresources = true; + break; + } + } + if (has_subresources) { + edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique (Recursive)"), OBJ_MENU_MAKE_UNIQUE_RECURSIVE); + } + + edit_menu->add_icon_item(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")), TTR("Save"), OBJ_MENU_SAVE); + } if (edited_resource->get_path().is_resource_file()) { edit_menu->add_separator(); @@ -210,14 +227,16 @@ void EditorResourcePicker::_update_menu_items() { // Add options to copy/paste resource. Ref<Resource> cb = EditorSettings::get_singleton()->get_resource_clipboard(); bool paste_valid = false; - if (cb.is_valid()) { - if (base_type.is_empty()) { - paste_valid = true; - } else { - for (int i = 0; i < base_type.get_slice_count(","); i++) { - if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) { - paste_valid = true; - break; + if (is_editable()) { + if (cb.is_valid()) { + if (base_type.is_empty()) { + paste_valid = true; + } else { + for (int i = 0; i < base_type.get_slice_count(","); i++) { + if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) { + paste_valid = true; + break; + } } } } @@ -236,7 +255,7 @@ void EditorResourcePicker::_update_menu_items() { } // Add options to convert existing resource to another type of resource. - if (edited_resource.is_valid()) { + if (is_editable() && edited_resource.is_valid()) { Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(edited_resource); if (conversions.size()) { edit_menu->add_separator(); @@ -295,7 +314,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { quick_open->set_title(TTR("Resource")); } break; - case OBJ_MENU_EDIT: { + case OBJ_MENU_INSPECT: { if (edited_resource.is_valid()) { emit_signal(SNAME("resource_selected"), edited_resource, true); } @@ -491,20 +510,21 @@ void EditorResourcePicker::_button_draw() { } void EditorResourcePicker::_button_input(const Ref<InputEvent> &p_event) { - if (!editable) { - return; - } - Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) { - _update_menu_items(); - - Vector2 pos = get_screen_position() + mb->get_position(); - edit_menu->reset_size(); - edit_menu->set_position(pos); - edit_menu->popup(); + // Only attempt to update and show the menu if we have + // a valid resource or the Picker is editable, as + // there will otherwise be nothing to display. + if (edited_resource.is_valid() || is_editable()) { + _update_menu_items(); + + Vector2 pos = get_screen_position() + mb->get_position(); + edit_menu->reset_size(); + edit_menu->set_position(pos); + edit_menu->popup(); + } } } } @@ -734,7 +754,7 @@ void EditorResourcePicker::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); - ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::BOOL, "edit"))); + ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::BOOL, "inspect"))); ADD_SIGNAL(MethodInfo("resource_changed", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); } @@ -866,7 +886,7 @@ void EditorResourcePicker::set_toggle_pressed(bool p_pressed) { void EditorResourcePicker::set_editable(bool p_editable) { editable = p_editable; - assign_button->set_disabled(!editable); + assign_button->set_disabled(!editable && !edited_resource.is_valid()); edit_button->set_visible(editable); } diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 3a4d5985bd..3d6127e656 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -63,7 +63,7 @@ class EditorResourcePicker : public HBoxContainer { enum MenuOption { OBJ_MENU_LOAD, OBJ_MENU_QUICKLOAD, - OBJ_MENU_EDIT, + OBJ_MENU_INSPECT, OBJ_MENU_CLEAR, OBJ_MENU_MAKE_UNIQUE, OBJ_MENU_MAKE_UNIQUE_RECURSIVE, diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 20e9d7a3df..5b98460e8e 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -605,6 +605,10 @@ bool EditorSpinSlider::is_flat() const { return flat; } +bool EditorSpinSlider::is_grabbing() const { + return grabbing_grabber || grabbing_spinner; +} + void EditorSpinSlider::_focus_entered() { _ensure_input_popup(); Rect2 gr = get_screen_rect(); diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h index f0adf5b7a1..afcaa3e4b6 100644 --- a/editor/editor_spin_slider.h +++ b/editor/editor_spin_slider.h @@ -110,6 +110,8 @@ public: void set_flat(bool p_enable); bool is_flat() const; + bool is_grabbing() const; + void setup_and_show() { _focus_entered(); } LineEdit *get_line_edit(); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 0c17469e86..827a657a31 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -32,7 +32,6 @@ #include "core/error/error_macros.h" #include "core/io/resource_loader.h" -#include "core/variant/dictionary.h" #include "editor/editor_fonts.h" #include "editor/editor_icons.gen.h" #include "editor/editor_scale.h" @@ -43,6 +42,118 @@ #include "modules/svg/image_loader_svg.h" #endif +HashMap<Color, Color> EditorColorMap::editor_color_map; + +void EditorColorMap::add_color_pair(const String p_from_color, const String p_to_color) { + editor_color_map[Color::html(p_from_color)] = Color::html(p_to_color); +} + +void EditorColorMap::create() { + // Some of the colors below are listed for completeness sake. + // This can be a basis for proper palette validation later. + + // Convert: FROM TO + add_color_pair("#478cbf", "#478cbf"); // Godot Blue + add_color_pair("#414042", "#414042"); // Godot Gray + + add_color_pair("#ffffff", "#414141"); // Pure white + add_color_pair("#000000", "#bfbfbf"); // Pure black + // Keep pure RGB colors as is, but list them for explicitly. + add_color_pair("#ff0000", "#ff0000"); // Pure red + add_color_pair("#00ff00", "#00ff00"); // Pure green + add_color_pair("#0000ff", "#0000ff"); // Pure blue + + // GUI Colors + add_color_pair("#e0e0e0", "#5a5a5a"); // Common icon color + add_color_pair("#fefefe", "#fefefe"); // Forced light color + add_color_pair("#808080", "#808080"); // GUI disabled color + add_color_pair("#b3b3b3", "#363636"); // GUI disabled light color + add_color_pair("#699ce8", "#699ce8"); // GUI highlight color + add_color_pair("#f9f9f9", "#606060"); // Scrollbar grabber highlight color + + add_color_pair("#c38ef1", "#a85de9"); // Animation + add_color_pair("#fc7f7f", "#cd3838"); // Spatial + add_color_pair("#8da5f3", "#3d64dd"); // 2D + add_color_pair("#4b70ea", "#1a3eac"); // 2D Dark + add_color_pair("#8eef97", "#2fa139"); // Control + + add_color_pair("#5fb2ff", "#0079f0"); // Selection (blue) + add_color_pair("#003e7a", "#2b74bb"); // Selection (darker blue) + add_color_pair("#f7f5cf", "#615f3a"); // Gizmo (yellow) + + // Rainbow + add_color_pair("#ff4545", "#ff2929"); // Red + add_color_pair("#ffe345", "#ffe337"); // Yellow + add_color_pair("#80ff45", "#74ff34"); // Green + add_color_pair("#45ffa2", "#2cff98"); // Aqua + add_color_pair("#45d7ff", "#22ccff"); // Blue + add_color_pair("#8045ff", "#702aff"); // Purple + add_color_pair("#ff4596", "#ff2781"); // Pink + + // Audio gradients + add_color_pair("#e1da5b", "#d6cf4b"); // Yellow + + add_color_pair("#62aeff", "#1678e0"); // Frozen gradient top + add_color_pair("#75d1e6", "#41acc5"); // Frozen gradient middle + add_color_pair("#84ffee", "#49ccba"); // Frozen gradient bottom + + add_color_pair("#f70000", "#c91616"); // Color track red + add_color_pair("#eec315", "#d58c0b"); // Color track orange + add_color_pair("#dbee15", "#b7d10a"); // Color track yellow + add_color_pair("#288027", "#218309"); // Color track green + + // Resource groups + add_color_pair("#ffca5f", "#fea900"); // Mesh resource (orange) + add_color_pair("#2998ff", "#68b6ff"); // Shape resource (blue) + add_color_pair("#a2d2ff", "#4998e3"); // Shape resource (light blue) + + // Animation editor tracks + // The property track icon color is set by the common icon color. + add_color_pair("#ea7940", "#bd5e2c"); // 3D Position track + add_color_pair("#ff2b88", "#bd165f"); // 3D Rotation track + add_color_pair("#eac840", "#bd9d1f"); // 3D Scale track + add_color_pair("#3cf34e", "#16a827"); // Call Method track + add_color_pair("#2877f6", "#236be6"); // Bezier Curve track + add_color_pair("#eae440", "#9f9722"); // Audio Playback track + add_color_pair("#a448f0", "#9853ce"); // Animation Playback track + add_color_pair("#5ad5c4", "#0a9c88"); // Blend Shape track + + // Control layouts + add_color_pair("#d6d6d6", "#474747"); // Highlighted part + add_color_pair("#474747", "#d6d6d6"); // Background part + add_color_pair("#919191", "#6e6e6e"); // Border part + + // TileSet editor icons + add_color_pair("#fce00e", "#aa8d24"); // New Single Tile + add_color_pair("#0e71fc", "#0350bd"); // New Autotile + add_color_pair("#c6ced4", "#828f9b"); // New Atlas + + // Visual script + add_color_pair("#41ecad", "#25e3a0"); // VisualScript variant + add_color_pair("#6f91f0", "#6d8eeb"); // VisualScript bool + add_color_pair("#5abbef", "#4fb2e9"); // VisualScript int + add_color_pair("#35d4f4", "#27ccf0"); // VisualScript float + add_color_pair("#4593ec", "#4690e7"); // VisualScript String + add_color_pair("#ac73f1", "#ad76ee"); // VisualScript Vector2 + add_color_pair("#f1738f", "#ee758e"); // VisualScript Rect2 + add_color_pair("#de66f0", "#dc6aed"); // VisualScript Vector3 + add_color_pair("#b9ec41", "#96ce1a"); // VisualScript Transform2D + add_color_pair("#f74949", "#f77070"); // VisualScript Plane + add_color_pair("#ec418e", "#ec69a3"); // VisualScript Quat + add_color_pair("#ee5677", "#ee7991"); // VisualScript AABB + add_color_pair("#e1ec41", "#b2bb19"); // VisualScript Basis + add_color_pair("#f68f45", "#f49047"); // VisualScript Transform + add_color_pair("#417aec", "#6993ec"); // VisualScript NodePath + add_color_pair("#41ec80", "#2ce573"); // VisualScript RID + add_color_pair("#55f3e3", "#12d5c3"); // VisualScript Object + add_color_pair("#54ed9e", "#57e99f"); // VisualScript Dictionary + // Visual shaders + add_color_pair("#77ce57", "#67c046"); // Vector funcs + add_color_pair("#ea686c", "#d95256"); // Vector transforms + add_color_pair("#eac968", "#d9b64f"); // Textures and cubemaps + add_color_pair("#cf68ea", "#c050dd"); // Functions and expressions +} + static Ref<StyleBoxTexture> make_stylebox(Ref<Texture2D> p_texture, float p_left, float p_top, float p_right, float p_bottom, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1, bool p_draw_center = true) { Ref<StyleBoxTexture> style(memnew(StyleBoxTexture)); style->set_texture(p_texture); @@ -113,7 +224,7 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, #ifdef MODULE_SVG_ENABLED // See also `generate_icon()` in `scene/resources/default_theme.cpp`. -static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, float p_saturation = 1.0, Dictionary p_convert_colors = Dictionary()) { +static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float p_saturation, const HashMap<Color, Color> &p_convert_colors = HashMap<Color, Color>()) { Ref<Image> img = memnew(Image); // Upsample icon generation only if the editor scale isn't an integer multiplier. @@ -121,8 +232,7 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, // with integer editor scales. const bool upsample = !Math::is_equal_approx(Math::round(p_scale), p_scale); ImageLoaderSVG img_loader; - img_loader.set_replace_colors(p_convert_colors); - img_loader.create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_color); + img_loader.create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_colors); if (p_saturation != 1.0) { img->adjust_bcs(1.0, 1.0, p_saturation); } @@ -132,126 +242,18 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, } #endif -#ifndef ADD_CONVERT_COLOR -#define ADD_CONVERT_COLOR(dictionary, old_color, new_color) dictionary[Color::html(old_color)] = Color::html(new_color) -#endif - void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = true, int p_thumb_size = 32, bool p_only_thumbs = false, float p_icon_saturation = 1.0) { #ifdef MODULE_SVG_ENABLED - // The default icon theme is designed to be used for a dark theme. - // This dictionary stores Color values to convert to other colors - // for better readability on a light theme. - // Godot Color values are used to avoid the ambiguity of strings - // (where "#ffffff", "fff", and "white" are all equivalent). - Dictionary dark_icon_color_dictionary; + HashMap<Color, Color> icon_color_map; // The names of the icons to never convert, even if one of their colors // are contained in the dictionary above. HashSet<StringName> exceptions; - // Some of the colors below are listed for completeness sake. - // This can be a basis for proper palette validation later. if (!p_dark_theme) { - // Convert color: FROM TO - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#478cbf", "#478cbf"); // Godot Blue - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#414042", "#414042"); // Godot Gray - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffffff", "#414141"); // Pure white - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#000000", "#bfbfbf"); // Pure black - // Keep pure RGB colors as is, but list them for explicitly. - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff0000", "#ff0000"); // Pure red - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#00ff00", "#00ff00"); // Pure green - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#0000ff", "#0000ff"); // Pure blue - - // GUI Colors - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e0e0e0", "#5a5a5a"); // Common icon color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fefefe", "#fefefe"); // Forced light color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#808080", "#808080"); // GUI disabled color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#b3b3b3", "#363636"); // GUI disabled light color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#699ce8", "#699ce8"); // GUI highlight color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f9f9f9", "#606060"); // Scrollbar grabber highlight color - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#c38ef1", "#a85de9"); // Animation - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fc7f7f", "#cd3838"); // Spatial - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8da5f3", "#3d64dd"); // 2D - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#4b70ea", "#1a3eac"); // 2D Dark - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8eef97", "#2fa139"); // Control - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5fb2ff", "#0079f0"); // Selection (blue) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#003e7a", "#2b74bb"); // Selection (darker blue) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f7f5cf", "#615f3a"); // Gizmo (yellow) - - // Rainbow - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff4545", "#ff2929"); // Red - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffe345", "#ffe337"); // Yellow - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#80ff45", "#74ff34"); // Green - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#45ffa2", "#2cff98"); // Aqua - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#45d7ff", "#22ccff"); // Blue - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8045ff", "#702aff"); // Purple - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff4596", "#ff2781"); // Pink - - // Audio gradients - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e1da5b", "#d6cf4b"); // Yellow - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#62aeff", "#1678e0"); // Frozen gradient top - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#75d1e6", "#41acc5"); // Frozen gradient middle - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#84ffee", "#49ccba"); // Frozen gradient bottom - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f70000", "#c91616"); // Color track red - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eec315", "#d58c0b"); // Color track orange - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#dbee15", "#b7d10a"); // Color track yellow - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#288027", "#218309"); // Color track green - - // Resource groups - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffca5f", "#fea900"); // Mesh resource (orange) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#2998ff", "#68b6ff"); // Shape resource (blue) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a2d2ff", "#4998e3"); // Shape resource (light blue) - - // Animation editor tracks - // The property track icon color is set by the common icon color. - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea7940", "#bd5e2c"); // 3D Position track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff2b88", "#bd165f"); // 3D Rotation track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eac840", "#bd9d1f"); // 3D Scale track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#3cf34e", "#16a827"); // Call Method track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#2877f6", "#236be6"); // Bezier Curve track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eae440", "#9f9722"); // Audio Playback track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a448f0", "#9853ce"); // Animation Playback track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5ad5c4", "#0a9c88"); // Blend Shape track - - // Control layouts - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#d6d6d6", "#474747"); // Highlighted part - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#474747", "#d6d6d6"); // Background part - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#919191", "#6e6e6e"); // Border part - - // TileSet editor icons - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fce00e", "#aa8d24"); // New Single Tile - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#0e71fc", "#0350bd"); // New Autotile - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#c6ced4", "#828f9b"); // New Atlas - - // Visual script - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#41ecad", "#25e3a0"); // VisualScript variant - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#6f91f0", "#6d8eeb"); // VisualScript bool - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5abbef", "#4fb2e9"); // VisualScript int - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#35d4f4", "#27ccf0"); // VisualScript float - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#4593ec", "#4690e7"); // VisualScript String - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ac73f1", "#ad76ee"); // VisualScript Vector2 - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f1738f", "#ee758e"); // VisualScript Rect2 - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#de66f0", "#dc6aed"); // VisualScript Vector3 - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#b9ec41", "#96ce1a"); // VisualScript Transform2D - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f74949", "#f77070"); // VisualScript Plane - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ec418e", "#ec69a3"); // VisualScript Quat - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ee5677", "#ee7991"); // VisualScript AABB - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e1ec41", "#b2bb19"); // VisualScript Basis - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f68f45", "#f49047"); // VisualScript Transform - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#417aec", "#6993ec"); // VisualScript NodePath - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#41ec80", "#2ce573"); // VisualScript RID - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#55f3e3", "#12d5c3"); // VisualScript Object - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#54ed9e", "#57e99f"); // VisualScript Dictionary - // Visual shaders - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#77ce57", "#67c046"); // Vector funcs - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea686c", "#d95256"); // Vector transforms - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eac968", "#d9b64f"); // Textures and cubemaps - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#cf68ea", "#c050dd"); // Functions and expressions + for (KeyValue<Color, Color> &E : EditorColorMap::get()) { + icon_color_map[E.key] = E.value; + } exceptions.insert("EditorPivot"); exceptions.insert("EditorHandle"); @@ -291,18 +293,18 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = const Color error_color = p_theme->get_color(SNAME("error_color"), SNAME("Editor")); const Color success_color = p_theme->get_color(SNAME("success_color"), SNAME("Editor")); const Color warning_color = p_theme->get_color(SNAME("warning_color"), SNAME("Editor")); - dark_icon_color_dictionary[Color::html("#ff5f5f")] = error_color; - dark_icon_color_dictionary[Color::html("#5fff97")] = success_color; - dark_icon_color_dictionary[Color::html("#ffdd65")] = warning_color; + icon_color_map[Color::html("#ff5f5f")] = error_color; + icon_color_map[Color::html("#5fff97")] = success_color; + icon_color_map[Color::html("#ffdd65")] = warning_color; // Use the accent color for some icons (checkbox, radio, toggle, etc.). - Dictionary accent_color_icon_color_dictionary; + HashMap<Color, Color> accent_color_map; HashSet<StringName> accent_color_icons; const Color accent_color = p_theme->get_color(SNAME("accent_color"), SNAME("Editor")); - accent_color_icon_color_dictionary[Color::html("699ce8")] = accent_color; + accent_color_map[Color::html("699ce8")] = accent_color; if (accent_color.get_luminance() > 0.75) { - accent_color_icon_color_dictionary[Color::html("ffffff")] = Color(0.2, 0.2, 0.2); + accent_color_map[Color::html("ffffff")] = Color(0.2, 0.2, 0.2); } accent_color_icons.insert("GuiChecked"); @@ -318,7 +320,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = Ref<ImageTexture> icon; if (accent_color_icons.has(editor_icons_names[i])) { - icon = editor_generate_icon(i, true, EDSCALE, 1.0, accent_color_icon_color_dictionary); + icon = editor_generate_icon(i, EDSCALE, 1.0, accent_color_map); } else { float saturation = p_icon_saturation; @@ -327,7 +329,11 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = } const int is_exception = exceptions.has(editor_icons_names[i]); - icon = editor_generate_icon(i, !is_exception, EDSCALE, saturation, dark_icon_color_dictionary); + if (is_exception) { + icon = editor_generate_icon(i, EDSCALE, saturation); + } else { + icon = editor_generate_icon(i, EDSCALE, saturation, icon_color_map); + } } p_theme->set_icon(editor_icons_names[i], SNAME("EditorIcons"), icon); @@ -342,7 +348,13 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = for (int i = 0; i < editor_bg_thumbs_count; i++) { const int index = editor_bg_thumbs_indices[i]; const int is_exception = exceptions.has(editor_icons_names[index]); - const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary); + + Ref<ImageTexture> icon; + if (!p_dark_theme && !is_exception) { + icon = editor_generate_icon(index, scale, force_filter, icon_color_map); + } else { + icon = editor_generate_icon(index, scale, force_filter); + } p_theme->set_icon(editor_icons_names[index], SNAME("EditorIcons"), icon); } @@ -351,7 +363,13 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = for (int i = 0; i < editor_md_thumbs_count; i++) { const int index = editor_md_thumbs_indices[i]; const bool is_exception = exceptions.has(editor_icons_names[index]); - const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary); + + Ref<ImageTexture> icon; + if (!p_dark_theme && !is_exception) { + icon = editor_generate_icon(index, scale, force_filter, icon_color_map); + } else { + icon = editor_generate_icon(index, scale, force_filter); + } p_theme->set_icon(editor_icons_names[index], SNAME("EditorIcons"), icon); } @@ -518,8 +536,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Color warning_color = Color(1, 0.87, 0.4); Color error_color = Color(1, 0.47, 0.42); Color property_color = font_color.lerp(Color(0.5, 0.5, 0.5), 0.5); - Color readonly_color = property_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.5); - Color readonly_warning_color = error_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.5); + Color readonly_color = property_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.25); + Color readonly_warning_color = error_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.25); if (!dark_theme) { // Darken some colors to be readable on a light background diff --git a/editor/editor_themes.h b/editor/editor_themes.h index 1c69761435..37db8160fa 100644 --- a/editor/editor_themes.h +++ b/editor/editor_themes.h @@ -34,6 +34,20 @@ #include "scene/resources/texture.h" #include "scene/resources/theme.h" +// The default icon theme is designed to be used for a dark theme. This map stores +// Color values to convert to other colors for better readability on a light theme. +class EditorColorMap { + // Godot Color values are used to avoid the ambiguity of strings + // (where "#ffffff", "fff", and "white" are all equivalent). + static HashMap<Color, Color> editor_color_map; + +public: + static void create(); + static void add_color_pair(const String p_from_color, const String p_to_color); + + static HashMap<Color, Color> &get() { return editor_color_map; }; +}; + Ref<Theme> create_editor_theme(Ref<Theme> p_theme = nullptr); Ref<Theme> create_custom_theme(Ref<Theme> p_theme = nullptr); diff --git a/editor/icons/Position2D.svg b/editor/icons/Marker2D.svg index 191f0b2a03..191f0b2a03 100644 --- a/editor/icons/Position2D.svg +++ b/editor/icons/Marker2D.svg diff --git a/editor/icons/Position3D.svg b/editor/icons/Marker3D.svg index 894b195589..894b195589 100644 --- a/editor/icons/Position3D.svg +++ b/editor/icons/Marker3D.svg diff --git a/editor/icons/NodeInfo.svg b/editor/icons/NodeInfo.svg new file mode 100644 index 0000000000..4e3f0c42d0 --- /dev/null +++ b/editor/icons/NodeInfo.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm-1 3h2v2h-2zm0 3h2v5h-2z" fill="#fff"/></svg> diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index b301bbf0f9..ed83535421 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -327,7 +327,7 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const Ref<Image> image; image.instantiate(); - Error err = ImageLoader::load_image(p_source_file, image, nullptr, false, 1.0); + Error err = ImageLoader::load_image(p_source_file, image); if (err != OK) { return err; } diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 0eed6184c0..17b94ec706 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -409,11 +409,26 @@ void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String } Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + // Parse import options. + int32_t loader_flags = ImageFormatLoader::FLAG_NONE; + + // Compression. CompressMode compress_mode = CompressMode(int(p_options["compress/mode"])); const float lossy = p_options["compress/lossy_quality"]; const int pack_channels = p_options["compress/channel_pack"]; + const int normal = p_options["compress/normal_map"]; + const int hdr_compression = p_options["compress/hdr_compression"]; + const int bptc_ldr = p_options["compress/bptc_ldr"]; + + // Mipmaps. const bool mipmaps = p_options["mipmaps/generate"]; const uint32_t mipmap_limit = mipmaps ? uint32_t(p_options["mipmaps/limit"]) : uint32_t(-1); + + // Roughness. + const int roughness = p_options["roughness/mode"]; + const String normal_map = p_options["roughness/src_normal"]; + + // Processing. const bool fix_alpha_border = p_options["process/fix_alpha_border"]; const bool premult_alpha = p_options["process/premult_alpha"]; const bool normal_map_invert_y = p_options["process/normal_map_invert_y"]; @@ -421,29 +436,29 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String const bool stream = false; const int size_limit = p_options["process/size_limit"]; const bool hdr_as_srgb = p_options["process/hdr_as_srgb"]; + if (hdr_as_srgb) { + loader_flags |= ImageFormatLoader::FLAG_FORCE_LINEAR; + } const bool hdr_clamp_exposure = p_options["process/hdr_clamp_exposure"]; - const int normal = p_options["compress/normal_map"]; - const int hdr_compression = p_options["compress/hdr_compression"]; - const int bptc_ldr = p_options["compress/bptc_ldr"]; - const int roughness = p_options["roughness/mode"]; - const String normal_map = p_options["roughness/src_normal"]; + float scale = 1.0; + // SVG-specific options. if (p_options.has("svg/scale")) { scale = p_options["svg/scale"]; } Ref<Image> normal_image; Image::RoughnessChannel roughness_channel = Image::ROUGHNESS_CHANNEL_R; - if (mipmaps && roughness > 1 && FileAccess::exists(normal_map)) { normal_image.instantiate(); if (ImageLoader::load_image(normal_map, normal_image) == OK) { roughness_channel = Image::RoughnessChannel(roughness - 2); } } + Ref<Image> image; image.instantiate(); - Error err = ImageLoader::load_image(p_source_file, image, nullptr, hdr_as_srgb, scale); + Error err = ImageLoader::load_image(p_source_file, image, nullptr, loader_flags, scale); if (err != OK) { return err; } diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 1bcbd2fe00..b0ff678a3e 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -247,7 +247,7 @@ void InspectorDock::_resource_file_selected(String p_file) { } if (res.is_null()) { - warning_dialog->set_text(TTR("Failed to load resource.")); + info_dialog->set_text(TTR("Failed to load resource.")); return; }; @@ -409,8 +409,8 @@ void InspectorDock::_menu_expand_revertable() { inspector->expand_revertable(); } -void InspectorDock::_warning_pressed() { - warning_dialog->popup_centered(); +void InspectorDock::_info_pressed() { + info_dialog->popup_centered(); } Container *InspectorDock::get_addon_area() { @@ -446,8 +446,13 @@ void InspectorDock::_notification(int p_what) { history_menu->set_icon(get_theme_icon(SNAME("History"), SNAME("EditorIcons"))); object_menu->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); - warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); - warning->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + if (info_is_warning) { + info->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); + info->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + } else { + info->set_icon(get_theme_icon(SNAME("NodeInfo"), SNAME("EditorIcons"))); + info->add_theme_color_override("font_color", get_theme_color(SNAME("font_color"), SNAME("Editor"))); + } } break; } } @@ -476,11 +481,22 @@ void InspectorDock::open_resource(const String &p_type) { _load_resource(p_type); } -void InspectorDock::set_warning(const String &p_message) { - warning->hide(); - if (!p_message.is_empty()) { - warning->show(); - warning_dialog->set_text(p_message); +void InspectorDock::set_info(const String &p_button_text, const String &p_message, bool p_is_warning) { + info->hide(); + info_is_warning = p_is_warning; + + if (info_is_warning) { + info->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); + info->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + } else { + info->set_icon(get_theme_icon(SNAME("NodeInfo"), SNAME("EditorIcons"))); + info->add_theme_color_override("font_color", get_theme_color(SNAME("font_color"), SNAME("Editor"))); + } + + if (!p_button_text.is_empty() && !p_message.is_empty()) { + info->show(); + info->set_text(p_button_text); + info_dialog->set_text(p_message); } } @@ -515,7 +531,7 @@ void InspectorDock::update(Object *p_object) { resource_extra_popup->set_item_disabled(resource_extra_popup->get_item_index(RESOURCE_MAKE_BUILT_IN), !is_resource || is_text_file); if (!is_object || is_text_file) { - warning->hide(); + info->hide(); editor_path->clear_path(); return; } @@ -707,12 +723,11 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { object_menu->get_popup()->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_menu)); object_menu->get_popup()->connect("id_pressed", callable_mp(this, &InspectorDock::_menu_option)); - warning = memnew(Button); - add_child(warning); - warning->set_text(TTR("Changes may be lost!")); - warning->set_clip_text(true); - warning->hide(); - warning->connect("pressed", callable_mp(this, &InspectorDock::_warning_pressed)); + info = memnew(Button); + add_child(info); + info->set_clip_text(true); + info->hide(); + info->connect("pressed", callable_mp(this, &InspectorDock::_info_pressed)); unique_resources_confirmation = memnew(ConfirmationDialog); add_child(unique_resources_confirmation); @@ -737,8 +752,8 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { unique_resources_confirmation->connect("confirmed", callable_mp(this, &InspectorDock::_menu_confirm_current)); - warning_dialog = memnew(AcceptDialog); - EditorNode::get_singleton()->get_gui_base()->add_child(warning_dialog); + info_dialog = memnew(AcceptDialog); + EditorNode::get_singleton()->get_gui_base()->add_child(info_dialog); load_resource_dialog = memnew(EditorFileDialog); add_child(load_resource_dialog); diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index e32410151f..5ebcbf70c7 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -93,8 +93,9 @@ class InspectorDock : public VBoxContainer { MenuButton *object_menu = nullptr; EditorPath *editor_path = nullptr; - Button *warning = nullptr; - AcceptDialog *warning_dialog = nullptr; + bool info_is_warning = false; // Display in yellow and use warning icon if true. + Button *info = nullptr; + AcceptDialog *info_dialog = nullptr; int current_option = -1; ConfirmationDialog *unique_resources_confirmation = nullptr; @@ -118,7 +119,7 @@ class InspectorDock : public VBoxContainer { void _paste_resource(); void _prepare_resource_extra_popup(); - void _warning_pressed(); + void _info_pressed(); void _resource_created(); void _resource_selected(const Ref<Resource> &p_res, const String &p_property); void _edit_forward(); @@ -145,7 +146,7 @@ public: void edit_resource(const Ref<Resource> &p_resource); void open_resource(const String &p_type); void clear(); - void set_warning(const String &p_message); + void set_info(const String &p_button_text, const String &p_message, bool p_is_warning); void update(Object *p_object); Container *get_addon_area(); EditorInspector *get_inspector() { return inspector; } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 5682df845e..2e83e2041f 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2958,6 +2958,9 @@ void CanvasItemEditor::_draw_ruler_tool() { Point2 corner = Point2(begin.x, end.y); Vector2 length_vector = (begin - end).abs() / zoom; + const real_t horizontal_angle_rad = length_vector.angle(); + const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad; + Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); @@ -2974,6 +2977,42 @@ void CanvasItemEditor::_draw_ruler_tool() { text_pos.x = CLAMP(text_pos.x, text_width / 2, viewport->get_rect().size.x - text_width * 1.5); text_pos.y = CLAMP(text_pos.y, text_height * 1.5, viewport->get_rect().size.y - text_height * 1.5); + // Draw lines. + viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3)); + + bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x)); + if (draw_secondary_lines) { + viewport->draw_line(begin, corner, ruler_secondary_color, Math::round(EDSCALE)); + viewport->draw_line(corner, end, ruler_secondary_color, Math::round(EDSCALE)); + + // Angle arcs. + int arc_point_count = 8; + real_t arc_radius_max_length_percent = 0.1; + real_t ruler_length = length_vector.length() * zoom; + real_t arc_max_radius = 50.0; + real_t arc_line_width = 2.0; + + const Vector2 end_to_begin = (end - begin); + + real_t arc_1_start_angle = end_to_begin.x < 0 + ? (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0) + : (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad); + real_t arc_1_end_angle = arc_1_start_angle + vertical_angle_rad; + // Constrain arc to triangle height & max size. + real_t arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius); + + real_t arc_2_start_angle = end_to_begin.x < 0 + ? (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad) + : (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI); + real_t arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad; + // Constrain arc to triangle width & max size. + real_t arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius); + + viewport->draw_arc(begin, arc_1_radius, arc_1_start_angle, arc_1_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); + viewport->draw_arc(end, arc_2_radius, arc_2_start_angle, arc_2_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); + } + + // Draw text. if (begin.is_equal_approx(end)) { viewport->draw_string_outline(font, text_pos, (String)ruler_tool_origin, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); viewport->draw_string(font, text_pos, (String)ruler_tool_origin, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color); @@ -2985,17 +3024,7 @@ void CanvasItemEditor::_draw_ruler_tool() { viewport->draw_string_outline(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); viewport->draw_string(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color); - bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x)); - - viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3)); - if (draw_secondary_lines) { - viewport->draw_line(begin, corner, ruler_secondary_color, Math::round(EDSCALE)); - viewport->draw_line(corner, end, ruler_secondary_color, Math::round(EDSCALE)); - } - if (draw_secondary_lines) { - const real_t horizontal_angle_rad = length_vector.angle(); - const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad; const int horizontal_angle = round(180 * horizontal_angle_rad / Math_PI); const int vertical_angle = round(180 * vertical_angle_rad / Math_PI); @@ -3032,32 +3061,6 @@ void CanvasItemEditor::_draw_ruler_tool() { } viewport->draw_string_outline(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color); - - // Angle arcs - int arc_point_count = 8; - real_t arc_radius_max_length_percent = 0.1; - real_t ruler_length = length_vector.length() * zoom; - real_t arc_max_radius = 50.0; - real_t arc_line_width = 2.0; - - const Vector2 end_to_begin = (end - begin); - - real_t arc_1_start_angle = end_to_begin.x < 0 - ? (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0) - : (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad); - real_t arc_1_end_angle = arc_1_start_angle + vertical_angle_rad; - // Constrain arc to triangle height & max size - real_t arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius); - - real_t arc_2_start_angle = end_to_begin.x < 0 - ? (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad) - : (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI); - real_t arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad; - // Constrain arc to triangle width & max size - real_t arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius); - - viewport->draw_arc(begin, arc_1_radius, arc_1_start_angle, arc_1_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); - viewport->draw_arc(end, arc_2_radius, arc_2_start_angle, arc_2_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); } if (grid_snap_active) { diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index 8f1e6c9ec2..043848080f 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -52,10 +52,10 @@ #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" +#include "scene/3d/marker_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/navigation_region_3d.h" #include "scene/3d/occluder_instance_3d.h" -#include "scene/3d/position_3d.h" #include "scene/3d/ray_cast_3d.h" #include "scene/3d/reflection_probe.h" #include "scene/3d/shape_cast_3d.h" @@ -2292,7 +2292,7 @@ void Label3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { /// -Position3DGizmoPlugin::Position3DGizmoPlugin() { +Marker3DGizmoPlugin::Marker3DGizmoPlugin() { pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); cursor_points = Vector<Vector3>(); @@ -2316,7 +2316,7 @@ Position3DGizmoPlugin::Position3DGizmoPlugin() { // Use the axis color which is brighter for the positive axis. // Use a darkened axis color for the negative axis. - // This makes it possible to see in which direction the Position3D node is rotated + // This makes it possible to see in which direction the Marker3D node is rotated // (which can be important depending on how it's used). const Color color_x = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_x_color"), SNAME("Editor")); cursor_colors.push_back(color_x); @@ -2352,19 +2352,19 @@ Position3DGizmoPlugin::Position3DGizmoPlugin() { pos3d_mesh->surface_set_material(0, mat); } -bool Position3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to<Position3D>(p_spatial) != nullptr; +bool Marker3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to<Marker3D>(p_spatial) != nullptr; } -String Position3DGizmoPlugin::get_gizmo_name() const { - return "Position3D"; +String Marker3DGizmoPlugin::get_gizmo_name() const { + return "Marker3D"; } -int Position3DGizmoPlugin::get_priority() const { +int Marker3DGizmoPlugin::get_priority() const { return -1; } -void Position3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { +void Marker3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->clear(); p_gizmo->add_mesh(pos3d_mesh); p_gizmo->add_collision_segments(cursor_points); diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index 739bf1b929..7dac1bd360 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -334,8 +334,8 @@ public: Label3DGizmoPlugin(); }; -class Position3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(Position3DGizmoPlugin, EditorNode3DGizmoPlugin); +class Marker3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(Marker3DGizmoPlugin, EditorNode3DGizmoPlugin); Ref<ArrayMesh> pos3d_mesh; Vector<Vector3> cursor_points; @@ -346,7 +346,7 @@ public: int get_priority() const override; void redraw(EditorNode3DGizmo *p_gizmo) override; - Position3DGizmoPlugin(); + Marker3DGizmoPlugin(); }; class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 5aa1046166..6add9e2e1e 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -7515,7 +7515,7 @@ void Node3DEditor::_register_all_gizmos() { add_gizmo_plugin(Ref<SoftDynamicBody3DGizmoPlugin>(memnew(SoftDynamicBody3DGizmoPlugin))); add_gizmo_plugin(Ref<Sprite3DGizmoPlugin>(memnew(Sprite3DGizmoPlugin))); add_gizmo_plugin(Ref<Label3DGizmoPlugin>(memnew(Label3DGizmoPlugin))); - add_gizmo_plugin(Ref<Position3DGizmoPlugin>(memnew(Position3DGizmoPlugin))); + add_gizmo_plugin(Ref<Marker3DGizmoPlugin>(memnew(Marker3DGizmoPlugin))); add_gizmo_plugin(Ref<RayCast3DGizmoPlugin>(memnew(RayCast3DGizmoPlugin))); add_gizmo_plugin(Ref<ShapeCast3DGizmoPlugin>(memnew(ShapeCast3DGizmoPlugin))); add_gizmo_plugin(Ref<SpringArm3DGizmoPlugin>(memnew(SpringArm3DGizmoPlugin))); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index e8c3cb8d60..a0c9ddb14b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -49,7 +49,6 @@ #include "editor/find_in_files.h" #include "editor/node_dock.h" #include "editor/plugins/shader_editor_plugin.h" -#include "modules/visual_script/editor/visual_script_editor.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" #include "script_text_editor.h" @@ -1156,8 +1155,8 @@ Ref<Script> ScriptEditor::_get_current_script() { } } -Array ScriptEditor::_get_open_scripts() const { - Array ret; +TypedArray<Script> ScriptEditor::_get_open_scripts() const { + TypedArray<Script> ret; Vector<Ref<Script>> scripts = get_open_scripts(); int scrits_amount = scripts.size(); for (int idx_script = 0; idx_script < scrits_amount; idx_script++) { @@ -3446,8 +3445,8 @@ Vector<Ref<Script>> ScriptEditor::get_open_scripts() const { return out_scripts; } -Array ScriptEditor::_get_open_script_editors() const { - Array script_editors; +TypedArray<ScriptEditorBase> ScriptEditor::_get_open_script_editors() const { + TypedArray<ScriptEditorBase> script_editors; for (int i = 0; i < tab_container->get_tab_count(); i++) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); if (!se) { diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 5bd93e6e42..d1898efb69 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -386,7 +386,7 @@ class ScriptEditor : public PanelContainer { Array _get_cached_breakpoints_for_script(const String &p_path) const; ScriptEditorBase *_get_current_editor() const; - Array _get_open_script_editors() const; + TypedArray<ScriptEditorBase> _get_open_script_editors() const; Ref<ConfigFile> script_editor_cache; void _save_editor_state(ScriptEditorBase *p_editor); @@ -452,7 +452,7 @@ class ScriptEditor : public PanelContainer { void _file_dialog_action(String p_file); Ref<Script> _get_current_script(); - Array _get_open_scripts() const; + TypedArray<Script> _get_open_scripts() const; HashSet<String> textfile_extensions; Ref<TextFile> _load_text_file(const String &p_path, Error *r_error) const; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 961f092650..e048ee2698 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -4312,6 +4312,9 @@ void VisualShaderEditor::_update_varying_tree() { case VisualShader::VARYING_TYPE_FLOAT: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons"))); break; + case VisualShader::VARYING_TYPE_INT: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons"))); + break; case VisualShader::VARYING_TYPE_VECTOR_2D: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons"))); break; @@ -4321,8 +4324,8 @@ void VisualShaderEditor::_update_varying_tree() { case VisualShader::VARYING_TYPE_VECTOR_4D: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons"))); break; - case VisualShader::VARYING_TYPE_COLOR: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons"))); + case VisualShader::VARYING_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons"))); break; case VisualShader::VARYING_TYPE_TRANSFORM: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons"))); @@ -4963,10 +4966,11 @@ VisualShaderEditor::VisualShaderEditor() { varying_type = memnew(OptionButton); hb->add_child(varying_type); varying_type->add_item("Float"); + varying_type->add_item("Int"); varying_type->add_item("Vector2"); varying_type->add_item("Vector3"); varying_type->add_item("Vector4"); - varying_type->add_item("Color"); + varying_type->add_item("Boolean"); varying_type->add_item("Transform"); varying_name = memnew(LineEdit); @@ -5759,10 +5763,11 @@ public: Ref<Texture2D> type_icon[] = { EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")), - EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")), }; diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index bb053d1909..9e3062d3bc 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -997,6 +997,7 @@ static const char *gdscript_properties_renames[][2] = { { "close_h_ofs", "close_h_offset" }, // Theme { "close_v_ofs", "close_v_offset" }, // Theme { "commentfocus", "comment_focus" }, // Theme + { "contacts_reported", "max_contacts_reported" }, // RigidDynamicBody { "drag_margin_bottom", "drag_bottom_margin" }, // Camera2D { "drag_margin_h_enabled", "drag_horizontal_enabled" }, // Camera2D { "drag_margin_left", "drag_left_margin" }, // Camera2D @@ -1015,6 +1016,7 @@ static const char *gdscript_properties_renames[][2] = { { "gravity_vec", "gravity_direction" }, // Area2D { "hseparation", "h_separation" }, // Theme { "iterations_per_second", "physics_ticks_per_second" }, // Engine + { "invert_enable", "invert_enabled" }, // Polygon2D { "margin_bottom", "offset_bottom" }, // Control broke NinePatchRect, StyleBox { "margin_left", "offset_left" }, // Control broke NinePatchRect, StyleBox { "margin_right", "offset_right" }, // Control broke NinePatchRect, StyleBox @@ -1098,6 +1100,7 @@ static const char *csharp_properties_renames[][2] = { { "GravityVec", "GravityDirection" }, // Area2D { "Hseparation", "HSeparation" }, // Theme { "IterationsPerSecond", "PhysicsTicksPerSecond" }, // Engine + { "InvertEnable", "InvertEnabled" }, // Polygon2D { "MarginBottom", "OffsetBottom" }, // Control broke NinePatchRect, StyleBox { "MarginLeft", "OffsetLeft" }, // Control broke NinePatchRect, StyleBox { "MarginRight", "OffsetRight" }, // Control broke NinePatchRect, StyleBox @@ -1372,6 +1375,8 @@ static const char *class_renames[][2] = { { "PinJoint", "PinJoint3D" }, { "PlaneShape", "WorldBoundaryShape3D" }, { "PopupDialog", "Popup" }, + { "Position2D", "Marker2D" }, + { "Position3D", "Marker3D" }, { "ProceduralSky", "Sky" }, { "RayCast", "RayCast3D" }, { "RayShape", "SeparationRayShape3D" }, |