diff options
Diffstat (limited to 'editor')
95 files changed, 2582 insertions, 1008 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index cb8d98932d..ae54c20fe2 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -449,10 +449,14 @@ void ActionMapEditor::update_action_list(const Vector<ActionInfo> &p_action_info // First Column - Icon Ref<InputEventKey> k = event; if (k.is_valid()) { - if (k->get_physical_keycode() == Key::NONE) { + if (k->get_physical_keycode() == Key::NONE && k->get_keycode() == Key::NONE && k->get_key_label() != Key::NONE) { + event_item->set_icon(0, action_tree->get_theme_icon(SNAME("KeyboardLabel"), SNAME("EditorIcons"))); + } else if (k->get_keycode() != Key::NONE) { event_item->set_icon(0, action_tree->get_theme_icon(SNAME("Keyboard"), SNAME("EditorIcons"))); - } else { + } else if (k->get_physical_keycode() != Key::NONE) { event_item->set_icon(0, action_tree->get_theme_icon(SNAME("KeyboardPhysical"), SNAME("EditorIcons"))); + } else { + event_item->set_icon(0, action_tree->get_theme_icon(SNAME("KeyboardError"), SNAME("EditorIcons"))); } } diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 639f5e6de5..8defa04ada 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -1489,32 +1489,21 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { } } -void AnimationBezierTrackEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec) { +void AnimationBezierTrackEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { v_scroll += p_scroll_vec.y * v_zoom; v_scroll = CLAMP(v_scroll, -100000, 100000); timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); queue_redraw(); } -void AnimationBezierTrackEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { +void AnimationBezierTrackEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { const float v_zoom_orig = v_zoom; - if (p_alt) { + Ref<InputEventWithModifiers> iewm = p_event; + if (iewm.is_valid() && iewm->is_alt_pressed()) { // Alternate zoom (doesn't affect timeline). - if (p_scroll_vec.y > 0) { - v_zoom = MIN(v_zoom * 1.2, 100000); - } else { - v_zoom = MAX(v_zoom / 1.2, 0.000001); - } + v_zoom = CLAMP(v_zoom * p_zoom_factor, 0.000001, 100000); } else { - if (p_scroll_vec.y > 0) { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / 1.05); - } else { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); - } + timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / p_zoom_factor); } v_scroll = v_scroll + (p_origin.y - get_size().y / 2.0) * (v_zoom - v_zoom_orig); queue_redraw(); @@ -1681,7 +1670,7 @@ void AnimationBezierTrackEdit::_bind_methods() { AnimationBezierTrackEdit::AnimationBezierTrackEdit() { panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_scroll_callback), callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback)); play_position = memnew(Control); play_position->set_mouse_filter(MOUSE_FILTER_PASS); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index e6d6424ef2..dbc231ccac 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -174,9 +174,8 @@ class AnimationBezierTrackEdit : public Control { SelectionSet selection; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _draw_line_clipped(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, int p_clip_left, int p_clip_right); void _draw_track(int p_track, const Color &p_color); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 857a9a664a..338a22c070 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1704,25 +1704,13 @@ Control::CursorShape AnimationTimelineEdit::get_cursor_shape(const Point2 &p_pos } } -void AnimationTimelineEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - // Timeline has no vertical scroll, so we change it to horizontal. - p_scroll_vec.x += p_scroll_vec.y; - _pan_callback(-p_scroll_vec * 32); -} - -void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec) { +void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { set_value(get_value() - p_scroll_vec.x / get_zoom_scale()); } -void AnimationTimelineEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - double new_zoom_value; +void AnimationTimelineEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { double current_zoom_value = get_zoom()->get_value(); - if (current_zoom_value <= 0.1) { - new_zoom_value = MAX(0.01, current_zoom_value - 0.01 * SIGN(p_scroll_vec.y)); - } else { - new_zoom_value = p_scroll_vec.y > 0 ? MAX(0.01, current_zoom_value / 1.05) : current_zoom_value * 1.05; - } - get_zoom()->set_value(new_zoom_value); + get_zoom()->set_value(MAX(0.01, current_zoom_value * p_zoom_factor)); } void AnimationTimelineEdit::set_use_fps(bool p_use_fps) { @@ -1798,7 +1786,8 @@ AnimationTimelineEdit::AnimationTimelineEdit() { len_hb->hide(); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_scroll_callback), callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback)); + panner->set_pan_axis(ViewPanner::PAN_AXIS_HORIZONTAL); set_layout_direction(Control::LAYOUT_DIRECTION_LTR); } @@ -5358,32 +5347,23 @@ void AnimationTrackEditor::_toggle_bezier_edit() { } } -void AnimationTrackEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - if (p_alt) { +void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { + Ref<InputEventWithModifiers> iewm = p_event; + if (iewm.is_valid() && iewm->is_alt_pressed()) { if (p_scroll_vec.x < 0 || p_scroll_vec.y < 0) { goto_prev_step(true); } else { goto_next_step(true); } } else { - _pan_callback(-p_scroll_vec * 32); + timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); + scroll->set_v_scroll(scroll->get_v_scroll() - p_scroll_vec.y); } } -void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec) { - timeline->set_value(timeline->get_value() - p_scroll_vec.x / timeline->get_zoom_scale()); - scroll->set_v_scroll(scroll->get_v_scroll() - p_scroll_vec.y); -} - -void AnimationTrackEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - double new_zoom_value; +void AnimationTrackEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { double current_zoom_value = timeline->get_zoom()->get_value(); - if (current_zoom_value <= 0.1) { - new_zoom_value = MAX(0.01, current_zoom_value - 0.01 * SIGN(p_scroll_vec.y)); - } else { - new_zoom_value = p_scroll_vec.y > 0 ? MAX(0.01, current_zoom_value / 1.05) : current_zoom_value * 1.05; - } - timeline->get_zoom()->set_value(new_zoom_value); + timeline->get_zoom()->set_value(MAX(0.01, current_zoom_value * p_zoom_factor)); } void AnimationTrackEditor::_cancel_bezier_edit() { @@ -6398,7 +6378,7 @@ AnimationTrackEditor::AnimationTrackEditor() { timeline->connect("length_changed", callable_mp(this, &AnimationTrackEditor::_update_length)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_scroll_callback), callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback)); scroll = memnew(ScrollContainer); timeline_vbox->add_child(scroll); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index ef06445011..8506d9b80d 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -159,9 +159,8 @@ class AnimationTimelineEdit : public Range { bool use_fps = false; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); bool dragging_timeline = false; bool dragging_hsize = false; @@ -460,9 +459,8 @@ class AnimationTrackEditor : public VBoxContainer { PropertyInfo _find_hint_for_track(int p_idx, NodePath &r_base_path, Variant *r_current_val = nullptr); Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _timeline_value_changed(double); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 6f4736dca7..af528e0381 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -30,6 +30,7 @@ #include "connections_dialog.h" +#include "core/config/project_settings.h" #include "editor/doc_tools.h" #include "editor/editor_help.h" #include "editor/editor_node.h" @@ -165,6 +166,7 @@ void ConnectDialog::_tree_node_selected() { if (!edit_mode) { set_dst_method(generate_method_callback_name(source, signal, current)); } + _update_method_tree(); _update_ok_enabled(); } @@ -182,6 +184,11 @@ void ConnectDialog::_unbind_count_changed(double p_count) { } } +void ConnectDialog::_method_selected() { + TreeItem *selected_item = method_tree->get_selected(); + dst_method->set_text(selected_item->get_text(0)); +} + /* * Adds a new parameter bind to connection. */ @@ -242,14 +249,153 @@ StringName ConnectDialog::generate_method_callback_name(Node *p_source, String p String dst_method; if (p_source == p_target) { - dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_to_self_name")).format(subst); + dst_method = String(GLOBAL_GET("editor/naming/default_signal_callback_to_self_name")).format(subst); } else { - dst_method = String(EDITOR_GET("interface/editors/default_signal_callback_name")).format(subst); + dst_method = String(GLOBAL_GET("editor/naming/default_signal_callback_name")).format(subst); } return dst_method; } +void ConnectDialog::_create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item) { + for (const MethodInfo &mi : p_methods) { + TreeItem *method_item = method_tree->create_item(p_parent_item); + method_item->set_text(0, mi.name); + if (mi.return_val.type == Variant::NIL) { + method_item->set_icon(0, get_theme_icon(SNAME("Variant"), "EditorIcons")); + } else { + method_item->set_icon(0, get_theme_icon(Variant::get_type_name(mi.return_val.type), "EditorIcons")); + } + } +} + +List<MethodInfo> ConnectDialog::_filter_method_list(const List<MethodInfo> &p_methods, const MethodInfo &p_signal, const String &p_search_string) const { + bool check_signal = compatible_methods_only->is_pressed(); + List<MethodInfo> ret; + + for (const MethodInfo &mi : p_methods) { + if (!p_search_string.is_empty() && !mi.name.contains(p_search_string)) { + continue; + } + + if (check_signal) { + if (mi.arguments.size() != p_signal.arguments.size()) { + continue; + } + + bool type_mismatch = false; + const List<PropertyInfo>::Element *E = p_signal.arguments.front(); + for (const List<PropertyInfo>::Element *F = mi.arguments.front(); F; F = F->next(), E = E->next()) { + Variant::Type stype = E->get().type; + Variant::Type mtype = F->get().type; + + if (stype != Variant::NIL && mtype != Variant::NIL && stype != mtype) { + type_mismatch = true; + break; + } + } + + if (type_mismatch) { + continue; + } + } + ret.push_back(mi); + } + return ret; +} + +void ConnectDialog::_update_method_tree() { + method_tree->clear(); + + Color disabled_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")) * 0.7; + String search_string = method_search->get_text(); + Node *target = tree->get_selected(); + if (!target) { + return; + } + + MethodInfo signal_info; + if (compatible_methods_only->is_pressed()) { + List<MethodInfo> signals; + source->get_signal_list(&signals); + for (const MethodInfo &mi : signals) { + if (mi.name == signal) { + signal_info = mi; + break; + } + } + } + + TreeItem *root_item = method_tree->create_item(); + root_item->set_text(0, TTR("Methods")); + root_item->set_selectable(0, false); + + // If a script is attached, get methods from it. + ScriptInstance *si = target->get_script_instance(); + if (si) { + TreeItem *si_item = method_tree->create_item(root_item); + si_item->set_text(0, TTR("Attached Script")); + si_item->set_icon(0, get_theme_icon(SNAME("Script"), SNAME("EditorIcons"))); + si_item->set_selectable(0, false); + + List<MethodInfo> methods; + si->get_method_list(&methods); + methods = _filter_method_list(methods, signal_info, search_string); + + if (methods.is_empty()) { + si_item->set_custom_color(0, disabled_color); + } else { + _create_method_tree_items(methods, si_item); + } + } + + if (script_methods_only->is_pressed()) { + empty_tree_label->set_visible(root_item->get_first_child() == nullptr); + return; + } + + // Get methods from each class in the heirarchy. + StringName current_class = target->get_class_name(); + do { + TreeItem *class_item = method_tree->create_item(root_item); + class_item->set_text(0, current_class); + Ref<Texture2D> icon = get_theme_icon(SNAME("Node"), SNAME("EditorIcons")); + if (has_theme_icon(current_class, SNAME("EditorIcons"))) { + icon = get_theme_icon(current_class, SNAME("EditorIcons")); + } + class_item->set_icon(0, icon); + class_item->set_selectable(0, false); + + List<MethodInfo> methods; + ClassDB::get_method_list(current_class, &methods, true); + methods = _filter_method_list(methods, signal_info, search_string); + + if (methods.is_empty()) { + class_item->set_custom_color(0, disabled_color); + } else { + _create_method_tree_items(methods, class_item); + } + current_class = ClassDB::get_parent_class_nocheck(current_class); + } while (current_class != StringName()); + + empty_tree_label->set_visible(root_item->get_first_child() == nullptr); +} + +void ConnectDialog::_method_check_button_pressed(const CheckButton *p_button) { + if (p_button == script_methods_only) { + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_script_methods_only", p_button->is_pressed()); + } else if (p_button == compatible_methods_only) { + EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "show_compatible_methods_only", p_button->is_pressed()); + } + _update_method_tree(); +} + +void ConnectDialog::_open_method_popup() { + method_popup->popup_centered(); + method_search->clear(); + method_search->grab_focus(); +} + /* * Enables or disables the connect button. The connect button is enabled if a * node is selected and valid in the selected mode. @@ -262,7 +408,7 @@ void ConnectDialog::_update_ok_enabled() { return; } - if (!advanced->is_pressed() && target->get_script().is_null()) { + if (dst_method->get_text().is_empty()) { get_ok_button()->set_disabled(true); return; } @@ -285,17 +431,16 @@ void ConnectDialog::_notification(int p_what) { Ref<StyleBox> style = get_theme_stylebox("normal", "LineEdit")->duplicate(); if (style.is_valid()) { - style->set_default_margin(SIDE_TOP, style->get_default_margin(SIDE_TOP) + 1.0); + style->set_content_margin(SIDE_TOP, style->get_content_margin(SIDE_TOP) + 1.0); from_signal->add_theme_style_override("normal", style); } + method_search->set_right_icon(get_theme_icon("Search", "EditorIcons")); + open_method_tree->set_icon(get_theme_icon("Edit", "EditorIcons")); } break; } } void ConnectDialog::_bind_methods() { - ClassDB::bind_method("_cancel", &ConnectDialog::_cancel_pressed); - ClassDB::bind_method("_update_ok_enabled", &ConnectDialog::_update_ok_enabled); - ADD_SIGNAL(MethodInfo("connected")); } @@ -303,10 +448,18 @@ Node *ConnectDialog::get_source() const { return source; } +ConnectDialog::ConnectionData ConnectDialog::get_source_connection_data() const { + return source_connection_data; +} + StringName ConnectDialog::get_signal_name() const { return signal; } +PackedStringArray ConnectDialog::get_signal_args() const { + return signal_args; +} + NodePath ConnectDialog::get_dst_path() const { return dst_path; } @@ -355,11 +508,12 @@ bool ConnectDialog::is_editing() const { * If creating a connection from scratch, sensible defaults are used. * If editing an existing connection, previous data is retained. */ -void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { +void ConnectDialog::init(const ConnectionData &p_cd, const PackedStringArray &p_signal_args, bool p_edit) { set_hide_on_ok(false); source = static_cast<Node *>(p_cd.source); signal = p_cd.signal; + signal_args = p_signal_args; tree->set_selected(nullptr); tree->set_marked(source, true); @@ -377,22 +531,7 @@ void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { deferred->set_pressed(b_deferred); one_shot->set_pressed(b_oneshot); - MethodInfo r_signal; - Ref<Script> source_script = source->get_script(); - if (source_script.is_valid() && source_script->has_script_signal(signal)) { - List<MethodInfo> signals; - source_script->get_script_signal_list(&signals); - for (MethodInfo &mi : signals) { - if (mi.name == signal) { - r_signal = mi; - break; - } - } - } else { - ClassDB::get_signal(source->get_class(), signal, &r_signal); - } - - unbind_count->set_max(r_signal.arguments.size()); + unbind_count->set_max(p_signal_args.size()); unbind_count->set_value(p_cd.unbinds); _unbind_count_changed(p_cd.unbinds); @@ -402,6 +541,8 @@ void ConnectDialog::init(ConnectionData p_cd, bool p_edit) { cdbinds->notify_changed(); edit_mode = p_edit; + + source_connection_data = p_cd; } void ConnectDialog::popup_dialog(const String &p_for_signal) { @@ -437,7 +578,6 @@ void ConnectDialog::_advanced_pressed() { error_label->set_visible(!_find_first_script(get_tree()->get_edited_scene_root(), get_tree()->get_edited_scene_root())); } - _update_ok_enabled(); EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "use_advanced_connections", advanced->is_pressed()); popup_centered(); @@ -458,8 +598,8 @@ ConnectDialog::ConnectDialog() { vbc_left->set_h_size_flags(Control::SIZE_EXPAND_FILL); from_signal = memnew(LineEdit); - from_signal->set_editable(false); vbc_left->add_margin_child(TTR("From Signal:"), from_signal); + from_signal->set_editable(false); tree = memnew(SceneTreeEditor(false)); tree->set_connecting_signal(true); @@ -476,6 +616,43 @@ ConnectDialog::ConnectDialog() { vbc_left->add_child(error_label); error_label->hide(); + method_popup = memnew(AcceptDialog); + method_popup->set_title(TTR("Select Method")); + method_popup->set_min_size(Vector2(400, 600) * EDSCALE); + add_child(method_popup); + + VBoxContainer *method_vbc = memnew(VBoxContainer); + method_popup->add_child(method_vbc); + + method_search = memnew(LineEdit); + method_vbc->add_child(method_search); + method_search->set_placeholder(TTR("Filter Methods")); + method_search->set_clear_button_enabled(true); + method_search->connect("text_changed", callable_mp(this, &ConnectDialog::_update_method_tree).unbind(1)); + + method_tree = memnew(Tree); + method_vbc->add_child(method_tree); + method_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); + method_tree->set_hide_root(true); + method_tree->connect("item_selected", callable_mp(this, &ConnectDialog::_method_selected)); + method_tree->connect("item_activated", callable_mp((Window *)method_popup, &Window::hide)); + + empty_tree_label = memnew(Label(TTR("No method found matching given filters."))); + method_tree->add_child(empty_tree_label); + empty_tree_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER); + + script_methods_only = memnew(CheckButton(TTR("Script Methods Only"))); + method_vbc->add_child(script_methods_only); + script_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END); + script_methods_only->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_script_methods_only", true)); + script_methods_only->connect("pressed", callable_mp(this, &ConnectDialog::_method_check_button_pressed).bind(script_methods_only)); + + compatible_methods_only = memnew(CheckButton(TTR("Compatible Methods Only"))); + method_vbc->add_child(compatible_methods_only); + compatible_methods_only->set_h_size_flags(Control::SIZE_SHRINK_END); + compatible_methods_only->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "show_compatible_methods_only", true)); + compatible_methods_only->connect("pressed", callable_mp(this, &ConnectDialog::_method_check_button_pressed).bind(compatible_methods_only)); + vbc_right = memnew(VBoxContainer); main_hb->add_child(vbc_right); vbc_right->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -521,14 +698,22 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Unbind Signal Arguments:"), unbind_count); + HBoxContainer *hbc_method = memnew(HBoxContainer); + vbc_left->add_margin_child(TTR("Receiver Method:"), hbc_method); + dst_method = memnew(LineEdit); dst_method->set_h_size_flags(Control::SIZE_EXPAND_FILL); + dst_method->connect("text_changed", callable_mp(method_tree, &Tree::deselect_all).unbind(1)); dst_method->connect("text_submitted", callable_mp(this, &ConnectDialog::_text_submitted)); - vbc_left->add_margin_child(TTR("Receiver Method:"), dst_method); + hbc_method->add_child(dst_method); + + open_method_tree = memnew(Button); + hbc_method->add_child(open_method_tree); + open_method_tree->set_text("Pick"); + open_method_tree->connect("pressed", callable_mp(this, &ConnectDialog::_open_method_popup)); - advanced = memnew(CheckButton); + advanced = memnew(CheckButton(TTR("Advanced"))); vbc_left->add_child(advanced); - advanced->set_text(TTR("Advanced")); advanced->set_h_size_flags(Control::SIZE_SHRINK_BEGIN | Control::SIZE_EXPAND); advanced->set_pressed(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "use_advanced_connections", false)); advanced->connect("pressed", callable_mp(this, &ConnectDialog::_advanced_pressed)); @@ -566,7 +751,7 @@ ConnectDialog::~ConnectDialog() { // Originally copied and adapted from EditorProperty, try to keep style in sync. Control *ConnectionsDockTree::make_custom_tooltip(const String &p_text) const { EditorHelpBit *help_bit = memnew(EditorHelpBit); - help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE); + help_bit->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 1)); // p_text is expected to be something like this: // "gui_input::(event: InputEvent)::<Signal description>" @@ -605,9 +790,6 @@ void ConnectionsDock::_filter_changed(const String &p_text) { * Creates or edits connections based on state of the ConnectDialog when "Connect" is pressed. */ void ConnectionsDock::_make_or_edit_connection() { - TreeItem *it = tree->get_selected(); - ERR_FAIL_COND(!it); - NodePath dst_path = connect_dialog->get_dst_path(); Node *target = selected_node->get_node(dst_path); ERR_FAIL_COND(!target); @@ -645,27 +827,21 @@ void ConnectionsDock::_make_or_edit_connection() { add_script_function = !found_inherited_function; } - PackedStringArray script_function_args; - if (add_script_function) { - // Pick up args here before "it" is deleted by update_tree. - script_function_args = it->get_metadata(0).operator Dictionary()["args"]; - script_function_args.resize(script_function_args.size() - cd.unbinds); - for (int i = 0; i < cd.binds.size(); i++) { - script_function_args.push_back("extra_arg_" + itos(i) + ":" + Variant::get_type_name(cd.binds[i].get_type())); - } - } if (connect_dialog->is_editing()) { - _disconnect(*it); + _disconnect(connect_dialog->get_source_connection_data()); _connect(cd); } else { _connect(cd); } - // IMPORTANT NOTE: _disconnect and _connect cause an update_tree, which will delete the object "it" is pointing to. - it = nullptr; - if (add_script_function) { + PackedStringArray script_function_args = connect_dialog->get_signal_args(); + script_function_args.resize(script_function_args.size() - cd.unbinds); + for (int i = 0; i < cd.binds.size(); i++) { + script_function_args.push_back("extra_arg_" + itos(i) + ":" + Variant::get_type_name(cd.binds[i].get_type())); + } + EditorNode::get_singleton()->emit_signal(SNAME("script_add_function_request"), target, cd.method, script_function_args); hide(); } @@ -676,7 +852,7 @@ void ConnectionsDock::_make_or_edit_connection() { /* * Creates single connection w/ undo-redo functionality. */ -void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) { +void ConnectionsDock::_connect(const ConnectDialog::ConnectionData &p_cd) { Node *source = Object::cast_to<Node>(p_cd.source); Node *target = Object::cast_to<Node>(p_cd.target); @@ -700,18 +876,15 @@ void ConnectionsDock::_connect(ConnectDialog::ConnectionData p_cd) { /* * Break single connection w/ undo-redo functionality. */ -void ConnectionsDock::_disconnect(TreeItem &p_item) { - Connection connection = p_item.get_metadata(0); - ConnectDialog::ConnectionData cd = connection; - - ERR_FAIL_COND(cd.source != selected_node); // Shouldn't happen but... Bugcheck. +void ConnectionsDock::_disconnect(const ConnectDialog::ConnectionData &p_cd) { + ERR_FAIL_COND(p_cd.source != selected_node); // Shouldn't happen but... Bugcheck. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), cd.signal, cd.method)); + undo_redo->create_action(vformat(TTR("Disconnect '%s' from '%s'"), p_cd.signal, p_cd.method)); - Callable callable = cd.get_callable(); - undo_redo->add_do_method(selected_node, "disconnect", cd.signal, callable); - undo_redo->add_undo_method(selected_node, "connect", cd.signal, callable, cd.binds, cd.flags); + Callable callable = p_cd.get_callable(); + undo_redo->add_do_method(selected_node, "disconnect", p_cd.signal, callable); + undo_redo->add_undo_method(selected_node, "connect", p_cd.signal, callable, p_cd.flags); undo_redo->add_do_method(this, "update_tree"); undo_redo->add_undo_method(this, "update_tree"); undo_redo->add_do_method(SceneTreeDock::get_singleton()->get_tree_editor(), "update_tree"); // To force redraw of scene tree. @@ -741,7 +914,7 @@ void ConnectionsDock::_disconnect_all() { if (!_is_connection_inherited(connection)) { ConnectDialog::ConnectionData cd = connection; undo_redo->add_do_method(selected_node, "disconnect", cd.signal, cd.get_callable()); - undo_redo->add_undo_method(selected_node, "connect", cd.signal, cd.get_callable(), cd.binds, cd.flags); + undo_redo->add_undo_method(selected_node, "connect", cd.signal, cd.get_callable(), cd.flags); } child = child->get_next(); } @@ -795,7 +968,10 @@ bool ConnectionsDock::_is_connection_inherited(Connection &p_connection) { * Open connection dialog with TreeItem data to CREATE a brand-new connection. */ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) { - String signal_name = p_item.get_metadata(0).operator Dictionary()["name"]; + Dictionary sinfo = p_item.get_metadata(0); + String signal_name = sinfo["name"]; + PackedStringArray signal_args = sinfo["args"]; + const String &signal_name_ref = signal_name; Node *dst_node = selected_node->get_owner() ? selected_node->get_owner() : selected_node; @@ -809,22 +985,30 @@ void ConnectionsDock::_open_connection_dialog(TreeItem &p_item) { cd.target = dst_node; cd.method = ConnectDialog::generate_method_callback_name(cd.source, signal_name, cd.target); connect_dialog->popup_dialog(signal_name_ref); - connect_dialog->init(cd); + connect_dialog->init(cd, signal_args); connect_dialog->set_title(TTR("Connect a Signal to a Method")); } /* * Open connection dialog with Connection data to EDIT an existing connection. */ -void ConnectionsDock::_open_connection_dialog(ConnectDialog::ConnectionData p_cd) { - Node *src = Object::cast_to<Node>(p_cd.source); - Node *dst = Object::cast_to<Node>(p_cd.target); +void ConnectionsDock::_open_edit_connection_dialog(TreeItem &p_item) { + TreeItem *signal_item = p_item.get_parent(); + ERR_FAIL_COND(!signal_item); + + Connection connection = p_item.get_metadata(0); + ConnectDialog::ConnectionData cd = connection; + + Node *src = Object::cast_to<Node>(cd.source); + Node *dst = Object::cast_to<Node>(cd.target); if (src && dst) { - const String &signal_name_ref = p_cd.signal; - connect_dialog->set_title(TTR("Edit Connection:") + p_cd.signal); + const String &signal_name_ref = cd.signal; + PackedStringArray signal_args = signal_item->get_metadata(0).operator Dictionary()["args"]; + + connect_dialog->set_title(vformat(TTR("Edit Connection: '%s'"), cd.signal)); connect_dialog->popup_dialog(signal_name_ref); - connect_dialog->init(p_cd, true); + connect_dialog->init(cd, signal_args, true); } } @@ -899,14 +1083,14 @@ void ConnectionsDock::_handle_slot_menu_option(int p_option) { switch (p_option) { case EDIT: { - Connection connection = item->get_metadata(0); - _open_connection_dialog(connection); + _open_edit_connection_dialog(*item); } break; case GO_TO_SCRIPT: { _go_to_script(*item); } break; case DISCONNECT: { - _disconnect(*item); + Connection connection = item->get_metadata(0); + _disconnect(connection); update_tree(); } break; } @@ -957,7 +1141,8 @@ void ConnectionsDock::_connect_pressed() { if (_is_item_signal(*item)) { _open_connection_dialog(*item); } else { - _disconnect(*item); + Connection connection = item->get_metadata(0); + _disconnect(connection); update_tree(); } } @@ -1238,9 +1423,6 @@ ConnectionsDock::ConnectionsDock() { tree->connect("item_mouse_selected", callable_mp(this, &ConnectionsDock::_rmb_pressed)); add_theme_constant_override("separation", 3 * EDSCALE); - - EDITOR_DEF("interface/editors/default_signal_callback_name", "_on_{node_name}_{signal_name}"); - EDITOR_DEF("interface/editors/default_signal_callback_to_self_name", "_on_{signal_name}"); } ConnectionsDock::~ConnectionsDock() { diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index 829a98caed..f70970996d 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -88,7 +88,7 @@ public: method = base_callable.get_method(); } - Callable get_callable() { + Callable get_callable() const { if (unbinds > 0) { return Callable(target, method).unbind(unbinds); } else if (!binds.is_empty()) { @@ -107,16 +107,26 @@ private: Label *connect_to_label = nullptr; LineEdit *from_signal = nullptr; Node *source = nullptr; + ConnectionData source_connection_data; StringName signal; + PackedStringArray signal_args; LineEdit *dst_method = nullptr; ConnectDialogBinds *cdbinds = nullptr; bool edit_mode = false; bool first_popup = true; NodePath dst_path; VBoxContainer *vbc_right = nullptr; - SceneTreeEditor *tree = nullptr; AcceptDialog *error = nullptr; + + Button *open_method_tree = nullptr; + AcceptDialog *method_popup = nullptr; + Tree *method_tree = nullptr; + Label *empty_tree_label = nullptr; + LineEdit *method_search = nullptr; + CheckButton *script_methods_only = nullptr; + CheckButton *compatible_methods_only = nullptr; + SpinBox *unbind_count = nullptr; EditorInspector *bind_editor = nullptr; OptionButton *type_list = nullptr; @@ -132,6 +142,14 @@ private: void _item_activated(); void _text_submitted(const String &p_text); void _tree_node_selected(); + + void _method_selected(); + void _create_method_tree_items(const List<MethodInfo> &p_methods, TreeItem *p_parent_item); + List<MethodInfo> _filter_method_list(const List<MethodInfo> &p_methods, const MethodInfo &p_signal, const String &p_search_string) const; + void _update_method_tree(); + void _method_check_button_pressed(const CheckButton *p_button); + void _open_method_popup(); + void _unbind_count_changed(double p_count); void _add_bind(); void _remove_bind(); @@ -145,7 +163,9 @@ protected: public: static StringName generate_method_callback_name(Node *p_source, String p_signal_name, Node *p_target); Node *get_source() const; + ConnectionData get_source_connection_data() const; StringName get_signal_name() const; + PackedStringArray get_signal_args() const; NodePath get_dst_path() const; void set_dst_node(Node *p_node); StringName get_dst_method_name() const; @@ -157,7 +177,7 @@ public: bool get_one_shot() const; bool is_editing() const; - void init(ConnectionData p_cd, bool p_edit = false); + void init(const ConnectionData &p_cd, const PackedStringArray &p_signal_args, bool p_edit = false); void popup_dialog(const String &p_for_signal); ConnectDialog(); @@ -203,8 +223,8 @@ class ConnectionsDock : public VBoxContainer { void _filter_changed(const String &p_text); void _make_or_edit_connection(); - void _connect(ConnectDialog::ConnectionData p_cd); - void _disconnect(TreeItem &p_item); + void _connect(const ConnectDialog::ConnectionData &p_cd); + void _disconnect(const ConnectDialog::ConnectionData &p_cd); void _disconnect_all(); void _tree_item_selected(); @@ -213,7 +233,7 @@ class ConnectionsDock : public VBoxContainer { bool _is_connection_inherited(Connection &p_connection); void _open_connection_dialog(TreeItem &p_item); - void _open_connection_dialog(ConnectDialog::ConnectionData p_cd); + void _open_edit_connection_dialog(TreeItem &p_item); void _go_to_script(TreeItem &p_item); void _handle_signal_menu_option(int p_option); diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index a925e2d1d3..c98ec7b2d5 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -536,12 +536,17 @@ void DependencyRemoveDialog::show(const Vector<String> &p_folders, const Vector< } void DependencyRemoveDialog::ok_pressed() { - for (int i = 0; i < files_to_delete.size(); ++i) { - if (ResourceCache::has(files_to_delete[i])) { - Ref<Resource> res = ResourceCache::get_ref(files_to_delete[i]); + for (const KeyValue<String, String> &E : all_remove_files) { + String file = E.key; + + if (ResourceCache::has(file)) { + Ref<Resource> res = ResourceCache::get_ref(file); + emit_signal(SNAME("resource_removed"), res); res->set_path(""); } + } + for (int i = 0; i < files_to_delete.size(); ++i) { // If the file we are deleting for e.g. the main scene, default environment, // or audio bus layout, we must clear its definition in Project Settings. if (files_to_delete[i] == String(GLOBAL_GET("application/config/icon"))) { @@ -621,6 +626,7 @@ void DependencyRemoveDialog::ok_pressed() { } void DependencyRemoveDialog::_bind_methods() { + ADD_SIGNAL(MethodInfo("resource_removed", PropertyInfo(Variant::OBJECT, "obj"))); ADD_SIGNAL(MethodInfo("file_removed", PropertyInfo(Variant::STRING, "file"))); ADD_SIGNAL(MethodInfo("folder_removed", PropertyInfo(Variant::STRING, "folder"))); } diff --git a/editor/editor_data.h b/editor/editor_data.h index 6a89b3572c..d00501280d 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -201,7 +201,6 @@ public: String get_scene_type(int p_idx) const; void set_scene_path(int p_idx, const String &p_path); Ref<Script> get_scene_root_script(int p_idx) const; - void set_edited_scene_version(uint64_t version, int p_scene_idx = -1); void set_scene_modified_time(int p_idx, uint64_t p_time); uint64_t get_scene_modified_time(int p_idx) const; void clear_edited_scenes(); diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index a83a53ad15..33a1fe4b45 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -980,7 +980,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() { no_profile_selected_help = memnew(Label(TTR("Create or import a profile to edit available classes and properties."))); // Add some spacing above the help label. Ref<StyleBoxEmpty> sb = memnew(StyleBoxEmpty); - sb->set_default_margin(SIDE_TOP, 20 * EDSCALE); + sb->set_content_margin(SIDE_TOP, 20 * EDSCALE); no_profile_selected_help->add_theme_style_override("normal", sb); no_profile_selected_help->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); no_profile_selected_help->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 16ecac4dac..ef33b82390 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1552,6 +1552,11 @@ void EditorFileSystem::_update_script_classes() { int index = -1; EditorFileSystemDirectory *efd = find_file(path, &index); + if (!efd || index < 0) { + // The file was removed + continue; + } + for (int i = 0; i < ScriptServer::get_language_count(); i++) { ScriptLanguage *lang = ScriptServer::get_language(i); if (lang->supports_documentation() && efd->files[index]->type == lang->get_type()) { diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 9b1a5e028b..e11251596a 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -195,8 +195,8 @@ void EditorHelp::_class_desc_resized(bool p_force_update_theme) { display_margin = new_display_margin; Ref<StyleBox> class_desc_stylebox = EditorNode::get_singleton()->get_theme_base()->get_theme_stylebox(SNAME("background"), SNAME("EditorHelp"))->duplicate(); - class_desc_stylebox->set_default_margin(SIDE_LEFT, display_margin); - class_desc_stylebox->set_default_margin(SIDE_RIGHT, display_margin); + class_desc_stylebox->set_content_margin(SIDE_LEFT, display_margin); + class_desc_stylebox->set_content_margin(SIDE_RIGHT, display_margin); class_desc->add_theme_style_override("normal", class_desc_stylebox); class_desc->add_theme_style_override("focused", class_desc_stylebox); } @@ -2370,7 +2370,7 @@ EditorHelpBit::EditorHelpBit() { rich_text = memnew(RichTextLabel); add_child(rich_text); rich_text->connect("meta_clicked", callable_mp(this, &EditorHelpBit::_meta_clicked)); - rich_text->set_fit_content_height(true); + rich_text->set_fit_content(true); set_custom_minimum_size(Size2(0, 50 * EDSCALE)); } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 28e89ff0d8..0166d4c719 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -886,7 +886,7 @@ void EditorProperty::_update_pin_flags() { static Control *make_help_bit(const String &p_text, bool p_property) { EditorHelpBit *help_bit = memnew(EditorHelpBit); - help_bit->get_rich_text()->set_fixed_size_to_width(360 * EDSCALE); + help_bit->get_rich_text()->set_custom_minimum_size(Size2(360 * EDSCALE, 1)); PackedStringArray slices = p_text.split("::", false); if (slices.is_empty()) { @@ -3282,6 +3282,11 @@ void EditorInspector::update_tree() { ped->parse_end(object); _parse_added_editors(main_vbox, nullptr, ped); } + + if (_is_main_editor_inspector()) { + // Updating inspector might invalidate some editing owners. + EditorNode::get_singleton()->hide_unused_editors(); + } } void EditorInspector::update_property(const String &p_prop) { @@ -3306,6 +3311,9 @@ void EditorInspector::_clear() { sections.clear(); pending.clear(); restart_request_props.clear(); + if (_is_main_editor_inspector()) { + EditorNode::get_singleton()->hide_unused_editors(this); + } } Object *EditorInspector::get_edited_object() { @@ -3640,6 +3648,10 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo } } +bool EditorInspector::_is_main_editor_inspector() const { + return InspectorDock::get_singleton() && InspectorDock::get_inspector_singleton() == this; +} + void EditorInspector::_property_changed(const String &p_path, const Variant &p_value, const String &p_name, bool p_changing, bool p_update_all) { // The "changing" variable must be true for properties that trigger events as typing occurs, // like "text_changed" signal. E.g. text property of Label, Button, RichTextLabel, etc. diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 699a88e657..1690302e6e 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -502,6 +502,7 @@ class EditorInspector : public ScrollContainer { bool restrict_to_basic = false; void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field); + bool _is_main_editor_inspector() const; void _property_changed(const String &p_path, const Variant &p_value, const String &p_name = "", bool p_changing = false, bool p_update_all = false); void _multiple_properties_changed(Vector<String> p_paths, Array p_values, bool p_changing = false); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index bbd927dcd9..0d5c820a3c 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -58,6 +58,7 @@ #include "scene/gui/tab_bar.h" #include "scene/gui/tab_container.h" #include "scene/main/window.h" +#include "scene/property_utils.h" #include "scene/resources/packed_scene.h" #include "servers/display_server.h" #include "servers/navigation_server_3d.h" @@ -124,6 +125,7 @@ #include "editor/plugins/asset_library_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/debugger_editor_plugin.h" +#include "editor/plugins/dedicated_server_export_plugin.h" #include "editor/plugins/editor_preview_plugins.h" #include "editor/plugins/editor_resource_conversion_plugin.h" #include "editor/plugins/gdextension_export_plugin.h" @@ -997,6 +999,7 @@ void EditorNode::_resources_reimported(const Vector<String> &p_resources) { for (const String &E : scenes) { reload_scene(E); + reload_instances_with_path_in_edited_scenes(E); } scene_tabs->set_current_tab(current_tab); @@ -2078,51 +2081,56 @@ bool EditorNode::_is_class_editor_disabled_by_feature_profile(const StringName & return false; } -void EditorNode::edit_item(Object *p_object) { +void EditorNode::edit_item(Object *p_object, Object *p_editing_owner) { + ERR_FAIL_NULL(p_editing_owner); + if (p_object && _is_class_editor_disabled_by_feature_profile(p_object->get_class())) { return; } - Vector<EditorPlugin *> top_plugins = editor_plugins_over->get_plugins_list(); Vector<EditorPlugin *> item_plugins; if (p_object) { item_plugins = editor_data.get_subeditors(p_object); } if (!item_plugins.is_empty()) { - bool same = true; - if (item_plugins.size() == top_plugins.size()) { - for (int i = 0; i < item_plugins.size(); i++) { - if (item_plugins[i] != top_plugins[i]) { - same = false; - } + ObjectID owner_id = p_editing_owner->get_instance_id(); + + for (EditorPlugin *plugin : active_plugins[owner_id]) { + if (!item_plugins.has(plugin)) { + plugin->make_visible(false); + plugin->edit(nullptr); } - } else { - same = false; } - if (!same) { - _display_top_editors(false); - _set_top_editors(item_plugins); + for (EditorPlugin *plugin : item_plugins) { + for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) { + if (kv.key != owner_id) { + EditorPropertyResource *epres = Object::cast_to<EditorPropertyResource>(ObjectDB::get_instance(kv.key)); + if (epres && kv.value.has(plugin)) { + // If it's resource property editing the same resource type, fold it. + epres->fold_resource(); + } + kv.value.erase(plugin); + } + } + active_plugins[owner_id].insert(plugin); + editor_plugins_over->add_plugin(plugin); + plugin->edit(p_object); + plugin->make_visible(true); } - _set_editing_top_editors(p_object); - _display_top_editors(true); - } else if (!top_plugins.is_empty()) { - hide_top_editors(); + } else { + hide_unused_editors(p_editing_owner); } } -void EditorNode::edit_item_resource(Ref<Resource> p_resource) { - edit_item(p_resource.ptr()); -} - void EditorNode::push_item(Object *p_object, const String &p_property, bool p_inspector_only) { if (!p_object) { InspectorDock::get_inspector_singleton()->edit(nullptr); NodeDock::get_singleton()->set_node(nullptr); SceneTreeDock::get_singleton()->set_selected(nullptr); InspectorDock::get_singleton()->update(nullptr); - _display_top_editors(false); + hide_unused_editors(); return; } @@ -2150,22 +2158,34 @@ void EditorNode::_save_default_environment() { } } -void EditorNode::hide_top_editors() { - _display_top_editors(false); - - editor_plugins_over->clear(); -} - -void EditorNode::_display_top_editors(bool p_display) { - editor_plugins_over->make_visible(p_display); -} - -void EditorNode::_set_top_editors(Vector<EditorPlugin *> p_editor_plugins_over) { - editor_plugins_over->set_plugins_list(p_editor_plugins_over); -} +void EditorNode::hide_unused_editors(const Object *p_editing_owner) { + if (p_editing_owner) { + const ObjectID id = p_editing_owner->get_instance_id(); + for (EditorPlugin *plugin : active_plugins[id]) { + plugin->make_visible(false); + plugin->edit(nullptr); + editor_plugins_over->remove_plugin(plugin); + } + active_plugins.erase(id); + } else { + // If no editing owner is provided, this method will go over all owners and check if they are valid. + // This is to sweep properties that were removed from the inspector. + List<ObjectID> to_remove; + for (KeyValue<ObjectID, HashSet<EditorPlugin *>> &kv : active_plugins) { + if (!ObjectDB::get_instance(kv.key)) { + to_remove.push_back(kv.key); + for (EditorPlugin *plugin : kv.value) { + plugin->make_visible(false); + plugin->edit(nullptr); + editor_plugins_over->remove_plugin(plugin); + } + } + } -void EditorNode::_set_editing_top_editors(Object *p_current_object) { - editor_plugins_over->edit(p_current_object); + for (const ObjectID &id : to_remove) { + active_plugins.erase(id); + } + } } static bool overrides_external_editor(Object *p_object) { @@ -2199,7 +2219,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) { NodeDock::get_singleton()->set_node(nullptr); InspectorDock::get_singleton()->update(nullptr); - _display_top_editors(false); + hide_unused_editors(); return; } @@ -2327,6 +2347,9 @@ void EditorNode::_edit_current(bool p_skip_foreign) { InspectorDock::get_inspector_singleton()->set_use_folding(!disable_folding); } + Object *editor_owner = is_node ? (Object *)SceneTreeDock::get_singleton() : is_resource ? (Object *)InspectorDock::get_inspector_singleton() + : (Object *)this; + // Take care of the main editor plugin. if (!inspector_only) { @@ -2343,6 +2366,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) { } } + ObjectID editor_owner_id = editor_owner->get_instance_id(); if (main_plugin && !skip_main_plugin) { // Special case if use of external editor is true. Resource *current_res = Object::cast_to<Resource>(current_obj); @@ -2353,15 +2377,22 @@ void EditorNode::_edit_current(bool p_skip_foreign) { } else if (main_plugin != editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible_in_tree() || ScriptEditor::get_singleton()->can_take_away_focus())) { + // Unedit previous plugin. + editor_plugin_screen->edit(nullptr); + active_plugins[editor_owner_id].erase(editor_plugin_screen); // Update screen main_plugin. editor_select(plugin_index); main_plugin->edit(current_obj); } else { editor_plugin_screen->edit(current_obj); } + is_main_screen_editing = true; + } else if (!main_plugin && editor_plugin_screen && is_main_screen_editing) { + editor_plugin_screen->edit(nullptr); + is_main_screen_editing = false; } - edit_item(current_obj); + edit_item(current_obj, editor_owner); } InspectorDock::get_singleton()->update(current_obj); @@ -3058,7 +3089,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } String EditorNode::adjust_scene_name_casing(const String &root_name) { - switch (GLOBAL_GET("editor/scene/scene_naming").operator int()) { + switch (GLOBAL_GET("editor/naming/scene_name_casing").operator int()) { case SCENE_NAME_CASING_AUTO: // Use casing of the root node. break; @@ -3626,12 +3657,12 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { if (get_edited_scene()) { if (current_tab < 2) { - // Use heuristic instead. - int n2d = 0, n3d = 0; - _find_node_types(get_edited_scene(), n2d, n3d); - if (n2d > n3d) { + Node *editor_node = SceneTreeDock::get_singleton()->get_tree_editor()->get_selected(); + editor_node = editor_node == nullptr ? get_edited_scene() : editor_node; + + if (Object::cast_to<Node2D>(editor_node) || Object::cast_to<Control>(editor_node)) { editor_select(EDITOR_2D); - } else if (n3d > n2d) { + } else if (Object::cast_to<Node3D>(editor_node)) { editor_select(EDITOR_3D); } } @@ -3905,6 +3936,134 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b return OK; } +HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *p_node) { + HashMap<StringName, Variant> modified_property_map; + + List<PropertyInfo> pinfo; + p_node->get_property_list(&pinfo); + for (const PropertyInfo &E : pinfo) { + if (E.usage & PROPERTY_USAGE_STORAGE) { + bool is_valid_revert = false; + Variant revert_value = EditorPropertyRevert::get_property_revert_value(p_node, E.name, &is_valid_revert); + Variant current_value = p_node->get(E.name); + if (is_valid_revert) { + if (PropertyUtils::is_property_value_different(current_value, revert_value)) { + modified_property_map[E.name] = current_value; + } + } + } + } + + return modified_property_map; +} + +void EditorNode::update_diff_data_for_node( + Node *p_edited_scene, + Node *p_root, + Node *p_node, + HashMap<NodePath, ModificationNodeEntry> &p_modification_table, + List<AdditiveNodeEntry> &p_addition_list) { + bool node_part_of_subscene = p_node != p_edited_scene && + p_edited_scene->get_scene_inherited_state().is_valid() && + p_edited_scene->get_scene_inherited_state()->find_node_by_path(p_edited_scene->get_path_to(p_node)) >= 0; + + // Loop through the owners until either we reach the root node or nullptr + Node *valid_node_owner = p_node->get_owner(); + while (valid_node_owner) { + if (valid_node_owner == p_root) { + break; + } + valid_node_owner = valid_node_owner->get_owner(); + } + + if ((valid_node_owner == p_root && (p_root != p_edited_scene || !p_edited_scene->get_scene_file_path().is_empty())) || node_part_of_subscene || p_node == p_root) { + HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node); + + // Find all valid connections to other nodes. + List<Connection> connections_to; + p_node->get_all_signal_connections(&connections_to); + + List<ConnectionWithNodePath> valid_connections_to; + for (const Connection &c : connections_to) { + Node *connection_target_node = Object::cast_to<Node>(c.callable.get_object()); + if (connection_target_node) { + // TODO: add support for reinstating custom callables + if (!c.callable.is_custom()) { + ConnectionWithNodePath connection_to; + connection_to.connection = c; + connection_to.node_path = p_node->get_path_to(connection_target_node); + valid_connections_to.push_back(connection_to); + } + } + } + + // Find all valid connections from other nodes. + List<Connection> connections_from; + p_node->get_signals_connected_to_this(&connections_from); + + List<Connection> valid_connections_from; + for (const Connection &c : connections_from) { + Node *source_node = Object::cast_to<Node>(c.signal.get_object()); + + Node *valid_source_owner = nullptr; + if (source_node) { + valid_source_owner = source_node->get_owner(); + while (valid_source_owner) { + if (valid_source_owner == p_root) { + break; + } + valid_source_owner = valid_source_owner->get_owner(); + } + } + + if (!source_node || valid_source_owner == nullptr) { + // TODO: add support for reinstating custom callables + if (!c.callable.is_custom()) { + valid_connections_from.push_back(c); + } + } + } + + // Find all node groups. + List<Node::GroupInfo> groups; + p_node->get_groups(&groups); + + if (!modified_properties.is_empty() || !valid_connections_to.is_empty() || !valid_connections_from.is_empty() || !groups.is_empty()) { + ModificationNodeEntry modification_node_entry; + modification_node_entry.property_table = modified_properties; + modification_node_entry.connections_to = valid_connections_to; + modification_node_entry.connections_from = valid_connections_from; + modification_node_entry.groups = groups; + + p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry; + } + } else { + AdditiveNodeEntry new_additive_node_entry; + new_additive_node_entry.node = p_node; + new_additive_node_entry.parent = p_root->get_path_to(p_node->get_parent()); + new_additive_node_entry.owner = p_node->get_owner(); + new_additive_node_entry.index = p_node->get_index(); + + Node2D *node_2d = Object::cast_to<Node2D>(p_node); + if (node_2d) { + new_additive_node_entry.transform_2d = node_2d->get_relative_transform_to_parent(node_2d->get_parent()); + } + Node3D *node_3d = Object::cast_to<Node3D>(p_node); + if (node_3d) { + new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent()); + } + p_addition_list.push_back(new_additive_node_entry); + + return; + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + update_diff_data_for_node(p_edited_scene, p_root, child, p_modification_table, p_addition_list); + } +} +// + void EditorNode::open_request(const String &p_path) { if (!opening_prev) { List<String>::Element *prev_scene_item = previous_scenes.find(p_path); @@ -5282,7 +5441,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) { _scene_tab_closed(scene_tabs->get_hovered_tab()); } } else { - if ((mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) || (mb->get_button_index() == MouseButton::MIDDLE && mb->is_pressed())) { + if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click()) { _menu_option_confirm(FILE_NEW_SCENE, true); } } @@ -5761,6 +5920,353 @@ void EditorNode::reload_scene(const String &p_path) { scene_tabs->set_current_tab(current_tab); } +void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list) { + String scene_file_path = p_node->get_scene_file_path(); + + // This is going to get messy... + if (p_node->get_scene_file_path() == p_instance_path) { + p_instance_list.push_back(p_node); + } else { + Node *current_node = p_node; + + Ref<SceneState> inherited_state = current_node->get_scene_inherited_state(); + while (inherited_state.is_valid()) { + String inherited_path = inherited_state->get_path(); + if (inherited_path == p_instance_path) { + p_instance_list.push_back(p_node); + break; + } + + inherited_state = inherited_state->get_base_scene_state(); + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + find_all_instances_inheriting_path_in_node(p_root, child, p_instance_path, p_instance_list); + } +} + +void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_instance_path) { + int original_edited_scene_idx = editor_data.get_edited_scene(); + HashMap<int, List<Node *>> edited_scene_map; + + // Walk through each opened scene to get a global list of all instances which match + // the current reimported scenes. + for (int i = 0; i < editor_data.get_edited_scene_count(); i++) { + if (editor_data.get_scene_path(i) != p_instance_path) { + Node *edited_scene_root = editor_data.get_edited_scene_root(i); + + if (edited_scene_root) { + List<Node *> valid_nodes; + find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, p_instance_path, valid_nodes); + if (valid_nodes.size() > 0) { + edited_scene_map[i] = valid_nodes; + } + } + } + } + + if (edited_scene_map.size() > 0) { + // Reload the new instance. + Error err; + Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(p_instance_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err); + instance_scene_packed_scene->set_path(p_instance_path, true); + + ERR_FAIL_COND(err != OK); + ERR_FAIL_COND(instance_scene_packed_scene.is_null()); + + HashMap<String, Ref<PackedScene>> local_scene_cache; + local_scene_cache[p_instance_path] = instance_scene_packed_scene; + + for (const KeyValue<int, List<Node *>> &edited_scene_map_elem : edited_scene_map) { + // Set the current scene. + int current_scene_idx = edited_scene_map_elem.key; + editor_data.set_edited_scene(current_scene_idx); + Node *current_edited_scene = editor_data.get_edited_scene_root(current_scene_idx); + + // Clear the history for this tab (should we allow history to be retained?). + EditorUndoRedoManager::get_singleton()->clear_history(); + + // Update the version + editor_data.is_scene_changed(current_scene_idx); + + for (Node *original_node : edited_scene_map_elem.value) { + // Walk the tree for the current node and extract relevant diff data, storing it in the modification table. + // For additional nodes which are part of the current scene, they get added to the addition table. + HashMap<NodePath, ModificationNodeEntry> modification_table; + List<AdditiveNodeEntry> addition_list; + update_diff_data_for_node(current_edited_scene, original_node, original_node, modification_table, addition_list); + + // Disconnect all relevant connections, all connections from and persistent connections to. + for (const KeyValue<NodePath, ModificationNodeEntry> &modification_table_entry : modification_table) { + for (Connection conn : modification_table_entry.value.connections_from) { + conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable); + } + for (ConnectionWithNodePath cwnp : modification_table_entry.value.connections_to) { + Connection conn = cwnp.connection; + if (conn.flags & CONNECT_PERSIST) { + conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable); + } + } + } + + // Store all the paths for any selected nodes which are ancestors of the node we're replacing. + List<NodePath> selected_node_paths; + for (Node *selected_node : editor_selection->get_selected_node_list()) { + if (selected_node == original_node || original_node->is_ancestor_of(selected_node)) { + selected_node_paths.push_back(original_node->get_path_to(selected_node)); + editor_selection->remove_node(selected_node); + } + } + + // Remove all nodes which were added as additional elements (they will be restored later). + for (AdditiveNodeEntry additive_node_entry : addition_list) { + Node *addition_node = additive_node_entry.node; + addition_node->get_parent()->remove_child(addition_node); + } + + // Clear ownership of the nodes (kind of hack to workaround an issue with + // replace_by when called on nodes in other tabs). + List<Node *> nodes_owned_by_original_node; + original_node->get_owned_by(original_node, &nodes_owned_by_original_node); + for (Node *owned_node : nodes_owned_by_original_node) { + owned_node->set_owner(nullptr); + } + + // Delete all the remaining node children. + while (original_node->get_child_count()) { + Node *child = original_node->get_child(0); + + original_node->remove_child(child); + child->queue_free(); + } + + // Reset the editable instance state. + bool is_editable = true; + Node *owner = original_node->get_owner(); + if (owner) { + is_editable = owner->is_editable_instance(original_node); + } + + // Load a replacement scene for the node. + Ref<PackedScene> current_packed_scene; + if (original_node->get_scene_file_path() == p_instance_path) { + // If the node file name directly matches the scene we're replacing, + // just load it since we already cached it. + current_packed_scene = instance_scene_packed_scene; + } else { + // Otherwise, check the inheritance chain, reloading and caching any scenes + // we require along the way. + List<String> required_load_paths; + String scene_path = original_node->get_scene_file_path(); + // Do we need to check if the paths are empty? + if (!scene_path.is_empty()) { + required_load_paths.push_front(scene_path); + } + Ref<SceneState> inherited_state = original_node->get_scene_inherited_state(); + while (inherited_state.is_valid()) { + String inherited_path = inherited_state->get_path(); + // Do we need to check if the paths are empty? + if (!inherited_path.is_empty()) { + required_load_paths.push_front(inherited_path); + } + inherited_state = inherited_state->get_base_scene_state(); + } + + // Ensure the inheritance chain is loaded in the correct order so that cache can + // be properly updated. + for (String path : required_load_paths) { + if (!local_scene_cache.find(path)) { + current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err); + current_packed_scene->set_path(path, true); + local_scene_cache[path] = current_packed_scene; + } else { + current_packed_scene = local_scene_cache[path]; + } + } + } + + ERR_FAIL_COND(current_packed_scene.is_null()); + + // Instantiate the node. + Node *instantiated_node = nullptr; + if (current_packed_scene.is_valid()) { + instantiated_node = current_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); + } + + ERR_FAIL_COND(!instantiated_node); + + bool original_node_is_displayed_folded = original_node->is_displayed_folded(); + bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder(); + + // Update the name to match + instantiated_node->set_name(original_node->get_name()); + + // Is this replacing the edited root node? + String original_node_file_path = original_node->get_scene_file_path(); + + if (current_edited_scene == original_node) { + instantiated_node->set_scene_instance_state(original_node->get_scene_instance_state()); + // Fix unsaved inherited scene + if (original_node_file_path.is_empty()) { + Ref<SceneState> state = current_packed_scene->get_state(); + state->set_path(current_packed_scene->get_path()); + instantiated_node->set_scene_inherited_state(state); + instantiated_node->set_scene_file_path(String()); + } + editor_data.set_edited_scene_root(instantiated_node); + current_edited_scene = instantiated_node; + + if (original_node->is_inside_tree()) { + SceneTreeDock::get_singleton()->set_edited_scene(current_edited_scene); + original_node->get_tree()->set_edited_scene_root(instantiated_node); + } + } + + // Replace the original node with the instantiated version. + original_node->replace_by(instantiated_node, false); + + // Mark the old node for deletion. + original_node->queue_free(); + + // Restore the folded and placeholder state from the original node. + instantiated_node->set_display_folded(original_node_is_displayed_folded); + instantiated_node->set_scene_instance_load_placeholder(original_node_scene_instance_load_placeholder); + + if (owner) { + Ref<SceneState> ss_inst = owner->get_scene_instance_state(); + if (ss_inst.is_valid()) { + ss_inst->update_instance_resource(p_instance_path, current_packed_scene); + } + + owner->set_editable_instance(instantiated_node, is_editable); + } + + // Attempt to re-add all the additional nodes. + for (AdditiveNodeEntry additive_node_entry : addition_list) { + Node *parent_node = instantiated_node->get_node_or_null(additive_node_entry.parent); + + if (!parent_node) { + parent_node = current_edited_scene; + } + + parent_node->add_child(additive_node_entry.node); + parent_node->move_child(additive_node_entry.node, additive_node_entry.index); + // If the additive node's owner was the node which got replaced, update it. + if (additive_node_entry.owner == original_node) { + additive_node_entry.owner = instantiated_node; + } + + additive_node_entry.node->set_owner(additive_node_entry.owner); + + // If the parent node was lost, attempt to restore the original global transform. + { + Node2D *node_2d = Object::cast_to<Node2D>(additive_node_entry.node); + if (node_2d) { + node_2d->set_transform(additive_node_entry.transform_2d); + } + + Node3D *node_3d = Object::cast_to<Node3D>(additive_node_entry.node); + if (node_3d) { + node_3d->set_transform(additive_node_entry.transform_3d); + } + } + } + + // Restore the selection. + if (selected_node_paths.size()) { + for (NodePath selected_node_path : selected_node_paths) { + Node *selected_node = instantiated_node->get_node_or_null(selected_node_path); + if (selected_node) { + editor_selection->add_node(selected_node); + } + } + editor_selection->update(); + } + + // Attempt to restore the modified properties and signals for the instantitated node and all its owned children. + for (KeyValue<NodePath, ModificationNodeEntry> &E : modification_table) { + NodePath new_current_path = E.key; + Node *modifiable_node = instantiated_node->get_node_or_null(new_current_path); + + if (modifiable_node) { + // Get properties for this node. + List<PropertyInfo> pinfo; + modifiable_node->get_property_list(&pinfo); + + // Get names of all valid property names (TODO: make this more efficent). + List<String> property_names; + for (const PropertyInfo &E2 : pinfo) { + if (E2.usage & PROPERTY_USAGE_STORAGE) { + property_names.push_back(E2.name); + } + } + + // Restore the modified properties for this node. + for (const KeyValue<StringName, Variant> &E2 : E.value.property_table) { + if (property_names.find(E2.key)) { + modifiable_node->set(E2.key, E2.value); + } + } + // Restore the connections to other nodes. + for (const ConnectionWithNodePath &E2 : E.value.connections_to) { + Connection conn = E2.connection; + + // Get the node the callable is targetting. + Node *target_node = cast_to<Node>(conn.callable.get_object()); + + // If the callable object no longer exists or is marked for deletion, + // attempt to reaccquire the closest match by using the node path + // we saved earlier. + if (!target_node || !target_node->is_queued_for_deletion()) { + target_node = modifiable_node->get_node_or_null(E2.node_path); + } + + if (target_node) { + // Reconstruct the callable. + Callable new_callable = Callable(target_node, conn.callable.get_method()); + + if (!modifiable_node->is_connected(conn.signal.get_name(), new_callable)) { + ERR_FAIL_COND(modifiable_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK); + } + } + } + + // Restore the connections from other nodes. + for (const Connection &E2 : E.value.connections_from) { + Connection conn = E2; + + bool valid = modifiable_node->has_method(conn.callable.get_method()) || Ref<Script>(modifiable_node->get_script()).is_null() || Ref<Script>(modifiable_node->get_script())->has_method(conn.callable.get_method()); + ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", conn.signal.get_object()->get_class(), conn.signal.get_name(), conn.callable.get_object()->get_class(), conn.callable.get_method())); + + // Get the object which the signal is connected from. + Object *source_object = conn.signal.get_object(); + + if (source_object) { + ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(modifiable_node, conn.callable.get_method()), conn.flags) != OK); + } + } + + // Re-add the groups. + for (const Node::GroupInfo &E2 : E.value.groups) { + modifiable_node->add_to_group(E2.name, E2.persistent); + } + } + } + } + // Cleanup the history of the changes. + editor_history.cleanup_history(); + + current_edited_scene->propagate_notification(NOTIFICATION_NODE_RECACHE_REQUESTED); + } + edited_scene_map.clear(); + } + editor_data.set_edited_scene(original_edited_scene_idx); + + _edit_current(); +} + int EditorNode::plugin_init_callback_count = 0; void EditorNode::add_plugin_init_callback(EditorPluginInitializeCallback p_callback) { @@ -5924,7 +6430,7 @@ void EditorNode::_feature_profile_changed() { } void EditorNode::_bind_methods() { - GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/scene/scene_naming", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), SCENE_NAME_CASING_SNAKE_CASE); + GLOBAL_DEF(PropertyInfo(Variant::INT, "editor/naming/scene_name_casing", PROPERTY_HINT_ENUM, "Auto,PascalCase,snake_case"), SCENE_NAME_CASING_SNAKE_CASE); ClassDB::bind_method("edit_current", &EditorNode::edit_current); ClassDB::bind_method("edit_node", &EditorNode::edit_node); @@ -6756,6 +7262,7 @@ EditorNode::EditorNode() { project_title->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); project_title->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER); project_title->set_h_size_flags(Control::SIZE_EXPAND_FILL); + project_title->set_mouse_filter(Control::MOUSE_FILTER_PASS); left_spacer->add_child(project_title); } @@ -6917,7 +7424,7 @@ EditorNode::EditorNode() { _reset_play_buttons(); - ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTR("Run Specific Scene"), KeyModifierMask::META | KeyModifierMask::SHIFT | Key::F5); + ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTR("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5); ED_SHORTCUT_OVERRIDE("editor/run_specific_scene", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::R); play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_specific_scene")); @@ -7374,6 +7881,11 @@ EditorNode::EditorNode() { EditorExport::get_singleton()->add_export_plugin(gdextension_export_plugin); + Ref<DedicatedServerExportPlugin> dedicated_server_export_plugin; + dedicated_server_export_plugin.instantiate(); + + EditorExport::get_singleton()->add_export_plugin(dedicated_server_export_plugin); + Ref<PackedSceneEditorTranslationParserPlugin> packed_scene_translation_parser_plugin; packed_scene_translation_parser_plugin.instantiate(); EditorTranslationParser::get_singleton()->add_parser(packed_scene_translation_parser_plugin, EditorTranslationParser::STANDARD); diff --git a/editor/editor_node.h b/editor/editor_node.h index d7a4bd4434..bb10abb589 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -68,6 +68,7 @@ class EditorLayoutsDialog; class EditorLog; class EditorPluginList; class EditorQuickOpen; +class EditorPropertyResource; class EditorResourcePreview; class EditorResourceConversionPlugin; class EditorRun; @@ -86,6 +87,7 @@ class ImportDock; class LinkButton; class MenuBar; class MenuButton; +class Node2D; class NodeDock; class OptionButton; class OrphanResourcesDialog; @@ -293,6 +295,8 @@ private: bool _initializing_plugins = false; HashMap<String, EditorPlugin *> addon_name_to_plugin; LocalVector<String> pending_addons; + HashMap<ObjectID, HashSet<EditorPlugin *>> active_plugins; + bool is_main_screen_editing = false; PanelContainer *scene_root_parent = nullptr; Control *theme_base = nullptr; @@ -592,10 +596,6 @@ private: void _inherit_request(String p_file); void _instantiate_request(const Vector<String> &p_files); - void _display_top_editors(bool p_display); - void _set_top_editors(Vector<EditorPlugin *> p_editor_plugins_over); - void _set_editing_top_editors(Object *p_current_object); - void _quick_opened(); void _quick_run(); void _open_command_palette(); @@ -796,9 +796,8 @@ public: void show_about() { _menu_option_confirm(HELP_ABOUT, false); } void push_item(Object *p_object, const String &p_property = "", bool p_inspector_only = false); - void edit_item(Object *p_object); - void edit_item_resource(Ref<Resource> p_resource); - void hide_top_editors(); + void edit_item(Object *p_object, Object *p_editing_owner); + void hide_unused_editors(const Object *p_editing_owner = nullptr); void select_editor_by_name(const String &p_name); @@ -820,6 +819,37 @@ public: 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, bool p_silent_change_tab = false); Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false); + HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node); + + struct AdditiveNodeEntry { + Node *node = nullptr; + NodePath parent = NodePath(); + Node *owner = nullptr; + int index = 0; + // Used if the original parent node is lost + Transform2D transform_2d; + Transform3D transform_3d; + }; + + struct ConnectionWithNodePath { + Connection connection; + NodePath node_path; + }; + + struct ModificationNodeEntry { + HashMap<StringName, Variant> property_table; + List<ConnectionWithNodePath> connections_to; + List<Connection> connections_from; + List<Node::GroupInfo> groups; + }; + + void update_diff_data_for_node( + Node *p_edited_scene, + Node *p_root, + Node *p_node, + HashMap<NodePath, ModificationNodeEntry> &p_modification_table, + List<AdditiveNodeEntry> &p_addition_list); + bool is_scene_open(const String &p_path); void set_current_scene(int p_idx); @@ -872,6 +902,9 @@ public: void reload_scene(const String &p_path); + void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list); + void reload_instances_with_path_in_edited_scenes(const String &p_path); + bool is_exiting() const { return exiting; } Button *get_pause_button() { return pause_button; } diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 7f02148dfc..14cdbc364e 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -662,7 +662,7 @@ void EditorPlugin::make_visible(bool p_visible) { } void EditorPlugin::edit(Object *p_object) { - if (p_object->is_class("Resource")) { + if (Object::cast_to<Resource>(p_object)) { GDVIRTUAL_CALL(_edit, Ref<Resource>(Object::cast_to<Resource>(p_object))); } else { GDVIRTUAL_CALL(_edit, p_object); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 46f52ec4af..152e77acb7 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -268,7 +268,7 @@ void EditorPropertyTextEnum::_custom_value_accepted() { _custom_value_submitted(new_value); } -void EditorPropertyTextEnum::_custom_value_cancelled() { +void EditorPropertyTextEnum::_custom_value_canceled() { custom_value_edit->set_text(get_edited_object()->get(get_edited_property())); edit_custom_layout->hide(); @@ -380,7 +380,7 @@ EditorPropertyTextEnum::EditorPropertyTextEnum() { cancel_button = memnew(Button); cancel_button->set_flat(true); edit_custom_layout->add_child(cancel_button); - cancel_button->connect("pressed", callable_mp(this, &EditorPropertyTextEnum::_custom_value_cancelled)); + cancel_button->connect("pressed", callable_mp(this, &EditorPropertyTextEnum::_custom_value_canceled)); add_focusable(option_button); add_focusable(edit_button); @@ -3905,40 +3905,7 @@ void EditorPropertyResource::_open_editor_pressed() { Ref<Resource> res = get_edited_object()->get(get_edited_property()); if (res.is_valid()) { // May clear the editor so do it deferred. - callable_mp(EditorNode::get_singleton(), &EditorNode::edit_item_resource).bind(res).call_deferred(); - } -} - -void EditorPropertyResource::_fold_other_editors(Object *p_self) { - if (this == p_self) { - return; - } - - Ref<Resource> res = get_edited_object()->get(get_edited_property()); - if (!res.is_valid()) { - return; - } - - bool use_editor = false; - for (int i = 0; i < EditorNode::get_editor_data().get_editor_plugin_count(); i++) { - EditorPlugin *ep = EditorNode::get_editor_data().get_editor_plugin(i); - if (ep->handles(res.ptr())) { - use_editor = true; - break; - } - } - if (!use_editor) { - return; - } - - opened_editor = false; - - bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); - if (unfolded) { - // Refold. - resource_picker->set_toggle_pressed(false); - get_edited_object()->editor_set_section_unfold(get_edited_property(), false); - update_property(); + callable_mp(EditorNode::get_singleton(), &EditorNode::edit_item).bind(res.ptr(), this).call_deferred(); } } @@ -4091,20 +4058,17 @@ void EditorPropertyResource::update_property() { sub_inspector_vbox->add_child(sub_inspector); resource_picker->set_toggle_pressed(true); - bool use_editor = false; + Array editor_list; for (int i = 0; i < EditorNode::get_editor_data().get_editor_plugin_count(); i++) { EditorPlugin *ep = EditorNode::get_editor_data().get_editor_plugin(i); if (ep->handles(res.ptr())) { - use_editor = true; + editor_list.push_back(ep); } } - if (use_editor) { - // Open editor directly and hide other such editors which are currently open. + if (!editor_list.is_empty()) { + // Open editor directly. _open_editor_pressed(); - if (is_inside_tree()) { - get_tree()->call_deferred(SNAME("call_group"), "_editor_resource_properties", "_fold_other_editors", this); - } opened_editor = true; } @@ -4123,7 +4087,7 @@ void EditorPropertyResource::update_property() { sub_inspector_vbox = nullptr; if (opened_editor) { - EditorNode::get_singleton()->hide_top_editors(); + EditorNode::get_singleton()->hide_unused_editors(); opened_editor = false; } @@ -4157,6 +4121,15 @@ void EditorPropertyResource::set_use_sub_inspector(bool p_enable) { use_sub_inspector = p_enable; } +void EditorPropertyResource::fold_resource() { + bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property()); + if (unfolded) { + resource_picker->set_toggle_pressed(false); + get_edited_object()->editor_set_section_unfold(get_edited_property(), false); + update_property(); + } +} + void EditorPropertyResource::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -4168,14 +4141,8 @@ void EditorPropertyResource::_notification(int p_what) { } } -void EditorPropertyResource::_bind_methods() { - ClassDB::bind_method(D_METHOD("_fold_other_editors"), &EditorPropertyResource::_fold_other_editors); -} - EditorPropertyResource::EditorPropertyResource() { use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector")); - - add_to_group("_editor_resource_properties"); } ////////////// DEFAULT PLUGIN ////////////////////// diff --git a/editor/editor_properties.h b/editor/editor_properties.h index a255af30ee..d29ec12f97 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -121,7 +121,7 @@ class EditorPropertyTextEnum : public EditorProperty { void _edit_custom_value(); void _custom_value_submitted(String p_value); void _custom_value_accepted(); - void _custom_value_cancelled(); + void _custom_value_canceled(); protected: virtual void _set_read_only(bool p_read_only) override; @@ -834,13 +834,11 @@ class EditorPropertyResource : public EditorProperty { void _sub_inspector_object_id_selected(int p_id); void _open_editor_pressed(); - void _fold_other_editors(Object *p_self); void _update_property_bg(); void _update_preferred_shader(); protected: virtual void _set_read_only(bool p_read_only) override; - static void _bind_methods(); void _notification(int p_what); public: @@ -852,6 +850,7 @@ public: void expand_revertable() override; void set_use_sub_inspector(bool p_enable); + void fold_resource(); EditorPropertyResource(); }; diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index cb71a2457b..86ffbccefd 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -97,7 +97,7 @@ void EditorResourcePicker::_update_resource_preview(const String &p_path, const } if (p_preview.is_valid()) { - preview_rect->set_offset(SIDE_LEFT, assign_button->get_icon()->get_width() + assign_button->get_theme_stylebox(SNAME("normal"))->get_default_margin(SIDE_LEFT) + get_theme_constant(SNAME("h_separation"), SNAME("Button"))); + preview_rect->set_offset(SIDE_LEFT, assign_button->get_icon()->get_width() + assign_button->get_theme_stylebox(SNAME("normal"))->get_content_margin(SIDE_LEFT) + get_theme_constant(SNAME("h_separation"), SNAME("Button"))); // Resource-specific stretching. if (Ref<GradientTexture1D>(edited_resource).is_valid() || Ref<Gradient>(edited_resource).is_valid()) { diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 4bcd91376a..d3cceee1a3 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -272,7 +272,9 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { OS::ProcessID pid = 0; Error err = OS::get_singleton()->create_instance(args, &pid); ERR_FAIL_COND_V(err, err); - pids.push_back(pid); + if (pid != 0) { + pids.push_back(pid); + } } status = STATUS_PLAY; diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 4879790b74..21e15bc996 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -474,10 +474,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { /* Filesystem */ // External Programs - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/raster_image_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/vector_image_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/audio_editor", "", "") - EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/external_programs/3d_model_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/raster_image_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/vector_image_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/audio_editor", "", "") + EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "filesystem/external_programs/3d_model_editor", "", "") // Directories EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_DIR, "filesystem/directories/autoscan_project_path", "", "") diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 177266e366..54e14074d9 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -278,11 +278,11 @@ void EditorSpinSlider::_update_value_input_stylebox() { // higher margin to match the location where the text begins. // The margin values below were determined by empirical testing. if (is_layout_rtl()) { - stylebox->set_default_margin(SIDE_LEFT, 0); - stylebox->set_default_margin(SIDE_RIGHT, (!get_label().is_empty() ? 23 : 16) * EDSCALE); + stylebox->set_content_margin(SIDE_LEFT, 0); + stylebox->set_content_margin(SIDE_RIGHT, (!get_label().is_empty() ? 23 : 16) * EDSCALE); } else { - stylebox->set_default_margin(SIDE_LEFT, (!get_label().is_empty() ? 23 : 16) * EDSCALE); - stylebox->set_default_margin(SIDE_RIGHT, 0); + stylebox->set_content_margin(SIDE_LEFT, (!get_label().is_empty() ? 23 : 16) * EDSCALE); + stylebox->set_content_margin(SIDE_RIGHT, 0); } value_input->add_theme_style_override("normal", stylebox); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 7a880d2ab3..5cae3ef3fd 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -197,15 +197,15 @@ void EditorColorMap::create() { 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); - style->set_margin_size_individual(p_left * EDSCALE, p_top * EDSCALE, p_right * EDSCALE, p_bottom * EDSCALE); - style->set_default_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE); + style->set_texture_margin_individual(p_left * EDSCALE, p_top * EDSCALE, p_right * EDSCALE, p_bottom * EDSCALE); + style->set_content_margin_individual((p_left + p_margin_left) * EDSCALE, (p_top + p_margin_top) * EDSCALE, (p_right + p_margin_right) * EDSCALE, (p_bottom + p_margin_bottom) * EDSCALE); style->set_draw_center(p_draw_center); return style; } static Ref<StyleBoxEmpty> make_empty_stylebox(float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1) { Ref<StyleBoxEmpty> style(memnew(StyleBoxEmpty)); - style->set_default_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE); + style->set_content_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE); return style; } @@ -215,7 +215,7 @@ static Ref<StyleBoxFlat> make_flat_stylebox(Color p_color, float p_margin_left = // Adjust level of detail based on the corners' effective sizes. style->set_corner_detail(Math::ceil(0.8 * p_corner_width * EDSCALE)); style->set_corner_radius_all(p_corner_width * EDSCALE); - style->set_default_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE); + style->set_content_margin_individual(p_margin_left * EDSCALE, p_margin_top * EDSCALE, p_margin_right * EDSCALE, p_margin_bottom * EDSCALE); // Work around issue about antialiased edges being blurrier (GH-35279). style->set_anti_aliased(false); return style; @@ -648,7 +648,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { const Vector2 widget_default_margin = Vector2(extra_spacing + 6, extra_spacing + default_margin_size + 1) * EDSCALE; Ref<StyleBoxFlat> style_widget = style_default->duplicate(); - style_widget->set_default_margin_individual(widget_default_margin.x, widget_default_margin.y, widget_default_margin.x, widget_default_margin.y); + style_widget->set_content_margin_individual(widget_default_margin.x, widget_default_margin.y, widget_default_margin.x, widget_default_margin.y); style_widget->set_bg_color(dark_color_1); if (draw_extra_borders) { style_widget->set_border_width_all(Math::round(EDSCALE)); @@ -684,7 +684,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Style for windows, popups, etc.. Ref<StyleBoxFlat> style_popup = style_default->duplicate(); const int popup_margin_size = default_margin_size * EDSCALE * 3; - style_popup->set_default_margin_all(popup_margin_size); + style_popup->set_content_margin_all(popup_margin_size); style_popup->set_border_color(contrast_color_1); const Color shadow_color = Color(0, 0, 0, dark_theme ? 0.3 : 0.1); style_popup->set_shadow_color(shadow_color); @@ -722,12 +722,12 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_tab_base->set_corner_radius(CORNER_BOTTOM_RIGHT, 0); // When using a border width greater than 0, visually line up the left of the selected tab with the underlying panel. - style_tab_base->set_expand_margin_size(SIDE_LEFT, -border_width); + style_tab_base->set_expand_margin(SIDE_LEFT, -border_width); - style_tab_base->set_default_margin(SIDE_LEFT, widget_default_margin.x + 5 * EDSCALE); - style_tab_base->set_default_margin(SIDE_RIGHT, widget_default_margin.x + 5 * EDSCALE); - style_tab_base->set_default_margin(SIDE_BOTTOM, widget_default_margin.y); - style_tab_base->set_default_margin(SIDE_TOP, widget_default_margin.y); + style_tab_base->set_content_margin(SIDE_LEFT, widget_default_margin.x + 5 * EDSCALE); + style_tab_base->set_content_margin(SIDE_RIGHT, widget_default_margin.x + 5 * EDSCALE); + style_tab_base->set_content_margin(SIDE_BOTTOM, widget_default_margin.y); + style_tab_base->set_content_margin(SIDE_TOP, widget_default_margin.y); Ref<StyleBoxFlat> style_tab_selected = style_tab_base->duplicate(); @@ -740,13 +740,13 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_tab_selected->set_corner_radius_all(0); Ref<StyleBoxFlat> style_tab_unselected = style_tab_base->duplicate(); - style_tab_unselected->set_expand_margin_size(SIDE_BOTTOM, 0); + style_tab_unselected->set_expand_margin(SIDE_BOTTOM, 0); style_tab_unselected->set_bg_color(dark_color_1); // Add some spacing between unselected tabs to make them easier to distinguish from each other style_tab_unselected->set_border_color(Color(0, 0, 0, 0)); Ref<StyleBoxFlat> style_tab_disabled = style_tab_base->duplicate(); - style_tab_disabled->set_expand_margin_size(SIDE_BOTTOM, 0); + style_tab_disabled->set_expand_margin(SIDE_BOTTOM, 0); style_tab_disabled->set_bg_color(disabled_bg_color); style_tab_disabled->set_border_color(disabled_bg_color); @@ -772,7 +772,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // CanvasItem Editor Ref<StyleBoxFlat> style_canvas_editor_info = make_flat_stylebox(Color(0.0, 0.0, 0.0, 0.2)); - style_canvas_editor_info->set_expand_margin_size_all(4 * EDSCALE); + style_canvas_editor_info->set_expand_margin_all(4 * EDSCALE); theme->set_stylebox("CanvasItemInfoOverlay", "EditorStyles", style_canvas_editor_info); // 2D and 3D contextual toolbar. @@ -787,7 +787,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Add an underline to the StyleBox, but prevent its minimum vertical size from changing. toolbar_stylebox->set_border_color(accent_color); toolbar_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE)); - toolbar_stylebox->set_default_margin(SIDE_BOTTOM, 0); + toolbar_stylebox->set_content_margin(SIDE_BOTTOM, 0); theme->set_stylebox("ContextualToolbar", "EditorStyles", toolbar_stylebox); // Script Editor @@ -808,11 +808,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> style_write_movie_button = style_widget_pressed->duplicate(); style_write_movie_button->set_bg_color(accent_color); style_write_movie_button->set_corner_radius_all(corner_radius * EDSCALE); - style_write_movie_button->set_default_margin(SIDE_TOP, 0); - style_write_movie_button->set_default_margin(SIDE_BOTTOM, 0); - style_write_movie_button->set_default_margin(SIDE_LEFT, 0); - style_write_movie_button->set_default_margin(SIDE_RIGHT, 0); - style_write_movie_button->set_expand_margin_size(SIDE_RIGHT, 2 * EDSCALE); + style_write_movie_button->set_content_margin(SIDE_TOP, 0); + style_write_movie_button->set_content_margin(SIDE_BOTTOM, 0); + style_write_movie_button->set_content_margin(SIDE_LEFT, 0); + style_write_movie_button->set_content_margin(SIDE_RIGHT, 0); + style_write_movie_button->set_expand_margin(SIDE_RIGHT, 2 * EDSCALE); theme->set_stylebox("MovieWriterButtonPressed", "EditorStyles", style_write_movie_button); theme->set_stylebox("normal", "MenuButton", style_menu); @@ -855,16 +855,16 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { color_inspector_action.a = 0.5; Ref<StyleBoxFlat> style_inspector_action = style_widget->duplicate(); style_inspector_action->set_bg_color(color_inspector_action); - style_inspector_action->set_default_margin(SIDE_RIGHT, ACTION_BUTTON_EXTRA_MARGIN); + style_inspector_action->set_content_margin(SIDE_RIGHT, ACTION_BUTTON_EXTRA_MARGIN); theme->set_stylebox("normal", "InspectorActionButton", style_inspector_action); style_inspector_action = style_widget_hover->duplicate(); - style_inspector_action->set_default_margin(SIDE_RIGHT, ACTION_BUTTON_EXTRA_MARGIN); + style_inspector_action->set_content_margin(SIDE_RIGHT, ACTION_BUTTON_EXTRA_MARGIN); theme->set_stylebox("hover", "InspectorActionButton", style_inspector_action); style_inspector_action = style_widget_pressed->duplicate(); - style_inspector_action->set_default_margin(SIDE_RIGHT, ACTION_BUTTON_EXTRA_MARGIN); + style_inspector_action->set_content_margin(SIDE_RIGHT, ACTION_BUTTON_EXTRA_MARGIN); theme->set_stylebox("pressed", "InspectorActionButton", style_inspector_action); style_inspector_action = style_widget_disabled->duplicate(); - style_inspector_action->set_default_margin(SIDE_RIGHT, ACTION_BUTTON_EXTRA_MARGIN); + style_inspector_action->set_content_margin(SIDE_RIGHT, ACTION_BUTTON_EXTRA_MARGIN); theme->set_stylebox("disabled", "InspectorActionButton", style_inspector_action); theme->set_constant("h_separation", "InspectorActionButton", ACTION_BUTTON_EXTRA_MARGIN); @@ -908,11 +908,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> style_option_button_pressed = style_widget_pressed->duplicate(); Ref<StyleBoxFlat> style_option_button_disabled = style_widget_disabled->duplicate(); - style_option_button_focus->set_default_margin(SIDE_RIGHT, 4 * EDSCALE); - style_option_button_normal->set_default_margin(SIDE_RIGHT, 4 * EDSCALE); - style_option_button_hover->set_default_margin(SIDE_RIGHT, 4 * EDSCALE); - style_option_button_pressed->set_default_margin(SIDE_RIGHT, 4 * EDSCALE); - style_option_button_disabled->set_default_margin(SIDE_RIGHT, 4 * EDSCALE); + style_option_button_focus->set_content_margin(SIDE_RIGHT, 4 * EDSCALE); + style_option_button_normal->set_content_margin(SIDE_RIGHT, 4 * EDSCALE); + style_option_button_hover->set_content_margin(SIDE_RIGHT, 4 * EDSCALE); + style_option_button_pressed->set_content_margin(SIDE_RIGHT, 4 * EDSCALE); + style_option_button_disabled->set_content_margin(SIDE_RIGHT, 4 * EDSCALE); theme->set_stylebox("focus", "OptionButton", style_option_button_focus); theme->set_stylebox("normal", "OptionButton", style_widget); @@ -978,7 +978,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Checkbox Ref<StyleBoxFlat> sb_checkbox = style_menu->duplicate(); - sb_checkbox->set_default_margin_all(default_margin_size * EDSCALE); + sb_checkbox->set_content_margin_all(default_margin_size * EDSCALE); theme->set_stylebox("normal", "CheckBox", sb_checkbox); theme->set_stylebox("pressed", "CheckBox", sb_checkbox); @@ -1018,7 +1018,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Use 1 pixel for the sides, since if 0 is used, the highlight of hovered items is drawn // on top of the popup border. This causes a 'gap' in the panel border when an item is highlighted, // and it looks weird. 1px solves this. - style_popup_menu->set_default_margin_individual(EDSCALE, 2 * EDSCALE, EDSCALE, 2 * EDSCALE); + style_popup_menu->set_content_margin_individual(EDSCALE, 2 * EDSCALE, EDSCALE, 2 * EDSCALE); // Always display a border for PopupMenus so they can be distinguished from their background. style_popup_menu->set_border_width_all(EDSCALE); if (draw_extra_borders) { @@ -1078,7 +1078,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { sub_inspector_bg->set_bg_color(dark_color_1.lerp(si_base_color, 0.08)); sub_inspector_bg->set_border_width_all(2 * EDSCALE); sub_inspector_bg->set_border_color(si_base_color * Color(0.7, 0.7, 0.7, 0.8)); - sub_inspector_bg->set_default_margin_all(4 * EDSCALE); + sub_inspector_bg->set_content_margin_all(4 * EDSCALE); sub_inspector_bg->set_corner_radius(CORNER_TOP_LEFT, 0); sub_inspector_bg->set_corner_radius(CORNER_TOP_RIGHT, 0); @@ -1318,7 +1318,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_content_panel->set_corner_radius(CORNER_TOP_LEFT, 0); style_content_panel->set_corner_radius(CORNER_TOP_RIGHT, 0); // Compensate for the border. - style_content_panel->set_default_margin_individual(margin_size_extra * EDSCALE, (2 + margin_size_extra) * EDSCALE, margin_size_extra * EDSCALE, margin_size_extra * EDSCALE); + style_content_panel->set_content_margin_individual(margin_size_extra * EDSCALE, (2 + margin_size_extra) * EDSCALE, margin_size_extra * EDSCALE, margin_size_extra * EDSCALE); theme->set_stylebox("panel", "TabContainer", style_content_panel); // Bottom panel. @@ -1339,15 +1339,15 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // This stylebox is used in 3d and 2d viewports (no borders). Ref<StyleBoxFlat> style_content_panel_vp = style_content_panel->duplicate(); - style_content_panel_vp->set_default_margin_individual(border_width * 2, default_margin_size * EDSCALE, border_width * 2, border_width * 2); + style_content_panel_vp->set_content_margin_individual(border_width * 2, default_margin_size * EDSCALE, border_width * 2, border_width * 2); theme->set_stylebox("Content", "EditorStyles", style_content_panel_vp); // This stylebox is used by preview tabs in the Theme Editor. Ref<StyleBoxFlat> style_theme_preview_tab = style_tab_selected_odd->duplicate(); - style_theme_preview_tab->set_expand_margin_size(SIDE_BOTTOM, 5 * EDSCALE); + style_theme_preview_tab->set_expand_margin(SIDE_BOTTOM, 5 * EDSCALE); theme->set_stylebox("ThemeEditorPreviewFG", "EditorStyles", style_theme_preview_tab); Ref<StyleBoxFlat> style_theme_preview_bg_tab = style_tab_unselected->duplicate(); - style_theme_preview_bg_tab->set_expand_margin_size(SIDE_BOTTOM, 2 * EDSCALE); + style_theme_preview_bg_tab->set_expand_margin(SIDE_BOTTOM, 2 * EDSCALE); theme->set_stylebox("ThemeEditorPreviewBG", "EditorStyles", style_theme_preview_bg_tab); // Separators @@ -1361,9 +1361,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_stylebox("DebuggerPanel", "EditorStyles", style_panel_debugger); Ref<StyleBoxFlat> style_panel_invisible_top = style_content_panel->duplicate(); - int stylebox_offset = theme->get_font(SNAME("tab_selected"), SNAME("TabContainer"))->get_height(theme->get_font_size(SNAME("tab_selected"), SNAME("TabContainer"))) + theme->get_stylebox(SNAME("tab_selected"), SNAME("TabContainer"))->get_minimum_size().height + theme->get_stylebox(SNAME("panel"), SNAME("TabContainer"))->get_default_margin(SIDE_TOP); - style_panel_invisible_top->set_expand_margin_size(SIDE_TOP, -stylebox_offset); - style_panel_invisible_top->set_default_margin(SIDE_TOP, 0); + int stylebox_offset = theme->get_font(SNAME("tab_selected"), SNAME("TabContainer"))->get_height(theme->get_font_size(SNAME("tab_selected"), SNAME("TabContainer"))) + theme->get_stylebox(SNAME("tab_selected"), SNAME("TabContainer"))->get_minimum_size().height + theme->get_stylebox(SNAME("panel"), SNAME("TabContainer"))->get_content_margin(SIDE_TOP); + style_panel_invisible_top->set_expand_margin(SIDE_TOP, -stylebox_offset); + style_panel_invisible_top->set_content_margin(SIDE_TOP, 0); theme->set_stylebox("BottomPanelDebuggerOverride", "EditorStyles", style_panel_invisible_top); // LineEdit @@ -1371,7 +1371,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> style_line_edit = style_widget->duplicate(); // The original style_widget style has an extra 1 pixel offset that makes LineEdits not align with Buttons, // so this compensates for that. - style_line_edit->set_default_margin(SIDE_TOP, style_line_edit->get_default_margin(SIDE_TOP) - 1 * EDSCALE); + style_line_edit->set_content_margin(SIDE_TOP, style_line_edit->get_content_margin(SIDE_TOP) - 1 * EDSCALE); // Don't round the bottom corner to make the line look sharper. style_tab_selected->set_corner_radius(CORNER_BOTTOM_LEFT, 0); @@ -1459,12 +1459,12 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { style_window_title->set_corner_radius(CORNER_TOP_LEFT, 0); style_window_title->set_corner_radius(CORNER_TOP_RIGHT, 0); // Prevent visible line between window title and body. - style_window_title->set_expand_margin_size(SIDE_BOTTOM, 2 * EDSCALE); + style_window_title->set_expand_margin(SIDE_BOTTOM, 2 * EDSCALE); Ref<StyleBoxFlat> style_window = style_popup->duplicate(); style_window->set_border_color(base_color); style_window->set_border_width(SIDE_TOP, 24 * EDSCALE); - style_window->set_expand_margin_size(SIDE_TOP, 24 * EDSCALE); + style_window->set_expand_margin(SIDE_TOP, 24 * EDSCALE); theme->set_stylebox("embedded_border", "Window", style_window); theme->set_color("title_color", "Window", font_color); @@ -1492,11 +1492,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // HScrollBar Ref<Texture2D> empty_icon = memnew(ImageTexture); - theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); - theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 2, 2, 2, 2)); - theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); + theme->set_stylebox("scroll", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("scroll_focus", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); + theme->set_stylebox("grabber_highlight", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber_pressed", "HScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); theme->set_icon("increment", "HScrollBar", empty_icon); theme->set_icon("increment_highlight", "HScrollBar", empty_icon); @@ -1506,11 +1506,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_icon("decrement_pressed", "HScrollBar", empty_icon); // VScrollBar - theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 0, 0, 0, 0)); - theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); - theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 2, 2, 2, 2)); - theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 2, 2, 2, 2)); + theme->set_stylebox("scroll", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("scroll_focus", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollBg"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabber"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); + theme->set_stylebox("grabber_highlight", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberHl"), SNAME("EditorIcons")), 5, 5, 5, 5, 1, 1, 1, 1)); + theme->set_stylebox("grabber_pressed", "VScrollBar", make_stylebox(theme->get_icon(SNAME("GuiScrollGrabberPressed"), SNAME("EditorIcons")), 6, 6, 6, 6, 1, 1, 1, 1)); theme->set_icon("increment", "VScrollBar", empty_icon); theme->set_icon("increment_highlight", "VScrollBar", empty_icon); @@ -1598,7 +1598,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // is only relevant for default tooltips. Ref<StyleBoxFlat> style_tooltip = style_popup->duplicate(); style_tooltip->set_shadow_size(0); - style_tooltip->set_default_margin_all(default_margin_size * EDSCALE * 0.5); + style_tooltip->set_content_margin_all(default_margin_size * EDSCALE * 0.5); style_tooltip->set_bg_color(dark_color_3 * Color(0.8, 0.8, 0.8, 0.9)); style_tooltip->set_border_width_all(0); theme->set_color("font_color", "TooltipLabel", font_hover_color); @@ -1610,10 +1610,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Ref<StyleBoxFlat> control_editor_popup_style = style_popup->duplicate(); control_editor_popup_style->set_shadow_size(0); - control_editor_popup_style->set_default_margin(SIDE_LEFT, default_margin_size * EDSCALE); - control_editor_popup_style->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE); - control_editor_popup_style->set_default_margin(SIDE_RIGHT, default_margin_size * EDSCALE); - control_editor_popup_style->set_default_margin(SIDE_BOTTOM, default_margin_size * EDSCALE); + control_editor_popup_style->set_content_margin(SIDE_LEFT, default_margin_size * EDSCALE); + control_editor_popup_style->set_content_margin(SIDE_TOP, default_margin_size * EDSCALE); + control_editor_popup_style->set_content_margin(SIDE_RIGHT, default_margin_size * EDSCALE); + control_editor_popup_style->set_content_margin(SIDE_BOTTOM, default_margin_size * EDSCALE); control_editor_popup_style->set_border_width_all(0); theme->set_stylebox("panel", "ControlEditorPopupPanel", control_editor_popup_style); @@ -1826,8 +1826,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Dictionary editor add item. // Expand to the left and right by 4px to compensate for the dictionary editor margins. Ref<StyleBoxFlat> style_dictionary_add_item = make_flat_stylebox(prop_subsection_color, 0, 4, 0, 4, corner_radius); - style_dictionary_add_item->set_expand_margin_size(SIDE_LEFT, 4 * EDSCALE); - style_dictionary_add_item->set_expand_margin_size(SIDE_RIGHT, 4 * EDSCALE); + style_dictionary_add_item->set_expand_margin(SIDE_LEFT, 4 * EDSCALE); + style_dictionary_add_item->set_expand_margin(SIDE_RIGHT, 4 * EDSCALE); theme->set_stylebox("DictionaryAddItem", "EditorStyles", style_dictionary_add_item); Ref<StyleBoxEmpty> vshader_label_style = make_empty_stylebox(2, 1, 2, 1); diff --git a/editor/editor_title_bar.cpp b/editor/editor_title_bar.cpp index 0271bbd64a..ae5cdfd72b 100644 --- a/editor/editor_title_bar.cpp +++ b/editor/editor_title_bar.cpp @@ -30,7 +30,7 @@ #include "editor/editor_title_bar.h" -void EditorTitleBar::input(const Ref<InputEvent> &p_event) { +void EditorTitleBar::gui_input(const Ref<InputEvent> &p_event) { if (!can_move) { return; } diff --git a/editor/editor_title_bar.h b/editor/editor_title_bar.h index 6cac163830..4055476b82 100644 --- a/editor/editor_title_bar.h +++ b/editor/editor_title_bar.h @@ -42,7 +42,7 @@ class EditorTitleBar : public HBoxContainer { bool can_move = false; protected: - virtual void input(const Ref<InputEvent> &p_event) override; + virtual void gui_input(const Ref<InputEvent> &p_event) override; static void _bind_methods(){}; public: diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp index 43f2c4e74e..73b645f351 100644 --- a/editor/editor_toaster.cpp +++ b/editor/editor_toaster.cpp @@ -525,7 +525,7 @@ EditorToaster::EditorToaster() { Ref<StyleBoxFlat> boxes[] = { info_panel_style_background, warning_panel_style_background, error_panel_style_background }; for (int i = 0; i < 3; i++) { - boxes[i]->set_default_margin_individual(int(stylebox_radius * 2.5), 3, int(stylebox_radius * 2.5), 3); + boxes[i]->set_content_margin_individual(int(stylebox_radius * 2.5), 3, int(stylebox_radius * 2.5), 3); } // Theming (progress). diff --git a/editor/editor_zoom_widget.cpp b/editor/editor_zoom_widget.cpp index 5a334bdaa6..3998b33a53 100644 --- a/editor/editor_zoom_widget.cpp +++ b/editor/editor_zoom_widget.cpp @@ -41,12 +41,12 @@ void EditorZoomWidget::_update_zoom_label() { // lower the editor scale to increase the available real estate, // even if their display doesn't have a particularly low DPI. if (zoom >= 10) { - // Don't show a decimal when the zoom level is higher than 1000 %. - zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))) + " " + TS->percent_sign(); + zoom_text = TS->format_number(rtos(Math::round((zoom / MAX(1, EDSCALE)) * 100))); } else { - zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, 0.1))) + " " + TS->percent_sign(); + // 2 decimal places if the zoom is below 10%, 1 decimal place if it's below 1000%. + zoom_text = TS->format_number(rtos(Math::snapped((zoom / MAX(1, EDSCALE)) * 100, (zoom >= 0.1) ? 0.1 : 0.01))); } - + zoom_text += " " + TS->percent_sign(); zoom_reset->set_text(zoom_text); } @@ -134,7 +134,7 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte float new_zoom_index = closest_zoom_index + p_increment_count; float new_zoom = Math::pow(2.f, new_zoom_index / 12.f); - // Restore Editor scale transformation + // Restore Editor scale transformation. new_zoom *= MAX(1, EDSCALE); set_zoom(new_zoom); @@ -179,8 +179,12 @@ EditorZoomWidget::EditorZoomWidget() { zoom_reset = memnew(Button); zoom_reset->set_flat(true); + zoom_reset->add_theme_style_override("normal", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("hover", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("focus", memnew(StyleBoxEmpty)); + zoom_reset->add_theme_style_override("pressed", memnew(StyleBoxEmpty)); add_child(zoom_reset); - zoom_reset->add_theme_constant_override("outline_size", 1); + zoom_reset->add_theme_constant_override("outline_size", Math::ceil(2 * EDSCALE)); zoom_reset->add_theme_color_override("font_outline_color", Color(0, 0, 0)); zoom_reset->add_theme_color_override("font_color", Color(1, 1, 1)); zoom_reset->connect("pressed", callable_mp(this, &EditorZoomWidget::_button_zoom_reset)); @@ -189,7 +193,7 @@ EditorZoomWidget::EditorZoomWidget() { zoom_reset->set_focus_mode(FOCUS_NONE); zoom_reset->set_text_alignment(HORIZONTAL_ALIGNMENT_CENTER); // Prevent the button's size from changing when the text size changes - zoom_reset->set_custom_minimum_size(Size2(75 * EDSCALE, 0)); + zoom_reset->set_custom_minimum_size(Size2(56 * EDSCALE, 0)); zoom_plus = memnew(Button); zoom_plus->set_flat(true); @@ -201,5 +205,5 @@ EditorZoomWidget::EditorZoomWidget() { _update_zoom_label(); - add_theme_constant_override("separation", Math::round(-8 * EDSCALE)); + add_theme_constant_override("separation", 0); } diff --git a/editor/event_listener_line_edit.cpp b/editor/event_listener_line_edit.cpp index 274fe34c52..ea4a7133bf 100644 --- a/editor/event_listener_line_edit.cpp +++ b/editor/event_listener_line_edit.cpp @@ -59,16 +59,42 @@ static const char *_joy_axis_descriptions[(size_t)JoyAxis::MAX * 2] = { String EventListenerLineEdit::get_event_text(const Ref<InputEvent> &p_event, bool p_include_device) { ERR_FAIL_COND_V_MSG(p_event.is_null(), String(), "Provided event is not a valid instance of InputEvent"); - String text = p_event->as_text(); - + String text; Ref<InputEventKey> key = p_event; - if (key.is_valid() && key->is_command_or_control_autoremap()) { + if (key.is_valid()) { + String mods_text = key->InputEventWithModifiers::as_text(); + mods_text = mods_text.is_empty() ? mods_text : mods_text + "+"; + if (key->is_command_or_control_autoremap()) { #ifdef MACOS_ENABLED - text = text.replace("Command", "Command/Ctrl"); + mods_text = mods_text.replace("Command", "Command/Ctrl"); #else - text = text.replace("Ctrl", "Command/Ctrl"); + mods_text = mods_text.replace("Ctrl", "Command/Ctrl"); #endif + } + + if (key->get_keycode() != Key::NONE) { + text += mods_text + keycode_get_string(key->get_keycode()); + } + if (key->get_physical_keycode() != Key::NONE) { + if (!text.is_empty()) { + text += " or "; + } + text += mods_text + keycode_get_string(key->get_physical_keycode()) + " (" + RTR("Physical") + ")"; + } + if (key->get_key_label() != Key::NONE) { + if (!text.is_empty()) { + text += " or "; + } + text += mods_text + keycode_get_string(key->get_key_label()) + " (Unicode)"; + } + + if (text.is_empty()) { + text = "(" + RTR("Unset") + ")"; + } + } else { + text = p_event->as_text(); } + Ref<InputEventMouse> mouse = p_event; Ref<InputEventJoypadMotion> jp_motion = p_event; Ref<InputEventJoypadButton> jp_button = p_event; diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 1d147cc5b9..4900ced2e4 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -45,6 +45,7 @@ void EditorExport::_save() { config->set_value(section, "name", preset->get_name()); config->set_value(section, "platform", preset->get_platform()->get_name()); config->set_value(section, "runnable", preset->is_runnable()); + config->set_value(section, "dedicated_server", preset->is_dedicated_server()); config->set_value(section, "custom_features", preset->get_custom_features()); bool save_files = false; @@ -64,6 +65,11 @@ void EditorExport::_save() { config->set_value(section, "export_filter", "exclude"); save_files = true; } break; + case EditorExportPreset::EXPORT_CUSTOMIZED: { + config->set_value(section, "export_filter", "customized"); + config->set_value(section, "customized_files", preset->get_customized_files()); + save_files = false; + }; } if (save_files) { @@ -213,6 +219,7 @@ void EditorExport::load_config() { preset->set_name(config->get_value(section, "name")); preset->set_runnable(config->get_value(section, "runnable")); + preset->set_dedicated_server(config->get_value(section, "dedicated_server", false)); if (config->has_section_key(section, "custom_features")) { preset->set_custom_features(config->get_value(section, "custom_features")); @@ -233,6 +240,10 @@ void EditorExport::load_config() { } else if (export_filter == "exclude") { preset->set_export_filter(EditorExportPreset::EXCLUDE_SELECTED_RESOURCES); get_files = true; + } else if (export_filter == "customized") { + preset->set_export_filter(EditorExportPreset::EXPORT_CUSTOMIZED); + preset->set_customized_files(config->get_value(section, "customized_files", Dictionary())); + get_files = false; } if (get_files) { diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 8da52e20f7..9f79eecfb7 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -343,6 +343,24 @@ void EditorExportPlatform::_export_find_resources(EditorFileSystemDirectory *p_d } } +void EditorExportPlatform::_export_find_customized_resources(const Ref<EditorExportPreset> &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, HashSet<String> &p_paths) { + for (int i = 0; i < p_dir->get_subdir_count(); i++) { + EditorFileSystemDirectory *subdir = p_dir->get_subdir(i); + _export_find_customized_resources(p_preset, subdir, p_preset->get_file_export_mode(subdir->get_path(), p_mode), p_paths); + } + + for (int i = 0; i < p_dir->get_file_count(); i++) { + if (p_dir->get_file_type(i) == "TextFile") { + continue; + } + String path = p_dir->get_file_path(i); + EditorExportPreset::FileExportMode file_mode = p_preset->get_file_export_mode(path, p_mode); + if (file_mode != EditorExportPreset::MODE_FILE_REMOVE) { + p_paths.insert(path); + } + } +} + void EditorExportPlatform::_export_find_dependencies(const String &p_path, HashSet<String> &p_paths) { if (p_paths.has(p_path)) { return; @@ -639,10 +657,20 @@ bool EditorExportPlatform::_export_customize_object(Object *p_object, LocalVecto return changed; } +bool EditorExportPlatform::_is_editable_ancestor(Node *p_root, Node *p_node) { + while (p_node != nullptr && p_node != p_root) { + if (p_root->is_editable_instance(p_node)) { + return true; + } + p_node = p_node->get_owner(); + } + return false; +} + bool EditorExportPlatform::_export_customize_scene_resources(Node *p_root, Node *p_node, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins) { bool changed = false; - if (p_node == p_root || p_node->get_owner() == p_root) { + if (p_root == p_node || p_node->get_owner() == p_root || _is_editable_ancestor(p_root, p_node)) { if (_export_customize_object(p_node, customize_resources_plugins)) { changed = true; } @@ -757,10 +785,10 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector break; } } + } - if (_export_customize_object(res.ptr(), customize_resources_plugins)) { - modified = true; - } + if (_export_customize_object(res.ptr(), customize_resources_plugins)) { + modified = true; } if (modified || p_force_save) { @@ -796,6 +824,8 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & for (int i = 0; i < files.size(); i++) { paths.erase(files[i]); } + } else if (p_preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED) { + _export_find_customized_resources(p_preset, EditorFileSystem::get_singleton()->get_filesystem(), p_preset->get_file_export_mode("res://"), paths); } else { bool scenes_only = p_preset->get_export_filter() == EditorExportPreset::EXPORT_SELECTED_SCENES; @@ -939,14 +969,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & LocalVector<Ref<EditorExportPlugin>> customize_scenes_plugins; for (int i = 0; i < export_plugins.size(); i++) { - if (export_plugins[i]->_begin_customize_resources(Ref<EditorExportPlatform>(this), features_psa)) { + if (export_plugins.write[i]->_begin_customize_resources(Ref<EditorExportPlatform>(this), features_psa)) { customize_resources_plugins.push_back(export_plugins[i]); custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash); uint64_t hash = export_plugins[i]->_get_customization_configuration_hash(); custom_resources_hash = hash_murmur3_one_64(hash, custom_resources_hash); } - if (export_plugins[i]->_begin_customize_scenes(Ref<EditorExportPlatform>(this), features_psa)) { + if (export_plugins.write[i]->_begin_customize_scenes(Ref<EditorExportPlatform>(this), features_psa)) { customize_scenes_plugins.push_back(export_plugins[i]); custom_resources_hash = hash_murmur3_one_64(export_plugins[i]->_get_name().hash64(), custom_resources_hash); @@ -1218,6 +1248,9 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } } } + for (int i = 0; i < export_plugins.size(); i++) { + custom_list.append_array(export_plugins[i]->_get_export_features(Ref<EditorExportPlatform>(this), p_debug)); + } ProjectSettings::CustomMap custom_map; if (path_remaps.size()) { @@ -1294,7 +1327,9 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } else { // Use default text server data. String icu_data_file = EditorPaths::get_singleton()->get_cache_dir().path_join("tmp_icu_data"); - TS->save_support_data(icu_data_file); + if (!TS->save_support_data(icu_data_file)) { + return ERR_INVALID_DATA; + } Vector<uint8_t> array = FileAccess::get_file_as_bytes(icu_data_file); err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key); DirAccess::remove_file_or_error(icu_data_file); diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index 1fb35759ac..3b4e92c9bd 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -91,6 +91,7 @@ private: Vector<ExportMessage> messages; void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet<String> &p_paths); + void _export_find_customized_resources(const Ref<EditorExportPreset> &p_preset, EditorFileSystemDirectory *p_dir, EditorExportPreset::FileExportMode p_mode, HashSet<String> &p_paths); void _export_find_dependencies(const String &p_path, HashSet<String> &p_paths); static Error _save_pack_file(void *p_userdata, const String &p_path, const Vector<uint8_t> &p_data, int p_file, int p_total, const Vector<String> &p_enc_in_filters, const Vector<String> &p_enc_ex_filters, const Vector<uint8_t> &p_key); @@ -112,6 +113,7 @@ private: bool _export_customize_array(Array &array, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins); bool _export_customize_object(Object *p_object, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins); bool _export_customize_scene_resources(Node *p_root, Node *p_node, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins); + bool _is_editable_ancestor(Node *p_root, Node *p_node); String _export_customize(const String &p_path, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins, LocalVector<Ref<EditorExportPlugin>> &customize_scenes_plugins, HashMap<String, FileExportCache> &export_cache, const String &export_base_path, bool p_force_save); diff --git a/editor/export/editor_export_plugin.cpp b/editor/export/editor_export_plugin.cpp index dfd4520eec..0add55820f 100644 --- a/editor/export/editor_export_plugin.cpp +++ b/editor/export/editor_export_plugin.cpp @@ -141,7 +141,7 @@ void EditorExportPlugin::_export_end_script() { // Customization -bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const { +bool EditorExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) { bool ret = false; GDVIRTUAL_CALL(_begin_customize_resources, p_platform, p_features, ret); return ret; @@ -153,7 +153,7 @@ Ref<Resource> EditorExportPlugin::_customize_resource(const Ref<Resource> &p_res return ret; } -bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const { +bool EditorExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) { bool ret = false; GDVIRTUAL_CALL(_begin_customize_scenes, p_platform, p_features, ret); return ret; @@ -185,6 +185,12 @@ String EditorExportPlugin::_get_name() const { return ret; } +PackedStringArray EditorExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const { + PackedStringArray ret; + GDVIRTUAL_CALL(_get_export_features, p_platform, p_debug, ret); + return ret; +} + void EditorExportPlugin::_export_file(const String &p_path, const String &p_type, const HashSet<String> &p_features) { } diff --git a/editor/export/editor_export_plugin.h b/editor/export/editor_export_plugin.h index 5ac0a70c3e..fad647a67b 100644 --- a/editor/export/editor_export_plugin.h +++ b/editor/export/editor_export_plugin.h @@ -120,18 +120,22 @@ protected: GDVIRTUAL0(_end_customize_scenes) GDVIRTUAL0(_end_customize_resources) + GDVIRTUAL2RC(PackedStringArray, _get_export_features, const Ref<EditorExportPlatform> &, bool); + GDVIRTUAL0RC(String, _get_name) - bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const; // Return true if this plugin does property export customization - Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path); // If nothing is returned, it means do not touch (nothing changed). If something is returned (either the same or a different resource) it means changes are made. + virtual bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features); // Return true if this plugin does property export customization + virtual Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path); // If nothing is returned, it means do not touch (nothing changed). If something is returned (either the same or a different resource) it means changes are made. + + virtual bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features); // Return true if this plugin does property export customization + virtual Node *_customize_scene(Node *p_root, const String &p_path); // Return true if a change was made - bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) const; // Return true if this plugin does property export customization - Node *_customize_scene(Node *p_root, const String &p_path); // Return true if a change was made + virtual uint64_t _get_customization_configuration_hash() const; // Hash used for caching customized resources and scenes. - uint64_t _get_customization_configuration_hash() const; // Hash used for caching customized resources and scenes. + virtual void _end_customize_scenes(); + virtual void _end_customize_resources(); - void _end_customize_scenes(); - void _end_customize_resources(); + virtual PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_export_platform, bool p_debug) const; virtual String _get_name() const; diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp index c6365806b3..6beef623bc 100644 --- a/editor/export/editor_export_preset.cpp +++ b/editor/export/editor_export_preset.cpp @@ -64,15 +64,29 @@ Ref<EditorExportPlatform> EditorExportPreset::get_platform() const { return platform; } -void EditorExportPreset::update_files_to_export() { - Vector<String> to_remove; - for (const String &E : selected_files) { - if (!FileAccess::exists(E)) { - to_remove.push_back(E); +void EditorExportPreset::update_files() { + { + Vector<String> to_remove; + for (const String &E : selected_files) { + if (!FileAccess::exists(E)) { + to_remove.push_back(E); + } + } + for (int i = 0; i < to_remove.size(); ++i) { + selected_files.erase(to_remove[i]); } } - for (int i = 0; i < to_remove.size(); ++i) { - selected_files.erase(to_remove[i]); + + { + Vector<String> to_remove; + for (const KeyValue<String, FileExportMode> &E : customized_files) { + if (!FileAccess::exists(E.key) && !DirAccess::exists(E.key)) { + to_remove.push_back(E.key); + } + } + for (int i = 0; i < to_remove.size(); ++i) { + customized_files.erase(to_remove[i]); + } } } @@ -84,6 +98,48 @@ Vector<String> EditorExportPreset::get_files_to_export() const { return files; } +Dictionary EditorExportPreset::get_customized_files() const { + Dictionary files; + for (const KeyValue<String, FileExportMode> &E : customized_files) { + String mode; + switch (E.value) { + case MODE_FILE_NOT_CUSTOMIZED: { + continue; + } break; + case MODE_FILE_STRIP: { + mode = "strip"; + } break; + case MODE_FILE_KEEP: { + mode = "keep"; + } break; + case MODE_FILE_REMOVE: { + mode = "remove"; + } + } + files[E.key] = mode; + } + return files; +} + +int EditorExportPreset::get_customized_files_count() const { + return customized_files.size(); +} + +void EditorExportPreset::set_customized_files(const Dictionary &p_files) { + for (const Variant *key = p_files.next(nullptr); key; key = p_files.next(key)) { + EditorExportPreset::FileExportMode mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED; + String value = p_files[*key]; + if (value == "strip") { + mode = EditorExportPreset::MODE_FILE_STRIP; + } else if (value == "keep") { + mode = EditorExportPreset::MODE_FILE_KEEP; + } else if (value == "remove") { + mode = EditorExportPreset::MODE_FILE_REMOVE; + } + set_file_export_mode(*key, mode); + } +} + void EditorExportPreset::set_name(const String &p_name) { name = p_name; EditorExport::singleton->save_presets(); @@ -102,6 +158,15 @@ bool EditorExportPreset::is_runnable() const { return runnable; } +void EditorExportPreset::set_dedicated_server(bool p_enable) { + dedicated_server = p_enable; + EditorExport::singleton->save_presets(); +} + +bool EditorExportPreset::is_dedicated_server() const { + return dedicated_server; +} + void EditorExportPreset::set_export_filter(ExportFilter p_filter) { export_filter = p_filter; EditorExport::singleton->save_presets(); @@ -158,6 +223,23 @@ bool EditorExportPreset::has_export_file(const String &p_path) { return selected_files.has(p_path); } +void EditorExportPreset::set_file_export_mode(const String &p_path, EditorExportPreset::FileExportMode p_mode) { + if (p_mode == FileExportMode::MODE_FILE_NOT_CUSTOMIZED) { + customized_files.erase(p_path); + } else { + customized_files.insert(p_path, p_mode); + } + EditorExport::singleton->save_presets(); +} + +EditorExportPreset::FileExportMode EditorExportPreset::get_file_export_mode(const String &p_path, EditorExportPreset::FileExportMode p_default) const { + HashMap<String, FileExportMode>::ConstIterator i = customized_files.find(p_path); + if (i) { + return i->value; + } + return p_default; +} + void EditorExportPreset::set_custom_features(const String &p_custom_features) { custom_features = p_custom_features; EditorExport::singleton->save_presets(); diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h index de208f410e..db139d8860 100644 --- a/editor/export/editor_export_preset.h +++ b/editor/export/editor_export_preset.h @@ -44,6 +44,14 @@ public: EXPORT_SELECTED_SCENES, EXPORT_SELECTED_RESOURCES, EXCLUDE_SELECTED_RESOURCES, + EXPORT_CUSTOMIZED, + }; + + enum FileExportMode { + MODE_FILE_NOT_CUSTOMIZED, + MODE_FILE_STRIP, + MODE_FILE_KEEP, + MODE_FILE_REMOVE, }; private: @@ -55,7 +63,9 @@ private: String exporter; HashSet<String> selected_files; + HashMap<String, FileExportMode> customized_files; bool runnable = false; + bool dedicated_server = false; friend class EditorExport; friend class EditorExportPlatform; @@ -85,20 +95,29 @@ public: bool has(const StringName &p_property) const { return values.has(p_property); } - void update_files_to_export(); + void update_files(); Vector<String> get_files_to_export() const; + Dictionary get_customized_files() const; + int get_customized_files_count() const; + void set_customized_files(const Dictionary &p_files); void add_export_file(const String &p_path); void remove_export_file(const String &p_path); bool has_export_file(const String &p_path); + void set_file_export_mode(const String &p_path, FileExportMode p_mode); + FileExportMode get_file_export_mode(const String &p_path, FileExportMode p_default = MODE_FILE_NOT_CUSTOMIZED) const; + void set_name(const String &p_name); String get_name() const; void set_runnable(bool p_enable); bool is_runnable() const; + void set_dedicated_server(bool p_enable); + bool is_dedicated_server() const; + void set_export_filter(ExportFilter p_filter); ExportFilter get_export_filter() const; diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 2caebc6f04..52c192164f 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -45,6 +45,7 @@ #include "scene/gui/link_button.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" +#include "scene/gui/popup_menu.h" #include "scene/gui/split_container.h" #include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" @@ -165,7 +166,7 @@ void ProjectExportDialog::_update_presets() { if (preset->is_runnable()) { preset_name += " (" + TTR("Runnable") + ")"; } - preset->update_files_to_export(); + preset->update_files(); presets->add_item(preset_name, preset->get_platform()->get_logo()); } @@ -244,6 +245,7 @@ void ProjectExportDialog::_edit_preset(int p_index) { export_filter->select(current->get_export_filter()); include_filters->set_text(current->get_include_filter()); exclude_filters->set_text(current->get_exclude_filter()); + server_strip_message->set_visible(current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED); _fill_resource_tree(); @@ -570,6 +572,7 @@ void ProjectExportDialog::_duplicate_preset() { if (make_runnable) { preset->set_runnable(make_runnable); } + preset->set_dedicated_server(current->is_dedicated_server()); preset->set_export_filter(current->get_export_filter()); preset->set_include_filter(current->get_include_filter()); preset->set_exclude_filter(current->get_exclude_filter()); @@ -692,7 +695,16 @@ void ProjectExportDialog::_export_type_changed(int p_which) { return; } - current->set_export_filter(EditorExportPreset::ExportFilter(p_which)); + EditorExportPreset::ExportFilter filter_type = (EditorExportPreset::ExportFilter)p_which; + current->set_export_filter(filter_type); + current->set_dedicated_server(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED); + server_strip_message->set_visible(filter_type == EditorExportPreset::EXPORT_CUSTOMIZED); + + // Default to stripping everything when first switching to server build. + if (filter_type == EditorExportPreset::EXPORT_CUSTOMIZED && current->get_customized_files_count() == 0) { + current->set_file_export_mode("res://", EditorExportPreset::MODE_FILE_STRIP); + } + updating = true; _fill_resource_tree(); updating = false; @@ -728,25 +740,53 @@ void ProjectExportDialog::_fill_resource_tree() { return; } + TreeItem *root = include_files->create_item(); + + if (f == EditorExportPreset::EXPORT_CUSTOMIZED) { + include_files->set_columns(2); + include_files->set_column_expand(1, false); + include_files->set_column_custom_minimum_width(1, 250 * EDSCALE); + } else { + include_files->set_columns(1); + } + include_label->show(); include_margin->show(); - TreeItem *root = include_files->create_item(); + _fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f); +} - _fill_tree(EditorFileSystem::get_singleton()->get_filesystem(), root, current, f == EditorExportPreset::EXPORT_SELECTED_SCENES); +void ProjectExportDialog::_setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode) { + if (p_mode == EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) { + p_item->set_checked(0, false); + p_item->set_cell_mode(1, TreeItem::CELL_MODE_STRING); + p_item->set_text(1, ""); + p_item->set_editable(1, false); + p_item->set_selectable(1, false); + } else { + p_item->set_checked(0, true); + p_item->set_cell_mode(1, TreeItem::CELL_MODE_CUSTOM); + p_item->set_text(1, file_mode_popup->get_item_text(file_mode_popup->get_item_index(p_mode))); + p_item->set_editable(1, true); + p_item->set_selectable(1, true); + } } -bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> ¤t, bool p_only_scenes) { +bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> ¤t, EditorExportPreset::ExportFilter p_export_filter) { p_item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); p_item->set_icon(0, presets->get_theme_icon(SNAME("folder"), SNAME("FileDialog"))); p_item->set_text(0, p_dir->get_name() + "/"); p_item->set_editable(0, true); p_item->set_metadata(0, p_dir->get_path()); + if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) { + _setup_item_for_file_mode(p_item, current->get_file_export_mode(p_dir->get_path())); + } + bool used = false; for (int i = 0; i < p_dir->get_subdir_count(); i++) { TreeItem *subdir = include_files->create_item(p_item); - if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_only_scenes)) { + if (_fill_tree(p_dir->get_subdir(i), subdir, current, p_export_filter)) { used = true; } else { memdelete(subdir); @@ -755,7 +795,7 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem for (int i = 0; i < p_dir->get_file_count(); i++) { String type = p_dir->get_file_type(i); - if (p_only_scenes && type != "PackedScene") { + if (p_export_filter == EditorExportPreset::EXPORT_SELECTED_SCENES && type != "PackedScene") { continue; } if (type == "TextFile") { @@ -770,9 +810,14 @@ bool ProjectExportDialog::_fill_tree(EditorFileSystemDirectory *p_dir, TreeItem file->set_icon(0, EditorNode::get_singleton()->get_class_icon(type)); file->set_editable(0, true); - file->set_checked(0, current->has_export_file(path)); file->set_metadata(0, path); - file->propagate_check(0); + + if (p_export_filter == EditorExportPreset::EXPORT_CUSTOMIZED) { + _setup_item_for_file_mode(file, current->get_file_export_mode(path)); + } else { + file->set_checked(0, current->has_export_file(path)); + file->propagate_check(0); + } used = true; } @@ -794,7 +839,19 @@ void ProjectExportDialog::_tree_changed() { return; } - item->propagate_check(0); + if (current->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED) { + EditorExportPreset::FileExportMode file_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED; + String path = item->get_metadata(0); + + if (item->is_checked(0)) { + file_mode = current->get_file_export_mode(path, EditorExportPreset::MODE_FILE_STRIP); + } + + _setup_item_for_file_mode(item, file_mode); + current->set_file_export_mode(path, file_mode); + } else { + item->propagate_check(0); + } } void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) { @@ -814,6 +871,30 @@ void ProjectExportDialog::_check_propagated_to_item(Object *p_obj, int column) { } } +void ProjectExportDialog::_tree_popup_edited(bool p_arrow_clicked) { + Rect2 bounds = include_files->get_custom_popup_rect(); + bounds.position += get_global_canvas_transform().get_origin(); + bounds.size *= get_global_canvas_transform().get_scale(); + if (!is_embedding_subwindows()) { + bounds.position += get_position(); + } + file_mode_popup->popup(bounds); +} + +void ProjectExportDialog::_set_file_export_mode(int p_id) { + Ref<EditorExportPreset> current = get_current_preset(); + if (current.is_null()) { + return; + } + + TreeItem *item = include_files->get_edited(); + String path = item->get_metadata(0); + + current->set_file_export_mode(path, (EditorExportPreset::FileExportMode)p_id); + + item->set_text(1, file_mode_popup->get_item_text(file_mode_popup->get_item_index(p_id))); +} + void ProjectExportDialog::_export_pck_zip() { Ref<EditorExportPreset> current = get_current_preset(); ERR_FAIL_COND(current.is_null()); @@ -1068,6 +1149,7 @@ ProjectExportDialog::ProjectExportDialog() { export_filter->add_item(TTR("Export selected scenes (and dependencies)")); export_filter->add_item(TTR("Export selected resources (and dependencies)")); export_filter->add_item(TTR("Export all resources in the project except resources checked below")); + export_filter->add_item(TTR("Export as dedicated server")); resources_vb->add_margin_child(TTR("Export Mode:"), export_filter); export_filter->connect("item_selected", callable_mp(this, &ProjectExportDialog::_export_type_changed)); @@ -1082,6 +1164,36 @@ ProjectExportDialog::ProjectExportDialog() { include_margin->add_child(include_files); include_files->connect("item_edited", callable_mp(this, &ProjectExportDialog::_tree_changed)); include_files->connect("check_propagated_to_item", callable_mp(this, &ProjectExportDialog::_check_propagated_to_item)); + include_files->connect("custom_popup_edited", callable_mp(this, &ProjectExportDialog::_tree_popup_edited)); + + server_strip_message = memnew(Label); + server_strip_message->set_visible(false); + server_strip_message->set_autowrap_mode(TextServer::AUTOWRAP_WORD_SMART); + resources_vb->add_child(server_strip_message); + + { + List<StringName> resource_names; + ClassDB::get_inheriters_from_class("Resource", &resource_names); + + PackedStringArray strippable; + for (StringName resource_name : resource_names) { + if (ClassDB::has_method(resource_name, "create_placeholder", true)) { + strippable.push_back(resource_name); + } + } + strippable.sort(); + + String message = TTR("\"Strip Visuals\" will replace the following resources with placeholders:") + " "; + message += String(", ").join(strippable); + server_strip_message->set_text(message); + } + + file_mode_popup = memnew(PopupMenu); + add_child(file_mode_popup); + file_mode_popup->add_item(TTR("Strip Visuals"), EditorExportPreset::MODE_FILE_STRIP); + file_mode_popup->add_item(TTR("Keep"), EditorExportPreset::MODE_FILE_KEEP); + file_mode_popup->add_item(TTR("Remove"), EditorExportPreset::MODE_FILE_REMOVE); + file_mode_popup->connect("id_pressed", callable_mp(this, &ProjectExportDialog::_set_file_export_mode)); include_filters = memnew(LineEdit); resources_vb->add_margin_child( diff --git a/editor/export/project_export.h b/editor/export/project_export.h index d392dba2a4..63f8fc4a2e 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -31,11 +31,11 @@ #ifndef PROJECT_EXPORT_H #define PROJECT_EXPORT_H +#include "editor/export/editor_export_preset.h" #include "scene/gui/dialogs.h" class CheckBox; class CheckButton; -class EditorExportPreset; class EditorFileDialog; class EditorFileSystemDirectory; class EditorInspector; @@ -43,6 +43,7 @@ class EditorPropertyPath; class ItemList; class MenuButton; class OptionButton; +class PopupMenu; class RichTextLabel; class TabContainer; class Tree; @@ -75,6 +76,8 @@ private: LineEdit *include_filters = nullptr; LineEdit *exclude_filters = nullptr; Tree *include_files = nullptr; + Label *server_strip_message = nullptr; + PopupMenu *file_mode_popup = nullptr; Label *include_label = nullptr; MarginContainer *include_margin = nullptr; @@ -113,9 +116,12 @@ private: void _export_type_changed(int p_which); void _filter_changed(const String &p_filter); void _fill_resource_tree(); - bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> ¤t, bool p_only_scenes); + void _setup_item_for_file_mode(TreeItem *p_item, EditorExportPreset::FileExportMode p_mode); + bool _fill_tree(EditorFileSystemDirectory *p_dir, TreeItem *p_item, Ref<EditorExportPreset> ¤t, EditorExportPreset::ExportFilter p_export_filter); void _tree_changed(); void _check_propagated_to_item(Object *p_obj, int column); + void _tree_popup_edited(bool p_arrow_clicked); + void _set_file_export_mode(int p_id); Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index f41792af7f..378e06b4c9 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -1531,6 +1531,10 @@ void FileSystemDock::_make_scene_confirm() { EditorNode::get_singleton()->save_scene_list({ scene_path }); } +void FileSystemDock::_resource_removed(const Ref<Resource> &p_resource) { + emit_signal(SNAME("resource_removed"), p_resource); +} + void FileSystemDock::_file_removed(String p_file) { emit_signal(SNAME("file_removed"), p_file); @@ -2648,7 +2652,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str p_popup->add_icon_shortcut(get_theme_icon(SNAME("Filesystem"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/show_in_explorer"), FILE_SHOW_IN_EXPLORER); p_popup->set_item_text(p_popup->get_item_index(FILE_SHOW_IN_EXPLORER), item_text); if (!is_directory) { - p_popup->add_icon_item(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), TTR("Open in External Program"), FILE_OPEN_EXTERNAL); + p_popup->add_icon_shortcut(get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL); } path = fpath; } @@ -2869,6 +2873,8 @@ void FileSystemDock::_tree_gui_input(Ref<InputEvent> p_event) { _tree_rmb_option(FILE_RENAME); } else if (ED_IS_SHORTCUT("filesystem_dock/show_in_explorer", p_event)) { _tree_rmb_option(FILE_SHOW_IN_EXPLORER); + } else if (ED_IS_SHORTCUT("filesystem_dock/open_in_external_program", p_event)) { + _tree_rmb_option(FILE_OPEN_EXTERNAL); } else if (ED_IS_SHORTCUT("editor/open_search", p_event)) { focus_on_filter(); } else { @@ -2939,12 +2945,19 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) { } } -void FileSystemDock::_get_imported_files(const String &p_path, Vector<String> &r_files) const { +bool FileSystemDock::_get_imported_files(const String &p_path, String &r_extension, Vector<String> &r_files) const { if (!p_path.ends_with("/")) { if (FileAccess::exists(p_path + ".import")) { + if (r_extension.is_empty()) { + r_extension = p_path.get_extension(); + } else if (r_extension != p_path.get_extension()) { + r_files.clear(); + return false; // File type mismatch, stop search. + } + r_files.push_back(p_path); } - return; + return true; } Ref<DirAccess> da = DirAccess::open(p_path); @@ -2953,11 +2966,14 @@ void FileSystemDock::_get_imported_files(const String &p_path, Vector<String> &r while (!n.is_empty()) { if (n != "." && n != ".." && !n.ends_with(".import")) { String npath = p_path + n + (da->current_is_dir() ? "/" : ""); - _get_imported_files(npath, r_files); + if (!_get_imported_files(npath, r_extension, r_files)) { + return false; + } } n = da->get_next(); } da->list_dir_end(); + return true; } void FileSystemDock::_update_import_dock() { @@ -2982,10 +2998,16 @@ void FileSystemDock::_update_import_dock() { } } - // Expand directory selection + if (!selected.is_empty() && selected[0] == "res://") { + // Scanning res:// is costly and unlikely to yield any useful results. + return; + } + + // Expand directory selection. Vector<String> efiles; - for (int i = 0; i < selected.size(); i++) { - _get_imported_files(selected[i], efiles); + String extension; + for (const String &fpath : selected) { + _get_imported_files(fpath, extension, efiles); } // Check import. @@ -3077,6 +3099,7 @@ void FileSystemDock::_bind_methods() { ADD_SIGNAL(MethodInfo("inherit", PropertyInfo(Variant::STRING, "file"))); ADD_SIGNAL(MethodInfo("instantiate", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"))); + ADD_SIGNAL(MethodInfo("resource_removed", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); ADD_SIGNAL(MethodInfo("file_removed", PropertyInfo(Variant::STRING, "file"))); ADD_SIGNAL(MethodInfo("folder_removed", PropertyInfo(Variant::STRING, "folder"))); ADD_SIGNAL(MethodInfo("files_moved", PropertyInfo(Variant::STRING, "old_file"), PropertyInfo(Variant::STRING, "new_file"))); @@ -3098,6 +3121,7 @@ FileSystemDock::FileSystemDock() { ED_SHORTCUT("filesystem_dock/rename", TTR("Rename..."), Key::F2); ED_SHORTCUT_OVERRIDE("filesystem_dock/rename", "macos", Key::ENTER); ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager")); + ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program")); VBoxContainer *top_vbc = memnew(VBoxContainer); add_child(top_vbc); @@ -3235,6 +3259,7 @@ FileSystemDock::FileSystemDock() { add_child(owners_editor); remove_dialog = memnew(DependencyRemoveDialog); + remove_dialog->connect("resource_removed", callable_mp(this, &FileSystemDock::_resource_removed)); remove_dialog->connect("file_removed", callable_mp(this, &FileSystemDock::_file_removed)); remove_dialog->connect("folder_removed", callable_mp(this, &FileSystemDock::_folder_removed)); add_child(remove_dialog); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 3f12b73043..ede6869eea 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -214,7 +214,7 @@ private: void _file_multi_selected(int p_index, bool p_selected); void _tree_multi_selected(Object *p_item, int p_column, bool p_selected); - void _get_imported_files(const String &p_path, Vector<String> &r_files) const; + bool _get_imported_files(const String &p_path, String &r_extension, Vector<String> &r_files) const; void _update_import_dock(); void _get_all_items_in_dir(EditorFileSystemDirectory *p_efsd, Vector<String> &r_files, Vector<String> &r_folders) const; @@ -227,6 +227,7 @@ private: void _update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const; void _update_project_settings_after_move(const HashMap<String, String> &p_renames) const; + void _resource_removed(const Ref<Resource> &p_resource); void _file_removed(String p_file); void _folder_removed(String p_folder); diff --git a/editor/icons/BoneMapHumanBody.svg b/editor/icons/BoneMapHumanBody.svg index 8674157aaa..818ee63069 100644 --- a/editor/icons/BoneMapHumanBody.svg +++ b/editor/icons/BoneMapHumanBody.svg @@ -1,26 +1 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" - y="0px" width="1024px" height="1024px" viewBox="0 0 1024 1024" enable-background="new 0 0 1024 1024" xml:space="preserve"> -<path fill="#3F3F3F" d="M0,0h1024v1024H0V0z"/> -<path fill="#B2B2B2" d="M512,536.162c7,35,11.645,66.898,14,114c2,40,4,51,2,66c-7.384,55.369,6.77,183.898,8.666,206.667 - c2,24-7.653,24.241-10.666,46.333c-2.449,17.958,79,18.439,65-9c-25-49-2-84,4-221c0.521-11.921-8.967-47.874-2-94 - c11.086-73.414,8.42-107.242,6.5-145.662c-1.245-31.973-1-56.963-9-138.963c-0.976-10.002,5.915-79.268,11.954-79.088 - c42,1.25,97.313-5.009,118.145-14.68c28.901,3.73,97.81-12.047,127.887-16.126c14.541,9.407,16.673,3.335,37.515,9.019 - c5.5,1.5,17.336-1.443,12-5c-7.409-4.937-20.75-8.25-23-12c10.75-2.5,22.365-9.578,36-13.166c9.5-2.5,18.866-11.748,15.5-12.334l0,0 - c-11.5-2-26.03,4.547-37.5,6.5c-15.724,2.678-25.238,3.24-33.334,5.167c-1.227,0.292-3.103,0.763-5.791,0.958 - c0,0-0.02,0.16-0.053,0.437c-36.818,0.994-80.322-9.724-130.31-5.569c-34.026-3.925-94.181-5.16-113.513-5.493 - c-13.911-0.239-59.293-2.583-71.75-0.5c-0.668-4.083-1.5-9.75,0.949-16.468c14.881-7.246,19.188-17.796,27.301-34.694 - c0.922,4.424,6.252,4.929,12.459-14.231c5.661-17.478,2.323-22.254-2.313-22.525c0.172-2.056,0.279-4.105,0.313-6.142 - C573.746,76.562,566,42.163,512,42.163s-61.746,34.399-60.959,82.44c0.034,2.037,0.142,4.086,0.313,6.142 - c-4.637,0.271-7.975,5.047-2.313,22.525c6.207,19.16,11.537,18.655,12.459,14.231c8.112,16.898,12.42,27.448,27.301,34.694 - c2.449,6.718,1.617,12.385,0.949,16.468c-12.457-2.083-57.839,0.261-71.75,0.5c-19.332,0.333-79.486,1.568-113.513,5.493 - c-49.987-4.155-93.491,6.563-130.31,5.569c-0.033-0.277-0.053-0.437-0.053-0.437c-2.688-0.195-4.564-0.666-5.791-0.958 - c-8.096-1.927-17.61-2.489-33.334-5.167c-11.47-1.953-26-8.5-37.5-6.5l0,0c-3.366,0.586,6,9.834,15.5,12.334 - c13.635,3.588,25.25,10.666,36,13.166c-2.25,3.75-15.591,7.063-23,12c-5.336,3.557,6.5,6.5,12,5 - c20.842-5.684,22.974,0.388,37.515-9.019c30.077,4.079,98.985,19.857,127.887,16.126c20.832,9.671,76.145,15.93,118.145,14.68 - c6.039-0.18,12.93,69.085,11.954,79.088c-8,82-7.755,106.99-9,138.963c-1.92,38.419-4.586,72.248,6.5,145.662 - c6.967,46.126-2.521,82.079-2,94c6,137,29,172,4,221c-14,27.439,67.449,26.958,65,9c-3.013-22.092-12.666-22.333-10.666-46.333 - c1.896-22.769,16.05-151.298,8.666-206.667c-2-15,0-26,2-66C500.356,603.061,505,571.162,512,536.162z"/> -</svg> +<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1451_455)"><path d="M0 0H256V256H0V0Z" fill="#3F3F3F"/><path d="M128 134.04C129.75 142.79 130.911 150.765 131.5 162.54C132 172.54 132.5 175.29 132 179.04C130.154 192.883 133.692 225.015 134.166 230.707C134.666 236.707 132.253 236.767 131.5 242.29C130.888 246.78 151.25 246.9 147.75 240.04C141.5 227.79 147.25 219.04 148.75 184.79C148.88 181.81 146.508 172.822 148.25 161.29C151.021 142.937 150.355 134.48 149.875 124.875C149.564 116.882 149.625 110.634 147.625 90.134C147.381 87.6335 149.104 70.317 150.613 70.362C161.113 70.6745 174.942 69.1098 180.15 66.692C187.375 67.6245 204.602 63.6803 212.121 62.6605C215.757 65.0123 216.29 63.4943 221.5 64.9153C222.875 65.2903 225.834 64.5545 224.5 63.6653C222.648 62.431 219.313 61.6028 218.75 60.6653C221.438 60.0403 224.341 58.2708 227.75 57.3738C230.125 56.7488 232.467 54.4368 231.625 54.2903C228.75 53.7903 225.118 55.427 222.25 55.9153C218.319 56.5848 215.941 56.7253 213.917 57.207C213.61 57.28 213.141 57.3978 212.469 57.4465C212.469 57.4465 212.464 57.4865 212.456 57.5558C203.251 57.8043 192.375 55.1248 179.878 56.1635C171.372 55.1823 156.333 54.8735 151.5 54.7903C148.022 54.7305 136.677 54.1445 133.562 54.6653C133.395 53.6445 133.187 52.2278 133.8 50.5483C137.52 48.7368 138.597 46.0993 140.625 41.8748C140.855 42.9808 142.188 43.107 143.74 38.317C145.155 33.9475 144.32 32.7535 143.161 32.6858C143.204 32.1718 143.231 31.6595 143.24 31.1503C143.436 19.1403 141.5 10.5405 128 10.5405C114.5 10.5405 112.563 19.1403 112.76 31.1505C112.769 31.6598 112.796 32.172 112.838 32.686C111.679 32.7538 110.845 33.9478 112.26 38.3173C113.812 43.1073 115.144 42.981 115.375 41.875C117.403 46.0995 118.48 48.737 122.2 50.5485C122.812 52.228 122.604 53.6448 122.437 54.6655C119.323 54.1448 107.978 54.7308 104.5 54.7905C99.6669 54.8738 84.6284 55.1825 76.1217 56.1638C63.6249 55.125 52.7489 57.8045 43.5442 57.556C43.5359 57.4868 43.5309 57.4468 43.5309 57.4468C42.8589 57.398 42.3899 57.2803 42.0832 57.2073C40.0592 56.7255 37.6807 56.585 33.7497 55.9155C30.8822 55.4273 27.2497 53.7905 24.3747 54.2905C23.5332 54.437 25.8747 56.749 28.2497 57.374C31.6584 58.271 34.5622 60.0405 37.2497 60.6655C36.6872 61.603 33.3519 62.4313 31.4997 63.6655C30.1657 64.5548 33.1247 65.2905 34.4997 64.9155C39.7102 63.4945 40.2432 65.0125 43.8784 62.6608C51.3977 63.6805 68.6247 67.625 75.8502 66.6923C81.0582 69.11 94.8864 70.6748 105.386 70.3623C106.896 70.3173 108.619 87.6335 108.375 90.1343C106.375 110.634 106.436 116.882 106.125 124.875C105.645 134.48 104.978 142.937 107.75 161.291C109.492 172.822 107.12 181.81 107.25 184.791C108.75 219.041 114.5 227.791 108.25 240.041C104.75 246.9 125.112 246.78 124.5 242.291C123.747 236.768 121.333 236.707 121.833 230.707C122.307 225.015 125.846 192.883 124 179.041C123.5 175.291 124 172.541 124.5 162.541C125.089 150.765 126.25 142.79 128 134.04Z" fill="#B2B2B2"/></g><defs><clipPath id="clip0_1451_455"><rect width="256" height="256" fill="white"/></clipPath></defs></svg> diff --git a/editor/icons/BoneMapHumanFace.svg b/editor/icons/BoneMapHumanFace.svg index 6cb21140bc..e38c5cb790 100644 --- a/editor/icons/BoneMapHumanFace.svg +++ b/editor/icons/BoneMapHumanFace.svg @@ -1 +1 @@ -<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m788.105 552.967c17.995-57.892 31.896-124.566 30.875-198.071-3.758-270.403-249.846-251.479-295.568-244.947-359.868 51.409-219.047 452.358-220.453 496.426-4.899 153.499 83.686 170.991 161.665 215.554 2.646 1.512 7.259 1.786 13.313 1.111 7.223 25.179 11.762 59.035 9.548 75.638-3.266 24.495 209.021 24.495 209.021 0 0-62.883 12.233-124.363 33.827-188.89 7.143-2.284 16.054-7.601 25.963-16.95 13.681-12.908 34.839-21.774 45.726-63.145 15.615-59.338 3.869-76.074-13.917-76.726z" fill="#b2b2b2"/></svg> +<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1451_458)"><path d="M0 0H256V256H0V0Z" fill="#3F3F3F"/><path d="M197.026 138.242C201.525 123.769 205 107.1 204.745 88.724C203.805 21.1232 142.283 25.8542 130.853 27.4872C40.8859 40.3395 76.0912 140.577 75.7397 151.594C74.5149 189.968 96.6612 194.342 116.156 205.482C116.817 205.86 117.971 205.929 119.484 205.76C121.29 212.055 122.425 220.519 121.871 224.669C121.055 230.793 174.126 230.793 174.126 224.669C174.126 208.949 177.185 193.579 182.583 177.447C184.369 176.876 186.597 175.547 189.074 173.21C192.494 169.983 197.784 167.766 200.505 157.423C204.409 142.589 201.473 138.405 197.026 138.242Z" fill="#B2B2B2"/></g><defs><clipPath id="clip0_1451_458"><rect width="256" height="256" fill="white"/></clipPath></defs></svg> diff --git a/editor/icons/BoneMapHumanLeftHand.svg b/editor/icons/BoneMapHumanLeftHand.svg index 08c68bb4be..a9861f818c 100644 --- a/editor/icons/BoneMapHumanLeftHand.svg +++ b/editor/icons/BoneMapHumanLeftHand.svg @@ -1 +1 @@ -<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m703.906 786.098c7.046-66.929 28.135-153.363 18.529-260.192-1.143-12.71-4.5-48.282-4.46-82.732.025-21.174-2.111-48.505-1.975-64.174.167-19.333-.428-41.584-.625-55.755-1.052-75.44-13.225-85.827-30.813-85.827-17.246 0-26.77 14.266-27.062 84.582-.061 14.42.5 51 .5 58.5 0 17.508-.333 34.167 0 53.5.447 25.955-4.279 68-9 68-3.902 0-8.099-39.299-9.575-76.999-.756-19.326-3.219-58.336-2.6-70.102 1.759-33.413.474-58.914 1.537-90.165 3.183-93.607-13.016-111.729-34.695-111.729-21.973 0-35.979 57.688-34.849 114.224.128 6.394-1.165 50.739.188 89.859.754 21.811-1.07 49.627-1.683 69.67-1.095 35.768-5.755 63.896-8.869 63.896-2.641 0-4.135-32.584-5.456-65.706-.859-21.557-4.468-58.477-3.664-83.616 1.886-59.012-1.139-110.226-1.063-121.501.635-94.955-14.66-123.101-36.052-123.101-21.476 0-37.188 30.192-36.6 123.343.067 10.53-2.62 99.926-1.759 121.816.865 21.992-2.773 65.062-3.517 84.818-1.299 34.521-6.49 63.947-9.124 63.947-3.281 0-10.794-25.638-11.724-60.965-.587-22.275 1.231-50.99.624-70.688-1.257-40.707-3.175-64.631-3.877-99.708-1.945-97.182-16.352-106.289-38.142-106.289-17.957 0-32.453 28.673-32.657 115.03-.065 27.702-2.429 62.626-.315 94.329.805 12.081-.622 42.512-1.875 73.894-.799 20.007-1.102 47.501-1.137 63.775-.17 78.595-26.712 133.424-36.555 131.308-30.333-6.521-51.648-43.918-71.219-117.307-10.551-39.566-36.667-71.149-69.9-77.813-25.9-5.193-19.783 46.161-1.319 125.293 8.65 37.068 27.909 86.227 39.566 122.655 31.653 98.917 125.574 188.563 160.903 228.546 17.146 19.403 236.894 19.403 264.59 0 11.525-8.07 43.087-101.557 45.724-126.616z" fill="#b2b2b2"/></svg> +<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1451_461)"><path d="M0 0H256V256H0V0Z" fill="#3F3F3F"/><path d="M175.976 196.525C177.738 179.792 183.01 158.184 180.609 131.477C180.323 128.299 179.484 119.406 179.494 110.794C179.5 105.5 178.966 98.6674 179 94.7501C179.042 89.9169 178.893 84.3541 178.844 80.8114C178.581 61.9514 175.537 59.3546 171.14 59.3546C166.829 59.3546 164.448 62.9211 164.375 80.5001C164.36 84.1051 164.5 93.2501 164.5 95.1251C164.5 99.5021 164.417 103.667 164.5 108.5C164.612 114.989 163.43 125.5 162.25 125.5C161.274 125.5 160.225 115.675 159.856 106.25C159.667 101.419 159.051 91.6664 159.206 88.7249C159.646 80.3716 159.325 73.9964 159.59 66.1836C160.386 42.7819 156.336 38.2514 150.917 38.2514C145.423 38.2514 141.922 52.6734 142.204 66.8074C142.236 68.4059 141.913 79.4921 142.251 89.2721C142.44 94.7249 141.984 101.679 141.831 106.69C141.557 115.632 140.392 122.664 139.613 122.664C138.953 122.664 138.58 114.518 138.249 106.237C138.035 100.848 137.132 91.6179 137.333 85.3331C137.805 70.5801 137.049 57.7766 137.068 54.9579C137.226 31.2191 133.403 24.1826 128.055 24.1826C122.686 24.1826 118.758 31.7306 118.905 55.0184C118.921 57.6509 118.25 79.9999 118.465 85.4724C118.681 90.9704 117.772 101.738 117.586 106.677C117.261 115.307 115.963 122.664 115.305 122.664C114.484 122.664 112.606 116.254 112.374 107.422C112.227 101.854 112.681 94.6749 112.53 89.7504C112.215 79.5736 111.736 73.5926 111.56 64.8234C111.074 40.5279 107.472 38.2511 102.025 38.2511C97.5357 38.2511 93.9117 45.4194 93.8607 67.0086C93.8445 73.9341 93.2535 82.6651 93.782 90.5909C93.9832 93.6111 93.6265 101.219 93.3132 109.064C93.1135 114.066 93.0377 120.94 93.029 125.008C92.9865 144.657 86.351 158.364 83.8902 157.835C76.307 156.205 70.9782 146.856 66.0855 128.508C63.4477 118.617 56.9187 110.721 48.6105 109.055C42.1355 107.757 43.6647 120.595 48.2807 140.378C50.4432 149.645 55.258 161.935 58.1722 171.042C66.0855 195.771 89.5657 218.183 98.398 228.179C102.684 233.029 157.621 233.029 164.545 228.179C167.427 226.161 175.317 202.789 175.976 196.525Z" fill="#B2B2B2"/></g><defs><clipPath id="clip0_1451_461"><rect width="256" height="256" fill="white"/></clipPath></defs></svg> diff --git a/editor/icons/BoneMapHumanRightHand.svg b/editor/icons/BoneMapHumanRightHand.svg index 4e40af35d8..e92898152f 100644 --- a/editor/icons/BoneMapHumanRightHand.svg +++ b/editor/icons/BoneMapHumanRightHand.svg @@ -1 +1 @@ -<svg enable-background="new 0 0 1024 1024" height="1024" viewBox="0 0 1024 1024" width="1024" xmlns="http://www.w3.org/2000/svg"><path d="m0 0h1024v1024h-1024z" fill="#3f3f3f"/><path d="m320.094 786.098c-7.046-66.929-28.135-153.363-18.529-260.192 1.143-12.71 4.5-48.282 4.46-82.732-.025-21.174 2.111-48.505 1.975-64.174-.167-19.333.428-41.584.625-55.755 1.052-75.44 13.225-85.827 30.813-85.827 17.246 0 26.77 14.266 27.062 84.582.061 14.42-.5 51-.5 58.5 0 17.508.333 34.167 0 53.5-.447 25.955 4.279 68 9 68 3.902 0 8.099-39.299 9.575-76.999.756-19.326 3.219-58.336 2.6-70.102-1.759-33.413-.474-58.914-1.537-90.165-3.183-93.607 13.016-111.729 34.695-111.729 21.973 0 35.979 57.688 34.849 114.224-.128 6.394 1.165 50.739-.188 89.859-.754 21.811 1.07 49.627 1.683 69.67 1.095 35.768 5.755 63.896 8.869 63.896 2.641 0 4.135-32.584 5.456-65.706.859-21.557 4.468-58.477 3.664-83.616-1.886-59.012 1.139-110.226 1.063-121.501-.635-94.955 14.66-123.101 36.052-123.101 21.476 0 37.188 30.192 36.6 123.343-.067 10.53 2.62 99.926 1.759 121.816-.865 21.992 2.773 65.062 3.517 84.818 1.299 34.521 6.49 63.947 9.124 63.947 3.281 0 10.794-25.638 11.724-60.965.587-22.275-1.231-50.99-.624-70.688 1.257-40.707 3.176-64.631 3.877-99.708 1.945-97.182 16.352-106.289 38.142-106.289 17.957 0 32.453 28.673 32.657 115.03.065 27.702 2.429 62.626.314 94.329-.805 12.081.622 42.512 1.875 73.894.799 20.007 1.102 47.501 1.137 63.775.171 78.595 26.713 133.424 36.556 131.308 30.333-6.521 51.648-43.918 71.219-117.307 10.551-39.566 36.667-71.149 69.9-77.813 25.9-5.193 19.783 46.161 1.318 125.293-8.649 37.068-27.909 86.227-39.566 122.655-31.652 98.917-125.573 188.563-160.902 228.546-17.146 19.403-236.894 19.403-264.59 0-11.525-8.07-43.087-101.557-45.724-126.616z" fill="#b2b2b2"/></svg> +<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_1451_464)"><path d="M0 0H256V256H0V0Z" fill="#3F3F3F"/><path d="M80.0233 196.525C78.2618 179.792 72.9896 158.184 75.3911 131.477C75.6768 128.299 76.5161 119.406 76.5061 110.794C76.4998 105.5 77.0338 98.6674 76.9998 94.7501C76.9581 89.9169 77.1068 84.3541 77.1561 80.8114C77.4191 61.9514 80.4623 59.3546 84.8593 59.3546C89.1708 59.3546 91.5518 62.9211 91.6248 80.5001C91.6401 84.1051 91.4998 93.2501 91.4998 95.1251C91.4998 99.5021 91.5831 103.667 91.4998 108.5C91.3881 114.989 92.5696 125.5 93.7498 125.5C94.7253 125.5 95.7746 115.675 96.1436 106.25C96.3326 101.419 96.9483 91.6664 96.7935 88.7249C96.3538 80.3716 96.6751 73.9964 96.4093 66.1836C95.6136 42.7819 99.6633 38.2514 105.083 38.2514C110.576 38.2514 114.078 52.6734 113.795 66.8074C113.763 68.4059 114.087 79.4921 113.748 89.2721C113.56 94.7249 114.016 101.679 114.169 106.69C114.443 115.632 115.608 122.664 116.386 122.664C117.047 122.664 117.42 114.518 117.75 106.237C117.965 100.848 118.867 91.6179 118.666 85.3331C118.195 70.5801 118.951 57.7766 118.932 54.9579C118.773 31.2191 122.597 24.1826 127.945 24.1826C133.314 24.1826 137.242 31.7306 137.095 55.0184C137.078 57.6509 137.75 79.9999 137.535 85.4724C137.319 90.9704 138.228 101.738 138.414 106.677C138.739 115.307 140.037 122.664 140.695 122.664C141.515 122.664 143.394 116.254 143.626 107.422C143.773 101.854 143.318 94.6749 143.47 89.7504C143.784 79.5736 144.264 73.5926 144.439 64.8234C144.926 40.5279 148.527 38.2511 153.975 38.2511C158.464 38.2511 162.088 45.4194 162.139 67.0086C162.155 73.9341 162.746 82.6651 162.218 90.5909C162.016 93.6111 162.373 101.219 162.686 109.064C162.886 114.066 162.962 120.94 162.971 125.008C163.013 144.657 169.649 158.364 172.11 157.835C179.693 156.205 185.022 146.856 189.914 128.508C192.552 118.617 199.081 110.721 207.389 109.055C213.864 107.757 212.335 120.595 207.719 140.378C205.557 149.645 200.742 161.935 197.827 171.042C189.914 195.771 166.434 218.183 157.602 228.179C153.315 233.029 98.3783 233.029 91.4543 228.179C88.5731 226.161 80.6826 202.789 80.0233 196.525Z" fill="#B2B2B2"/></g><defs><clipPath id="clip0_1451_464"><rect width="256" height="256" fill="white"/></clipPath></defs></svg> diff --git a/editor/icons/Keyboard.svg b/editor/icons/Keyboard.svg index b9dfab71ed..b6d963f9d7 100644 --- a/editor/icons/Keyboard.svg +++ b/editor/icons/Keyboard.svg @@ -1 +1 @@ -<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".996"><path d="m4 2a1 1 0 0 0 -1 1v9.084c0 .506.448.916 1 .916h8c.552 0 1-.41 1-.916v-9.084a1 1 0 0 0 -1-1zm1.543 1.139h1.393l1.834 4.199h1.295v.437c.708.052 1.246.239 1.61.559.368.316.55.747.55 1.295 0 .552-.182.99-.55 1.314-.368.32-.906.505-1.61.553v.467h-1.294v-.473c-.708-.06-1.247-.248-1.615-.564-.364-.316-.545-.75-.545-1.297 0-.548.181-.977.545-1.29.368-.315.907-.504 1.615-.564v-.437h-1.464l-.282-.733h-1.595l-.284.733h-1.439l1.836-4.2zm.684 1.39-.409 1.057h.817zm3.84 4.338v1.526c.28-.04.483-.12.607-.24.124-.125.185-.302.185-.53 0-.224-.063-.396-.191-.516-.124-.12-.326-.2-.602-.24zm-1.296.006c-.284.04-.487.12-.61.24-.12.116-.182.288-.182.516 0 .22.065.392.193.512.132.12.331.202.6.246v-1.514z" fill="#e0e0e0"/><path d="m27 2h7v14h-7z" fill="#fff"/><path d="m1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-9h-1v9a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-9z" fill="#e0e0e0"/></g></svg> +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path d="M6.584 5.135 6.17 4.059l-.412 1.076h.826zm3.203 2.976a1.032 1.032 0 0 0-.287.041.953.953 0 0 0-.09.034c-.028.012-.057.024-.084.039a.912.912 0 0 0-.152.107.988.988 0 0 0-.23.305.937.937 0 0 0-.04.097 1.017 1.017 0 0 0-.068.323 1.553 1.553 0 0 0 0 .244 1.374 1.374 0 0 0 .068.328 1.03 1.03 0 0 0 .201.336c.023.022.045.044.069.064a.96.96 0 0 0 .152.104c.027.015.056.027.084.039.03.012.06.024.09.033a.965.965 0 0 0 .19.035 1.028 1.028 0 0 0 .197 0 .974.974 0 0 0 .36-.107.876.876 0 0 0 .077-.049.872.872 0 0 0 .074-.055.882.882 0 0 0 .13-.136.978.978 0 0 0 .177-.368 1.225 1.225 0 0 0 .035-.224 1.61 1.61 0 0 0 0-.244 1.361 1.361 0 0 0-.035-.223 1.092 1.092 0 0 0-.121-.285.87.87 0 0 0-.12-.15.862.862 0 0 0-.14-.124c-.025-.017-.05-.035-.078-.05-.027-.015-.056-.027-.086-.04a.892.892 0 0 0-.373-.074z" style="fill-opacity:.99607843;fill:#e0e0e0"/><path d="M4 2c-.616-.02-1.084.59-1 1.178.003 3-.007 6 .005 9 .057.577.672.889 1.203.822 2.631-.003 5.263.006 7.894-.005a.973.973 0 0 0 .898-1.09c-.003-3.002.007-6.005-.005-9.007-.042-.592-.643-.976-1.203-.898H4Zm1.475.646H6.89l1.865 4.27H7.268l-.286-.744H5.36l-.287.744H3.61l1.866-4.27Zm4.312 4.301c1.069-.042 2.164.679 2.363 1.766.232 1.01-.34 2.144-1.326 2.502.296.465.837-.109 1.06-.007l.544.642c-.64.756-1.883.677-2.605.084-.394-.448-.866-.673-1.409-.887-1.175-.66-1.391-2.456-.43-3.39.463-.486 1.141-.716 1.803-.71Z" style="fill:#e0e0e0;fill-opacity:.996078"/><path fill="#e0e0e0" d="M1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4Z" style="fill-opacity:.996"/></svg> diff --git a/editor/icons/KeyboardError.svg b/editor/icons/KeyboardError.svg new file mode 100644 index 0000000000..e20d133155 --- /dev/null +++ b/editor/icons/KeyboardError.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path d="M4 2c-.616-.02-1.084.59-1 1.178.003 3-.007 6 .005 9 .057.577.672.889 1.203.822 2.631-.003 5.263.006 7.894-.005a.973.973 0 0 0 .898-1.09c-.003-3.002.007-6.005-.005-9.007-.042-.592-.643-.976-1.203-.898H4Zm4.223 1.262c1.06.005 2.29.257 2.92 1.197.532.862.275 2.057-.484 2.703-.346.382-.862.629-1.075 1.117.055.345-.33.172-.537.213H7.148c-.037-.749.503-1.335 1.026-1.796.406-.253.744-1.002.129-1.22-.626-.25-1.374.117-1.645.715l-2.08-1.039c.599-1.147 1.868-1.818 3.136-1.872.17-.013.339-.018.509-.018Zm.127 5.697c.798-.057 1.616.616 1.54 1.45-.023.81-.841 1.413-1.623 1.328-.833.022-1.6-.771-1.443-1.613.097-.721.83-1.195 1.526-1.165z" style="fill:#ff5f5f;fill-opacity:1"/><path fill="#e0e0e0" d="M1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4Z" style="fill-opacity:1;fill:#ff5f5f"/></svg> diff --git a/editor/icons/KeyboardLabel.svg b/editor/icons/KeyboardLabel.svg new file mode 100644 index 0000000000..07a687f447 --- /dev/null +++ b/editor/icons/KeyboardLabel.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M11.814 1.977c-.078 0-.157.008-.232.023H4c-.7-.034-1.143.765-1 1.39.008 2.95-.017 5.9.014 8.848.13.705.965.847 1.562.76 2.542-.008 5.085.02 7.627-.014.734-.1.9-.952.797-1.564-.003-2.84.006-5.68-.006-8.522-.035-.594-.628-.927-1.18-.921zM9.797 4.016l.572.58-.572.578-.57-.578.57-.58zm-.606 1.05.594.606-.594.601-.591-.601.591-.606zm1.213 0 .596.606-.596.601-.593-.601.593-.606zm.717 1.436h1.06c.053.217.093.428.122.63.028.201.043.395.043.58a2.363 2.363 0 0 1-.133.724 1.425 1.425 0 0 1-.31.515c-.249.265-.598.399-1.05.399-.331 0-.594-.08-.785-.235a1.091 1.091 0 0 1-.236-.275c-.063.1-.14.19-.236.265-.206.163-.467.245-.787.245-.252 0-.457-.057-.614-.166a2.75 2.75 0 0 1-.095.42 1.936 1.936 0 0 1-.403.722c-.2.22-.452.383-.756.49-.303.11-.654.166-1.052.166-.466 0-.865-.089-1.2-.265a1.817 1.817 0 0 1-.765-.752c-.18-.327-.27-.715-.27-1.164 0-.256.027-.525.082-.809.055-.284.126-.545.21-.781h1.001c-.062.232-.112.46-.15.684a3.87 3.87 0 0 0-.053.613c0 .37.1.643.3.82.204.177.523.264.96.264.222 0 .425-.03.61-.092a.97.97 0 0 0 .439-.299.803.803 0 0 0 .166-.521 5.463 5.463 0 0 0-.051-.725 11.61 11.61 0 0 0-.068-.482 26.51 26.51 0 0 0-.096-.606h1.135l.043.276c.047.32.123.532.228.634.105.1.24.15.402.15.165 0 .284-.04.358-.124.076-.086.115-.224.115-.412v-.524h1.027v.524c0 .203.032.351.096.447.065.095.202.142.412.142a.637.637 0 0 0 .33-.078c.089-.052.133-.15.133-.297 0-.128-.019-.295-.054-.498l-.108-.605z" style="fill:#e0e0e0;fill-opacity:.996078"/><path fill="#e0e0e0" d="M1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4Z" style="fill-opacity:.996"/></svg> diff --git a/editor/icons/KeyboardPhysical.svg b/editor/icons/KeyboardPhysical.svg index 4364e0b4fa..7d4b7e2999 100644 --- a/editor/icons/KeyboardPhysical.svg +++ b/editor/icons/KeyboardPhysical.svg @@ -1 +1 @@ -<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".996"><path d="m4 2a1 1 0 0 0 -1 1v9.084c0 .506.448.916 1 .916h8c.552 0 1-.41 1-.916v-9.084a1 1 0 0 0 -1-1zm2.762 1.768h2.476l3.264 7.464h-2.604l-.502-1.3h-2.835l-.502 1.3h-2.561zm1.217 2.474-.725 1.878h1.45z" fill="#e0e0e0"/><path d="m27 2h7v14h-7z" fill="#fff"/><path d="m1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-9h-1v9a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-9z" fill="#e0e0e0"/></g></svg> +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M4 2a1 1 0 0 0-1 1v9.084c0 .506.448.916 1 .916h8c.552 0 1-.41 1-.916V3a1 1 0 0 0-1-1Zm2.762 1.768h2.476l3.264 7.464H9.898l-.502-1.3H6.561l-.502 1.3H3.498Zm1.217 2.474L7.254 8.12h1.45z" style="fill-opacity:.996"/><path fill="#e0e0e0" d="M1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V4h-1v9a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4Z" style="fill-opacity:.996"/></svg> diff --git a/editor/import/editor_import_collada.cpp b/editor/import/editor_import_collada.cpp index 4b0ece3367..1ffede6502 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/editor_import_collada.cpp @@ -734,7 +734,7 @@ Error ColladaImport::_create_mesh_surfaces(bool p_optimize, Ref<ImporterMesh> &p /* CREATE PRIMITIVE ARRAY */ /**************************/ - // The way collada uses indices is more optimal, and friendlier with 3D modelling software, + // The way collada uses indices is more optimal, and friendlier with 3D modeling software, // because it can index everything, not only vertices (similar to how the WII works). // This is, however, more incompatible with standard video cards, so arrays must be converted. // Must convert to GL/DX format. diff --git a/editor/input_event_configuration_dialog.cpp b/editor/input_event_configuration_dialog.cpp index 08d4bfff4a..48e3759e57 100644 --- a/editor/input_event_configuration_dialog.cpp +++ b/editor/input_event_configuration_dialog.cpp @@ -38,9 +38,10 @@ #include "scene/gui/separator.h" #include "scene/gui/tree.h" -void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, bool p_update_input_list_selection) { +void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, const Ref<InputEvent> &p_original_event, bool p_update_input_list_selection) { if (p_event.is_valid()) { event = p_event; + original_event = p_original_event; // If the event is changed to something which is not the same as the listener, // clear out the event from the listener text box to avoid confusion. @@ -61,7 +62,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b // Update option values and visibility bool show_mods = false; bool show_device = false; - bool show_phys_key = false; + bool show_key = false; if (mod.is_valid()) { show_mods = true; @@ -74,9 +75,25 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b } if (k.is_valid()) { - show_phys_key = true; - physical_key_checkbox->set_pressed(k->get_physical_keycode() != Key::NONE && k->get_keycode() == Key::NONE); - + show_key = true; + if (k->get_keycode() == Key::NONE && k->get_physical_keycode() == Key::NONE && k->get_key_label() != Key::NONE) { + key_mode->select(KEYMODE_UNICODE); + } else if (k->get_keycode() != Key::NONE) { + key_mode->select(KEYMODE_KEYCODE); + } else if (k->get_physical_keycode() != Key::NONE) { + key_mode->select(KEYMODE_PHY_KEYCODE); + } else { + // Invalid key. + event = Ref<InputEvent>(); + original_event = Ref<InputEvent>(); + event_listener->clear_event(); + event_as_text->set_text(TTR("No Event Configured")); + + additional_options_container->hide(); + input_list_tree->deselect_all(); + _update_input_list(); + return; + } } else if (joyb.is_valid() || joym.is_valid() || mb.is_valid()) { show_device = true; _set_current_device(event->get_device()); @@ -84,11 +101,20 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b mod_container->set_visible(show_mods); device_container->set_visible(show_device); - physical_key_checkbox->set_visible(show_phys_key); + key_mode->set_visible(show_key); additional_options_container->show(); + // Update mode selector based on original key event. + Ref<InputEventKey> ko = p_original_event; + if (ko.is_valid()) { + key_mode->set_item_disabled(KEYMODE_KEYCODE, ko->get_keycode() == Key::NONE); + key_mode->set_item_disabled(KEYMODE_PHY_KEYCODE, ko->get_physical_keycode() == Key::NONE); + key_mode->set_item_disabled(KEYMODE_UNICODE, ko->get_key_label() == Key::NONE); + } + // Update selected item in input list. if (p_update_input_list_selection && (k.is_valid() || joyb.is_valid() || joym.is_valid() || mb.is_valid())) { + in_tree_update = true; TreeItem *category = input_list_tree->get_root()->get_first_child(); while (category) { TreeItem *input_item = category->get_first_child(); @@ -97,6 +123,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b // input_type should always be > 0, unless the tree structure has been misconfigured. int input_type = input_item->get_parent()->get_meta("__type", 0); if (input_type == 0) { + in_tree_update = false; return; } @@ -112,6 +139,7 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b category->set_collapsed(false); input_item->select(0); input_list_tree->ensure_cursor_is_visible(); + in_tree_update = false; return; } input_item = input_item->get_next(); @@ -122,10 +150,12 @@ void InputEventConfigurationDialog::_set_event(const Ref<InputEvent> &p_event, b category->set_collapsed(true); // Event not in this category, so collapse; category = category->get_next(); } + in_tree_update = false; } } else { // Event is not valid, reset dialog - event = p_event; + event = Ref<InputEvent>(); + original_event = Ref<InputEvent>(); event_listener->clear_event(); event_as_text->set_text(TTR("No Event Configured")); @@ -141,8 +171,10 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven return; } - // Create an editable reference + // Create an editable reference and a copy of full event. Ref<InputEvent> received_event = p_event; + Ref<InputEvent> received_original_event = received_event->duplicate(); + // Check what the type is and if it is allowed. Ref<InputEventKey> k = received_event; Ref<InputEventJoypadButton> joyb = received_event; @@ -170,11 +202,15 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven if (k.is_valid()) { k->set_pressed(false); // To avoid serialisation of 'pressed' property - doesn't matter for actions anyway. - // Maintain physical keycode option state - if (physical_key_checkbox->is_pressed()) { + if (key_mode->get_selected_id() == KEYMODE_KEYCODE) { + k->set_physical_keycode(Key::NONE); + k->set_key_label(Key::NONE); + } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) { k->set_keycode(Key::NONE); - } else { + k->set_key_label(Key::NONE); + } else if (key_mode->get_selected_id() == KEYMODE_UNICODE) { k->set_physical_keycode(Key::NONE); + k->set_keycode(Key::NONE); } } @@ -186,7 +222,7 @@ void InputEventConfigurationDialog::_on_listen_input_changed(const Ref<InputEven // Maintain device selection. received_event->set_device(_get_current_device()); - _set_event(received_event); + _set_event(received_event, received_original_event); } void InputEventConfigurationDialog::_on_listen_focus_changed() { @@ -326,14 +362,14 @@ void InputEventConfigurationDialog::_mod_toggled(bool p_checked, int p_index) { } } - _set_event(ie); + _set_event(ie, original_event); } void InputEventConfigurationDialog::_autoremap_command_or_control_toggled(bool p_checked) { Ref<InputEventWithModifiers> ie = event; if (ie.is_valid()) { ie->set_command_or_control_autoremap(p_checked); - _set_event(ie); + _set_event(ie, original_event); } if (p_checked) { @@ -345,27 +381,38 @@ void InputEventConfigurationDialog::_autoremap_command_or_control_toggled(bool p } } -void InputEventConfigurationDialog::_physical_keycode_toggled(bool p_checked) { +void InputEventConfigurationDialog::_key_mode_selected(int p_mode) { Ref<InputEventKey> k = event; - - if (k.is_null()) { + Ref<InputEventKey> ko = original_event; + if (k.is_null() || ko.is_null()) { return; } - if (p_checked) { - k->set_physical_keycode(k->get_keycode()); + if (key_mode->get_selected_id() == KEYMODE_KEYCODE) { + k->set_keycode(ko->get_keycode()); + k->set_physical_keycode(Key::NONE); + k->set_key_label(Key::NONE); + } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) { k->set_keycode(Key::NONE); - } else { - k->set_keycode((Key)k->get_physical_keycode()); + k->set_physical_keycode(ko->get_physical_keycode()); + k->set_key_label(Key::NONE); + } else if (key_mode->get_selected_id() == KEYMODE_UNICODE) { k->set_physical_keycode(Key::NONE); + k->set_keycode(Key::NONE); + k->set_key_label(ko->get_key_label()); } - _set_event(k); + _set_event(k, original_event); } void InputEventConfigurationDialog::_input_list_item_selected() { TreeItem *selected = input_list_tree->get_selected(); + // Called form _set_event, do not update for a second time. + if (in_tree_update) { + return; + } + // Invalid tree selection - type only exists on the "category" items, which are not a valid selection. if (selected->has_meta("__type")) { return; @@ -379,15 +426,11 @@ void InputEventConfigurationDialog::_input_list_item_selected() { Ref<InputEventKey> k; k.instantiate(); - if (physical_key_checkbox->is_pressed()) { - k->set_physical_keycode(keycode); - k->set_keycode(Key::NONE); - } else { - k->set_physical_keycode(Key::NONE); - k->set_keycode(keycode); - } + k->set_physical_keycode(keycode); + k->set_keycode(keycode); + k->set_key_label(keycode); - // Maintain modifier state from checkboxes + // Maintain modifier state from checkboxes. k->set_alt_pressed(mod_checkboxes[MOD_ALT]->is_pressed()); k->set_shift_pressed(mod_checkboxes[MOD_SHIFT]->is_pressed()); if (autoremap_command_or_control_checkbox->is_pressed()) { @@ -397,7 +440,23 @@ void InputEventConfigurationDialog::_input_list_item_selected() { k->set_meta_pressed(mod_checkboxes[MOD_META]->is_pressed()); } - _set_event(k, false); + Ref<InputEventKey> ko = k->duplicate(); + + if (key_mode->get_selected_id() == KEYMODE_UNICODE) { + key_mode->select(KEYMODE_PHY_KEYCODE); + } + + if (key_mode->get_selected_id() == KEYMODE_KEYCODE) { + k->set_physical_keycode(Key::NONE); + k->set_keycode(keycode); + k->set_key_label(Key::NONE); + } else if (key_mode->get_selected_id() == KEYMODE_PHY_KEYCODE) { + k->set_physical_keycode(keycode); + k->set_keycode(Key::NONE); + k->set_key_label(Key::NONE); + } + + _set_event(k, ko, false); } break; case INPUT_MOUSE_BUTTON: { MouseButton idx = (MouseButton)(int)selected->get_meta("__index"); @@ -417,7 +476,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() { // Maintain selected device mb->set_device(_get_current_device()); - _set_event(mb, false); + _set_event(mb, mb, false); } break; case INPUT_JOY_BUTTON: { JoyButton idx = (JoyButton)(int)selected->get_meta("__index"); @@ -426,7 +485,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() { // Maintain selected device jb->set_device(_get_current_device()); - _set_event(jb, false); + _set_event(jb, jb, false); } break; case INPUT_JOY_MOTION: { JoyAxis axis = (JoyAxis)(int)selected->get_meta("__axis"); @@ -440,7 +499,7 @@ void InputEventConfigurationDialog::_input_list_item_selected() { // Maintain selected device jm->set_device(_get_current_device()); - _set_event(jm, false); + _set_event(jm, jm, false); } break; } } @@ -470,7 +529,9 @@ void InputEventConfigurationDialog::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { input_list_search->set_right_icon(input_list_search->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); - physical_key_checkbox->set_icon(get_theme_icon(SNAME("KeyboardPhysical"), SNAME("EditorIcons"))); + key_mode->set_item_icon(KEYMODE_KEYCODE, get_theme_icon(SNAME("Keyboard"), SNAME("EditorIcons"))); + key_mode->set_item_icon(KEYMODE_PHY_KEYCODE, get_theme_icon(SNAME("KeyboardPhysical"), SNAME("EditorIcons"))); + key_mode->set_item_icon(KEYMODE_UNICODE, get_theme_icon(SNAME("KeyboardLabel"), SNAME("EditorIcons"))); icon_cache.keyboard = get_theme_icon(SNAME("Keyboard"), SNAME("EditorIcons")); icon_cache.mouse = get_theme_icon(SNAME("Mouse"), SNAME("EditorIcons")); @@ -484,22 +545,22 @@ void InputEventConfigurationDialog::_notification(int p_what) { void InputEventConfigurationDialog::popup_and_configure(const Ref<InputEvent> &p_event) { if (p_event.is_valid()) { - _set_event(p_event); + _set_event(p_event, p_event->duplicate()); } else { // Clear Event - _set_event(p_event); + _set_event(Ref<InputEvent>(), Ref<InputEvent>()); // Clear Checkbox Values for (int i = 0; i < MOD_MAX; i++) { mod_checkboxes[i]->set_pressed(false); } - // Enable the Physical Key checkbox by default to encourage its use. + // Enable the Physical Key by default to encourage its use. // Physical Key should be used for most game inputs as it allows keys to work // on non-QWERTY layouts out of the box. // This is especially important for WASD movement layouts. - physical_key_checkbox->set_pressed(true); + key_mode->select(KEYMODE_PHY_KEYCODE); autoremap_command_or_control_checkbox->set_pressed(false); // Select "All Devices" by default. @@ -621,14 +682,15 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { mod_container->hide(); additional_options_container->add_child(mod_container); - // Physical Key Checkbox + // Key Mode Selection - physical_key_checkbox = memnew(CheckBox); - physical_key_checkbox->set_text(TTR("Use Physical Keycode")); - physical_key_checkbox->set_tooltip_text(TTR("Stores the physical position of the key on the keyboard rather than the key's value. Used for compatibility with non-latin layouts.\nThis should generally be enabled for most game shortcuts, but not in non-game applications.")); - physical_key_checkbox->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_physical_keycode_toggled)); - physical_key_checkbox->hide(); - additional_options_container->add_child(physical_key_checkbox); + key_mode = memnew(OptionButton); + key_mode->add_item("Keycode (Latin equvialent)", KEYMODE_KEYCODE); + key_mode->add_item("Physical Keycode (poistion of US QWERTY keyboard)", KEYMODE_PHY_KEYCODE); + key_mode->add_item("Unicode (case-insencetive)", KEYMODE_UNICODE); + key_mode->connect("item_selected", callable_mp(this, &InputEventConfigurationDialog::_key_mode_selected)); + key_mode->hide(); + additional_options_container->add_child(key_mode); main_vbox->add_child(additional_options_container); } diff --git a/editor/input_event_configuration_dialog.h b/editor/input_event_configuration_dialog.h index 67906233dd..e7ab0da4d6 100644 --- a/editor/input_event_configuration_dialog.h +++ b/editor/input_event_configuration_dialog.h @@ -50,7 +50,10 @@ private: Ref<Texture2D> joypad_axis; } icon_cache; - Ref<InputEvent> event = Ref<InputEvent>(); + Ref<InputEvent> event; + Ref<InputEvent> original_event; + + bool in_tree_update = false; // Listening for input EventListenerLineEdit *event_listener = nullptr; @@ -88,9 +91,15 @@ private: CheckBox *mod_checkboxes[MOD_MAX]; CheckBox *autoremap_command_or_control_checkbox = nullptr; - CheckBox *physical_key_checkbox = nullptr; + enum KeyMode { + KEYMODE_KEYCODE, + KEYMODE_PHY_KEYCODE, + KEYMODE_UNICODE, + }; + + OptionButton *key_mode = nullptr; - void _set_event(const Ref<InputEvent> &p_event, bool p_update_input_list_selection = true); + void _set_event(const Ref<InputEvent> &p_event, const Ref<InputEvent> &p_original_event, bool p_update_input_list_selection = true); void _on_listen_input_changed(const Ref<InputEvent> &p_event); void _on_listen_focus_changed(); @@ -100,7 +109,7 @@ private: void _mod_toggled(bool p_checked, int p_index); void _autoremap_command_or_control_toggled(bool p_checked); - void _physical_keycode_toggled(bool p_checked); + void _key_mode_selected(int p_mode); void _device_selection_changed(int p_option_button_index); void _set_current_device(int p_device); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index b28373e308..52482ecb16 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -189,8 +189,7 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) { int history_id = EditorUndoRedoManager::get_singleton()->get_history_for_object(current).id; EditorUndoRedoManager::get_singleton()->clear_history(true, history_id); - EditorNode::get_singleton()->get_editor_plugins_over()->edit(nullptr); - EditorNode::get_singleton()->get_editor_plugins_over()->edit(current); + EditorNode::get_singleton()->edit_item(current, inspector); } } break; diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 4f6487fa77..97398b8b69 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -92,7 +92,7 @@ void PluginConfigDialog::_on_confirmed() { _clear_fields(); } -void PluginConfigDialog::_on_cancelled() { +void PluginConfigDialog::_on_canceled() { _clear_fields(); } @@ -162,7 +162,7 @@ void PluginConfigDialog::_notification(int p_what) { case NOTIFICATION_READY: { connect("confirmed", callable_mp(this, &PluginConfigDialog::_on_confirmed)); - get_cancel_button()->connect("pressed", callable_mp(this, &PluginConfigDialog::_on_cancelled)); + get_cancel_button()->connect("pressed", callable_mp(this, &PluginConfigDialog::_on_canceled)); } break; } } diff --git a/editor/plugin_config_dialog.h b/editor/plugin_config_dialog.h index e8df308c54..50b901a39e 100644 --- a/editor/plugin_config_dialog.h +++ b/editor/plugin_config_dialog.h @@ -58,7 +58,7 @@ class PluginConfigDialog : public ConfirmationDialog { void _clear_fields(); void _on_confirmed(); - void _on_cancelled(); + void _on_canceled(); void _on_language_changed(const int p_language); void _on_required_text_changed(const String &p_text); String _get_subfolder(); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index a675495429..9632670658 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -1127,7 +1127,7 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action connecting = false; } -void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance, bool p_multi_transitions) { +void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_multi_transitions) { Color linecolor = get_theme_color(SNAME("font_color"), SNAME("Label")); Color icon_color(1, 1, 1); Color accent = get_theme_color(SNAME("accent_color"), SNAME("Editor")); @@ -1138,7 +1138,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co accent.a *= 0.6; } - const Ref<Texture2D> icons[6] = { + const Ref<Texture2D> icons[] = { get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionSyncBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionEndBig"), SNAME("EditorIcons")), @@ -1146,6 +1146,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co get_theme_icon(SNAME("TransitionSyncAutoBig"), SNAME("EditorIcons")), get_theme_icon(SNAME("TransitionEndAutoBig"), SNAME("EditorIcons")) }; + const int ICON_COUNT = sizeof(icons) / sizeof(*icons); if (p_selected) { state_machine_draw->draw_line(p_from, p_to, accent, 6); @@ -1153,12 +1154,18 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co if (p_travel) { linecolor = accent; - linecolor.set_hsv(1.0, linecolor.get_s(), linecolor.get_v()); } state_machine_draw->draw_line(p_from, p_to, linecolor, 2); - Ref<Texture2D> icon = icons[p_mode + (p_auto_advance ? 3 : 0)]; + if (p_fade_ratio > 0.0) { + Color fade_linecolor = accent; + fade_linecolor.set_hsv(1.0, fade_linecolor.get_s(), fade_linecolor.get_v()); + state_machine_draw->draw_line(p_from, p_from.lerp(p_to, p_fade_ratio), fade_linecolor, 2); + } + int icon_index = p_mode + (p_auto_advance ? ICON_COUNT / 2 : 0); + ERR_FAIL_COND(icon_index >= ICON_COUNT); + Ref<Texture2D> icon = icons[icon_index]; Transform2D xf; xf.columns[0] = (p_to - p_from).normalized(); @@ -1237,7 +1244,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { if (playback.is_valid()) { playing = playback->is_playing(); current = playback->get_current_node(); - blend_from = playback->get_blend_from_node(); + blend_from = playback->get_fading_from_node(); travel_path = playback->get_travel_path(); } @@ -1327,7 +1334,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } - _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, false, false); + _connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, 0.0, false, false); } Ref<Texture2D> tr_reference_icon = get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons")); @@ -1357,6 +1364,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { tl.mode = tr->get_switch_mode(); tl.width = tr_bidi_offset; tl.travel = false; + tl.fade_ratio = 0.0; tl.hidden = false; if (state_machine->has_local_transition(local_to, local_from)) { //offset if same exists @@ -1378,6 +1386,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { if (blend_from == local_from && current == local_to) { tl.travel = true; + tl.fade_ratio = MIN(1.0, fading_pos / fading_time); } if (travel_path.size()) { @@ -1418,7 +1427,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { for (int i = 0; i < transition_lines.size(); i++) { TransitionLine tl = transition_lines[i]; if (!tl.hidden) { - _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.auto_advance, !tl.multi_transitions.is_empty()); + _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, !tl.multi_transitions.is_empty()); } } @@ -1508,25 +1517,24 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { state_machine_play_pos->queue_redraw(); } -void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { +void AnimationNodeStateMachineEditor::_state_machine_pos_draw_individual(String p_name, float p_ratio) { AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); if (!tree) { return; } Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); - if (!playback.is_valid() || !playback->is_playing()) { return; } - if (playback->get_current_node() == state_machine->start_node || playback->get_current_node() == state_machine->end_node) { + if (p_name == state_machine->start_node || p_name == state_machine->end_node || p_name.is_empty()) { return; } int idx = -1; for (int i = 0; i < node_rects.size(); i++) { - if (node_rects[i].node_name == playback->get_current_node()) { + if (node_rects[i].node_name == p_name) { idx = i; break; } @@ -1550,10 +1558,7 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, current_length); - - float pos = CLAMP(play_pos, 0, len); - float c = pos / len; + float c = p_ratio; Color fg = get_theme_color(SNAME("font_color"), SNAME("Label")); Color bg = fg; bg.a *= 0.3; @@ -1565,6 +1570,32 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { state_machine_play_pos->draw_line(from, to, fg, 2); } +void AnimationNodeStateMachineEditor::_state_machine_pos_draw_all() { + AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree(); + if (!tree) { + return; + } + + Ref<AnimationNodeStateMachinePlayback> playback = tree->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (!playback.is_valid() || !playback->is_playing()) { + return; + } + + { + float len = MAX(0.0001, current_length); + float pos = CLAMP(current_play_pos, 0, len); + float c = pos / len; + _state_machine_pos_draw_individual(playback->get_current_node(), c); + } + + { + float len = MAX(0.0001, fade_from_length); + float pos = CLAMP(fade_from_current_play_pos, 0, len); + float c = pos / len; + _state_machine_pos_draw_individual(playback->get_fading_from_node(), c); + } +} + void AnimationNodeStateMachineEditor::_update_graph() { if (updating) { return; @@ -1687,17 +1718,28 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { Vector<StringName> tp; bool is_playing = false; StringName current_node; - StringName blend_from_node; - play_pos = 0; + StringName fading_from_node; + + current_play_pos = 0; current_length = 0; + fade_from_current_play_pos = 0; + fade_from_length = 0; + + fading_time = 0; + fading_pos = 0; + if (playback.is_valid()) { tp = playback->get_travel_path(); is_playing = playback->is_playing(); current_node = playback->get_current_node(); - blend_from_node = playback->get_blend_from_node(); - play_pos = playback->get_current_play_pos(); + fading_from_node = playback->get_fading_from_node(); + current_play_pos = playback->get_current_play_pos(); current_length = playback->get_current_length(); + fade_from_current_play_pos = playback->get_fade_from_play_pos(); + fade_from_length = playback->get_fade_from_length(); + fading_time = playback->get_fading_time(); + fading_pos = playback->get_fading_pos(); } { @@ -1714,12 +1756,19 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { } //redraw if travel state changed - if (!same_travel_path || last_active != is_playing || last_current_node != current_node || last_blend_from_node != blend_from_node) { + if (!same_travel_path || + last_active != is_playing || + last_current_node != current_node || + last_fading_from_node != fading_from_node || + last_fading_time != fading_time || + last_fading_pos != fading_pos) { state_machine_draw->queue_redraw(); last_travel_path = tp; last_current_node = current_node; last_active = is_playing; - last_blend_from_node = blend_from_node; + last_fading_from_node = fading_from_node; + last_fading_time = fading_time; + last_fading_pos = fading_pos; state_machine_play_pos->queue_redraw(); } @@ -1737,14 +1786,15 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { // when current_node is a state machine, use playback of current_node to set play_pos if (current_node_playback.is_valid()) { - play_pos = current_node_playback->get_current_play_pos(); + current_play_pos = current_node_playback->get_current_play_pos(); current_length = current_node_playback->get_current_length(); } } } - if (last_play_pos != play_pos) { - last_play_pos = play_pos; + if (last_play_pos != current_play_pos || fade_from_last_play_pos != fade_from_current_play_pos) { + last_play_pos = current_play_pos; + fade_from_last_play_pos = fade_from_current_play_pos; state_machine_play_pos->queue_redraw(); } } break; @@ -2048,7 +2098,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { state_machine_draw->add_child(state_machine_play_pos); state_machine_play_pos->set_mouse_filter(MOUSE_FILTER_PASS); //pass all to parent state_machine_play_pos->set_anchors_and_offsets_preset(PRESET_FULL_RECT); - state_machine_play_pos->connect("draw", callable_mp(this, &AnimationNodeStateMachineEditor::_state_machine_pos_draw)); + state_machine_play_pos->connect("draw", callable_mp(this, &AnimationNodeStateMachineEditor::_state_machine_pos_draw_all)); v_scroll = memnew(VScrollBar); state_machine_draw->add_child(v_scroll); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 46fe13ccc1..66338c820e 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -85,9 +85,12 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { static AnimationNodeStateMachineEditor *singleton; void _state_machine_gui_input(const Ref<InputEvent> &p_event); - void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, bool p_auto_advance, bool p_multi_transitions); + void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_multi_transitions); + void _state_machine_draw(); - void _state_machine_pos_draw(); + + void _state_machine_pos_draw_individual(String p_name, float p_ratio); + void _state_machine_pos_draw_all(); void _update_graph(); @@ -150,6 +153,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { float width = 0; bool selected; bool travel; + float fade_ratio; bool hidden; int transition_index; Vector<TransitionLine> multi_transitions; @@ -204,13 +208,23 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { void _delete_tree_draw(); bool last_active = false; - StringName last_blend_from_node; + StringName last_fading_from_node; StringName last_current_node; Vector<StringName> last_travel_path; + + float fade_from_last_play_pos = 0.0f; + float fade_from_current_play_pos = 0.0f; + float fade_from_length = 0.0f; + float last_play_pos = 0.0f; - float play_pos = 0.0f; + float current_play_pos = 0.0f; float current_length = 0.0f; + float last_fading_time = 0.0f; + float last_fading_pos = 0.0f; + float fading_time = 0.0f; + float fading_pos = 0.0f; + float error_time = 0.0f; String error_text; diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 720deb0b92..ab46e8f04a 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -65,13 +65,14 @@ void AnimationTreeEditor::edit(AnimationTree *p_tree) { tree = p_tree; Vector<String> path; - if (tree && tree->has_meta("_tree_edit_path")) { - path = tree->get_meta("_tree_edit_path"); - } else { - current_root = ObjectID(); + if (tree) { + if (tree->has_meta("_tree_edit_path")) { + path = tree->get_meta("_tree_edit_path"); + } else { + current_root = ObjectID(); + } + edit_path(path); } - - edit_path(path); } void AnimationTreeEditor::_node_removed(Node *p_node) { diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index eab5eb0404..2639765283 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -107,7 +107,7 @@ void EditorAssetLibraryItem::_bind_methods() { EditorAssetLibraryItem::EditorAssetLibraryItem() { Ref<StyleBoxEmpty> border; border.instantiate(); - border->set_default_margin_all(5 * EDSCALE); + border->set_content_margin_all(5 * EDSCALE); add_theme_style_override("panel", border); HBoxContainer *hb = memnew(HBoxContainer); @@ -1549,7 +1549,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { Ref<StyleBoxEmpty> border2; border2.instantiate(); - border2->set_default_margin_individual(15 * EDSCALE, 15 * EDSCALE, 35 * EDSCALE, 15 * EDSCALE); + border2->set_content_margin_individual(15 * EDSCALE, 15 * EDSCALE, 35 * EDSCALE, 15 * EDSCALE); PanelContainer *library_vb_border = memnew(PanelContainer); library_scroll->add_child(library_vb_border); diff --git a/editor/plugins/bit_map_editor_plugin.cpp b/editor/plugins/bit_map_editor_plugin.cpp index 4f82aaf071..30fc60b0e0 100644 --- a/editor/plugins/bit_map_editor_plugin.cpp +++ b/editor/plugins/bit_map_editor_plugin.cpp @@ -53,7 +53,7 @@ BitMapEditor::BitMapEditor() { // Reduce extra padding on top and bottom of size label. Ref<StyleBoxEmpty> stylebox; stylebox.instantiate(); - stylebox->set_default_margin(SIDE_RIGHT, 4 * EDSCALE); + stylebox->set_content_margin(SIDE_RIGHT, 4 * EDSCALE); size_label->add_theme_style_override("normal", stylebox); } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 4c14755b03..e09636d297 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -246,7 +246,7 @@ public: } }; -bool CanvasItemEditor::_is_node_locked(const Node *p_node) { +bool CanvasItemEditor::_is_node_locked(const Node *p_node) const { return p_node->get_meta("_edit_lock_", false); } @@ -770,7 +770,7 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po return still_selected; } -List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_locked, bool remove_canvas_item_if_parent_in_selection) { +List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retrieve_locked, bool remove_canvas_item_if_parent_in_selection) const { List<CanvasItem *> selection; for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) { CanvasItem *ci = Object::cast_to<CanvasItem>(E.key); @@ -1226,7 +1226,6 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo bool panner_active = panner->gui_input(p_event, warped_panning ? viewport->get_global_rect() : Rect2()); if (panner->is_panning() != pan_pressed) { pan_pressed = panner->is_panning(); - _update_cursor(); } if (panner_active) { @@ -1260,57 +1259,25 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo } } - Ref<InputEventMagnifyGesture> magnify_gesture = p_event; - if (magnify_gesture.is_valid() && !p_already_accepted) { - // Zoom gesture - _zoom_on_position(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); - return true; - } - - Ref<InputEventPanGesture> pan_gesture = p_event; - if (pan_gesture.is_valid() && !p_already_accepted) { - // If ctrl key pressed, then zoom instead of pan. - if (pan_gesture->is_ctrl_pressed()) { - const real_t factor = pan_gesture->get_delta().y; - - zoom_widget->set_zoom_by_increments(1); - if (factor != 1.f) { - zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * factor + 1.f)); - } - _zoom_on_position(zoom_widget->get_zoom(), pan_gesture->get_position()); - - return true; - } - - // Pan gesture - const Vector2 delta = (pan_speed / zoom) * pan_gesture->get_delta(); - view_offset.x += delta.x; - view_offset.y += delta.y; - update_viewport(); - return true; - } - return false; } -void CanvasItemEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * pan_speed); -} - -void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec) { +void CanvasItemEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { view_offset.x -= p_scroll_vec.x / zoom; view_offset.y -= p_scroll_vec.y / zoom; update_viewport(); } -void CanvasItemEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - int scroll_sign = (int)SIGN(p_scroll_vec.y); - // Inverted so that scrolling up (-1) zooms in, scrolling down (+1) zooms out. - zoom_widget->set_zoom_by_increments(-scroll_sign, p_alt); - if (!Math::is_equal_approx(ABS(p_scroll_vec.y), (real_t)1.0)) { - // Handle high-precision (analog) scrolling. - zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * ABS(p_scroll_vec.y) + 1.f)); +void CanvasItemEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid()) { + // Special behvior for scroll events, as the zoom_by_increment method can smartly end up on powers of two. + int increment = p_zoom_factor > 1.0 ? 1 : -1; + zoom_widget->set_zoom_by_increments(increment, mb->is_alt_pressed()); + } else { + zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor); } + _zoom_on_position(zoom_widget->get_zoom(), p_origin); } @@ -2570,8 +2537,10 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { // Handles the mouse hovering _gui_input_hover(p_event); - // Change the cursor - _update_cursor(); + if (mb.is_valid()) { + // Update the default cursor. + _update_cursor(); + } // Grab focus if (!viewport->has_focus() && (!get_viewport()->gui_get_focus_owner() || !get_viewport()->gui_get_focus_owner()->is_text_field())) { @@ -2580,6 +2549,31 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { } void CanvasItemEditor::_update_cursor() { + // Choose the correct default cursor. + CursorShape c = CURSOR_ARROW; + switch (tool) { + case TOOL_MOVE: + c = CURSOR_MOVE; + break; + case TOOL_EDIT_PIVOT: + c = CURSOR_CROSS; + break; + case TOOL_PAN: + c = CURSOR_DRAG; + break; + case TOOL_RULER: + c = CURSOR_CROSS; + break; + default: + break; + } + if (pan_pressed) { + c = CURSOR_DRAG; + } + set_default_cursor_shape(c); +} + +Control::CursorShape CanvasItemEditor::get_cursor_shape(const Point2 &p_pos) const { // Compute an eventual rotation of the cursor const CursorShape rotation_array[4] = { CURSOR_HSIZE, CURSOR_BDIAGSIZE, CURSOR_VSIZE, CURSOR_FDIAGSIZE }; int rotation_array_index = 0; @@ -2601,26 +2595,8 @@ void CanvasItemEditor::_update_cursor() { } // Choose the correct cursor - CursorShape c = CURSOR_ARROW; + CursorShape c = get_default_cursor_shape(); switch (drag_type) { - case DRAG_NONE: - switch (tool) { - case TOOL_MOVE: - c = CURSOR_MOVE; - break; - case TOOL_EDIT_PIVOT: - c = CURSOR_CROSS; - break; - case TOOL_PAN: - c = CURSOR_DRAG; - break; - case TOOL_RULER: - c = CURSOR_CROSS; - break; - default: - break; - } - break; case DRAG_LEFT: case DRAG_RIGHT: c = rotation_array[rotation_array_index]; @@ -2662,16 +2638,7 @@ void CanvasItemEditor::_update_cursor() { if (pan_pressed) { c = CURSOR_DRAG; } - - if (c != viewport->get_default_cursor_shape()) { - viewport->set_default_cursor_shape(c); - - // Force refresh cursor if it's over the viewport. - if (viewport->get_global_rect().has_point(get_global_mouse_position())) { - DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)viewport->get_default_cursor_shape(); - DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape); - } - } + return c; } void CanvasItemEditor::_draw_text_at_position(Point2 p_position, String p_string, Side p_side) { @@ -3869,7 +3836,7 @@ void CanvasItemEditor::_update_editor_settings() { context_menu_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles"))); panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/2d_editor_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EDITOR_GET("editors/panning/simple_panning"))); - pan_speed = int(EDITOR_GET("editors/panning/2d_editor_pan_speed")); + panner->set_scroll_speed(EDITOR_GET("editors/panning/2d_editor_pan_speed")); warped_panning = bool(EDITOR_GET("editors/panning/warped_mouse_panning")); } @@ -3958,8 +3925,8 @@ void CanvasItemEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { select_sb->set_texture(get_theme_icon(SNAME("EditorRect2D"), SNAME("EditorIcons"))); - select_sb->set_margin_size_all(4); - select_sb->set_default_margin_all(4); + select_sb->set_texture_margin_all(4); + select_sb->set_content_margin_all(4); AnimationPlayerEditor::get_singleton()->get_track_editor()->connect("visibility_changed", callable_mp(this, &CanvasItemEditor::_keying_changed)); _keying_changed(); @@ -3990,6 +3957,10 @@ void CanvasItemEditor::_selection_changed() { } void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { + if (!p_canvas_item) { + return; + } + Array selection = editor_selection->get_selected_nodes(); if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) { _reset_drag(); @@ -5056,7 +5027,7 @@ CanvasItemEditor::CanvasItemEditor() { zoom_widget->connect("zoom_changed", callable_mp(this, &CanvasItemEditor::_update_zoom)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &CanvasItemEditor::_scroll_callback), callable_mp(this, &CanvasItemEditor::_pan_callback), callable_mp(this, &CanvasItemEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &CanvasItemEditor::_pan_callback), callable_mp(this, &CanvasItemEditor::_zoom_callback)); viewport = memnew(CanvasItemEditorViewport(this)); viewport_scrollable->add_child(viewport); @@ -5925,7 +5896,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(CanvasItemEditor *p_canvas_it EditorNode::get_singleton()->get_gui_base()->add_child(selector); selector->set_title(TTR("Change Default Type")); selector->connect("confirmed", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_confirmed)); - selector->connect("cancelled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed)); + selector->connect("canceled", callable_mp(this, &CanvasItemEditorViewport::_on_change_type_closed)); VBoxContainer *vbc = memnew(VBoxContainer); selector->add_child(vbc); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index f4fcc8a3d2..ebe87a56f7 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -363,12 +363,10 @@ private: Ref<ViewPanner> panner; bool warped_panning = true; - int pan_speed = 20; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); - bool _is_node_locked(const Node *p_node); + bool _is_node_locked(const Node *p_node) const; 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, bool p_allow_locked = false); @@ -402,7 +400,7 @@ private: void _prepare_grid_menu(); void _on_grid_menu_id_pressed(int p_id); - List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true); + List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true) const; Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list); void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true); Rect2 _get_encompassing_rect(const Node *p_node); @@ -552,6 +550,8 @@ public: void focus_selection(); void center_at(const Point2 &p_pos); + virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override; + EditorSelection *editor_selection = nullptr; CanvasItemEditor(); diff --git a/editor/plugins/dedicated_server_export_plugin.cpp b/editor/plugins/dedicated_server_export_plugin.cpp new file mode 100644 index 0000000000..013706034e --- /dev/null +++ b/editor/plugins/dedicated_server_export_plugin.cpp @@ -0,0 +1,139 @@ +/**************************************************************************/ +/* dedicated_server_export_plugin.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "dedicated_server_export_plugin.h" + +EditorExportPreset::FileExportMode DedicatedServerExportPlugin::_get_export_mode_for_path(const String &p_path) { + Ref<EditorExportPreset> preset = get_export_preset(); + ERR_FAIL_COND_V(preset.is_null(), EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED); + + EditorExportPreset::FileExportMode mode = preset->get_file_export_mode(p_path); + if (mode != EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) { + return mode; + } + + String path = p_path; + if (path.begins_with("res://")) { + path = path.substr(6); + } + + Vector<String> parts = path.split("/"); + + while (parts.size() > 0) { + parts.resize(parts.size() - 1); + + String test_path = "res://"; + if (parts.size() > 0) { + test_path += String("/").join(parts) + "/"; + } + + mode = preset->get_file_export_mode(test_path); + if (mode != EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED) { + break; + } + } + + return mode; +} + +PackedStringArray DedicatedServerExportPlugin::_get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const { + PackedStringArray ret; + + Ref<EditorExportPreset> preset = get_export_preset(); + ERR_FAIL_COND_V(preset.is_null(), ret); + + if (preset->is_dedicated_server()) { + ret.append("dedicated_server"); + } + return ret; +} + +uint64_t DedicatedServerExportPlugin::_get_customization_configuration_hash() const { + Ref<EditorExportPreset> preset = get_export_preset(); + ERR_FAIL_COND_V(preset.is_null(), 0); + + if (preset->get_export_filter() != EditorExportPreset::EXPORT_CUSTOMIZED) { + return 0; + } + + return preset->get_customized_files().hash(); +} + +bool DedicatedServerExportPlugin::_begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) { + Ref<EditorExportPreset> preset = get_export_preset(); + ERR_FAIL_COND_V(preset.is_null(), false); + + current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED; + + return preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED; +} + +bool DedicatedServerExportPlugin::_begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) { + Ref<EditorExportPreset> preset = get_export_preset(); + ERR_FAIL_COND_V(preset.is_null(), false); + + current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED; + + return preset->get_export_filter() == EditorExportPreset::EXPORT_CUSTOMIZED; +} + +Node *DedicatedServerExportPlugin::_customize_scene(Node *p_root, const String &p_path) { + // Simply set the export mode based on the scene path. All the real + // customization happens in _customize_resource(). + current_export_mode = _get_export_mode_for_path(p_path); + return nullptr; +} + +Ref<Resource> DedicatedServerExportPlugin::_customize_resource(const Ref<Resource> &p_resource, const String &p_path) { + // If the resource has a path, we use that to get our export mode. But if it + // doesn't, we assume that this resource is embedded in the last resource with + // a path. + if (p_path != "") { + current_export_mode = _get_export_mode_for_path(p_path); + } + + if (p_resource.is_valid() && current_export_mode == EditorExportPreset::MODE_FILE_STRIP && p_resource->has_method("create_placeholder")) { + Callable::CallError err; + Ref<Resource> result = const_cast<Resource *>(p_resource.ptr())->callp("create_placeholder", nullptr, 0, err); + if (err.error == Callable::CallError::CALL_OK) { + return result; + } + } + + return Ref<Resource>(); +} + +void DedicatedServerExportPlugin::_end_customize_scenes() { + current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED; +} + +void DedicatedServerExportPlugin::_end_customize_resources() { + current_export_mode = EditorExportPreset::MODE_FILE_NOT_CUSTOMIZED; +} diff --git a/editor/plugins/dedicated_server_export_plugin.h b/editor/plugins/dedicated_server_export_plugin.h new file mode 100644 index 0000000000..cb014ae52d --- /dev/null +++ b/editor/plugins/dedicated_server_export_plugin.h @@ -0,0 +1,58 @@ +/**************************************************************************/ +/* dedicated_server_export_plugin.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DEDICATED_SERVER_EXPORT_PLUGIN_H +#define DEDICATED_SERVER_EXPORT_PLUGIN_H + +#include "editor/export/editor_export.h" + +class DedicatedServerExportPlugin : public EditorExportPlugin { +private: + EditorExportPreset::FileExportMode current_export_mode; + + EditorExportPreset::FileExportMode _get_export_mode_for_path(const String &p_path); + +protected: + String _get_name() const override { return "DedicatedServer"; } + + PackedStringArray _get_export_features(const Ref<EditorExportPlatform> &p_platform, bool p_debug) const override; + uint64_t _get_customization_configuration_hash() const override; + + bool _begin_customize_scenes(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override; + bool _begin_customize_resources(const Ref<EditorExportPlatform> &p_platform, const Vector<String> &p_features) override; + + Node *_customize_scene(Node *p_root, const String &p_path) override; + Ref<Resource> _customize_resource(const Ref<Resource> &p_resource, const String &p_path) override; + + void _end_customize_scenes() override; + void _end_customize_resources() override; +}; + +#endif // DEDICATED_SERVER_EXPORT_PLUGIN_H diff --git a/editor/plugins/gradient_editor.cpp b/editor/plugins/gradient_editor.cpp index 3676c2c222..2eb17b3f13 100644 --- a/editor/plugins/gradient_editor.cpp +++ b/editor/plugins/gradient_editor.cpp @@ -52,12 +52,12 @@ void GradientEditor::reverse_gradient() { int GradientEditor::_get_point_from_pos(int x) { int result = -1; - int total_w = get_size().width - get_size().height - draw_spacing; + int total_w = get_size().width - get_size().height - draw_spacing - handle_width; float min_distance = 1e20; for (int i = 0; i < points.size(); i++) { // Check if we clicked at point. float distance = ABS(x - points[i].offset * total_w); - float min = (handle_width / 2 * 1.7); // Make it easier to grab. + float min = handle_width * 0.85; // Allow the mouse to be more than half a handle width away for ease of grabbing. if (distance <= min && distance < min_distance) { result = i; min_distance = distance; @@ -198,116 +198,87 @@ void GradientEditor::gui_input(const Ref<InputEvent> &p_event) { } Ref<InputEventMouseButton> mb = p_event; - // Show color picker on double click. - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_double_click() && mb->is_pressed()) { - grabbed = _get_point_from_pos(mb->get_position().x); - _show_color_picker(); - accept_event(); - return; - } - - // Delete point on right click. - if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) { - grabbed = _get_point_from_pos(mb->get_position().x); - if (grabbed != -1) { - points.remove_at(grabbed); - grabbed = -1; - grabbing = false; - queue_redraw(); - emit_signal(SNAME("ramp_changed")); - accept_event(); - } - } - - // Hold alt key to duplicate selected color. - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed() && mb->is_alt_pressed()) { - int x = mb->get_position().x; - grabbed = _get_point_from_pos(x); - if (grabbed != -1) { - int total_w = get_size().width - get_size().height - draw_spacing; - Gradient::Point new_point = points[grabbed]; - new_point.offset = CLAMP(x / float(total_w), 0, 1); + if (mb.is_valid() && mb->is_pressed()) { + float adjusted_mb_x = mb->get_position().x - handle_width / 2; - points.push_back(new_point); - points.sort(); - for (int i = 0; i < points.size(); ++i) { - if (points[i].offset == new_point.offset) { - grabbed = i; - break; - } + // Delete point on right click. + if (mb->get_button_index() == MouseButton::RIGHT) { + grabbed = _get_point_from_pos(adjusted_mb_x); + if (grabbed != -1) { + points.remove_at(grabbed); + grabbed = -1; + grabbing = false; + queue_redraw(); + emit_signal(SNAME("ramp_changed")); + accept_event(); } - - emit_signal(SNAME("ramp_changed")); - queue_redraw(); } - } - // Select. - if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { - queue_redraw(); - int x = mb->get_position().x; - int total_w = get_size().width - get_size().height - draw_spacing; + // Hold Alt key to duplicate selected color. + if (mb->get_button_index() == MouseButton::LEFT && mb->is_alt_pressed()) { + grabbed = _get_point_from_pos(adjusted_mb_x); - //Check if color selector was clicked. - if (x > total_w + draw_spacing) { - _show_color_picker(); - return; - } - - grabbing = true; + if (grabbed != -1) { + int total_w = get_size().width - get_size().height - draw_spacing - handle_width; + Gradient::Point new_point = points[grabbed]; + new_point.offset = CLAMP(adjusted_mb_x / float(total_w), 0, 1); + points.push_back(new_point); + points.sort(); + for (int i = 0; i < points.size(); ++i) { + if (points[i].offset == new_point.offset) { + grabbed = i; + break; + } + } - grabbed = _get_point_from_pos(x); - //grab or select - if (grabbed != -1) { - return; + emit_signal(SNAME("ramp_changed")); + queue_redraw(); + } } - // Insert point. - Gradient::Point new_point; - new_point.offset = CLAMP(x / float(total_w), 0, 1); - - Gradient::Point prev; - Gradient::Point next; + // Select. + if (mb->get_button_index() == MouseButton::LEFT) { + queue_redraw(); + int total_w = get_size().width - get_size().height - draw_spacing - handle_width; - int pos = -1; - for (int i = 0; i < points.size(); i++) { - if (points[i].offset < new_point.offset) { - pos = i; + // Check if color selector was clicked or ramp was double-clicked. + if (adjusted_mb_x > total_w + draw_spacing) { + if (!mb->is_double_click()) { + _show_color_picker(); + } + return; + } else if (mb->is_double_click()) { + grabbed = _get_point_from_pos(adjusted_mb_x); + _show_color_picker(); + accept_event(); + return; } - } - if (pos == -1) { - prev.color = Color(0, 0, 0); - prev.offset = 0; - if (points.size()) { - next = points[0]; - } else { - next.color = Color(1, 1, 1); - next.offset = 1.0; - } - } else { - if (pos == points.size() - 1) { - next.color = Color(1, 1, 1); - next.offset = 1.0; - } else { - next = points[pos + 1]; + grabbing = true; + grabbed = _get_point_from_pos(adjusted_mb_x); + + // Grab or select. + if (grabbed != -1) { + return; } - prev = points[pos]; - } - new_point.color = prev.color.lerp(next.color, (new_point.offset - prev.offset) / (next.offset - prev.offset)); + // Insert point. + Gradient::Point new_point; + new_point.offset = CLAMP(adjusted_mb_x / float(total_w), 0, 1); + new_point.color = gradient->get_color_at_offset(new_point.offset); - points.push_back(new_point); - points.sort(); - for (int i = 0; i < points.size(); i++) { - if (points[i].offset == new_point.offset) { - grabbed = i; - break; + points.push_back(new_point); + points.sort(); + for (int i = 0; i < points.size(); i++) { + if (points[i].offset == new_point.offset) { + grabbed = i; + break; + } } - } - emit_signal(SNAME("ramp_changed")); + emit_signal(SNAME("ramp_changed")); + } } if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) { @@ -321,18 +292,16 @@ void GradientEditor::gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid() && grabbing) { - int total_w = get_size().width - get_size().height - draw_spacing; - - int x = mm->get_position().x; - - float newofs = CLAMP(x / float(total_w), 0, 1); + float adjusted_mm_x = mm->get_position().x - handle_width / 2; + int total_w = get_size().width - get_size().height - draw_spacing - handle_width; + float newofs = CLAMP(adjusted_mm_x / float(total_w), 0, 1); // Snap to "round" coordinates if holding Ctrl. // Be more precise if holding Shift as well. if (mm->is_ctrl_pressed()) { newofs = Math::snapped(newofs, mm->is_shift_pressed() ? 0.025 : 0.1); } else if (mm->is_shift_pressed()) { - // Snap to nearest point if holding just Shift + // Snap to nearest point if holding just Shift. const float snap_threshold = 0.03; float smallest_ofs = snap_threshold; bool found = false; @@ -421,7 +390,7 @@ void GradientEditor::_notification(int p_what) { // Draw borders around color ramp if in focus. if (has_focus()) { - draw_rect(Rect2(handle_width / 2, 0, total_w, h), Color(1, 1, 1, 0.9), false); + draw_rect(Rect2(handle_width / 2, 0, total_w, h), Color(1, 1, 1, 0.9), false, 1); } // Draw point markers. @@ -432,18 +401,18 @@ void GradientEditor::_notification(int p_what) { draw_line(Vector2(points[i].offset * total_w + handle_width / 2, 0), Vector2(points[i].offset * total_w + handle_width / 2, h / 2), col); Rect2 rect = Rect2(points[i].offset * total_w, h / 2, handle_width, h / 2); draw_rect(rect, points[i].color, true); - draw_rect(rect, col, false); + draw_rect(rect, col, false, 1); if (grabbed == i) { const Color focus_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); rect = rect.grow(-1); if (has_focus()) { - draw_rect(rect, focus_color, false); + draw_rect(rect, focus_color, false, 1); } else { - draw_rect(rect, focus_color.darkened(0.4), false); + draw_rect(rect, focus_color.darkened(0.4), false, 1); } rect = rect.grow(-1); - draw_rect(rect, col, false); + draw_rect(rect, col, false, 1); } } @@ -454,7 +423,7 @@ void GradientEditor::_notification(int p_what) { // Draw with selection color. draw_rect(Rect2(button_offset, 0, h, h), points[grabbed].color); } else { - // If no color selected draw grey color with 'X' on top. + // If no color selected draw gray color with 'X' on top. draw_rect(Rect2(button_offset, 0, h, h), Color(0.5, 0.5, 0.5, 1)); draw_line(Vector2(button_offset, 0), Vector2(button_offset + h, h), Color(1, 1, 1, 0.6)); draw_line(Vector2(button_offset, h), Vector2(button_offset + h, 0), Color(1, 1, 1, 0.6)); diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp index 1adcc2a3b4..519cfcaa94 100644 --- a/editor/plugins/lightmap_gi_editor_plugin.cpp +++ b/editor/plugins/lightmap_gi_editor_plugin.cpp @@ -35,12 +35,43 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) { if (lightmap) { - LightmapGI::BakeError err; + LightmapGI::BakeError err = LightmapGI::BAKE_ERROR_OK; const uint64_t time_started = OS::get_singleton()->get_ticks_msec(); - if (get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root() == lightmap) { - err = lightmap->bake(lightmap, p_file, bake_func_step); + if (get_tree()->get_edited_scene_root()) { + Ref<LightmapGIData> lightmapGIData = lightmap->get_light_data(); + + if (lightmapGIData.is_valid()) { + String path = lightmapGIData->get_path(); + if (!path.is_resource_file()) { + int srpos = path.find("::"); + if (srpos != -1) { + String base = path.substr(0, srpos); + 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) { + err = LightmapGI::BAKE_ERROR_FOREIGN_DATA; + } + } else { + if (FileAccess::exists(base + ".import")) { + err = LightmapGI::BAKE_ERROR_FOREIGN_DATA; + } + } + } + } else { + if (FileAccess::exists(path + ".import")) { + err = LightmapGI::BAKE_ERROR_FOREIGN_DATA; + } + } + } + + if (err == LightmapGI::BAKE_ERROR_OK) { + if (get_tree()->get_edited_scene_root() == lightmap) { + err = lightmap->bake(lightmap, p_file, bake_func_step); + } else { + err = lightmap->bake(lightmap->get_parent(), p_file, bake_func_step); + } + } } else { - err = lightmap->bake(lightmap->get_parent(), p_file, bake_func_step); + err = LightmapGI::BAKE_ERROR_NO_SCENE_ROOT; } bake_func_end(time_started); @@ -59,16 +90,21 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) { file_dialog->set_current_path(scene_path); file_dialog->popup_file_dialog(); - } break; - case LightmapGI::BAKE_ERROR_NO_MESHES: + case LightmapGI::BAKE_ERROR_NO_MESHES: { EditorNode::get_singleton()->show_warning(TTR("No meshes to bake. Make sure they contain an UV2 channel and that the 'Bake Light' flag is on.")); - break; - case LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE: + } break; + case LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE: { EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable.")); - break; + } break; + case LightmapGI::BAKE_ERROR_NO_SCENE_ROOT: { + EditorNode::get_singleton()->show_warning(TTR("No editor scene root found.")); + } break; + case LightmapGI::BAKE_ERROR_FOREIGN_DATA: { + EditorNode::get_singleton()->show_warning(TTR("Lightmap data is not local to the scene.")); + } break; default: { - } + } break; } } } diff --git a/editor/plugins/navigation_link_2d_editor_plugin.cpp b/editor/plugins/navigation_link_2d_editor_plugin.cpp index 21a1d839f0..dff92ced27 100644 --- a/editor/plugins/navigation_link_2d_editor_plugin.cpp +++ b/editor/plugins/navigation_link_2d_editor_plugin.cpp @@ -65,20 +65,20 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { - // Start location - if (xform.xform(node->get_start_location()).distance_to(mb->get_position()) < grab_threshold) { + // Start position + if (xform.xform(node->get_start_position()).distance_to(mb->get_position()) < grab_threshold) { start_grabbed = true; - original_start_location = node->get_start_location(); + original_start_position = node->get_start_position(); return true; } else { start_grabbed = false; } - // End location - if (xform.xform(node->get_end_location()).distance_to(mb->get_position()) < grab_threshold) { + // End position + if (xform.xform(node->get_end_position()).distance_to(mb->get_position()) < grab_threshold) { end_grabbed = true; - original_end_location = node->get_end_location(); + original_end_position = node->get_end_position(); return true; } else { @@ -87,10 +87,10 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e } else { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); if (start_grabbed) { - undo_redo->create_action(TTR("Set start_location")); - undo_redo->add_do_method(node, "set_start_location", node->get_start_location()); + undo_redo->create_action(TTR("Set start_position")); + undo_redo->add_do_method(node, "set_start_position", node->get_start_position()); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(node, "set_start_location", original_start_location); + undo_redo->add_undo_method(node, "set_start_position", original_start_position); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); @@ -100,10 +100,10 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e } if (end_grabbed) { - undo_redo->create_action(TTR("Set end_location")); - undo_redo->add_do_method(node, "set_end_location", node->get_end_location()); + undo_redo->create_action(TTR("Set end_position")); + undo_redo->add_do_method(node, "set_end_position", node->get_end_position()); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(node, "set_end_location", original_end_location); + undo_redo->add_undo_method(node, "set_end_position", original_end_position); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); @@ -120,14 +120,14 @@ bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e point = node->get_global_transform().affine_inverse().xform(point); if (start_grabbed) { - node->set_start_location(point); + node->set_start_position(point); canvas_item_editor->update_viewport(); return true; } if (end_grabbed) { - node->set_end_location(point); + node->set_end_position(point); canvas_item_editor->update_viewport(); return true; @@ -143,13 +143,13 @@ void NavigationLink2DEditor::forward_canvas_draw_over_viewport(Control *p_overla } Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform(); - Vector2 global_start_location = gt.xform(node->get_start_location()); - Vector2 global_end_location = gt.xform(node->get_end_location()); + Vector2 global_start_position = gt.xform(node->get_start_position()); + Vector2 global_end_position = gt.xform(node->get_end_position()); // Only drawing the handles here, since the debug rendering will fill in the rest. const Ref<Texture2D> handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); - p_overlay->draw_texture(handle, global_start_location - handle->get_size() / 2); - p_overlay->draw_texture(handle, global_end_location - handle->get_size() / 2); + p_overlay->draw_texture(handle, global_start_position - handle->get_size() / 2); + p_overlay->draw_texture(handle, global_end_position - handle->get_size() / 2); } void NavigationLink2DEditor::edit(NavigationLink2D *p_node) { diff --git a/editor/plugins/navigation_link_2d_editor_plugin.h b/editor/plugins/navigation_link_2d_editor_plugin.h index 76444403d0..ea731ca2cd 100644 --- a/editor/plugins/navigation_link_2d_editor_plugin.h +++ b/editor/plugins/navigation_link_2d_editor_plugin.h @@ -43,10 +43,10 @@ class NavigationLink2DEditor : public Control { NavigationLink2D *node = nullptr; bool start_grabbed = false; - Vector2 original_start_location; + Vector2 original_start_position; bool end_grabbed = false; - Vector2 original_end_location; + Vector2 original_end_position; protected: void _notification(int p_what); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index bb71c27bff..e11dc1c81d 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -5057,8 +5057,8 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3 up_vector = NavigationServer3D::get_singleton()->map_get_up(nav_map); Vector3::Axis up_axis = up_vector.max_axis_index(); - Vector3 start_location = link->get_start_location(); - Vector3 end_location = link->get_end_location(); + Vector3 start_position = link->get_start_position(); + Vector3 end_position = link->get_end_position(); Ref<Material> link_material = get_material("navigation_link_material", p_gizmo); Ref<Material> link_material_disabled = get_material("navigation_link_material_disabled", p_gizmo); @@ -5068,10 +5068,10 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw line between the points. Vector<Vector3> lines; - lines.append(start_location); - lines.append(end_location); + lines.append(start_position); + lines.append(end_position); - // Draw start location search radius + // Draw start position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -5082,21 +5082,21 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(start_location + Vector3(0, a.x, a.y)); - lines.append(start_location + Vector3(0, b.x, b.y)); + lines.append(start_position + Vector3(0, a.x, a.y)); + lines.append(start_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(start_location + Vector3(a.x, 0, a.y)); - lines.append(start_location + Vector3(b.x, 0, b.y)); + lines.append(start_position + Vector3(a.x, 0, a.y)); + lines.append(start_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(start_location + Vector3(a.x, a.y, 0)); - lines.append(start_location + Vector3(b.x, b.y, 0)); + lines.append(start_position + Vector3(a.x, a.y, 0)); + lines.append(start_position + Vector3(b.x, b.y, 0)); break; } } - // Draw end location search radius + // Draw end position search radius for (int i = 0; i < 30; i++) { // Create a circle const float ra = Math::deg_to_rad((float)(i * 12)); @@ -5107,16 +5107,16 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // Draw axis-aligned circle switch (up_axis) { case Vector3::AXIS_X: - lines.append(end_location + Vector3(0, a.x, a.y)); - lines.append(end_location + Vector3(0, b.x, b.y)); + lines.append(end_position + Vector3(0, a.x, a.y)); + lines.append(end_position + Vector3(0, b.x, b.y)); break; case Vector3::AXIS_Y: - lines.append(end_location + Vector3(a.x, 0, a.y)); - lines.append(end_location + Vector3(b.x, 0, b.y)); + lines.append(end_position + Vector3(a.x, 0, a.y)); + lines.append(end_position + Vector3(b.x, 0, b.y)); break; case Vector3::AXIS_Z: - lines.append(end_location + Vector3(a.x, a.y, 0)); - lines.append(end_location + Vector3(b.x, b.y, 0)); + lines.append(end_position + Vector3(a.x, a.y, 0)); + lines.append(end_position + Vector3(b.x, b.y, 0)); break; } } @@ -5125,8 +5125,8 @@ void NavigationLink3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->add_collision_segments(lines); Vector<Vector3> handles; - handles.append(start_location); - handles.append(end_location); + handles.append(start_position); + handles.append(end_position); p_gizmo->add_handles(handles, handles_material); } @@ -5136,7 +5136,7 @@ String NavigationLink3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g Variant NavigationLink3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { NavigationLink3D *link = Object::cast_to<NavigationLink3D>(p_gizmo->get_node_3d()); - return p_id == 0 ? link->get_start_location() : link->get_end_location(); + return p_id == 0 ? link->get_start_position() : link->get_end_position(); } void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { @@ -5151,8 +5151,8 @@ void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i Vector3 ray_from = p_camera->project_ray_origin(p_point); Vector3 ray_dir = p_camera->project_ray_normal(p_point); - Vector3 location = p_id == 0 ? link->get_start_location() : link->get_end_location(); - Plane move_plane = Plane(cam_dir, gt.xform(location)); + Vector3 position = p_id == 0 ? link->get_start_position() : link->get_end_position(); + Plane move_plane = Plane(cam_dir, gt.xform(position)); Vector3 intersection; if (!move_plane.intersects_ray(ray_from, ray_dir, &intersection)) { @@ -5164,11 +5164,11 @@ void NavigationLink3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i intersection.snap(Vector3(snap, snap, snap)); } - location = gi.xform(intersection); + position = gi.xform(intersection); if (p_id == 0) { - link->set_start_location(location); + link->set_start_position(position); } else if (p_id == 1) { - link->set_end_location(location); + link->set_end_position(position); } } @@ -5177,22 +5177,22 @@ void NavigationLink3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo if (p_cancel) { if (p_id == 0) { - link->set_start_location(p_restore); + link->set_start_position(p_restore); } else { - link->set_end_location(p_restore); + link->set_end_position(p_restore); } return; } EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); if (p_id == 0) { - ur->create_action(TTR("Change Start Location")); - ur->add_do_method(link, "set_start_location", link->get_start_location()); - ur->add_undo_method(link, "set_start_location", p_restore); + ur->create_action(TTR("Change Start Position")); + ur->add_do_method(link, "set_start_position", link->get_start_position()); + ur->add_undo_method(link, "set_start_position", p_restore); } else { - ur->create_action(TTR("Change End Location")); - ur->add_do_method(link, "set_end_location", link->get_end_location()); - ur->add_undo_method(link, "set_end_location", p_restore); + ur->create_action(TTR("Change End Position")); + ur->add_do_method(link, "set_end_position", link->get_end_position()); + ur->add_undo_method(link, "set_end_position", p_restore); } ur->commit_action(); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 05fc464226..fb35668310 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -939,21 +939,13 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } -void Polygon2DEditor::_uv_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _uv_pan_callback(-p_scroll_vec * 32); -} - -void Polygon2DEditor::_uv_pan_callback(Vector2 p_scroll_vec) { +void Polygon2DEditor::_uv_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { uv_hscroll->set_value(uv_hscroll->get_value() - p_scroll_vec.x); uv_vscroll->set_value(uv_vscroll->get_value() - p_scroll_vec.y); } -void Polygon2DEditor::_uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - if (p_scroll_vec.y < 0) { - uv_zoom->set_value(uv_zoom->get_value() / (1 - (0.1 * Math::abs(p_scroll_vec.y)))); - } else { - uv_zoom->set_value(uv_zoom->get_value() * (1 - (0.1 * Math::abs(p_scroll_vec.y)))); - } +void Polygon2DEditor::_uv_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + uv_zoom->set_value(uv_zoom->get_value() * p_zoom_factor); } void Polygon2DEditor::_uv_scroll_changed(real_t) { @@ -1254,7 +1246,7 @@ Polygon2DEditor::Polygon2DEditor() { uv_edit = memnew(AcceptDialog); add_child(uv_edit); uv_edit->set_title(TTR("Polygon 2D UV Editor")); - uv_edit->connect("cancelled", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide)); + uv_edit->connect("canceled", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide)); VBoxContainer *uv_main_vb = memnew(VBoxContainer); uv_edit->add_child(uv_main_vb); @@ -1478,7 +1470,7 @@ Polygon2DEditor::Polygon2DEditor() { bone_scroll->add_child(bone_scroll_vb); uv_panner.instantiate(); - uv_panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_uv_scroll_callback), callable_mp(this, &Polygon2DEditor::_uv_pan_callback), callable_mp(this, &Polygon2DEditor::_uv_zoom_callback)); + uv_panner->set_callbacks(callable_mp(this, &Polygon2DEditor::_uv_pan_callback), callable_mp(this, &Polygon2DEditor::_uv_zoom_callback)); uv_edit_draw->connect("draw", callable_mp(this, &Polygon2DEditor::_uv_draw)); uv_edit_draw->connect("gui_input", callable_mp(this, &Polygon2DEditor::_uv_input)); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 7246c08bea..2c55a5f631 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -90,9 +90,8 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { TextureRect *uv_icon_zoom = nullptr; Ref<ViewPanner> uv_panner; - void _uv_scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _uv_pan_callback(Vector2 p_scroll_vec); - void _uv_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _uv_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _uv_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); VBoxContainer *bone_scroll_main_vb = nullptr; ScrollContainer *bone_scroll = nullptr; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 6bb725f7a0..6b4e7184d9 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -252,12 +252,14 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { } else if (p_line.get_type() == Variant::DICTIONARY) { Dictionary meta = p_line.operator Dictionary(); const int line = meta["line"].operator int64_t() - 1; + const String code = meta["code"].operator String(); + const String quote_style = EDITOR_GET("text_editor/completion/use_single_quotes") ? "'" : "\""; CodeEdit *text_editor = code_editor->get_text_editor(); String prev_line = line > 0 ? text_editor->get_line(line - 1) : ""; if (prev_line.contains("@warning_ignore")) { const int closing_bracket_idx = prev_line.find(")"); - const String text_to_insert = ", " + meta["code"].operator String(); + const String text_to_insert = ", " + code.quote(quote_style); prev_line = prev_line.insert(closing_bracket_idx, text_to_insert); text_editor->set_line(line - 1, prev_line); } else { @@ -268,7 +270,7 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { } else { annotation_indent = String(" ").repeat(text_editor->get_indent_size() * indent); } - text_editor->insert_line_at(line, annotation_indent + "@warning_ignore(" + meta["code"].operator String() + ")"); + text_editor->insert_line_at(line, annotation_indent + "@warning_ignore(" + code.quote(quote_style) + ")"); } _validate_script(); @@ -2159,7 +2161,7 @@ ScriptTextEditor::ScriptTextEditor() { connection_info_dialog = memnew(ConnectionInfoDialog); - SET_DRAG_FORWARDING_GCD(code_editor, ScriptTextEditor); + SET_DRAG_FORWARDING_GCD(code_editor->get_text_editor(), ScriptTextEditor); } ScriptTextEditor::~ScriptTextEditor() { diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index bc9a33d5d6..907bc81674 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -117,6 +117,10 @@ void ShaderEditorPlugin::_move_shader_tab(int p_from, int p_to) { } void ShaderEditorPlugin::edit(Object *p_object) { + if (!p_object) { + return; + } + EditedShader es; ShaderInclude *si = Object::cast_to<ShaderInclude>(p_object); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 3ee9823f3a..782e365138 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -266,15 +266,15 @@ void Skeleton3DEditor::reset_pose(const bool p_all_bones) { if (!skeleton) { return; } - const int bone_len = skeleton->get_bone_count(); - if (!bone_len) { + const int bone_count = skeleton->get_bone_count(); + if (!bone_count) { return; } EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS); if (p_all_bones) { - for (int i = 0; i < bone_len; i++) { + for (int i = 0; i < bone_count; i++) { ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i)); ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i)); ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i)); @@ -333,15 +333,15 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) { if (!skeleton) { return; } - const int bone_len = skeleton->get_bone_count(); - if (!bone_len) { + const int bone_count = skeleton->get_bone_count(); + if (!bone_count) { return; } EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS); if (p_all_bones) { - for (int i = 0; i < bone_len; i++) { + for (int i = 0; i < bone_count; i++) { ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_pose(i)); ur->add_undo_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i)); } @@ -362,57 +362,56 @@ void Skeleton3DEditor::create_physical_skeleton() { ERR_FAIL_COND(!get_tree()); Node *owner = get_tree()->get_edited_scene_root(); - const int bc = skeleton->get_bone_count(); + const int bone_count = skeleton->get_bone_count(); - if (!bc) { + if (!bone_count) { + EditorNode::get_singleton()->show_warning(vformat(TTR("Cannot create a physical skeleton for a Skeleton3D node with no bones."))); return; } Vector<BoneInfo> bones_infos; - bones_infos.resize(bc); + bones_infos.resize(bone_count); - if (bc > 0) { - ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL); - for (int bone_id = 0; bc > bone_id; ++bone_id) { - const int parent = skeleton->get_bone_parent(bone_id); + ur->create_action(TTR("Create physical bones"), UndoRedo::MERGE_ALL); + for (int bone_id = 0; bone_count > bone_id; ++bone_id) { + const int parent = skeleton->get_bone_parent(bone_id); - if (parent < 0) { - bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); - } else { - const int parent_parent = skeleton->get_bone_parent(parent); - - bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); - - // Create physical bone on parent. - if (!bones_infos[parent].physical_bone) { - PhysicalBone3D *physical_bone = create_physical_bone(parent, bone_id, bones_infos); - if (physical_bone && physical_bone->get_child(0)) { - CollisionShape3D *collision_shape = Object::cast_to<CollisionShape3D>(physical_bone->get_child(0)); - if (collision_shape) { - bones_infos.write[parent].physical_bone = physical_bone; - - ur->add_do_method(skeleton, "add_child", physical_bone); - ur->add_do_method(physical_bone, "set_owner", owner); - ur->add_do_method(collision_shape, "set_owner", owner); - ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent)); - - // Create joint between parent of parent. - if (parent_parent != -1) { - ur->add_do_method(physical_bone, "set_joint_type", PhysicalBone3D::JOINT_TYPE_PIN); - } + if (parent < 0) { + bones_infos.write[bone_id].relative_rest = skeleton->get_bone_rest(bone_id); + } else { + const int parent_parent = skeleton->get_bone_parent(parent); + + bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id); + + // Create physical bone on parent. + if (!bones_infos[parent].physical_bone) { + PhysicalBone3D *physical_bone = create_physical_bone(parent, bone_id, bones_infos); + if (physical_bone && physical_bone->get_child(0)) { + CollisionShape3D *collision_shape = Object::cast_to<CollisionShape3D>(physical_bone->get_child(0)); + if (collision_shape) { + bones_infos.write[parent].physical_bone = physical_bone; + + ur->add_do_method(skeleton, "add_child", physical_bone); + ur->add_do_method(physical_bone, "set_owner", owner); + ur->add_do_method(collision_shape, "set_owner", owner); + ur->add_do_property(physical_bone, "bone_name", skeleton->get_bone_name(parent)); + + // Create joint between parent of parent. + if (parent_parent != -1) { + ur->add_do_method(physical_bone, "set_joint_type", PhysicalBone3D::JOINT_TYPE_PIN); + } - ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, physical_bone); - ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, collision_shape); + ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, physical_bone); + ur->add_do_method(Node3DEditor::get_singleton(), SceneStringNames::get_singleton()->_request_gizmo, collision_shape); - ur->add_do_reference(physical_bone); - ur->add_undo_method(skeleton, "remove_child", physical_bone); - } + ur->add_do_reference(physical_bone); + ur->add_undo_method(skeleton, "remove_child", physical_bone); } } } } - ur->commit_action(); } + ur->commit_action(); } PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos) { @@ -457,6 +456,11 @@ PhysicalBone3D *Skeleton3DEditor::create_physical_bone(int bone_id, int bone_chi } void Skeleton3DEditor::export_skeleton_profile() { + if (!skeleton->get_bone_count()) { + EditorNode::get_singleton()->show_warning(vformat(TTR("Cannot export a SkeletonProfile for a Skeleton3D node with no bones."))); + return; + } + file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE); file_dialog->set_title(TTR("Export Skeleton Profile As...")); @@ -481,9 +485,9 @@ void Skeleton3DEditor::_file_selected(const String &p_file) { Vector2 position_max; Vector2 position_min; - int len = skeleton->get_bone_count(); - sp->set_bone_size(len); - for (int i = 0; i < len; i++) { + const int bone_count = skeleton->get_bone_count(); + sp->set_bone_size(bone_count); + for (int i = 0; i < bone_count; i++) { sp->set_bone_name(i, skeleton->get_bone_name(i)); int parent = skeleton->get_bone_parent(i); if (parent >= 0) { @@ -509,7 +513,7 @@ void Skeleton3DEditor::_file_selected(const String &p_file) { Vector2 center = Vector2((position_max.x + position_min.x) * 0.5, (position_max.y + position_min.y) * 0.5); float nrm = MAX(bound.x, bound.y); if (nrm > 0) { - for (int i = 0; i < len; i++) { + for (int i = 0; i < bone_count; i++) { handle_positions.write[i] = (handle_positions[i] - center) / nrm * 0.9; sp->set_handle_offset(i, Vector2(0.5 + handle_positions[i].x, 0.5 - handle_positions[i].y)); } @@ -980,25 +984,31 @@ void Skeleton3DEditor::_draw_gizmo() { } void Skeleton3DEditor::_draw_handles() { - handles_mesh_instance->show(); + const int bone_count = skeleton->get_bone_count(); - const int bone_len = skeleton->get_bone_count(); handles_mesh->clear_surfaces(); - handles_mesh->surface_begin(Mesh::PRIMITIVE_POINTS); - for (int i = 0; i < bone_len; i++) { - Color c; - if (i == selected_bone) { - c = Color(1, 1, 0); - } else { - c = Color(0.1, 0.25, 0.8); + if (bone_count) { + handles_mesh_instance->show(); + + handles_mesh->surface_begin(Mesh::PRIMITIVE_POINTS); + + for (int i = 0; i < bone_count; i++) { + Color c; + if (i == selected_bone) { + c = Color(1, 1, 0); + } else { + c = Color(0.1, 0.25, 0.8); + } + Vector3 point = skeleton->get_bone_global_pose(i).origin; + handles_mesh->surface_set_color(c); + handles_mesh->surface_add_vertex(point); } - Vector3 point = skeleton->get_bone_global_pose(i).origin; - handles_mesh->surface_set_color(c); - handles_mesh->surface_add_vertex(point); + handles_mesh->surface_end(); + handles_mesh->surface_set_material(0, handle_material); + } else { + handles_mesh_instance->hide(); } - handles_mesh->surface_end(); - handles_mesh->surface_set_material(0, handle_material); } TreeItem *Skeleton3DEditor::_find(TreeItem *p_node, const NodePath &p_path) { @@ -1253,8 +1263,8 @@ int Skeleton3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gi Transform3D gt = skeleton->get_global_transform(); int closest_idx = -1; real_t closest_dist = 1e10; - const int bone_len = skeleton->get_bone_count(); - for (int i = 0; i < bone_len; i++) { + const int bone_count = skeleton->get_bone_count(); + for (int i = 0; i < bone_count; i++) { Vector3 joint_pos_3d = gt.xform(skeleton->get_bone_global_pose(i).origin); Vector2 joint_pos_2d = p_camera->unproject_position(joint_pos_3d); real_t dist_3d = ray_from.distance_to(joint_pos_3d); @@ -1349,6 +1359,10 @@ void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_node_3d()); p_gizmo->clear(); + if (!skeleton->get_bone_count()) { + return; + } + int selected = -1; Skeleton3DEditor *se = Skeleton3DEditor::get_singleton(); if (se) { diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 9bad2f2fbf..c5aa60c816 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -256,10 +256,10 @@ void TextureRegionEditor::_region_draw() { margins[2] = node_ninepatch->get_patch_margin(SIDE_LEFT); margins[3] = node_ninepatch->get_patch_margin(SIDE_RIGHT); } else if (obj_styleBox.is_valid()) { - margins[0] = obj_styleBox->get_margin_size(SIDE_TOP); - margins[1] = obj_styleBox->get_margin_size(SIDE_BOTTOM); - margins[2] = obj_styleBox->get_margin_size(SIDE_LEFT); - margins[3] = obj_styleBox->get_margin_size(SIDE_RIGHT); + margins[0] = obj_styleBox->get_texture_margin(SIDE_TOP); + margins[1] = obj_styleBox->get_texture_margin(SIDE_BOTTOM); + margins[2] = obj_styleBox->get_texture_margin(SIDE_LEFT); + margins[3] = obj_styleBox->get_texture_margin(SIDE_RIGHT); } Vector2 pos[4] = { @@ -314,10 +314,10 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { margins[2] = node_ninepatch->get_patch_margin(SIDE_LEFT); margins[3] = node_ninepatch->get_patch_margin(SIDE_RIGHT); } else if (obj_styleBox.is_valid()) { - margins[0] = obj_styleBox->get_margin_size(SIDE_TOP); - margins[1] = obj_styleBox->get_margin_size(SIDE_BOTTOM); - margins[2] = obj_styleBox->get_margin_size(SIDE_LEFT); - margins[3] = obj_styleBox->get_margin_size(SIDE_RIGHT); + margins[0] = obj_styleBox->get_texture_margin(SIDE_TOP); + margins[1] = obj_styleBox->get_texture_margin(SIDE_BOTTOM); + margins[2] = obj_styleBox->get_texture_margin(SIDE_LEFT); + margins[3] = obj_styleBox->get_texture_margin(SIDE_RIGHT); } Vector2 pos[4] = { @@ -431,8 +431,8 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { undo_redo->add_do_method(node_ninepatch, "set_patch_margin", side[edited_margin], node_ninepatch->get_patch_margin(side[edited_margin])); undo_redo->add_undo_method(node_ninepatch, "set_patch_margin", side[edited_margin], prev_margin); } else if (obj_styleBox.is_valid()) { - undo_redo->add_do_method(obj_styleBox.ptr(), "set_margin_size", side[edited_margin], obj_styleBox->get_margin_size(side[edited_margin])); - undo_redo->add_undo_method(obj_styleBox.ptr(), "set_margin_size", side[edited_margin], prev_margin); + undo_redo->add_do_method(obj_styleBox.ptr(), "set_texture_margin", side[edited_margin], obj_styleBox->get_texture_margin(side[edited_margin])); + undo_redo->add_undo_method(obj_styleBox.ptr(), "set_texture_margin", side[edited_margin], prev_margin); obj_styleBox->emit_signal(CoreStringNames::get_singleton()->changed); } edited_margin = -1; @@ -474,7 +474,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { node_ninepatch->set_patch_margin(side[edited_margin], prev_margin); } if (obj_styleBox.is_valid()) { - obj_styleBox->set_margin_size(side[edited_margin], prev_margin); + obj_styleBox->set_texture_margin(side[edited_margin], prev_margin); } edited_margin = -1; } else { @@ -535,7 +535,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { node_ninepatch->set_patch_margin(side[edited_margin], new_margin); } if (obj_styleBox.is_valid()) { - obj_styleBox->set_margin_size(side[edited_margin], new_margin); + obj_styleBox->set_texture_margin(side[edited_margin], new_margin); } } else { Vector2 new_pos = mtx.affine_inverse().xform(mm->get_position()); @@ -620,22 +620,14 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } } -void TextureRegionEditor::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void TextureRegionEditor::_pan_callback(Vector2 p_scroll_vec) { +void TextureRegionEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { p_scroll_vec /= draw_zoom; hscroll->set_value(hscroll->get_value() - p_scroll_vec.x); vscroll->set_value(vscroll->get_value() - p_scroll_vec.y); } -void TextureRegionEditor::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - if (p_scroll_vec.y < 0) { - _zoom_on_position(draw_zoom * ((0.95 + (0.05 * Math::abs(p_scroll_vec.y))) / 0.95), p_origin); - } else { - _zoom_on_position(draw_zoom * (1 - (0.05 * Math::abs(p_scroll_vec.y))), p_origin); - } +void TextureRegionEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + _zoom_on_position(draw_zoom * p_zoom_factor, p_origin); } void TextureRegionEditor::_scroll_changed(float) { @@ -748,6 +740,9 @@ void TextureRegionEditor::_update_rect() { } } else if (obj_styleBox.is_valid()) { rect = obj_styleBox->get_region_rect(); + if (rect == Rect2()) { + rect = Rect2(Vector2(), obj_styleBox->get_texture()->get_size()); + } } } @@ -1169,7 +1164,7 @@ TextureRegionEditor::TextureRegionEditor() { hb_grid->hide(); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_scroll_callback), callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &TextureRegionEditor::_pan_callback), callable_mp(this, &TextureRegionEditor::_zoom_callback)); edit_draw = memnew(Panel); vb->add_child(edit_draw); diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 0325700d25..ba64a04084 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -103,9 +103,8 @@ class TextureRegionEditor : public AcceptDialog { bool request_center = false; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); void _set_snap_mode(int p_mode); void _set_snap_off_x(float p_val); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index 40aac77a99..14ff59b442 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -3335,8 +3335,10 @@ void ThemeTypeEditor::set_edited_theme(const Ref<Theme> &p_theme) { } edited_theme = p_theme; - edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); - _update_type_list(); + if (edited_theme.is_valid()) { + edited_theme->connect("changed", callable_mp(this, &ThemeTypeEditor::_update_type_list_debounced)); + _update_type_list(); + } add_type_dialog->set_edited_theme(edited_theme); } @@ -3496,7 +3498,9 @@ void ThemeEditor::edit(const Ref<Theme> &p_theme) { preview_tab->set_preview_theme(p_theme); } - theme_name->set_text(TTR("Theme:") + " " + theme->get_path().get_file()); + if (theme.is_valid()) { + theme_name->set_text(TTR("Theme:") + " " + theme->get_path().get_file()); + } } Ref<Theme> ThemeEditor::get_edited_theme() { diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index 0ac375407c..e430848875 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -47,18 +47,14 @@ void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) { } } -void TileAtlasView::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - _pan_callback(-p_scroll_vec * 32); -} - -void TileAtlasView::_pan_callback(Vector2 p_scroll_vec) { +void TileAtlasView::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { panning += p_scroll_vec; _update_zoom_and_panning(true); emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning); } -void TileAtlasView::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - zoom_widget->set_zoom_by_increments(-p_scroll_vec.y * 2); +void TileAtlasView::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + zoom_widget->set_zoom(zoom_widget->get_zoom() * p_zoom_factor); _update_zoom_and_panning(true); emit_signal(SNAME("transform_changed"), zoom_widget->get_zoom(), panning); } @@ -583,7 +579,7 @@ TileAtlasView::TileAtlasView() { add_child(button_center_view); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &TileAtlasView::_scroll_callback), callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &TileAtlasView::_pan_callback), callable_mp(this, &TileAtlasView::_zoom_callback)); panner->set_enable_rmb(true); center_container = memnew(CenterContainer); diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index f719bee704..4a7547f34b 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -65,9 +65,8 @@ private: virtual void gui_input(const Ref<InputEvent> &p_event) override; Ref<ViewPanner> panner; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); HashMap<Vector2, HashMap<int, Rect2i>> alternative_tiles_rect_cache; void _update_alternative_tiles_rect_cache(); diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 2394130ad6..d79bd8ced3 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -266,7 +266,7 @@ void TileMapEditorTilesPlugin::_patterns_item_list_gui_input(const Ref<InputEven } Ref<TileSet> tile_set = tile_map->get_tileset(); - if (!tile_set.is_valid()) { + if (!tile_set.is_valid() || EditorNode::get_singleton()->is_resource_read_only(tile_set)) { return; } @@ -1277,13 +1277,15 @@ void TileMapEditorTilesPlugin::_stop_dragging() { tile_map->set_cell(tile_map_layer, kv.key, kv.value.source_id, kv.value.get_atlas_coords(), kv.value.alternative_tile); } - // Creating a pattern in the pattern list. - select_last_pattern = true; - int new_pattern_index = tile_set->get_patterns_count(); - undo_redo->create_action(TTR("Add TileSet pattern")); - undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index); - undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index); - undo_redo->commit_action(); + if (EditorNode::get_singleton()->is_resource_read_only(tile_set)) { + // Creating a pattern in the pattern list. + select_last_pattern = true; + int new_pattern_index = tile_set->get_patterns_count(); + undo_redo->create_action(TTR("Add TileSet pattern")); + undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index); + undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index); + undo_redo->commit_action(); + } } else { // Get the top-left cell. Vector2i top_left; @@ -1989,6 +1991,15 @@ TypedArray<Vector2i> TileMapEditorTilesPlugin::_get_tile_map_selection() const { void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) { _stop_dragging(); // Avoids staying in a wrong drag state. + // Disable sort button if the tileset is read-only + TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id)); + if (tile_map) { + Ref<TileSet> tile_set = tile_map->get_tileset(); + if (tile_set.is_valid()) { + source_sort_button->set_disabled(EditorNode::get_singleton()->is_resource_read_only(tile_set)); + } + } + if (tile_map_id != p_tile_map_id) { tile_map_id = p_tile_map_id; @@ -3986,7 +3997,7 @@ TileMapEditor::TileMapEditor() { tabs_bar->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed)); // --- TileMap toolbar --- - tile_map_toolbar = memnew(HBoxContainer); + tile_map_toolbar = memnew(HFlowContainer); tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL); add_child(tile_map_toolbar); @@ -4001,8 +4012,11 @@ TileMapEditor::TileMapEditor() { } } - // Wide empty separation control. - tile_map_toolbar->add_spacer(); + // Wide empty separation control. (like BoxContainer::add_spacer()) + Control *c = memnew(Control); + c->set_mouse_filter(MOUSE_FILTER_PASS); + c->set_h_size_flags(SIZE_EXPAND_FILL); + tile_map_toolbar->add_child(c); // Layer selector. layers_selection_button = memnew(OptionButton); diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index fb9c2f3689..1cab1d1500 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -38,6 +38,7 @@ #include "scene/2d/tile_map.h" #include "scene/gui/box_container.h" #include "scene/gui/check_box.h" +#include "scene/gui/flow_container.h" #include "scene/gui/item_list.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" @@ -323,7 +324,7 @@ private: Vector<TileMapEditorPlugin *> tile_map_editor_plugins; // Toolbar. - HBoxContainer *tile_map_toolbar = nullptr; + HFlowContainer *tile_map_toolbar = nullptr; OptionButton *layers_selection_button = nullptr; Button *toggle_highlight_selected_layer_button = nullptr; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 32421daa92..a12a647e99 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -585,6 +585,7 @@ void TileSetAtlasSourceEditor::_update_atlas_source_inspector() { // Update visibility. bool inspector_visible = tools_button_group->get_pressed_button() == tool_setup_atlas_source_button; atlas_source_inspector->set_visible(inspector_visible); + atlas_source_inspector->set_read_only(read_only); } void TileSetAtlasSourceEditor::_update_tile_inspector() { @@ -599,6 +600,7 @@ void TileSetAtlasSourceEditor::_update_tile_inspector() { tile_inspector->hide(); tile_inspector_no_tile_selected_label->hide(); } + tile_inspector->set_read_only(read_only); } void TileSetAtlasSourceEditor::_update_tile_data_editors() { @@ -970,19 +972,19 @@ void TileSetAtlasSourceEditor::_update_toolbar() { current_tile_data_editor_toolbar->hide(); } tools_settings_erase_button->show(); - tool_advanced_menu_buttom->show(); + tool_advanced_menu_button->show(); } else if (tools_button_group->get_pressed_button() == tool_select_button) { if (current_tile_data_editor_toolbar) { current_tile_data_editor_toolbar->hide(); } tools_settings_erase_button->hide(); - tool_advanced_menu_buttom->hide(); + tool_advanced_menu_button->hide(); } else if (tools_button_group->get_pressed_button() == tool_paint_button) { if (current_tile_data_editor_toolbar) { current_tile_data_editor_toolbar->show(); } tools_settings_erase_button->hide(); - tool_advanced_menu_buttom->hide(); + tool_advanced_menu_button->hide(); } } @@ -2188,7 +2190,12 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource ERR_FAIL_COND(p_source_id < 0); ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source); - if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == tile_set_atlas_source_id) { + bool new_read_only_state = false; + if (p_tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == tile_set_atlas_source_id && new_read_only_state == read_only) { return; } @@ -2205,11 +2212,23 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource tile_set_atlas_source = p_tile_set_atlas_source; tile_set_atlas_source_id = p_source_id; - // Add the listener again. + // Read-only is off by default. + read_only = new_read_only_state; + if (tile_set.is_valid()) { tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed)); } + if (read_only && tools_button_group->get_pressed_button() == tool_paint_button) { + tool_paint_button->set_pressed(false); + tool_setup_atlas_source_button->set_pressed(true); + } + + // Disable buttons in read-only mode. + tool_paint_button->set_disabled(read_only); + tools_settings_erase_button->set_disabled(read_only); + tool_advanced_menu_button->set_disabled(read_only); + // Update everything. _update_source_inspector(); @@ -2344,7 +2363,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { tools_settings_erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons"))); - tool_advanced_menu_buttom->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + tool_advanced_menu_button->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); resize_handle = get_theme_icon(SNAME("EditorHandle"), SNAME("EditorIcons")); resize_handle_disabled = get_theme_icon(SNAME("EditorHandleDisabled"), SNAME("EditorIcons")); @@ -2352,6 +2371,18 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { if (tile_set_changed_needs_update) { + // Read-only is off by default + read_only = false; + // Add the listener again and check for read-only status. + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + + // Disable buttons in read-only mode. + tool_paint_button->set_disabled(read_only); + tools_settings_erase_button->set_disabled(read_only); + tool_advanced_menu_button->set_disabled(read_only); + // Update everything. _update_source_inspector(); @@ -2516,12 +2547,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tools_settings_erase_button->set_shortcut_context(this); tool_settings->add_child(tools_settings_erase_button); - tool_advanced_menu_buttom = memnew(MenuButton); - tool_advanced_menu_buttom->set_flat(true); - tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES); - tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES); - tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); - tool_settings->add_child(tool_advanced_menu_buttom); + tool_advanced_menu_button = memnew(MenuButton); + tool_advanced_menu_button->set_flat(true); + tool_advanced_menu_button->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES); + tool_advanced_menu_button->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES); + tool_advanced_menu_button->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); + tool_settings->add_child(tool_advanced_menu_button); _update_toolbar(); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index bcab1296ad..a4826bc56f 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -113,6 +113,8 @@ public: }; private: + bool read_only = false; + Ref<TileSet> tile_set; TileSetAtlasSource *tile_set_atlas_source = nullptr; int tile_set_atlas_source_id = TileSet::INVALID_SOURCE; @@ -209,7 +211,7 @@ private: HBoxContainer *tool_settings = nullptr; HBoxContainer *tool_settings_tile_data_toolbar_container = nullptr; Button *tools_settings_erase_button = nullptr; - MenuButton *tool_advanced_menu_buttom = nullptr; + MenuButton *tool_advanced_menu_button = nullptr; // Selection. RBSet<TileSelection> selection; diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 53c2d4de51..39d17c718e 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -89,6 +89,10 @@ void TileSetEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, bool TileSetEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { ERR_FAIL_COND_V(!tile_set.is_valid(), false); + if (read_only) { + return false; + } + if (p_from == sources_list) { Dictionary d = p_data; @@ -223,7 +227,7 @@ void TileSetEditor::_source_selected(int p_source_index) { ERR_FAIL_COND(!tile_set.is_valid()); // Update the selected source. - sources_delete_button->set_disabled(p_source_index < 0); + sources_delete_button->set_disabled(p_source_index < 0 || read_only); if (p_source_index >= 0) { int source_id = sources_list->get_item_metadata(p_source_index); @@ -356,8 +360,19 @@ void TileSetEditor::_notification(int p_what) { if (tile_set.is_valid()) { tile_set->set_edited(true); } + + read_only = false; + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + _update_sources_list(); _update_patterns_list(); + + sources_add_button->set_disabled(read_only); + sources_advanced_menu_button->set_disabled(read_only); + source_sort_button->set_disabled(read_only); + tile_set_changed_needs_update = false; } } break; @@ -367,6 +382,10 @@ void TileSetEditor::_notification(int p_what) { void TileSetEditor::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(!tile_set.is_valid()); + if (EditorNode::get_singleton()->is_resource_read_only(tile_set)) { + return; + } + if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) { Vector<int> selected = patterns_item_list->get_selected_items(); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); @@ -667,7 +686,12 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p } void TileSetEditor::edit(Ref<TileSet> p_tile_set) { - if (p_tile_set == tile_set) { + bool new_read_only_state = false; + if (tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && new_read_only_state == read_only) { return; } @@ -679,8 +703,15 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { // Change the edited object. tile_set = p_tile_set; - // Add the listener again. + // Read-only status is false by default + read_only = new_read_only_state; + + // Add the listener again and check for read-only status. if (tile_set.is_valid()) { + sources_add_button->set_disabled(read_only); + sources_advanced_menu_button->set_disabled(read_only); + source_sort_button->set_disabled(read_only); + tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed)); if (first_edit) { first_edit = false; @@ -690,10 +721,6 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) { } _update_patterns_list(); } - - tile_set_atlas_source_editor->hide(); - tile_set_scenes_collection_source_editor->hide(); - no_source_selected_label->show(); } TileSetEditor::TileSetEditor() { diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index e3dff11277..d36d3bde41 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -45,6 +45,8 @@ class TileSetEditor : public VBoxContainer { static TileSetEditor *singleton; private: + bool read_only = false; + Ref<TileSet> tile_set; bool tile_set_changed_needs_update = false; HSplitContainer *split_container = nullptr; diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp index 6251cd18f7..cc276597fa 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -284,7 +284,7 @@ void TileSetScenesCollectionSourceEditor::_update_tile_inspector() { void TileSetScenesCollectionSourceEditor::_update_action_buttons() { Vector<int> selected_indices = scene_tiles_list->get_selected_items(); - scene_tile_delete_button->set_disabled(selected_indices.size() <= 0); + scene_tile_delete_button->set_disabled(selected_indices.size() <= 0 || read_only); } void TileSetScenesCollectionSourceEditor::_update_scenes_list() { @@ -342,6 +342,12 @@ void TileSetScenesCollectionSourceEditor::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { if (tile_set_scenes_collection_source_changed_needs_update) { + read_only = false; + // Add the listener again and check for read-only status. + if (tile_set.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(tile_set); + } + // Update everything. _update_source_inspector(); _update_scenes_list(); @@ -365,7 +371,12 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS ERR_FAIL_COND(p_source_id < 0); ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source); - if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id) { + bool new_read_only_state = false; + if (p_tile_set.is_valid()) { + new_read_only_state = EditorNode::get_singleton()->is_resource_read_only(p_tile_set); + } + + if (p_tile_set == tile_set && p_tile_set_scenes_collection_source == tile_set_scenes_collection_source && p_source_id == tile_set_source_id && new_read_only_state == read_only) { return; } @@ -379,6 +390,16 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS tile_set_scenes_collection_source = p_tile_set_scenes_collection_source; tile_set_source_id = p_source_id; + // Read-only status is false by default + read_only = new_read_only_state; + + if (tile_set.is_valid()) { + scenes_collection_source_inspector->set_read_only(read_only); + tile_inspector->set_read_only(read_only); + + scene_tile_add_button->set_disabled(read_only); + } + // Add the listener again. if (tile_set_scenes_collection_source) { tile_set_scenes_collection_source->connect("changed", callable_mp(this, &TileSetScenesCollectionSourceEditor::_tile_set_scenes_collection_source_changed)); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h index 0901205a29..2a0e8595c4 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h @@ -91,6 +91,8 @@ private: }; private: + bool read_only = false; + Ref<TileSet> tile_set; TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; int tile_set_source_id = -1; diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp index 19ee0ae98d..77dd0f7793 100644 --- a/editor/plugins/tiles/tiles_editor_plugin.cpp +++ b/editor/plugins/tiles/tiles_editor_plugin.cpp @@ -101,7 +101,7 @@ void TilesEditorPlugin::_thread() { encompassing_rect.expand_to(world_pos); // Texture. - Ref<TileSetAtlasSource> atlas_source = tile_set->get_source(tile_map->get_cell_source_id(0, cell)); + Ref<TileSetAtlasSource> atlas_source = item.tile_set->get_source(tile_map->get_cell_source_id(0, cell)); if (atlas_source.is_valid()) { Vector2i coords = tile_map->get_cell_atlas_coords(0, cell); int alternative = tile_map->get_cell_alternative_tile(0, cell); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 96b1ad7ee0..af70e64b6a 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -40,6 +40,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" +#include "editor/filesystem_dock.h" #include "editor/inspector_dock.h" #include "editor/plugins/curve_editor_plugin.h" #include "editor/plugins/shader_editor_plugin.h" @@ -1271,18 +1272,55 @@ Dictionary VisualShaderEditor::get_custom_node_data(Ref<VisualShaderNodeCustom> return dict; } -void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { - Ref<Script> scr = Ref<Script>(p_resource.ptr()); - if (scr.is_null() || scr->get_instance_base_type() != "VisualShaderNodeCustom") { +void VisualShaderEditor::_get_current_mode_limits(int &r_begin_type, int &r_end_type) const { + switch (visual_shader->get_mode()) { + case Shader::MODE_CANVAS_ITEM: + case Shader::MODE_SPATIAL: { + r_begin_type = 0; + r_end_type = 3; + } break; + case Shader::MODE_PARTICLES: { + r_begin_type = 3; + r_end_type = 5 + r_begin_type; + } break; + case Shader::MODE_SKY: { + r_begin_type = 8; + r_end_type = 1 + r_begin_type; + } break; + case Shader::MODE_FOG: { + r_begin_type = 9; + r_end_type = 1 + r_begin_type; + } break; + default: { + } break; + } +} + +void VisualShaderEditor::_script_created(const Ref<Script> &p_script) { + if (p_script.is_null() || p_script->get_instance_base_type() != "VisualShaderNodeCustom") { + return; + } + Ref<VisualShaderNodeCustom> ref; + ref.instantiate(); + ref->set_script(p_script); + + Dictionary dict = get_custom_node_data(ref); + add_custom_type(dict["name"], dict["script"], dict["description"], dict["return_icon_type"], dict["category"], dict["highend"]); + + _update_options_menu(); +} + +void VisualShaderEditor::_update_custom_script(const Ref<Script> &p_script) { + if (p_script.is_null() || p_script->get_instance_base_type() != "VisualShaderNodeCustom") { return; } Ref<VisualShaderNodeCustom> ref; ref.instantiate(); - ref->set_script(scr); + ref->set_script(p_script); if (!ref->is_available(visual_shader->get_mode(), visual_shader->get_shader_type())) { for (int i = 0; i < add_options.size(); i++) { - if (add_options[i].is_custom && add_options[i].script == scr) { + if (add_options[i].is_custom && add_options[i].script == p_script) { add_options.remove_at(i); _update_options_menu(); // TODO: Make indication for the existed custom nodes with that script on graph to be disabled. @@ -1296,8 +1334,8 @@ void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { bool found_type = false; bool need_rebuild = false; - for (int i = 0; i < add_options.size(); i++) { - if (add_options[i].is_custom && add_options[i].script == scr) { + for (int i = custom_node_option_idx; i < add_options.size(); i++) { + if (add_options[i].script == p_script) { found_type = true; add_options.write[i].name = dict["name"]; @@ -1306,31 +1344,11 @@ void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { add_options.write[i].category = dict["category"]; add_options.write[i].highend = dict["highend"]; - int max_type = 0; - int type_offset = 0; - switch (visual_shader->get_mode()) { - case Shader::MODE_CANVAS_ITEM: - case Shader::MODE_SPATIAL: { - max_type = 3; - } break; - case Shader::MODE_PARTICLES: { - max_type = 5; - type_offset = 3; - } break; - case Shader::MODE_SKY: { - max_type = 1; - type_offset = 8; - } break; - case Shader::MODE_FOG: { - max_type = 1; - type_offset = 9; - } break; - default: { - } break; - } - max_type = type_offset + max_type; + int begin_type = 0; + int end_type = 0; + _get_current_mode_limits(begin_type, end_type); - for (int t = type_offset; t < max_type; t++) { + for (int t = begin_type; t < end_type; t++) { VisualShader::Type type = (VisualShader::Type)t; Vector<int> nodes = visual_shader->get_node_list(type); @@ -1339,28 +1357,27 @@ void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { List<VisualShader::Connection> custom_node_input_connections; List<VisualShader::Connection> custom_node_output_connections; + for (const VisualShader::Connection &E : node_connections) { int from = E.from_node; - int from_idx = E.from_port; + int from_port = E.from_port; int to = E.to_node; - int to_idx = E.to_port; + int to_port = E.to_port; - if (graph_plugin->get_node_script(from) == scr) { - custom_node_output_connections.push_back({ from, from_idx, to, to_idx }); - } else if (graph_plugin->get_node_script(to) == scr) { - custom_node_input_connections.push_back({ from, from_idx, to, to_idx }); + if (graph_plugin->get_node_script(from) == p_script) { + custom_node_output_connections.push_back({ from, from_port, to, to_port }); + } else if (graph_plugin->get_node_script(to) == p_script) { + custom_node_input_connections.push_back({ from, from_port, to, to_port }); } } - for (int j = 0; j < nodes.size(); j++) { - int node_id = nodes[j]; - + for (int node_id : nodes) { Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, node_id); if (vsnode.is_null()) { continue; } Ref<VisualShaderNodeCustom> custom_node = Ref<VisualShaderNodeCustom>(vsnode.ptr()); - if (custom_node.is_null() || custom_node->get_script() != scr) { + if (custom_node.is_null() || custom_node->get_script() != p_script) { continue; } need_rebuild = true; @@ -1429,6 +1446,89 @@ void VisualShaderEditor::update_custom_type(const Ref<Resource> &p_resource) { } } +void VisualShaderEditor::_resource_saved(const Ref<Resource> &p_resource) { + _update_custom_script(Ref<Script>(p_resource.ptr())); +} + +void VisualShaderEditor::_resources_removed() { + bool has_any_instance = false; + + for (const Ref<Script> &scr : custom_scripts_to_delete) { + for (int i = custom_node_option_idx; i < add_options.size(); i++) { + if (add_options[i].script == scr) { + add_options.remove_at(i); + + // Removes all node instances using that script from the graph. + { + int begin_type = 0; + int end_type = 0; + _get_current_mode_limits(begin_type, end_type); + + for (int t = begin_type; t < end_type; t++) { + VisualShader::Type type = (VisualShader::Type)t; + + List<VisualShader::Connection> node_connections; + visual_shader->get_node_connections(type, &node_connections); + + for (const VisualShader::Connection &E : node_connections) { + int from = E.from_node; + int from_port = E.from_port; + int to = E.to_node; + int to_port = E.to_port; + + if (graph_plugin->get_node_script(from) == scr || graph_plugin->get_node_script(to) == scr) { + visual_shader->disconnect_nodes(type, from, from_port, to, to_port); + graph_plugin->disconnect_nodes(type, from, from_port, to, to_port); + } + } + + Vector<int> nodes = visual_shader->get_node_list(type); + for (int node_id : nodes) { + Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, node_id); + if (vsnode.is_null()) { + continue; + } + Ref<VisualShaderNodeCustom> custom_node = Ref<VisualShaderNodeCustom>(vsnode.ptr()); + if (custom_node.is_null() || custom_node->get_script() != scr) { + continue; + } + visual_shader->remove_node(type, node_id); + graph_plugin->remove_node(type, node_id, false); + + has_any_instance = true; + } + } + } + + break; + } + } + } + if (has_any_instance) { + EditorUndoRedoManager::get_singleton()->clear_history(); // Need to clear undo history, otherwise it may lead to hard-detected errors and crashes (since the script was removed). + ResourceSaver::save(visual_shader, visual_shader->get_path()); + } + _update_options_menu(); + + custom_scripts_to_delete.clear(); + pending_custom_scripts_to_delete = false; +} + +void VisualShaderEditor::_resource_removed(const Ref<Resource> &p_resource) { + Ref<Script> scr = Ref<Script>(p_resource.ptr()); + if (scr.is_null() || scr->get_instance_base_type() != "VisualShaderNodeCustom") { + return; + } + + custom_scripts_to_delete.push_back(scr); + + if (!pending_custom_scripts_to_delete) { + pending_custom_scripts_to_delete = true; + + call_deferred("_resources_removed"); + } +} + void VisualShaderEditor::_update_options_menu_deferred() { _update_options_menu(); @@ -4901,13 +5001,16 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_expand_output_port", &VisualShaderEditor::_expand_output_port); ClassDB::bind_method("_update_options_menu_deferred", &VisualShaderEditor::_update_options_menu_deferred); ClassDB::bind_method("_rebuild_shader_deferred", &VisualShaderEditor::_rebuild_shader_deferred); + ClassDB::bind_method("_resources_removed", &VisualShaderEditor::_resources_removed); ClassDB::bind_method("_is_available", &VisualShaderEditor::_is_available); } VisualShaderEditor::VisualShaderEditor() { ShaderLanguage::get_keyword_list(&keyword_list); - EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &VisualShaderEditor::update_custom_type)); + EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &VisualShaderEditor::_resource_saved)); + FileSystemDock::get_singleton()->get_script_create_dialog()->connect("script_created", callable_mp(this, &VisualShaderEditor::_script_created)); + FileSystemDock::get_singleton()->connect("resource_removed", callable_mp(this, &VisualShaderEditor::_resource_removed)); graph = memnew(GraphEdit); graph->get_zoom_hbox()->set_h_size_flags(SIZE_EXPAND_FILL); @@ -5186,7 +5289,7 @@ VisualShaderEditor::VisualShaderEditor() { members_dialog->set_ok_button_text(TTR("Create")); members_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VisualShaderEditor::_member_create)); members_dialog->get_ok_button()->set_disabled(true); - members_dialog->connect("cancelled", callable_mp(this, &VisualShaderEditor::_member_cancel)); + members_dialog->connect("canceled", callable_mp(this, &VisualShaderEditor::_member_cancel)); add_child(members_dialog); // add varyings dialog diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index c4f6b4952c..519a390ccc 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -188,6 +188,9 @@ class VisualShaderEditor : public VBoxContainer { PanelContainer *error_panel = nullptr; Label *error_label = nullptr; + bool pending_custom_scripts_to_delete = false; + List<Ref<Script>> custom_scripts_to_delete; + bool _block_update_options_menu = false; bool _block_rebuild_shader = false; @@ -503,6 +506,13 @@ class VisualShaderEditor : public VBoxContainer { void _visibility_changed(); + void _get_current_mode_limits(int &r_begin_type, int &r_end_type) const; + void _update_custom_script(const Ref<Script> &p_script); + void _script_created(const Ref<Script> &p_script); + void _resource_saved(const Ref<Resource> &p_resource); + void _resource_removed(const Ref<Resource> &p_resource); + void _resources_removed(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -517,7 +527,6 @@ public: void add_custom_type(const String &p_name, const Ref<Script> &p_script, const String &p_description, int p_return_icon_type, const String &p_category, bool p_highend); Dictionary get_custom_node_data(Ref<VisualShaderNodeCustom> &p_custom_node); - void update_custom_type(const Ref<Resource> &p_resource); virtual Size2 get_minimum_size() const override; void edit(VisualShader *p_visual_shader); diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp index a3ccf392e6..f9f72fee77 100644 --- a/editor/plugins/voxel_gi_editor_plugin.cpp +++ b/editor/plugins/voxel_gi_editor_plugin.cpp @@ -35,7 +35,9 @@ void VoxelGIEditorPlugin::_bake() { if (voxel_gi) { - if (voxel_gi->get_probe_data().is_null()) { + Ref<VoxelGIData> voxel_gi_data = voxel_gi->get_probe_data(); + + if (voxel_gi_data.is_null()) { String path = get_tree()->get_edited_scene_root()->get_scene_file_path(); if (path.is_empty()) { path = "res://" + voxel_gi->get_name() + "_data.res"; @@ -46,7 +48,32 @@ void VoxelGIEditorPlugin::_bake() { probe_file->set_current_path(path); probe_file->popup_file_dialog(); return; + } else { + String path = voxel_gi_data->get_path(); + if (!path.is_resource_file()) { + int srpos = path.find("::"); + if (srpos != -1) { + String base = path.substr(0, srpos); + 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) { + EditorNode::get_singleton()->show_warning(TTR("Voxel GI data is not local to the scene.")); + return; + } + } else { + if (FileAccess::exists(base + ".import")) { + EditorNode::get_singleton()->show_warning(TTR("Voxel GI data is part of an imported resource.")); + return; + } + } + } + } else { + if (FileAccess::exists(path + ".import")) { + EditorNode::get_singleton()->show_warning(TTR("Voxel GI data is an imported resource.")); + return; + } + } } + voxel_gi->bake(); } } diff --git a/editor/progress_dialog.cpp b/editor/progress_dialog.cpp index db11f80826..37f941c7b2 100644 --- a/editor/progress_dialog.cpp +++ b/editor/progress_dialog.cpp @@ -195,7 +195,7 @@ void ProgressDialog::add_task(const String &p_task, const String &p_label, int p cancel_hb->hide(); } cancel_hb->move_to_front(); - cancelled = false; + canceled = false; _popup(); if (p_can_cancel) { cancel->grab_focus(); @@ -203,12 +203,12 @@ void ProgressDialog::add_task(const String &p_task, const String &p_label, int p } bool ProgressDialog::task_step(const String &p_task, const String &p_state, int p_step, bool p_force_redraw) { - ERR_FAIL_COND_V(!tasks.has(p_task), cancelled); + ERR_FAIL_COND_V(!tasks.has(p_task), canceled); if (!p_force_redraw) { uint64_t tus = OS::get_singleton()->get_ticks_usec(); if (tus - last_progress_tick < 200000) { //200ms - return cancelled; + return canceled; } } @@ -228,7 +228,7 @@ bool ProgressDialog::task_step(const String &p_task, const String &p_state, int #ifndef ANDROID_ENABLED Main::iteration(); // this will not work on a lot of platforms, so it's only meant for the editor #endif - return cancelled; + return canceled; } void ProgressDialog::end_task(const String &p_task) { @@ -246,7 +246,7 @@ void ProgressDialog::end_task(const String &p_task) { } void ProgressDialog::_cancel_pressed() { - cancelled = true; + canceled = true; } void ProgressDialog::_bind_methods() { diff --git a/editor/progress_dialog.h b/editor/progress_dialog.h index 5346f01a9e..7ac4864c9c 100644 --- a/editor/progress_dialog.h +++ b/editor/progress_dialog.h @@ -85,7 +85,7 @@ class ProgressDialog : public PopupPanel { void _popup(); void _cancel_pressed(); - bool cancelled = false; + bool canceled = false; protected: void _notification(int p_what); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 5f61d10f9d..5800fcca91 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -1285,6 +1285,8 @@ static const char *gdscript_signals_renames[][2] = { // {"changed","settings_changed"}, // EditorSettings { "about_to_show", "about_to_popup" }, // Popup { "button_release", "button_released" }, // XRController3D + { "cancelled", "canceled" }, // AcceptDialog + { "item_double_clicked", "item_icon_double_clicked" }, // Tree { "network_peer_connected", "peer_connected" }, // MultiplayerAPI { "network_peer_disconnected", "peer_disconnected" }, // MultiplayerAPI { "network_peer_packet", "peer_packet" }, // MultiplayerAPI @@ -2547,14 +2549,14 @@ bool ProjectConverter3To4::test_conversion(RegExContainer ®_container) { valid = valid && test_conversion_with_regex("tool", "@tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n tool", "\n tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\ntool", "\n\n@tool", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nremote func", "\n\n@rpc(any_peer) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nremotesync func", "\n\n@rpc(any_peer, call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nsync func", "\n\n@rpc(any_peer, call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nremote func", "\n\n@rpc(\"any_peer\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nremotesync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nsync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\nslave func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\npuppet func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\npuppetsync func", "\n\n@rpc(call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\npuppetsync func", "\n\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_with_regex("\n\nmaster func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); - valid = valid && test_conversion_with_regex("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(call_local) func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); + valid = valid && test_conversion_with_regex("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container); valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function , get_function", "var size : Vector2 = Vector2() : get = get_function, set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid && test_conversion_gdscript_builtin("var size : Vector2 = Vector2() setget set_function , ", "var size : Vector2 = Vector2() : set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); @@ -4085,13 +4087,13 @@ void ProjectConverter3To4::rename_gdscript_keywords(Vector<String> &lines, const line = reg_container.keyword_gdscript_onready.sub(line, "@onready", true); } if (line.contains("remote")) { - line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(any_peer) func", true); + line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(\"any_peer\") func", true); } if (line.contains("remote")) { - line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(any_peer, call_local) func", true); + line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(\"any_peer\", \"call_local\") func", true); } if (line.contains("sync")) { - line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(any_peer, call_local) func", true); + line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(\"any_peer\", \"call_local\") func", true); } if (line.contains("slave")) { line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true); @@ -4100,13 +4102,13 @@ void ProjectConverter3To4::rename_gdscript_keywords(Vector<String> &lines, const line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true); } if (line.contains("puppet")) { - line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(\"call_local\") func", true); } if (line.contains("master")) { line = reg_container.keyword_gdscript_master.sub(line, error_message + "@rpc func", true); } if (line.contains("master")) { - line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + "@rpc(\"call_local\") func", true); } } } @@ -4154,25 +4156,25 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("remote")) { old = line; - line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(any_peer) func", true); + line = reg_container.keyword_gdscript_remote.sub(line, "@rpc(\"any_peer\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "remote func", "@rpc(any_peer) func", line)); + found_renames.append(line_formatter(current_line, "remote func", "@rpc(\"any_peer\") func", line)); } } if (line.contains("remote")) { old = line; - line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(any_peer, call_local)) func", true); + line = reg_container.keyword_gdscript_remotesync.sub(line, "@rpc(\"any_peer\", \"call_local\")) func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "remotesync func", "@rpc(any_peer, call_local)) func", line)); + found_renames.append(line_formatter(current_line, "remotesync func", "@rpc(\"any_peer\", \"call_local\")) func", line)); } } if (line.contains("sync")) { old = line; - line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(any_peer, call_local)) func", true); + line = reg_container.keyword_gdscript_sync.sub(line, "@rpc(\"any_peer\", \"call_local\")) func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "sync func", "@rpc(any_peer, call_local)) func", line)); + found_renames.append(line_formatter(current_line, "sync func", "@rpc(\"any_peer\", \"call_local\")) func", line)); } } @@ -4194,9 +4196,9 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("puppet")) { old = line; - line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_puppetsync.sub(line, "@rpc(\"call_local\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "puppetsync func", "@rpc(call_local) func", line)); + found_renames.append(line_formatter(current_line, "puppetsync func", "@rpc(\"call_local\") func", line)); } } @@ -4210,9 +4212,9 @@ Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<S if (line.contains("master")) { old = line; - line = reg_container.keyword_gdscript_master.sub(line, "@rpc(call_local) func", true); + line = reg_container.keyword_gdscript_master.sub(line, "@rpc(\"call_local\") func", true); if (old != line) { - found_renames.append(line_formatter(current_line, "mastersync func", "@rpc(call_local) func", line)); + found_renames.append(line_formatter(current_line, "mastersync func", "@rpc(\"call_local\") func", line)); } } } diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 733f140a56..86e77fbbbe 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -1428,6 +1428,9 @@ void SceneTreeDock::_script_open_request(const Ref<Script> &p_script) { void SceneTreeDock::_push_item(Object *p_object) { EditorNode::get_singleton()->push_item(p_object); + if (p_object == nullptr) { + EditorNode::get_singleton()->hide_unused_editors(this); + } } void SceneTreeDock::_handle_select(Node *p_node) { @@ -2004,13 +2007,13 @@ void SceneTreeDock::_shader_created(Ref<Shader> p_shader) { void SceneTreeDock::_script_creation_closed() { script_create_dialog->disconnect("script_created", callable_mp(this, &SceneTreeDock::_script_created)); script_create_dialog->disconnect("confirmed", callable_mp(this, &SceneTreeDock::_script_creation_closed)); - script_create_dialog->disconnect("cancelled", callable_mp(this, &SceneTreeDock::_script_creation_closed)); + script_create_dialog->disconnect("canceled", callable_mp(this, &SceneTreeDock::_script_creation_closed)); } void SceneTreeDock::_shader_creation_closed() { shader_create_dialog->disconnect("shader_created", callable_mp(this, &SceneTreeDock::_shader_created)); shader_create_dialog->disconnect("confirmed", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); - shader_create_dialog->disconnect("cancelled", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); + shader_create_dialog->disconnect("canceled", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); } void SceneTreeDock::_toggle_editable_children_from_selection() { @@ -2061,7 +2064,7 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { return; } - EditorNode::get_singleton()->get_editor_plugins_over()->make_visible(false); + EditorNode::get_singleton()->hide_unused_editors(this); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(p_cut ? TTR("Cut Node(s)") : TTR("Remove Node(s)"), UndoRedo::MERGE_DISABLE, remove_list.front()->get()); @@ -2188,7 +2191,7 @@ void SceneTreeDock::_do_create(Node *p_parent) { ERR_FAIL_COND(!child); String new_name = p_parent->validate_child_name(child); - if (GLOBAL_GET("editor/node_naming/name_casing").operator int() != NAME_CASING_PASCAL_CASE) { + if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { new_name = adjust_name_casing(new_name); } child->set_name(new_name); @@ -3090,7 +3093,7 @@ void SceneTreeDock::attach_script_to_selected(bool p_extend) { script_create_dialog->connect("script_created", callable_mp(this, &SceneTreeDock::_script_created)); script_create_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_script_creation_closed)); - script_create_dialog->connect("cancelled", callable_mp(this, &SceneTreeDock::_script_creation_closed)); + script_create_dialog->connect("canceled", callable_mp(this, &SceneTreeDock::_script_creation_closed)); script_create_dialog->set_inheritance_base_type("Node"); script_create_dialog->config(inherits, path); script_create_dialog->popup_centered(); @@ -3132,7 +3135,7 @@ void SceneTreeDock::attach_shader_to_selected(int p_preferred_mode) { shader_create_dialog->connect("shader_created", callable_mp(this, &SceneTreeDock::_shader_created)); shader_create_dialog->connect("confirmed", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); - shader_create_dialog->connect("cancelled", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); + shader_create_dialog->connect("canceled", callable_mp(this, &SceneTreeDock::_shader_creation_closed)); shader_create_dialog->config(path, true, true, -1, p_preferred_mode); shader_create_dialog->popup_centered(); } @@ -3610,7 +3613,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec scene_tree->connect("script_dropped", callable_mp(this, &SceneTreeDock::_script_dropped)); scene_tree->connect("nodes_dragged", callable_mp(this, &SceneTreeDock::_nodes_drag_begin)); - scene_tree->get_scene_tree()->connect("item_double_clicked", callable_mp(this, &SceneTreeDock::_focus_node)); + scene_tree->get_scene_tree()->connect("item_icon_double_clicked", callable_mp(this, &SceneTreeDock::_focus_node)); editor_selection->connect("selection_changed", callable_mp(this, &SceneTreeDock::_selection_changed)); diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index e0d748c478..42801cdaf1 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -677,7 +677,7 @@ bool SceneTreeEditor::_item_matches_all_terms(TreeItem *p_item, PackedStringArra for (int i = 0; i < p_terms.size(); i++) { String term = p_terms[i]; - // Recognise special filter. + // Recognize special filter. if (term.contains(":") && !term.get_slicec(':', 0).is_empty()) { String parameter = term.get_slicec(':', 0); String argument = term.get_slicec(':', 1); @@ -971,7 +971,7 @@ void SceneTreeEditor::_renamed() { String raw_new_name = which->get_text(0); if (raw_new_name.strip_edges().is_empty()) { // If name is empty, fallback to class name. - if (GLOBAL_GET("editor/node_naming/name_casing").operator int() != NAME_CASING_PASCAL_CASE) { + if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { raw_new_name = Node::adjust_name_casing(n->get_class()); } else { raw_new_name = n->get_class(); |