diff options
Diffstat (limited to 'editor')
247 files changed, 5659 insertions, 2589 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 0d89f37dd2..cf5b18ce3c 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -358,7 +358,7 @@ void ActionMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { action_list_search->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - add_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + add_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); if (!actions_cache.is_empty()) { update_action_list(); } diff --git a/editor/add_metadata_dialog.cpp b/editor/add_metadata_dialog.cpp index 0a070e37b6..66a7b820f5 100644 --- a/editor/add_metadata_dialog.cpp +++ b/editor/add_metadata_dialog.cpp @@ -64,7 +64,6 @@ AddMetadataDialog::AddMetadataDialog() { } void AddMetadataDialog::_complete_init(const StringName &p_title) { - add_meta_name->grab_focus(); add_meta_name->set_text(""); validation_panel->update(); @@ -90,6 +89,7 @@ void AddMetadataDialog::open(const StringName p_title, List<StringName> &p_exist this->_existing_metas = p_existing_metas; _complete_init(p_title); popup_centered(); + add_meta_name->grab_focus(); } StringName AddMetadataDialog::get_meta_name() { diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 8d7c6a1f16..36ca417638 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -275,7 +275,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { } String base_path = animation->track_get_path(i); - int end = base_path.find(":"); + int end = base_path.find_char(':'); if (end != -1) { base_path = base_path.substr(0, end + 1); } @@ -1650,7 +1650,7 @@ void AnimationBezierTrackEdit::_zoom_callback(float p_zoom_factor, Vector2 p_ori Ref<InputEventWithModifiers> iewm = p_event; if (iewm.is_valid() && iewm->is_alt_pressed()) { // Alternate zoom (doesn't affect timeline). - timeline_v_zoom = CLAMP(timeline_v_zoom * p_zoom_factor, 0.000001, 100000); + timeline_v_zoom = CLAMP(timeline_v_zoom / p_zoom_factor, 0.000001, 100000); } else { float zoom_factor = p_zoom_factor > 1.0 ? AnimationTimelineEdit::SCROLL_ZOOM_FACTOR_IN : AnimationTimelineEdit::SCROLL_ZOOM_FACTOR_OUT; timeline->_zoom_callback(zoom_factor, p_origin, p_event); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 741d127ea2..19e0647fb7 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -63,7 +63,6 @@ constexpr double FPS_DECIMAL = 1.0; constexpr double SECOND_DECIMAL = 0.0001; -constexpr double FPS_STEP_FRACTION = 0.0625; void AnimationTrackKeyEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); @@ -1440,8 +1439,8 @@ void AnimationTimelineEdit::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - add_track->set_icon(get_editor_theme_icon(SNAME("Add"))); - loop->set_icon(get_editor_theme_icon(SNAME("Loop"))); + add_track->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + loop->set_button_icon(get_editor_theme_icon(SNAME("Loop"))); time_icon->set_texture(get_editor_theme_icon(SNAME("Time"))); add_track->get_popup()->clear(); @@ -1818,15 +1817,15 @@ void AnimationTimelineEdit::update_values() { switch (animation->get_loop_mode()) { case Animation::LOOP_NONE: { - loop->set_icon(get_editor_theme_icon(SNAME("Loop"))); + loop->set_button_icon(get_editor_theme_icon(SNAME("Loop"))); loop->set_pressed(false); } break; case Animation::LOOP_LINEAR: { - loop->set_icon(get_editor_theme_icon(SNAME("Loop"))); + loop->set_button_icon(get_editor_theme_icon(SNAME("Loop"))); loop->set_pressed(true); } break; case Animation::LOOP_PINGPONG: { - loop->set_icon(get_editor_theme_icon(SNAME("PingPongLoop"))); + loop->set_button_icon(get_editor_theme_icon(SNAME("PingPongLoop"))); loop->set_pressed(true); } break; default: @@ -2192,7 +2191,7 @@ void AnimationTrackEdit::_notification(int p_what) { offset = offset * scale + limit; Color marker_color = animation->get_marker_color(marker); marker_color.a = 0.2; - draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color); + draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color, Math::round(EDSCALE)); } } } @@ -3313,7 +3312,7 @@ Variant AnimationTrackEdit::get_drag_data(const Point2 &p_point) { Button *tb = memnew(Button); tb->set_flat(true); tb->set_text(path_cache); - tb->set_icon(icon_cache); + tb->set_button_icon(icon_cache); tb->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); tb->add_theme_constant_override("icon_max_width", get_theme_constant("class_icon_size", EditorStringName(Editor))); set_drag_preview(tb); @@ -3648,7 +3647,7 @@ void AnimationTrackEditGroup::_notification(int p_what) { offset = offset * scale + limit; Color marker_color = editor->get_current_animation()->get_marker_color(marker); marker_color.a = 0.2; - draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color); + draw_line(Point2(offset, 0), Point2(offset, get_size().height), marker_color, Math::round(EDSCALE)); } } } @@ -3776,6 +3775,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re step->set_read_only(false); snap_keys->set_disabled(false); snap_timeline->set_disabled(false); + fps_compat->set_disabled(false); snap_mode->set_disabled(false); auto_fit->set_disabled(false); auto_fit_bezier->set_disabled(false); @@ -3798,6 +3798,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re step->set_read_only(true); snap_keys->set_disabled(true); snap_timeline->set_disabled(true); + fps_compat->set_disabled(true); snap_mode->set_disabled(true); bezier_edit_icon->set_disabled(true); auto_fit->set_disabled(true); @@ -4339,7 +4340,7 @@ void AnimationTrackEditor::insert_node_value_key(Node *p_node, const String &p_p if (track_path == np) { actual_value = value; // All good. } else { - int sep = track_path.rfind(":"); + int sep = track_path.rfind_char(':'); if (sep != -1) { String base_path = track_path.substr(0, sep); if (base_path == np) { @@ -5029,7 +5030,12 @@ void AnimationTrackEditor::_snap_mode_changed(int p_mode) { } marker_edit->set_use_fps(use_fps); // To ensure that the conversion results are consistent between serialization and load, the value is snapped with 0.0625 to be a rational number when FPS mode is used. - step->set_step(use_fps ? FPS_STEP_FRACTION : SECOND_DECIMAL); + step->set_step(use_fps ? FPS_DECIMAL : SECOND_DECIMAL); + if (use_fps) { + fps_compat->hide(); + } else { + fps_compat->show(); + } _update_step_spinbox(); } @@ -5045,7 +5051,6 @@ void AnimationTrackEditor::_update_step_spinbox() { } else { step->set_value(1.0 / animation->get_step()); } - } else { step->set_value(animation->get_step()); } @@ -5054,6 +5059,20 @@ void AnimationTrackEditor::_update_step_spinbox() { _update_snap_unit(); } +void AnimationTrackEditor::_update_fps_compat_mode(bool p_enabled) { + _update_snap_unit(); +} + +void AnimationTrackEditor::_update_nearest_fps_label() { + bool is_fps_invalid = nearest_fps == 0; + if (is_fps_invalid) { + nearest_fps_label->hide(); + } else { + nearest_fps_label->show(); + nearest_fps_label->set_text("Nearest FPS: " + itos(nearest_fps)); + } +} + void AnimationTrackEditor::_animation_update() { timeline->queue_redraw(); timeline->update_values(); @@ -5112,18 +5131,19 @@ void AnimationTrackEditor::_notification(int p_what) { } case NOTIFICATION_THEME_CHANGED: { zoom_icon->set_texture(get_editor_theme_icon(SNAME("Zoom"))); - bezier_edit_icon->set_icon(get_editor_theme_icon(SNAME("EditBezier"))); - snap_timeline->set_icon(get_editor_theme_icon(SNAME("SnapTimeline"))); - snap_keys->set_icon(get_editor_theme_icon(SNAME("SnapKeys"))); - view_group->set_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); - selected_filter->set_icon(get_editor_theme_icon(SNAME("AnimationFilter"))); - imported_anim_warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); - dummy_player_warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); - inactive_player_warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + bezier_edit_icon->set_button_icon(get_editor_theme_icon(SNAME("EditBezier"))); + snap_timeline->set_button_icon(get_editor_theme_icon(SNAME("SnapTimeline"))); + snap_keys->set_button_icon(get_editor_theme_icon(SNAME("SnapKeys"))); + fps_compat->set_button_icon(get_editor_theme_icon(SNAME("FPS"))); + view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); + selected_filter->set_button_icon(get_editor_theme_icon(SNAME("AnimationFilter"))); + imported_anim_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + dummy_player_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + inactive_player_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); edit->get_popup()->set_item_icon(edit->get_popup()->get_item_index(EDIT_APPLY_RESET), get_editor_theme_icon(SNAME("Reload"))); - auto_fit->set_icon(get_editor_theme_icon(SNAME("AnimationAutoFit"))); - auto_fit_bezier->set_icon(get_editor_theme_icon(SNAME("AnimationAutoFitBezier"))); + auto_fit->set_button_icon(get_editor_theme_icon(SNAME("AnimationAutoFit"))); + auto_fit_bezier->set_button_icon(get_editor_theme_icon(SNAME("AnimationAutoFitBezier"))); const int timeline_separation = get_theme_constant(SNAME("timeline_v_separation"), SNAME("AnimationTrackEditor")); timeline_vbox->add_theme_constant_override("separation", timeline_separation); @@ -5160,9 +5180,8 @@ void AnimationTrackEditor::_update_step(double p_new_step) { double step_value = p_new_step; if (timeline->is_using_fps()) { if (step_value != 0.0) { - // step_value must also be less than or equal to 1000 to ensure that no error accumulates due to interactions with retrieving values from inner range. + // A step_value should be less than or equal to 1000 to ensure that no error accumulates due to interactions with retrieving values from inner range. step_value = 1.0 / MIN(1000.0, p_new_step); - ; } timeline->queue_redraw(); } @@ -5297,6 +5316,28 @@ void AnimationTrackEditor::_add_track(int p_type) { return; } adding_track_type = p_type; + Vector<StringName> valid_types; + switch (adding_track_type) { + case Animation::TYPE_BLEND_SHAPE: { + // Blend Shape is a property of MeshInstance3D. + valid_types.push_back(SNAME("MeshInstance3D")); + } break; + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: { + // 3D Properties come from nodes inheriting Node3D. + valid_types.push_back(SNAME("Node3D")); + } break; + case Animation::TYPE_AUDIO: { + valid_types.push_back(SNAME("AudioStreamPlayer")); + valid_types.push_back(SNAME("AudioStreamPlayer2D")); + valid_types.push_back(SNAME("AudioStreamPlayer3D")); + } break; + case Animation::TYPE_ANIMATION: { + valid_types.push_back(SNAME("AnimationPlayer")); + } break; + } + pick_track->set_valid_types(valid_types); pick_track->popup_scenetree_dialog(nullptr, root_node); pick_track->get_filter_line_edit()->clear(); pick_track->get_filter_line_edit()->grab_focus(); @@ -6337,8 +6378,9 @@ bool AnimationTrackEditor::_is_track_compatible(int p_target_track_idx, Variant: } if (path_valid) { - if (is_source_bezier) + if (is_source_bezier) { p_source_value_type = Variant::FLOAT; + } return property_type == p_source_value_type; } else { if (animation->track_get_key_count(p_target_track_idx) > 0) { @@ -6453,7 +6495,7 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { path = NodePath(node->get_path().get_names(), path.get_subnames(), true); // Store full path instead for copying. } else { text = path; - int sep = text.find(":"); + int sep = text.find_char(':'); if (sep != -1) { text = text.substr(sep + 1, text.length()); } @@ -7279,7 +7321,7 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) { void AnimationTrackEditor::_view_group_toggle() { _update_tracks(); - view_group->set_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); + view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup"))); bezier_edit->set_filtered(selected_filter->is_pressed()); } @@ -7313,39 +7355,54 @@ void AnimationTrackEditor::_selection_changed() { } void AnimationTrackEditor::_update_snap_unit() { + nearest_fps = 0; + if (step->get_value() <= 0) { snap_unit = 0; + _update_nearest_fps_label(); return; // Avoid zero div. } if (timeline->is_using_fps()) { snap_unit = 1.0 / step->get_value(); } else { - double integer; - double fraction = Math::modf(step->get_value(), &integer); - fraction = 1.0 / Math::round(1.0 / fraction); - snap_unit = integer + fraction; + if (fps_compat->is_pressed()) { + snap_unit = CLAMP(step->get_value(), 0.0, 1.0); + if (!Math::is_zero_approx(snap_unit)) { + real_t fps = Math::round(1.0 / snap_unit); + nearest_fps = int(fps); + snap_unit = 1.0 / fps; + } + } else { + snap_unit = step->get_value(); + } } + _update_nearest_fps_label(); } float AnimationTrackEditor::snap_time(float p_value, bool p_relative) { if (is_snap_keys_enabled()) { + double current_snap = snap_unit; if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) { // Use more precise snapping when holding Shift. - snap_unit *= 0.25; + current_snap *= 0.25; } if (p_relative) { - double rel = Math::fmod(timeline->get_value(), snap_unit); - p_value = Math::snapped(p_value + rel, snap_unit) - rel; + double rel = Math::fmod(timeline->get_value(), current_snap); + p_value = Math::snapped(p_value + rel, current_snap) - rel; } else { - p_value = Math::snapped(p_value, snap_unit); + p_value = Math::snapped(p_value, current_snap); } } return p_value; } +float AnimationTrackEditor::get_snap_unit() { + return snap_unit; +} + void AnimationTrackEditor::_show_imported_anim_warning() { // It looks terrible on a single line but the TTR extractor doesn't support line breaks yet. EditorNode::get_singleton()->show_warning( @@ -7599,6 +7656,18 @@ AnimationTrackEditor::AnimationTrackEditor() { snap_keys->set_pressed(true); snap_keys->set_tooltip_text(TTR("Apply snapping to selected key(s).")); + fps_compat = memnew(Button); + fps_compat->set_flat(true); + bottom_hb->add_child(fps_compat); + fps_compat->set_disabled(true); + fps_compat->set_toggle_mode(true); + fps_compat->set_pressed(true); + fps_compat->set_tooltip_text(TTR("Apply snapping to the nearest integer FPS.")); + fps_compat->connect(SceneStringName(toggled), callable_mp(this, &AnimationTrackEditor::_update_fps_compat_mode)); + + nearest_fps_label = memnew(Label); + bottom_hb->add_child(nearest_fps_label); + step = memnew(EditorSpinSlider); step->set_min(0); step->set_max(1000000); @@ -7913,6 +7982,11 @@ AnimationTrackEditor::~AnimationTrackEditor() { // AnimationTrackKeyEditEditorPlugin. +void AnimationTrackKeyEditEditor::_time_edit_spun() { + _time_edit_entered(); + _time_edit_exited(); +} + void AnimationTrackKeyEditEditor::_time_edit_entered() { int key = animation->track_find_key(track, key_ofs, Animation::FIND_MODE_APPROX); if (key == -1) { @@ -7940,7 +8014,7 @@ void AnimationTrackKeyEditEditor::_time_edit_exited() { int existing = animation->track_find_key(track, new_time, Animation::FIND_MODE_APPROX); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Animation Change Keyframe Time"), UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Animation Change Keyframe Time")); if (existing != -1) { undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", track, animation->track_get_key_time(track, existing)); @@ -7987,6 +8061,7 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat spinner->set_min(0); spinner->set_allow_greater(true); spinner->set_allow_lesser(true); + add_child(spinner); if (use_fps) { spinner->set_step(FPS_DECIMAL); @@ -7995,14 +8070,13 @@ AnimationTrackKeyEditEditor::AnimationTrackKeyEditEditor(Ref<Animation> p_animat fps = 1.0 / fps; } spinner->set_value(key_ofs * fps); + spinner->connect("updown_pressed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_spun), CONNECT_DEFERRED); } else { spinner->set_step(SECOND_DECIMAL); spinner->set_value(key_ofs); spinner->set_max(animation->get_length()); } - add_child(spinner); - spinner->connect("grabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED); spinner->connect("ungrabbed", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED); spinner->connect("value_focus_entered", callable_mp(this, &AnimationTrackKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED); @@ -8237,9 +8311,6 @@ void AnimationMarkerEdit::_notification(int p_what) { Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label")); Color color = get_theme_color(SceneStringName(font_color), SNAME("Label")); - int hsep = get_theme_constant(SNAME("h_separation"), SNAME("ItemList")); - Color linecolor = color; - linecolor.a = 0.2; // SECTION PREVIEW // @@ -8298,31 +8369,21 @@ void AnimationMarkerEdit::_notification(int p_what) { draw_key(name, scale, int(offset), is_selected, limit, limit_end); - const int font_size = 16; + const int font_size = 12 * EDSCALE; Size2 string_size = font->get_string_size(name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size); if (int(offset) <= limit_end && int(offset) >= limit && should_show_all_marker_names) { float bottom = get_size().height + string_size.y - font->get_descent(font_size); float extrusion = MAX(0, offset + string_size.x - limit_end); // How much the string would extrude outside limit_end if unadjusted. Color marker_color = animation->get_marker_color(name); - draw_string(font, Point2(offset - extrusion, bottom), name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, marker_color); - draw_string_outline(font, Point2(offset - extrusion, bottom), name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, 1, color); + float margin = 4 * EDSCALE; + Point2 pos = Point2(offset - extrusion + margin, bottom + margin); + draw_string(font, pos, name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, marker_color); + draw_string_outline(font, pos, name, HORIZONTAL_ALIGNMENT_LEFT, -1.0, font_size, 1, color); } } } draw_fg(limit, get_size().width - timeline->get_buttons_width()); - - // BUTTONS // - - { - int ofs = get_size().width - timeline->get_buttons_width(); - - draw_line(Point2(ofs, 0), Point2(ofs, get_size().height), linecolor, Math::round(EDSCALE)); - - ofs += hsep; - } - - draw_line(Vector2(0, get_size().height), get_size(), linecolor, Math::round(EDSCALE)); } break; case NOTIFICATION_MOUSE_ENTER: @@ -9123,9 +9184,6 @@ void AnimationMultiMarkerKeyEdit::_get_property_list(List<PropertyInfo> *p_list) // AnimationMarkerKeyEditEditorPlugin -void AnimationMarkerKeyEditEditor::_time_edit_entered() { -} - void AnimationMarkerKeyEditEditor::_time_edit_exited() { real_t new_time = spinner->get_value(); @@ -9144,7 +9202,7 @@ void AnimationMarkerKeyEditEditor::_time_edit_exited() { } EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Animation Change Marker Time"), UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Animation Change Marker Time")); Color color = animation->get_marker_color(marker_name); undo_redo->add_do_method(animation.ptr(), "add_marker", marker_name, new_time); @@ -9185,6 +9243,7 @@ AnimationMarkerKeyEditEditor::AnimationMarkerKeyEditEditor(Ref<Animation> p_anim spinner->set_min(0); spinner->set_allow_greater(true); spinner->set_allow_lesser(true); + add_child(spinner); float time = animation->get_marker_time(marker_name); @@ -9195,17 +9254,14 @@ AnimationMarkerKeyEditEditor::AnimationMarkerKeyEditEditor(Ref<Animation> p_anim fps = 1.0 / fps; } spinner->set_value(time * fps); + spinner->connect("updown_pressed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED); } else { spinner->set_step(SECOND_DECIMAL); spinner->set_value(time); spinner->set_max(animation->get_length()); } - add_child(spinner); - - spinner->connect("grabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED); spinner->connect("ungrabbed", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED); - spinner->connect("value_focus_entered", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_entered), CONNECT_DEFERRED); spinner->connect("value_focus_exited", callable_mp(this, &AnimationMarkerKeyEditEditor::_time_edit_exited), CONNECT_DEFERRED); } diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 0da474afd4..f17386b0c9 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -600,6 +600,8 @@ class AnimationTrackEditor : public VBoxContainer { AnimationMarkerEdit *marker_edit = nullptr; HSlider *zoom = nullptr; EditorSpinSlider *step = nullptr; + Button *fps_compat = nullptr; + Label *nearest_fps_label = nullptr; TextureRect *zoom_icon = nullptr; Button *snap_keys = nullptr; Button *snap_timeline = nullptr; @@ -637,6 +639,8 @@ class AnimationTrackEditor : public VBoxContainer { void _track_grab_focus(int p_track); void _update_scroll(double); + void _update_nearest_fps_label(); + void _update_fps_compat_mode(bool p_enabled); void _update_step(double p_new_step); void _update_length(double p_new_len); void _dropped_track(int p_from_track, int p_to_track); @@ -716,7 +720,7 @@ class AnimationTrackEditor : public VBoxContainer { struct SelectedKey { int track = 0; int key = 0; - bool operator<(const SelectedKey &p_key) const { return track == p_key.track ? key < p_key.key : track < p_key.track; }; + bool operator<(const SelectedKey &p_key) const { return track == p_key.track ? key < p_key.key : track < p_key.track; } }; struct KeyInfo { @@ -853,6 +857,8 @@ class AnimationTrackEditor : public VBoxContainer { void _pick_track_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates); double snap_unit; + bool fps_compatible = true; + int nearest_fps = 0; void _update_snap_unit(); protected: @@ -935,6 +941,7 @@ public: bool can_add_reset_key() const; float get_moving_selection_offset() const; float snap_time(float p_value, bool p_relative = false); + float get_snap_unit(); bool is_grouping_tracks(); PackedStringArray get_selected_section() const; bool is_marker_selected(const StringName &p_marker) const; @@ -970,6 +977,7 @@ class AnimationTrackKeyEditEditor : public EditorProperty { Variant value; } key_data_cache; + void _time_edit_spun(); void _time_edit_entered(); void _time_edit_exited(); @@ -989,7 +997,6 @@ class AnimationMarkerKeyEditEditor : public EditorProperty { EditorSpinSlider *spinner = nullptr; - void _time_edit_entered(); void _time_edit_exited(); public: diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 88a32b1a6d..05eeef4fc9 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -104,8 +104,8 @@ void FindReplaceBar::_notification(int p_what) { [[fallthrough]]; } case NOTIFICATION_READY: { - find_prev->set_icon(get_editor_theme_icon(SNAME("MoveUp"))); - find_next->set_icon(get_editor_theme_icon(SNAME("MoveDown"))); + find_prev->set_button_icon(get_editor_theme_icon(SNAME("MoveUp"))); + find_next->set_button_icon(get_editor_theme_icon(SNAME("MoveDown"))); hide_button->set_texture_normal(get_editor_theme_icon(SNAME("Close"))); hide_button->set_texture_hover(get_editor_theme_icon(SNAME("Close"))); hide_button->set_texture_pressed(get_editor_theme_icon(SNAME("Close"))); @@ -549,7 +549,7 @@ void FindReplaceBar::_update_toggle_replace_button(bool p_replace_visible) { String shortcut = ED_GET_SHORTCUT(p_replace_visible ? "script_text_editor/find" : "script_text_editor/replace")->get_as_text(); toggle_replace_button->set_tooltip_text(vformat("%s (%s)", tooltip, shortcut)); StringName rtl_compliant_arrow = is_layout_rtl() ? SNAME("GuiTreeArrowLeft") : SNAME("GuiTreeArrowRight"); - toggle_replace_button->set_icon(get_editor_theme_icon(p_replace_visible ? SNAME("GuiTreeArrowDown") : rtl_compliant_arrow)); + toggle_replace_button->set_button_icon(get_editor_theme_icon(p_replace_visible ? SNAME("GuiTreeArrowDown") : rtl_compliant_arrow)); } void FindReplaceBar::_show_search(bool p_with_replace, bool p_show_only) { @@ -1493,8 +1493,8 @@ void CodeTextEditor::goto_error() { void CodeTextEditor::_update_text_editor_theme() { emit_signal(SNAME("load_theme_settings")); - error_button->set_icon(get_editor_theme_icon(SNAME("StatusError"))); - warning_button->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + error_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError"))); + warning_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); Ref<Font> status_bar_font = get_theme_font(SNAME("status_source"), EditorStringName(EditorFonts)); int status_bar_font_size = get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts)); @@ -1771,7 +1771,7 @@ void CodeTextEditor::show_toggle_scripts_button() { void CodeTextEditor::update_toggle_scripts_button() { ERR_FAIL_NULL(toggle_scripts_list); bool forward = toggle_scripts_list->is_visible() == is_layout_rtl(); - toggle_scripts_button->set_icon(get_editor_theme_icon(forward ? SNAME("Forward") : SNAME("Back"))); + toggle_scripts_button->set_button_icon(get_editor_theme_icon(forward ? SNAME("Forward") : SNAME("Back"))); toggle_scripts_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text())); } diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 1c269a62c5..d76c324be0 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -488,7 +488,7 @@ void ConnectDialog::_notification(int p_what) { } method_search->set_right_icon(get_editor_theme_icon("Search")); - open_method_tree->set_icon(get_editor_theme_icon("Edit")); + open_method_tree->set_button_icon(get_editor_theme_icon("Edit")); } break; } } @@ -524,7 +524,7 @@ void ConnectDialog::set_dst_node(Node *p_node) { StringName ConnectDialog::get_dst_method_name() const { String txt = dst_method->get_text(); if (txt.contains("(")) { - txt = txt.left(txt.find("(")).strip_edges(); + txt = txt.left(txt.find_char('(')).strip_edges(); } return txt; } @@ -1079,17 +1079,17 @@ void ConnectionsDock::_tree_item_selected() { TreeItem *item = tree->get_selected(); if (item && _get_item_type(*item) == TREE_ITEM_TYPE_SIGNAL) { connect_button->set_text(TTR("Connect...")); - connect_button->set_icon(get_editor_theme_icon(SNAME("Instance"))); + connect_button->set_button_icon(get_editor_theme_icon(SNAME("Instance"))); connect_button->set_disabled(false); } else if (item && _get_item_type(*item) == TREE_ITEM_TYPE_CONNECTION) { connect_button->set_text(TTR("Disconnect")); - connect_button->set_icon(get_editor_theme_icon(SNAME("Unlinked"))); + connect_button->set_button_icon(get_editor_theme_icon(SNAME("Unlinked"))); Object::Connection connection = item->get_metadata(0); connect_button->set_disabled(_is_connection_inherited(connection)); } else { connect_button->set_text(TTR("Connect...")); - connect_button->set_icon(get_editor_theme_icon(SNAME("Instance"))); + connect_button->set_button_icon(get_editor_theme_icon(SNAME("Instance"))); connect_button->set_disabled(true); } } @@ -1586,7 +1586,7 @@ void ConnectionsDock::update_tree() { } connect_button->set_text(TTR("Connect...")); - connect_button->set_icon(get_editor_theme_icon(SNAME("Instance"))); + connect_button->set_button_icon(get_editor_theme_icon(SNAME("Instance"))); connect_button->set_disabled(true); } diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index 330ac3b437..2273014f72 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -160,13 +160,13 @@ bool CreateDialog::_should_hide_type(const StringName &p_type) const { String script_path = ScriptServer::get_global_class_path(p_type); if (script_path.begins_with("res://addons/")) { - int i = script_path.find("/", 13); // 13 is length of "res://addons/". + int i = script_path.find_char('/', 13); // 13 is length of "res://addons/". while (i > -1) { const String plugin_path = script_path.substr(0, i).path_join("plugin.cfg"); if (FileAccess::exists(plugin_path)) { return !EditorNode::get_singleton()->is_addon_plugin_enabled(plugin_path); } - i = script_path.find("/", i + 1); + i = script_path.find_char('/', i + 1); } } } @@ -468,7 +468,7 @@ void CreateDialog::_notification(int p_what) { recent->set_fixed_icon_size(Size2(icon_width, icon_width)); search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - favorite->set_icon(get_editor_theme_icon(SNAME("Favorites"))); + favorite->set_button_icon(get_editor_theme_icon(SNAME("Favorites"))); } break; } } @@ -613,7 +613,7 @@ Variant CreateDialog::get_drag_data_fw(const Point2 &p_point, Control *p_from) { Button *tb = memnew(Button); tb->set_flat(true); - tb->set_icon(ti->get_icon(0)); + tb->set_button_icon(ti->get_icon(0)); tb->set_text(ti->get_text(0)); tb->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); favorites->set_drag_preview(tb); diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp index 2af629676a..904085630b 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp @@ -147,7 +147,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) { String breakpoint = E->get(); - String path = breakpoint.left(breakpoint.find(":", 6)); // Skip initial part of path, aka "res://" + String path = breakpoint.left(breakpoint.find_char(':', 6)); // Skip initial part of path, aka "res://" int line = breakpoint.substr(path.size()).to_int(); DebugAdapterProtocol::get_singleton()->on_debug_breakpoint_toggled(path, line, true); diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h index fac9525943..860c2bf582 100644 --- a/editor/debugger/editor_debugger_inspector.h +++ b/editor/debugger/editor_debugger_inspector.h @@ -48,7 +48,7 @@ public: List<PropertyInfo> prop_list; HashMap<StringName, Variant> prop_values; - ObjectID get_remote_object_id() { return remote_object_id; }; + ObjectID get_remote_object_id() { return remote_object_id; } String get_title(); Variant get_variant(const StringName &p_name); diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index b4265f9fc0..f06d9be78f 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -105,6 +105,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() { node->connect("breakpoint_selected", callable_mp(this, &EditorDebuggerNode::_error_selected).bind(id)); node->connect("clear_execution", callable_mp(this, &EditorDebuggerNode::_clear_execution)); node->connect("breaked", callable_mp(this, &EditorDebuggerNode::_breaked).bind(id)); + node->connect("remote_tree_select_requested", callable_mp(this, &EditorDebuggerNode::_remote_tree_select_requested).bind(id)); node->connect("remote_tree_updated", callable_mp(this, &EditorDebuggerNode::_remote_tree_updated).bind(id)); node->connect("remote_object_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_updated).bind(id)); node->connect("remote_object_property_updated", callable_mp(this, &EditorDebuggerNode::_remote_object_property_updated).bind(id)); @@ -119,7 +120,7 @@ ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() { tabs->add_child(node); - node->set_name("Session " + itos(tabs->get_tab_count())); + node->set_name(vformat(TTR("Session %d"), tabs->get_tab_count())); if (tabs->get_tab_count() > 1) { node->clear_style(); tabs->set_tabs_visible(true); @@ -159,9 +160,9 @@ void EditorDebuggerNode::_text_editor_stack_goto(const ScriptEditorDebugger *p_d } else { // If the script is built-in, it can be opened only if the scene is loaded in memory. int i = file.find("::"); - int j = file.rfind("(", i); + int j = file.rfind_char('(', i); if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path. - file = file.substr(j + 1, file.find(")", i) - j - 1); + file = file.substr(j + 1, file.find_char(')', i) - j - 1); } Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0)); stack_script = ResourceLoader::load(file); @@ -182,9 +183,9 @@ void EditorDebuggerNode::_text_editor_stack_clear(const ScriptEditorDebugger *p_ } else { // If the script is built-in, it can be opened only if the scene is loaded in memory. int i = file.find("::"); - int j = file.rfind("(", i); + int j = file.rfind_char('(', i); if (j > -1) { // If the script is named, the string is "name (file)", so we need to extract the path. - file = file.substr(j + 1, file.find(")", i) - j - 1); + file = file.substr(j + 1, file.find_char(')', i) - j - 1); } Ref<PackedScene> ps = ResourceLoader::load(file.get_slice("::", 0)); stack_script = ResourceLoader::load(file); @@ -417,18 +418,18 @@ void EditorDebuggerNode::_update_errors() { if (error_count == 0 && warning_count == 0) { debugger_button->set_text(TTR("Debugger")); debugger_button->remove_theme_color_override(SceneStringName(font_color)); - debugger_button->set_icon(Ref<Texture2D>()); + debugger_button->set_button_icon(Ref<Texture2D>()); } else { debugger_button->set_text(TTR("Debugger") + " (" + itos(error_count + warning_count) + ")"); if (error_count >= 1 && warning_count >= 1) { - debugger_button->set_icon(get_editor_theme_icon(SNAME("ErrorWarning"))); + debugger_button->set_button_icon(get_editor_theme_icon(SNAME("ErrorWarning"))); // Use error color to represent the highest level of severity reported. debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); } else if (error_count >= 1) { - debugger_button->set_icon(get_editor_theme_icon(SNAME("Error"))); + debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Error"))); debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); } else { - debugger_button->set_icon(get_editor_theme_icon(SNAME("Warning"))); + debugger_button->set_button_icon(get_editor_theme_icon(SNAME("Warning"))); debugger_button->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); } } @@ -637,6 +638,13 @@ void EditorDebuggerNode::request_remote_tree() { get_current_debugger()->request_remote_tree(); } +void EditorDebuggerNode::_remote_tree_select_requested(ObjectID p_id, int p_debugger) { + if (p_debugger != tabs->get_current_tab()) { + return; + } + remote_scene_tree->select_node(p_id); +} + void EditorDebuggerNode::_remote_tree_updated(int p_debugger) { if (p_debugger != tabs->get_current_tab()) { return; diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h index 12e097f652..12c0d30c42 100644 --- a/editor/debugger/editor_debugger_node.h +++ b/editor/debugger/editor_debugger_node.h @@ -51,11 +51,8 @@ class EditorDebuggerNode : public MarginContainer { public: enum CameraOverride { OVERRIDE_NONE, - OVERRIDE_2D, - OVERRIDE_3D_1, // 3D Viewport 1 - OVERRIDE_3D_2, // 3D Viewport 2 - OVERRIDE_3D_3, // 3D Viewport 3 - OVERRIDE_3D_4 // 3D Viewport 4 + OVERRIDE_INGAME, + OVERRIDE_EDITORS, }; private: @@ -132,6 +129,7 @@ protected: void _debugger_stopped(int p_id); void _debugger_wants_stop(int p_id); void _debugger_changed(int p_tab); + void _remote_tree_select_requested(ObjectID p_id, int p_debugger); void _remote_tree_updated(int p_debugger); void _remote_tree_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _remote_object_updated(ObjectID p_id, int p_debugger); diff --git a/editor/debugger/editor_debugger_tree.cpp b/editor/debugger/editor_debugger_tree.cpp index a900842651..a9e4adf674 100644 --- a/editor/debugger/editor_debugger_tree.cpp +++ b/editor/debugger/editor_debugger_tree.cpp @@ -30,6 +30,7 @@ #include "editor_debugger_tree.h" +#include "editor/debugger/editor_debugger_node.h" #include "editor/editor_node.h" #include "editor/editor_string_names.h" #include "editor/gui/editor_file_dialog.h" @@ -148,7 +149,8 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int updating_scene_tree = true; const String last_path = get_selected_path(); const String filter = SceneTreeDock::get_singleton()->get_filter(); - bool filter_changed = filter != last_filter; + bool should_scroll = scrolling_to_item || filter != last_filter; + scrolling_to_item = false; TreeItem *scroll_item = nullptr; // Nodes are in a flatten list, depth first. Use a stack of parents, avoid recursion. @@ -185,8 +187,18 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int // Select previously selected node. if (debugger_id == p_debugger) { // Can use remote id. if (node.id == inspected_object_id) { + if (selection_uncollapse_all) { + selection_uncollapse_all = false; + + // Temporarily set to `false`, to allow caching the unfolds. + updating_scene_tree = false; + item->uncollapse_tree(); + updating_scene_tree = true; + } + item->select(0); - if (filter_changed) { + + if (should_scroll) { scroll_item = item; } } @@ -194,7 +206,7 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int if (last_path == _get_path(item)) { updating_scene_tree = false; // Force emission of new selection. item->select(0); - if (filter_changed) { + if (should_scroll) { scroll_item = item; } updating_scene_tree = true; @@ -258,14 +270,30 @@ void EditorDebuggerTree::update_scene_tree(const SceneDebuggerTree *p_tree, int } } } - debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree + + debugger_id = p_debugger; // Needed by hook, could be avoided if every debugger had its own tree. if (scroll_item) { - callable_mp((Tree *)this, &Tree::scroll_to_item).call_deferred(scroll_item, false); + scroll_to_item(scroll_item, false); } last_filter = filter; updating_scene_tree = false; } +void EditorDebuggerTree::select_node(ObjectID p_id) { + // Manually select, as the tree control may be out-of-date for some reason (e.g. not shown yet). + selection_uncollapse_all = true; + inspected_object_id = uint64_t(p_id); + scrolling_to_item = true; + emit_signal(SNAME("object_selected"), inspected_object_id, debugger_id); + + if (!updating_scene_tree) { + // Request a tree refresh. + EditorDebuggerNode::get_singleton()->request_remote_tree(); + } + // Set the value immediately, so no update flooding happens and causes a crash. + updating_scene_tree = true; +} + Variant EditorDebuggerTree::get_drag_data(const Point2 &p_point) { if (get_button_id_at_position(p_point) != -1) { return Variant(); @@ -354,7 +382,7 @@ void EditorDebuggerTree::_item_menu_id_pressed(int p_option) { text = "."; } else { text = text.replace("/root/", ""); - int slash = text.find("/"); + int slash = text.find_char('/'); if (slash < 0) { text = "."; } else { diff --git a/editor/debugger/editor_debugger_tree.h b/editor/debugger/editor_debugger_tree.h index 705df17baf..d048688cad 100644 --- a/editor/debugger/editor_debugger_tree.h +++ b/editor/debugger/editor_debugger_tree.h @@ -49,6 +49,8 @@ private: ObjectID inspected_object_id; int debugger_id = 0; bool updating_scene_tree = false; + bool scrolling_to_item = false; + bool selection_uncollapse_all = false; HashSet<ObjectID> unfold_cache; PopupMenu *item_menu = nullptr; EditorFileDialog *file_dialog = nullptr; @@ -78,6 +80,7 @@ public: ObjectID get_selected_object(); int get_current_debugger(); // Would love to have one tree for every debugger. void update_scene_tree(const SceneDebuggerTree *p_tree, int p_debugger); + void select_node(ObjectID p_id); EditorDebuggerTree(); }; diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index d244b6b4cd..33fa208f70 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -30,6 +30,7 @@ #include "editor_profiler.h" +#include "core/io/image.h" #include "core/os/os.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -389,10 +390,10 @@ void EditorProfiler::_update_frame() { void EditorProfiler::_update_button_text() { if (activate->is_pressed()) { - activate->set_icon(get_editor_theme_icon(SNAME("Stop"))); + activate->set_button_icon(get_editor_theme_icon(SNAME("Stop"))); activate->set_text(TTR("Stop")); } else { - activate->set_icon(get_editor_theme_icon(SNAME("Play"))); + activate->set_button_icon(get_editor_theme_icon(SNAME("Play"))); activate->set_text(TTR("Start")); } } @@ -427,8 +428,8 @@ void EditorProfiler::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { - activate->set_icon(get_editor_theme_icon(SNAME("Play"))); - clear_button->set_icon(get_editor_theme_icon(SNAME("Clear"))); + activate->set_button_icon(get_editor_theme_icon(SNAME("Play"))); + clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear"))); theme_cache.seek_line_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor)); theme_cache.seek_line_color.a = 0.8; diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 7b831a1c8b..9a83277e99 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -30,6 +30,7 @@ #include "editor_visual_profiler.h" +#include "core/io/image.h" #include "core/os/os.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -410,12 +411,12 @@ void EditorVisualProfiler::_update_frame(bool p_focus_selected) { void EditorVisualProfiler::_activate_pressed() { if (activate->is_pressed()) { - activate->set_icon(get_editor_theme_icon(SNAME("Stop"))); + activate->set_button_icon(get_editor_theme_icon(SNAME("Stop"))); activate->set_text(TTR("Stop")); _clear_pressed(); //always clear on start clear_button->set_disabled(false); } else { - activate->set_icon(get_editor_theme_icon(SNAME("Play"))); + activate->set_button_icon(get_editor_theme_icon(SNAME("Play"))); activate->set_text(TTR("Start")); } emit_signal(SNAME("enable_profiling"), activate->is_pressed()); @@ -437,8 +438,8 @@ void EditorVisualProfiler::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { - activate->set_icon(get_editor_theme_icon(SNAME("Play"))); - clear_button->set_icon(get_editor_theme_icon(SNAME("Clear"))); + activate->set_button_icon(get_editor_theme_icon(SNAME("Play"))); + clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear"))); } break; } } @@ -656,10 +657,10 @@ void EditorVisualProfiler::_bind_methods() { void EditorVisualProfiler::_update_button_text() { if (activate->is_pressed()) { - activate->set_icon(get_editor_theme_icon(SNAME("Stop"))); + activate->set_button_icon(get_editor_theme_icon(SNAME("Stop"))); activate->set_text(TTR("Stop")); } else { - activate->set_icon(get_editor_theme_icon(SNAME("Play"))); + activate->set_button_icon(get_editor_theme_icon(SNAME("Play"))); activate->set_text(TTR("Start")); } } diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index cbe7910518..da59450dd0 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -95,9 +95,9 @@ void ScriptEditorDebugger::debug_copy() { void ScriptEditorDebugger::debug_skip_breakpoints() { skip_breakpoints_value = !skip_breakpoints_value; if (skip_breakpoints_value) { - skip_breakpoints->set_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn"))); + skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOn"))); } else { - skip_breakpoints->set_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff"))); + skip_breakpoints->set_button_icon(get_editor_theme_icon(SNAME("DebugSkipBreakpointsOff"))); } Array msg; @@ -806,6 +806,10 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread } else if (p_msg == "request_quit") { emit_signal(SNAME("stop_requested")); _stop_and_notify(); + } else if (p_msg == "remote_node_clicked") { + if (!p_data.is_empty()) { + emit_signal(SNAME("remote_tree_select_requested"), p_data[0]); + } } else if (p_msg == "performance:profile_names") { Vector<StringName> monitors; monitors.resize(p_data.size()); @@ -827,7 +831,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread bool parsed = EditorDebuggerNode::get_singleton()->plugins_capture(this, p_msg, p_data); if (!parsed) { - WARN_PRINT("unknown message " + p_msg); + WARN_PRINT("Unknown message: " + p_msg); } } } @@ -870,14 +874,14 @@ void ScriptEditorDebugger::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { tabs->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DebuggerPanel"), EditorStringName(EditorStyles))); - skip_breakpoints->set_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff"))); - copy->set_icon(get_editor_theme_icon(SNAME("ActionCopy"))); - step->set_icon(get_editor_theme_icon(SNAME("DebugStep"))); - next->set_icon(get_editor_theme_icon(SNAME("DebugNext"))); - dobreak->set_icon(get_editor_theme_icon(SNAME("Pause"))); - docontinue->set_icon(get_editor_theme_icon(SNAME("DebugContinue"))); - vmem_refresh->set_icon(get_editor_theme_icon(SNAME("Reload"))); - vmem_export->set_icon(get_editor_theme_icon(SNAME("Save"))); + skip_breakpoints->set_button_icon(get_editor_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff"))); + copy->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy"))); + step->set_button_icon(get_editor_theme_icon(SNAME("DebugStep"))); + next->set_button_icon(get_editor_theme_icon(SNAME("DebugNext"))); + dobreak->set_button_icon(get_editor_theme_icon(SNAME("Pause"))); + docontinue->set_button_icon(get_editor_theme_icon(SNAME("DebugContinue"))); + vmem_refresh->set_button_icon(get_editor_theme_icon(SNAME("Reload"))); + vmem_export->set_button_icon(get_editor_theme_icon(SNAME("Save"))); search->set_right_icon(get_editor_theme_icon(SNAME("Search"))); reason->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); @@ -905,37 +909,42 @@ void ScriptEditorDebugger::_notification(int p_what) { if (is_session_active()) { peer->poll(); - if (camera_override == CameraOverride::OVERRIDE_2D) { - Dictionary state = CanvasItemEditor::get_singleton()->get_state(); - float zoom = state["zoom"]; - Point2 offset = state["ofs"]; - Transform2D transform; - - transform.scale_basis(Size2(zoom, zoom)); - transform.columns[2] = -offset * zoom; - - Array msg; - msg.push_back(transform); - _put_msg("scene:override_camera_2D:transform", msg); - - } else if (camera_override >= CameraOverride::OVERRIDE_3D_1) { - int viewport_idx = camera_override - CameraOverride::OVERRIDE_3D_1; - Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(viewport_idx); - Camera3D *const cam = viewport->get_camera_3d(); - - Array msg; - msg.push_back(cam->get_camera_transform()); - if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) { - msg.push_back(false); - msg.push_back(cam->get_size()); - } else { - msg.push_back(true); - msg.push_back(cam->get_fov()); + if (camera_override == CameraOverride::OVERRIDE_EDITORS) { + // CanvasItem Editor + { + Dictionary state = CanvasItemEditor::get_singleton()->get_state(); + float zoom = state["zoom"]; + Point2 offset = state["ofs"]; + Transform2D transform; + + transform.scale_basis(Size2(zoom, zoom)); + transform.columns[2] = -offset * zoom; + + Array msg; + msg.push_back(transform); + _put_msg("scene:transform_camera_2d", msg); + } + + // Node3D Editor + { + Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_last_used_viewport(); + const Camera3D *cam = viewport->get_camera_3d(); + + Array msg; + msg.push_back(cam->get_camera_transform()); + if (cam->get_projection() == Camera3D::PROJECTION_ORTHOGONAL) { + msg.push_back(false); + msg.push_back(cam->get_size()); + } else { + msg.push_back(true); + msg.push_back(cam->get_fov()); + } + msg.push_back(cam->get_near()); + msg.push_back(cam->get_far()); + _put_msg("scene:transform_camera_3d", msg); } - msg.push_back(cam->get_near()); - msg.push_back(cam->get_far()); - _put_msg("scene:override_camera_3D:transform", msg); } + if (is_breaked() && can_request_idle_draw) { _put_msg("servers:draw", Array()); can_request_idle_draw = false; @@ -1024,6 +1033,9 @@ void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { _update_buttons_state(); emit_signal(SNAME("started")); + Array quit_keys = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("editor/stop_running_project")); + _put_msg("scene:setup_scene", quit_keys); + if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) { profiler->set_profiling(true); } @@ -1161,6 +1173,12 @@ String ScriptEditorDebugger::get_var_value(const String &p_var) const { return inspector->get_stack_variable(p_var); } +void ScriptEditorDebugger::_resources_reimported(const PackedStringArray &p_resources) { + Array msg; + msg.push_back(p_resources); + _put_msg("scene:reload_cached_files", msg); +} + int ScriptEditorDebugger::_get_node_path_cache(const NodePath &p_path) { const int *r = node_path_cache.getptr(p_path); if (r) { @@ -1469,23 +1487,10 @@ CameraOverride ScriptEditorDebugger::get_camera_override() const { } void ScriptEditorDebugger::set_camera_override(CameraOverride p_override) { - if (p_override == CameraOverride::OVERRIDE_2D && camera_override != CameraOverride::OVERRIDE_2D) { - Array msg; - msg.push_back(true); - _put_msg("scene:override_camera_2D:set", msg); - } else if (p_override != CameraOverride::OVERRIDE_2D && camera_override == CameraOverride::OVERRIDE_2D) { - Array msg; - msg.push_back(false); - _put_msg("scene:override_camera_2D:set", msg); - } else if (p_override >= CameraOverride::OVERRIDE_3D_1 && camera_override < CameraOverride::OVERRIDE_3D_1) { - Array msg; - msg.push_back(true); - _put_msg("scene:override_camera_3D:set", msg); - } else if (p_override < CameraOverride::OVERRIDE_3D_1 && camera_override >= CameraOverride::OVERRIDE_3D_1) { - Array msg; - msg.push_back(false); - _put_msg("scene:override_camera_3D:set", msg); - } + Array msg; + msg.push_back(p_override != CameraOverride::OVERRIDE_NONE); + msg.push_back(p_override == CameraOverride::OVERRIDE_EDITORS); + _put_msg("scene:override_cameras", msg); camera_override = p_override; } @@ -1776,6 +1781,7 @@ void ScriptEditorDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("remote_object_updated", PropertyInfo(Variant::INT, "id"))); ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property"))); ADD_SIGNAL(MethodInfo("remote_tree_updated")); + ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::NODE_PATH, "path"))); ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level"))); ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump"))); ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars"))); @@ -1821,6 +1827,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { tabs->connect("tab_changed", callable_mp(this, &ScriptEditorDebugger::_tab_changed)); InspectorDock::get_inspector_singleton()->connect("object_id_selected", callable_mp(this, &ScriptEditorDebugger::_remote_object_selected)); + EditorFileSystem::get_singleton()->connect("resources_reimported", callable_mp(this, &ScriptEditorDebugger::_resources_reimported)); { //debugger VBoxContainer *vbc = memnew(VBoxContainer); diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index 1908b1e5a7..06a968e141 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -198,6 +198,8 @@ private: void _video_mem_request(); void _video_mem_export(); + void _resources_reimported(const PackedStringArray &p_resources); + int _get_node_path_cache(const NodePath &p_path); int _get_res_path_cache(const String &p_path); diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp index 8ba5811ffa..9ca12070fe 100644 --- a/editor/dependency_editor.cpp +++ b/editor/dependency_editor.cpp @@ -471,7 +471,7 @@ void DependencyRemoveDialog::_find_localization_remaps_of_removed_files(Vector<R for (int j = 0; j < remap_keys.size(); j++) { PackedStringArray remapped_files = remaps[remap_keys[j]]; for (int k = 0; k < remapped_files.size(); k++) { - int splitter_pos = remapped_files[k].rfind(":"); + int splitter_pos = remapped_files[k].rfind_char(':'); String res_path = remapped_files[k].substr(0, splitter_pos); if (res_path == path) { String locale_name = remapped_files[k].substr(splitter_pos + 1); diff --git a/editor/directory_create_dialog.cpp b/editor/directory_create_dialog.cpp index ee03d5a7f6..d68af88fc8 100644 --- a/editor/directory_create_dialog.cpp +++ b/editor/directory_create_dialog.cpp @@ -142,7 +142,7 @@ void DirectoryCreateDialog::config(const String &p_base_dir, const Callable &p_a validation_panel->update(); if (p_mode == MODE_FILE) { - int extension_pos = p_default_name.rfind("."); + int extension_pos = p_default_name.rfind_char('.'); if (extension_pos > -1) { dir_path->select(0, extension_pos); return; diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp index 79e0c7ebd1..842c4acce0 100644 --- a/editor/doc_tools.cpp +++ b/editor/doc_tools.cpp @@ -908,6 +908,23 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) { c.properties.sort(); + List<StringName> enums; + Variant::get_enums_for_type(Variant::Type(i), &enums); + + for (const StringName &E : enums) { + List<StringName> enumerations; + Variant::get_enumerations_for_enum(Variant::Type(i), E, &enumerations); + + for (const StringName &F : enumerations) { + DocData::ConstantDoc constant; + constant.name = F; + constant.value = itos(Variant::get_enum_value(Variant::Type(i), E, F)); + constant.is_value_valid = true; + constant.enumeration = E; + c.constants.push_back(constant); + } + } + List<StringName> constants; Variant::get_constants_for_type(Variant::Type(i), &constants); diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp index 1e44a9bdc9..72755e8943 100644 --- a/editor/editor_asset_installer.cpp +++ b/editor/editor_asset_installer.cpp @@ -130,14 +130,14 @@ void EditorAssetInstaller::open_asset(const String &p_path, bool p_autoskip_topl // Create intermediate directories if they aren't reported by unzip. // We are only interested in subfolders, so skip the root slash. - int separator = source_name.find("/", 1); + int separator = source_name.find_char('/', 1); while (separator != -1) { String dir_name = source_name.substr(0, separator + 1); if (!dir_name.is_empty() && !asset_files.has(dir_name)) { asset_files.insert(dir_name); } - separator = source_name.find("/", separator + 1); + separator = source_name.find_char('/', separator + 1); } if (!source_name.is_empty() && !asset_files.has(source_name)) { @@ -214,7 +214,7 @@ void EditorAssetInstaller::_rebuild_source_tree() { TreeItem *parent_item; - int separator = path.rfind("/"); + int separator = path.rfind_char('/'); if (separator == -1) { parent_item = root; } else { @@ -313,7 +313,7 @@ void EditorAssetInstaller::_rebuild_destination_tree() { TreeItem *parent_item; - int separator = path.rfind("/"); + int separator = path.rfind_char('/'); if (separator == -1) { parent_item = root; } else { @@ -411,9 +411,9 @@ void EditorAssetInstaller::_toggle_source_tree(bool p_visible, bool p_scroll_to_ show_source_files_button->set_pressed_no_signal(p_visible); // To keep in sync if triggered by something else. if (p_visible) { - show_source_files_button->set_icon(get_editor_theme_icon(SNAME("Back"))); + show_source_files_button->set_button_icon(get_editor_theme_icon(SNAME("Back"))); } else { - show_source_files_button->set_icon(get_editor_theme_icon(SNAME("Forward"))); + show_source_files_button->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); } if (p_visible && p_scroll_to_error && first_file_conflict) { @@ -597,9 +597,9 @@ void EditorAssetInstaller::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { if (show_source_files_button->is_pressed()) { - show_source_files_button->set_icon(get_editor_theme_icon(SNAME("Back"))); + show_source_files_button->set_button_icon(get_editor_theme_icon(SNAME("Back"))); } else { - show_source_files_button->set_icon(get_editor_theme_icon(SNAME("Forward"))); + show_source_files_button->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); } asset_conflicts_link->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index 24fc7a9c2b..0649272216 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -91,19 +91,28 @@ void EditorAudioBus::_notification(int p_what) { Color mute_color = EditorThemeManager::is_dark_theme() ? Color(1.0, 0.16, 0.16) : Color(2.35, 1.03, 1.03); Color bypass_color = EditorThemeManager::is_dark_theme() ? Color(0.13, 0.8, 1.0) : Color(1.03, 2.04, 2.35); float darkening_factor = EditorThemeManager::is_dark_theme() ? 0.15 : 0.65; - - Ref<StyleBoxFlat>(solo->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(solo_color.darkened(darkening_factor)); - Ref<StyleBoxFlat>(mute->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(mute_color.darkened(darkening_factor)); - Ref<StyleBoxFlat>(bypass->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(bypass_color.darkened(darkening_factor)); - - solo->set_icon(get_editor_theme_icon(SNAME("AudioBusSolo"))); + Color solo_color_darkened = solo_color.darkened(darkening_factor); + Color mute_color_darkened = mute_color.darkened(darkening_factor); + Color bypass_color_darkened = bypass_color.darkened(darkening_factor); + + Ref<StyleBoxFlat>(solo->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(solo_color_darkened); + Ref<StyleBoxFlat>(mute->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(mute_color_darkened); + Ref<StyleBoxFlat>(bypass->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(bypass_color_darkened); + Ref<StyleBoxFlat>(solo->get_theme_stylebox("hover_pressed"))->set_border_color(solo_color_darkened); + Ref<StyleBoxFlat>(mute->get_theme_stylebox("hover_pressed"))->set_border_color(mute_color_darkened); + Ref<StyleBoxFlat>(bypass->get_theme_stylebox("hover_pressed"))->set_border_color(bypass_color_darkened); + + solo->set_button_icon(get_editor_theme_icon(SNAME("AudioBusSolo"))); solo->add_theme_color_override("icon_pressed_color", solo_color); - mute->set_icon(get_editor_theme_icon(SNAME("AudioBusMute"))); + solo->add_theme_color_override("icon_hover_pressed_color", solo_color_darkened); + mute->set_button_icon(get_editor_theme_icon(SNAME("AudioBusMute"))); mute->add_theme_color_override("icon_pressed_color", mute_color); - bypass->set_icon(get_editor_theme_icon(SNAME("AudioBusBypass"))); + mute->add_theme_color_override("icon_hover_pressed_color", mute_color_darkened); + bypass->set_button_icon(get_editor_theme_icon(SNAME("AudioBusBypass"))); bypass->add_theme_color_override("icon_pressed_color", bypass_color); + bypass->add_theme_color_override("icon_hover_pressed_color", bypass_color_darkened); - bus_options->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + bus_options->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); audio_value_preview_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SceneStringName(font_color), SNAME("TooltipLabel"))); audio_value_preview_label->add_theme_color_override("font_shadow_color", get_theme_color(SNAME("font_shadow_color"), SNAME("TooltipLabel"))); @@ -841,13 +850,18 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { child->begin_bulk_theme_override(); child->add_theme_style_override(CoreStringName(normal), sbempty); child->add_theme_style_override("hover", sbempty); + child->add_theme_style_override("hover_mirrored", sbempty); child->add_theme_style_override("focus", sbempty); + child->add_theme_style_override("focus_mirrored", sbempty); Ref<StyleBoxFlat> sbflat = memnew(StyleBoxFlat); sbflat->set_content_margin_all(0); sbflat->set_bg_color(Color(1, 1, 1, 0)); sbflat->set_border_width(Side::SIDE_BOTTOM, Math::round(3 * EDSCALE)); child->add_theme_style_override(SceneStringName(pressed), sbflat); + child->add_theme_style_override("pressed_mirrored", sbflat); + child->add_theme_style_override("hover_pressed", sbflat); + child->add_theme_style_override("hover_pressed_mirrored", sbflat); child->end_bulk_theme_override(); } diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index 1613d1d62f..23a2f5b13c 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -55,12 +55,12 @@ void EditorAutoloadSettings::_notification(int p_what) { file_dialog->add_filter("*." + E); } - browse_button->set_icon(get_editor_theme_icon(SNAME("Folder"))); + browse_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); } break; case NOTIFICATION_THEME_CHANGED: { - browse_button->set_icon(get_editor_theme_icon(SNAME("Folder"))); - add_autoload->set_icon(get_editor_theme_icon(SNAME("Add"))); + browse_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); + add_autoload->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index ee16c61c89..bb02172b1a 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -547,6 +547,7 @@ Variant EditorData::instantiate_custom_type(const String &p_type, const String & if (n) { n->set_name(p_type); } + n->set_meta(SceneStringName(_custom_type_script), script); ((Object *)ob)->set_script(script); return ob; } @@ -1008,6 +1009,7 @@ Variant EditorData::script_class_instance(const String &p_class) { // Store in a variant to initialize the refcount if needed. Variant obj = ClassDB::instantiate(script->get_instance_base_type()); if (obj) { + Object::cast_to<Object>(obj)->set_meta(SceneStringName(_custom_type_script), script); obj.operator Object *()->set_script(script); } return obj; diff --git a/editor/editor_dock_manager.cpp b/editor/editor_dock_manager.cpp index 0bdda41f26..1db073ec81 100644 --- a/editor/editor_dock_manager.cpp +++ b/editor/editor_dock_manager.cpp @@ -852,21 +852,21 @@ void DockContextPopup::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { if (make_float_button) { - make_float_button->set_icon(get_editor_theme_icon(SNAME("MakeFloating"))); + make_float_button->set_button_icon(get_editor_theme_icon(SNAME("MakeFloating"))); } if (is_layout_rtl()) { - tab_move_left_button->set_icon(get_editor_theme_icon(SNAME("Forward"))); - tab_move_right_button->set_icon(get_editor_theme_icon(SNAME("Back"))); + tab_move_left_button->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); + tab_move_right_button->set_button_icon(get_editor_theme_icon(SNAME("Back"))); tab_move_left_button->set_tooltip_text(TTR("Move this dock right one tab.")); tab_move_right_button->set_tooltip_text(TTR("Move this dock left one tab.")); } else { - tab_move_left_button->set_icon(get_editor_theme_icon(SNAME("Back"))); - tab_move_right_button->set_icon(get_editor_theme_icon(SNAME("Forward"))); + tab_move_left_button->set_button_icon(get_editor_theme_icon(SNAME("Back"))); + tab_move_right_button->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); tab_move_left_button->set_tooltip_text(TTR("Move this dock left one tab.")); tab_move_right_button->set_tooltip_text(TTR("Move this dock right one tab.")); } - dock_to_bottom_button->set_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide"))); - close_button->set_icon(get_editor_theme_icon(SNAME("Close"))); + dock_to_bottom_button->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide"))); + close_button->set_button_icon(get_editor_theme_icon(SNAME("Close"))); } break; } } diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp index 44fc9e3702..9cf10c0ecb 100644 --- a/editor/editor_feature_profile.cpp +++ b/editor/editor_feature_profile.cpp @@ -49,6 +49,7 @@ const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = { TTRC("FileSystem Dock"), TTRC("Import Dock"), TTRC("History Dock"), + TTRC("Game View"), }; const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = { @@ -60,6 +61,7 @@ const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = { TTRC("Allows to browse the local file system via a dedicated dock."), TTRC("Allows to configure import settings for individual assets. Requires the FileSystem dock to function."), TTRC("Provides an overview of the editor's and each scene's undo history."), + TTRC("Provides tools for selecting and debugging nodes at runtime."), }; const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = { @@ -71,6 +73,7 @@ const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = { "filesystem_dock", "import_dock", "history_dock", + "game", }; void EditorFeatureProfile::set_disable_class(const StringName &p_class, bool p_disabled) { @@ -307,6 +310,7 @@ void EditorFeatureProfile::_bind_methods() { BIND_ENUM_CONSTANT(FEATURE_FILESYSTEM_DOCK); BIND_ENUM_CONSTANT(FEATURE_IMPORT_DOCK); BIND_ENUM_CONSTANT(FEATURE_HISTORY_DOCK); + BIND_ENUM_CONSTANT(FEATURE_GAME); BIND_ENUM_CONSTANT(FEATURE_MAX); } diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h index 7458a04e19..e84936dd34 100644 --- a/editor/editor_feature_profile.h +++ b/editor/editor_feature_profile.h @@ -55,6 +55,7 @@ public: FEATURE_FILESYSTEM_DOCK, FEATURE_IMPORT_DOCK, FEATURE_HISTORY_DOCK, + FEATURE_GAME, FEATURE_MAX }; diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 2d1a914120..b5306154ba 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -248,11 +248,16 @@ void EditorFileSystem::_first_scan_filesystem() { ep.step(TTR("Scanning file structure..."), 0, true); nb_files_total = _scan_new_dir(first_scan_root_dir, d); + // Preloading GDExtensions file extensions to prevent looping on all the resource loaders + // for each files in _first_scan_process_scripts. + List<String> gdextension_extensions; + ResourceLoader::get_recognized_extensions_for_type("GDExtension", &gdextension_extensions); + // This loads the global class names from the scripts and ensures that even if the // global_script_class_cache.cfg was missing or invalid, the global class names are valid in ScriptServer. // At the same time, to prevent looping multiple times in all files, it looks for extensions. ep.step(TTR("Loading global class names..."), 1, true); - _first_scan_process_scripts(first_scan_root_dir, existing_class_names, extensions); + _first_scan_process_scripts(first_scan_root_dir, gdextension_extensions, existing_class_names, extensions); // Removing invalid global class to prevent having invalid paths in ScriptServer. _remove_invalid_global_class_names(existing_class_names); @@ -276,16 +281,16 @@ void EditorFileSystem::_first_scan_filesystem() { ep.step(TTR("Starting file scan..."), 5, true); } -void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions) { +void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_scan_dir, List<String> &p_gdextension_extensions, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions) { for (ScannedDirectory *scan_sub_dir : p_scan_dir->subdirs) { - _first_scan_process_scripts(scan_sub_dir, p_existing_class_names, p_extensions); + _first_scan_process_scripts(scan_sub_dir, p_gdextension_extensions, p_existing_class_names, p_extensions); } for (const String &scan_file : p_scan_dir->files) { // Optimization to skip the ResourceLoader::get_resource_type for files // that are not scripts. Some loader get_resource_type methods read the file // which can be very slow on large projects. - String ext = scan_file.get_extension().to_lower(); + const String ext = scan_file.get_extension().to_lower(); bool is_script = false; for (int i = 0; i < ScriptServer::get_language_count(); i++) { if (ScriptServer::get_language(i)->get_extension() == ext) { @@ -293,24 +298,29 @@ void EditorFileSystem::_first_scan_process_scripts(const ScannedDirectory *p_sca break; } } - if (!is_script) { - continue; // Not a script. - } + if (is_script) { + const String path = p_scan_dir->full_path.path_join(scan_file); + const String type = ResourceLoader::get_resource_type(path); - String path = p_scan_dir->full_path.path_join(scan_file); - String type = ResourceLoader::get_resource_type(path); + if (ClassDB::is_parent_class(type, SNAME("Script"))) { + String script_class_extends; + String script_class_icon_path; + String script_class_name = _get_global_script_class(type, path, &script_class_extends, &script_class_icon_path); + _register_global_class_script(path, path, type, script_class_name, script_class_extends, script_class_icon_path); - if (ClassDB::is_parent_class(type, SNAME("Script"))) { - String script_class_extends; - String script_class_icon_path; - String script_class_name = _get_global_script_class(type, path, &script_class_extends, &script_class_icon_path); - _register_global_class_script(path, path, type, script_class_name, script_class_extends, script_class_icon_path); + if (!script_class_name.is_empty()) { + p_existing_class_names.insert(script_class_name); + } + } + } - if (!script_class_name.is_empty()) { - p_existing_class_names.insert(script_class_name); + // Check for GDExtensions. + if (p_gdextension_extensions.find(ext)) { + const String path = p_scan_dir->full_path.path_join(scan_file); + const String type = ResourceLoader::get_resource_type(path); + if (type == SNAME("GDExtension")) { + p_extensions.insert(path); } - } else if (type == SNAME("GDExtension")) { - p_extensions.insert(path); } } } @@ -1017,7 +1027,9 @@ void EditorFileSystem::scan() { void EditorFileSystem::ScanProgress::increment() { current++; float ratio = current / MAX(hi, 1.0f); - progress->step(ratio * 1000.0f); + if (progress) { + progress->step(ratio * 1000.0f); + } EditorFileSystem::singleton->scan_total = ratio; } @@ -1247,6 +1259,15 @@ void EditorFileSystem::_process_file_system(const ScannedDirectory *p_scan_dir, } } } + + if (fi->uid == ResourceUID::INVALID_ID && ResourceLoader::exists(path) && !ResourceLoader::has_custom_uid_support(path) && !FileAccess::exists(path + ".uid")) { + // Create a UID. + Ref<FileAccess> f = FileAccess::open(path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + fi->uid = ResourceUID::get_singleton()->create_id(); + f->store_line(ResourceUID::get_singleton()->id_to_text(fi->uid)); + } + } } if (fi->uid != ResourceUID::INVALID_ID) { @@ -1283,7 +1304,7 @@ void EditorFileSystem::_process_removed_files(const HashSet<String> &p_processed } } -void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress) { +void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive) { uint64_t current_mtime = FileAccess::get_modified_time(p_dir->get_path()); bool updated_dir = false; @@ -1477,7 +1498,9 @@ void EditorFileSystem::_scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanPr scan_actions.push_back(ia); continue; } - _scan_fs_changes(p_dir->get_subdir(i), p_progress); + if (p_recursive) { + _scan_fs_changes(p_dir->get_subdir(i), p_progress); + } } nb_files_total = MAX(nb_files_total + diff_nb_files, 0); @@ -1493,6 +1516,9 @@ void EditorFileSystem::_delete_internal_files(const String &p_file) { } da->remove(p_file + ".import"); } + if (FileAccess::exists(p_file + ".uid")) { + DirAccess::remove_absolute(p_file + ".uid"); + } } int EditorFileSystem::_insert_actions_delete_files_directory(EditorFileSystemDirectory *p_dir) { @@ -2362,10 +2388,18 @@ void EditorFileSystem::update_files(const Vector<String> &p_script_paths) { if (!is_scanning()) { _process_update_pending(); } - call_deferred(SNAME("emit_signal"), "filesystem_changed"); // Update later + if (!filesystem_changed_queued) { + filesystem_changed_queued = true; + callable_mp(this, &EditorFileSystem::_notify_filesystem_changed).call_deferred(); + } } } +void EditorFileSystem::_notify_filesystem_changed() { + emit_signal("filesystem_changed"); + filesystem_changed_queued = false; +} + HashSet<String> EditorFileSystem::get_valid_extensions() const { return valid_extensions; } @@ -2559,7 +2593,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector EditorFileSystemDirectory *fs = nullptr; int cpos = -1; bool found = _find_file(file, &fs, cpos); - ERR_FAIL_COND_V_MSG(!found, ERR_UNCONFIGURED, "Can't find file '" + file + "'."); + ERR_FAIL_COND_V_MSG(!found, ERR_UNCONFIGURED, vformat("Can't find file '%s' during group reimport.", file)); //update modified times, to avoid reimport fs->files[cpos]->modified_time = FileAccess::get_modified_time(file); @@ -2609,7 +2643,7 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin int cpos = -1; if (p_update_file_system) { bool found = _find_file(p_file, &fs, cpos); - ERR_FAIL_COND_V_MSG(!found, ERR_FILE_NOT_FOUND, "Can't find file '" + p_file + "'."); + ERR_FAIL_COND_V_MSG(!found, ERR_FILE_NOT_FOUND, vformat("Can't find file '%s' during file reimport.", p_file)); } //try to obtain existing params @@ -2718,13 +2752,17 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin } } + if (uid == ResourceUID::INVALID_ID) { + uid = ResourceUID::get_singleton()->create_id(); + } + //finally, perform import!! String base_path = ResourceFormatImporter::get_singleton()->get_import_base_path(p_file); List<String> import_variants; List<String> gen_files; Variant meta; - Error err = importer->import(p_file, base_path, params, &import_variants, &gen_files, &meta); + Error err = importer->import(uid, p_file, base_path, params, &import_variants, &gen_files, &meta); // As import is complete, save the .import file. @@ -2745,10 +2783,6 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin f->store_line("type=\"" + importer->get_resource_type() + "\""); } - if (uid == ResourceUID::INVALID_ID) { - uid = ResourceUID::get_singleton()->create_id(); - } - f->store_line("uid=\"" + ResourceUID::get_singleton()->id_to_text(uid) + "\""); // Store in readable format. if (err == OK) { @@ -2902,6 +2936,96 @@ void EditorFileSystem::reimport_file_with_custom_parameters(const String &p_file emit_signal(SNAME("resources_reimported"), reloads); } +Error EditorFileSystem::_copy_file(const String &p_from, const String &p_to) { + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); + if (FileAccess::exists(p_from + ".import")) { + Error err = da->copy(p_from, p_to); + if (err != OK) { + return err; + } + + // Remove uid from .import file to avoid conflict. + Ref<ConfigFile> cfg; + cfg.instantiate(); + cfg->load(p_from + ".import"); + cfg->erase_section_key("remap", "uid"); + err = cfg->save(p_to + ".import"); + if (err != OK) { + return err; + } + } else if (ResourceLoader::get_resource_uid(p_from) == ResourceUID::INVALID_ID) { + // Files which do not use an uid can just be copied. + Error err = da->copy(p_from, p_to); + if (err != OK) { + return err; + } + } else { + // Load the resource and save it again in the new location (this generates a new UID). + Error err; + Ref<Resource> res = ResourceLoader::load(p_from, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); + if (err == OK && res.is_valid()) { + err = ResourceSaver::save(res, p_to, ResourceSaver::FLAG_COMPRESS); + if (err != OK) { + return err; + } + } else if (err != OK) { + // When loading files like text files the error is OK but the resource is still null. + // We can ignore such files. + return err; + } + } + return OK; +} + +bool EditorFileSystem::_copy_directory(const String &p_from, const String &p_to, List<CopiedFile> *p_files) { + Ref<DirAccess> old_dir = DirAccess::open(p_from); + ERR_FAIL_COND_V(old_dir.is_null(), false); + + Error err = make_dir_recursive(p_to); + if (err != OK && err != ERR_ALREADY_EXISTS) { + return false; + } + + bool success = true; + old_dir->set_include_navigational(false); + old_dir->list_dir_begin(); + + for (String F = old_dir->_get_next(); !F.is_empty(); F = old_dir->_get_next()) { + if (old_dir->current_is_dir()) { + success = _copy_directory(p_from.path_join(F), p_to.path_join(F), p_files) && success; + } else if (F.get_extension() != "import") { + CopiedFile copy; + copy.from = p_from.path_join(F); + copy.to = p_to.path_join(F); + p_files->push_back(copy); + } + } + return success; +} + +void EditorFileSystem::_queue_refresh_filesystem() { + if (refresh_queued) { + return; + } + refresh_queued = true; + get_tree()->connect(SNAME("process_frame"), callable_mp(this, &EditorFileSystem::_refresh_filesystem), CONNECT_ONE_SHOT); +} + +void EditorFileSystem::_refresh_filesystem() { + for (const ObjectID &id : folders_to_sort) { + EditorFileSystemDirectory *dir = Object::cast_to<EditorFileSystemDirectory>(ObjectDB::get_instance(id)); + if (dir) { + dir->subdirs.sort_custom<DirectoryComparator>(); + } + } + folders_to_sort.clear(); + + _update_scan_actions(); + + emit_signal(SNAME("filesystem_changed")); + refresh_queued = false; +} + void EditorFileSystem::_reimport_thread(uint32_t p_index, ImportThreadData *p_import_data) { int current_max = p_import_data->reimport_from + int(p_index); p_import_data->max_index.exchange_if_greater(current_max); @@ -3225,10 +3349,9 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p const String path = da->get_current_dir(); EditorFileSystemDirectory *parent = get_filesystem_path(path); ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND); + folders_to_sort.insert(parent->get_instance_id()); const PackedStringArray folders = p_path.trim_prefix(path).trim_suffix("/").split("/"); - bool first = true; - for (const String &folder : folders) { const int current = parent->find_dir_index(folder); if (current > -1) { @@ -3240,18 +3363,59 @@ Error EditorFileSystem::make_dir_recursive(const String &p_path, const String &p efd->parent = parent; efd->name = folder; parent->subdirs.push_back(efd); - - if (first) { - parent->subdirs.sort_custom<DirectoryComparator>(); - first = false; - } parent = efd; } - emit_signal(SNAME("filesystem_changed")); + _queue_refresh_filesystem(); + return OK; +} + +Error EditorFileSystem::copy_file(const String &p_from, const String &p_to) { + _copy_file(p_from, p_to); + + EditorFileSystemDirectory *parent = get_filesystem_path(p_to.get_base_dir()); + ERR_FAIL_NULL_V(parent, ERR_FILE_NOT_FOUND); + + ScanProgress sp; + _scan_fs_changes(parent, sp, false); + + _queue_refresh_filesystem(); return OK; } +Error EditorFileSystem::copy_directory(const String &p_from, const String &p_to) { + List<CopiedFile> files; + bool success = _copy_directory(p_from, p_to, &files); + + EditorProgress *ep = nullptr; + if (files.size() > 10) { + ep = memnew(EditorProgress("_copy_files", TTR("Copying files..."), files.size())); + } + + int i = 0; + for (const CopiedFile &F : files) { + if (_copy_file(F.from, F.to) != OK) { + success = false; + } + if (ep) { + ep->step(F.from.get_file(), i++, false); + } + } + memdelete_notnull(ep); + + EditorFileSystemDirectory *efd = get_filesystem_path(p_to); + ERR_FAIL_NULL_V(efd, FAILED); + ERR_FAIL_NULL_V(efd->get_parent(), FAILED); + + folders_to_sort.insert(efd->get_parent()->get_instance_id()); + + ScanProgress sp; + _scan_fs_changes(efd, sp); + + _queue_refresh_filesystem(); + return success ? OK : FAILED; +} + ResourceUID::ID EditorFileSystem::_resource_saver_get_resource_id_for_path(const String &p_path, bool p_generate) { if (!p_path.is_resource_file() || p_path.begins_with(ProjectSettings::get_singleton()->get_project_data_path())) { // Saved externally (configuration file) or internal file, do not assign an ID. diff --git a/editor/editor_file_system.h b/editor/editor_file_system.h index 7aa0137f4e..11573ef0d7 100644 --- a/editor/editor_file_system.h +++ b/editor/editor_file_system.h @@ -180,6 +180,7 @@ class EditorFileSystem : public Node { EditorFileSystemDirectory *new_filesystem = nullptr; ScannedDirectory *first_scan_root_dir = nullptr; + bool filesystem_changed_queued = false; bool scanning = false; bool importing = false; bool first_scan = true; @@ -189,9 +190,10 @@ class EditorFileSystem : public Node { bool revalidate_import_files = false; int nb_files_total = 0; + void _notify_filesystem_changed(); void _scan_filesystem(); void _first_scan_filesystem(); - void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions); + void _first_scan_process_scripts(const ScannedDirectory *p_scan_dir, List<String> &p_gdextension_extensions, HashSet<String> &p_existing_class_names, HashSet<String> &p_extensions); HashSet<String> late_update_files; @@ -239,7 +241,7 @@ class EditorFileSystem : public Node { bool _find_file(const String &p_file, EditorFileSystemDirectory **r_d, int &r_file_pos) const; - void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress); + void _scan_fs_changes(EditorFileSystemDirectory *p_dir, ScanProgress &p_progress, bool p_recursive = true); void _delete_internal_files(const String &p_file); int _insert_actions_delete_files_directory(EditorFileSystemDirectory *p_dir); @@ -324,6 +326,19 @@ class EditorFileSystem : public Node { HashSet<String> group_file_cache; HashMap<String, String> file_icon_cache; + struct CopiedFile { + String from; + String to; + }; + + bool refresh_queued = false; + HashSet<ObjectID> folders_to_sort; + + Error _copy_file(const String &p_from, const String &p_to); + bool _copy_directory(const String &p_from, const String &p_to, List<CopiedFile> *p_files); + void _queue_refresh_filesystem(); + void _refresh_filesystem(); + struct ImportThreadData { const ImportFile *reimport_files; int reimport_from; @@ -378,6 +393,8 @@ public: void move_group_file(const String &p_path, const String &p_new_path); Error make_dir_recursive(const String &p_path, const String &p_base_path = String()); + Error copy_file(const String &p_from, const String &p_to); + Error copy_directory(const String &p_from, const String &p_to); static bool _should_skip_directory(const String &p_path); diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp index 18f5610655..5cb38fa875 100644 --- a/editor/editor_folding.cpp +++ b/editor/editor_folding.cpp @@ -249,7 +249,7 @@ void EditorFolding::_do_object_unfolds(Object *p_object, HashSet<Ref<Resource>> } } } else { //path - int last = E.name.rfind("/"); + int last = E.name.rfind_char('/'); if (last != -1) { bool can_revert = EditorPropertyRevert::can_property_revert(p_object, E.name); if (can_revert) { diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index cfe257fcfc..f0c54b9edd 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -148,7 +148,7 @@ static String _contextualize_class_specifier(const String &p_class_specifier, co // Here equal length + begins_with from above implies p_class_specifier == p_edited_class :) if (p_class_specifier.length() == p_edited_class.length()) { - int rfind = p_class_specifier.rfind("."); + int rfind = p_class_specifier.rfind_char('.'); if (rfind == -1) { // Single identifier return p_class_specifier; } @@ -234,7 +234,7 @@ void EditorHelp::_class_desc_select(const String &p_select) { enum_class_name = "@GlobalScope"; enum_name = link; } else { - const int dot_pos = link.rfind("."); + const int dot_pos = link.rfind_char('.'); if (dot_pos >= 0) { enum_class_name = link.left(dot_pos); enum_name = link.substr(dot_pos + 1); @@ -3109,9 +3109,9 @@ void EditorHelp::set_scroll(int p_scroll) { void EditorHelp::update_toggle_scripts_button() { if (is_layout_rtl()) { - toggle_scripts_button->set_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back"))); + toggle_scripts_button->set_button_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Forward") : SNAME("Back"))); } else { - toggle_scripts_button->set_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"))); + toggle_scripts_button->set_button_icon(get_editor_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"))); } toggle_scripts_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text())); } @@ -3252,7 +3252,7 @@ EditorHelpBit::HelpData EditorHelpBit::_get_property_help_data(const StringName enum_class_name = "@GlobalScope"; enum_name = property.enumeration; } else { - const int dot_pos = property.enumeration.rfind("."); + const int dot_pos = property.enumeration.rfind_char('.'); if (dot_pos >= 0) { enum_class_name = property.enumeration.left(dot_pos); enum_name = property.enumeration.substr(dot_pos + 1); @@ -3619,7 +3619,7 @@ void EditorHelpBit::_meta_clicked(const String &p_select) { enum_class_name = "@GlobalScope"; enum_name = link; } else { - const int dot_pos = link.rfind("."); + const int dot_pos = link.rfind_char('.'); if (dot_pos >= 0) { enum_class_name = link.left(dot_pos); enum_name = link.substr(dot_pos + 1); @@ -3868,7 +3868,7 @@ void EditorHelpBitTooltip::show_tooltip(EditorHelpBit *p_help_bit, Control *p_ta EditorHelpBitTooltip *tooltip = memnew(EditorHelpBitTooltip(p_target)); p_help_bit->connect("request_hide", callable_mp(tooltip, &EditorHelpBitTooltip::_safe_queue_free)); tooltip->add_child(p_help_bit); - p_target->get_viewport()->add_child(tooltip); + p_target->add_child(tooltip); p_help_bit->update_content_height(); tooltip->popup_under_cursor(); } @@ -4153,8 +4153,8 @@ void FindBar::popup_search() { void FindBar::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - find_prev->set_icon(get_editor_theme_icon(SNAME("MoveUp"))); - find_next->set_icon(get_editor_theme_icon(SNAME("MoveDown"))); + find_prev->set_button_icon(get_editor_theme_icon(SNAME("MoveUp"))); + find_next->set_button_icon(get_editor_theme_icon(SNAME("MoveDown"))); hide_button->set_texture_normal(get_editor_theme_icon(SNAME("Close"))); hide_button->set_texture_hover(get_editor_theme_icon(SNAME("Close"))); hide_button->set_texture_pressed(get_editor_theme_icon(SNAME("Close"))); diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index 47f16f219f..0fc7052a2b 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -151,7 +151,7 @@ void EditorHelpSearch::_update_results() { search_flags |= SEARCH_SHOW_HIERARCHY; } - search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags))); + search.instantiate(results_tree, results_tree, &tree_cache, term, search_flags); // Clear old search flags to force rebuild on short term. old_search_flags = 0; @@ -162,7 +162,7 @@ void EditorHelpSearch::_update_results() { hierarchy_button->set_disabled(true); // Always show hierarchy for short searches. - search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags | SEARCH_SHOW_HIERARCHY))); + search.instantiate(results_tree, results_tree, &tree_cache, term, search_flags | SEARCH_SHOW_HIERARCHY); old_search_flags = search_flags; set_process(true); @@ -244,8 +244,8 @@ void EditorHelpSearch::_notification(int p_what) { search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); search_box->add_theme_icon_override("right_icon", get_editor_theme_icon(SNAME("Search"))); - case_sensitive_button->set_icon(get_editor_theme_icon(SNAME("MatchCase"))); - hierarchy_button->set_icon(get_editor_theme_icon(SNAME("ClassList"))); + case_sensitive_button->set_button_icon(get_editor_theme_icon(SNAME("MatchCase"))); + hierarchy_button->set_button_icon(get_editor_theme_icon(SNAME("ClassList"))); if (is_visible()) { _update_results(); @@ -1166,7 +1166,7 @@ TreeItem *EditorHelpSearch::Runner::_create_class_item(TreeItem *p_parent, const if (p_matching_keyword.is_empty()) { item->set_text(0, p_doc->name); } else { - item->set_text(0, p_doc->name + " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword))); + item->set_text(0, p_doc->name + " - " + vformat(TTR("Matches the \"%s\" keyword."), p_matching_keyword)); } if (!term.is_empty()) { @@ -1272,7 +1272,7 @@ TreeItem *EditorHelpSearch::Runner::_create_member_item(TreeItem *p_parent, cons text = p_class_name + "." + p_text; } if (!p_matching_keyword.is_empty()) { - text += " - " + TTR(vformat("Matches the \"%s\" keyword.", p_matching_keyword)); + text += " - " + vformat(TTR("Matches the \"%s\" keyword."), p_matching_keyword); } item->set_text(0, text); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index e6553cc92b..1a973d7b77 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -931,10 +931,19 @@ float EditorProperty::get_name_split_ratio() const { return split_ratio; } +void EditorProperty::set_favoritable(bool p_favoritable) { + can_favorite = p_favoritable; +} + +bool EditorProperty::is_favoritable() const { + return can_favorite; +} + void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) { object = p_object; property = p_property; - _update_pin_flags(); + + _update_flags(); } static bool _is_value_potential_override(Node *p_node, const String &p_property) { @@ -953,12 +962,14 @@ static bool _is_value_potential_override(Node *p_node, const String &p_property) } } -void EditorProperty::_update_pin_flags() { +void EditorProperty::_update_flags() { can_pin = false; pin_hidden = true; + if (read_only) { return; } + if (Node *node = Object::cast_to<Node>(object)) { // Avoid errors down the road by ignoring nodes which are not part of a scene if (!node->get_owner()) { @@ -1034,6 +1045,10 @@ void EditorProperty::menu_option(int p_option) { case MENU_COPY_PROPERTY_PATH: { DisplayServer::get_singleton()->clipboard_set(property_path); } break; + case MENU_FAVORITE_PROPERTY: { + emit_signal(SNAME("property_favorited"), property, !favorited); + queue_redraw(); + } break; case MENU_PIN_VALUE: { emit_signal(SNAME("property_pinned"), property, !pinned); queue_redraw(); @@ -1091,6 +1106,7 @@ void EditorProperty::_bind_methods() { ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING_NAME, "property"))); ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "checked"))); + ADD_SIGNAL(MethodInfo("property_favorited", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "favorited"))); ADD_SIGNAL(MethodInfo("property_pinned", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "pinned"))); ADD_SIGNAL(MethodInfo("property_can_revert_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "can_revert"))); ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); @@ -1129,8 +1145,21 @@ void EditorProperty::_update_popup() { menu->set_item_disabled(MENU_PASTE_VALUE, is_read_only()); menu->set_item_disabled(MENU_COPY_PROPERTY_PATH, internal); - if (!pin_hidden) { + if (can_favorite || !pin_hidden) { menu->add_separator(); + } + + if (can_favorite) { + if (favorited) { + menu->add_icon_item(get_editor_theme_icon(SNAME("Unfavorite")), TTR("Unfavorite Property"), MENU_FAVORITE_PROPERTY); + menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be put back at its original place.")); + } else { + menu->add_icon_item(get_editor_theme_icon(SNAME("Favorites")), TTR("Favorite Property"), MENU_FAVORITE_PROPERTY); + menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be placed at the top for all objects of this class.")); + } + } + + if (!pin_hidden) { if (can_pin) { menu->add_icon_check_item(get_editor_theme_icon(SNAME("Pin")), TTR("Pin Value"), MENU_PIN_VALUE); menu->set_item_checked(menu->get_item_index(MENU_PIN_VALUE), pinned); @@ -1219,9 +1248,14 @@ void EditorInspectorPlugin::_bind_methods() { void EditorInspectorCategory::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - menu->set_item_icon(menu->get_item_index(MENU_OPEN_DOCS), get_editor_theme_icon(SNAME("Help"))); + if (menu) { + if (is_favorite) { + menu->set_item_icon(menu->get_item_index(EditorInspector::MENU_UNFAVORITE_ALL), get_editor_theme_icon(SNAME("Unfavorite"))); + } else { + menu->set_item_icon(menu->get_item_index(MENU_OPEN_DOCS), get_editor_theme_icon(SNAME("Help"))); + } + } } break; case NOTIFICATION_DRAW: { Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); @@ -1278,6 +1312,15 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons return memnew(Control); // Make the standard tooltip invisible. } +void EditorInspectorCategory::set_as_favorite(EditorInspector *p_for_inspector) { + is_favorite = true; + + menu = memnew(PopupMenu); + menu->add_item(TTR("Unfavorite All"), EditorInspector::MENU_UNFAVORITE_ALL); + add_child(menu); + menu->connect(SceneStringName(id_pressed), callable_mp(p_for_inspector, &EditorInspector::_handle_menu_option)); +} + Size2 EditorInspectorCategory::get_minimum_size() const { Ref<Font> font = get_theme_font(SNAME("bold"), EditorStringName(EditorFonts)); int font_size = get_theme_font_size(SNAME("bold_size"), EditorStringName(EditorFonts)); @@ -1306,7 +1349,7 @@ void EditorInspectorCategory::_handle_menu_option(int p_option) { } void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) { - if (doc_class_name.is_empty()) { + if (!is_favorite && doc_class_name.is_empty()) { return; } @@ -1315,20 +1358,21 @@ void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) { return; } - menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name)); + if (!is_favorite) { + if (!menu) { + menu = memnew(PopupMenu); + menu->add_icon_item(get_editor_theme_icon(SNAME("Help")), TTR("Open Documentation"), MENU_OPEN_DOCS); + add_child(menu); + menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option)); + } + menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name)); + } menu->set_position(get_screen_position() + mb_event->get_position()); menu->reset_size(); menu->popup(); } -EditorInspectorCategory::EditorInspectorCategory() { - menu = memnew(PopupMenu); - menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option)); - menu->add_item(TTR("Open Documentation"), MENU_OPEN_DOCS); - add_child(menu); -} - //////////////////////////////////////////////// //////////////////////////////////////////////// @@ -1622,6 +1666,10 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) { } } +String EditorInspectorSection::get_section() const { + return section; +} + VBoxContainer *EditorInspectorSection::get_vbox() { return vbox; } @@ -2234,7 +2282,7 @@ void EditorInspectorArray::_setup() { if (element_position > 0) { ae.move_up = memnew(Button); - ae.move_up->set_icon(get_editor_theme_icon(SNAME("MoveUp"))); + ae.move_up->set_button_icon(get_editor_theme_icon(SNAME("MoveUp"))); ae.move_up->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorArray::_move_element).bind(element_position, element_position - 1)); move_vbox->add_child(ae.move_up); } @@ -2250,7 +2298,7 @@ void EditorInspectorArray::_setup() { if (element_position < count - 1) { ae.move_down = memnew(Button); - ae.move_down->set_icon(get_editor_theme_icon(SNAME("MoveDown"))); + ae.move_down->set_button_icon(get_editor_theme_icon(SNAME("MoveDown"))); ae.move_down->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorArray::_move_element).bind(element_position, element_position + 2)); move_vbox->add_child(ae.move_down); } @@ -2273,7 +2321,7 @@ void EditorInspectorArray::_setup() { ae.hbox->add_child(ae.vbox); ae.erase = memnew(Button); - ae.erase->set_icon(get_editor_theme_icon(SNAME("Remove"))); + ae.erase->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); ae.erase->set_v_size_flags(SIZE_SHRINK_CENTER); ae.erase->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorArray::_remove_item).bind(element_position)); ae.hbox->add_child(ae.erase); @@ -2355,10 +2403,10 @@ void EditorInspectorArray::_notification(int p_what) { ae.move_texture_rect->set_texture(get_editor_theme_icon(SNAME("TripleBar"))); } if (ae.move_up) { - ae.move_up->set_icon(get_editor_theme_icon(SNAME("MoveUp"))); + ae.move_up->set_button_icon(get_editor_theme_icon(SNAME("MoveUp"))); } if (ae.move_down) { - ae.move_down->set_icon(get_editor_theme_icon(SNAME("MoveDown"))); + ae.move_down->set_button_icon(get_editor_theme_icon(SNAME("MoveDown"))); } Size2 min_size = get_theme_stylebox(SNAME("Focus"), EditorStringName(EditorStyles))->get_minimum_size(); ae.margin->begin_bulk_theme_override(); @@ -2369,11 +2417,11 @@ void EditorInspectorArray::_notification(int p_what) { ae.margin->end_bulk_theme_override(); if (ae.erase) { - ae.erase->set_icon(get_editor_theme_icon(SNAME("Remove"))); + ae.erase->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); } } - add_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + add_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); update_minimum_size(); } break; @@ -2542,10 +2590,10 @@ void EditorPaginator::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - first_page_button->set_icon(get_editor_theme_icon(SNAME("PageFirst"))); - prev_page_button->set_icon(get_editor_theme_icon(SNAME("PagePrevious"))); - next_page_button->set_icon(get_editor_theme_icon(SNAME("PageNext"))); - last_page_button->set_icon(get_editor_theme_icon(SNAME("PageLast"))); + first_page_button->set_button_icon(get_editor_theme_icon(SNAME("PageFirst"))); + prev_page_button->set_button_icon(get_editor_theme_icon(SNAME("PagePrevious"))); + next_page_button->set_button_icon(get_editor_theme_icon(SNAME("PageNext"))); + last_page_button->set_button_icon(get_editor_theme_icon(SNAME("PageLast"))); } break; } } @@ -2675,7 +2723,13 @@ String EditorInspector::get_selected_path() const { void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorInspectorSection *p_section, Ref<EditorInspectorPlugin> ped) { for (const EditorInspectorPlugin::AddedEditor &F : ped->added_editors) { EditorProperty *ep = Object::cast_to<EditorProperty>(F.property_editor); - current_vbox->add_child(F.property_editor); + + if (ep && !F.properties.is_empty() && current_favorites.has(F.properties[0])) { + ep->favorited = true; + favorites_vbox->add_child(F.property_editor); + } else { + current_vbox->add_child(F.property_editor); + } if (ep) { ep->object = object; @@ -2684,6 +2738,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), CONNECT_DEFERRED); ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); + ep->connect("property_favorited", callable_mp(this, &EditorInspector::_set_property_favorited), CONNECT_DEFERRED); ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned)); ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed)); @@ -2727,7 +2782,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn ep->set_read_only(read_only); ep->update_property(); - ep->_update_pin_flags(); + ep->_update_flags(); ep->update_editor_property_status(); ep->set_deletable(deletable_properties); ep->update_cache(); @@ -2837,6 +2892,7 @@ void EditorInspector::update_tree() { String subgroup; String subgroup_base; int section_depth = 0; + bool disable_favorite = false; VBoxContainer *category_vbox = nullptr; List<PropertyInfo> plist; @@ -2844,13 +2900,17 @@ void EditorInspector::update_tree() { HashMap<VBoxContainer *, HashMap<String, VBoxContainer *>> vbox_per_path; HashMap<String, EditorInspectorArray *> editor_inspector_array_per_prefix; + HashMap<String, HashMap<String, LocalVector<EditorProperty *>>> favorites_to_add; Color sscolor = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)); // Get the lists of editors to add the beginning. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { ped->parse_begin(object); - _parse_added_editors(main_vbox, nullptr, ped); + _parse_added_editors(begin_vbox, nullptr, ped); + } + if (begin_vbox->get_child_count()) { + begin_vbox->show(); } StringName doc_name; @@ -2897,6 +2957,7 @@ void EditorInspector::update_tree() { subgroup = ""; subgroup_base = ""; section_depth = 0; + disable_favorite = false; vbox_per_path.clear(); editor_inspector_array_per_prefix.clear(); @@ -2960,6 +3021,11 @@ void EditorInspector::update_tree() { } else { category_icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object"); } + + // Property favorites aren't compatible with built-in scripts. + if (scr->is_built_in()) { + disable_favorite = true; + } } } @@ -3058,9 +3124,14 @@ void EditorInspector::update_tree() { } } + // Don't allow to favorite array items. + if (!disable_favorite) { + disable_favorite = !array_prefix.is_empty(); + } + if (!array_prefix.is_empty()) { path = path.trim_prefix(array_prefix); - int char_index = path.find("/"); + int char_index = path.find_char('/'); if (char_index >= 0) { path = path.right(-char_index - 1); } else { @@ -3100,10 +3171,10 @@ void EditorInspector::update_tree() { } // Get the property label's string. - String name_override = (path.contains("/")) ? path.substr(path.rfind("/") + 1) : path; + String name_override = (path.contains("/")) ? path.substr(path.rfind_char('/') + 1) : path; String feature_tag; { - const int dot = name_override.find("."); + const int dot = name_override.find_char('.'); if (dot != -1) { feature_tag = name_override.substr(dot); name_override = name_override.substr(0, dot); @@ -3118,7 +3189,7 @@ void EditorInspector::update_tree() { const String property_label_string = EditorPropertyNameProcessor::get_singleton()->process_name(name_override, name_style, p.name, doc_name) + feature_tag; // Remove the property from the path. - int idx = path.rfind("/"); + int idx = path.rfind_char('/'); if (idx > -1) { path = path.left(idx); } else { @@ -3249,7 +3320,7 @@ void EditorInspector::update_tree() { array_element_prefix = class_name_components[0]; editor_inspector_array = memnew(EditorInspectorArray(all_read_only)); - String array_label = path.contains("/") ? path.substr(path.rfind("/") + 1) : path; + String array_label = path.contains("/") ? path.substr(path.rfind_char('/') + 1) : path; array_label = EditorPropertyNameProcessor::get_singleton()->process_name(property_label_string, property_name_style, p.name, doc_name); int page = per_array_page.has(array_element_prefix) ? per_array_page[array_element_prefix] : 0; editor_inspector_array->setup_with_move_element_function(object, array_label, array_element_prefix, page, c, use_folding); @@ -3457,6 +3528,7 @@ void EditorInspector::update_tree() { ep->set_draw_warning(draw_warning); ep->set_use_folding(use_folding); + ep->set_favoritable(can_favorite && !disable_favorite); ep->set_checkable(checkable); ep->set_checked(checked); ep->set_keying(keying); @@ -3472,7 +3544,12 @@ void EditorInspector::update_tree() { } } - current_vbox->add_child(editors[i].property_editor); + if (ep && ep->is_favoritable() && current_favorites.has(p.name)) { + ep->favorited = true; + favorites_to_add[group][subgroup].push_back(ep); + } else { + current_vbox->add_child(editors[i].property_editor); + } if (ep) { // Eventually, set other properties/signals after the property editor got added to the tree. @@ -3481,6 +3558,7 @@ void EditorInspector::update_tree() { ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed)); ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), CONNECT_DEFERRED); ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value)); + ep->connect("property_favorited", callable_mp(this, &EditorInspector::_set_property_favorited), CONNECT_DEFERRED); ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked)); ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned)); ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected)); @@ -3511,7 +3589,7 @@ void EditorInspector::update_tree() { ep->set_internal(p.usage & PROPERTY_USAGE_INTERNAL); ep->update_property(); - ep->_update_pin_flags(); + ep->_update_flags(); ep->update_editor_property_status(); ep->update_cache(); @@ -3522,6 +3600,77 @@ void EditorInspector::update_tree() { } } + if (!current_favorites.is_empty()) { + favorites_section->show(); + + // Organize the favorited properties in their sections, to keep context and differentiate from others with the same name. + bool is_localized = property_name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED; + for (const KeyValue<String, HashMap<String, LocalVector<EditorProperty *>>> &KV : favorites_to_add) { + String section_name = KV.key; + String label; + String tooltip; + VBoxContainer *parent_vbox = favorites_vbox; + if (!section_name.is_empty()) { + if (is_localized) { + label = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + tooltip = section_name; + } else { + label = section_name; + tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + } + + EditorInspectorSection *section = memnew(EditorInspectorSection); + favorites_groups_vbox->add_child(section); + parent_vbox = section->get_vbox(); + section->setup("", section_name, object, sscolor, false); + section->set_tooltip_text(tooltip); + } + + for (const KeyValue<String, LocalVector<EditorProperty *>> &KV2 : KV.value) { + section_name = KV2.key; + VBoxContainer *vbox = parent_vbox; + if (!section_name.is_empty()) { + if (is_localized) { + label = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + tooltip = section_name; + } else { + label = section_name; + tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name); + } + + EditorInspectorSection *section = memnew(EditorInspectorSection); + vbox->add_child(section); + vbox = section->get_vbox(); + section->setup("", section_name, object, sscolor, false); + section->set_tooltip_text(tooltip); + } + + for (EditorProperty *ep : KV2.value) { + vbox->add_child(ep); + } + } + } + + // Show a separator if there's no category to clearly divide the properties. + favorites_separator->hide(); + if (main_vbox->get_child_count() > 0) { + EditorInspectorCategory *category = Object::cast_to<EditorInspectorCategory>(main_vbox->get_child(0)); + if (!category) { + favorites_separator->show(); + } + } + + // Clean up empty sections. + for (List<EditorInspectorSection *>::Element *I = sections.back(); I; I = I->prev()) { + EditorInspectorSection *section = I->get(); + if (section->get_vbox()->get_child_count() == 0) { + sections.erase(section); + vbox_per_path[main_vbox].erase(section->get_section()); + memdelete(section); + } + } + } + if (!hide_metadata && !object->call("_hide_metadata_from_inspector")) { // Add 4px of spacing between the "Add Metadata" button and the content above it. Control *spacer = memnew(Control); @@ -3529,7 +3678,7 @@ void EditorInspector::update_tree() { main_vbox->add_child(spacer); Button *add_md = EditorInspector::create_inspector_action_button(TTR("Add Metadata")); - add_md->set_icon(get_editor_theme_icon(SNAME("Add"))); + add_md->set_button_icon(get_editor_theme_icon(SNAME("Add"))); add_md->connect(SceneStringName(pressed), callable_mp(this, &EditorInspector::_show_add_meta_dialog)); main_vbox->add_child(add_md); if (all_read_only) { @@ -3564,6 +3713,19 @@ void EditorInspector::update_property(const String &p_prop) { } void EditorInspector::_clear(bool p_hide_plugins) { + begin_vbox->hide(); + while (begin_vbox->get_child_count()) { + memdelete(begin_vbox->get_child(0)); + } + + favorites_section->hide(); + while (favorites_vbox->get_child_count()) { + memdelete(favorites_vbox->get_child(0)); + } + while (favorites_groups_vbox->get_child_count()) { + memdelete(favorites_groups_vbox->get_child(0)); + } + while (main_vbox->get_child_count()) { memdelete(main_vbox->get_child(0)); } @@ -3610,6 +3772,10 @@ void EditorInspector::edit(Object *p_object) { update_scroll_request = scroll_cache[object->get_instance_id()]; //done this way because wait until full size is accommodated } object->connect(CoreStringName(property_list_changed), callable_mp(this, &EditorInspector::_changed_callback)); + + can_favorite = Object::cast_to<Node>(object) || Object::cast_to<Resource>(object); + _update_current_favorites(); + update_tree(); } @@ -4104,10 +4270,164 @@ void EditorInspector::_node_removed(Node *p_node) { } } +void EditorInspector::_update_current_favorites() { + current_favorites.clear(); + if (!can_favorite) { + return; + } + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + + // Fetch script properties. + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + // FIXME: Only properties from a saved script will be available, unsaved ones will be ignored. + // Can cause a little wonkiness, while nothing serious, would be nice to find a way to get + // unsaved ones without needing to get the entire property list of an object. + scr->get_script_property_list(&plist); + + String path; + HashMap<String, LocalVector<String>> props; + + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY) { + path = favorites.has(p.hint_string) ? p.hint_string : String(); + } else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && !path.is_empty()) { + props[path].push_back(p.name); + } + } + + // Add favorited properties while removing invalid ones. + bool invalid_props = false; + for (const KeyValue<String, LocalVector<String>> &KV : props) { + path = KV.key; + for (int i = 0; i < favorites[path].size(); i++) { + String prop = favorites[path][i]; + if (KV.value.has(prop)) { + current_favorites.append(prop); + } else { + invalid_props = true; + favorites[path].erase(prop); + i--; + } + } + + if (favorites[path].is_empty()) { + favorites.erase(path); + } + } + + if (invalid_props) { + EditorSettings::get_singleton()->set_favorite_properties(favorites); + } + } + + // Fetch built-in properties. + StringName class_name = object->get_class_name(); + for (const KeyValue<String, PackedStringArray> &KV : favorites) { + if (ClassDB::is_parent_class(class_name, KV.key)) { + current_favorites.append_array(KV.value); + } + } +} + +void EditorInspector::_set_property_favorited(const String &p_path, bool p_favorited) { + if (!object) { + return; + } + + StringName class_name = object->get_class_name(); + while (!class_name.is_empty()) { + bool has_prop = ClassDB::has_property(class_name, p_path, true); + if (has_prop) { + break; + } + + class_name = ClassDB::get_parent_class_nocheck(class_name); + } + + if (class_name.is_empty()) { + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + scr->get_script_property_list(&plist); + + String path; + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY) { + path = p.hint_string; + } else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && p.name == p_path) { + class_name = path; + break; + } + } + } + + ERR_FAIL_COND_MSG(class_name.is_empty(), "Can't favorite invalid property. If said property was from a script and recently renamed, try saving it first."); + } + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + if (p_favorited) { + current_favorites.append(p_path); + favorites[class_name].append(p_path); + } else { + current_favorites.erase(p_path); + + if (favorites.has(class_name) && favorites[class_name].has(p_path)) { + if (favorites[class_name].size() > 1) { + favorites[class_name].erase(p_path); + } else { + favorites.erase(class_name); + } + } + } + EditorSettings::get_singleton()->set_favorite_properties(favorites); + + update_tree(); +} + +void EditorInspector::_clear_current_favorites() { + current_favorites.clear(); + + HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties(); + + Ref<Script> scr = object->get_script(); + if (scr.is_valid()) { + List<PropertyInfo> plist; + scr->get_script_property_list(&plist); + + for (PropertyInfo &p : plist) { + if (p.usage & PROPERTY_USAGE_CATEGORY && favorites.has(p.hint_string)) { + favorites.erase(p.hint_string); + } + } + } + + StringName class_name = object->get_class_name(); + while (class_name) { + if (favorites.has(class_name)) { + favorites.erase(class_name); + } + + class_name = ClassDB::get_parent_class(class_name); + } + + EditorSettings::get_singleton()->set_favorite_properties(favorites); + update_tree(); +} + void EditorInspector::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - main_vbox->add_theme_constant_override("separation", get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector"))); + favorites_category->icon = get_editor_theme_icon(SNAME("Favorites")); + + int separation = get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector")); + base_vbox->add_theme_constant_override("separation", separation); + begin_vbox->add_theme_constant_override("separation", separation); + favorites_section->add_theme_constant_override("separation", separation); + favorites_groups_vbox->add_theme_constant_override("separation", separation); + main_vbox->add_theme_constant_override("separation", separation); } break; case NOTIFICATION_READY: { @@ -4205,6 +4525,7 @@ void EditorInspector::_notification(int p_what) { void EditorInspector::_changed_callback() { //this is called when property change is notified via notify_property_list_changed() if (object != nullptr) { + _update_current_favorites(); _edit_request_change(object, String()); } } @@ -4294,6 +4615,14 @@ void EditorInspector::_add_meta_confirm() { undo_redo->commit_action(); } +void EditorInspector::_handle_menu_option(int p_option) { + switch (p_option) { + case MENU_UNFAVORITE_ALL: + _clear_current_favorites(); + break; + } +} + void EditorInspector::_bind_methods() { ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change); ClassDB::bind_method("get_selected_path", &EditorInspector::get_selected_path); @@ -4312,9 +4641,36 @@ void EditorInspector::_bind_methods() { EditorInspector::EditorInspector() { object = nullptr; + + base_vbox = memnew(VBoxContainer); + base_vbox->set_h_size_flags(SIZE_EXPAND_FILL); + add_child(base_vbox); + + begin_vbox = memnew(VBoxContainer); + base_vbox->add_child(begin_vbox); + begin_vbox->hide(); + + favorites_section = memnew(VBoxContainer); + base_vbox->add_child(favorites_section); + favorites_section->hide(); + + favorites_category = memnew(EditorInspectorCategory); + favorites_category->set_as_favorite(this); + favorites_section->add_child(favorites_category); + favorites_category->label = TTR("Favorites"); + + favorites_vbox = memnew(VBoxContainer); + favorites_section->add_child(favorites_vbox); + favorites_groups_vbox = memnew(VBoxContainer); + favorites_section->add_child(favorites_groups_vbox); + + favorites_separator = memnew(HSeparator); + favorites_section->add_child(favorites_separator); + favorites_separator->hide(); + main_vbox = memnew(VBoxContainer); - main_vbox->set_h_size_flags(SIZE_EXPAND_FILL); - add_child(main_vbox); + base_vbox->add_child(main_vbox); + set_horizontal_scroll_mode(SCROLL_MODE_DISABLED); set_follow_focus(true); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index 0309213b76..2e4633ccea 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -41,6 +41,7 @@ class Button; class ConfirmationDialog; class EditorInspector; class EditorValidationPanel; +class HSeparator; class LineEdit; class MarginContainer; class OptionButton; @@ -64,6 +65,7 @@ public: MENU_COPY_VALUE, MENU_PASTE_VALUE, MENU_COPY_PROPERTY_PATH, + MENU_FAVORITE_PROPERTY, MENU_PIN_VALUE, MENU_OPEN_DOCUMENTATION, }; @@ -112,6 +114,9 @@ private: bool pin_hidden = false; bool pinned = false; + bool can_favorite = false; + bool favorited = false; + bool use_folding = false; bool draw_top_bg = true; @@ -134,7 +139,7 @@ private: GDVIRTUAL0(_update_property) GDVIRTUAL1(_set_read_only, bool) - void _update_pin_flags(); + void _update_flags(); protected: bool has_borders = false; @@ -218,6 +223,9 @@ public: void set_name_split_ratio(float p_ratio); float get_name_split_ratio() const; + void set_favoritable(bool p_favoritable); + bool is_favoritable() const; + void set_object_and_property(Object *p_object, const StringName &p_property); virtual Control *make_custom_tooltip(const String &p_text) const override; @@ -285,6 +293,7 @@ class EditorInspectorCategory : public Control { String label; String doc_class_name; PopupMenu *menu = nullptr; + bool is_favorite = false; void _handle_menu_option(int p_option); @@ -293,10 +302,10 @@ protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; public: + void set_as_favorite(EditorInspector *p_for_inspector); + virtual Size2 get_minimum_size() const override; virtual Control *make_custom_tooltip(const String &p_text) const override; - - EditorInspectorCategory(); }; class EditorInspectorSection : public Container { @@ -331,6 +340,7 @@ public: virtual Size2 get_minimum_size() const override; void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth = 0, int p_level = 1); + String get_section() const; VBoxContainer *get_vbox(); void unfold(); void fold(); @@ -480,13 +490,31 @@ public: class EditorInspector : public ScrollContainer { GDCLASS(EditorInspector, ScrollContainer); + friend class EditorInspectorCategory; + enum { MAX_PLUGINS = 1024 }; static Ref<EditorInspectorPlugin> inspector_plugins[MAX_PLUGINS]; static int inspector_plugin_count; + // Right-click context menu options. + enum ClassMenuOption { + MENU_UNFAVORITE_ALL, + }; + + bool can_favorite = false; + PackedStringArray current_favorites; + VBoxContainer *favorites_section = nullptr; + EditorInspectorCategory *favorites_category = nullptr; + VBoxContainer *favorites_vbox = nullptr; + VBoxContainer *favorites_groups_vbox = nullptr; + HSeparator *favorites_separator = nullptr; + EditorInspector *root_inspector = nullptr; + + VBoxContainer *base_vbox = nullptr; + VBoxContainer *begin_vbox = nullptr; VBoxContainer *main_vbox = nullptr; // Map used to cache the instantiated editors. @@ -557,6 +585,10 @@ class EditorInspector : public ScrollContainer { void _property_selected(const String &p_path, int p_focusable); void _object_id_selected(const String &p_path, ObjectID p_id); + void _update_current_favorites(); + void _set_property_favorited(const String &p_path, bool p_favorited); + void _clear_current_favorites(); + void _node_removed(Node *p_node); HashMap<StringName, int> per_array_page; @@ -584,6 +616,8 @@ class EditorInspector : public ScrollContainer { void _add_meta_confirm(); void _show_add_meta_dialog(); + void _handle_menu_option(int p_option); + protected: static void _bind_methods(); void _notification(int p_what); diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp index 264c80dcbf..e85258df50 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -43,6 +43,7 @@ #include "editor/gui/editor_quick_open_dialog.h" #include "editor/gui/editor_run_bar.h" #include "editor/gui/editor_scene_tabs.h" +#include "editor/gui/editor_toaster.h" #include "editor/gui/scene_tree_editor.h" #include "editor/inspector_dock.h" #include "editor/plugins/node_3d_editor_plugin.h" @@ -89,6 +90,10 @@ Ref<EditorSettings> EditorInterface::get_editor_settings() const { return EditorSettings::get_singleton(); } +EditorToaster *EditorInterface::get_editor_toaster() const { + return EditorToaster::get_singleton(); +} + EditorUndoRedoManager *EditorInterface::get_editor_undo_redo() const { return EditorUndoRedoManager::get_singleton(); } @@ -280,14 +285,10 @@ void EditorInterface::set_current_feature_profile(const String &p_profile_name) // Editor dialogs. void EditorInterface::popup_node_selector(const Callable &p_callback, const TypedArray<StringName> &p_valid_types, Node *p_current_value) { - // TODO: Should reuse dialog instance instead of creating a fresh one, but need to rework set_valid_types first. - if (node_selector) { - node_selector->disconnect(SNAME("selected"), callable_mp(this, &EditorInterface::_node_selected).bind(p_callback)); - node_selector->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_node_selection_canceled).bind(p_callback)); - get_base_control()->remove_child(node_selector); - node_selector->queue_free(); + if (!node_selector) { + node_selector = memnew(SceneTreeDialog); + get_base_control()->add_child(node_selector); } - node_selector = memnew(SceneTreeDialog); Vector<StringName> valid_types; int length = p_valid_types.size(); @@ -296,27 +297,18 @@ void EditorInterface::popup_node_selector(const Callable &p_callback, const Type valid_types.write[i] = p_valid_types[i]; } node_selector->set_valid_types(valid_types); - - get_base_control()->add_child(node_selector); - node_selector->popup_scenetree_dialog(p_current_value); - const Callable selected_callback = callable_mp(this, &EditorInterface::_node_selected).bind(p_callback); - node_selector->connect(SNAME("selected"), selected_callback, CONNECT_DEFERRED); - - const Callable canceled_callback = callable_mp(this, &EditorInterface::_node_selection_canceled).bind(p_callback); - node_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED); + const Callable callback = callable_mp(this, &EditorInterface::_node_selected); + node_selector->connect(SNAME("selected"), callback.bind(p_callback), CONNECT_DEFERRED); + node_selector->connect(SNAME("canceled"), callback.bind(NodePath(), p_callback), CONNECT_DEFERRED); } void EditorInterface::popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter, const String &p_current_value) { - // TODO: Should reuse dialog instance instead of creating a fresh one, but need to rework set_type_filter first. - if (property_selector) { - property_selector->disconnect(SNAME("selected"), callable_mp(this, &EditorInterface::_property_selected).bind(p_callback)); - property_selector->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_property_selection_canceled).bind(p_callback)); - get_base_control()->remove_child(property_selector); - property_selector->queue_free(); + if (!property_selector) { + property_selector = memnew(PropertySelector); + get_base_control()->add_child(property_selector); } - property_selector = memnew(PropertySelector); Vector<Variant::Type> type_filter; int length = p_type_filter.size(); @@ -325,16 +317,24 @@ void EditorInterface::popup_property_selector(Object *p_object, const Callable & type_filter.write[i] = (Variant::Type)p_type_filter[i]; } property_selector->set_type_filter(type_filter); + property_selector->select_property_from_instance(p_object, p_current_value); - get_base_control()->add_child(property_selector); + const Callable callback = callable_mp(this, &EditorInterface::_property_selected); + property_selector->connect(SNAME("selected"), callback.bind(p_callback), CONNECT_DEFERRED); + property_selector->connect(SNAME("canceled"), callback.bind(String(), p_callback), CONNECT_DEFERRED); +} - property_selector->select_property_from_instance(p_object, p_current_value); +void EditorInterface::popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value) { + if (!method_selector) { + method_selector = memnew(PropertySelector); + get_base_control()->add_child(method_selector); + } - const Callable selected_callback = callable_mp(this, &EditorInterface::_property_selected).bind(p_callback); - property_selector->connect(SNAME("selected"), selected_callback, CONNECT_DEFERRED); + method_selector->select_method_from_instance(p_object, p_current_value); - const Callable canceled_callback = callable_mp(this, &EditorInterface::_property_selection_canceled).bind(p_callback); - property_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED); + const Callable callback = callable_mp(this, &EditorInterface::_method_selected); + method_selector->connect(SNAME("selected"), callback.bind(p_callback), CONNECT_DEFERRED); + method_selector->connect(SNAME("canceled"), callback.bind(String(), p_callback), CONNECT_DEFERRED); } void EditorInterface::popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types) { @@ -356,20 +356,40 @@ void EditorInterface::popup_quick_open(const Callable &p_callback, const TypedAr } void EditorInterface::_node_selected(const NodePath &p_node_path, const Callable &p_callback) { - const NodePath path = get_edited_scene_root()->get_path().rel_path_to(p_node_path); - _call_dialog_callback(p_callback, path, "node selected"); -} + const Callable callback = callable_mp(this, &EditorInterface::_node_selected); + node_selector->disconnect(SNAME("selected"), callback); + node_selector->disconnect(SNAME("canceled"), callback); -void EditorInterface::_node_selection_canceled(const Callable &p_callback) { - _call_dialog_callback(p_callback, NodePath(), "node selection canceled"); + if (p_node_path.is_empty()) { + _call_dialog_callback(p_callback, NodePath(), "node selection canceled"); + } else { + const NodePath path = get_edited_scene_root()->get_path().rel_path_to(p_node_path); + _call_dialog_callback(p_callback, path, "node selected"); + } } void EditorInterface::_property_selected(const String &p_property_name, const Callable &p_callback) { - _call_dialog_callback(p_callback, NodePath(p_property_name).get_as_property_path(), "property selected"); + const Callable callback = callable_mp(this, &EditorInterface::_property_selected); + property_selector->disconnect(SNAME("selected"), callback); + property_selector->disconnect(SNAME("canceled"), callback); + + if (p_property_name.is_empty()) { + _call_dialog_callback(p_callback, NodePath(p_property_name).get_as_property_path(), "property selection canceled"); + } else { + _call_dialog_callback(p_callback, NodePath(p_property_name).get_as_property_path(), "property selected"); + } } -void EditorInterface::_property_selection_canceled(const Callable &p_callback) { - _call_dialog_callback(p_callback, NodePath(), "property selection canceled"); +void EditorInterface::_method_selected(const String &p_method_name, const Callable &p_callback) { + const Callable callback = callable_mp(this, &EditorInterface::_method_selected); + method_selector->disconnect(SNAME("selected"), callback); + method_selector->disconnect(SNAME("canceled"), callback); + + if (p_method_name.is_empty()) { + _call_dialog_callback(p_callback, p_method_name, "method selection canceled"); + } else { + _call_dialog_callback(p_callback, p_method_name, "method selected"); + } } void EditorInterface::_quick_open(const String &p_file_path, const Callable &p_callback) { @@ -556,6 +576,7 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("get_resource_previewer"), &EditorInterface::get_resource_previewer); ClassDB::bind_method(D_METHOD("get_selection"), &EditorInterface::get_selection); ClassDB::bind_method(D_METHOD("get_editor_settings"), &EditorInterface::get_editor_settings); + ClassDB::bind_method(D_METHOD("get_editor_toaster"), &EditorInterface::get_editor_toaster); ClassDB::bind_method(D_METHOD("get_editor_undo_redo"), &EditorInterface::get_editor_undo_redo); ClassDB::bind_method(D_METHOD("make_mesh_previews", "meshes", "preview_size"), &EditorInterface::_make_mesh_previews); @@ -593,6 +614,7 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("popup_node_selector", "callback", "valid_types", "current_value"), &EditorInterface::popup_node_selector, DEFVAL(TypedArray<StringName>()), DEFVAL(Variant())); ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter", "current_value"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()), DEFVAL(String())); + ClassDB::bind_method(D_METHOD("popup_method_selector", "object", "callback", "current_value"), &EditorInterface::popup_method_selector, DEFVAL(String())); ClassDB::bind_method(D_METHOD("popup_quick_open", "callback", "base_types"), &EditorInterface::popup_quick_open, DEFVAL(TypedArray<StringName>())); // Editor docks. diff --git a/editor/editor_interface.h b/editor/editor_interface.h index 4877444dac..2ae77331b1 100644 --- a/editor/editor_interface.h +++ b/editor/editor_interface.h @@ -45,6 +45,7 @@ class EditorPlugin; class EditorResourcePreview; class EditorSelection; class EditorSettings; +class EditorToaster; class EditorUndoRedoManager; class FileSystemDock; class Mesh; @@ -66,12 +67,12 @@ class EditorInterface : public Object { // Editor dialogs. PropertySelector *property_selector = nullptr; + PropertySelector *method_selector = nullptr; SceneTreeDialog *node_selector = nullptr; void _node_selected(const NodePath &p_node_paths, const Callable &p_callback); - void _node_selection_canceled(const Callable &p_callback); void _property_selected(const String &p_property_name, const Callable &p_callback); - void _property_selection_canceled(const Callable &p_callback); + void _method_selected(const String &p_property_name, const Callable &p_callback); void _quick_open(const String &p_file_path, const Callable &p_callback); void _call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context); @@ -102,6 +103,7 @@ public: EditorResourcePreview *get_resource_previewer() const; EditorSelection *get_selection() const; Ref<EditorSettings> get_editor_settings() const; + EditorToaster *get_editor_toaster() const; EditorUndoRedoManager *get_editor_undo_redo() const; Vector<Ref<Texture2D>> make_mesh_previews(const Vector<Ref<Mesh>> &p_meshes, Vector<Transform3D> *p_transforms, int p_preview_size); @@ -139,6 +141,7 @@ public: void popup_node_selector(const Callable &p_callback, const TypedArray<StringName> &p_valid_types = TypedArray<StringName>(), Node *p_current_value = nullptr); // Must use Vector<int> because exposing Vector<Variant::Type> is not supported. void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array(), const String &p_current_value = String()); + void popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value = String()); void popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types = TypedArray<StringName>()); // Editor docks. diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index aec374929e..db26a75cb8 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -100,20 +100,20 @@ void EditorLog::_update_theme() { log->add_theme_font_size_override("mono_font_size", font_size); log->end_bulk_theme_override(); - type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_editor_theme_icon(SNAME("Popup"))); - type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_editor_theme_icon(SNAME("StatusError"))); - type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_editor_theme_icon(SNAME("StatusWarning"))); - type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_editor_theme_icon(SNAME("Edit"))); + type_filter_map[MSG_TYPE_STD]->toggle_button->set_button_icon(get_editor_theme_icon(SNAME("Popup"))); + type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_button_icon(get_editor_theme_icon(SNAME("StatusError"))); + type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_button_icon(get_editor_theme_icon(SNAME("StatusWarning"))); + type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); type_filter_map[MSG_TYPE_STD]->toggle_button->set_theme_type_variation("EditorLogFilterButton"); type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_theme_type_variation("EditorLogFilterButton"); type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_theme_type_variation("EditorLogFilterButton"); type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_theme_type_variation("EditorLogFilterButton"); - clear_button->set_icon(get_editor_theme_icon(SNAME("Clear"))); - copy_button->set_icon(get_editor_theme_icon(SNAME("ActionCopy"))); - collapse_button->set_icon(get_editor_theme_icon(SNAME("CombineLines"))); - show_search_button->set_icon(get_editor_theme_icon(SNAME("Search"))); + clear_button->set_button_icon(get_editor_theme_icon(SNAME("Clear"))); + copy_button->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy"))); + collapse_button->set_button_icon(get_editor_theme_icon(SNAME("CombineLines"))); + show_search_button->set_button_icon(get_editor_theme_icon(SNAME("Search"))); search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); theme_cache.error_color = get_theme_color(SNAME("error_color"), EditorStringName(Editor)); @@ -204,7 +204,7 @@ void EditorLog::_clear_request() { log->clear(); messages.clear(); _reset_message_counts(); - tool_button->set_icon(Ref<Texture2D>()); + tool_button->set_button_icon(Ref<Texture2D>()); } void EditorLog::_copy_request() { @@ -359,14 +359,14 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { Ref<Texture2D> icon = theme_cache.error_icon; log->add_image(icon); log->add_text(" "); - tool_button->set_icon(icon); + tool_button->set_button_icon(icon); } break; case MSG_TYPE_WARNING: { log->push_color(theme_cache.warning_color); Ref<Texture2D> icon = theme_cache.warning_icon; log->add_image(icon); log->add_text(" "); - tool_button->set_icon(icon); + tool_button->set_button_icon(icon); } break; case MSG_TYPE_EDITOR: { // Distinguish editor messages from messages printed by the project diff --git a/editor/editor_main_screen.cpp b/editor/editor_main_screen.cpp index 77bbee5a7f..6da2bce60e 100644 --- a/editor/editor_main_screen.cpp +++ b/editor/editor_main_screen.cpp @@ -66,9 +66,9 @@ void EditorMainScreen::_notification(int p_what) { Ref<Texture2D> icon = p_editor->get_icon(); if (icon.is_valid()) { - tb->set_icon(icon); + tb->set_button_icon(icon); } else if (has_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { - tb->set_icon(get_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))); + tb->set_button_icon(get_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))); } } } break; @@ -244,7 +244,7 @@ void EditorMainScreen::add_main_plugin(EditorPlugin *p_editor) { icon = get_editor_theme_icon(p_editor->get_name()); } if (icon.is_valid()) { - tb->set_icon(icon); + tb->set_button_icon(icon); // Make sure the control is updated if the icon is reimported. icon->connect_changed(callable_mp((Control *)tb, &Control::update_minimum_size)); } diff --git a/editor/editor_main_screen.h b/editor/editor_main_screen.h index 153a182bc2..ca78ceaa88 100644 --- a/editor/editor_main_screen.h +++ b/editor/editor_main_screen.h @@ -47,6 +47,7 @@ public: EDITOR_2D = 0, EDITOR_3D, EDITOR_SCRIPT, + EDITOR_GAME, EDITOR_ASSETLIB, }; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index dd6c88ef25..f056a477c4 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -35,6 +35,7 @@ #include "core/input/input.h" #include "core/io/config_file.h" #include "core/io/file_access.h" +#include "core/io/image.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" #include "core/object/class_db.h" @@ -144,6 +145,7 @@ #include "editor/plugins/editor_plugin.h" #include "editor/plugins/editor_preview_plugins.h" #include "editor/plugins/editor_resource_conversion_plugin.h" +#include "editor/plugins/game_view_plugin.h" #include "editor/plugins/gdextension_export_plugin.h" #include "editor/plugins/material_editor_plugin.h" #include "editor/plugins/mesh_library_editor_plugin.h" @@ -251,8 +253,8 @@ void EditorNode::disambiguate_filenames(const Vector<String> p_full_paths, Vecto // append that to yield "folder/foo.tscn". if (difference > 0) { String parent = full_path.substr(0, difference); - int slash_idx = parent.rfind("/"); - slash_idx = parent.rfind("/", slash_idx - 1); + int slash_idx = parent.rfind_char('/'); + slash_idx = parent.rfind_char('/', slash_idx - 1); parent = (slash_idx >= 0 && parent.length() > 1) ? parent.substr(slash_idx + 1) : parent; r_filenames.write[set_idx] = parent + r_filenames[set_idx]; } @@ -356,6 +358,8 @@ void EditorNode::shortcut_input(const Ref<InputEvent> &p_event) { editor_main_screen->select(EditorMainScreen::EDITOR_3D); } else if (ED_IS_SHORTCUT("editor/editor_script", p_event)) { editor_main_screen->select(EditorMainScreen::EDITOR_SCRIPT); + } else if (ED_IS_SHORTCUT("editor/editor_game", p_event)) { + editor_main_screen->select(EditorMainScreen::EDITOR_GAME); } else if (ED_IS_SHORTCUT("editor/editor_help", p_event)) { emit_signal(SNAME("request_help_search"), ""); } else if (ED_IS_SHORTCUT("editor/editor_assetlib", p_event) && AssetLibraryEditorPlugin::is_available()) { @@ -531,7 +535,7 @@ void EditorNode::_update_theme(bool p_skip_creation) { editor_main_screen->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("Content"), EditorStringName(EditorStyles))); bottom_panel->add_theme_style_override(SceneStringName(panel), theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); - distraction_free->set_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons))); + distraction_free->set_button_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons))); distraction_free->add_theme_style_override(SceneStringName(pressed), theme->get_stylebox(CoreStringName(normal), "FlatMenuButton")); help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons))); @@ -620,7 +624,7 @@ void EditorNode::_notification(int p_what) { // Update the icon itself only when the spinner is visible. if (_should_display_update_spinner()) { - update_spinner->set_icon(theme->get_icon("Progress" + itos(update_spinner_step + 1), EditorStringName(EditorIcons))); + update_spinner->set_button_icon(theme->get_icon("Progress" + itos(update_spinner_step + 1), EditorStringName(EditorIcons))); } } @@ -1450,6 +1454,16 @@ void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String file->popup_file_dialog(); } +void EditorNode::ensure_uid_file(const String &p_new_resource_path) { + if (ResourceLoader::exists(p_new_resource_path) && !ResourceLoader::has_custom_uid_support(p_new_resource_path) && !FileAccess::exists(p_new_resource_path + ".uid")) { + Ref<FileAccess> f = FileAccess::open(p_new_resource_path + ".uid", FileAccess::WRITE); + if (f.is_valid()) { + const ResourceUID::ID id = ResourceUID::get_singleton()->create_id(); + f->store_line(ResourceUID::get_singleton()->id_to_text(id)); + } + } +} + void EditorNode::_menu_option(int p_option) { _menu_option_confirm(p_option, false); } @@ -2149,7 +2163,7 @@ void EditorNode::_dialog_action(String p_file) { } if (ml.is_null()) { - ml = Ref<MeshLibrary>(memnew(MeshLibrary)); + ml.instantiate(); } MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, merge_with_existing_library, apply_mesh_instance_transforms); @@ -2169,6 +2183,12 @@ void EditorNode::_dialog_action(String p_file) { case RESOURCE_SAVE_AS: { ERR_FAIL_COND(saving_resource.is_null()); save_resource_in_path(saving_resource, p_file); + + if (current_menu_option == RESOURCE_SAVE_AS) { + // Create .uid file when making new Resource. + ensure_uid_file(p_file); + } + saving_resource = Ref<Resource>(); ObjectID current_id = editor_history.get_current(); Object *current_obj = current_id.is_valid() ? ObjectDB::get_instance(current_id) : nullptr; @@ -3378,6 +3398,8 @@ void EditorNode::unload_editor_addons() { remove_editor_plugin(E.value, false); memdelete(E.value); } + + addon_name_to_plugin.clear(); } void EditorNode::_discard_changes(const String &p_str) { @@ -4672,6 +4694,11 @@ void EditorNode::stop_child_process(OS::ProcessID p_pid) { Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const { ERR_FAIL_NULL_V(p_object, nullptr); + const Node *node = Object::cast_to<const Node>(p_object); + if (node && node->has_meta(SceneStringName(_custom_type_script))) { + return node->get_meta(SceneStringName(_custom_type_script)); + } + Ref<Script> scr = p_object->get_script(); if (scr.is_valid()) { @@ -6569,6 +6596,7 @@ void EditorNode::_feature_profile_changed() { editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D)); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_SCRIPT)); + editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_GAME, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_GAME)); if (AssetLibraryEditorPlugin::is_available()) { editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_ASSET_LIB)); } @@ -6579,6 +6607,7 @@ void EditorNode::_feature_profile_changed() { editor_dock_manager->set_dock_enabled(history_dock, true); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_3D, true); editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_SCRIPT, true); + editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_GAME, true); if (AssetLibraryEditorPlugin::is_available()) { editor_main_screen->set_button_enabled(EditorMainScreen::EDITOR_ASSETLIB, true); } @@ -7489,7 +7518,7 @@ EditorNode::EditorNode() { update_spinner = memnew(MenuButton); right_menu_hb->add_child(update_spinner); - update_spinner->set_icon(theme->get_icon(SNAME("Progress1"), EditorStringName(EditorIcons))); + update_spinner->set_button_icon(theme->get_icon(SNAME("Progress1"), EditorStringName(EditorIcons))); update_spinner->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &EditorNode::_menu_option)); PopupMenu *p = update_spinner->get_popup(); p->add_radio_check_item(TTR("Update Continuously"), SETTINGS_UPDATE_CONTINUOUSLY); @@ -7677,24 +7706,28 @@ EditorNode::EditorNode() { disk_changed = memnew(ConfirmationDialog); { - disk_changed->set_title(TTR("Files have been modified on disk")); + disk_changed->set_title(TTR("Files have been modified outside Godot")); VBoxContainer *vbc = memnew(VBoxContainer); disk_changed->add_child(vbc); Label *dl = memnew(Label); - dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?")); + dl->set_text(TTR("The following files are newer on disk:")); vbc->add_child(dl); disk_changed_list = memnew(Tree); vbc->add_child(disk_changed_list); disk_changed_list->set_v_size_flags(Control::SIZE_EXPAND_FILL); + Label *what_action_label = memnew(Label); + what_action_label->set_text(TTR("What action should be taken?")); + vbc->add_child(what_action_label); + disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_reload_modified_scenes)); disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &EditorNode::_reload_project_settings)); - disk_changed->set_ok_button_text(TTR("Discard local changes and reload")); + disk_changed->set_ok_button_text(TTR("Reload from disk")); - disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->add_button(TTR("Ignore external changes"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); disk_changed->connect("custom_action", callable_mp(this, &EditorNode::_resave_scenes)); } @@ -7706,6 +7739,7 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(CanvasItemEditorPlugin)); add_editor_plugin(memnew(Node3DEditorPlugin)); add_editor_plugin(memnew(ScriptEditorPlugin)); + add_editor_plugin(memnew(GameViewPlugin)); EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor(); @@ -7888,12 +7922,14 @@ EditorNode::EditorNode() { ED_SHORTCUT_AND_COMMAND("editor/editor_2d", TTR("Open 2D Editor"), KeyModifierMask::CTRL | Key::F1); ED_SHORTCUT_AND_COMMAND("editor/editor_3d", TTR("Open 3D Editor"), KeyModifierMask::CTRL | Key::F2); ED_SHORTCUT_AND_COMMAND("editor/editor_script", TTR("Open Script Editor"), KeyModifierMask::CTRL | Key::F3); - ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTR("Open Asset Library"), KeyModifierMask::CTRL | Key::F4); + ED_SHORTCUT_AND_COMMAND("editor/editor_game", TTR("Open Game View"), KeyModifierMask::CTRL | Key::F4); + ED_SHORTCUT_AND_COMMAND("editor/editor_assetlib", TTR("Open Asset Library"), KeyModifierMask::CTRL | Key::F5); ED_SHORTCUT_OVERRIDE("editor/editor_2d", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_1); ED_SHORTCUT_OVERRIDE("editor/editor_3d", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_2); ED_SHORTCUT_OVERRIDE("editor/editor_script", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_3); - ED_SHORTCUT_OVERRIDE("editor/editor_assetlib", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_4); + ED_SHORTCUT_OVERRIDE("editor/editor_game", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_4); + ED_SHORTCUT_OVERRIDE("editor/editor_assetlib", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::KEY_5); ED_SHORTCUT_AND_COMMAND("editor/editor_next", TTR("Open the next Editor")); ED_SHORTCUT_AND_COMMAND("editor/editor_prev", TTR("Open the previous Editor")); diff --git a/editor/editor_node.h b/editor/editor_node.h index 696caf857c..4a283983c8 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -747,6 +747,7 @@ public: void save_resource_in_path(const Ref<Resource> &p_resource, const String &p_path); void save_resource(const Ref<Resource> &p_resource); void save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path = String()); + void ensure_uid_file(const String &p_new_resource_path); void show_about() { _menu_option_confirm(HELP_ABOUT, false); } @@ -789,7 +790,7 @@ public: struct AdditiveNodeEntry { Node *node = nullptr; - NodePath parent = NodePath(); + NodePath parent; Node *owner = nullptr; int index = 0; // Used if the original parent node is lost @@ -932,7 +933,7 @@ public: void dim_editor(bool p_dimming); bool is_editor_dimmed() const; - void edit_current() { _edit_current(); }; + void edit_current() { _edit_current(); } bool has_scenes_in_session(); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index c5a35e466c..2b2b32eb22 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -192,7 +192,7 @@ void EditorPropertyMultilineText::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { Ref<Texture2D> df = get_editor_theme_icon(SNAME("DistractionFree")); - open_big_text->set_icon(df); + open_big_text->set_button_icon(df); Ref<Font> font; int font_size; @@ -340,9 +340,9 @@ void EditorPropertyTextEnum::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - edit_button->set_icon(get_editor_theme_icon(SNAME("Edit"))); - accept_button->set_icon(get_editor_theme_icon(SNAME("ImportCheck"))); - cancel_button->set_icon(get_editor_theme_icon(SNAME("ImportFail"))); + edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); + accept_button->set_button_icon(get_editor_theme_icon(SNAME("ImportCheck"))); + cancel_button->set_button_icon(get_editor_theme_icon(SNAME("ImportFail"))); } break; } } @@ -428,7 +428,7 @@ void EditorPropertyLocale::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - locale_edit->set_icon(get_editor_theme_icon(SNAME("Translation"))); + locale_edit->set_button_icon(get_editor_theme_icon(SNAME("Translation"))); } break; } } @@ -462,10 +462,28 @@ void EditorPropertyPath::_set_read_only(bool p_read_only) { } void EditorPropertyPath::_path_selected(const String &p_path) { - emit_changed(get_edited_property(), p_path); + String full_path = p_path; + + if (!global) { + const ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path); + if (id != ResourceUID::INVALID_ID) { + full_path = ResourceUID::get_singleton()->id_to_text(id); + } + } + + emit_changed(get_edited_property(), full_path); update_property(); } +String EditorPropertyPath::_get_path_text() { + String full_path = get_edited_property_value(); + if (full_path.begins_with("uid://")) { + full_path = ResourceUID::uid_to_path(full_path); + } + + return full_path; +} + void EditorPropertyPath::_path_pressed() { if (!dialog) { dialog = memnew(EditorFileDialog); @@ -474,7 +492,7 @@ void EditorPropertyPath::_path_pressed() { add_child(dialog); } - String full_path = get_edited_property_value(); + String full_path = _get_path_text(); dialog->clear_filters(); @@ -502,7 +520,7 @@ void EditorPropertyPath::_path_pressed() { } void EditorPropertyPath::update_property() { - String full_path = get_edited_property_value(); + String full_path = _get_path_text(); path->set_text(full_path); path->set_tooltip_text(full_path); } @@ -522,9 +540,9 @@ void EditorPropertyPath::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (folder) { - path_edit->set_icon(get_editor_theme_icon(SNAME("FolderBrowse"))); + path_edit->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse"))); } else { - path_edit->set_icon(get_editor_theme_icon(SNAME("FileBrowse"))); + path_edit->set_button_icon(get_editor_theme_icon(SNAME("FileBrowse"))); } } break; } @@ -547,8 +565,7 @@ void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_d return; } - emit_changed(get_edited_property(), filesPaths[0]); - update_property(); + _path_selected(filesPaths[0]); } bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { @@ -1343,12 +1360,12 @@ void EditorPropertyObjectID::update_property() { edit->set_text(type + " ID: " + uitos(id)); edit->set_tooltip_text(type + " ID: " + uitos(id)); edit->set_disabled(false); - edit->set_icon(EditorNode::get_singleton()->get_class_icon(type)); + edit->set_button_icon(EditorNode::get_singleton()->get_class_icon(type)); } else { edit->set_text(TTR("<empty>")); edit->set_tooltip_text(""); edit->set_disabled(true); - edit->set_icon(Ref<Texture2D>()); + edit->set_button_icon(Ref<Texture2D>()); } } @@ -1378,7 +1395,7 @@ void EditorPropertySignal::update_property() { edit->set_text("Signal: " + signal.get_name()); edit->set_disabled(false); - edit->set_icon(get_editor_theme_icon(SNAME("Signals"))); + edit->set_button_icon(get_editor_theme_icon(SNAME("Signals"))); } EditorPropertySignal::EditorPropertySignal() { @@ -1397,7 +1414,7 @@ void EditorPropertyCallable::update_property() { edit->set_text("Callable"); edit->set_disabled(true); - edit->set_icon(get_editor_theme_icon(SNAME("Callable"))); + edit->set_button_icon(get_editor_theme_icon(SNAME("Callable"))); } EditorPropertyCallable::EditorPropertyCallable() { @@ -2024,9 +2041,9 @@ void EditorPropertyQuaternion::_notification(int p_what) { for (int i = 0; i < 3; i++) { euler[i]->add_theme_color_override("label_color", colors[i]); } - edit_button->set_icon(get_editor_theme_icon(SNAME("Edit"))); + edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); euler_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("property_color"), SNAME("EditorProperty"))); - warning->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); } break; } @@ -2638,7 +2655,7 @@ EditorPropertyColor::EditorPropertyColor() { void EditorPropertyNodePath::_set_read_only(bool p_read_only) { assign->set_disabled(p_read_only); menu->set_disabled(p_read_only); -}; +} Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool &r_valid) const { if (p_prop == get_edited_property()) { @@ -2648,7 +2665,7 @@ Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool return Variant(); } -void EditorPropertyNodePath::_node_selected(const NodePath &p_path) { +void EditorPropertyNodePath::_node_selected(const NodePath &p_path, bool p_absolute) { NodePath path = p_path; Node *base_node = get_base_node(); @@ -2658,7 +2675,7 @@ void EditorPropertyNodePath::_node_selected(const NodePath &p_path) { path = get_tree()->get_edited_scene_root()->get_path_to(to_node); } - if (base_node) { // for AnimationTrackKeyEdit + if (p_absolute && base_node) { // for AnimationTrackKeyEdit path = base_node->get_path().rel_path_to(p_path); } @@ -2680,7 +2697,7 @@ void EditorPropertyNodePath::_node_assign() { scene_tree->get_scene_tree()->set_show_enabled_subscene(true); scene_tree->set_valid_types(valid_types); add_child(scene_tree); - scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected)); + scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected).bind(true)); } Variant val = get_edited_property_value(); @@ -2748,7 +2765,7 @@ void EditorPropertyNodePath::_accept_text() { void EditorPropertyNodePath::_text_submitted(const String &p_text) { NodePath np = p_text; - emit_changed(get_edited_property(), np); + _node_selected(np, false); edit->hide(); assign->show(); menu->show(); @@ -2829,7 +2846,7 @@ void EditorPropertyNodePath::update_property() { assign->set_tooltip_text(p); if (p.is_empty()) { - assign->set_icon(Ref<Texture2D>()); + assign->set_button_icon(Ref<Texture2D>()); assign->set_text(TTR("Assign...")); assign->set_flat(false); return; @@ -2837,7 +2854,7 @@ void EditorPropertyNodePath::update_property() { assign->set_flat(true); if (!base_node || !base_node->has_node(p)) { - assign->set_icon(Ref<Texture2D>()); + assign->set_button_icon(Ref<Texture2D>()); assign->set_text(p); return; } @@ -2846,13 +2863,13 @@ void EditorPropertyNodePath::update_property() { ERR_FAIL_NULL(target_node); if (String(target_node->get_name()).contains("@")) { - assign->set_icon(Ref<Texture2D>()); + assign->set_button_icon(Ref<Texture2D>()); assign->set_text(p); return; } assign->set_text(target_node->get_name()); - assign->set_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node")); + assign->set_button_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node")); } void EditorPropertyNodePath::setup(const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root, bool p_editing_node) { @@ -2865,7 +2882,7 @@ void EditorPropertyNodePath::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); menu->get_popup()->set_item_icon(ACTION_CLEAR, get_editor_theme_icon(SNAME("Clear"))); menu->get_popup()->set_item_icon(ACTION_COPY, get_editor_theme_icon(SNAME("ActionCopy"))); menu->get_popup()->set_item_icon(ACTION_EDIT, get_editor_theme_icon(SNAME("Edit"))); @@ -3221,6 +3238,7 @@ void EditorPropertyResource::setup(Object *p_object, const String &p_path, const } resource_picker->set_base_type(p_base_type); + resource_picker->set_resource_owner(p_object); resource_picker->set_editable(true); resource_picker->set_h_size_flags(SIZE_EXPAND_FILL); add_child(resource_picker); diff --git a/editor/editor_properties.h b/editor/editor_properties.h index 004630da3e..ae9c454195 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -142,6 +142,8 @@ class EditorPropertyPath : public EditorProperty { LineEdit *path = nullptr; Button *path_edit = nullptr; + String _get_path_text(); + void _path_selected(const String &p_path); void _path_pressed(); void _path_focus_exited(); @@ -628,7 +630,7 @@ class EditorPropertyNodePath : public EditorProperty { bool editing_node = false; Vector<StringName> valid_types; - void _node_selected(const NodePath &p_path); + void _node_selected(const NodePath &p_path, bool p_absolute = true); void _node_assign(); Node *get_base_node(); void _update_menu(); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index f03eef4d4d..3cc3a0f7c2 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -283,7 +283,7 @@ void EditorPropertyArray::_create_new_property_slot() { HBoxContainer *hbox = memnew(HBoxContainer); Button *reorder_button = memnew(Button); - reorder_button->set_icon(get_editor_theme_icon(SNAME("TripleBar"))); + reorder_button->set_button_icon(get_editor_theme_icon(SNAME("TripleBar"))); reorder_button->set_default_cursor_shape(Control::CURSOR_MOVE); reorder_button->set_disabled(is_read_only()); reorder_button->connect(SceneStringName(gui_input), callable_mp(this, &EditorPropertyArray::_reorder_button_gui_input)); @@ -298,13 +298,13 @@ void EditorPropertyArray::_create_new_property_slot() { if (is_untyped_array) { Button *edit_btn = memnew(Button); - edit_btn->set_icon(get_editor_theme_icon(SNAME("Edit"))); + edit_btn->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); edit_btn->set_disabled(is_read_only()); edit_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_change_type).bind(edit_btn, idx)); hbox->add_child(edit_btn); } else { Button *remove_btn = memnew(Button); - remove_btn->set_icon(get_editor_theme_icon(SNAME("Remove"))); + remove_btn->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); remove_btn->set_disabled(is_read_only()); remove_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_remove_pressed).bind(idx)); hbox->add_child(remove_btn); @@ -395,7 +395,7 @@ void EditorPropertyArray::update_property() { vbox->add_child(property_vbox); button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Element")); - button_add_item->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyArray::_add_element)); button_add_item->set_disabled(is_read_only()); vbox->add_child(button_add_item); @@ -657,7 +657,7 @@ void EditorPropertyArray::_notification(int p_what) { } if (button_add_item) { - button_add_item->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } } break; @@ -740,10 +740,10 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint // The format of p_hint_string is: // subType/subTypeHint:nextSubtype ... etc. if (!p_hint_string.is_empty()) { - int hint_subtype_separator = p_hint_string.find(":"); + int hint_subtype_separator = p_hint_string.find_char(':'); if (hint_subtype_separator >= 0) { String subtype_string = p_hint_string.substr(0, hint_subtype_separator); - int slash_pos = subtype_string.find("/"); + int slash_pos = subtype_string.find_char('/'); if (slash_pos >= 0) { subtype_hint = PropertyHint(subtype_string.substr(slash_pos + 1, subtype_string.size() - slash_pos - 1).to_int()); subtype_string = subtype_string.substr(0, slash_pos); @@ -939,13 +939,13 @@ void EditorPropertyDictionary::_create_new_property_slot(int p_idx) { if (is_untyped_dict) { Button *edit_btn = memnew(Button); - edit_btn->set_icon(get_editor_theme_icon(SNAME("Edit"))); + edit_btn->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); edit_btn->set_disabled(is_read_only()); edit_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyDictionary::_change_type).bind(edit_btn, slots.size())); hbox->add_child(edit_btn); } else if (p_idx >= 0) { Button *remove_btn = memnew(Button); - remove_btn->set_icon(get_editor_theme_icon(SNAME("Remove"))); + remove_btn->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); remove_btn->set_disabled(is_read_only()); remove_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyDictionary::_remove_pressed).bind(slots.size())); hbox->add_child(remove_btn); @@ -1006,10 +1006,10 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_s PackedStringArray types = p_hint_string.split(";"); if (types.size() > 0 && !types[0].is_empty()) { String key = types[0]; - int hint_key_subtype_separator = key.find(":"); + int hint_key_subtype_separator = key.find_char(':'); if (hint_key_subtype_separator >= 0) { String key_subtype_string = key.substr(0, hint_key_subtype_separator); - int slash_pos = key_subtype_string.find("/"); + int slash_pos = key_subtype_string.find_char('/'); if (slash_pos >= 0) { key_subtype_hint = PropertyHint(key_subtype_string.substr(slash_pos + 1, key_subtype_string.size() - slash_pos - 1).to_int()); key_subtype_string = key_subtype_string.substr(0, slash_pos); @@ -1025,10 +1025,10 @@ void EditorPropertyDictionary::setup(PropertyHint p_hint, const String &p_hint_s } if (types.size() > 1 && !types[1].is_empty()) { String value = types[1]; - int hint_value_subtype_separator = value.find(":"); + int hint_value_subtype_separator = value.find_char(':'); if (hint_value_subtype_separator >= 0) { String value_subtype_string = value.substr(0, hint_value_subtype_separator); - int slash_pos = value_subtype_string.find("/"); + int slash_pos = value_subtype_string.find_char('/'); if (slash_pos >= 0) { value_subtype_hint = PropertyHint(value_subtype_string.substr(slash_pos + 1, value_subtype_string.size() - slash_pos - 1).to_int()); value_subtype_string = value_subtype_string.substr(0, slash_pos); @@ -1122,7 +1122,7 @@ void EditorPropertyDictionary::update_property() { _create_new_property_slot(EditorPropertyDictionaryObject::NEW_VALUE_INDEX); button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Key/Value Pair")); - button_add_item->set_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons))); + button_add_item->set_button_icon(get_theme_icon(SNAME("Add"), EditorStringName(EditorIcons))); button_add_item->set_disabled(is_read_only()); button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyDictionary::_add_key_value)); add_vbox->add_child(button_add_item); @@ -1173,6 +1173,11 @@ void EditorPropertyDictionary::update_property() { new_prop->set_h_size_flags(SIZE_EXPAND_FILL); new_prop->set_read_only(is_read_only()); slot.set_prop(new_prop); + } else if (slot.index != EditorPropertyDictionaryObject::NEW_KEY_INDEX && slot.index != EditorPropertyDictionaryObject::NEW_VALUE_INDEX) { + Variant key = dict.get_key_at_index(slot.index); + String cs = key.get_construct_string(); + slot.prop->set_label(cs); + slot.prop->set_tooltip_text(cs); } // We need to grab the focus of the property that is being changed, even if the type didn't actually changed. @@ -1200,7 +1205,8 @@ void EditorPropertyDictionary::update_property() { void EditorPropertyDictionary::_remove_pressed(int p_slot_index) { Dictionary dict = object->get_dict().duplicate(); - dict.erase(dict.get_key_at_index(p_slot_index)); + int index = slots[p_slot_index].index; + dict.erase(dict.get_key_at_index(index)); emit_changed(get_edited_property(), dict); } @@ -1227,7 +1233,7 @@ void EditorPropertyDictionary::_notification(int p_what) { } if (button_add_item) { - button_add_item->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); add_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("DictionaryAddItem"))); } } break; @@ -1424,7 +1430,7 @@ void EditorPropertyLocalizableString::update_property() { hbox->add_child(prop); prop->set_h_size_flags(SIZE_EXPAND_FILL); Button *edit_btn = memnew(Button); - edit_btn->set_icon(get_editor_theme_icon(SNAME("Remove"))); + edit_btn->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); hbox->add_child(edit_btn); edit_btn->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLocalizableString::_remove_item).bind(edit_btn, remove_index)); @@ -1433,7 +1439,7 @@ void EditorPropertyLocalizableString::update_property() { if (page_index == max_page) { button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Translation")); - button_add_item->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); button_add_item->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLocalizableString::_add_locale_popup)); property_vbox->add_child(button_add_item); } @@ -1459,7 +1465,7 @@ void EditorPropertyLocalizableString::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_ENTER_TREE: { if (button_add_item) { - button_add_item->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_add_item->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } } break; } diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index e38ab456cb..ca8854f797 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -198,6 +198,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["gi"] = "GI"; capitalize_string_remaps["gl"] = "GL"; capitalize_string_remaps["glb"] = "GLB"; + capitalize_string_remaps["gles"] = "GLES"; capitalize_string_remaps["gles2"] = "GLES2"; capitalize_string_remaps["gles3"] = "GLES3"; capitalize_string_remaps["gltf"] = "glTF"; @@ -231,6 +232,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["kb"] = "(KB)"; // Unit. capitalize_string_remaps["lcd"] = "LCD"; capitalize_string_remaps["ldr"] = "LDR"; + capitalize_string_remaps["linuxbsd"] = "Linux/*BSD"; capitalize_string_remaps["lod"] = "LOD"; capitalize_string_remaps["lods"] = "LODs"; capitalize_string_remaps["lowpass"] = "Low-pass"; @@ -248,6 +250,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["oidn"] = "OIDN"; capitalize_string_remaps["ok"] = "OK"; capitalize_string_remaps["opengl"] = "OpenGL"; + capitalize_string_remaps["opengl3"] = "OpenGL 3"; capitalize_string_remaps["opentype"] = "OpenType"; capitalize_string_remaps["openxr"] = "OpenXR"; capitalize_string_remaps["osslsigncode"] = "osslsigncode"; diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index 0f0287718c..4c3fc7f8c2 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -61,11 +61,11 @@ void EditorResourcePicker::_update_resource() { assign_button->set_custom_minimum_size(assign_button_min_size); if (edited_resource == Ref<Resource>()) { - assign_button->set_icon(Ref<Texture2D>()); + assign_button->set_button_icon(Ref<Texture2D>()); assign_button->set_text(TTR("<empty>")); assign_button->set_tooltip_text(""); } else { - assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), SNAME("Object"))); + assign_button->set_button_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), SNAME("Object"))); if (!edited_resource->get_name().is_empty()) { assign_button->set_text(edited_resource->get_name()); @@ -103,7 +103,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(CoreStringName(normal))->get_content_margin(SIDE_LEFT) + get_theme_constant(SNAME("h_separation"), SNAME("Button"))); + preview_rect->set_offset(SIDE_LEFT, assign_button->get_button_icon()->get_width() + assign_button->get_theme_stylebox(CoreStringName(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()) { @@ -224,7 +224,9 @@ void EditorResourcePicker::_update_menu_items() { } if (is_editable()) { - edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR); + if (!_is_custom_type_script()) { + edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Clear")), TTR("Clear"), OBJ_MENU_CLEAR); + } edit_menu->add_icon_item(get_editor_theme_icon(SNAME("Duplicate")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE); // Check whether the resource has subresources. @@ -694,6 +696,16 @@ bool EditorResourcePicker::_is_type_valid(const String &p_type_name, const HashS return false; } +bool EditorResourcePicker::_is_custom_type_script() const { + Ref<Script> resource_as_script = edited_resource; + + if (resource_as_script.is_valid() && resource_owner && resource_owner->has_meta(SceneStringName(_custom_type_script))) { + return true; + } + + return false; +} + Variant EditorResourcePicker::get_drag_data_fw(const Point2 &p_point, Control *p_from) { if (edited_resource.is_valid()) { Dictionary drag_data = EditorNode::get_singleton()->drag_resource(edited_resource, p_from); @@ -819,7 +831,7 @@ void EditorResourcePicker::_notification(int p_what) { edit_menu->add_theme_constant_override("icon_max_width", icon_width); } - edit_button->set_icon(get_theme_icon(SNAME("select_arrow"), SNAME("Tree"))); + edit_button->set_button_icon(get_theme_icon(SNAME("select_arrow"), SNAME("Tree"))); } break; case NOTIFICATION_DRAW: { @@ -953,6 +965,10 @@ bool EditorResourcePicker::is_toggle_pressed() const { return assign_button->is_pressed(); } +void EditorResourcePicker::set_resource_owner(Object *p_object) { + resource_owner = p_object; +} + void EditorResourcePicker::set_editable(bool p_editable) { editable = p_editable; assign_button->set_disabled(!editable && !edited_resource.is_valid()); @@ -1098,7 +1114,10 @@ void EditorScriptPicker::set_create_options(Object *p_menu_node) { return; } - menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT); + if (!(script_owner && script_owner->has_meta(SceneStringName(_custom_type_script)))) { + menu_node->add_icon_item(get_editor_theme_icon(SNAME("ScriptCreate")), TTR("New Script..."), OBJ_MENU_NEW_SCRIPT); + } + if (script_owner) { Ref<Script> scr = script_owner->get_script(); if (scr.is_valid()) { diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 0a32dea3ed..8fb774a2cb 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -81,6 +81,8 @@ class EditorResourcePicker : public HBoxContainer { CONVERT_BASE_ID = 1000, }; + Object *resource_owner = nullptr; + PopupMenu *edit_menu = nullptr; void _update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj); @@ -102,6 +104,7 @@ class EditorResourcePicker : public HBoxContainer { void _ensure_allowed_types() const; bool _is_drop_valid(const Dictionary &p_drag_data) const; bool _is_type_valid(const String &p_type_name, const HashSet<StringName> &p_allowed_types) const; + bool _is_custom_type_script() const; 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; @@ -137,6 +140,8 @@ public: void set_toggle_pressed(bool p_pressed); bool is_toggle_pressed() const; + void set_resource_owner(Object *p_object); + void set_editable(bool p_editable); bool is_editable() const; diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index d5135f4198..caed02ae58 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -223,11 +223,6 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { args.push_back(p_scene); } - // Pass the debugger stop shortcut to the running instance(s). - String shortcut; - VariantWriter::write_to_string(ED_GET_SHORTCUT("editor/stop_running_project"), shortcut); - OS::get_singleton()->set_environment("__GODOT_EDITOR_STOP_SHORTCUT__", shortcut); - String exec = OS::get_singleton()->get_executable_path(); int instance_count = RunInstancesDialog::get_singleton()->get_instance_count(); for (int i = 0; i < instance_count; i++) { diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index e0e1ef6d19..3e7c8466a2 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -39,7 +39,7 @@ void EditorRunNative::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - remote_debug->set_icon(get_editor_theme_icon(SNAME("PlayRemote"))); + remote_debug->set_button_icon(get_editor_theme_icon(SNAME("PlayRemote"))); } break; case NOTIFICATION_PROCESS: { diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index 27cbb9810c..d88cc4d5fa 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -96,7 +96,7 @@ class SectionedInspectorFilter : public Object { List<PropertyInfo> pinfo; edited->get_property_list(&pinfo); for (PropertyInfo &pi : pinfo) { - int sp = pi.name.find("/"); + int sp = pi.name.find_char('/'); if (pi.name == "resource_path" || pi.name == "resource_name" || pi.name == "resource_local_to_scene" || pi.name.begins_with("script/") || pi.name.begins_with("_global_script")) { //skip resource stuff continue; @@ -255,7 +255,7 @@ void SectionedInspector::update_category_list() { continue; } - int sp = pi.name.find("/"); + int sp = pi.name.find_char('/'); if (sp == -1) { pi.name = "global/" + pi.name; } diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 12a7c3a2ff..b0d1c3e6bb 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -602,6 +602,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "filesystem/file_dialog/thumbnail_size", 64, "32,128,16") // Quick Open dialog + EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "filesystem/quick_open_dialog/max_results", 100, "0,10000,1", PROPERTY_USAGE_DEFAULT) + _initial_set("filesystem/quick_open_dialog/show_search_highlight", true); + _initial_set("filesystem/quick_open_dialog/enable_fuzzy_matching", true); + EDITOR_SETTING_USAGE(Variant::INT, PROPERTY_HINT_RANGE, "filesystem/quick_open_dialog/max_fuzzy_misses", 2, "0,10,1", PROPERTY_USAGE_DEFAULT) _initial_set("filesystem/quick_open_dialog/include_addons", false); EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "filesystem/quick_open_dialog/default_display_mode", 0, "Adaptive,Last Used") @@ -711,6 +715,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("text_editor/script_list/sort_members_outline_alphabetically", false, true); _initial_set("text_editor/script_list/script_temperature_enabled", true); _initial_set("text_editor/script_list/script_temperature_history_size", 15); + _initial_set("text_editor/script_list/highlight_scene_scripts", true); _initial_set("text_editor/script_list/group_help_pages", true); EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/script_list/sort_scripts_by", 0, "Name,Path,None"); EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "text_editor/script_list/list_script_names_as", 0, "Name,Parent Directory And Name,Full Path"); @@ -1225,7 +1230,7 @@ fail: extra_config->set_value("init_projects", "list", list); } - singleton = Ref<EditorSettings>(memnew(EditorSettings)); + singleton.instantiate(); singleton->set_path(config_file_path, true); singleton->save_changed_setting = true; singleton->_load_defaults(extra_config); @@ -1492,10 +1497,26 @@ void EditorSettings::set_favorites(const Vector<String> &p_favorites) { } } +void EditorSettings::set_favorite_properties(const HashMap<String, PackedStringArray> &p_favorite_properties) { + favorite_properties = p_favorite_properties; + String favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties"); + + Ref<ConfigFile> cf; + cf.instantiate(); + for (const KeyValue<String, PackedStringArray> &kv : p_favorite_properties) { + cf->set_value(kv.key, "properties", kv.value); + } + cf->save(favorite_properties_file); +} + Vector<String> EditorSettings::get_favorites() const { return favorites; } +HashMap<String, PackedStringArray> EditorSettings::get_favorite_properties() const { + return favorite_properties; +} + void EditorSettings::set_recent_dirs(const Vector<String> &p_recent_dirs) { recent_dirs = p_recent_dirs; String recent_dirs_file; @@ -1518,23 +1539,51 @@ Vector<String> EditorSettings::get_recent_dirs() const { void EditorSettings::load_favorites_and_recent_dirs() { String favorites_file; + String favorite_properties_file; String recent_dirs_file; if (Engine::get_singleton()->is_project_manager_hint()) { favorites_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_dirs"); + favorite_properties_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_properties"); recent_dirs_file = EditorPaths::get_singleton()->get_config_dir().path_join("recent_dirs"); } else { favorites_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorites"); + favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties"); recent_dirs_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("recent_dirs"); } + + /// File Favorites + Ref<FileAccess> f = FileAccess::open(favorites_file, FileAccess::READ); if (f.is_valid()) { String line = f->get_line().strip_edges(); while (!line.is_empty()) { - favorites.push_back(line); + favorites.append(line); line = f->get_line().strip_edges(); } } + /// Inspector Favorites + + Ref<ConfigFile> cf; + cf.instantiate(); + if (cf->load(favorite_properties_file) == OK) { + List<String> secs; + cf->get_sections(&secs); + + for (String &E : secs) { + PackedStringArray properties = PackedStringArray(cf->get_value(E, "properties")); + if (EditorNode::get_editor_data().is_type_recognized(E) || ResourceLoader::exists(E, "Script")) { + for (const String &property : properties) { + if (!favorite_properties[E].has(property)) { + favorite_properties[E].push_back(property); + } + } + } + } + } + + /// Recent Directories + f = FileAccess::open(recent_dirs_file, FileAccess::READ); if (f.is_valid()) { String line = f->get_line().strip_edges(); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index d1ccedfe6c..3c8a4de866 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -102,6 +102,7 @@ private: HashMap<String, List<Ref<InputEvent>>> builtin_action_overrides; Vector<String> favorites; + HashMap<String, PackedStringArray> favorite_properties; Vector<String> recent_dirs; bool save_changed_setting = true; @@ -176,6 +177,8 @@ public: void set_favorites(const Vector<String> &p_favorites); Vector<String> get_favorites() const; + void set_favorite_properties(const HashMap<String, PackedStringArray> &p_favorite_properties); + HashMap<String, PackedStringArray> get_favorite_properties() const; void set_recent_dirs(const Vector<String> &p_recent_dirs); Vector<String> get_recent_dirs() const; void load_favorites_and_recent_dirs(); diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index d07608d852..8989b9cf9b 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -106,8 +106,8 @@ void EditorSettingsDialog::update_navigation_preset() { orbit_mod_key_2 = InputEventKey::create_reference(Key::NONE); pan_mod_key_1 = InputEventKey::create_reference(Key::SHIFT); pan_mod_key_2 = InputEventKey::create_reference(Key::NONE); - zoom_mod_key_1 = InputEventKey::create_reference(Key::SHIFT); - zoom_mod_key_2 = InputEventKey::create_reference(Key::CTRL); + zoom_mod_key_1 = InputEventKey::create_reference(Key::CTRL); + zoom_mod_key_2 = InputEventKey::create_reference(Key::NONE); } else if (nav_scheme == Node3DEditorViewport::NAVIGATION_MAYA) { set_preset = true; set_orbit_mouse_button = Node3DEditorViewport::NAVIGATION_LEFT_MOUSE; @@ -285,7 +285,7 @@ void EditorSettingsDialog::_update_icons() { shortcut_search_box->set_right_icon(shortcuts->get_editor_theme_icon(SNAME("Search"))); shortcut_search_box->set_clear_button_enabled(true); - restart_close_button->set_icon(shortcuts->get_editor_theme_icon(SNAME("Close"))); + restart_close_button->set_button_icon(shortcuts->get_editor_theme_icon(SNAME("Close"))); restart_container->add_theme_style_override(SceneStringName(panel), shortcuts->get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); restart_icon->set_texture(shortcuts->get_editor_theme_icon(SNAME("StatusWarning"))); restart_label->add_theme_color_override(SceneStringName(font_color), shortcuts->get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); diff --git a/editor/editor_translation_parser.cpp b/editor/editor_translation_parser.cpp index 8a77ce4a82..d12bbc1af2 100644 --- a/editor/editor_translation_parser.cpp +++ b/editor/editor_translation_parser.cpp @@ -31,7 +31,6 @@ #include "editor_translation_parser.h" #include "core/error/error_macros.h" -#include "core/io/file_access.h" #include "core/object/script_language.h" #include "core/templates/hash_set.h" @@ -65,6 +64,21 @@ Error EditorTranslationParserPlugin::parse_file(const String &p_path, Vector<Str } } +void EditorTranslationParserPlugin::get_comments(Vector<String> *r_ids_comment, Vector<String> *r_ids_ctx_plural_comment) { + TypedArray<String> ids_comment; + TypedArray<String> ids_ctx_plural_comment; + + if (GDVIRTUAL_CALL(_get_comments, ids_comment, ids_ctx_plural_comment)) { + for (int i = 0; i < ids_comment.size(); i++) { + r_ids_comment->append(ids_comment[i]); + } + + for (int i = 0; i < ids_ctx_plural_comment.size(); i++) { + r_ids_ctx_plural_comment->append(ids_ctx_plural_comment[i]); + } + } +} + void EditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_extensions) const { Vector<String> extensions; if (GDVIRTUAL_CALL(_get_recognized_extensions, extensions)) { @@ -78,6 +92,7 @@ void EditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_ex void EditorTranslationParserPlugin::_bind_methods() { GDVIRTUAL_BIND(_parse_file, "path", "msgids", "msgids_context_plural"); + GDVIRTUAL_BIND(_get_comments, "msgids_comment", "msgids_context_plural_comment"); GDVIRTUAL_BIND(_get_recognized_extensions); } diff --git a/editor/editor_translation_parser.h b/editor/editor_translation_parser.h index 78dc726c52..20c8ed7939 100644 --- a/editor/editor_translation_parser.h +++ b/editor/editor_translation_parser.h @@ -43,10 +43,12 @@ protected: static void _bind_methods(); GDVIRTUAL3(_parse_file, String, TypedArray<String>, TypedArray<Array>) + GDVIRTUAL2(_get_comments, TypedArray<String>, TypedArray<String>) GDVIRTUAL0RC(Vector<String>, _get_recognized_extensions) public: virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural); + virtual void get_comments(Vector<String> *r_ids_comment, Vector<String> *r_ids_ctx_plural_comment); virtual void get_recognized_extensions(List<String> *r_extensions) const; }; diff --git a/editor/engine_update_label.cpp b/editor/engine_update_label.cpp index facbfc7c6b..0d40181257 100644 --- a/editor/engine_update_label.cpp +++ b/editor/engine_update_label.cpp @@ -240,8 +240,8 @@ EngineUpdateLabel::VersionType EngineUpdateLabel::_get_version_type(const String } String EngineUpdateLabel::_extract_sub_string(const String &p_line) const { - int j = p_line.find("\"") + 1; - return p_line.substr(j, p_line.find("\"", j) - j); + int j = p_line.find_char('"') + 1; + return p_line.substr(j, p_line.find_char('"', j) - j); } void EngineUpdateLabel::_notification(int p_what) { diff --git a/editor/export/codesign.cpp b/editor/export/codesign.cpp index 72d496b04d..cc53068d48 100644 --- a/editor/export/codesign.cpp +++ b/editor/export/codesign.cpp @@ -1381,14 +1381,14 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const r_error_msg = TTR("Invalid entitlements file."); ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid entitlements file."); } - cet = Ref<CodeSignEntitlementsText>(memnew(CodeSignEntitlementsText(entitlements))); - ceb = Ref<CodeSignEntitlementsBinary>(memnew(CodeSignEntitlementsBinary(entitlements))); + cet.instantiate(entitlements); + ceb.instantiate(entitlements); } print_verbose("CodeSign: Generating requirements..."); Ref<CodeSignRequirements> rq; String team_id = ""; - rq = Ref<CodeSignRequirements>(memnew(CodeSignRequirements())); + rq.instantiate(); // Sign executables. for (int i = 0; i < files_to_sign.size(); i++) { @@ -1487,7 +1487,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const print_verbose("CodeSign: Generating signature..."); Ref<CodeSignSignature> cs; - cs = Ref<CodeSignSignature>(memnew(CodeSignSignature())); + cs.instantiate(); print_verbose("CodeSign: Writing signature superblob..."); // Write signature data to the executable. diff --git a/editor/export/codesign.h b/editor/export/codesign.h index 9a858c49ac..92366da0f6 100644 --- a/editor/export/codesign.h +++ b/editor/export/codesign.h @@ -166,7 +166,7 @@ public: virtual int get_size() const override; - virtual uint32_t get_index_type() const override { return 0x00000002; }; + virtual uint32_t get_index_type() const override { return 0x00000002; } virtual void write_to_file(Ref<FileAccess> p_file) const override; }; @@ -188,7 +188,7 @@ public: virtual int get_size() const override; - virtual uint32_t get_index_type() const override { return 0x00000005; }; + virtual uint32_t get_index_type() const override { return 0x00000005; } virtual void write_to_file(Ref<FileAccess> p_file) const override; }; @@ -210,7 +210,7 @@ public: virtual int get_size() const override; - virtual uint32_t get_index_type() const override { return 0x00000007; }; + virtual uint32_t get_index_type() const override { return 0x00000007; } virtual void write_to_file(Ref<FileAccess> p_file) const override; }; @@ -311,7 +311,7 @@ public: virtual PackedByteArray get_hash_sha256() const override; virtual int get_size() const override; - virtual uint32_t get_index_type() const override { return 0x00000000; }; + virtual uint32_t get_index_type() const override { return 0x00000000; } virtual void write_to_file(Ref<FileAccess> p_file) const override; }; @@ -330,7 +330,7 @@ public: virtual PackedByteArray get_hash_sha256() const override; virtual int get_size() const override; - virtual uint32_t get_index_type() const override { return 0x00010000; }; + virtual uint32_t get_index_type() const override { return 0x00010000; } virtual void write_to_file(Ref<FileAccess> p_file) const override; }; diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 6ca83c5e25..cddc8173cb 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -87,6 +87,8 @@ void EditorExport::_save() { config->set_value(section, "encryption_include_filters", preset->get_enc_in_filter()); config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter()); + config->set_value(section, "seed", preset->get_seed()); + config->set_value(section, "encrypt_pck", preset->get_enc_pck()); config->set_value(section, "encrypt_directory", preset->get_enc_directory()); config->set_value(section, "script_export_mode", preset->get_script_export_mode()); @@ -307,6 +309,9 @@ void EditorExport::load_config() { preset->set_script_export_mode(config->get_value(section, "script_export_mode", EditorExportPreset::MODE_SCRIPT_BINARY_TOKENS_COMPRESSED)); preset->set_patches(config->get_value(section, "patches", Vector<String>())); + if (config->has_section_key(section, "seed")) { + preset->set_seed(config->get_value(section, "seed")); + } if (config->has_section_key(section, "encrypt_pck")) { preset->set_enc_pck(config->get_value(section, "encrypt_pck")); } diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index 50fa49dc52..27216c2399 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -59,6 +59,17 @@ static int _get_pad(int p_alignment, int p_n) { return pad; } +template <typename T> +static bool _has_pack_path(const T &p_paths, const String &p_path) { + for (const String &E : p_paths) { + if (E.simplify_path().trim_prefix("res://") == p_path) { + return true; + } + } + + return false; +} + #define PCK_PADDING 16 bool EditorExportPlatform::fill_log_messages(RichTextLabel *p_log, Error p_err) { @@ -205,26 +216,28 @@ void EditorExportPlatform::_unload_patches() { PackedData::get_singleton()->clear(); } -Error EditorExportPlatform::_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) { +Error EditorExportPlatform::_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, uint64_t p_seed) { ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export."); PackData *pd = (PackData *)p_userdata; + String simplified_path = p_path.simplify_path(); + SavedData sd; - sd.path_utf8 = p_path.utf8(); + sd.path_utf8 = simplified_path.trim_prefix("res://").utf8(); sd.ofs = pd->f->get_position(); sd.size = p_data.size(); sd.encrypted = false; for (int i = 0; i < p_enc_in_filters.size(); ++i) { - if (p_path.matchn(p_enc_in_filters[i]) || p_path.replace("res://", "").matchn(p_enc_in_filters[i])) { + if (simplified_path.matchn(p_enc_in_filters[i]) || simplified_path.trim_prefix("res://").matchn(p_enc_in_filters[i])) { sd.encrypted = true; break; } } for (int i = 0; i < p_enc_ex_filters.size(); ++i) { - if (p_path.matchn(p_enc_ex_filters[i]) || p_path.replace("res://", "").matchn(p_enc_ex_filters[i])) { + if (simplified_path.matchn(p_enc_ex_filters[i]) || simplified_path.trim_prefix("res://").matchn(p_enc_ex_filters[i])) { sd.encrypted = false; break; } @@ -234,10 +247,27 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa Ref<FileAccess> ftmp = pd->f; if (sd.encrypted) { + Vector<uint8_t> iv; + if (p_seed != 0) { + uint64_t seed = p_seed; + + const uint8_t *ptr = p_data.ptr(); + int64_t len = p_data.size(); + for (int64_t i = 0; i < len; i++) { + seed = ((seed << 5) + seed) ^ ptr[i]; + } + + RandomPCG rng = RandomPCG(seed, RandomPCG::DEFAULT_INC); + iv.resize(16); + for (int i = 0; i < 16; i++) { + iv.write[i] = rng.rand() % 256; + } + } + fae.instantiate(); ERR_FAIL_COND_V(fae.is_null(), ERR_SKIP); - Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false); + Error err = fae->open_and_parse(ftmp, p_key, FileAccessEncrypted::MODE_WRITE_AES256, false, iv); ERR_FAIL_COND_V(err != OK, ERR_SKIP); ftmp = fae; } @@ -275,15 +305,15 @@ Error EditorExportPlatform::_save_pack_file(void *p_userdata, const String &p_pa return OK; } -Error EditorExportPlatform::_save_pack_patch_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) { +Error EditorExportPlatform::_save_pack_patch_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, uint64_t p_seed) { if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) { return OK; } - return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key); + return _save_pack_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed); } -Error EditorExportPlatform::_save_zip_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) { +Error EditorExportPlatform::_save_zip_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, uint64_t p_seed) { ERR_FAIL_COND_V_MSG(p_total < 1, ERR_PARAMETER_RANGE_ERROR, "Must select at least one file to export."); String path = p_path.replace_first("res://", ""); @@ -315,12 +345,12 @@ Error EditorExportPlatform::_save_zip_file(void *p_userdata, const String &p_pat return OK; } -Error EditorExportPlatform::_save_zip_patch_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) { +Error EditorExportPlatform::_save_zip_patch_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, uint64_t p_seed) { if (_check_hash(PackedData::get_singleton()->get_file_hash(p_path), p_data)) { return OK; } - return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key); + return _save_zip_file(p_userdata, p_path, p_data, p_file, p_total, p_enc_in_filters, p_enc_ex_filters, p_key, p_seed); } Ref<ImageTexture> EditorExportPlatform::get_option_icon(int p_index) const { @@ -919,7 +949,7 @@ Vector<String> EditorExportPlatform::get_forced_export_files() { return files; } -Error EditorExportPlatform::_script_save_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) { +Error EditorExportPlatform::_script_save_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, uint64_t p_seed) { Callable cb = ((ScriptCallbackData *)p_userdata)->file_cb; ERR_FAIL_COND_V(!cb.is_valid(), FAILED); @@ -965,10 +995,10 @@ Error EditorExportPlatform::_export_project_files(const Ref<EditorExportPreset> ScriptCallbackData data; data.file_cb = p_save_func; data.so_cb = p_so_func; - return export_project_files(p_preset, p_debug, _script_save_file, &data, _script_add_shared_object); + return export_project_files(p_preset, p_debug, _script_save_file, nullptr, &data, _script_add_shared_object); } -Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func) { +Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, void *p_udata, EditorExportSaveSharedObject p_so_func) { //figure out paths of files that will be exported HashSet<String> paths; Vector<String> path_remaps; @@ -1030,8 +1060,10 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<String> enc_in_filters; Vector<String> enc_ex_filters; Vector<uint8_t> key; + uint64_t seed = 0; if (enc_pck) { + seed = p_preset->get_seed(); Vector<String> enc_in_split = p_preset->get_enc_in_filter().split(","); for (int i = 0; i < enc_in_split.size(); i++) { String f = enc_in_split[i].strip_edges(); @@ -1082,6 +1114,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Error err = OK; Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins(); + Vector<String> extra_paths; struct SortByName { bool operator()(const Ref<EditorExportPlugin> &left, const Ref<EditorExportPlugin> &right) const { @@ -1102,10 +1135,12 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } } for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) { - err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, 0, paths.size(), enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } + + extra_paths.push_back(export_plugins[i]->extra_files[j].path); } export_plugins.write[i]->_clear(); @@ -1218,7 +1253,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } for (int j = 0; j < export_plugins[i]->extra_files.size(); j++) { - err = p_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_plugins[i]->extra_files[j].path, export_plugins[i]->extra_files[j].data, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1227,6 +1262,8 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & path_remaps.push_back(path); path_remaps.push_back(export_plugins[i]->extra_files[j].path); } + + extra_paths.push_back(export_plugins[i]->extra_files[j].path); } if (export_plugins[i]->skipped) { @@ -1248,7 +1285,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (importer_type == "keep") { // Just keep file as-is. Vector<uint8_t> array = FileAccess::get_file_as_bytes(path); - err = p_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; @@ -1291,13 +1328,13 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & sarr.resize(cs.size()); memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); - err = p_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } // Now actual remapped file: sarr = FileAccess::get_file_as_bytes(export_path); - err = p_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_path, sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1327,14 +1364,14 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & if (remap == "path") { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); - err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); } else if (remap.begins_with("path.")) { String feature = remap.get_slice(".", 1); if (remap_features.has(feature)) { String remapped_path = config->get_value("remap", remap); Vector<uint8_t> array = FileAccess::get_file_as_bytes(remapped_path); - err = p_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, remapped_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); } else { // Remove paths if feature not enabled. config->erase_section_key("remap", remap); @@ -1360,7 +1397,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & sarr.resize(cs.size()); memcpy(sarr.ptrw(), cs.ptr(), sarr.size()); - err = p_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, path + ".import", sarr, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; @@ -1381,7 +1418,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & } Vector<uint8_t> array = FileAccess::get_file_as_bytes(export_path); - err = p_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, export_path, array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1445,7 +1482,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & new_file.write[j] = utf8[j]; } - err = p_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, from + ".remap", new_file, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1458,8 +1495,16 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<String> forced_export = get_forced_export_files(); for (int i = 0; i < forced_export.size(); i++) { - Vector<uint8_t> array = FileAccess::get_file_as_bytes(forced_export[i]); - err = p_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key); + Vector<uint8_t> array; + if (GDExtension::get_extension_list_config_file() == forced_export[i]) { + array = _filter_extension_list_config_file(forced_export[i], paths); + if (array.size() == 0) { + continue; + } + } else { + array = FileAccess::get_file_as_bytes(forced_export[i]); + } + err = p_save_func(p_udata, forced_export[i], array, idx, total, enc_in_filters, enc_ex_filters, key, seed); if (err != OK) { return err; } @@ -1471,7 +1516,46 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> & Vector<uint8_t> data = FileAccess::get_file_as_bytes(engine_cfb); DirAccess::remove_file_or_error(engine_cfb); - return p_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key); + err = p_save_func(p_udata, "res://" + config_file, data, idx, total, enc_in_filters, enc_ex_filters, key, seed); + if (err != OK) { + return err; + } + + if (p_remove_func) { + for (const String &E : PackedData::get_singleton()->get_file_paths()) { + String simplified_path = E.simplify_path(); + if (simplified_path == config_file) { + continue; + } + + String pack_path = simplified_path.trim_suffix(".remap"); + + if (!_has_pack_path(paths, pack_path) && !_has_pack_path(extra_paths, pack_path) && !_has_pack_path(path_remaps, pack_path) && !_has_pack_path(forced_export, pack_path)) { + err = p_remove_func(p_udata, E); + if (err != OK) { + return err; + } + } + } + } + + return OK; +} + +Vector<uint8_t> EditorExportPlatform::_filter_extension_list_config_file(const String &p_config_path, const HashSet<String> &p_paths) { + Ref<FileAccess> f = FileAccess::open(p_config_path, FileAccess::READ); + if (f.is_null()) { + ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_config_path) + "'."); + } + Vector<uint8_t> data; + while (!f->eof_reached()) { + String l = f->get_line().strip_edges(); + if (p_paths.has(l)) { + data.append_array(l.to_utf8_buffer()); + data.append('\n'); + } + } + return data; } Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const SharedObject &p_so) { @@ -1483,6 +1567,29 @@ Error EditorExportPlatform::_pack_add_shared_object(void *p_userdata, const Shar return OK; } +Error EditorExportPlatform::_remove_pack_file(void *p_userdata, const String &p_path) { + PackData *pd = (PackData *)p_userdata; + + SavedData sd; + sd.path_utf8 = p_path.utf8(); + sd.ofs = pd->f->get_position(); + sd.size = 0; + sd.removal = true; + + // This padding will likely never be added, as we should already be aligned when removals are added. + int pad = _get_pad(PCK_PADDING, pd->f->get_position()); + for (int i = 0; i < pad; i++) { + pd->f->store_8(0); + } + + sd.md5.resize(16); + sd.md5.fill(0); + + pd->file_ofs.push_back(sd); + + return OK; +} + Error EditorExportPlatform::_zip_add_shared_object(void *p_userdata, const SharedObject &p_so) { ZipData *zip_data = (ZipData *)p_userdata; if (zip_data->so_files) { @@ -1613,7 +1720,7 @@ Dictionary EditorExportPlatform::_save_pack(const Ref<EditorExportPreset> &p_pre Vector<SharedObject> so_files; int64_t embedded_start = 0; int64_t embedded_size = 0; - Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, nullptr, p_embed, &embedded_start, &embedded_size); + Error err_code = save_pack(p_preset, p_debug, p_path, &so_files, nullptr, nullptr, p_embed, &embedded_start, &embedded_size); Dictionary ret; ret["result"] = err_code; @@ -1699,7 +1806,7 @@ Dictionary EditorExportPlatform::_save_zip_patch(const Ref<EditorExportPreset> & return ret; } -Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) { +Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) { EditorProgress ep("savepack", TTR("Packing"), 102, true); if (p_save_func == nullptr) { @@ -1722,7 +1829,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b pd.f = ftmp; pd.so_files = p_so_files; - Error err = export_project_files(p_preset, p_debug, p_save_func, &pd, _pack_add_shared_object); + Error err = export_project_files(p_preset, p_debug, p_save_func, p_remove_func, &pd, _pack_add_shared_object); // Close temp file. pd.f.unref(); @@ -1808,6 +1915,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b Ref<FileAccess> fhead = f; if (enc_pck && enc_directory) { + uint64_t seed = p_preset->get_seed(); String script_key = _get_script_encryption_key(p_preset); Vector<uint8_t> key; key.resize(32); @@ -1842,7 +1950,27 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b return ERR_CANT_CREATE; } - err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false); + Vector<uint8_t> iv; + if (seed != 0) { + for (int i = 0; i < pd.file_ofs.size(); i++) { + for (int64_t j = 0; j < pd.file_ofs[i].path_utf8.length(); j++) { + seed = ((seed << 5) + seed) ^ pd.file_ofs[i].path_utf8.get_data()[j]; + } + for (int64_t j = 0; j < pd.file_ofs[i].md5.size(); j++) { + seed = ((seed << 5) + seed) ^ pd.file_ofs[i].md5[j]; + } + seed = ((seed << 5) + seed) ^ pd.file_ofs[i].ofs; + seed = ((seed << 5) + seed) ^ pd.file_ofs[i].size; + } + + RandomPCG rng = RandomPCG(seed, RandomPCG::DEFAULT_INC); + iv.resize(16); + for (int i = 0; i < 16; i++) { + iv.write[i] = rng.rand() % 256; + } + } + + err = fae->open_and_parse(f, key, FileAccessEncrypted::MODE_WRITE_AES256, false, iv); if (err != OK) { add_message(EXPORT_MESSAGE_ERROR, TTR("Save PCK"), TTR("Can't open encrypted file to write.")); return ERR_CANT_CREATE; @@ -1868,6 +1996,9 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b if (pd.file_ofs[i].encrypted) { flags |= PACK_FILE_ENCRYPTED; } + if (pd.file_ofs[i].removal) { + flags |= PACK_FILE_REMOVAL; + } fhead->store_32(flags); } @@ -1936,7 +2067,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b } Error EditorExportPlatform::save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, bool p_embed, int64_t *r_embedded_start, int64_t *r_embedded_size) { - return save_pack(p_preset, p_debug, p_path, p_so_files, _save_pack_patch_file, p_embed, r_embedded_start, r_embedded_size); + return save_pack(p_preset, p_debug, p_path, p_so_files, _save_pack_patch_file, _remove_pack_file, p_embed, r_embedded_start, r_embedded_size); } Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files, EditorExportSaveFunction p_save_func) { @@ -1957,7 +2088,7 @@ Error EditorExportPlatform::save_zip(const Ref<EditorExportPreset> &p_preset, bo zd.zip = zip; zd.so_files = p_so_files; - Error err = export_project_files(p_preset, p_debug, p_save_func, &zd, _zip_add_shared_object); + Error err = export_project_files(p_preset, p_debug, p_save_func, nullptr, &zd, _zip_add_shared_object); if (err != OK && err != ERR_SKIP) { add_message(EXPORT_MESSAGE_ERROR, TTR("Save ZIP"), TTR("Failed to export project files.")); } diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index ef3274c5e4..a33bdce72a 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -53,7 +53,8 @@ protected: static void _bind_methods(); public: - typedef Error (*EditorExportSaveFunction)(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); + typedef Error (*EditorExportSaveFunction)(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, uint64_t p_seed); + typedef Error (*EditorExportRemoveFunction)(void *p_userdata, const String &p_path); typedef Error (*EditorExportSaveSharedObject)(void *p_userdata, const SharedObject &p_so); enum DebugFlags { @@ -82,6 +83,7 @@ private: uint64_t ofs = 0; uint64_t size = 0; bool encrypted = false; + bool removal = false; Vector<uint8_t> md5; CharString path_utf8; @@ -112,12 +114,14 @@ private: static bool _check_hash(const uint8_t *p_hash, const Vector<uint8_t> &p_data); - 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); - static Error _save_pack_patch_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); + 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, uint64_t p_seed); + static Error _save_pack_patch_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, uint64_t p_seed); static Error _pack_add_shared_object(void *p_userdata, const SharedObject &p_so); - static Error _save_zip_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); - static Error _save_zip_patch_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); + static Error _remove_pack_file(void *p_userdata, const String &p_path); + + static Error _save_zip_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, uint64_t p_seed); + static Error _save_zip_patch_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, uint64_t p_seed); static Error _zip_add_shared_object(void *p_userdata, const SharedObject &p_so); struct ScriptCallbackData { @@ -125,12 +129,14 @@ private: Callable so_cb; }; - static Error _script_save_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); + static Error _script_save_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, uint64_t p_seed); static Error _script_add_shared_object(void *p_userdata, const SharedObject &p_so); void _edit_files_with_filter(Ref<DirAccess> &da, const Vector<String> &p_filters, HashSet<String> &r_list, bool exclude); void _edit_filter_list(HashSet<String> &r_list, const String &p_filter, bool exclude); + static Vector<uint8_t> _filter_extension_list_config_file(const String &p_config_path, const HashSet<String> &p_paths); + struct FileExportCache { uint64_t source_modified_time = 0; String source_md5; @@ -287,7 +293,7 @@ public: Array get_current_presets() const; Error _export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, const Callable &p_save_func, const Callable &p_so_func); - Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr); + Error export_project_files(const Ref<EditorExportPreset> &p_preset, bool p_debug, EditorExportSaveFunction p_save_func, EditorExportRemoveFunction p_remove_func, void *p_udata, EditorExportSaveSharedObject p_so_func = nullptr); Dictionary _save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, bool p_embed = false); Dictionary _save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); @@ -295,7 +301,7 @@ public: Dictionary _save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); Dictionary _save_zip_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path); - Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr); + Error save_pack(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr, EditorExportRemoveFunction p_remove_func = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr); Error save_zip(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, EditorExportSaveFunction p_save_func = nullptr); Error save_pack_patch(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, Vector<SharedObject> *p_so_files = nullptr, bool p_embed = false, int64_t *r_embedded_start = nullptr, int64_t *r_embedded_size = nullptr); diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp index 52f7a0cee8..15d684cac5 100644 --- a/editor/export/editor_export_platform_pc.cpp +++ b/editor/export/editor_export_platform_pc.cpp @@ -194,7 +194,7 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> int64_t embedded_pos; int64_t embedded_size; - Error err = save_pack(p_preset, p_debug, pck_path, &so_files, nullptr, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size); + Error err = save_pack(p_preset, p_debug, pck_path, &so_files, nullptr, nullptr, p_preset->get("binary_format/embed_pck"), &embedded_pos, &embedded_size); if (err == OK && p_preset->get("binary_format/embed_pck")) { if (embedded_size >= 0x100000000 && String(p_preset->get("binary_format/architecture")).contains("32")) { add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("On 32-bit exports the embedded PCK cannot be bigger than 4 GiB.")); @@ -223,13 +223,13 @@ Error EditorExportPlatformPC::export_project_data(const Ref<EditorExportPreset> if (err == OK) { err = da->copy_dir(src_path, target_path, -1, true); if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), TTR(vformat("Failed to copy shared object \"%s\".", src_path))); + add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), vformat(TTR("Failed to copy shared object \"%s\"."), src_path)); } } } else { err = da->copy(src_path, target_path); if (err != OK) { - add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), TTR(vformat("Failed to copy shared object \"%s\".", src_path))); + add_message(EXPORT_MESSAGE_ERROR, TTR("GDExtension"), vformat(TTR("Failed to copy shared object \"%s\"."), src_path)); } if (err == OK) { err = sign_shared_object(p_preset, p_debug, target_path); diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp index da7059b777..8ff5dd7551 100644 --- a/editor/export/editor_export_preset.cpp +++ b/editor/export/editor_export_preset.cpp @@ -451,6 +451,15 @@ String EditorExportPreset::get_enc_ex_filter() const { return enc_ex_filters; } +void EditorExportPreset::set_seed(uint64_t p_seed) { + seed = p_seed; + EditorExport::singleton->save_presets(); +} + +uint64_t EditorExportPreset::get_seed() const { + return seed; +} + void EditorExportPreset::set_enc_pck(bool p_enabled) { enc_pck = p_enabled; EditorExport::singleton->save_presets(); diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h index af3a23fc50..4834a483eb 100644 --- a/editor/export/editor_export_preset.h +++ b/editor/export/editor_export_preset.h @@ -92,6 +92,7 @@ private: String enc_ex_filters; bool enc_pck = false; bool enc_directory = false; + uint64_t seed = 0; String script_key; int script_mode = MODE_SCRIPT_BINARY_TOKENS_COMPRESSED; @@ -165,6 +166,9 @@ public: void set_enc_ex_filter(const String &p_filter); String get_enc_ex_filter() const; + void set_seed(uint64_t p_seed); + uint64_t get_seed() const; + void set_enc_pck(bool p_enabled); bool get_enc_pck() const; diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp index 2309319376..a90c16f66e 100644 --- a/editor/export/export_template_manager.cpp +++ b/editor/export/export_template_manager.cpp @@ -889,7 +889,7 @@ void ExportTemplateManager::_notification(int p_what) { current_missing_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); current_installed_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor))); - mirror_options_button->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + mirror_options_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index f9137082d7..0fc62416af 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -100,15 +100,15 @@ void ProjectExportDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME("Duplicate"))); - delete_preset->set_icon(presets->get_editor_theme_icon(SNAME("Remove"))); - patch_add_btn->set_icon(get_editor_theme_icon(SNAME("Add"))); + duplicate_preset->set_button_icon(presets->get_editor_theme_icon(SNAME("Duplicate"))); + delete_preset->set_button_icon(presets->get_editor_theme_icon(SNAME("Remove"))); + patch_add_btn->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } break; case NOTIFICATION_READY: { - duplicate_preset->set_icon(presets->get_editor_theme_icon(SNAME("Duplicate"))); - delete_preset->set_icon(presets->get_editor_theme_icon(SNAME("Remove"))); - patch_add_btn->set_icon(get_editor_theme_icon(SNAME("Add"))); + duplicate_preset->set_button_icon(presets->get_editor_theme_icon(SNAME("Duplicate"))); + delete_preset->set_button_icon(presets->get_editor_theme_icon(SNAME("Remove"))); + patch_add_btn->set_button_icon(get_editor_theme_icon(SNAME("Add"))); connect(SceneStringName(confirmed), callable_mp(this, &ProjectExportDialog::_export_pck_zip)); _update_export_all(); } break; @@ -382,10 +382,16 @@ void ProjectExportDialog::_edit_preset(int p_index) { bool enc_pck_mode = current->get_enc_pck(); enc_pck->set_pressed(enc_pck_mode); + uint64_t seed = current->get_seed(); + if (!updating_seed) { + seed_input->set_text(itos(seed)); + } + enc_directory->set_disabled(!enc_pck_mode); enc_in_filters->set_editable(enc_pck_mode); enc_ex_filters->set_editable(enc_pck_mode); script_key->set_editable(enc_pck_mode); + seed_input->set_editable(enc_pck_mode); bool enc_directory_mode = current->get_enc_directory(); enc_directory->set_pressed(enc_directory_mode); @@ -591,6 +597,21 @@ void ProjectExportDialog::_enc_pck_changed(bool p_pressed) { _update_current_preset(); } +void ProjectExportDialog::_seed_input_changed(const String &p_text) { + if (updating) { + return; + } + + Ref<EditorExportPreset> current = get_current_preset(); + ERR_FAIL_COND(current.is_null()); + + current->set_seed(seed_input->get_text().to_int()); + + updating_seed = true; + _update_current_preset(); + updating_seed = false; +} + void ProjectExportDialog::_enc_directory_changed(bool p_pressed) { if (updating) { return; @@ -1185,6 +1206,9 @@ void ProjectExportDialog::_export_pck_zip_selected(const String &p_path) { bool export_debug = fd_option.get(TTR("Export With Debug"), true); bool export_as_patch = fd_option.get(TTR("Export As Patch"), true); + EditorSettings::get_singleton()->set_project_metadata("export_options", "export_debug", export_debug); + EditorSettings::get_singleton()->set_project_metadata("export_options", "export_as_patch", export_as_patch); + if (p_path.ends_with(".zip")) { if (export_as_patch) { platform->export_zip_patch(current, export_debug, p_path); @@ -1284,6 +1308,8 @@ void ProjectExportDialog::_export_project_to_path(const String &p_path) { Dictionary fd_option = export_project->get_selected_options(); bool export_debug = fd_option.get(TTR("Export With Debug"), true); + EditorSettings::get_singleton()->set_project_metadata("export_options", "export_debug", export_debug); + Error err = platform->export_project(current, export_debug, current->get_export_path(), 0); result_dialog_log->clear(); if (err != ERR_SKIP) { @@ -1623,6 +1649,10 @@ ProjectExportDialog::ProjectExportDialog() { sec_vb->add_child(script_key_error); sections->add_child(sec_scroll_container); + seed_input = memnew(LineEdit); + seed_input->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_seed_input_changed)); + sec_vb->add_margin_child(TTR("Initialization vector seed"), seed_input); + Label *sec_info = memnew(Label); sec_info->set_text(TTR("Note: Encryption key needs to be stored in the binary,\nyou need to build the export templates from source.")); sec_vb->add_child(sec_info); @@ -1749,9 +1779,9 @@ ProjectExportDialog::ProjectExportDialog() { export_project->connect("file_selected", callable_mp(this, &ProjectExportDialog::_export_project_to_path)); export_project->get_line_edit()->connect(SceneStringName(text_changed), callable_mp(this, &ProjectExportDialog::_validate_export_path)); - export_project->add_option(TTR("Export With Debug"), Vector<String>(), true); - export_pck_zip->add_option(TTR("Export With Debug"), Vector<String>(), true); - export_pck_zip->add_option(TTR("Export As Patch"), Vector<String>(), true); + export_project->add_option(TTR("Export With Debug"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true)); + export_pck_zip->add_option(TTR("Export With Debug"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_debug", true)); + export_pck_zip->add_option(TTR("Export As Patch"), Vector<String>(), EditorSettings::get_singleton()->get_project_metadata("export_options", "export_as_patch", true)); set_hide_on_ok(false); diff --git a/editor/export/project_export.h b/editor/export/project_export.h index e360596be6..68676bfc84 100644 --- a/editor/export/project_export.h +++ b/editor/export/project_export.h @@ -172,6 +172,7 @@ class ProjectExportDialog : public ConfirmationDialog { CheckButton *enc_directory = nullptr; LineEdit *enc_in_filters = nullptr; LineEdit *enc_ex_filters = nullptr; + LineEdit *seed_input = nullptr; OptionButton *script_mode = nullptr; @@ -192,9 +193,11 @@ class ProjectExportDialog : public ConfirmationDialog { bool updating_script_key = false; bool updating_enc_filters = false; + bool updating_seed = false; void _enc_pck_changed(bool p_pressed); void _enc_directory_changed(bool p_pressed); void _enc_filters_changed(const String &p_text); + void _seed_input_changed(const String &p_text); void _script_encryption_key_changed(const String &p_key); bool _validate_script_encryption_key(const String &p_key); @@ -216,7 +219,7 @@ public: Ref<EditorExportPreset> get_current_preset() const; - bool is_exporting() const { return exporting; }; + bool is_exporting() const { return exporting; } ProjectExportDialog(); ~ProjectExportDialog(); diff --git a/editor/fbx_importer_manager.cpp b/editor/fbx_importer_manager.cpp index 2650b642fa..f612c3bd81 100644 --- a/editor/fbx_importer_manager.cpp +++ b/editor/fbx_importer_manager.cpp @@ -40,7 +40,7 @@ void FBXImporterManager::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - fbx_path_browse->set_icon(get_editor_theme_icon(SNAME("FileBrowse"))); + fbx_path_browse->set_button_icon(get_editor_theme_icon(SNAME("FileBrowse"))); } break; case NOTIFICATION_READY: { diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 53982b37b9..c7e12d1f3b 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -137,7 +137,7 @@ bool FileSystemList::edit_selected() { String name = get_item_text(s); line_editor->set_text(name); - line_editor->select(0, name.rfind(".")); + line_editor->select(0, name.rfind_char('.')); popup_edit_commited = false; // Start edit popup processing. popup_editor->popup(); @@ -203,9 +203,7 @@ Ref<Texture2D> FileSystemDock::_get_tree_item_icon(bool p_is_valid, const String } } -bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path) { - bool parent_should_expand = false; - +void FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path) { // Create a tree item for the subdirectory. TreeItem *subdirectory_item = tree->create_item(p_parent); String dname = p_dir->get_name(); @@ -213,6 +211,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory if (dname.is_empty()) { dname = "res://"; + resources_item = subdirectory_item; } // Set custom folder color (if applicable). @@ -258,16 +257,13 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory } else { subdirectory_item->set_collapsed(!uncollapsed_paths.has(lpath)); } - if (!searched_tokens.is_empty() && _matches_all_search_tokens(dname)) { - parent_should_expand = true; - } // Create items for all subdirectories. bool reversed = file_sort == FileSortOption::FILE_SORT_NAME_REVERSE; for (int i = reversed ? p_dir->get_subdir_count() - 1 : 0; reversed ? i >= 0 : i < p_dir->get_subdir_count(); reversed ? i-- : i++) { - parent_should_expand = (_create_tree(subdirectory_item, p_dir->get_subdir(i), uncollapsed_paths, p_select_in_favorites, p_unfold_path) || parent_should_expand); + _create_tree(subdirectory_item, p_dir->get_subdir(i), uncollapsed_paths, p_select_in_favorites, p_unfold_path); } // Create all items for the files in the subdirectory. @@ -283,17 +279,6 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory continue; } - String file_name = p_dir->get_file(i); - if (!searched_tokens.is_empty()) { - if (!_matches_all_search_tokens(file_name)) { - // The searched string is not in the file name, we skip it. - continue; - } else { - // We expand all parents. - parent_should_expand = true; - } - } - FileInfo file_info; file_info.name = p_dir->get_file(i); file_info.type = p_dir->get_file_type(i); @@ -346,24 +331,12 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory subdirectory_item->set_as_cursor(0); } } - - if (!searched_tokens.is_empty()) { - if (parent_should_expand) { - subdirectory_item->set_collapsed(false); - } else if (dname != "res://") { - subdirectory_item->get_parent()->remove_child(subdirectory_item); - memdelete(subdirectory_item); - } - } - - return parent_should_expand; } Vector<String> FileSystemDock::get_uncollapsed_paths() const { Vector<String> uncollapsed_paths; TreeItem *root = tree->get_root(); if (root) { - TreeItem *favorites_item = root->get_first_child(); if (!favorites_item->is_collapsed()) { uncollapsed_paths.push_back(favorites_item->get_metadata(0)); } @@ -400,7 +373,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo TreeItem *root = tree->create_item(); // Handles the favorites. - TreeItem *favorites_item = tree->create_item(root); + favorites_item = tree->create_item(root); favorites_item->set_icon(0, get_editor_theme_icon(SNAME("Favorites"))); favorites_item->set_text(0, TTR("Favorites:")); favorites_item->set_metadata(0, "Favorites"); @@ -453,24 +426,22 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo color = Color(1, 1, 1); } - if (searched_tokens.is_empty() || _matches_all_search_tokens(text)) { - TreeItem *ti = tree->create_item(favorites_item); - ti->set_text(0, text); - ti->set_icon(0, icon); - ti->set_icon_modulate(0, color); - ti->set_tooltip_text(0, favorite); - ti->set_selectable(0, true); - ti->set_metadata(0, favorite); - if (p_select_in_favorites && favorite == current_path) { - ti->select(0); - ti->set_as_cursor(0); - } - if (!favorite.ends_with("/")) { - Array udata; - udata.push_back(tree_update_id); - udata.push_back(ti); - EditorResourcePreview::get_singleton()->queue_resource_preview(favorite, this, "_tree_thumbnail_done", udata); - } + TreeItem *ti = tree->create_item(favorites_item); + ti->set_text(0, text); + ti->set_icon(0, icon); + ti->set_icon_modulate(0, color); + ti->set_tooltip_text(0, favorite); + ti->set_selectable(0, true); + ti->set_metadata(0, favorite); + if (p_select_in_favorites && favorite == current_path) { + ti->select(0); + ti->set_as_cursor(0); + } + if (!favorite.ends_with("/")) { + Array udata; + udata.push_back(tree_update_id); + udata.push_back(ti); + EditorResourcePreview::get_singleton()->queue_resource_preview(favorite, this, "_tree_thumbnail_done", udata); } } @@ -495,7 +466,7 @@ void FileSystemDock::_update_display_mode(bool p_force) { if (p_force || old_display_mode != display_mode) { switch (display_mode) { case DISPLAY_MODE_TREE_ONLY: - button_toggle_display_mode->set_icon(get_editor_theme_icon(SNAME("Panels1"))); + button_toggle_display_mode->set_button_icon(get_editor_theme_icon(SNAME("Panels1"))); tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); toolbar2_hbc->show(); @@ -512,7 +483,7 @@ void FileSystemDock::_update_display_mode(bool p_force) { const int actual_offset = is_vertical ? split_box_offset_v : split_box_offset_h; split_box->set_split_offset(actual_offset); const StringName icon = is_vertical ? SNAME("Panels2") : SNAME("Panels2Alt"); - button_toggle_display_mode->set_icon(get_editor_theme_icon(icon)); + button_toggle_display_mode->set_button_icon(get_editor_theme_icon(icon)); tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); @@ -597,7 +568,7 @@ void FileSystemDock::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { _update_display_mode(true); - button_reload->set_icon(get_editor_theme_icon(SNAME("Reload"))); + button_reload->set_button_icon(get_editor_theme_icon(SNAME("Reload"))); StringName mode_icon = "Panels1"; if (display_mode == DISPLAY_MODE_VSPLIT) { @@ -605,28 +576,28 @@ void FileSystemDock::_notification(int p_what) { } else if (display_mode == DISPLAY_MODE_HSPLIT) { mode_icon = "Panels2Alt"; } - button_toggle_display_mode->set_icon(get_editor_theme_icon(mode_icon)); + button_toggle_display_mode->set_button_icon(get_editor_theme_icon(mode_icon)); if (file_list_display_mode == FILE_LIST_DISPLAY_LIST) { - button_file_list_display_mode->set_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); + button_file_list_display_mode->set_button_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); } else { - button_file_list_display_mode->set_icon(get_editor_theme_icon(SNAME("FileList"))); + button_file_list_display_mode->set_button_icon(get_editor_theme_icon(SNAME("FileList"))); } tree_search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - tree_button_sort->set_icon(get_editor_theme_icon(SNAME("Sort"))); + tree_button_sort->set_button_icon(get_editor_theme_icon(SNAME("Sort"))); file_list_search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - file_list_button_sort->set_icon(get_editor_theme_icon(SNAME("Sort"))); + file_list_button_sort->set_button_icon(get_editor_theme_icon(SNAME("Sort"))); - button_dock_placement->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + button_dock_placement->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); if (is_layout_rtl()) { - button_hist_next->set_icon(get_editor_theme_icon(SNAME("Back"))); - button_hist_prev->set_icon(get_editor_theme_icon(SNAME("Forward"))); + button_hist_next->set_button_icon(get_editor_theme_icon(SNAME("Back"))); + button_hist_prev->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); } else { - button_hist_next->set_icon(get_editor_theme_icon(SNAME("Forward"))); - button_hist_prev->set_icon(get_editor_theme_icon(SNAME("Back"))); + button_hist_next->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); + button_hist_prev->set_button_icon(get_editor_theme_icon(SNAME("Back"))); } overwrite_dialog_scroll->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), "Tree")); @@ -676,7 +647,6 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s return; } - TreeItem *favorites_item = tree->get_root()->get_first_child(); if (selected->get_parent() == favorites_item && !String(selected->get_metadata(0)).ends_with("/")) { // Go to the favorites if we click in the favorites and the path has changed. current_path = "Favorites"; @@ -771,6 +741,36 @@ void FileSystemDock::_navigate_to_path(const String &p_path, bool p_select_in_fa } } +bool FileSystemDock::_update_filtered_items(TreeItem *p_tree_item) { + TreeItem *item = p_tree_item; + if (!item) { + item = tree->get_root(); + } + ERR_FAIL_NULL_V(item, false); + + bool keep_visible = false; + for (TreeItem *child = item->get_first_child(); child; child = child->get_next()) { + keep_visible = _update_filtered_items(child) || keep_visible; + } + + if (searched_tokens.is_empty()) { + item->set_visible(true); + // Always uncollapse root (the hidden item above res:// and favorites). + item->set_collapsed(item != tree->get_root() && !uncollapsed_paths_before_search.has(item->get_metadata(0))); + return true; + } + + if (keep_visible) { + item->set_collapsed(false); + } else { + // res:// and favorites are always visible. + keep_visible = item == resources_item || item == favorites_item; + keep_visible = keep_visible || _matches_all_search_tokens(item->get_text(0)); + } + item->set_visible(keep_visible); + return keep_visible; +} + void FileSystemDock::navigate_to_path(const String &p_path) { file_list_search_box->clear(); _navigate_to_path(p_path); @@ -818,11 +818,11 @@ void FileSystemDock::_toggle_file_display() { void FileSystemDock::_set_file_display(bool p_active) { if (p_active) { file_list_display_mode = FILE_LIST_DISPLAY_LIST; - button_file_list_display_mode->set_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); + button_file_list_display_mode->set_button_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); button_file_list_display_mode->set_tooltip_text(TTR("View items as a grid of thumbnails.")); } else { file_list_display_mode = FILE_LIST_DISPLAY_THUMBNAILS; - button_file_list_display_mode->set_icon(get_editor_theme_icon(SNAME("FileList"))); + button_file_list_display_mode->set_button_icon(get_editor_theme_icon(SNAME("FileList"))); button_file_list_display_mode->set_tooltip_text(TTR("View items as a list.")); } @@ -1446,6 +1446,13 @@ void FileSystemDock::_try_move_item(const FileOrFolder &p_item, const String &p_ } } + if (p_item.is_file && FileAccess::exists(old_path + ".uid")) { + err = da->rename(old_path + ".uid", new_path + ".uid"); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Error moving:") + "\n" + old_path + ".uid\n"); + } + } + // Update scene if it is open. for (int i = 0; i < file_changed_paths.size(); ++i) { String new_item_path = p_item.is_file ? new_path : file_changed_paths[i].replace_first(old_path, new_path); @@ -1491,76 +1498,22 @@ void FileSystemDock::_try_duplicate_item(const FileOrFolder &p_item, const Strin EditorNode::get_singleton()->add_io_error(TTR("Cannot move a folder into itself.") + "\n" + old_path + "\n"); return; } - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); if (p_item.is_file) { print_verbose("Duplicating " + old_path + " -> " + new_path); // Create the directory structure. - da->make_dir_recursive(new_path.get_base_dir()); - - if (FileAccess::exists(old_path + ".import")) { - Error err = da->copy(old_path, new_path); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n"); - return; - } - - // Remove uid from .import file to avoid conflict. - Ref<ConfigFile> cfg; - cfg.instantiate(); - cfg->load(old_path + ".import"); - cfg->erase_section_key("remap", "uid"); - err = cfg->save(new_path + ".import"); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ".import: " + error_names[err] + "\n"); - return; - } - } else { - // Files which do not use an uid can just be copied. - if (ResourceLoader::get_resource_uid(old_path) == ResourceUID::INVALID_ID) { - Error err = da->copy(old_path, new_path); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n"); - } - return; - } + EditorFileSystem::get_singleton()->make_dir_recursive(p_new_path.get_base_dir()); - // Load the resource and save it again in the new location (this generates a new UID). - Error err; - Ref<Resource> res = ResourceLoader::load(old_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); - if (err == OK && res.is_valid()) { - err = ResourceSaver::save(res, new_path, ResourceSaver::FLAG_COMPRESS); - if (err != OK) { - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + " " + vformat(TTR("Failed to save resource at %s: %s"), new_path, error_names[err])); - } - } else if (err != OK) { - // When loading files like text files the error is OK but the resource is still null. - // We can ignore such files. - EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + " " + vformat(TTR("Failed to load resource at %s: %s"), new_path, error_names[err])); - } + Error err = EditorFileSystem::get_singleton()->copy_file(old_path, new_path); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Error duplicating:") + "\n" + old_path + ": " + error_names[err] + "\n"); } } else { - da->make_dir(new_path); - - // Recursively duplicate all files inside the folder. - Ref<DirAccess> old_dir = DirAccess::open(old_path); - ERR_FAIL_COND(old_dir.is_null()); - - Ref<FileAccess> file_access = FileAccess::create(FileAccess::ACCESS_RESOURCES); - old_dir->set_include_navigational(false); - old_dir->list_dir_begin(); - for (String f = old_dir->_get_next(); !f.is_empty(); f = old_dir->_get_next()) { - if (f.get_extension() == "import") { - continue; - } - if (file_access->file_exists(old_path + f)) { - _try_duplicate_item(FileOrFolder(old_path + f, true), new_path + f); - } else if (da->dir_exists(old_path + f)) { - _try_duplicate_item(FileOrFolder(old_path + f, false), new_path + f); - } + Error err = EditorFileSystem::get_singleton()->copy_directory(old_path, new_path); + if (err != OK) { + EditorNode::get_singleton()->add_io_error(TTR("Error duplicating directory:") + "\n" + old_path + "\n"); } - old_dir->list_dir_end(); } } @@ -1695,21 +1648,27 @@ String FileSystemDock::_get_unique_name(const FileOrFolder &p_entry, const Strin return new_path; } -void FileSystemDock::_update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const { - Vector<String> favorites_list = EditorSettings::get_singleton()->get_favorites(); - Vector<String> new_favorites; - - for (const String &old_path : favorites_list) { +void FileSystemDock::_update_favorites_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const { + Vector<String> favorite_files = EditorSettings::get_singleton()->get_favorites(); + Vector<String> new_favorite_files; + for (const String &old_path : favorite_files) { if (p_folders_renames.has(old_path)) { - new_favorites.push_back(p_folders_renames[old_path]); + new_favorite_files.push_back(p_folders_renames[old_path]); } else if (p_files_renames.has(old_path)) { - new_favorites.push_back(p_files_renames[old_path]); + new_favorite_files.push_back(p_files_renames[old_path]); } else { - new_favorites.push_back(old_path); + new_favorite_files.push_back(old_path); } } + EditorSettings::get_singleton()->set_favorites(new_favorite_files); - EditorSettings::get_singleton()->set_favorites(new_favorites); + HashMap<String, PackedStringArray> favorite_properties = EditorSettings::get_singleton()->get_favorite_properties(); + for (const KeyValue<String, String> &KV : p_files_renames) { + if (favorite_properties.has(KV.key)) { + favorite_properties.replace_key(KV.key, KV.value); + } + } + EditorSettings::get_singleton()->set_favorite_properties(favorite_properties); } void FileSystemDock::_make_scene_confirm() { @@ -1852,7 +1811,7 @@ void FileSystemDock::_rename_operation_confirm() { _update_resource_paths_after_move(file_renames, uids); _update_dependencies_after_move(file_renames, file_owners); _update_project_settings_after_move(file_renames, folder_renames); - _update_favorites_list_after_move(file_renames, folder_renames); + _update_favorites_after_move(file_renames, folder_renames); EditorSceneTabs::get_singleton()->set_current_tab(current_tab); @@ -1866,21 +1825,15 @@ void FileSystemDock::_rename_operation_confirm() { } void FileSystemDock::_duplicate_operation_confirm(const String &p_path) { - String base_dir = p_path.trim_suffix("/").get_base_dir(); - Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - if (!da->dir_exists(base_dir)) { - Error err = da->make_dir_recursive(base_dir); - + const String base_dir = p_path.trim_suffix("/").get_base_dir(); + if (!DirAccess::dir_exists_absolute(base_dir)) { + Error err = EditorFileSystem::get_singleton()->make_dir_recursive(base_dir); if (err != OK) { EditorNode::get_singleton()->show_warning(vformat(TTR("Could not create base directory: %s"), error_names[err])); return; } } _try_duplicate_item(to_duplicate, p_path); - - // Rescan everything. - print_verbose("FileSystem: calling rescan."); - _rescan(); } void FileSystemDock::_overwrite_dialog_action(bool p_overwrite) { @@ -2019,7 +1972,7 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_cop _update_resource_paths_after_move(file_renames, uids); _update_dependencies_after_move(file_renames, file_owners); _update_project_settings_after_move(file_renames, folder_renames); - _update_favorites_list_after_move(file_renames, folder_renames); + _update_favorites_after_move(file_renames, folder_renames); EditorSceneTabs::get_singleton()->set_current_tab(current_tab); @@ -2075,7 +2028,6 @@ Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion, bo // Build a list of selected items with the active one at the first position. Vector<String> selected_strings; - TreeItem *favorites_item = tree->get_root()->get_first_child(); TreeItem *cursor_item = tree->get_selected(); if (cursor_item && (p_include_unselected_cursor || cursor_item->is_selected(0)) && cursor_item != favorites_item) { selected_strings.push_back(cursor_item->get_metadata(0)); @@ -2480,7 +2432,7 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected if (to_rename.is_file) { String name = to_rename.path.get_file(); - tree->set_editor_selection(0, name.rfind(".")); + tree->set_editor_selection(0, name.rfind_char('.')); } else { String name = to_rename.path.left(-1).get_file(); // Removes the "/" suffix for folders. tree->set_editor_selection(0, name.length()); @@ -2532,9 +2484,6 @@ void FileSystemDock::_file_option(int p_option, const Vector<String> &p_selected make_dir_dialog->popup_centered(); } break; - case FILE_INFO: { - } break; - case FILE_REIMPORT: { ImportDock::get_singleton()->reimport_resources(p_selected); } break; @@ -2687,16 +2636,12 @@ void FileSystemDock::_search_changed(const String &p_text, const Control *p_from tree_search_box->set_text(searched_string); } - bool unfold_path = (p_text.is_empty() && !current_path.is_empty()); - switch (display_mode) { - case DISPLAY_MODE_TREE_ONLY: { - _update_tree(searched_tokens.is_empty() ? uncollapsed_paths_before_search : Vector<String>(), false, false, unfold_path); - } break; - case DISPLAY_MODE_HSPLIT: - case DISPLAY_MODE_VSPLIT: { - _update_file_list(false); - _update_tree(searched_tokens.is_empty() ? uncollapsed_paths_before_search : Vector<String>(), false, false, unfold_path); - } break; + _update_filtered_items(); + if (display_mode == DISPLAY_MODE_HSPLIT || display_mode == DISPLAY_MODE_VSPLIT) { + _update_file_list(false); + } + if (searched_tokens.is_empty()) { + _navigate_to_path(current_path); } } @@ -2776,7 +2721,7 @@ void FileSystemDock::focus_on_filter() { } void FileSystemDock::create_directory(const String &p_path, const String &p_base_dir) { - Error err = EditorFileSystem::get_singleton()->make_dir_recursive(p_path, p_base_dir); + Error err = EditorFileSystem::get_singleton()->make_dir_recursive(p_path.trim_prefix(p_base_dir), p_base_dir); if (err != OK) { EditorNode::get_singleton()->show_warning(vformat(TTR("Could not create folder: %s"), error_names[err])); } @@ -2836,7 +2781,6 @@ Variant FileSystemDock::get_drag_data_fw(const Point2 &p_point, Control *p_from) // Check if the first selected is in favorite. TreeItem *selected = tree->get_next_selected(tree->get_root()); while (selected) { - TreeItem *favorites_item = tree->get_root()->get_first_child(); if (selected == favorites_item) { // The "Favorites" item is not draggable. return Variant(); @@ -2888,10 +2832,6 @@ bool FileSystemDock::can_drop_data_fw(const Point2 &p_point, const Variant &p_da } int drop_section = tree->get_drop_section_at_position(p_point); - TreeItem *favorites_item = tree->get_root()->get_first_child(); - - TreeItem *resources_item = favorites_item->get_next(); - if (ti == favorites_item) { return (drop_section == 1); // The parent, first fav. } @@ -2972,9 +2912,6 @@ void FileSystemDock::drop_data_fw(const Point2 &p_point, const Variant &p_data, int drop_position; Vector<String> drag_files = drag_data["files"]; - TreeItem *favorites_item = tree->get_root()->get_first_child(); - TreeItem *resources_item = favorites_item->get_next(); - if (ti == favorites_item) { // Drop on the favorite folder. drop_position = 0; @@ -3402,7 +3339,6 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect [[maybe_unused]] bool added_separator = false; if (favorites_list.has(fpath)) { - TreeItem *favorites_item = tree->get_root()->get_first_child(); TreeItem *cursor_item = tree->get_selected(); bool is_item_in_favorites = false; while (cursor_item != nullptr) { @@ -3431,7 +3367,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, const Vect const bool is_directory = fpath.ends_with("/"); p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("Terminal")), ED_GET_SHORTCUT("filesystem_dock/open_in_terminal"), FILE_OPEN_IN_TERMINAL); - p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Containing Folder in Terminal")); + p_popup->set_item_text(p_popup->get_item_index(FILE_OPEN_IN_TERMINAL), is_directory ? TTR("Open in Terminal") : TTR("Open Folder in Terminal")); if (!is_directory) { p_popup->add_icon_shortcut(get_editor_theme_icon(SNAME("ExternalLink")), ED_GET_SHORTCUT("filesystem_dock/open_in_external_program"), FILE_OPEN_EXTERNAL); @@ -3722,10 +3658,10 @@ void FileSystemDock::_file_list_gui_input(Ref<InputEvent> p_event) { tree_item->select(0); } else { // Find parent folder. - fpath = fpath.substr(0, fpath.rfind("/") + 1); + fpath = fpath.substr(0, fpath.rfind_char('/') + 1); if (fpath.size() > String("res://").size()) { fpath = fpath.left(fpath.size() - 2); // Remove last '/'. - const int slash_idx = fpath.rfind("/"); + const int slash_idx = fpath.rfind_char('/'); fpath = fpath.substr(slash_idx + 1, fpath.size() - slash_idx - 1); } @@ -4060,17 +3996,17 @@ FileSystemDock::FileSystemDock() { // `KeyModifierMask::CMD_OR_CTRL | Key::C` conflicts with other editor shortcuts. ED_SHORTCUT("filesystem_dock/copy_path", TTR("Copy Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C); - ED_SHORTCUT("filesystem_dock/copy_absolute_path", TTR("Copy Absolute Path")); - ED_SHORTCUT("filesystem_dock/copy_uid", TTR("Copy UID")); + ED_SHORTCUT("filesystem_dock/copy_absolute_path", TTR("Copy Absolute Path"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::C); + ED_SHORTCUT("filesystem_dock/copy_uid", TTR("Copy UID"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | KeyModifierMask::SHIFT | Key::C); ED_SHORTCUT("filesystem_dock/duplicate", TTR("Duplicate..."), KeyModifierMask::CMD_OR_CTRL | Key::D); ED_SHORTCUT("filesystem_dock/delete", TTR("Delete"), Key::KEY_DELETE); ED_SHORTCUT("filesystem_dock/rename", TTR("Rename..."), Key::F2); ED_SHORTCUT_OVERRIDE("filesystem_dock/rename", "macos", Key::ENTER); #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) // Opening the system file manager or opening in an external program is not supported on the Android and web editors. - 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")); - ED_SHORTCUT("filesystem_dock/open_in_terminal", TTR("Open in Terminal")); + ED_SHORTCUT("filesystem_dock/show_in_explorer", TTR("Open in File Manager"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::R); + ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::E); + ED_SHORTCUT("filesystem_dock/open_in_terminal", TTR("Open in Terminal"), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT | Key::T); #endif // Properly translating color names would require a separate HashMap, so for simplicity they are provided as comments. diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index 819abbd389..d2e403a8af 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -116,7 +116,6 @@ private: FILE_REMOVE, FILE_DUPLICATE, FILE_REIMPORT, - FILE_INFO, FILE_NEW, FILE_SHOW_IN_EXPLORER, FILE_OPEN_EXTERNAL, @@ -233,6 +232,8 @@ private: FileSystemTree *tree = nullptr; FileSystemList *files = nullptr; bool import_dock_needs_update = false; + TreeItem *resources_item = nullptr; + TreeItem *favorites_item = nullptr; bool holding_branch = false; Vector<TreeItem *> tree_items_selected_on_drag_begin; @@ -246,9 +247,10 @@ private: void _reselect_items_selected_on_drag_begin(bool reset = false); Ref<Texture2D> _get_tree_item_icon(bool p_is_valid, const String &p_file_type, const String &p_icon_path); - bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false); + void _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false); void _update_tree(const Vector<String> &p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false, bool p_select_in_favorites = false, bool p_unfold_path = false); void _navigate_to_path(const String &p_path, bool p_select_in_favorites = false); + bool _update_filtered_items(TreeItem *p_tree_item = nullptr); void _file_list_gui_input(Ref<InputEvent> p_event); void _tree_gui_input(Ref<InputEvent> p_event); @@ -276,7 +278,7 @@ private: void _before_move(HashMap<String, ResourceUID::ID> &r_uids, HashSet<String> &r_file_owners) const; void _update_dependencies_after_move(const HashMap<String, String> &p_renames, const HashSet<String> &p_file_owners) const; void _update_resource_paths_after_move(const HashMap<String, String> &p_renames, const HashMap<String, ResourceUID::ID> &p_uids) const; - void _update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const; + void _update_favorites_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 HashMap<String, String> &p_folders_renames); String _get_unique_name(const FileOrFolder &p_entry, const String &p_at_path); @@ -402,7 +404,7 @@ public: FileSortOption get_file_sort() const { return file_sort; } void set_file_list_display_mode(FileListDisplayMode p_mode); - FileListDisplayMode get_file_list_display_mode() const { return file_list_display_mode; }; + FileListDisplayMode get_file_list_display_mode() const { return file_list_display_mode; } Tree *get_tree_control() { return tree; } diff --git a/editor/group_settings_editor.cpp b/editor/group_settings_editor.cpp index bb899af582..93d5ee0716 100644 --- a/editor/group_settings_editor.cpp +++ b/editor/group_settings_editor.cpp @@ -45,7 +45,7 @@ void GroupSettingsEditor::_notification(int p_what) { update_groups(); } break; case NOTIFICATION_THEME_CHANGED: { - add_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + add_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } break; } } diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index e0de6bbcb1..bd0805bd7e 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -373,7 +373,7 @@ void GroupsEditor::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { filter->set_right_icon(get_editor_theme_icon("Search")); - add->set_icon(get_editor_theme_icon("Add")); + add->set_button_icon(get_editor_theme_icon("Add")); _update_tree(); } break; case NOTIFICATION_VISIBILITY_CHANGED: { diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp index f6ba74fe95..3cc1e37be0 100644 --- a/editor/gui/editor_bottom_panel.cpp +++ b/editor/gui/editor_bottom_panel.cpp @@ -31,35 +31,35 @@ #include "editor_bottom_panel.h" #include "editor/debugger/editor_debugger_node.h" -#include "editor/editor_about.h" #include "editor/editor_command_palette.h" #include "editor/editor_node.h" #include "editor/editor_string_names.h" -#include "editor/engine_update_label.h" #include "editor/gui/editor_toaster.h" #include "editor/gui/editor_version_button.h" #include "editor/themes/editor_scale.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" +#include "scene/gui/split_container.h" void EditorBottomPanel::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - expand_button->set_icon(get_editor_theme_icon(SNAME("ExpandBottomDock"))); + pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin"))); + expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock"))); } break; } } -void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control) { +void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock) { for (int i = 0; i < items.size(); i++) { if (items[i].control == p_control) { - _switch_to_item(p_visible, i); + _switch_to_item(p_visible, i, p_ignore_lock); return; } } } -void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) { +void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock) { ERR_FAIL_INDEX(p_idx, items.size()); if (items[p_idx].control->is_visible() == p_visible) { @@ -70,6 +70,10 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) { ERR_FAIL_NULL(center_split); if (p_visible) { + if (!p_ignore_lock && lock_panel_switching && pin_button->is_visible()) { + return; + } + for (int i = 0; i < items.size(); i++) { items[i].button->set_pressed_no_signal(i == p_idx); items[i].control->set_visible(i == p_idx); @@ -80,18 +84,23 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) { } else { add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); } + center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE); center_split->set_collapsed(false); + pin_button->show(); + + expand_button->show(); if (expand_button->is_pressed()) { EditorNode::get_top_split()->hide(); } - expand_button->show(); } else { add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles))); items[p_idx].button->set_pressed_no_signal(false); items[p_idx].control->set_visible(false); center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN); center_split->set_collapsed(true); + pin_button->hide(); + expand_button->hide(); if (expand_button->is_pressed()) { EditorNode::get_top_split()->show(); @@ -101,13 +110,17 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) { last_opened_control = items[p_idx].control; } +void EditorBottomPanel::_pin_button_toggled(bool p_pressed) { + lock_panel_switching = p_pressed; +} + void EditorBottomPanel::_expand_button_toggled(bool p_pressed) { EditorNode::get_top_split()->set_visible(!p_pressed); } bool EditorBottomPanel::_button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) { if (!p_button->is_pressed()) { - _switch_by_control(true, p_control); + _switch_by_control(true, p_control, true); } return false; } @@ -149,7 +162,7 @@ void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, c Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref<Shortcut> &p_shortcut, bool p_at_front) { Button *tb = memnew(Button); tb->set_theme_type_variation("BottomPanelButton"); - tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item)); + tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item, true)); tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorBottomPanel::_button_drag_hover).bind(tb, p_item), Callable()); tb->set_text(p_text); tb->set_shortcut(p_shortcut); @@ -231,10 +244,10 @@ void EditorBottomPanel::toggle_last_opened_bottom_panel() { // Select by control instead of index, so that the last bottom panel is opened correctly // if it's been reordered since. if (last_opened_control) { - _switch_by_control(!last_opened_control->is_visible(), last_opened_control); + _switch_by_control(!last_opened_control->is_visible(), last_opened_control, true); } else { // Open the first panel in the list if no panel was opened this session. - _switch_to_item(true, 0); + _switch_to_item(true, 0, true); } } @@ -263,10 +276,17 @@ EditorBottomPanel::EditorBottomPanel() { Control *h_spacer = memnew(Control); bottom_hbox->add_child(h_spacer); + pin_button = memnew(Button); + bottom_hbox->add_child(pin_button); + pin_button->hide(); + pin_button->set_theme_type_variation("FlatMenuButton"); + pin_button->set_toggle_mode(true); + pin_button->set_tooltip_text(TTR("Pin Bottom Panel Switching")); + pin_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_pin_button_toggled)); + expand_button = memnew(Button); bottom_hbox->add_child(expand_button); expand_button->hide(); - expand_button->set_flat(false); expand_button->set_theme_type_variation("FlatMenuButton"); expand_button->set_toggle_mode(true); expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12)); diff --git a/editor/gui/editor_bottom_panel.h b/editor/gui/editor_bottom_panel.h index 3d44b3750a..950f0e2570 100644 --- a/editor/gui/editor_bottom_panel.h +++ b/editor/gui/editor_bottom_panel.h @@ -49,16 +49,19 @@ class EditorBottomPanel : public PanelContainer { }; Vector<BottomPanelItem> items; + bool lock_panel_switching = false; VBoxContainer *item_vbox = nullptr; HBoxContainer *bottom_hbox = nullptr; HBoxContainer *button_hbox = nullptr; EditorToaster *editor_toaster = nullptr; + Button *pin_button = nullptr; Button *expand_button = nullptr; Control *last_opened_control = nullptr; - void _switch_by_control(bool p_visible, Control *p_control); - void _switch_to_item(bool p_visible, int p_idx); + void _switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock = false); + void _switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock = false); + void _pin_button_toggled(bool p_pressed); void _expand_button_toggled(bool p_pressed); bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control); diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index 91b7810f77..0381609804 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -65,7 +65,7 @@ void EditorFileDialog::_native_popup() { } else if (access == ACCESS_USERDATA) { root = OS::get_singleton()->get_user_data_dir(); } - DisplayServer::get_singleton()->file_dialog_with_options_show(get_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, _get_options(), callable_mp(this, &EditorFileDialog::_native_dialog_cb)); + DisplayServer::get_singleton()->file_dialog_with_options_show(get_translated_title(), ProjectSettings::get_singleton()->globalize_path(dir->get_text()), root, file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), processed_filters, _get_options(), callable_mp(this, &EditorFileDialog::_native_dialog_cb)); } void EditorFileDialog::popup(const Rect2i &p_rect) { @@ -156,7 +156,7 @@ void EditorFileDialog::popup_file_dialog() { } void EditorFileDialog::_focus_file_text() { - int lp = file->get_text().rfind("."); + int lp = file->get_text().rfind_char('.'); if (lp != -1) { file->select(0, lp); file->grab_focus(); @@ -350,7 +350,7 @@ void EditorFileDialog::shortcut_input(const Ref<InputEvent> &p_event) { void EditorFileDialog::set_enable_multiple_selection(bool p_enable) { item_list->set_select_mode(p_enable ? ItemList::SELECT_MULTI : ItemList::SELECT_SINGLE); -}; +} Vector<String> EditorFileDialog::get_selected_files() const { Vector<String> list; @@ -360,7 +360,7 @@ Vector<String> EditorFileDialog::get_selected_files() const { } } return list; -}; +} void EditorFileDialog::update_dir() { if (drives->is_visible()) { @@ -1148,37 +1148,54 @@ void EditorFileDialog::_filter_selected(int) { void EditorFileDialog::update_filters() { filter->clear(); + processed_filters.clear(); if (filters.size() > 1) { String all_filters; + String all_filters_full; const int max_filters = 5; for (int i = 0; i < MIN(max_filters, filters.size()); i++) { - String flt = filters[i].get_slice(";", 0).strip_edges(); + String flt = filters[i].get_slicec(';', 0).strip_edges(); if (i > 0) { all_filters += ", "; } all_filters += flt; } + for (int i = 0; i < filters.size(); i++) { + String flt = filters[i].get_slicec(';', 0).strip_edges(); + if (i > 0) { + all_filters_full += ","; + } + all_filters_full += flt; + } if (max_filters < filters.size()) { all_filters += ", ..."; } - filter->add_item(TTR("All Recognized") + " (" + all_filters + ")"); + String f = TTR("All Recognized") + " (" + all_filters + ")"; + filter->add_item(f); + processed_filters.push_back(all_filters_full + ";" + f); } for (int i = 0; i < filters.size(); i++) { - String flt = filters[i].get_slice(";", 0).strip_edges(); + String flt = filters[i].get_slicec(';', 0).strip_edges(); String desc = filters[i].get_slice(";", 1).strip_edges(); if (desc.length()) { - filter->add_item(desc + " (" + flt + ")"); + String f = desc + " (" + flt + ")"; + filter->add_item(f); + processed_filters.push_back(flt + ";" + f); } else { - filter->add_item("(" + flt + ")"); + String f = "(" + flt + ")"; + filter->add_item(f); + processed_filters.push_back(flt + ";" + f); } } - filter->add_item(TTR("All Files (*)")); + String f = TTR("All Files (*)"); + filter->add_item(f); + processed_filters.push_back("*.*;" + f); } void EditorFileDialog::clear_filters() { @@ -1246,7 +1263,7 @@ void EditorFileDialog::set_current_path(const String &p_path) { if (!p_path.size()) { return; } - int pos = MAX(p_path.rfind("/"), p_path.rfind("\\")); + int pos = MAX(p_path.rfind_char('/'), p_path.rfind_char('\\')); if (pos == -1) { set_current_file(p_path); } else { @@ -1355,6 +1372,13 @@ EditorFileDialog::Access EditorFileDialog::get_access() const { void EditorFileDialog::_make_dir_confirm() { const String stripped_dirname = makedirname->get_text().strip_edges(); + if (stripped_dirname.is_empty()) { + error_dialog->set_text(TTR("The path specified is invalid.")); + error_dialog->popup_centered(Size2(250, 50) * EDSCALE); + makedirname->set_text(""); // Reset label. + return; + } + if (dir_access->dir_exists(stripped_dirname)) { error_dialog->set_text(TTR("Could not create folder. File with that name already exists.")); error_dialog->popup_centered(Size2(250, 50) * EDSCALE); @@ -1476,29 +1500,29 @@ void EditorFileDialog::_update_drives(bool p_select) { void EditorFileDialog::_update_icons() { // Update icons. - mode_thumbnails->set_icon(theme_cache.mode_thumbnails); - mode_list->set_icon(theme_cache.mode_list); + mode_thumbnails->set_button_icon(theme_cache.mode_thumbnails); + mode_list->set_button_icon(theme_cache.mode_list); if (is_layout_rtl()) { - dir_prev->set_icon(theme_cache.forward_folder); - dir_next->set_icon(theme_cache.back_folder); + dir_prev->set_button_icon(theme_cache.forward_folder); + dir_next->set_button_icon(theme_cache.back_folder); } else { - dir_prev->set_icon(theme_cache.back_folder); - dir_next->set_icon(theme_cache.forward_folder); + dir_prev->set_button_icon(theme_cache.back_folder); + dir_next->set_button_icon(theme_cache.forward_folder); } - dir_up->set_icon(theme_cache.parent_folder); + dir_up->set_button_icon(theme_cache.parent_folder); - refresh->set_icon(theme_cache.reload); - favorite->set_icon(theme_cache.favorite); - show_hidden->set_icon(theme_cache.toggle_hidden); - makedir->set_icon(theme_cache.create_folder); + refresh->set_button_icon(theme_cache.reload); + favorite->set_button_icon(theme_cache.favorite); + show_hidden->set_button_icon(theme_cache.toggle_hidden); + makedir->set_button_icon(theme_cache.create_folder); filter_box->set_right_icon(theme_cache.filter_box); - file_sort_button->set_icon(theme_cache.file_sort_button); + file_sort_button->set_button_icon(theme_cache.file_sort_button); filter_box->set_clear_button_enabled(true); - fav_up->set_icon(theme_cache.favorites_up); - fav_down->set_icon(theme_cache.favorites_down); + fav_up->set_button_icon(theme_cache.favorites_up); + fav_down->set_button_icon(theme_cache.favorites_down); } void EditorFileDialog::_favorite_selected(int p_idx) { diff --git a/editor/gui/editor_file_dialog.h b/editor/gui/editor_file_dialog.h index 1922155133..7a928a6188 100644 --- a/editor/gui/editor_file_dialog.h +++ b/editor/gui/editor_file_dialog.h @@ -145,6 +145,7 @@ private: void _push_history(); Vector<String> filters; + Vector<String> processed_filters; bool previews_enabled = true; bool preview_waiting = false; diff --git a/editor/gui/editor_quick_open_dialog.cpp b/editor/gui/editor_quick_open_dialog.cpp index 94a5ff94a3..44e7b3e483 100644 --- a/editor/gui/editor_quick_open_dialog.cpp +++ b/editor/gui/editor_quick_open_dialog.cpp @@ -30,6 +30,7 @@ #include "editor_quick_open_dialog.h" +#include "core/string/fuzzy_search.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" #include "editor/editor_resource_preview.h" @@ -45,6 +46,55 @@ #include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" +void HighlightedLabel::draw_substr_rects(const Vector2i &p_substr, Vector2 p_offset, int p_line_limit, int line_spacing) { + for (int i = get_lines_skipped(); i < p_line_limit; i++) { + RID line = get_line_rid(i); + Vector<Vector2> ranges = TS->shaped_text_get_selection(line, p_substr.x, p_substr.x + p_substr.y); + Rect2 line_rect = get_line_rect(i); + for (const Vector2 &range : ranges) { + Rect2 rect = Rect2(Point2(range.x, 0) + line_rect.position, Size2(range.y - range.x, line_rect.size.y)); + rect.position = p_offset + line_rect.position; + rect.position.x += range.x; + rect.size = Size2(range.y - range.x, line_rect.size.y); + rect.size.x = MIN(rect.size.x, line_rect.size.x - range.x); + if (rect.size.x > 0) { + draw_rect(rect, Color(1, 1, 1, 0.07), true); + draw_rect(rect, Color(0.5, 0.7, 1.0, 0.4), false, 1); + } + } + p_offset.y += line_spacing + TS->shaped_text_get_ascent(line) + TS->shaped_text_get_descent(line); + } +} + +void HighlightedLabel::add_highlight(const Vector2i &p_interval) { + if (p_interval.y > 0) { + highlights.append(p_interval); + queue_redraw(); + } +} + +void HighlightedLabel::reset_highlights() { + highlights.clear(); + queue_redraw(); +} + +void HighlightedLabel::_notification(int p_notification) { + if (p_notification == NOTIFICATION_DRAW) { + if (highlights.is_empty()) { + return; + } + + Vector2 offset; + int line_limit; + int line_spacing; + get_layout_data(offset, line_limit, line_spacing); + + for (const Vector2i &substr : highlights) { + draw_substr_rects(substr, offset, line_limit, line_spacing); + } + } +} + EditorQuickOpenDialog::EditorQuickOpenDialog() { VBoxContainer *vbc = memnew(VBoxContainer); vbc->add_theme_constant_override("separation", 0); @@ -100,7 +150,7 @@ void EditorQuickOpenDialog::popup_dialog(const Vector<StringName> &p_base_types, get_ok_button()->set_disabled(container->has_nothing_selected()); set_title(get_dialog_title(p_base_types)); - popup_centered_clamped(Size2(710, 650) * EDSCALE, 0.8f); + popup_centered_clamped(Size2(780, 650) * EDSCALE, 0.8f); search_box->grab_focus(); } @@ -119,13 +169,18 @@ void EditorQuickOpenDialog::cancel_pressed() { } void EditorQuickOpenDialog::_search_box_text_changed(const String &p_query) { - container->update_results(p_query.to_lower()); - + container->set_query_and_update(p_query); get_ok_button()->set_disabled(container->has_nothing_selected()); } //------------------------- Result Container +void style_button(Button *p_button) { + p_button->set_flat(true); + p_button->set_focus_mode(Control::FOCUS_NONE); + p_button->set_default_cursor_shape(Control::CURSOR_POINTING_HAND); +} + QuickOpenResultContainer::QuickOpenResultContainer() { set_h_size_flags(Control::SIZE_EXPAND_FILL); set_v_size_flags(Control::SIZE_EXPAND_FILL); @@ -175,91 +230,107 @@ QuickOpenResultContainer::QuickOpenResultContainer() { } { - // Bottom bar - HBoxContainer *bottom_bar = memnew(HBoxContainer); - add_child(bottom_bar); - + // Selected filepath file_details_path = memnew(Label); file_details_path->set_h_size_flags(Control::SIZE_EXPAND_FILL); file_details_path->set_horizontal_alignment(HorizontalAlignment::HORIZONTAL_ALIGNMENT_CENTER); file_details_path->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); - bottom_bar->add_child(file_details_path); + add_child(file_details_path); + } - { - HBoxContainer *hbc = memnew(HBoxContainer); - hbc->add_theme_constant_override("separation", 3); - bottom_bar->add_child(hbc); - - include_addons_toggle = memnew(CheckButton); - include_addons_toggle->set_flat(true); - include_addons_toggle->set_focus_mode(Control::FOCUS_NONE); - include_addons_toggle->set_default_cursor_shape(CURSOR_POINTING_HAND); - include_addons_toggle->set_tooltip_text(TTR("Include files from addons")); - include_addons_toggle->connect(SceneStringName(toggled), callable_mp(this, &QuickOpenResultContainer::_toggle_include_addons)); - hbc->add_child(include_addons_toggle); - - VSeparator *vsep = memnew(VSeparator); - vsep->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - vsep->set_custom_minimum_size(Size2i(0, 14 * EDSCALE)); - hbc->add_child(vsep); - - display_mode_toggle = memnew(Button); - display_mode_toggle->set_flat(true); - display_mode_toggle->set_focus_mode(Control::FOCUS_NONE); - display_mode_toggle->set_default_cursor_shape(CURSOR_POINTING_HAND); - display_mode_toggle->connect(SceneStringName(pressed), callable_mp(this, &QuickOpenResultContainer::_toggle_display_mode)); - hbc->add_child(display_mode_toggle); - } + { + // Bottom bar + HBoxContainer *bottom_bar = memnew(HBoxContainer); + bottom_bar->set_h_size_flags(Control::SIZE_EXPAND_FILL); + bottom_bar->set_alignment(ALIGNMENT_END); + bottom_bar->add_theme_constant_override("separation", 3); + add_child(bottom_bar); + + fuzzy_search_toggle = memnew(CheckButton); + style_button(fuzzy_search_toggle); + fuzzy_search_toggle->set_text(TTR("Fuzzy Search")); + fuzzy_search_toggle->set_tooltip_text(TTR("Enable fuzzy matching")); + fuzzy_search_toggle->connect(SceneStringName(toggled), callable_mp(this, &QuickOpenResultContainer::_toggle_fuzzy_search)); + bottom_bar->add_child(fuzzy_search_toggle); + + include_addons_toggle = memnew(CheckButton); + style_button(include_addons_toggle); + include_addons_toggle->set_text(TTR("Addons")); + include_addons_toggle->set_tooltip_text(TTR("Include files from addons")); + include_addons_toggle->connect(SceneStringName(toggled), callable_mp(this, &QuickOpenResultContainer::_toggle_include_addons)); + bottom_bar->add_child(include_addons_toggle); + + VSeparator *vsep = memnew(VSeparator); + vsep->set_v_size_flags(Control::SIZE_SHRINK_CENTER); + vsep->set_custom_minimum_size(Size2i(0, 14 * EDSCALE)); + bottom_bar->add_child(vsep); + + display_mode_toggle = memnew(Button); + style_button(display_mode_toggle); + display_mode_toggle->connect(SceneStringName(pressed), callable_mp(this, &QuickOpenResultContainer::_toggle_display_mode)); + bottom_bar->add_child(display_mode_toggle); } +} - // Creating and deleting nodes while searching is slow, so we allocate - // a bunch of result nodes and fill in the content based on result ranking. - result_items.resize(TOTAL_ALLOCATED_RESULT_ITEMS); - for (int i = 0; i < TOTAL_ALLOCATED_RESULT_ITEMS; i++) { +void QuickOpenResultContainer::_ensure_result_vector_capacity() { + int target_size = EDITOR_GET("filesystem/quick_open_dialog/max_results"); + int initial_size = result_items.size(); + for (int i = target_size; i < initial_size; i++) { + result_items[i]->queue_free(); + } + result_items.resize(target_size); + for (int i = initial_size; i < target_size; i++) { QuickOpenResultItem *item = memnew(QuickOpenResultItem); item->connect(SceneStringName(gui_input), callable_mp(this, &QuickOpenResultContainer::_item_input).bind(i)); result_items.write[i] = item; - } -} - -QuickOpenResultContainer::~QuickOpenResultContainer() { - if (never_opened) { - for (QuickOpenResultItem *E : result_items) { - memdelete(E); + if (!never_opened) { + _layout_result_item(item); } } } void QuickOpenResultContainer::init(const Vector<StringName> &p_base_types) { + _ensure_result_vector_capacity(); base_types = p_base_types; - never_opened = false; const int display_mode_behavior = EDITOR_GET("filesystem/quick_open_dialog/default_display_mode"); const bool adaptive_display_mode = (display_mode_behavior == 0); if (adaptive_display_mode) { _set_display_mode(get_adaptive_display_mode(p_base_types)); + } else if (never_opened) { + int last = EditorSettings::get_singleton()->get_project_metadata("quick_open_dialog", "last_mode", (int)QuickOpenDisplayMode::LIST); + _set_display_mode((QuickOpenDisplayMode)last); } + const bool fuzzy_matching = EDITOR_GET("filesystem/quick_open_dialog/enable_fuzzy_matching"); const bool include_addons = EDITOR_GET("filesystem/quick_open_dialog/include_addons"); + fuzzy_search_toggle->set_pressed_no_signal(fuzzy_matching); include_addons_toggle->set_pressed_no_signal(include_addons); + never_opened = false; - _create_initial_results(include_addons); + const bool enable_highlights = EDITOR_GET("filesystem/quick_open_dialog/show_search_highlight"); + for (QuickOpenResultItem *E : result_items) { + E->enable_highlights = enable_highlights; + } + + _create_initial_results(); } -void QuickOpenResultContainer::_create_initial_results(bool p_include_addons) { - file_type_icons.insert("__default_icon", get_editor_theme_icon(SNAME("Object"))); - _find_candidates_in_folder(EditorFileSystem::get_singleton()->get_filesystem(), p_include_addons); - max_total_results = MIN(candidates.size(), TOTAL_ALLOCATED_RESULT_ITEMS); +void QuickOpenResultContainer::_create_initial_results() { file_type_icons.clear(); - - update_results(query); + file_type_icons.insert("__default_icon", get_editor_theme_icon(SNAME("Object"))); + filepaths.clear(); + filetypes.clear(); + _find_filepaths_in_folder(EditorFileSystem::get_singleton()->get_filesystem(), include_addons_toggle->is_pressed()); + max_total_results = MIN(filepaths.size(), result_items.size()); + update_results(); } -void QuickOpenResultContainer::_find_candidates_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons) { +void QuickOpenResultContainer::_find_filepaths_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons) { for (int i = 0; i < p_directory->get_subdir_count(); i++) { if (p_include_addons || p_directory->get_name() != "addons") { - _find_candidates_in_folder(p_directory->get_subdir(i), p_include_addons); + _find_filepaths_in_folder(p_directory->get_subdir(i), p_include_addons); } } @@ -276,146 +347,91 @@ void QuickOpenResultContainer::_find_candidates_in_folder(EditorFileSystemDirect bool is_valid = ClassDB::is_parent_class(engine_type, parent_type) || (!is_engine_type && EditorNode::get_editor_data().script_class_is_parent(script_type, parent_type)); if (is_valid) { - Candidate c; - c.file_name = file_path.get_file(); - c.file_directory = file_path.get_base_dir(); - - EditorResourcePreview::PreviewItem item = EditorResourcePreview::get_singleton()->get_resource_preview_if_available(file_path); - if (item.preview.is_valid()) { - c.thumbnail = item.preview; - } else if (file_type_icons.has(actual_type)) { - c.thumbnail = *file_type_icons.lookup_ptr(actual_type); - } else if (has_theme_icon(actual_type, EditorStringName(EditorIcons))) { - c.thumbnail = get_editor_theme_icon(actual_type); - file_type_icons.insert(actual_type, c.thumbnail); - } else { - c.thumbnail = *file_type_icons.lookup_ptr("__default_icon"); - } - - candidates.push_back(c); - + filepaths.append(file_path); + filetypes.insert(file_path, actual_type); break; // Stop testing base types as soon as we get a match. } } } } -void QuickOpenResultContainer::update_results(const String &p_query) { +void QuickOpenResultContainer::set_query_and_update(const String &p_query) { query = p_query; - - int relevant_candidates = _sort_candidates(p_query); - _update_result_items(MIN(relevant_candidates, max_total_results), 0); -} - -int QuickOpenResultContainer::_sort_candidates(const String &p_query) { - if (p_query.is_empty()) { - return 0; + update_results(); +} + +void QuickOpenResultContainer::_setup_candidate(QuickOpenResultCandidate &candidate, const String &filepath) { + StringName actual_type = *filetypes.lookup_ptr(filepath); + candidate.file_path = filepath; + candidate.result = nullptr; + + EditorResourcePreview::PreviewItem item = EditorResourcePreview::get_singleton()->get_resource_preview_if_available(filepath); + if (item.preview.is_valid()) { + candidate.thumbnail = item.preview; + } else if (file_type_icons.has(actual_type)) { + candidate.thumbnail = *file_type_icons.lookup_ptr(actual_type); + } else if (has_theme_icon(actual_type, EditorStringName(EditorIcons))) { + candidate.thumbnail = get_editor_theme_icon(actual_type); + file_type_icons.insert(actual_type, candidate.thumbnail); + } else { + candidate.thumbnail = *file_type_icons.lookup_ptr("__default_icon"); } +} - const PackedStringArray search_tokens = p_query.to_lower().replace("/", " ").split(" ", false); +void QuickOpenResultContainer::_setup_candidate(QuickOpenResultCandidate &p_candidate, const FuzzySearchResult &p_result) { + _setup_candidate(p_candidate, p_result.target); + p_candidate.result = &p_result; +} - if (search_tokens.is_empty()) { - return 0; +void QuickOpenResultContainer::update_results() { + showing_history = false; + candidates.clear(); + if (query.is_empty()) { + _use_default_candidates(); + } else { + _score_and_sort_candidates(); } + _update_result_items(MIN(candidates.size(), max_total_results), 0); +} - // First, we assign a score to each candidate. - int num_relevant_candidates = 0; - for (Candidate &c : candidates) { - c.score = 0; - int prev_token_match_pos = -1; - - for (const String &token : search_tokens) { - const int file_pos = c.file_name.findn(token); - const int dir_pos = c.file_directory.findn(token); - - const bool file_match = file_pos > -1; - const bool dir_match = dir_pos > -1; - if (!file_match && !dir_match) { - c.score = -1.0f; - break; - } - - float token_score = file_match ? 0.6f : 0.1999f; - - // Add bias for shorter filenames/paths: they resemble the query more. - const String &matched_string = file_match ? c.file_name : c.file_directory; - int matched_string_token_pos = file_match ? file_pos : dir_pos; - token_score += 0.1f * (1.0f - ((float)matched_string_token_pos / (float)matched_string.length())); - - // Add bias if the match happened in the file name, not the extension. - if (file_match) { - int ext_pos = matched_string.rfind("."); - if (ext_pos == -1 || ext_pos > matched_string_token_pos) { - token_score += 0.1f; - } - } - - // Add bias if token is in order. - { - int candidate_string_token_pos = file_match ? (c.file_directory.length() + file_pos) : dir_pos; - - if (prev_token_match_pos != -1 && candidate_string_token_pos > prev_token_match_pos) { - token_score += 0.2f; - } - - prev_token_match_pos = candidate_string_token_pos; - } - - c.score += token_score; +void QuickOpenResultContainer::_use_default_candidates() { + if (filepaths.size() <= SHOW_ALL_FILES_THRESHOLD) { + candidates.resize(filepaths.size()); + QuickOpenResultCandidate *candidates_write = candidates.ptrw(); + for (const String &filepath : filepaths) { + _setup_candidate(*candidates_write++, filepath); } - - if (c.score > 0.0f) { - num_relevant_candidates++; + } else if (base_types.size() == 1) { + Vector<QuickOpenResultCandidate> *history = selected_history.lookup_ptr(base_types[0]); + if (history) { + showing_history = true; + candidates.append_array(*history); } } - - // Now we will sort the candidates based on score, resolving ties by favoring: - // 1. Shorter file length. - // 2. Shorter directory length. - // 3. Lower alphabetic order. - struct CandidateComparator { - _FORCE_INLINE_ bool operator()(const Candidate &p_a, const Candidate &p_b) const { - if (!Math::is_equal_approx(p_a.score, p_b.score)) { - return p_a.score > p_b.score; - } - - if (p_a.file_name.length() != p_b.file_name.length()) { - return p_a.file_name.length() < p_b.file_name.length(); - } - - if (p_a.file_directory.length() != p_b.file_directory.length()) { - return p_a.file_directory.length() < p_b.file_directory.length(); - } - - return p_a.file_name < p_b.file_name; - } - }; - candidates.sort_custom<CandidateComparator>(); - - return num_relevant_candidates; } -void QuickOpenResultContainer::_update_result_items(int p_new_visible_results_count, int p_new_selection_index) { - List<Candidate> *type_history = nullptr; - - showing_history = false; - - if (query.is_empty()) { - if (candidates.size() <= SHOW_ALL_FILES_THRESHOLD) { - p_new_visible_results_count = candidates.size(); - } else { - p_new_visible_results_count = 0; +void QuickOpenResultContainer::_update_fuzzy_search_results() { + FuzzySearch fuzzy_search; + fuzzy_search.start_offset = 6; // Don't match against "res://" at the start of each filepath. + fuzzy_search.set_query(query); + fuzzy_search.max_results = max_total_results; + bool fuzzy_matching = EDITOR_GET("filesystem/quick_open_dialog/enable_fuzzy_matching"); + int max_misses = EDITOR_GET("filesystem/quick_open_dialog/max_fuzzy_misses"); + fuzzy_search.allow_subsequences = fuzzy_matching; + fuzzy_search.max_misses = fuzzy_matching ? max_misses : 0; + fuzzy_search.search_all(filepaths, search_results); +} - if (base_types.size() == 1) { - type_history = selected_history.lookup_ptr(base_types[0]); - if (type_history) { - p_new_visible_results_count = type_history->size(); - showing_history = true; - } - } - } +void QuickOpenResultContainer::_score_and_sort_candidates() { + _update_fuzzy_search_results(); + candidates.resize(search_results.size()); + QuickOpenResultCandidate *candidates_write = candidates.ptrw(); + for (const FuzzySearchResult &result : search_results) { + _setup_candidate(*candidates_write++, result); } +} +void QuickOpenResultContainer::_update_result_items(int p_new_visible_results_count, int p_new_selection_index) { // Only need to update items that were not hidden in previous update. int num_items_needing_updates = MAX(num_visible_results, p_new_visible_results_count); num_visible_results = p_new_visible_results_count; @@ -424,13 +440,7 @@ void QuickOpenResultContainer::_update_result_items(int p_new_visible_results_co QuickOpenResultItem *item = result_items[i]; if (i < num_visible_results) { - if (type_history) { - const Candidate &c = type_history->get(i); - item->set_content(c.thumbnail, c.file_name, c.file_directory); - } else { - const Candidate &c = candidates[i]; - item->set_content(c.thumbnail, c.file_name, c.file_directory); - } + item->set_content(candidates[i]); } else { item->reset(); } @@ -443,7 +453,7 @@ void QuickOpenResultContainer::_update_result_items(int p_new_visible_results_co no_results_container->set_visible(!any_results); if (!any_results) { - if (candidates.is_empty()) { + if (filepaths.is_empty()) { no_results_label->set_text(TTR("No files found for this type")); } else if (query.is_empty()) { no_results_label->set_text(TTR("Start searching to find files...")); @@ -471,10 +481,12 @@ void QuickOpenResultContainer::handle_search_box_input(const Ref<InputEvent> &p_ } break; case Key::LEFT: case Key::RIGHT: { - // Both grid and the search box use left/right keys. By default, grid will take it. - // It would be nice if we could check for ALT to give the event to the searchbox cursor. - // However, if you press ALT, the searchbox also denies the input. - move_selection = (content_display_mode == QuickOpenDisplayMode::GRID); + if (content_display_mode == QuickOpenDisplayMode::GRID) { + // Maybe strip off the shift modifier to allow non-selecting navigation by character? + if (key_event->get_modifiers_mask() == 0) { + move_selection = true; + } + } } break; default: break; // Let the event through so it will reach the search box. @@ -489,6 +501,10 @@ void QuickOpenResultContainer::handle_search_box_input(const Ref<InputEvent> &p_ } void QuickOpenResultContainer::_move_selection_index(Key p_key) { + // Don't move selection if there are no results. + if (num_visible_results <= 0) { + return; + } const int max_index = num_visible_results - 1; int idx = selection_index; @@ -562,11 +578,15 @@ void QuickOpenResultContainer::_item_input(const Ref<InputEvent> &p_ev, int p_in } } +void QuickOpenResultContainer::_toggle_fuzzy_search(bool p_pressed) { + EditorSettings::get_singleton()->set("filesystem/quick_open_dialog/enable_fuzzy_matching", p_pressed); + update_results(); +} + void QuickOpenResultContainer::_toggle_include_addons(bool p_pressed) { EditorSettings::get_singleton()->set("filesystem/quick_open_dialog/include_addons", p_pressed); - cleanup(); - _create_initial_results(p_pressed); + _create_initial_results(); } void QuickOpenResultContainer::_toggle_display_mode() { @@ -574,49 +594,49 @@ void QuickOpenResultContainer::_toggle_display_mode() { _set_display_mode(new_display_mode); } -void QuickOpenResultContainer::_set_display_mode(QuickOpenDisplayMode p_display_mode) { - content_display_mode = p_display_mode; +CanvasItem *QuickOpenResultContainer::_get_result_root() { + if (content_display_mode == QuickOpenDisplayMode::LIST) { + return list; + } else { + return grid; + } +} - const bool show_list = (content_display_mode == QuickOpenDisplayMode::LIST); - if ((show_list && list->is_visible()) || (!show_list && grid->is_visible())) { - return; +void QuickOpenResultContainer::_layout_result_item(QuickOpenResultItem *item) { + item->set_display_mode(content_display_mode); + Node *parent = item->get_parent(); + if (parent) { + parent->remove_child(item); } + _get_result_root()->add_child(item); +} - hide(); +void QuickOpenResultContainer::_set_display_mode(QuickOpenDisplayMode p_display_mode) { + CanvasItem *prev_root = _get_result_root(); - // Move result item nodes from one container to the other. - CanvasItem *prev_root; - CanvasItem *next_root; - if (content_display_mode == QuickOpenDisplayMode::LIST) { - prev_root = Object::cast_to<CanvasItem>(grid); - next_root = Object::cast_to<CanvasItem>(list); - } else { - prev_root = Object::cast_to<CanvasItem>(list); - next_root = Object::cast_to<CanvasItem>(grid); + if (prev_root->is_visible() && content_display_mode == p_display_mode) { + return; } - const bool first_time = !list->is_visible() && !grid->is_visible(); + content_display_mode = p_display_mode; + CanvasItem *next_root = _get_result_root(); - prev_root->hide(); - for (QuickOpenResultItem *item : result_items) { - item->set_display_mode(content_display_mode); + EditorSettings::get_singleton()->set_project_metadata("quick_open_dialog", "last_mode", (int)content_display_mode); - if (!first_time) { - prev_root->remove_child(item); - } + prev_root->hide(); + next_root->show(); - next_root->add_child(item); + for (QuickOpenResultItem *item : result_items) { + _layout_result_item(item); } - next_root->show(); - show(); _update_result_items(num_visible_results, selection_index); if (content_display_mode == QuickOpenDisplayMode::LIST) { - display_mode_toggle->set_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); + display_mode_toggle->set_button_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); display_mode_toggle->set_tooltip_text(TTR("Grid view")); } else { - display_mode_toggle->set_icon(get_editor_theme_icon(SNAME("FileList"))); + display_mode_toggle->set_button_icon(get_editor_theme_icon(SNAME("FileList"))); display_mode_toggle->set_tooltip_text(TTR("List view")); } } @@ -627,16 +647,7 @@ bool QuickOpenResultContainer::has_nothing_selected() const { String QuickOpenResultContainer::get_selected() const { ERR_FAIL_COND_V_MSG(has_nothing_selected(), String(), "Tried to get selected file, but nothing was selected."); - - if (showing_history) { - const List<Candidate> *type_history = selected_history.lookup_ptr(base_types[0]); - - const Candidate &c = type_history->get(selection_index); - return c.file_directory.path_join(c.file_name); - } else { - const Candidate &c = candidates[selection_index]; - return c.file_directory.path_join(c.file_name); - } + return candidates[selection_index].file_path; } QuickOpenDisplayMode QuickOpenResultContainer::get_adaptive_display_mode(const Vector<StringName> &p_base_types) { @@ -649,8 +660,9 @@ QuickOpenDisplayMode QuickOpenResultContainer::get_adaptive_display_mode(const V for (const StringName &type : grid_preferred_types) { for (const StringName &base_type : p_base_types) { - if (base_type == type || ClassDB::is_parent_class(base_type, type)) + if (base_type == type || ClassDB::is_parent_class(base_type, type)) { return QuickOpenDisplayMode::GRID; + } } } @@ -664,32 +676,27 @@ void QuickOpenResultContainer::save_selected_item() { return; } - if (showing_history) { - // Selecting from history, so already added. - return; - } - const StringName &base_type = base_types[0]; + const QuickOpenResultCandidate &selected = candidates[selection_index]; + Vector<QuickOpenResultCandidate> *type_history = selected_history.lookup_ptr(base_type); - List<Candidate> *type_history = selected_history.lookup_ptr(base_type); if (!type_history) { - selected_history.insert(base_type, List<Candidate>()); + selected_history.insert(base_type, Vector<QuickOpenResultCandidate>()); type_history = selected_history.lookup_ptr(base_type); } else { - const Candidate &selected = candidates[selection_index]; - - for (const Candidate &candidate : *type_history) { - if (candidate.file_directory == selected.file_directory && candidate.file_name == selected.file_name) { - return; + for (int i = 0; i < type_history->size(); i++) { + if (selected.file_path == type_history->get(i).file_path) { + type_history->remove_at(i); + break; } } - - if (type_history->size() > 8) { - type_history->pop_back(); - } } - type_history->push_front(candidates[selection_index]); + type_history->insert(0, selected); + type_history->ptrw()->result = nullptr; + if (type_history->size() > MAX_HISTORY_SIZE) { + type_history->resize(MAX_HISTORY_SIZE); + } } void QuickOpenResultContainer::cleanup() { @@ -712,9 +719,9 @@ void QuickOpenResultContainer::_notification(int p_what) { panel_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); if (content_display_mode == QuickOpenDisplayMode::LIST) { - display_mode_toggle->set_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); + display_mode_toggle->set_button_icon(get_editor_theme_icon(SNAME("FileThumbnail"))); } else { - display_mode_toggle->set_icon(get_editor_theme_icon(SNAME("FileList"))); + display_mode_toggle->set_button_icon(get_editor_theme_icon(SNAME("FileList"))); } } break; } @@ -743,36 +750,35 @@ QuickOpenResultItem::QuickOpenResultItem() { void QuickOpenResultItem::set_display_mode(QuickOpenDisplayMode p_display_mode) { if (p_display_mode == QuickOpenDisplayMode::LIST) { grid_item->hide(); + grid_item->reset(); list_item->show(); } else { list_item->hide(); + list_item->reset(); grid_item->show(); } queue_redraw(); } -void QuickOpenResultItem::set_content(const Ref<Texture2D> &p_thumbnail, const String &p_file, const String &p_file_directory) { +void QuickOpenResultItem::set_content(const QuickOpenResultCandidate &p_candidate) { _set_enabled(true); if (list_item->is_visible()) { - list_item->set_content(p_thumbnail, p_file, p_file_directory); + list_item->set_content(p_candidate, enable_highlights); } else { - grid_item->set_content(p_thumbnail, p_file); + grid_item->set_content(p_candidate, enable_highlights); } + + queue_redraw(); } void QuickOpenResultItem::reset() { _set_enabled(false); - is_hovering = false; is_selected = false; - - if (list_item->is_visible()) { - list_item->reset(); - } else { - grid_item->reset(); - } + list_item->reset(); + grid_item->reset(); } void QuickOpenResultItem::highlight_item(bool p_enabled) { @@ -825,6 +831,22 @@ void QuickOpenResultItem::_notification(int p_what) { //----------------- List item +static Vector2i _get_path_interval(const Vector2i &p_interval, int p_dir_index) { + if (p_interval.x >= p_dir_index || p_interval.y < 1) { + return { -1, -1 }; + } + return { p_interval.x, MIN(p_interval.x + p_interval.y, p_dir_index) - p_interval.x }; +} + +static Vector2i _get_name_interval(const Vector2i &p_interval, int p_dir_index) { + if (p_interval.x + p_interval.y <= p_dir_index || p_interval.y < 1) { + return { -1, -1 }; + } + int first_name_idx = p_dir_index + 1; + int start = MAX(p_interval.x, first_name_idx); + return { start - first_name_idx, p_interval.y - start + p_interval.x }; +} + QuickOpenResultListItem::QuickOpenResultListItem() { set_h_size_flags(Control::SIZE_EXPAND_FILL); add_theme_constant_override("separation", 4 * EDSCALE); @@ -852,13 +874,13 @@ QuickOpenResultListItem::QuickOpenResultListItem() { text_container->set_v_size_flags(Control::SIZE_FILL); add_child(text_container); - name = memnew(Label); + name = memnew(HighlightedLabel); name->set_h_size_flags(Control::SIZE_EXPAND_FILL); name->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); name->set_horizontal_alignment(HorizontalAlignment::HORIZONTAL_ALIGNMENT_LEFT); text_container->add_child(name); - path = memnew(Label); + path = memnew(HighlightedLabel); path->set_h_size_flags(Control::SIZE_EXPAND_FILL); path->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); path->add_theme_font_size_override(SceneStringName(font_size), 12 * EDSCALE); @@ -866,18 +888,29 @@ QuickOpenResultListItem::QuickOpenResultListItem() { } } -void QuickOpenResultListItem::set_content(const Ref<Texture2D> &p_thumbnail, const String &p_file, const String &p_file_directory) { - thumbnail->set_texture(p_thumbnail); - name->set_text(p_file); - path->set_text(p_file_directory); +void QuickOpenResultListItem::set_content(const QuickOpenResultCandidate &p_candidate, bool p_highlight) { + thumbnail->set_texture(p_candidate.thumbnail); + name->set_text(p_candidate.file_path.get_file()); + path->set_text(p_candidate.file_path.get_base_dir()); + name->reset_highlights(); + path->reset_highlights(); + + if (p_highlight && p_candidate.result != nullptr) { + for (const FuzzyTokenMatch &match : p_candidate.result->token_matches) { + for (const Vector2i &interval : match.substrings) { + path->add_highlight(_get_path_interval(interval, p_candidate.result->dir_index)); + name->add_highlight(_get_name_interval(interval, p_candidate.result->dir_index)); + } + } + } const int max_size = 32 * EDSCALE; - bool uses_icon = p_thumbnail->get_width() < max_size; + bool uses_icon = p_candidate.thumbnail->get_width() < max_size; if (uses_icon) { - thumbnail->set_custom_minimum_size(p_thumbnail->get_size()); + thumbnail->set_custom_minimum_size(p_candidate.thumbnail->get_size()); - int margin_needed = (max_size - p_thumbnail->get_width()) / 2; + int margin_needed = (max_size - p_candidate.thumbnail->get_width()) / 2; image_container->add_theme_constant_override("margin_left", CONTAINER_MARGIN + margin_needed); image_container->add_theme_constant_override("margin_right", margin_needed); } else { @@ -888,9 +921,11 @@ void QuickOpenResultListItem::set_content(const Ref<Texture2D> &p_thumbnail, con } void QuickOpenResultListItem::reset() { - name->set_text(""); thumbnail->set_texture(nullptr); + name->set_text(""); path->set_text(""); + name->reset_highlights(); + path->reset_highlights(); } void QuickOpenResultListItem::highlight_item(const Color &p_color) { @@ -919,10 +954,10 @@ QuickOpenResultGridItem::QuickOpenResultGridItem() { thumbnail = memnew(TextureRect); thumbnail->set_h_size_flags(Control::SIZE_SHRINK_CENTER); thumbnail->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - thumbnail->set_custom_minimum_size(Size2i(80 * EDSCALE, 64 * EDSCALE)); + thumbnail->set_custom_minimum_size(Size2i(120 * EDSCALE, 64 * EDSCALE)); add_child(thumbnail); - name = memnew(Label); + name = memnew(HighlightedLabel); name->set_h_size_flags(Control::SIZE_EXPAND_FILL); name->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); name->set_horizontal_alignment(HorizontalAlignment::HORIZONTAL_ALIGNMENT_CENTER); @@ -930,16 +965,23 @@ QuickOpenResultGridItem::QuickOpenResultGridItem() { add_child(name); } -void QuickOpenResultGridItem::set_content(const Ref<Texture2D> &p_thumbnail, const String &p_file) { - thumbnail->set_texture(p_thumbnail); +void QuickOpenResultGridItem::set_content(const QuickOpenResultCandidate &p_candidate, bool p_highlight) { + thumbnail->set_texture(p_candidate.thumbnail); + name->set_text(p_candidate.file_path.get_file()); + name->set_tooltip_text(p_candidate.file_path); + name->reset_highlights(); - const String &file_name = p_file.get_basename(); - name->set_text(file_name); - name->set_tooltip_text(file_name); + if (p_highlight && p_candidate.result != nullptr) { + for (const FuzzyTokenMatch &match : p_candidate.result->token_matches) { + for (const Vector2i &interval : match.substrings) { + name->add_highlight(_get_name_interval(interval, p_candidate.result->dir_index)); + } + } + } - bool uses_icon = p_thumbnail->get_width() < (32 * EDSCALE); + bool uses_icon = p_candidate.thumbnail->get_width() < (32 * EDSCALE); - if (uses_icon || p_thumbnail->get_height() <= thumbnail->get_custom_minimum_size().y) { + if (uses_icon || p_candidate.thumbnail->get_height() <= thumbnail->get_custom_minimum_size().y) { thumbnail->set_expand_mode(TextureRect::EXPAND_KEEP_SIZE); thumbnail->set_stretch_mode(TextureRect::StretchMode::STRETCH_KEEP_CENTERED); } else { @@ -949,8 +991,9 @@ void QuickOpenResultGridItem::set_content(const Ref<Texture2D> &p_thumbnail, con } void QuickOpenResultGridItem::reset() { - name->set_text(""); thumbnail->set_texture(nullptr); + name->set_text(""); + name->reset_highlights(); } void QuickOpenResultGridItem::highlight_item(const Color &p_color) { diff --git a/editor/gui/editor_quick_open_dialog.h b/editor/gui/editor_quick_open_dialog.h index 49257aed6b..3b3f927527 100644 --- a/editor/gui/editor_quick_open_dialog.h +++ b/editor/gui/editor_quick_open_dialog.h @@ -48,6 +48,8 @@ class Texture2D; class TextureRect; class VBoxContainer; +class FuzzySearchResult; + class QuickOpenResultItem; enum class QuickOpenDisplayMode { @@ -55,13 +57,35 @@ enum class QuickOpenDisplayMode { LIST, }; +struct QuickOpenResultCandidate { + String file_path; + Ref<Texture2D> thumbnail; + const FuzzySearchResult *result = nullptr; +}; + +class HighlightedLabel : public Label { + GDCLASS(HighlightedLabel, Label) + + Vector<Vector2i> highlights; + + void draw_substr_rects(const Vector2i &p_substr, Vector2 p_offset, int p_line_limit, int line_spacing); + +public: + void add_highlight(const Vector2i &p_interval); + void reset_highlights(); + +protected: + void _notification(int p_notification); +}; + class QuickOpenResultContainer : public VBoxContainer { GDCLASS(QuickOpenResultContainer, VBoxContainer) public: void init(const Vector<StringName> &p_base_types); void handle_search_box_input(const Ref<InputEvent> &p_ie); - void update_results(const String &p_query); + void set_query_and_update(const String &p_query); + void update_results(); bool has_nothing_selected() const; String get_selected() const; @@ -70,27 +94,21 @@ public: void cleanup(); QuickOpenResultContainer(); - ~QuickOpenResultContainer(); protected: void _notification(int p_what); private: - static const int TOTAL_ALLOCATED_RESULT_ITEMS = 100; - static const int SHOW_ALL_FILES_THRESHOLD = 30; - - struct Candidate { - String file_name; - String file_directory; - - Ref<Texture2D> thumbnail; - float score = 0; - }; + static constexpr int SHOW_ALL_FILES_THRESHOLD = 30; + static constexpr int MAX_HISTORY_SIZE = 20; + Vector<FuzzySearchResult> search_results; Vector<StringName> base_types; - Vector<Candidate> candidates; + Vector<String> filepaths; + OAHashMap<String, StringName> filetypes; + Vector<QuickOpenResultCandidate> candidates; - OAHashMap<StringName, List<Candidate>> selected_history; + OAHashMap<StringName, Vector<QuickOpenResultCandidate>> selected_history; String query; int selection_index = -1; @@ -114,15 +132,21 @@ private: Label *file_details_path = nullptr; Button *display_mode_toggle = nullptr; CheckButton *include_addons_toggle = nullptr; + CheckButton *fuzzy_search_toggle = nullptr; OAHashMap<StringName, Ref<Texture2D>> file_type_icons; static QuickOpenDisplayMode get_adaptive_display_mode(const Vector<StringName> &p_base_types); - void _create_initial_results(bool p_include_addons); - void _find_candidates_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons); + void _ensure_result_vector_capacity(); + void _create_initial_results(); + void _find_filepaths_in_folder(EditorFileSystemDirectory *p_directory, bool p_include_addons); - int _sort_candidates(const String &p_query); + void _setup_candidate(QuickOpenResultCandidate &p_candidate, const String &p_filepath); + void _setup_candidate(QuickOpenResultCandidate &p_candidate, const FuzzySearchResult &p_result); + void _update_fuzzy_search_results(); + void _use_default_candidates(); + void _score_and_sort_candidates(); void _update_result_items(int p_new_visible_results_count, int p_new_selection_index); void _move_selection_index(Key p_key); @@ -130,9 +154,12 @@ private: void _item_input(const Ref<InputEvent> &p_ev, int p_index); + CanvasItem *_get_result_root(); + void _layout_result_item(QuickOpenResultItem *p_item); void _set_display_mode(QuickOpenDisplayMode p_display_mode); void _toggle_display_mode(); void _toggle_include_addons(bool p_pressed); + void _toggle_fuzzy_search(bool p_pressed); static void _bind_methods(); }; @@ -143,14 +170,14 @@ class QuickOpenResultGridItem : public VBoxContainer { public: QuickOpenResultGridItem(); - void set_content(const Ref<Texture2D> &p_thumbnail, const String &p_file_name); void reset(); + void set_content(const QuickOpenResultCandidate &p_candidate, bool p_highlight); void highlight_item(const Color &p_color); void remove_highlight(); private: TextureRect *thumbnail = nullptr; - Label *name = nullptr; + HighlightedLabel *name = nullptr; }; class QuickOpenResultListItem : public HBoxContainer { @@ -159,8 +186,8 @@ class QuickOpenResultListItem : public HBoxContainer { public: QuickOpenResultListItem(); - void set_content(const Ref<Texture2D> &p_thumbnail, const String &p_file_name, const String &p_file_directory); void reset(); + void set_content(const QuickOpenResultCandidate &p_candidate, bool p_highlight); void highlight_item(const Color &p_color); void remove_highlight(); @@ -174,8 +201,8 @@ private: VBoxContainer *text_container = nullptr; TextureRect *thumbnail = nullptr; - Label *name = nullptr; - Label *path = nullptr; + HighlightedLabel *name = nullptr; + HighlightedLabel *path = nullptr; }; class QuickOpenResultItem : public HBoxContainer { @@ -184,10 +211,11 @@ class QuickOpenResultItem : public HBoxContainer { public: QuickOpenResultItem(); - void set_content(const Ref<Texture2D> &p_thumbnail, const String &p_file_name, const String &p_file_directory); - void set_display_mode(QuickOpenDisplayMode p_display_mode); - void reset(); + bool enable_highlights = true; + void reset(); + void set_content(const QuickOpenResultCandidate &p_candidate); + void set_display_mode(QuickOpenDisplayMode p_display_mode); void highlight_item(bool p_enabled); protected: diff --git a/editor/gui/editor_run_bar.cpp b/editor/gui/editor_run_bar.cpp index 908b1e6719..64135c8d50 100644 --- a/editor/gui/editor_run_bar.cpp +++ b/editor/gui/editor_run_bar.cpp @@ -52,8 +52,8 @@ void EditorRunBar::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { _update_play_buttons(); - pause_button->set_icon(get_editor_theme_icon(SNAME("Pause"))); - stop_button->set_icon(get_editor_theme_icon(SNAME("Stop"))); + pause_button->set_button_icon(get_editor_theme_icon(SNAME("Pause"))); + stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop"))); if (is_movie_maker_enabled()) { main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("LaunchPadMovieMode"), EditorStringName(EditorStyles))); @@ -63,7 +63,7 @@ void EditorRunBar::_notification(int p_what) { write_movie_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("MovieWriterButtonNormal"), EditorStringName(EditorStyles))); } - write_movie_button->set_icon(get_editor_theme_icon(SNAME("MainMovieWrite"))); + write_movie_button->set_button_icon(get_editor_theme_icon(SNAME("MainMovieWrite"))); // This button behaves differently, so color it as such. write_movie_button->begin_bulk_theme_override(); write_movie_button->add_theme_color_override("icon_normal_color", get_theme_color(SNAME("movie_writer_icon_normal"), EditorStringName(EditorStyles))); @@ -77,15 +77,15 @@ void EditorRunBar::_notification(int p_what) { void EditorRunBar::_reset_play_buttons() { play_button->set_pressed(false); - play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); play_button->set_tooltip_text(TTR("Play the project.")); play_scene_button->set_pressed(false); - play_scene_button->set_icon(get_editor_theme_icon(SNAME("PlayScene"))); + play_scene_button->set_button_icon(get_editor_theme_icon(SNAME("PlayScene"))); play_scene_button->set_tooltip_text(TTR("Play the edited scene.")); play_custom_scene_button->set_pressed(false); - play_custom_scene_button->set_icon(get_editor_theme_icon(SNAME("PlayCustom"))); + play_custom_scene_button->set_button_icon(get_editor_theme_icon(SNAME("PlayCustom"))); play_custom_scene_button->set_tooltip_text(TTR("Play a custom scene.")); } @@ -106,7 +106,7 @@ void EditorRunBar::_update_play_buttons() { if (active_button) { active_button->set_pressed(true); - active_button->set_icon(get_editor_theme_icon(SNAME("Reload"))); + active_button->set_button_icon(get_editor_theme_icon(SNAME("Reload"))); active_button->set_tooltip_text(TTR("Reload the played scene.")); } } diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp index 4862b3436e..5b42afdbe8 100644 --- a/editor/gui/editor_scene_tabs.cpp +++ b/editor/gui/editor_scene_tabs.cpp @@ -53,7 +53,7 @@ void EditorSceneTabs::_notification(int p_what) { tabbar_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("tabbar_background"), SNAME("TabContainer"))); scene_tabs->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor))); - scene_tab_add->set_icon(get_editor_theme_icon(SNAME("Add"))); + scene_tab_add->set_button_icon(get_editor_theme_icon(SNAME("Add"))); scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f)); scene_tab_add_ph->set_custom_minimum_size(scene_tab_add->get_minimum_size()); diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp index a073a2338b..aa9e9f841d 100644 --- a/editor/gui/editor_spin_slider.cpp +++ b/editor/gui/editor_spin_slider.cpp @@ -37,10 +37,6 @@ #include "editor/themes/editor_scale.h" #include "scene/theme/theme_db.h" -bool EditorSpinSlider::is_text_field() const { - return true; -} - String EditorSpinSlider::get_tooltip(const Point2 &p_pos) const { if (!read_only && grabber->is_visible()) { Key key = (OS::get_singleton()->has_feature("macos") || OS::get_singleton()->has_feature("web_macos") || OS::get_singleton()->has_feature("web_ios")) ? Key::META : Key::CTRL; @@ -71,6 +67,7 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) { } else { set_value(get_value() - get_step()); } + emit_signal("updown_pressed"); return; } _grab_start(); @@ -441,7 +438,7 @@ void EditorSpinSlider::_draw_spin_slider() { Vector2 scale = get_global_transform_with_canvas().get_scale(); grabber->set_scale(scale); grabber->reset_size(); - grabber->set_position(get_global_position() + (grabber_rect.get_center() - grabber->get_size() * 0.5) * scale); + grabber->set_position((grabber_rect.get_center() - grabber->get_size() * 0.5) * scale); if (mousewheel_over_grabber) { Input::get_singleton()->warp_mouse(grabber->get_position() + grabber_rect.size); @@ -700,6 +697,7 @@ void EditorSpinSlider::_bind_methods() { ADD_SIGNAL(MethodInfo("grabbed")); ADD_SIGNAL(MethodInfo("ungrabbed")); + ADD_SIGNAL(MethodInfo("updown_pressed")); ADD_SIGNAL(MethodInfo("value_focus_entered")); ADD_SIGNAL(MethodInfo("value_focus_exited")); @@ -735,7 +733,7 @@ EditorSpinSlider::EditorSpinSlider() { grabber = memnew(TextureRect); add_child(grabber); grabber->hide(); - grabber->set_as_top_level(true); + grabber->set_z_index(1); grabber->set_mouse_filter(MOUSE_FILTER_STOP); grabber->connect(SceneStringName(mouse_entered), callable_mp(this, &EditorSpinSlider::_grabber_mouse_entered)); grabber->connect(SceneStringName(mouse_exited), callable_mp(this, &EditorSpinSlider::_grabber_mouse_exited)); diff --git a/editor/gui/editor_spin_slider.h b/editor/gui/editor_spin_slider.h index 2476c2f71b..dfc50878dd 100644 --- a/editor/gui/editor_spin_slider.h +++ b/editor/gui/editor_spin_slider.h @@ -101,8 +101,6 @@ protected: void _focus_entered(); public: - virtual bool is_text_field() const override; - String get_tooltip(const Point2 &p_pos) const override; String get_text_value() const; diff --git a/editor/gui/editor_toaster.cpp b/editor/gui/editor_toaster.cpp index 24f19db578..ff425ba65e 100644 --- a/editor/gui/editor_toaster.cpp +++ b/editor/gui/editor_toaster.cpp @@ -108,14 +108,13 @@ void EditorToaster::_notification(int p_what) { } } break; - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (vbox_container->is_visible()) { - main_button->set_icon(get_editor_theme_icon(SNAME("Notification"))); + main_button->set_button_icon(get_editor_theme_icon(SNAME("Notification"))); } else { - main_button->set_icon(get_editor_theme_icon(SNAME("NotificationDisabled"))); + main_button->set_button_icon(get_editor_theme_icon(SNAME("NotificationDisabled"))); } - disable_notifications_button->set_icon(get_editor_theme_icon(SNAME("NotificationDisabled"))); + disable_notifications_button->set_button_icon(get_editor_theme_icon(SNAME("NotificationDisabled"))); // Styleboxes background. info_panel_style_background->set_bg_color(get_theme_color(SNAME("base_color"), EditorStringName(Editor))); @@ -134,9 +133,6 @@ void EditorToaster::_notification(int p_what) { error_panel_style_progress->set_bg_color(get_theme_color(SNAME("base_color"), EditorStringName(Editor)).lightened(0.03)); error_panel_style_progress->set_border_color(get_theme_color(SNAME("error_color"), EditorStringName(Editor))); - - main_button->queue_redraw(); - disable_notifications_button->queue_redraw(); } break; case NOTIFICATION_TRANSFORM_CHANGED: { @@ -243,6 +239,7 @@ void EditorToaster::_auto_hide_or_free_toasts() { main_button->set_tooltip_text(TTR("No notifications.")); main_button->set_modulate(Color(0.5, 0.5, 0.5)); main_button->set_disabled(true); + set_process_internal(false); } else { main_button->set_tooltip_text(TTR("Show notifications.")); main_button->set_modulate(Color(1, 1, 1)); @@ -311,9 +308,9 @@ void EditorToaster::_draw_progress(Control *panel) { void EditorToaster::_set_notifications_enabled(bool p_enabled) { vbox_container->set_visible(p_enabled); if (p_enabled) { - main_button->set_icon(get_editor_theme_icon(SNAME("Notification"))); + main_button->set_button_icon(get_editor_theme_icon(SNAME("Notification"))); } else { - main_button->set_icon(get_editor_theme_icon(SNAME("NotificationDisabled"))); + main_button->set_button_icon(get_editor_theme_icon(SNAME("NotificationDisabled"))); } _update_disable_notifications_button(); } @@ -361,6 +358,9 @@ Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_ } panel->set_modulate(Color(1, 1, 1, 0)); panel->connect(SceneStringName(draw), callable_mp(this, &EditorToaster::_draw_progress).bind(panel)); + panel->connect(SceneStringName(theme_changed), callable_mp(this, &EditorToaster::_toast_theme_changed).bind(panel)); + + Toast &toast = toasts[panel]; // Horizontal container. HBoxContainer *hbox_container = memnew(HBoxContainer); @@ -375,20 +375,20 @@ Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_ if (p_time > 0.0) { Button *close_button = memnew(Button); close_button->set_flat(true); - close_button->set_icon(get_editor_theme_icon(SNAME("Close"))); - close_button->connect(SceneStringName(pressed), callable_mp(this, &EditorToaster::close).bind(panel)); - close_button->connect(SceneStringName(theme_changed), callable_mp(this, &EditorToaster::_close_button_theme_changed).bind(close_button)); + close_button->connect(SceneStringName(pressed), callable_mp(this, &EditorToaster::instant_close).bind(panel)); hbox_container->add_child(close_button); + + toast.close_button = close_button; } - toasts[panel].severity = p_severity; + toast.severity = p_severity; if (p_time > 0.0) { - toasts[panel].duration = p_time; - toasts[panel].remaining_time = p_time; + toast.duration = p_time; + toast.remaining_time = p_time; } else { - toasts[panel].duration = -1.0; + toast.duration = -1.0; } - toasts[panel].popped = true; + toast.popped = true; vbox_container->add_child(panel); _auto_hide_or_free_toasts(); _update_vbox_position(); @@ -406,7 +406,7 @@ void EditorToaster::popup_str(const String &p_message, Severity p_severity, cons // Since "_popup_str" adds nodes to the tree, and since the "add_child" method is not // thread-safe, it's better to defer the call to the next cycle to be thread-safe. is_processing_error = true; - MessageQueue::get_main_singleton()->push_callable(callable_mp(this, &EditorToaster::_popup_str).bind(p_message, p_severity, p_tooltip)); + callable_mp(this, &EditorToaster::_popup_str).call_deferred(p_message, p_severity, p_tooltip); is_processing_error = false; } @@ -433,19 +433,22 @@ void EditorToaster::_popup_str(const String &p_message, Severity p_severity, con hb->add_child(count_label); control = popup(hb, p_severity, default_message_duration, p_tooltip); - toasts[control].message = p_message; - toasts[control].tooltip = p_tooltip; - toasts[control].count = 1; - toasts[control].message_label = label; - toasts[control].message_count_label = count_label; + + Toast &toast = toasts[control]; + toast.message = p_message; + toast.tooltip = p_tooltip; + toast.count = 1; + toast.message_label = label; + toast.message_count_label = count_label; } else { - if (toasts[control].popped) { - toasts[control].count += 1; + Toast &toast = toasts[control]; + if (toast.popped) { + toast.count += 1; } else { - toasts[control].count = 1; + toast.count = 1; } - toasts[control].remaining_time = toasts[control].duration; - toasts[control].popped = true; + toast.remaining_time = toast.duration; + toast.popped = true; control->show(); vbox_container->move_child(control, vbox_container->get_child_count()); _auto_hide_or_free_toasts(); @@ -480,6 +483,16 @@ void EditorToaster::_popup_str(const String &p_message, Severity p_severity, con vbox_container->reset_size(); is_processing_error = false; + set_process_internal(true); +} + +void EditorToaster::_toast_theme_changed(Control *p_control) { + ERR_FAIL_COND(!toasts.has(p_control)); + + Toast &toast = toasts[p_control]; + if (toast.close_button) { + toast.close_button->set_button_icon(get_editor_theme_icon(SNAME("Close"))); + } } void EditorToaster::close(Control *p_control) { @@ -488,11 +501,17 @@ void EditorToaster::close(Control *p_control) { toasts[p_control].popped = false; } -void EditorToaster::_close_button_theme_changed(Control *p_close_button) { - Button *close_button = Object::cast_to<Button>(p_close_button); - if (close_button) { - close_button->set_icon(get_editor_theme_icon(SNAME("Close"))); - } +void EditorToaster::instant_close(Control *p_control) { + close(p_control); + p_control->set_modulate(Color(1, 1, 1, 0)); +} + +void EditorToaster::_bind_methods() { + ClassDB::bind_method(D_METHOD("push_toast", "message", "severity", "tooltip"), &EditorToaster::_popup_str, DEFVAL(EditorToaster::SEVERITY_INFO), DEFVAL(String())); + + BIND_ENUM_CONSTANT(SEVERITY_INFO); + BIND_ENUM_CONSTANT(SEVERITY_WARNING); + BIND_ENUM_CONSTANT(SEVERITY_ERROR); } EditorToaster *EditorToaster::get_singleton() { @@ -501,7 +520,6 @@ EditorToaster *EditorToaster::get_singleton() { EditorToaster::EditorToaster() { set_notify_transform(true); - set_process_internal(true); // VBox. vbox_container = memnew(VBoxContainer); @@ -566,7 +584,7 @@ EditorToaster::EditorToaster() { eh.errfunc = _error_handler; add_error_handler(&eh); -}; +} EditorToaster::~EditorToaster() { singleton = nullptr; diff --git a/editor/gui/editor_toaster.h b/editor/gui/editor_toaster.h index 4bf32d94ba..0d0080945e 100644 --- a/editor/gui/editor_toaster.h +++ b/editor/gui/editor_toaster.h @@ -31,8 +31,6 @@ #ifndef EDITOR_TOASTER_H #define EDITOR_TOASTER_H -#include "core/string/ustring.h" -#include "core/templates/local_vector.h" #include "scene/gui/box_container.h" class Button; @@ -76,6 +74,9 @@ private: real_t remaining_time = 0.0; bool popped = false; + // Buttons + Button *close_button = nullptr; + // Messages String message; String tooltip; @@ -101,9 +102,10 @@ private: void _set_notifications_enabled(bool p_enabled); void _repop_old(); void _popup_str(const String &p_message, Severity p_severity, const String &p_tooltip); - void _close_button_theme_changed(Control *p_close_button); + void _toast_theme_changed(Control *p_control); protected: + static void _bind_methods(); static EditorToaster *singleton; void _notification(int p_what); @@ -114,6 +116,7 @@ public: Control *popup(Control *p_control, Severity p_severity = SEVERITY_INFO, double p_time = 0.0, const String &p_tooltip = String()); void popup_str(const String &p_message, Severity p_severity = SEVERITY_INFO, const String &p_tooltip = String()); void close(Control *p_control); + void instant_close(Control *p_control); EditorToaster(); ~EditorToaster(); diff --git a/editor/gui/editor_zoom_widget.cpp b/editor/gui/editor_zoom_widget.cpp index 50a4f020ab..ff232a854f 100644 --- a/editor/gui/editor_zoom_widget.cpp +++ b/editor/gui/editor_zoom_widget.cpp @@ -162,8 +162,8 @@ void EditorZoomWidget::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - zoom_minus->set_icon(get_editor_theme_icon(SNAME("ZoomLess"))); - zoom_plus->set_icon(get_editor_theme_icon(SNAME("ZoomMore"))); + zoom_minus->set_button_icon(get_editor_theme_icon(SNAME("ZoomLess"))); + zoom_plus->set_button_icon(get_editor_theme_icon(SNAME("ZoomMore"))); } break; } } diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index 2e36b66025..e89912d5bc 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -369,16 +369,14 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { msg_temp += String::utf8("• ") + String(E.name) + "\n"; } } - } - if (num_connections >= 1 || num_groups >= 1) { - if (num_groups < 1) { - msg_temp += "\n"; - } - msg_temp += TTR("Click to show signals dock."); + } else { + msg_temp += "\n"; } Ref<Texture2D> icon_temp; SceneTreeEditorButton signal_temp = BUTTON_SIGNALS; + String msg_temp_end = TTR("Click to show signals dock."); + if (num_connections >= 1 && num_groups >= 1) { icon_temp = get_editor_theme_icon(SNAME("SignalsAndGroups")); } else if (num_connections >= 1) { @@ -386,9 +384,11 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { } else if (num_groups >= 1) { icon_temp = get_editor_theme_icon(SNAME("Groups")); signal_temp = BUTTON_GROUPS; + msg_temp_end = TTR("Click to show groups dock."); } if (num_connections >= 1 || num_groups >= 1) { + msg_temp += msg_temp_end; item->add_button(0, icon_temp, signal_temp, false, msg_temp); } } @@ -1098,8 +1098,19 @@ void SceneTreeEditor::rename_node(Node *p_node, const String &p_name, TreeItem * // Trim leading/trailing whitespace to prevent node names from containing accidental whitespace, which would make it more difficult to get the node via `get_node()`. new_name = new_name.strip_edges(); + if (new_name.is_empty() && p_node->get_owner() != nullptr && !p_node->get_scene_file_path().is_empty()) { + // If name is empty and node is root of an instance, revert to the original name. + const Ref<PackedScene> node_scene = ResourceLoader::load(p_node->get_scene_file_path()); + if (node_scene.is_valid()) { + const Ref<SceneState> &state = node_scene->get_state(); + if (state->get_node_count() > 0) { + new_name = state->get_node_name(0); // Root's name. + } + } + } + if (new_name.is_empty()) { - // If name is empty, fallback to class name. + // If name is still empty, fallback to class name. if (GLOBAL_GET("editor/naming/node_name_casing").operator int() != NAME_CASING_PASCAL_CASE) { new_name = Node::adjust_name_casing(p_node->get_class()); } else { @@ -1684,24 +1695,30 @@ void SceneTreeDialog::_show_all_nodes_changed(bool p_button_pressed) { } void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) { - if (p_valid.is_empty()) { - return; + if (allowed_types_hbox) { + allowed_types_hbox->queue_free(); + allowed_types_hbox = nullptr; + valid_type_icons.clear(); } tree->set_valid_types(p_valid); - HBoxContainer *hbox = memnew(HBoxContainer); - content->add_child(hbox); - content->move_child(hbox, 0); + if (p_valid.is_empty()) { + return; + } + + allowed_types_hbox = memnew(HBoxContainer); + content->add_child(allowed_types_hbox); + content->move_child(allowed_types_hbox, 0); { Label *label = memnew(Label); - hbox->add_child(label); + allowed_types_hbox->add_child(label); label->set_text(TTR("Allowed:")); } HFlowContainer *hflow = memnew(HFlowContainer); - hbox->add_child(hflow); + allowed_types_hbox->add_child(hflow); hflow->set_h_size_flags(Control::SIZE_EXPAND_FILL); for (const StringName &type : p_valid) { @@ -1735,6 +1752,9 @@ void SceneTreeDialog::set_valid_types(const Vector<StringName> &p_valid) { } show_all_nodes->show(); + if (is_inside_tree()) { + _update_valid_type_icons(); + } } void SceneTreeDialog::_notification(int p_what) { @@ -1753,11 +1773,7 @@ void SceneTreeDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - filter->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - for (TextureRect *trect : valid_type_icons) { - trect->set_custom_minimum_size(Vector2(get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)), 0)); - trect->set_texture(trect->get_meta("icon")); - } + _update_valid_type_icons(); } break; case NOTIFICATION_EXIT_TREE: { @@ -1766,6 +1782,14 @@ void SceneTreeDialog::_notification(int p_what) { } } +void SceneTreeDialog::_update_valid_type_icons() { + filter->set_right_icon(get_editor_theme_icon(SNAME("Search"))); + for (TextureRect *trect : valid_type_icons) { + trect->set_custom_minimum_size(Vector2(get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)), 0)); + trect->set_texture(trect->get_meta("icon")); + } +} + void SceneTreeDialog::_cancel() { hide(); } diff --git a/editor/gui/scene_tree_editor.h b/editor/gui/scene_tree_editor.h index e623c8405d..eed6d4b954 100644 --- a/editor/gui/scene_tree_editor.h +++ b/editor/gui/scene_tree_editor.h @@ -199,6 +199,7 @@ class SceneTreeDialog : public ConfirmationDialog { LineEdit *filter = nullptr; CheckButton *show_all_nodes = nullptr; LocalVector<TextureRect *> valid_type_icons; + HBoxContainer *allowed_types_hbox = nullptr; void _select(); void _cancel(); @@ -208,6 +209,7 @@ class SceneTreeDialog : public ConfirmationDialog { void _show_all_nodes_changed(bool p_button_pressed); protected: + void _update_valid_type_icons(); void _notification(int p_what); static void _bind_methods(); diff --git a/editor/icons/2DNodes.svg b/editor/icons/2DNodes.svg new file mode 100644 index 0000000000..90b92a4bc7 --- /dev/null +++ b/editor/icons/2DNodes.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="none" stroke="#8da5f3" stroke-width="2" d="M 8,13 C 5.2385763,13 3,10.761424 3,8 3,5.2385763 5.2385763,3 8,3"/><path fill="none" stroke="#8eef97" stroke-width="2" d="m 8,13 c 2.761424,0 5,-2.238576 5,-5 C 13,5.2385763 10.761424,3 8,3"/></svg>
\ No newline at end of file diff --git a/editor/icons/Camera.svg b/editor/icons/Camera.svg new file mode 100644 index 0000000000..8612d458a7 --- /dev/null +++ b/editor/icons/Camera.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16"><path fill="#e0e0e0" d="M9 2a3 3 0 0 0-3 2.777 3 3 0 1 0-3 5.047V12a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1v-1l3 2V7l-3 2V7.23A3 3 0 0 0 9 2z"/></svg>
\ No newline at end of file diff --git a/editor/icons/FPS.svg b/editor/icons/FPS.svg new file mode 100644 index 0000000000..5ee818c308 --- /dev/null +++ b/editor/icons/FPS.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M7.25 4h-2v8h2v-2c1.656 0 3-1.344 3-3 0-1.657-1.344-3-3-3zm0 4v-2c.553 0 1 .448 1 1s-.447 1-1 1zM.25 7v5h2v-2h2v-2h-2v-1c0-.553.447-1 1-1h1v-2h-1c-1.656 0-3 1.344-3 3zM13.25 7c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h2v-2h-2c-1.381 0-2.5 1.119-2.5 2.5s1.119 2.5 2.5 2.5c.276 0 .5.224.5.5s-.224.5-.5.5h-2v2h2c1.381 0 2.5-1.119 2.5-2.5s-1.119-2.5-2.5-2.5z"/></svg>
\ No newline at end of file diff --git a/editor/icons/FlipWinding.svg b/editor/icons/FlipWinding.svg new file mode 100644 index 0000000000..8964ca8d5d --- /dev/null +++ b/editor/icons/FlipWinding.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><rect width="4.596" height="4.596" x="5.7" y="5.7" fill="#e0e0e0" fill-opacity=".6" rx="1" ry="1" transform="rotate(45 8 8)"/><path fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 2a6 6 0 00-2.5 11m.5-3L6 14H2M9 14a6 6 0 002.5-11M11 6 10 2h4"/></svg>
\ No newline at end of file diff --git a/editor/icons/Game.svg b/editor/icons/Game.svg new file mode 100644 index 0000000000..e75e5c5312 --- /dev/null +++ b/editor/icons/Game.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M 1,15 V 12 C 1,11.5 1.5,11 2,11 H 3 V 10 C 3,9.5 3.5,9 4,9 h 1 c 0.5,0 1,0.5 1,1 v 1 H 8 V 5 h 2 v 6 h 4 c 0.5,0 1,0.5 1,1 v 3 z"/><circle cx="9" cy="4" r="3" fill="#e0e0e0"/></svg>
\ No newline at end of file diff --git a/editor/icons/LookAtModifier3D.svg b/editor/icons/LookAtModifier3D.svg new file mode 100644 index 0000000000..9315b297ef --- /dev/null +++ b/editor/icons/LookAtModifier3D.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#fc7f7f"><path d="m5.742 11.508c.916-2.959 3.507-4.508 5.592-4.508.803 0 1.673.223 2.492.658.297-.182.563-.423.768-.731.754-1.134.446-2.665-.688-3.419-.309-.205-.66-.338-1.026-.389-.188-1.349-1.433-2.291-2.782-2.103s-2.29 1.433-2.103 2.782c.051.367.184.717.389 1.026l-3.56 3.56c-1.134-.754-2.665-.446-3.419.688s-.446 2.664.688 3.419c.308.205.659.338 1.026.389.188 1.349 1.433 2.29 2.782 2.103.342-.048.658-.164.936-.333-.467-.612-.856-1.337-1.102-2.206-.085-.3-.085-.617.007-.936z"/><path d="m11.334 8c-1.704 0-3.861 1.299-4.637 3.804-.034.119-.034.246 0 .366.745 2.638 2.97 3.83 4.637 3.83s3.891-1.192 4.641-3.816c.034-.12.034-.247 0-.367-.734-2.526-2.938-3.817-4.641-3.817zm0 6.667c-1.473 0-2.667-1.194-2.667-2.667s1.194-2.666 2.667-2.666 2.667 1.193 2.667 2.666-1.194 2.667-2.667 2.667z"/><circle cx="11.334" cy="12" r="1.333"/></g></svg>
\ No newline at end of file diff --git a/editor/icons/MaterialPreviewQuad.svg b/editor/icons/MaterialPreviewQuad.svg new file mode 100644 index 0000000000..9765a15df7 --- /dev/null +++ b/editor/icons/MaterialPreviewQuad.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="none" stroke="#000" stroke-linejoin="round" stroke-opacity=".8" stroke-width="2" d="m2 1 12 1v11l-12 1z"/><path fill="#f9f9f9" d="m2 14 12-1v-11l-12-1z"/></svg>
\ No newline at end of file diff --git a/editor/icons/NextFrame.svg b/editor/icons/NextFrame.svg new file mode 100644 index 0000000000..9609b2538b --- /dev/null +++ b/editor/icons/NextFrame.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m 12,3 c -0.552285,0 -1,0.4477153 -1,1 v 8 c 0,0.552285 0.447715,1 1,1 h 1 c 0.552285,0 1,-0.447715 1,-1 V 4 C 14,3.4477153 13.552285,3 13,3 Z M 2.975,3.002 C 2.4332786,3.0155465 2.0009144,3.45811 2,4 v 8 c -3.148e-4,0.838862 0.9701632,1.305289 1.625,0.781 l 5,-4 c 0.4989606,-0.4003069 0.4989606,-1.1596931 0,-1.56 l -5,-4 C 3.4409271,3.0736532 3.2107095,2.9960875 2.975,3.002 Z"/></svg>
\ No newline at end of file diff --git a/editor/icons/Unfavorite.svg b/editor/icons/Unfavorite.svg new file mode 100644 index 0000000000..78f1b90fd0 --- /dev/null +++ b/editor/icons/Unfavorite.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M 8 1.6992188 L 5.6269531 5.796875 L 1 6.8945312 L 4.2363281 10.302734 L 3.8769531 14.976562 L 8.0175781 12.998047 L 12.173828 14.941406 L 11.777344 10.287109 L 15 6.8945312 L 10.373047 5.796875 L 8 1.6992188 z M 8 4.2773438 L 9.4882812 6.8457031 L 12.388672 7.5332031 L 10.369141 9.6601562 L 10.617188 12.576172 L 8.0097656 11.359375 L 5.4160156 12.599609 L 5.640625 9.6699219 L 3.6113281 7.5332031 L 6.5117188 6.8457031 L 8 4.2773438 z"/></svg>
\ No newline at end of file diff --git a/editor/import/3d/collada.h b/editor/import/3d/collada.h index 416b917a46..f9c26e090c 100644 --- a/editor/import/3d/collada.h +++ b/editor/import/3d/collada.h @@ -359,7 +359,7 @@ public: for (int i = 0; i < children.size(); i++) { memdelete(children[i]); } - }; + } }; struct NodeSkeleton : public Node { diff --git a/editor/import/3d/editor_import_collada.cpp b/editor/import/3d/editor_import_collada.cpp index 04a3f23154..c04278fc55 100644 --- a/editor/import/3d/editor_import_collada.cpp +++ b/editor/import/3d/editor_import_collada.cpp @@ -1263,7 +1263,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres //bleh, must ignore invalid ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid), ERR_INVALID_DATA); - mesh = Ref<ImporterMesh>(memnew(ImporterMesh)); + mesh.instantiate(); const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid]; String name = meshdata.name; if (name.is_empty()) { diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp index a579224ecd..59d39152e9 100644 --- a/editor/import/3d/resource_importer_obj.cpp +++ b/editor/import/3d/resource_importer_obj.cpp @@ -202,12 +202,12 @@ static Error _parse_material_library(const String &p_path, HashMap<String, Ref<S return OK; } -static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, List<String> *r_missing_deps) { +static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, bool p_single_mesh, bool p_generate_tangents, bool p_generate_lods, bool p_generate_shadow_mesh, bool p_generate_lightmap_uv2, float p_generate_lightmap_uv2_texel_size, const PackedByteArray &p_src_lightmap_cache, Vector3 p_scale_mesh, Vector3 p_offset_mesh, bool p_disable_compression, Vector<Vector<uint8_t>> &r_lightmap_caches, List<String> *r_missing_deps) { Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, vformat("Couldn't open OBJ file '%s', it may not exist or not be readable.", p_path)); - // Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree) - // This should only match, if it's indeed a COFF file header + // Avoid trying to load/interpret potential build artifacts from Visual Studio (e.g. when compiling native plugins inside the project tree). + // This should only match if it's indeed a COFF file header. // https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types const int first_bytes = f->get_16(); static const Vector<int> coff_header_machines{ @@ -445,6 +445,7 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, } mesh->add_surface(Mesh::PRIMITIVE_TRIANGLES, array, TypedArray<Array>(), Dictionary(), material, name, mesh_flags); + print_verbose("OBJ: Added surface :" + mesh->get_surface_name(mesh->get_surface_count() - 1)); if (!current_material.is_empty()) { @@ -508,6 +509,43 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, } } + if (p_generate_lightmap_uv2) { + Vector<uint8_t> lightmap_cache; + mesh->lightmap_unwrap_cached(Transform3D(), p_generate_lightmap_uv2_texel_size, p_src_lightmap_cache, lightmap_cache); + + if (!lightmap_cache.is_empty()) { + if (r_lightmap_caches.is_empty()) { + r_lightmap_caches.push_back(lightmap_cache); + } else { + // MD5 is stored at the beginning of the cache data. + const String new_md5 = String::md5(lightmap_cache.ptr()); + + for (int i = 0; i < r_lightmap_caches.size(); i++) { + const String md5 = String::md5(r_lightmap_caches[i].ptr()); + if (new_md5 < md5) { + r_lightmap_caches.insert(i, lightmap_cache); + break; + } + + if (new_md5 == md5) { + break; + } + } + } + } + } + + if (p_generate_lods) { + // Use normal merge/split angles that match the defaults used for 3D scene importing. + mesh->generate_lods(60.0f, {}); + } + + if (p_generate_shadow_mesh) { + mesh->create_shadow_mesh(); + } + + mesh->optimize_indices(); + if (p_single_mesh && mesh->get_surface_count() > 0) { r_meshes.push_back(mesh); } @@ -518,7 +556,10 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes, Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) { List<Ref<ImporterMesh>> meshes; - Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, r_missing_deps); + // LOD, shadow mesh and lightmap UV2 generation are handled by ResourceImporterScene in this case, + // so disable it within the OBJ mesh import. + Vector<Vector<uint8_t>> mesh_lightmap_caches; + Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, false, false, 0.2, PackedByteArray(), Vector3(1, 1, 1), Vector3(0, 0, 0), p_flags & IMPORT_FORCE_DISABLE_MESH_COMPRESSION, mesh_lightmap_caches, r_missing_deps); if (err != OK) { if (r_err) { @@ -587,19 +628,51 @@ String ResourceImporterOBJ::get_preset_name(int p_idx) const { void ResourceImporterOBJ::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_tangents"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_lods"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_shadow_mesh"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_lightmap_uv2", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "generate_lightmap_uv2_texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2)); r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "scale_mesh"), Vector3(1, 1, 1))); r_options->push_back(ImportOption(PropertyInfo(Variant::VECTOR3, "offset_mesh"), Vector3(0, 0, 0))); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_disable_mesh_compression"), false)); } bool ResourceImporterOBJ::get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const { + if (p_option == "generate_lightmap_uv2_texel_size" && !p_options["generate_lightmap_uv2"]) { + // Only display the lightmap texel size import option when lightmap UV2 generation is enabled. + return false; + } + return true; } -Error ResourceImporterOBJ::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterOBJ::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { List<Ref<ImporterMesh>> meshes; - Error err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], nullptr); + Vector<uint8_t> src_lightmap_cache; + Vector<Vector<uint8_t>> mesh_lightmap_caches; + + Error err; + { + src_lightmap_cache = FileAccess::get_file_as_bytes(p_source_file + ".unwrap_cache", &err); + if (err != OK) { + src_lightmap_cache.clear(); + } + } + + err = _parse_obj(p_source_file, meshes, true, p_options["generate_tangents"], p_options["generate_lods"], p_options["generate_shadow_mesh"], p_options["generate_lightmap_uv2"], p_options["generate_lightmap_uv2_texel_size"], src_lightmap_cache, p_options["scale_mesh"], p_options["offset_mesh"], p_options["force_disable_mesh_compression"], mesh_lightmap_caches, nullptr); + + if (mesh_lightmap_caches.size()) { + Ref<FileAccess> f = FileAccess::open(p_source_file + ".unwrap_cache", FileAccess::WRITE); + if (f.is_valid()) { + f->store_32(mesh_lightmap_caches.size()); + for (int i = 0; i < mesh_lightmap_caches.size(); i++) { + String md5 = String::md5(mesh_lightmap_caches[i].ptr()); + f->store_buffer(mesh_lightmap_caches[i].ptr(), mesh_lightmap_caches[i].size()); + } + } + } + err = OK; ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(meshes.size() != 1, ERR_BUG); diff --git a/editor/import/3d/resource_importer_obj.h b/editor/import/3d/resource_importer_obj.h index faf0f336c0..c4a99428ef 100644 --- a/editor/import/3d/resource_importer_obj.h +++ b/editor/import/3d/resource_importer_obj.h @@ -61,10 +61,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; - - // Threaded import can currently cause deadlocks, see GH-48265. - virtual bool can_import_threaded() const override { return false; } + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; ResourceImporterOBJ(); }; diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index cb348f713c..86af9caf26 100644 --- a/editor/import/3d/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -2043,9 +2043,7 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/shadow_meshes", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lightmap_uv", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "generate/lods", PROPERTY_HINT_ENUM, "Default,Enable,Disable"), 0)); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_split_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 25.0f)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "lods/normal_merge_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), 60.0f)); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "lods/raycast_normals", PROPERTY_HINT_NONE, ""), false)); } break; case INTERNAL_IMPORT_CATEGORY_MATERIAL: { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "use_external/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); @@ -2474,9 +2472,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ //do mesh processing bool generate_lods = p_generate_lods; - float split_angle = 25.0f; float merge_angle = 60.0f; - bool raycast_normals = false; bool create_shadow_meshes = p_create_shadow_meshes; bool bake_lightmaps = p_light_bake_mode == LIGHT_BAKE_STATIC_LIGHTMAPS; String save_to_file; @@ -2523,18 +2519,10 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ } } - if (mesh_settings.has("lods/normal_split_angle")) { - split_angle = mesh_settings["lods/normal_split_angle"]; - } - if (mesh_settings.has("lods/normal_merge_angle")) { merge_angle = mesh_settings["lods/normal_merge_angle"]; } - if (mesh_settings.has("lods/raycast_normals")) { - raycast_normals = mesh_settings["lods/raycast_normals"]; - } - if (bool(mesh_settings.get("save_to_file/enabled", false))) { save_to_file = mesh_settings.get("save_to_file/path", String()); if (!save_to_file.is_resource_file()) { @@ -2579,17 +2567,17 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ } } - src_mesh_node->get_mesh()->optimize_indices_for_cache(); - if (generate_lods) { Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node); - src_mesh_node->get_mesh()->generate_lods(merge_angle, split_angle, skin_pose_transform_array, raycast_normals); + src_mesh_node->get_mesh()->generate_lods(merge_angle, skin_pose_transform_array); } if (create_shadow_meshes) { src_mesh_node->get_mesh()->create_shadow_mesh(); } + src_mesh_node->get_mesh()->optimize_indices(); + if (!save_to_file.is_empty()) { Ref<Mesh> existing = ResourceCache::get_ref(save_to_file); if (existing.is_valid()) { @@ -2634,6 +2622,7 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_ mesh_node->set_layer_mask(src_mesh_node->get_layer_mask()); mesh_node->set_cast_shadows_setting(src_mesh_node->get_cast_shadows_setting()); + mesh_node->set_visible(src_mesh_node->is_visible()); mesh_node->set_visibility_range_begin(src_mesh_node->get_visibility_range_begin()); mesh_node->set_visibility_range_begin_margin(src_mesh_node->get_visibility_range_begin_margin()); mesh_node->set_visibility_range_end(src_mesh_node->get_visibility_range_end()); @@ -2883,7 +2872,7 @@ Error ResourceImporterScene::_check_resource_save_paths(const Dictionary &p_data return OK; } -Error ResourceImporterScene::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterScene::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { const String &src_path = p_source_file; Ref<EditorSceneFormatImporter> importer; @@ -3103,7 +3092,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p if (!scr.is_valid()) { EditorNode::add_io_error(TTR("Couldn't load post-import script:") + " " + post_import_script_path); } else { - post_import_script = Ref<EditorScenePostImport>(memnew(EditorScenePostImport)); + post_import_script.instantiate(); post_import_script->set_script(scr); if (!post_import_script->get_script_instance()) { EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):") + " " + post_import_script_path); diff --git a/editor/import/3d/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h index fe757dc2a3..b2f5fab0eb 100644 --- a/editor/import/3d/resource_importer_scene.h +++ b/editor/import/3d/resource_importer_scene.h @@ -299,13 +299,11 @@ public: void _compress_animations(AnimationPlayer *anim, int p_page_size_kb); Node *pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options); - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool has_advanced_options() const override; virtual void show_advanced_options(const String &p_path) override; - virtual bool can_import_threaded() const override { return false; } - ResourceImporterScene(const String &p_scene_import_type = "PackedScene", bool p_singleton = false); ~ResourceImporterScene(); diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index 011d0135b4..945c1811d7 100644 --- a/editor/import/3d/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp @@ -368,6 +368,7 @@ void SceneImportSettingsDialog::_fill_scene(Node *p_node, TreeItem *p_parent_ite mesh_node->set_transform(src_mesh_node->get_transform()); mesh_node->set_skin(src_mesh_node->get_skin()); mesh_node->set_skeleton_path(src_mesh_node->get_skeleton_path()); + mesh_node->set_visible(src_mesh_node->is_visible()); if (src_mesh_node->get_mesh().is_valid()) { Ref<ImporterMesh> editor_mesh = src_mesh_node->get_mesh(); mesh_node->set_mesh(editor_mesh->get_mesh()); @@ -1019,11 +1020,11 @@ void SceneImportSettingsDialog::_play_animation() { if (animation_player->has_animation(id)) { if (animation_player->is_playing()) { animation_player->pause(); - animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); set_process(false); } else { animation_player->play(id); - animation_play_button->set_icon(get_editor_theme_icon(SNAME("Pause"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause"))); set_process(true); } } @@ -1032,7 +1033,7 @@ void SceneImportSettingsDialog::_play_animation() { void SceneImportSettingsDialog::_stop_current_animation() { animation_pingpong = false; animation_player->stop(); - animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); animation_slider->set_value_no_signal(0.0); set_process(false); } @@ -1044,7 +1045,7 @@ void SceneImportSettingsDialog::_reset_animation(const String &p_animation_name) if (animation_player != nullptr && animation_player->is_playing()) { animation_player->stop(); } - animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); _reset_bone_transforms(); set_process(false); @@ -1066,7 +1067,7 @@ void SceneImportSettingsDialog::_reset_animation(const String &p_animation_name) animation_player->play(p_animation_name); } else { animation_player->stop(true); - animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); animation_player->set_assigned_animation(p_animation_name); animation_player->seek(0.0, true); animation_slider->set_value_no_signal(0.0); @@ -1081,7 +1082,7 @@ void SceneImportSettingsDialog::_animation_slider_value_changed(double p_value) } if (animation_player->is_playing()) { animation_player->stop(); - animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); set_process(false); } animation_player->seek(p_value * animation_map[selected_id].animation->get_length(), true); @@ -1097,7 +1098,7 @@ void SceneImportSettingsDialog::_animation_finished(const StringName &p_name) { switch (loop_mode) { case Animation::LOOP_NONE: { - animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); animation_slider->set_value_no_signal(1.0); set_process(false); } break; @@ -1318,17 +1319,17 @@ void SceneImportSettingsDialog::_notification(int p_what) { action_menu->end_bulk_theme_override(); if (animation_player != nullptr && animation_player->is_playing()) { - animation_play_button->set_icon(get_editor_theme_icon(SNAME("Pause"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause"))); } else { - animation_play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + animation_play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); } - animation_stop_button->set_icon(get_editor_theme_icon(SNAME("Stop"))); + animation_stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop"))); - light_1_switch->set_icon(theme_cache.light_1_icon); - light_2_switch->set_icon(theme_cache.light_2_icon); - light_rotate_switch->set_icon(theme_cache.rotate_icon); + light_1_switch->set_button_icon(theme_cache.light_1_icon); + light_2_switch->set_button_icon(theme_cache.light_2_icon); + light_rotate_switch->set_button_icon(theme_cache.rotate_icon); - animation_toggle_skeleton_visibility->set_icon(get_editor_theme_icon(SNAME("Skeleton3D"))); + animation_toggle_skeleton_visibility->set_button_icon(get_editor_theme_icon(SNAME("Skeleton3D"))); } break; case NOTIFICATION_PROCESS: { diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp index 9a0c62193c..dd45806385 100644 --- a/editor/import/audio_stream_import_settings.cpp +++ b/editor/import/audio_stream_import_settings.cpp @@ -45,8 +45,8 @@ void AudioStreamImportSettingsDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - _play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); - _stop_button->set_icon(get_editor_theme_icon(SNAME("Stop"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); + _stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop"))); _preview->set_color(get_theme_color(SNAME("dark_color_2"), EditorStringName(Editor))); color_rect->set_color(get_theme_color(SNAME("dark_color_1"), EditorStringName(Editor))); @@ -61,9 +61,9 @@ void AudioStreamImportSettingsDialog::_notification(int p_what) { _duration_label->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size(SNAME("status_source_size"), EditorStringName(EditorFonts))); _duration_label->end_bulk_theme_override(); - zoom_in->set_icon(get_editor_theme_icon(SNAME("ZoomMore"))); - zoom_out->set_icon(get_editor_theme_icon(SNAME("ZoomLess"))); - zoom_reset->set_icon(get_editor_theme_icon(SNAME("ZoomReset"))); + zoom_in->set_button_icon(get_editor_theme_icon(SNAME("ZoomMore"))); + zoom_out->set_button_icon(get_editor_theme_icon(SNAME("ZoomLess"))); + zoom_reset->set_button_icon(get_editor_theme_icon(SNAME("ZoomReset"))); _indicator->queue_redraw(); _preview->queue_redraw(); @@ -233,25 +233,25 @@ void AudioStreamImportSettingsDialog::_play() { // '_pausing' variable indicates that we want to pause the audio player, not stop it. See '_on_finished()'. _pausing = true; _player->stop(); - _play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); set_process(false); } else { _player->play(_current); - _play_button->set_icon(get_editor_theme_icon(SNAME("Pause"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause"))); set_process(true); } } void AudioStreamImportSettingsDialog::_stop() { _player->stop(); - _play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); _current = 0; _indicator->queue_redraw(); set_process(false); } void AudioStreamImportSettingsDialog::_on_finished() { - _play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); if (!_pausing) { _current = 0; _indicator->queue_redraw(); @@ -580,12 +580,10 @@ AudioStreamImportSettingsDialog::AudioStreamImportSettingsDialog() { bar_beats_edit->set_max(32); bar_beats_edit->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1)); interactive_hb->add_child(bar_beats_edit); - interactive_hb->add_spacer(); main_vbox->add_margin_child(TTR("Music Playback:"), interactive_hb); color_rect = memnew(ColorRect); - main_vbox->add_margin_child(TTR("Preview:"), color_rect); - + main_vbox->add_margin_child(TTR("Preview:"), color_rect, true); color_rect->set_custom_minimum_size(Size2(600, 200) * EDSCALE); color_rect->set_v_size_flags(Control::SIZE_EXPAND_FILL); diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp index e124697b20..8bbad91b68 100644 --- a/editor/import/dynamic_font_import_settings.cpp +++ b/editor/import/dynamic_font_import_settings.cpp @@ -940,7 +940,7 @@ void DynamicFontImportSettingsDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - add_var->set_icon(get_editor_theme_icon(SNAME("Add"))); + add_var->set_button_icon(get_editor_theme_icon(SNAME("Add"))); label_warn->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); } break; } diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp index 3243dcf256..650c0e27ca 100644 --- a/editor/import/editor_import_plugin.cpp +++ b/editor/import/editor_import_plugin.cpp @@ -163,7 +163,7 @@ bool EditorImportPlugin::get_option_visibility(const String &p_path, const Strin ERR_FAIL_V_MSG(false, "Unimplemented _get_option_visibility in add-on."); } -Error EditorImportPlugin::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error EditorImportPlugin::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { Dictionary options; TypedArray<String> platform_variants, gen_files; diff --git a/editor/import/editor_import_plugin.h b/editor/import/editor_import_plugin.h index ea5cfc2682..df472b416b 100644 --- a/editor/import/editor_import_plugin.h +++ b/editor/import/editor_import_plugin.h @@ -69,7 +69,7 @@ public: virtual int get_import_order() const override; virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata = nullptr) override; virtual bool can_import_threaded() const override; Error append_import_external_resource(const String &p_file, const HashMap<StringName, Variant> &p_custom_options = HashMap<StringName, Variant>(), const String &p_custom_importer = String(), Variant p_generator_parameters = Variant()); }; diff --git a/editor/import/resource_importer_bitmask.cpp b/editor/import/resource_importer_bitmask.cpp index e7b7850b02..8441a49666 100644 --- a/editor/import/resource_importer_bitmask.cpp +++ b/editor/import/resource_importer_bitmask.cpp @@ -72,7 +72,7 @@ void ResourceImporterBitMap::get_import_options(const String &p_path, List<Impor r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.5)); } -Error ResourceImporterBitMap::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterBitMap::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { int create_from = p_options["create_from"]; float threshold = p_options["threshold"]; Ref<Image> image; diff --git a/editor/import/resource_importer_bitmask.h b/editor/import/resource_importer_bitmask.h index 8963c8d918..fcb152b47d 100644 --- a/editor/import/resource_importer_bitmask.h +++ b/editor/import/resource_importer_bitmask.h @@ -48,7 +48,9 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } ResourceImporterBitMap(); ~ResourceImporterBitMap(); diff --git a/editor/import/resource_importer_bmfont.cpp b/editor/import/resource_importer_bmfont.cpp index 085ca1362d..b7efdbb6d6 100644 --- a/editor/import/resource_importer_bmfont.cpp +++ b/editor/import/resource_importer_bmfont.cpp @@ -67,7 +67,7 @@ void ResourceImporterBMFont::get_import_options(const String &p_path, List<Impor r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED)); } -Error ResourceImporterBMFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterBMFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { print_verbose("Importing BMFont font from: " + p_source_file); Array fallbacks = p_options["fallbacks"]; diff --git a/editor/import/resource_importer_bmfont.h b/editor/import/resource_importer_bmfont.h index d31cd03736..74fef9ff16 100644 --- a/editor/import/resource_importer_bmfont.h +++ b/editor/import/resource_importer_bmfont.h @@ -48,7 +48,9 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } ResourceImporterBMFont(); }; diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp index c181011402..17f6070d35 100644 --- a/editor/import/resource_importer_csv_translation.cpp +++ b/editor/import/resource_importer_csv_translation.cpp @@ -72,7 +72,7 @@ void ResourceImporterCSVTranslation::get_import_options(const String &p_path, Li r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "delimiter", PROPERTY_HINT_ENUM, "Comma,Semicolon,Tab"), 0)); } -Error ResourceImporterCSVTranslation::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterCSVTranslation::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { bool compress = p_options["compress"]; String delimiter; @@ -147,6 +147,9 @@ Error ResourceImporterCSVTranslation::import(const String &p_source_file, const if (r_gen_files) { r_gen_files->push_back(save_path); } + + ResourceUID::ID save_id = hash64_murmur3_64(translations[i]->get_locale().hash64(), p_source_id); + ResourceSaver::set_uid(save_path, save_id); } return OK; diff --git a/editor/import/resource_importer_csv_translation.h b/editor/import/resource_importer_csv_translation.h index c6b05eb043..63676c61a6 100644 --- a/editor/import/resource_importer_csv_translation.h +++ b/editor/import/resource_importer_csv_translation.h @@ -49,7 +49,9 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } ResourceImporterCSVTranslation(); }; diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp index fa222b2790..a4a5e445e3 100644 --- a/editor/import/resource_importer_dynamic_font.cpp +++ b/editor/import/resource_importer_dynamic_font.cpp @@ -141,7 +141,7 @@ void ResourceImporterDynamicFont::show_advanced_options(const String &p_path) { DynamicFontImportSettingsDialog::get_singleton()->open_settings(p_path); } -Error ResourceImporterDynamicFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterDynamicFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { print_verbose("Importing dynamic font from: " + p_source_file); int antialiasing = p_options["antialiasing"]; diff --git a/editor/import/resource_importer_dynamic_font.h b/editor/import/resource_importer_dynamic_font.h index de89e6b76f..73ef96d583 100644 --- a/editor/import/resource_importer_dynamic_font.h +++ b/editor/import/resource_importer_dynamic_font.h @@ -58,7 +58,9 @@ public: bool has_advanced_options() const override; void show_advanced_options(const String &p_path) override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } ResourceImporterDynamicFont(); }; diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp index 4f6dd4e4ef..5a4f64d245 100644 --- a/editor/import/resource_importer_image.cpp +++ b/editor/import/resource_importer_image.cpp @@ -70,7 +70,7 @@ String ResourceImporterImage::get_preset_name(int p_idx) const { void ResourceImporterImage::get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset) const { } -Error ResourceImporterImage::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterImage::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { Ref<FileAccess> f = FileAccess::open(p_source_file, FileAccess::READ); ERR_FAIL_COND_V_MSG(f.is_null(), ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'."); diff --git a/editor/import/resource_importer_image.h b/editor/import/resource_importer_image.h index 1490ab30d5..da1925bc5c 100644 --- a/editor/import/resource_importer_image.h +++ b/editor/import/resource_importer_image.h @@ -50,7 +50,9 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } ResourceImporterImage(); }; diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp index f01381904d..950058e88e 100644 --- a/editor/import/resource_importer_imagefont.cpp +++ b/editor/import/resource_importer_imagefont.cpp @@ -75,7 +75,7 @@ void ResourceImporterImageFont::get_import_options(const String &p_path, List<Im r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "scaling_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled (Integer),Enabled (Fractional)"), TextServer::FIXED_SIZE_SCALE_ENABLED)); } -Error ResourceImporterImageFont::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterImageFont::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { print_verbose("Importing image font from: " + p_source_file); int columns = p_options["columns"]; @@ -199,7 +199,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin case STEP_OFF_Y_BEGIN: { // Read advance and offset. if (range[c] == ' ') { - int next = range.find(" ", c + 1); + int next = range.find_char(' ', c + 1); if (next < c) { next = range.length(); } diff --git a/editor/import/resource_importer_imagefont.h b/editor/import/resource_importer_imagefont.h index 065351c361..79e9455d6d 100644 --- a/editor/import/resource_importer_imagefont.h +++ b/editor/import/resource_importer_imagefont.h @@ -48,7 +48,9 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } ResourceImporterImageFont(); }; diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index 312195fcd7..0d0c89425d 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -289,7 +289,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons } } -Error ResourceImporterLayeredTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterLayeredTexture::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { int compress_mode = p_options["compress/mode"]; float lossy = p_options["compress/lossy_quality"]; bool high_quality = p_options["compress/high_quality"]; diff --git a/editor/import/resource_importer_layered_texture.h b/editor/import/resource_importer_layered_texture.h index 26495eed8d..271f1f4543 100644 --- a/editor/import/resource_importer_layered_texture.h +++ b/editor/import/resource_importer_layered_texture.h @@ -112,11 +112,13 @@ public: void _save_tex(Vector<Ref<Image>> p_images, const String &p_to_path, int p_compress_mode, float p_lossy, Image::CompressMode p_vram_compression, Image::CompressSource p_csource, Image::UsedChannels used_channels, bool p_mipmaps, bool p_force_po2); - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const override; virtual String get_import_settings_string() const override; + virtual bool can_import_threaded() const override { return true; } + void set_mode(Mode p_mode) { mode = p_mode; } ResourceImporterLayeredTexture(bool p_singleton = false); diff --git a/editor/import/resource_importer_shader_file.cpp b/editor/import/resource_importer_shader_file.cpp index b7508e7644..639ce48f75 100644 --- a/editor/import/resource_importer_shader_file.cpp +++ b/editor/import/resource_importer_shader_file.cpp @@ -89,7 +89,7 @@ static String _include_function(const String &p_path, void *userpointer) { return file_inc->get_as_utf8_string(); } -Error ResourceImporterShaderFile::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterShaderFile::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { /* STEP 1, Read shader code */ ERR_FAIL_COND_V_EDMSG((OS::get_singleton()->get_current_rendering_method() == "gl_compatibility"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when using the gl_compatibility rendering_method. Please switch to the forward_plus or mobile rendering methods to use custom shaders."); ERR_FAIL_COND_V_EDMSG((DisplayServer::get_singleton()->get_name() == "headless"), ERR_UNAVAILABLE, "Cannot import custom .glsl shaders when running in headless mode."); diff --git a/editor/import/resource_importer_shader_file.h b/editor/import/resource_importer_shader_file.h index aefc967989..440a3d86b4 100644 --- a/editor/import/resource_importer_shader_file.h +++ b/editor/import/resource_importer_shader_file.h @@ -49,7 +49,9 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } ResourceImporterShaderFile(); }; diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 24a14c60ad..d72c15bc2a 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -241,7 +241,10 @@ void ResourceImporterTexture::get_import_options(const String &p_path, List<Impo r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/normal_map_invert_y"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_as_srgb"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/hdr_clamp_exposure"), false)); - r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0)); + + // Maximum bound is the highest allowed value for lossy compression (the lowest common denominator). + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "process/size_limit", PROPERTY_HINT_RANGE, "0,16383,1"), 0)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "detect_3d/compress_to", PROPERTY_HINT_ENUM, "Disabled,VRAM Compressed,Basis Universal"), (p_preset == PRESET_DETECT) ? 1 : 0)); // Do path based customization only if a path was passed. @@ -428,7 +431,7 @@ Dictionary ResourceImporterTexture::_load_editor_meta(const String &p_path) cons return f->get_var(); } -Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterTexture::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { // Parse import options. int32_t loader_flags = ImageFormatLoader::FLAG_NONE; @@ -454,7 +457,28 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String const bool normal_map_invert_y = p_options["process/normal_map_invert_y"]; // Support for texture streaming is not implemented yet. const bool stream = false; - const int size_limit = p_options["process/size_limit"]; + + int size_limit = p_options["process/size_limit"]; + bool using_fallback_size_limit = false; + if (size_limit == 0) { + using_fallback_size_limit = true; + // If no size limit is defined, use a fallback size limit to prevent textures from looking incorrect or failing to import. + switch (compress_mode) { + case COMPRESS_LOSSY: + // Maximum WebP size on either axis. + size_limit = 16383; + break; + case COMPRESS_BASIS_UNIVERSAL: + // Maximum Basis Universal size on either axis. + size_limit = 16384; + break; + default: + // As of June 2024, no GPU can correctly display a texture larger than 32768 pixels on either axis. + size_limit = 32768; + break; + } + } + const bool hdr_as_srgb = p_options["process/hdr_as_srgb"]; if (hdr_as_srgb) { loader_flags |= ImageFormatLoader::FLAG_FORCE_LINEAR; @@ -523,11 +547,19 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String int new_width = size_limit; int new_height = target_image->get_height() * new_width / target_image->get_width(); + if (using_fallback_size_limit) { + // Only warn if downsizing occurred when the user did not explicitly request it. + WARN_PRINT(vformat("%s: Texture was downsized on import as its width (%d pixels) exceeded the importable size limit (%d pixels).", p_source_file, target_image->get_width(), size_limit)); + } target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); } else { int new_height = size_limit; int new_width = target_image->get_width() * new_height / target_image->get_height(); + if (using_fallback_size_limit) { + // Only warn if downsizing occurred when the user did not explicitly request it. + WARN_PRINT(vformat("%s: Texture was downsized on import as its height (%d pixels) exceeded the importable size limit (%d pixels).", p_source_file, target_image->get_height(), size_limit)); + } target_image->resize(new_width, new_height, Image::INTERPOLATE_CUBIC); } @@ -558,7 +590,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { const Color color = target_image->get_pixel(i, j); - target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b)); + target_image->set_pixel(i, j, Color(color.r, 1 - color.g, color.b, color.a)); } } } diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index 6d74c4e2f9..8aa044f3c8 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -100,7 +100,9 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } void update_imports(); diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index d6ce39f6a6..7e645cc0d0 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -91,7 +91,7 @@ String ResourceImporterTextureAtlas::get_option_group_file() const { return "atlas_file"; } -Error ResourceImporterTextureAtlas::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterTextureAtlas::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { /* If this happens, it's because the atlas_file field was not filled, so just import a broken texture */ //use an xpm because it's size independent, the editor images are vector and size dependent diff --git a/editor/import/resource_importer_texture_atlas.h b/editor/import/resource_importer_texture_atlas.h index 0f2b10424c..943f221679 100644 --- a/editor/import/resource_importer_texture_atlas.h +++ b/editor/import/resource_importer_texture_atlas.h @@ -64,9 +64,11 @@ public: virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override; virtual String get_option_group_file() const override; - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) override; + virtual bool can_import_threaded() const override { return true; } + ResourceImporterTextureAtlas(); }; diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index 7a6f39906c..f500ec4a07 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -94,7 +94,7 @@ void ResourceImporterWAV::get_import_options(const String &p_path, List<ImportOp r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "PCM (Uncompressed),IMA ADPCM,Quite OK Audio"), 2)); } -Error ResourceImporterWAV::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { +Error ResourceImporterWAV::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { /* STEP 1, READ WAVE FILE */ Error err; @@ -112,7 +112,15 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s } /* GET FILESIZE */ - file->get_32(); // filesize + + // The file size in header is 8 bytes less than the actual size. + // See https://docs.fileformat.com/audio/wav/ + const int FILE_SIZE_HEADER_OFFSET = 8; + uint32_t file_size_header = file->get_32() + FILE_SIZE_HEADER_OFFSET; + uint64_t file_size = file->get_length(); + if (file_size != file_size_header) { + WARN_PRINT(vformat("File size %d is %s than the expected size %d. (%s)", file_size, file_size > file_size_header ? "larger" : "smaller", file_size_header, p_source_file)); + } /* CHECK WAVE */ @@ -198,11 +206,14 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s break; } + uint64_t remaining_bytes = file_size - file_pos; frames = chunksize; - - if (format_channels == 0) { - ERR_FAIL_COND_V(format_channels == 0, ERR_INVALID_DATA); + if (remaining_bytes < chunksize) { + WARN_PRINT(vformat("Data chunk size is smaller than expected. Proceeding with actual data size. (%s)", p_source_file)); + frames = remaining_bytes; } + + ERR_FAIL_COND_V(format_channels == 0, ERR_INVALID_DATA); frames /= format_channels; frames /= (format_bits >> 3); diff --git a/editor/import/resource_importer_wav.h b/editor/import/resource_importer_wav.h index 47af37ba41..361541c6c1 100644 --- a/editor/import/resource_importer_wav.h +++ b/editor/import/resource_importer_wav.h @@ -140,7 +140,9 @@ public: } } - virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override; + + virtual bool can_import_threaded() const override { return true; } ResourceImporterWAV(); }; diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 14065abf73..6c22a965ae 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -790,23 +790,14 @@ ImportDock::ImportDock() { import->set_text(TTR("Reimport")); import->set_disabled(true); import->connect(SceneStringName(pressed), callable_mp(this, &ImportDock::_reimport_pressed)); - if (!DisplayServer::get_singleton()->get_swap_cancel_ok()) { - advanced_spacer = hb->add_spacer(); - advanced = memnew(Button); - advanced->set_text(TTR("Advanced...")); - hb->add_child(advanced); - } + advanced_spacer = hb->add_spacer(); + advanced = memnew(Button); + advanced->set_text(TTR("Advanced...")); + hb->add_child(advanced); hb->add_spacer(); hb->add_child(import); hb->add_spacer(); - if (DisplayServer::get_singleton()->get_swap_cancel_ok()) { - advanced = memnew(Button); - advanced->set_text(TTR("Advanced...")); - hb->add_child(advanced); - advanced_spacer = hb->add_spacer(); - } - advanced->hide(); advanced_spacer->hide(); advanced->connect(SceneStringName(pressed), callable_mp(this, &ImportDock::_advanced_options)); diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index d13a022d52..46876644fb 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -427,35 +427,36 @@ void InspectorDock::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { - resource_new_button->set_icon(get_editor_theme_icon(SNAME("New"))); - resource_load_button->set_icon(get_editor_theme_icon(SNAME("Load"))); - resource_save_button->set_icon(get_editor_theme_icon(SNAME("Save"))); - resource_extra_button->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); - open_docs_button->set_icon(get_editor_theme_icon(SNAME("HelpSearch"))); + resource_new_button->set_button_icon(get_editor_theme_icon(SNAME("New"))); + resource_load_button->set_button_icon(get_editor_theme_icon(SNAME("Load"))); + resource_save_button->set_button_icon(get_editor_theme_icon(SNAME("Save"))); + resource_extra_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + open_docs_button->set_button_icon(get_editor_theme_icon(SNAME("HelpSearch"))); PopupMenu *resource_extra_popup = resource_extra_button->get_popup(); resource_extra_popup->set_item_icon(resource_extra_popup->get_item_index(RESOURCE_EDIT_CLIPBOARD), get_editor_theme_icon(SNAME("ActionPaste"))); resource_extra_popup->set_item_icon(resource_extra_popup->get_item_index(RESOURCE_COPY), get_editor_theme_icon(SNAME("ActionCopy"))); + resource_extra_popup->set_item_icon(resource_extra_popup->get_item_index(RESOURCE_SHOW_IN_FILESYSTEM), get_editor_theme_icon(SNAME("ShowInFileSystem"))); if (is_layout_rtl()) { - backward_button->set_icon(get_editor_theme_icon(SNAME("Forward"))); - forward_button->set_icon(get_editor_theme_icon(SNAME("Back"))); + backward_button->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); + forward_button->set_button_icon(get_editor_theme_icon(SNAME("Back"))); } else { - backward_button->set_icon(get_editor_theme_icon(SNAME("Back"))); - forward_button->set_icon(get_editor_theme_icon(SNAME("Forward"))); + backward_button->set_button_icon(get_editor_theme_icon(SNAME("Back"))); + forward_button->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); } const int icon_width = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)); history_menu->get_popup()->add_theme_constant_override("icon_max_width", icon_width); - history_menu->set_icon(get_editor_theme_icon(SNAME("History"))); - object_menu->set_icon(get_editor_theme_icon(SNAME("Tools"))); + history_menu->set_button_icon(get_editor_theme_icon(SNAME("History"))); + object_menu->set_button_icon(get_editor_theme_icon(SNAME("Tools"))); search->set_right_icon(get_editor_theme_icon(SNAME("Search"))); if (info_is_warning) { - info->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + info->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); info->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); } else { - info->set_icon(get_editor_theme_icon(SNAME("NodeInfo"))); + info->set_button_icon(get_editor_theme_icon(SNAME("NodeInfo"))); info->add_theme_color_override(SceneStringName(font_color), get_theme_color(SceneStringName(font_color), EditorStringName(Editor))); } } break; @@ -482,10 +483,10 @@ void InspectorDock::set_info(const String &p_button_text, const String &p_messag info_is_warning = p_is_warning; if (info_is_warning) { - info->set_icon(get_editor_theme_icon(SNAME("NodeWarning"))); + info->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning"))); info->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); } else { - info->set_icon(get_editor_theme_icon(SNAME("NodeInfo"))); + info->set_button_icon(get_editor_theme_icon(SNAME("NodeInfo"))); info->add_theme_color_override(SceneStringName(font_color), get_theme_color(SceneStringName(font_color), EditorStringName(Editor))); } diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index 60ce8100aa..924f7abdd2 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -113,7 +113,7 @@ class InspectorDock : public VBoxContainer { void _new_resource(); void _load_resource(const String &p_type = ""); - void _open_resource_selector() { _load_resource(); }; // just used to call from arg-less signal + void _open_resource_selector() { _load_resource(); } // just used to call from arg-less signal void _resource_file_selected(const String &p_file); void _save_resource(bool save_as); void _unref_resource(); diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index 921467ccbc..7b2e6e81ee 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -441,7 +441,7 @@ void LocalizationEditor::_filesystem_files_moved(const String &p_old_file, const bool remapped_files_updated = false; for (int j = 0; j < remapped_files.size(); j++) { - int splitter_pos = remapped_files[j].rfind(":"); + int splitter_pos = remapped_files[j].rfind_char(':'); String res_path = remapped_files[j].substr(0, splitter_pos); if (res_path == p_old_file) { @@ -482,7 +482,7 @@ void LocalizationEditor::_filesystem_file_removed(const String &p_file) { for (int i = 0; i < remap_keys.size() && !remaps_changed; i++) { PackedStringArray remapped_files = remaps[remap_keys[i]]; for (int j = 0; j < remapped_files.size() && !remaps_changed; j++) { - int splitter_pos = remapped_files[j].rfind(":"); + int splitter_pos = remapped_files[j].rfind_char(':'); String res_path = remapped_files[j].substr(0, splitter_pos); remaps_changed = p_file == res_path; if (remaps_changed) { @@ -567,7 +567,7 @@ void LocalizationEditor::update_translations() { PackedStringArray selected = remaps[keys[i]]; for (int j = 0; j < selected.size(); j++) { const String &s2 = selected[j]; - int qp = s2.rfind(":"); + int qp = s2.rfind_char(':'); String path = s2.substr(0, qp); String locale = s2.substr(qp + 1, s2.length()); diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp index 0da8d8291f..985a319c84 100644 --- a/editor/node_dock.cpp +++ b/editor/node_dock.cpp @@ -52,8 +52,8 @@ void NodeDock::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - connections_button->set_icon(get_editor_theme_icon(SNAME("Signals"))); - groups_button->set_icon(get_editor_theme_icon(SNAME("Groups"))); + connections_button->set_button_icon(get_editor_theme_icon(SNAME("Signals"))); + groups_button->set_button_icon(get_editor_theme_icon(SNAME("Groups"))); } break; } } diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 804f9c607e..990b8df49d 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -158,9 +158,9 @@ void AbstractPolygon2DEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - button_create->set_icon(get_editor_theme_icon(SNAME("CurveCreate"))); - button_edit->set_icon(get_editor_theme_icon(SNAME("CurveEdit"))); - button_delete->set_icon(get_editor_theme_icon(SNAME("CurveDelete"))); + button_create->set_button_icon(get_editor_theme_icon(SNAME("CurveCreate"))); + button_edit->set_button_icon(get_editor_theme_icon(SNAME("CurveEdit"))); + button_delete->set_button_icon(get_editor_theme_icon(SNAME("CurveDelete"))); } break; case NOTIFICATION_READY: { diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index cbf8b27b32..3f534eebc5 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -576,12 +576,12 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { error_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); - tool_blend->set_icon(get_editor_theme_icon(SNAME("EditPivot"))); - tool_select->set_icon(get_editor_theme_icon(SNAME("ToolSelect"))); - tool_create->set_icon(get_editor_theme_icon(SNAME("EditKey"))); - tool_erase->set_icon(get_editor_theme_icon(SNAME("Remove"))); - snap->set_icon(get_editor_theme_icon(SNAME("SnapGrid"))); - open_editor->set_icon(get_editor_theme_icon(SNAME("Edit"))); + tool_blend->set_button_icon(get_editor_theme_icon(SNAME("EditPivot"))); + tool_select->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect"))); + tool_create->set_button_icon(get_editor_theme_icon(SNAME("EditKey"))); + tool_erase->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); + snap->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); + open_editor->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); interpolation->clear(); interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), TTR("Continuous"), 0); interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), TTR("Discrete"), 1); diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 934f26415a..ba2dad5880 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -798,14 +798,14 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { error_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); - tool_blend->set_icon(get_editor_theme_icon(SNAME("EditPivot"))); - tool_select->set_icon(get_editor_theme_icon(SNAME("ToolSelect"))); - tool_create->set_icon(get_editor_theme_icon(SNAME("EditKey"))); - tool_triangle->set_icon(get_editor_theme_icon(SNAME("ToolTriangle"))); - tool_erase->set_icon(get_editor_theme_icon(SNAME("Remove"))); - snap->set_icon(get_editor_theme_icon(SNAME("SnapGrid"))); - open_editor->set_icon(get_editor_theme_icon(SNAME("Edit"))); - auto_triangles->set_icon(get_editor_theme_icon(SNAME("AutoTriangle"))); + tool_blend->set_button_icon(get_editor_theme_icon(SNAME("EditPivot"))); + tool_select->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect"))); + tool_create->set_button_icon(get_editor_theme_icon(SNAME("EditKey"))); + tool_triangle->set_button_icon(get_editor_theme_icon(SNAME("ToolTriangle"))); + tool_erase->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); + snap->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); + open_editor->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); + auto_triangles->set_button_icon(get_editor_theme_icon(SNAME("AutoTriangle"))); interpolation->clear(); interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackContinuous")), TTR("Continuous"), 0); interpolation->add_icon_item(get_editor_theme_icon(SNAME("TrackDiscrete")), TTR("Discrete"), 1); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 9e282cb3fa..096e92e235 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -45,6 +45,7 @@ #include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_player.h" #include "scene/gui/check_box.h" +#include "scene/gui/grid_container.h" #include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/panel.h" @@ -178,7 +179,7 @@ void AnimationNodeBlendTreeEditor::update_graph() { Button *delete_button = memnew(Button); delete_button->set_flat(true); delete_button->set_focus_mode(FOCUS_NONE); - delete_button->set_icon(get_editor_theme_icon(SNAME("Close"))); + delete_button->set_button_icon(get_editor_theme_icon(SNAME("Close"))); delete_button->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_delete_node_request).bind(E), CONNECT_DEFERRED); node->get_titlebar_hbox()->add_child(delete_button); } @@ -216,7 +217,7 @@ void AnimationNodeBlendTreeEditor::update_graph() { node->add_child(memnew(HSeparator)); Button *open_in_editor = memnew(Button); open_in_editor->set_text(TTR("Open Editor")); - open_in_editor->set_icon(get_editor_theme_icon(SNAME("Edit"))); + open_in_editor->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); node->add_child(open_in_editor); open_in_editor->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_open_in_editor).bind(E), CONNECT_DEFERRED); open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER); @@ -230,7 +231,7 @@ void AnimationNodeBlendTreeEditor::update_graph() { } else { inspect_filters->set_text(TTR("Edit Filters")); } - inspect_filters->set_icon(get_editor_theme_icon(SNAME("AnimationFilter"))); + inspect_filters->set_button_icon(get_editor_theme_icon(SNAME("AnimationFilter"))); node->add_child(inspect_filters); inspect_filters->connect(SceneStringName(pressed), callable_mp(this, &AnimationNodeBlendTreeEditor::_inspect_filters).bind(E), CONNECT_DEFERRED); inspect_filters->set_h_size_flags(SIZE_SHRINK_CENTER); @@ -240,7 +241,7 @@ void AnimationNodeBlendTreeEditor::update_graph() { if (anim.is_valid()) { MenuButton *mb = memnew(MenuButton); mb->set_text(anim->get_animation()); - mb->set_icon(get_editor_theme_icon(SNAME("Animation"))); + mb->set_button_icon(get_editor_theme_icon(SNAME("Animation"))); mb->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); mb->set_disabled(read_only); Array options; @@ -1265,7 +1266,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE); open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendTreeEditor::_file_opened)); - animation_node_inspector_plugin = Ref<EditorInspectorPluginAnimationNodeAnimation>(memnew(EditorInspectorPluginAnimationNodeAnimation)); + animation_node_inspector_plugin.instantiate(); EditorInspector::add_inspector_plugin(animation_node_inspector_plugin); } @@ -1375,7 +1376,7 @@ void AnimationNodeAnimationEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { button->set_theme_type_variation(SNAME("InspectorActionButton")); - button->set_icon(get_editor_theme_icon(SNAME("Edit"))); + button->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); } break; } } @@ -1397,32 +1398,30 @@ bool EditorInspectorPluginAnimationNodeAnimation::parse_property(Object *p_objec } AnimationNodeAnimationEditorDialog::AnimationNodeAnimationEditorDialog() { - set_title(TTR("Select Markers...")); - VBoxContainer *vbox = memnew(VBoxContainer); - add_child(vbox); - vbox->set_offsets_preset(Control::PRESET_FULL_RECT); - - HBoxContainer *container_start = memnew(HBoxContainer); - vbox->add_child(container_start); - Label *label_start = memnew(Label); - container_start->add_child(label_start); + set_title(TTR("Select Markers")); + + GridContainer *grid = memnew(GridContainer); + grid->set_columns(2); + grid->set_offsets_preset(Control::PRESET_FULL_RECT); + add_child(grid); + + Label *label_start = memnew(Label(TTR("Start Marker"))); + grid->add_child(label_start); label_start->set_h_size_flags(Control::SIZE_EXPAND_FILL); label_start->set_stretch_ratio(1); - label_start->set_text(TTR("Start Marker")); select_start = memnew(OptionButton); - container_start->add_child(select_start); + select_start->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + grid->add_child(select_start); select_start->set_h_size_flags(Control::SIZE_EXPAND_FILL); select_start->set_stretch_ratio(2); - HBoxContainer *container_end = memnew(HBoxContainer); - vbox->add_child(container_end); - Label *label_end = memnew(Label); - container_end->add_child(label_end); + Label *label_end = memnew(Label(TTR("End Marker"))); + grid->add_child(label_end); label_end->set_h_size_flags(Control::SIZE_EXPAND_FILL); label_end->set_stretch_ratio(1); - label_end->set_text(TTR("End Marker")); select_end = memnew(OptionButton); - container_end->add_child(select_end); + select_end->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); + grid->add_child(select_end); select_end->set_h_size_flags(Control::SIZE_EXPAND_FILL); select_end->set_stretch_ratio(2); } diff --git a/editor/plugins/animation_library_editor.cpp b/editor/plugins/animation_library_editor.cpp index 147b92c094..46410b581e 100644 --- a/editor/plugins/animation_library_editor.cpp +++ b/editor/plugins/animation_library_editor.cpp @@ -30,7 +30,12 @@ #include "animation_library_editor.h" +#include "core/string/print_string.h" +#include "core/string/ustring.h" +#include "core/templates/vector.h" +#include "core/variant/variant.h" #include "editor/editor_node.h" +#include "editor/editor_paths.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" @@ -518,6 +523,8 @@ void AnimationLibraryEditor::_item_renamed() { if (restore_text) { ti->set_text(0, old_text); } + + _save_mixer_lib_folding(ti); } void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button) { @@ -670,6 +677,8 @@ void AnimationLibraryEditor::update_tree() { TreeItem *root = tree->create_item(); List<StringName> libs; + Vector<uint64_t> collapsed_lib_ids = _load_mixer_libs_folding(); + mixer->get_animation_library_list(&libs); for (const StringName &K : libs) { @@ -759,12 +768,203 @@ void AnimationLibraryEditor::update_tree() { anitem->set_text(1, anim_path.get_file()); } } + anitem->add_button(1, get_editor_theme_icon("Save"), ANIM_BUTTON_FILE, animation_library_is_foreign, TTR("Save animation to resource on disk.")); anitem->add_button(1, get_editor_theme_icon("Remove"), ANIM_BUTTON_DELETE, animation_library_is_foreign, TTR("Remove animation from Library.")); + + for (const uint64_t &lib_id : collapsed_lib_ids) { + Object *lib_obj = ObjectDB::get_instance(ObjectID(lib_id)); + AnimationLibrary *cur_lib = Object::cast_to<AnimationLibrary>(lib_obj); + StringName M = mixer->get_animation_library_name(cur_lib); + + if (M == K) { + libitem->set_collapsed_recursive(true); + } + } } } } +void AnimationLibraryEditor::_save_mixer_lib_folding(TreeItem *p_item) { + //Check if ti is a library or animation + if (p_item->get_parent()->get_parent() != nullptr) { + return; + } + + Ref<ConfigFile> config; + config.instantiate(); + + String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("lib_folding.cfg"); + Error err = config->load(path); + if (err != OK && err != ERR_FILE_NOT_FOUND) { + ERR_PRINT("Error loading lib_folding.cfg: " + itos(err)); + } + + // Get unique identifier for this scene+mixer combination + String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text(); + + PackedStringArray collapsed_lib_names; + PackedStringArray collapsed_lib_ids; + + if (config->has_section(md)) { + collapsed_lib_names = String(config->get_value(md, "folding")).split("\n"); + collapsed_lib_ids = String(config->get_value(md, "id")).split("\n"); + } + + String lib_name = p_item->get_text(0); + + // Get library reference and check validity + Ref<AnimationLibrary> al; + uint64_t lib_id = 0; + + if (mixer->has_animation_library(lib_name)) { + al = mixer->get_animation_library(lib_name); + ERR_FAIL_COND(al.is_null()); + lib_id = uint64_t(al->get_instance_id()); + } else { + ERR_PRINT("Library not found: " + lib_name); + } + + int at = collapsed_lib_names.find(lib_name); + if (p_item->is_collapsed()) { + if (at != -1) { + //Entry exists and needs updating + collapsed_lib_ids.set(at, String::num_int64(lib_id + INT64_MIN)); + } else { + //Check if it's a rename + int id_at = collapsed_lib_ids.find(String::num_int64(lib_id + INT64_MIN)); + if (id_at != -1) { + //It's actually a rename + collapsed_lib_names.set(id_at, lib_name); + } else { + //It's a new entry + collapsed_lib_names.append(lib_name); + collapsed_lib_ids.append(String::num_int64(lib_id + INT64_MIN)); + } + } + } else { + if (at != -1) { + collapsed_lib_names.remove_at(at); + collapsed_lib_ids.remove_at(at); + } + } + + //Runtime IDs + config->set_value(md, "root", uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id())); + config->set_value(md, "mixer", uint64_t(mixer->get_instance_id())); + + //Plan B recovery mechanism + config->set_value(md, "mixer_signature", _get_mixer_signature()); + + //Save folding state as text and runtime ID + config->set_value(md, "folding", String("\n").join(collapsed_lib_names)); + config->set_value(md, "id", String("\n").join(collapsed_lib_ids)); + + err = config->save(path); + if (err != OK) { + ERR_PRINT("Error saving lib_folding.cfg: " + itos(err)); + } +} + +Vector<uint64_t> AnimationLibraryEditor::_load_mixer_libs_folding() { + Ref<ConfigFile> config; + config.instantiate(); + + String path = EditorPaths::get_singleton()->get_project_settings_dir().path_join("lib_folding.cfg"); + Error err = config->load(path); + if (err != OK && err != ERR_FILE_NOT_FOUND) { + ERR_PRINT("Error loading lib_folding.cfg: " + itos(err)); + return Vector<uint64_t>(); + } + + // Get unique identifier for this scene+mixer combination + String md = (mixer->get_tree()->get_edited_scene_root()->get_scene_file_path() + mixer->get_path()).md5_text(); + + Vector<uint64_t> collapsed_lib_ids; + + if (config->has_section(md)) { + _load_config_libs_folding(collapsed_lib_ids, config.ptr(), md); + + } else { + //The scene/mixer combination is no longer valid and we'll try to recover + uint64_t current_mixer_id = uint64_t(mixer->get_instance_id()); + String current_mixer_signature = _get_mixer_signature(); + List<String> sections; + config->get_sections(§ions); + + for (const String §ion : sections) { + Variant mixer_id = config->get_value(section, "mixer"); + if ((mixer_id.get_type() == Variant::INT && uint64_t(mixer_id) == current_mixer_id) || config->get_value(section, "mixer_signature") == current_mixer_signature) { // Ensure value exists and is correct type + // Found the mixer in a different section! + _load_config_libs_folding(collapsed_lib_ids, config.ptr(), section); + + //Cleanup old entry and copy fold data into new one! + String collapsed_lib_names_str = String(config->get_value(section, "folding")); + String collapsed_lib_ids_str = String(config->get_value(section, "id")); + config->erase_section(section); + + config->set_value(md, "root", uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id())); + config->set_value(md, "mixer", uint64_t(mixer->get_instance_id())); + config->set_value(md, "mixer_signature", _get_mixer_signature()); + config->set_value(md, "folding", collapsed_lib_names_str); + config->set_value(md, "id", collapsed_lib_ids_str); + + err = config->save(path); + if (err != OK) { + ERR_PRINT("Error saving lib_folding.cfg: " + itos(err)); + } + break; + } + } + } + + return collapsed_lib_ids; +} + +void AnimationLibraryEditor::_load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section) { + if (uint64_t(p_config->get_value(p_section, "root", 0)) != uint64_t(mixer->get_tree()->get_edited_scene_root()->get_instance_id())) { + // Root changed - tries to match by library names + PackedStringArray collapsed_lib_names = String(p_config->get_value(p_section, "folding", "")).split("\n"); + for (const String &lib_name : collapsed_lib_names) { + if (mixer->has_animation_library(lib_name)) { + p_lib_ids.append(mixer->get_animation_library(lib_name)->get_instance_id()); + } else { + print_line("Can't find ", lib_name, " in mixer"); + } + } + } else { + // Root same - uses saved instance IDs + for (const String &saved_id : String(p_config->get_value(p_section, "id")).split("\n")) { + p_lib_ids.append(uint64_t(saved_id.to_int() - INT64_MIN)); + } + } +} + +String AnimationLibraryEditor::_get_mixer_signature() const { + String signature = String(); + + // Get all libraries sorted for consistency + List<StringName> libs; + mixer->get_animation_library_list(&libs); + libs.sort_custom<StringName::AlphCompare>(); + + // Add libraries and their animations to signature + for (const StringName &lib_name : libs) { + signature += "::" + String(lib_name); + Ref<AnimationLibrary> lib = mixer->get_animation_library(lib_name); + if (lib.is_valid()) { + List<StringName> anims; + lib->get_animation_list(&anims); + anims.sort_custom<StringName::AlphCompare>(); + for (const StringName &anim_name : anims) { + signature += "," + String(anim_name); + } + } + } + + return signature.md5_text(); +} + void AnimationLibraryEditor::show_dialog() { update_tree(); popup_centered_ratio(0.5); @@ -773,8 +973,8 @@ void AnimationLibraryEditor::show_dialog() { void AnimationLibraryEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - new_library_button->set_icon(get_editor_theme_icon(SNAME("Add"))); - load_library_button->set_icon(get_editor_theme_icon(SNAME("Load"))); + new_library_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + load_library_button->set_button_icon(get_editor_theme_icon(SNAME("Load"))); } } } @@ -855,11 +1055,12 @@ AnimationLibraryEditor::AnimationLibraryEditor() { tree->set_column_custom_minimum_width(1, EDSCALE * 250); tree->set_column_expand(1, false); tree->set_hide_root(true); - tree->set_hide_folding(true); + tree->set_hide_folding(false); tree->set_v_size_flags(Control::SIZE_EXPAND_FILL); tree->connect("item_edited", callable_mp(this, &AnimationLibraryEditor::_item_renamed)); tree->connect("button_clicked", callable_mp(this, &AnimationLibraryEditor::_button_pressed)); + tree->connect("item_collapsed", callable_mp(this, &AnimationLibraryEditor::_save_mixer_lib_folding)); file_popup = memnew(PopupMenu); add_child(file_popup); diff --git a/editor/plugins/animation_library_editor.h b/editor/plugins/animation_library_editor.h index beb34c6343..c4ad1684a6 100644 --- a/editor/plugins/animation_library_editor.h +++ b/editor/plugins/animation_library_editor.h @@ -31,6 +31,8 @@ #ifndef ANIMATION_LIBRARY_EDITOR_H #define ANIMATION_LIBRARY_EDITOR_H +#include "core/io/config_file.h" +#include "core/templates/vector.h" #include "editor/animation_track_editor.h" #include "editor/plugins/editor_plugin.h" #include "scene/animation/animation_mixer.h" @@ -103,6 +105,11 @@ class AnimationLibraryEditor : public AcceptDialog { void _load_file(const String &p_path); void _load_files(const PackedStringArray &p_paths); + void _save_mixer_lib_folding(TreeItem *p_item); + Vector<uint64_t> _load_mixer_libs_folding(); + void _load_config_libs_folding(Vector<uint64_t> &p_lib_ids, ConfigFile *p_config, String p_section); + String _get_mixer_signature() const; + void _item_renamed(); void _button_pressed(TreeItem *p_item, int p_column, int p_id, MouseButton p_button); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 1581e7cc66..4edd021b4d 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -113,7 +113,7 @@ void AnimationPlayerEditor::_notification(int p_what) { // Need the last frame after it stopped. frame->set_value(player->get_current_animation_position()); track_editor->set_anim_pos(player->get_current_animation_position()); - stop->set_icon(stop_icon); + stop->set_button_icon(stop_icon); } last_active = player->is_playing(); @@ -145,16 +145,16 @@ void AnimationPlayerEditor::_notification(int p_what) { stop_icon = get_editor_theme_icon(SNAME("Stop")); pause_icon = get_editor_theme_icon(SNAME("Pause")); if (player && player->is_playing()) { - stop->set_icon(pause_icon); + stop->set_button_icon(pause_icon); } else { - stop->set_icon(stop_icon); + stop->set_button_icon(stop_icon); } - autoplay->set_icon(get_editor_theme_icon(SNAME("AutoPlay"))); - play->set_icon(get_editor_theme_icon(SNAME("PlayStart"))); - play_from->set_icon(get_editor_theme_icon(SNAME("Play"))); - play_bw->set_icon(get_editor_theme_icon(SNAME("PlayStartBackwards"))); - play_bw_from->set_icon(get_editor_theme_icon(SNAME("PlayBackwards"))); + autoplay->set_button_icon(get_editor_theme_icon(SNAME("AutoPlay"))); + play->set_button_icon(get_editor_theme_icon(SNAME("PlayStart"))); + play_from->set_button_icon(get_editor_theme_icon(SNAME("Play"))); + play_bw->set_button_icon(get_editor_theme_icon(SNAME("PlayStartBackwards"))); + play_bw_from->set_button_icon(get_editor_theme_icon(SNAME("PlayBackwards"))); autoplay_icon = get_editor_theme_icon(SNAME("AutoPlay")); reset_icon = get_editor_theme_icon(SNAME("Reload")); @@ -168,10 +168,10 @@ void AnimationPlayerEditor::_notification(int p_what) { autoplay_reset_icon = ImageTexture::create_from_image(autoplay_reset_img); } - onion_toggle->set_icon(get_editor_theme_icon(SNAME("Onion"))); - onion_skinning->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + onion_toggle->set_button_icon(get_editor_theme_icon(SNAME("Onion"))); + onion_skinning->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); - pin->set_icon(get_editor_theme_icon(SNAME("Pin"))); + pin->set_button_icon(get_editor_theme_icon(SNAME("Pin"))); tool_anim->add_theme_style_override(CoreStringName(normal), get_theme_stylebox(CoreStringName(normal), SNAME("Button"))); track_editor->get_edit_menu()->add_theme_style_override(CoreStringName(normal), get_theme_stylebox(CoreStringName(normal), SNAME("Button"))); @@ -307,7 +307,7 @@ void AnimationPlayerEditor::_play_pressed() { } //unstop - stop->set_icon(pause_icon); + stop->set_button_icon(pause_icon); } void AnimationPlayerEditor::_play_from_pressed() { @@ -331,7 +331,7 @@ void AnimationPlayerEditor::_play_from_pressed() { } //unstop - stop->set_icon(pause_icon); + stop->set_button_icon(pause_icon); } String AnimationPlayerEditor::_get_current() const { @@ -359,7 +359,7 @@ void AnimationPlayerEditor::_play_bw_pressed() { } //unstop - stop->set_icon(pause_icon); + stop->set_button_icon(pause_icon); } void AnimationPlayerEditor::_play_bw_from_pressed() { @@ -383,7 +383,7 @@ void AnimationPlayerEditor::_play_bw_from_pressed() { } //unstop - stop->set_icon(pause_icon); + stop->set_button_icon(pause_icon); } void AnimationPlayerEditor::_stop_pressed() { @@ -400,7 +400,7 @@ void AnimationPlayerEditor::_stop_pressed() { frame->set_value(0); track_editor->set_anim_pos(0); } - stop->set_icon(stop_icon); + stop->set_button_icon(stop_icon); } void AnimationPlayerEditor::_animation_selected(int p_which) { @@ -580,8 +580,10 @@ float AnimationPlayerEditor::_get_editor_step() const { const Ref<Animation> anim = player->get_animation(current); ERR_FAIL_COND_V(anim.is_null(), 0.0); + float step = track_editor->get_snap_unit(); + // Use more precise snapping when holding Shift - return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? anim->get_step() * 0.25 : anim->get_step(); + return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? step * 0.25 : step; } void AnimationPlayerEditor::_animation_name_edited() { @@ -959,9 +961,9 @@ void AnimationPlayerEditor::_update_animation() { updating = true; if (player->is_playing()) { - stop->set_icon(pause_icon); + stop->set_button_icon(pause_icon); } else { - stop->set_icon(stop_icon); + stop->set_button_icon(stop_icon); } scale->set_text(String::num(player->get_speed_scale(), 2)); @@ -1397,7 +1399,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_timeline_o } track_editor->set_anim_pos(pos); -}; +} void AnimationPlayerEditor::_animation_player_changed(Object *p_pl) { _update_player(); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index e9dd54f73b..8526150e0c 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -917,7 +917,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } if (state_machine_draw->has_focus()) { - state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), theme_cache.highlight_color, false); + state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), theme_cache.focus_color, false); } int sep = 3 * EDSCALE; @@ -1271,18 +1271,18 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { error_panel->add_theme_style_override(SceneStringName(panel), theme_cache.error_panel_style); error_label->add_theme_color_override(SceneStringName(font_color), theme_cache.error_color); - tool_select->set_icon(theme_cache.tool_icon_select); - tool_create->set_icon(theme_cache.tool_icon_create); - tool_connect->set_icon(theme_cache.tool_icon_connect); + tool_select->set_button_icon(theme_cache.tool_icon_select); + tool_create->set_button_icon(theme_cache.tool_icon_create); + tool_connect->set_button_icon(theme_cache.tool_icon_connect); switch_mode->clear(); switch_mode->add_icon_item(theme_cache.transition_icon_immediate, TTR("Immediate")); switch_mode->add_icon_item(theme_cache.transition_icon_sync, TTR("Sync")); switch_mode->add_icon_item(theme_cache.transition_icon_end, TTR("At End")); - auto_advance->set_icon(theme_cache.play_icon_auto); + auto_advance->set_button_icon(theme_cache.play_icon_auto); - tool_erase->set_icon(theme_cache.tool_icon_erase); + tool_erase->set_button_icon(theme_cache.tool_icon_erase); play_mode->clear(); play_mode->add_icon_item(theme_cache.play_icon_travel, TTR("Travel")); @@ -1642,6 +1642,7 @@ void AnimationNodeStateMachineEditor::_bind_methods() { BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, transition_icon_disabled_color, "transition_icon_disabled_color", "GraphStateMachine"); BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, highlight_color, "highlight_color", "GraphStateMachine"); BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, highlight_disabled_color, "highlight_disabled_color", "GraphStateMachine"); + BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, focus_color, "focus_color", "GraphStateMachine"); BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_COLOR, AnimationNodeStateMachineEditor, guideline_color, "guideline_color", "GraphStateMachine"); BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_ICON, AnimationNodeStateMachineEditor, transition_icons[0], "TransitionImmediateBig", "EditorIcons"); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index eb623a147d..0b6320f0ce 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -117,6 +117,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { Color transition_icon_disabled_color; Color highlight_color; Color highlight_disabled_color; + Color focus_color; Color guideline_color; Ref<Texture2D> transition_icons[6]{}; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index fec8d4b897..7c9c003ea1 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -212,12 +212,12 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const // Overlay and thumbnail need the same format for `blend_rect` to work. thumbnail->convert(Image::FORMAT_RGBA8); thumbnail->blend_rect(overlay, overlay->get_used_rect(), overlay_pos); - preview_images[i].button->set_icon(ImageTexture::create_from_image(thumbnail)); + preview_images[i].button->set_button_icon(ImageTexture::create_from_image(thumbnail)); // Make it clearer that clicking it will open an external link preview_images[i].button->set_default_cursor_shape(Control::CURSOR_POINTING_HAND); } else { - preview_images[i].button->set_icon(p_image); + preview_images[i].button->set_button_icon(p_image); } break; } @@ -302,7 +302,7 @@ void EditorAssetLibraryItemDescription::add_preview(int p_id, bool p_video, cons new_preview.video_link = p_url; new_preview.is_video = p_video; new_preview.button = memnew(Button); - new_preview.button->set_icon(previews->get_editor_theme_icon(SNAME("ThumbnailWait"))); + new_preview.button->set_button_icon(previews->get_editor_theme_icon(SNAME("ThumbnailWait"))); new_preview.button->set_toggle_mode(true); new_preview.button->connect(SceneStringName(pressed), callable_mp(this, &EditorAssetLibraryItemDescription::_preview_click).bind(p_id)); preview_hb->add_child(new_preview.button); @@ -907,7 +907,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons for (int i = 0; i < headers.size(); i++) { if (headers[i].findn("ETag:") == 0) { // Save etag String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().path_join("assetimage_" + image_queue[p_queue_id].image_url.md5_text()); - String new_etag = headers[i].substr(headers[i].find(":") + 1, headers[i].length()).strip_edges(); + String new_etag = headers[i].substr(headers[i].find_char(':') + 1, headers[i].length()).strip_edges(); Ref<FileAccess> file = FileAccess::open(cache_filename_base + ".etag", FileAccess::WRITE); if (file.is_valid()) { file->store_line(new_etag); diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp index f691bad3c3..23eeedea93 100644 --- a/editor/plugins/audio_stream_editor_plugin.cpp +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -50,8 +50,8 @@ void AudioStreamEditor::_notification(int p_what) { _current_label->add_theme_font_override(SceneStringName(font), font); _duration_label->add_theme_font_override(SceneStringName(font), font); - _play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); - _stop_button->set_icon(get_editor_theme_icon(SNAME("Stop"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); + _stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop"))); _preview->set_color(get_theme_color(SNAME("dark_color_2"), EditorStringName(Editor))); set_color(get_theme_color(SNAME("dark_color_1"), EditorStringName(Editor))); @@ -121,26 +121,26 @@ void AudioStreamEditor::_play() { if (_player->is_playing()) { _pausing = true; _player->stop(); - _play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); set_process(false); } else { _pausing = false; _player->play(_current); - _play_button->set_icon(get_editor_theme_icon(SNAME("Pause"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("Pause"))); set_process(true); } } void AudioStreamEditor::_stop() { _player->stop(); - _play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); _current = 0; _indicator->queue_redraw(); set_process(false); } void AudioStreamEditor::_on_finished() { - _play_button->set_icon(get_editor_theme_icon(SNAME("MainPlay"))); + _play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay"))); if (!_pausing) { _current = 0; _indicator->queue_redraw(); diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index d81ec21705..8b105955e7 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -119,7 +119,7 @@ void BoneMapperItem::create_editor() { hbox->add_child(skeleton_bone_selector); picker_button = memnew(Button); - picker_button->set_icon(get_editor_theme_icon(SNAME("ClassList"))); + picker_button->set_button_icon(get_editor_theme_icon(SNAME("ClassList"))); picker_button->connect(SceneStringName(pressed), callable_mp(this, &BoneMapperItem::_open_picker)); hbox->add_child(picker_button); @@ -296,7 +296,7 @@ void BoneMapper::create_editor() { group_hbox->add_child(profile_group_selector); clear_mapping_button = memnew(Button); - clear_mapping_button->set_icon(get_editor_theme_icon(SNAME("Clear"))); + clear_mapping_button->set_button_icon(get_editor_theme_icon(SNAME("Clear"))); clear_mapping_button->set_tooltip_text(TTR("Clear mappings in current group.")); clear_mapping_button->connect(SceneStringName(pressed), callable_mp(this, &BoneMapper::_clear_mapping_current_group)); group_hbox->add_child(clear_mapping_button); @@ -859,7 +859,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) { // 4-1. Guess Finger int tips_index = -1; - bool thumb_tips_size = 0; + bool thumb_tips_size = false; bool named_finger_is_found = false; LocalVector<String> fingers; fingers.push_back("thumb|pollex"); @@ -994,7 +994,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) { } tips_index = -1; - thumb_tips_size = 0; + thumb_tips_size = false; named_finger_is_found = false; if (right_hand_or_palm != -1) { LocalVector<LocalVector<String>> right_fingers_map; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index e9a796dae7..d3bae447cc 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -1464,10 +1464,12 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); // Remove not movable nodes - for (CanvasItem *E : selection) { - if (!_is_node_movable(E, true)) { + for (List<CanvasItem *>::Element *E = selection.front(); E;) { + List<CanvasItem *>::Element *N = E->next(); + if (!_is_node_movable(E->get(), true)) { selection.erase(E); } + E = N; } drag_selection = selection; @@ -1928,37 +1930,50 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { // Drag resize handles if (drag_type == DRAG_NONE) { - if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) { + if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && + ((tool == TOOL_SELECT && b->is_alt_pressed() && b->is_command_or_control_pressed()) || tool == TOOL_SCALE)) { bool has_locked_items = false; List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); - if (selection.size() == 1) { + + // Remove non-movable nodes. + for (CanvasItem *ci : selection) { + if (!_is_node_movable(ci, true)) { + selection.erase(ci); + } + } + + if (!selection.is_empty()) { CanvasItem *ci = selection.front()->get(); - if (_is_node_movable(ci)) { - Transform2D xform = transform * ci->get_global_transform_with_canvas(); - Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); - Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + Transform2D edit_transform; + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot); + } else { + edit_transform = ci->_edit_get_transform(); + } - drag_type = DRAG_SCALE_BOTH; + Transform2D xform = transform * ci->get_global_transform_with_canvas(); + Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * edit_transform).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; - if (show_transformation_gizmos) { - Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); - Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { - drag_type = DRAG_SCALE_X; - } - Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { - drag_type = DRAG_SCALE_Y; - } - } + drag_type = DRAG_SCALE_BOTH; - drag_from = transform.affine_inverse().xform(b->get_position()); - drag_selection = List<CanvasItem *>(); - drag_selection.push_back(ci); - _save_canvas_item_state(drag_selection); - return true; + if (show_transformation_gizmos) { + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); + Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + if (x_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { + drag_type = DRAG_SCALE_X; + } + Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + if (y_handle_rect.has_point(simple_xform.affine_inverse().xform(b->get_position()))) { + drag_type = DRAG_SCALE_Y; + } } + + drag_from = transform.affine_inverse().xform(b->get_position()); + drag_selection = selection; + _save_canvas_item_state(drag_selection); + return true; } else { if (has_locked_items) { EditorToaster::get_singleton()->popup_str(TTR(locked_transform_warning), EditorToaster::SEVERITY_WARNING); @@ -1966,66 +1981,87 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { return has_locked_items; } } - } - - if (drag_type == DRAG_SCALE_BOTH || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { + } else if (drag_type == DRAG_SCALE_BOTH || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { // Resize the node if (m.is_valid()) { _restore_canvas_item_state(drag_selection); - CanvasItem *ci = drag_selection.front()->get(); drag_to = transform.affine_inverse().xform(m->get_position()); - Transform2D parent_xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse(); - Transform2D unscaled_transform = (transform * parent_xform * ci->_edit_get_transform()).orthonormalized(); - Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform; - - bool uniform = m->is_shift_pressed(); - bool is_ctrl = m->is_command_or_control_pressed(); - - Point2 drag_from_local = simple_xform.xform(drag_from); - Point2 drag_to_local = simple_xform.xform(drag_to); - Point2 offset = drag_to_local - drag_from_local; + Size2 scale_max; + if (drag_type != DRAG_SCALE_BOTH) { + for (CanvasItem *ci : drag_selection) { + scale_max = scale_max.max(ci->_edit_get_scale()); + } + } - Size2 scale = ci->_edit_get_scale(); - Size2 original_scale = scale; - real_t ratio = scale.y / scale.x; - if (drag_type == DRAG_SCALE_BOTH) { - Size2 scale_factor = drag_to_local / drag_from_local; - if (uniform) { - scale *= (scale_factor.x + scale_factor.y) / 2.0; + for (CanvasItem *ci : drag_selection) { + Transform2D edit_transform; + bool using_temp_pivot = !Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y); + if (using_temp_pivot) { + edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot); } else { - scale *= scale_factor; + edit_transform = ci->_edit_get_transform(); } - } else { - Size2 scale_factor = Vector2(offset.x, -offset.y) / SCALE_HANDLE_DISTANCE; - Size2 parent_scale = parent_xform.get_scale(); - scale_factor *= Vector2(1.0 / parent_scale.x, 1.0 / parent_scale.y); - if (drag_type == DRAG_SCALE_X) { - scale.x += scale_factor.x; + Transform2D parent_xform = ci->get_global_transform_with_canvas() * ci->get_transform().affine_inverse(); + Transform2D unscaled_transform = (transform * parent_xform * edit_transform).orthonormalized(); + Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform; + + bool uniform = m->is_shift_pressed(); + bool is_ctrl = m->is_command_or_control_pressed(); + + Point2 drag_from_local = simple_xform.xform(drag_from); + Point2 drag_to_local = simple_xform.xform(drag_to); + Point2 offset = drag_to_local - drag_from_local; + + Size2 scale = ci->_edit_get_scale(); + Size2 original_scale = scale; + real_t ratio = scale.y / scale.x; + if (drag_type == DRAG_SCALE_BOTH) { + Size2 scale_factor = drag_to_local / drag_from_local; if (uniform) { - scale.y = scale.x * ratio; + scale *= (scale_factor.x + scale_factor.y) / 2.0; + } else { + scale *= scale_factor; } - } else if (drag_type == DRAG_SCALE_Y) { - scale.y -= scale_factor.y; - if (uniform) { - scale.x = scale.y / ratio; + } else { + Size2 scale_factor = Vector2(offset.x, -offset.y) / SCALE_HANDLE_DISTANCE; + Size2 parent_scale = parent_xform.get_scale(); + // Take into account the biggest scale, so all nodes are scaled uniformly. + scale_factor *= Vector2(1.0 / parent_scale.x, 1.0 / parent_scale.y) / (scale_max / original_scale); + + if (drag_type == DRAG_SCALE_X) { + scale.x += scale_factor.x; + if (uniform) { + scale.y = scale.x * ratio; + } + } else if (drag_type == DRAG_SCALE_Y) { + scale.y -= scale_factor.y; + if (uniform) { + scale.x = scale.y / ratio; + } } } - } - if (snap_scale && !is_ctrl) { - if (snap_relative) { - scale.x = original_scale.x * (roundf((scale.x / original_scale.x) / snap_scale_step) * snap_scale_step); - scale.y = original_scale.y * (roundf((scale.y / original_scale.y) / snap_scale_step) * snap_scale_step); - } else { - scale.x = roundf(scale.x / snap_scale_step) * snap_scale_step; - scale.y = roundf(scale.y / snap_scale_step) * snap_scale_step; + if (snap_scale && !is_ctrl) { + if (snap_relative) { + scale.x = original_scale.x * (Math::round((scale.x / original_scale.x) / snap_scale_step) * snap_scale_step); + scale.y = original_scale.y * (Math::round((scale.y / original_scale.y) / snap_scale_step) * snap_scale_step); + } else { + scale.x = Math::round(scale.x / snap_scale_step) * snap_scale_step; + scale.y = Math::round(scale.y / snap_scale_step) * snap_scale_step; + } + } + + ci->_edit_set_scale(scale); + + if (using_temp_pivot) { + Point2 ci_origin = ci->_edit_get_transform().get_origin(); + ci->_edit_set_position(ci_origin + (ci_origin - temp_pivot) * ((scale - original_scale) / original_scale)); } } - ci->_edit_set_scale(scale); return true; } @@ -2073,7 +2109,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if (drag_type == DRAG_NONE) { //Start moving the nodes if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) { - if ((b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) { + if ((tool == TOOL_SELECT && b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) { bool has_locked_items = false; List<CanvasItem *> selection = _get_edited_canvas_items(false, true, &has_locked_items); @@ -2133,7 +2169,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { } Point2 drag_delta = drag_to - drag_from; - if (drag_selection.size() == 1 && (drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y)) { + if (drag_type == DRAG_MOVE_X || drag_type == DRAG_MOVE_Y) { const CanvasItem *selected = drag_selection.front()->get(); Transform2D parent_xform = selected->get_global_transform_with_canvas() * selected->get_transform().affine_inverse(); Transform2D unscaled_transform = (transform * parent_xform * selected->_edit_get_transform()).orthonormalized(); @@ -3466,16 +3502,14 @@ void CanvasItemEditor::_draw_selection() { Ref<Texture2D> previous_position_icon = get_editor_theme_icon(SNAME("EditorPositionPrevious")); RID vp_ci = viewport->get_canvas_item(); - List<CanvasItem *> selection = _get_edited_canvas_items(true, false); - bool single = selection.size() == 1; + bool transform_tool = tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT; + for (CanvasItem *E : selection) { CanvasItem *ci = Object::cast_to<CanvasItem>(E); CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); - bool item_locked = ci->has_meta("_edit_lock_"); - // Draw the previous position if we are dragging the node if (show_helpers && (drag_type == DRAG_MOVE || drag_type == DRAG_ROTATE || @@ -3500,6 +3534,7 @@ void CanvasItemEditor::_draw_selection() { } } + bool item_locked = ci->has_meta("_edit_lock_"); Transform2D xform = transform * ci->get_global_transform_with_canvas(); // Draw the selected items position / surrounding boxes @@ -3529,7 +3564,7 @@ void CanvasItemEditor::_draw_selection() { viewport->draw_set_transform_matrix(viewport->get_transform()); } - if (single && !item_locked && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE || tool == TOOL_EDIT_PIVOT)) { //kind of sucks + if (single && !item_locked && transform_tool) { // Draw the pivot if (ci->_edit_use_pivot()) { // Draw the node's pivot @@ -3572,73 +3607,88 @@ void CanvasItemEditor::_draw_selection() { select_handle->draw(vp_ci, (ofs - (select_handle->get_size() / 2)).floor()); } } + } + } - // Draw the move handles - bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL); - bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT); - if (tool == TOOL_MOVE && show_transformation_gizmos) { - if (_is_node_movable(ci)) { - Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); - Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + // Remove non-movable nodes. + for (CanvasItem *ci : selection) { + if (!_is_node_movable(ci)) { + selection.erase(ci); + } + } - Size2 move_factor = Size2(MOVE_HANDLE_DISTANCE, MOVE_HANDLE_DISTANCE); - viewport->draw_set_transform_matrix(simple_xform); + if (!selection.is_empty() && transform_tool && show_transformation_gizmos) { + CanvasItem *ci = selection.front()->get(); - Vector<Point2> points = { - Vector2(move_factor.x * EDSCALE, 5 * EDSCALE), - Vector2(move_factor.x * EDSCALE, -5 * EDSCALE), - Vector2((move_factor.x + 10) * EDSCALE, 0) - }; + Transform2D xform = transform * ci->get_global_transform_with_canvas(); + bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL); + bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT); - viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); - viewport->draw_line(Point2(), Point2(move_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + // Draw the move handles. + if ((tool == TOOL_SELECT && is_alt && !is_ctrl) || tool == TOOL_MOVE) { + Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; - points.clear(); - points.push_back(Vector2(5 * EDSCALE, move_factor.y * EDSCALE)); - points.push_back(Vector2(-5 * EDSCALE, move_factor.y * EDSCALE)); - points.push_back(Vector2(0, (move_factor.y + 10) * EDSCALE)); + Size2 move_factor = Size2(MOVE_HANDLE_DISTANCE, MOVE_HANDLE_DISTANCE); + viewport->draw_set_transform_matrix(simple_xform); - viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); - viewport->draw_line(Point2(), Point2(0, move_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + Vector<Point2> points = { + Vector2(move_factor.x * EDSCALE, 5 * EDSCALE), + Vector2(move_factor.x * EDSCALE, -5 * EDSCALE), + Vector2((move_factor.x + 10) * EDSCALE, 0) + }; - viewport->draw_set_transform_matrix(viewport->get_transform()); - } - } + viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); + viewport->draw_line(Point2(), Point2(move_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); - // Draw the rescale handles - if (show_transformation_gizmos && ((is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y)) { - if (_is_node_movable(ci)) { - Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * ci->_edit_get_transform()).orthonormalized(); - Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + points.clear(); + points.push_back(Vector2(5 * EDSCALE, move_factor.y * EDSCALE)); + points.push_back(Vector2(-5 * EDSCALE, move_factor.y * EDSCALE)); + points.push_back(Vector2(0, (move_factor.y + 10) * EDSCALE)); - Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); - bool uniform = Input::get_singleton()->is_key_pressed(Key::SHIFT); - Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom; + viewport->draw_colored_polygon(points, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); + viewport->draw_line(Point2(), Point2(0, move_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); - if (drag_type == DRAG_SCALE_X) { - scale_factor.x += offset.x; - if (uniform) { - scale_factor.y += offset.x; - } - } else if (drag_type == DRAG_SCALE_Y) { - scale_factor.y += offset.y; - if (uniform) { - scale_factor.x += offset.y; - } - } + viewport->draw_set_transform_matrix(viewport->get_transform()); + } - viewport->draw_set_transform_matrix(simple_xform); - Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - viewport->draw_rect(x_handle_rect, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); - viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + // Draw the rescale handles. + if ((tool == TOOL_SELECT && is_alt && is_ctrl) || tool == TOOL_SCALE || drag_type == DRAG_SCALE_X || drag_type == DRAG_SCALE_Y) { + Transform2D edit_transform; + if (!Math::is_inf(temp_pivot.x) || !Math::is_inf(temp_pivot.y)) { + edit_transform = Transform2D(ci->_edit_get_rotation(), temp_pivot); + } else { + edit_transform = ci->_edit_get_transform(); + } + Transform2D unscaled_transform = (xform * ci->get_transform().affine_inverse() * edit_transform).orthonormalized(); + Transform2D simple_xform = viewport->get_transform() * unscaled_transform; - Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); - viewport->draw_rect(y_handle_rect, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); - viewport->draw_line(Point2(), Point2(0, scale_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE); + bool uniform = Input::get_singleton()->is_key_pressed(Key::SHIFT); + Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom; - viewport->draw_set_transform_matrix(viewport->get_transform()); + if (drag_type == DRAG_SCALE_X) { + scale_factor.x += offset.x; + if (uniform) { + scale_factor.y += offset.x; + } + } else if (drag_type == DRAG_SCALE_Y) { + scale_factor.y += offset.y; + if (uniform) { + scale_factor.x += offset.y; } } + + viewport->draw_set_transform_matrix(simple_xform); + Rect2 x_handle_rect = Rect2(scale_factor.x * EDSCALE, -5 * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + viewport->draw_rect(x_handle_rect, get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor))); + viewport->draw_line(Point2(), Point2(scale_factor.x * EDSCALE, 0), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + + Rect2 y_handle_rect = Rect2(-5 * EDSCALE, scale_factor.y * EDSCALE, 10 * EDSCALE, 10 * EDSCALE); + viewport->draw_rect(y_handle_rect, get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor))); + viewport->draw_line(Point2(), Point2(0, scale_factor.y * EDSCALE), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)), Math::round(EDSCALE)); + + viewport->draw_set_transform_matrix(viewport->get_transform()); } } @@ -3863,7 +3913,7 @@ void CanvasItemEditor::_draw_message() { Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label")); int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label")); - Point2 msgpos = Point2(RULER_WIDTH + 5 * EDSCALE, viewport->get_size().y - 20 * EDSCALE); + Point2 msgpos = Point2(RULER_WIDTH + 10 * EDSCALE, viewport->get_size().y - 14 * EDSCALE); viewport->draw_string(font, msgpos + Point2(1, 1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8)); viewport->draw_string(font, msgpos + Point2(-1, -1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8)); viewport->draw_string(font, msgpos, message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1)); @@ -3964,39 +4014,38 @@ void CanvasItemEditor::set_current_tool(Tool p_tool) { } void CanvasItemEditor::_update_editor_settings() { - button_center_view->set_icon(get_editor_theme_icon(SNAME("CenterView"))); - select_button->set_icon(get_editor_theme_icon(SNAME("ToolSelect"))); + button_center_view->set_button_icon(get_editor_theme_icon(SNAME("CenterView"))); + select_button->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect"))); select_sb->set_texture(get_editor_theme_icon(SNAME("EditorRect2D"))); - list_select_button->set_icon(get_editor_theme_icon(SNAME("ListSelect"))); - move_button->set_icon(get_editor_theme_icon(SNAME("ToolMove"))); - scale_button->set_icon(get_editor_theme_icon(SNAME("ToolScale"))); - rotate_button->set_icon(get_editor_theme_icon(SNAME("ToolRotate"))); - smart_snap_button->set_icon(get_editor_theme_icon(SNAME("Snap"))); - grid_snap_button->set_icon(get_editor_theme_icon(SNAME("SnapGrid"))); - snap_config_menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); - skeleton_menu->set_icon(get_editor_theme_icon(SNAME("Bone"))); - override_camera_button->set_icon(get_editor_theme_icon(SNAME("Camera2D"))); - pan_button->set_icon(get_editor_theme_icon(SNAME("ToolPan"))); - ruler_button->set_icon(get_editor_theme_icon(SNAME("Ruler"))); - pivot_button->set_icon(get_editor_theme_icon(SNAME("EditPivot"))); + list_select_button->set_button_icon(get_editor_theme_icon(SNAME("ListSelect"))); + move_button->set_button_icon(get_editor_theme_icon(SNAME("ToolMove"))); + scale_button->set_button_icon(get_editor_theme_icon(SNAME("ToolScale"))); + rotate_button->set_button_icon(get_editor_theme_icon(SNAME("ToolRotate"))); + smart_snap_button->set_button_icon(get_editor_theme_icon(SNAME("Snap"))); + grid_snap_button->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); + snap_config_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + skeleton_menu->set_button_icon(get_editor_theme_icon(SNAME("Bone"))); + pan_button->set_button_icon(get_editor_theme_icon(SNAME("ToolPan"))); + ruler_button->set_button_icon(get_editor_theme_icon(SNAME("Ruler"))); + pivot_button->set_button_icon(get_editor_theme_icon(SNAME("EditPivot"))); select_handle = get_editor_theme_icon(SNAME("EditorHandle")); anchor_handle = get_editor_theme_icon(SNAME("EditorControlAnchor")); - lock_button->set_icon(get_editor_theme_icon(SNAME("Lock"))); - unlock_button->set_icon(get_editor_theme_icon(SNAME("Unlock"))); - group_button->set_icon(get_editor_theme_icon(SNAME("Group"))); - ungroup_button->set_icon(get_editor_theme_icon(SNAME("Ungroup"))); - key_loc_button->set_icon(get_editor_theme_icon(SNAME("KeyPosition"))); - key_rot_button->set_icon(get_editor_theme_icon(SNAME("KeyRotation"))); - key_scale_button->set_icon(get_editor_theme_icon(SNAME("KeyScale"))); - key_insert_button->set_icon(get_editor_theme_icon(SNAME("Key"))); - key_auto_insert_button->set_icon(get_editor_theme_icon(SNAME("AutoKey"))); + lock_button->set_button_icon(get_editor_theme_icon(SNAME("Lock"))); + unlock_button->set_button_icon(get_editor_theme_icon(SNAME("Unlock"))); + group_button->set_button_icon(get_editor_theme_icon(SNAME("Group"))); + ungroup_button->set_button_icon(get_editor_theme_icon(SNAME("Ungroup"))); + key_loc_button->set_button_icon(get_editor_theme_icon(SNAME("KeyPosition"))); + key_rot_button->set_button_icon(get_editor_theme_icon(SNAME("KeyRotation"))); + key_scale_button->set_button_icon(get_editor_theme_icon(SNAME("KeyScale"))); + key_insert_button->set_button_icon(get_editor_theme_icon(SNAME("Key"))); + key_auto_insert_button->set_button_icon(get_editor_theme_icon(SNAME("AutoKey"))); // Use a different color for the active autokey icon to make them easier // to distinguish from the other key icons at the top. On a light theme, // the icon will be dark, so we need to lighten it before blending it // with the red color. const Color key_auto_color = EditorThemeManager::is_dark_theme() ? Color(1, 1, 1) : Color(4.25, 4.25, 4.25); key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55)); - animation_menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + animation_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); context_toolbar_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("ContextualToolbar"), EditorStringName(EditorStyles))); @@ -4014,8 +4063,6 @@ void CanvasItemEditor::_notification(int p_what) { case NOTIFICATION_READY: { _update_lock_and_group_button(); - EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true)); - EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false)); ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &CanvasItemEditor::_project_settings_changed)); } break; @@ -4114,15 +4161,6 @@ void CanvasItemEditor::_notification(int p_what) { _update_editor_settings(); } break; - case NOTIFICATION_VISIBILITY_CHANGED: { - if (!is_visible() && override_camera_button->is_pressed()) { - EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); - - debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE); - override_camera_button->set_pressed(false); - } - } break; - case NOTIFICATION_APPLICATION_FOCUS_OUT: case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { if (drag_type != DRAG_NONE) { @@ -4280,16 +4318,6 @@ void CanvasItemEditor::_button_toggle_grid_snap(bool p_status) { viewport->queue_redraw(); } -void CanvasItemEditor::_button_override_camera(bool p_pressed) { - EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); - - if (p_pressed) { - debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_2D); - } else { - debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE); - } -} - void CanvasItemEditor::_button_tool_select(int p_index) { Button *tb[TOOL_MAX] = { select_button, list_select_button, move_button, scale_button, rotate_button, pivot_button, pan_button, ruler_button }; for (int i = 0; i < TOOL_MAX; i++) { @@ -4396,17 +4424,6 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, te->commit_insert_queue(); } -void CanvasItemEditor::_update_override_camera_button(bool p_game_running) { - if (p_game_running) { - override_camera_button->set_disabled(false); - override_camera_button->set_tooltip_text(TTR("Project Camera Override\nOverrides the running project's camera with the editor viewport camera.")); - } else { - override_camera_button->set_disabled(true); - override_camera_button->set_pressed(false); - override_camera_button->set_tooltip_text(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature.")); - } -} - void CanvasItemEditor::_popup_callback(int p_op) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); last_option = MenuOption(p_op); @@ -5387,7 +5404,7 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(pivot_button); pivot_button->set_toggle_mode(true); pivot_button->connect(SceneStringName(pressed), callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT)); - pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.") + "\n" + TTR("Shift: Set temporary rotation pivot.") + "\n" + TTR("Click this button while holding Shift to put the temporary rotation pivot in the center of the selected nodes.")); + pivot_button->set_tooltip_text(TTR("Click to change object's pivot.") + "\n" + TTR("Shift: Set temporary pivot.") + "\n" + TTR("Click this button while holding Shift to put the temporary pivot in the center of the selected nodes.")); pan_button = memnew(Button); pan_button->set_theme_type_variation("FlatButton"); @@ -5512,16 +5529,6 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(memnew(VSeparator)); - override_camera_button = memnew(Button); - override_camera_button->set_theme_type_variation("FlatButton"); - main_menu_hbox->add_child(override_camera_button); - override_camera_button->connect(SceneStringName(toggled), callable_mp(this, &CanvasItemEditor::_button_override_camera)); - override_camera_button->set_toggle_mode(true); - override_camera_button->set_disabled(true); - _update_override_camera_button(false); - - main_menu_hbox->add_child(memnew(VSeparator)); - view_menu = memnew(MenuButton); view_menu->set_flat(false); view_menu->set_theme_type_variation("FlatMenuButton"); @@ -5661,7 +5668,7 @@ CanvasItemEditor::CanvasItemEditor() { snap_dialog->connect(SceneStringName(confirmed), callable_mp(this, &CanvasItemEditor::_snap_changed)); add_child(snap_dialog); - select_sb = Ref<StyleBoxTexture>(memnew(StyleBoxTexture)); + select_sb.instantiate(); selection_menu = memnew(PopupMenu); add_child(selection_menu); @@ -6264,7 +6271,7 @@ void CanvasItemEditorViewport::_update_theme() { for (BaseButton *btn : btn_list) { CheckBox *check = Object::cast_to<CheckBox>(btn); - check->set_icon(get_editor_theme_icon(check->get_text())); + check->set_button_icon(get_editor_theme_icon(check->get_text())); } label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index bae9efebc9..c5335bf9c1 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -335,7 +335,6 @@ private: Button *group_button = nullptr; Button *ungroup_button = nullptr; - Button *override_camera_button = nullptr; MenuButton *view_menu = nullptr; PopupMenu *grid_menu = nullptr; PopupMenu *theme_menu = nullptr; @@ -518,11 +517,8 @@ private: void _zoom_on_position(real_t p_zoom, Point2 p_position = Point2()); void _button_toggle_smart_snap(bool p_status); void _button_toggle_grid_snap(bool p_status); - void _button_override_camera(bool p_pressed); void _button_tool_select(int p_index); - void _update_override_camera_button(bool p_game_running); - HSplitContainer *left_panel_split = nullptr; HSplitContainer *right_panel_split = nullptr; VSplitContainer *bottom_split = nullptr; diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp index cd13deb3e9..a52d949819 100644 --- a/editor/plugins/control_editor_plugin.cpp +++ b/editor/plugins/control_editor_plugin.cpp @@ -157,7 +157,7 @@ ControlPositioningWarning::ControlPositioningWarning() { void EditorPropertyAnchorsPreset::_set_read_only(bool p_read_only) { options->set_disabled(p_read_only); -}; +} void EditorPropertyAnchorsPreset::_option_selected(int p_which) { int64_t val = options->get_item_metadata(p_which); @@ -221,7 +221,7 @@ void EditorPropertySizeFlags::_set_read_only(bool p_read_only) { check->set_disabled(p_read_only); } flag_presets->set_disabled(p_read_only); -}; +} void EditorPropertySizeFlags::_preset_selected(int p_which) { int preset = flag_presets->get_item_id(p_which); @@ -536,7 +536,6 @@ ControlEditorPopupButton::ControlEditorPopupButton() { set_focus_mode(FOCUS_NONE); popup_panel = memnew(PopupPanel); - popup_panel->set_theme_type_variation("ControlEditorPopupPanel"); add_child(popup_panel); popup_panel->connect("about_to_popup", callable_mp(this, &ControlEditorPopupButton::_popup_visibility_changed).bind(true)); popup_panel->connect("popup_hide", callable_mp(this, &ControlEditorPopupButton::_popup_visibility_changed).bind(false)); @@ -573,27 +572,27 @@ void AnchorPresetPicker::_notification(int p_notification) { switch (p_notification) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - preset_buttons[PRESET_TOP_LEFT]->set_icon(get_editor_theme_icon(SNAME("ControlAlignTopLeft"))); - preset_buttons[PRESET_CENTER_TOP]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenterTop"))); - preset_buttons[PRESET_TOP_RIGHT]->set_icon(get_editor_theme_icon(SNAME("ControlAlignTopRight"))); + preset_buttons[PRESET_TOP_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopLeft"))); + preset_buttons[PRESET_CENTER_TOP]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterTop"))); + preset_buttons[PRESET_TOP_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopRight"))); - preset_buttons[PRESET_CENTER_LEFT]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenterLeft"))); - preset_buttons[PRESET_CENTER]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenter"))); - preset_buttons[PRESET_CENTER_RIGHT]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenterRight"))); + preset_buttons[PRESET_CENTER_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterLeft"))); + preset_buttons[PRESET_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter"))); + preset_buttons[PRESET_CENTER_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterRight"))); - preset_buttons[PRESET_BOTTOM_LEFT]->set_icon(get_editor_theme_icon(SNAME("ControlAlignBottomLeft"))); - preset_buttons[PRESET_CENTER_BOTTOM]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenterBottom"))); - preset_buttons[PRESET_BOTTOM_RIGHT]->set_icon(get_editor_theme_icon(SNAME("ControlAlignBottomRight"))); + preset_buttons[PRESET_BOTTOM_LEFT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomLeft"))); + preset_buttons[PRESET_CENTER_BOTTOM]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterBottom"))); + preset_buttons[PRESET_BOTTOM_RIGHT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomRight"))); - preset_buttons[PRESET_TOP_WIDE]->set_icon(get_editor_theme_icon(SNAME("ControlAlignTopWide"))); - preset_buttons[PRESET_HCENTER_WIDE]->set_icon(get_editor_theme_icon(SNAME("ControlAlignHCenterWide"))); - preset_buttons[PRESET_BOTTOM_WIDE]->set_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide"))); + preset_buttons[PRESET_TOP_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignTopWide"))); + preset_buttons[PRESET_HCENTER_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignHCenterWide"))); + preset_buttons[PRESET_BOTTOM_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignBottomWide"))); - preset_buttons[PRESET_LEFT_WIDE]->set_icon(get_editor_theme_icon(SNAME("ControlAlignLeftWide"))); - preset_buttons[PRESET_VCENTER_WIDE]->set_icon(get_editor_theme_icon(SNAME("ControlAlignVCenterWide"))); - preset_buttons[PRESET_RIGHT_WIDE]->set_icon(get_editor_theme_icon(SNAME("ControlAlignRightWide"))); + preset_buttons[PRESET_LEFT_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignLeftWide"))); + preset_buttons[PRESET_VCENTER_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignVCenterWide"))); + preset_buttons[PRESET_RIGHT_WIDE]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignRightWide"))); - preset_buttons[PRESET_FULL_RECT]->set_icon(get_editor_theme_icon(SNAME("ControlAlignFullRect"))); + preset_buttons[PRESET_FULL_RECT]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignFullRect"))); } break; } } @@ -691,17 +690,17 @@ void SizeFlagPresetPicker::_notification(int p_notification) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (vertical) { - preset_buttons[SIZE_SHRINK_BEGIN]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenterTop"))); - preset_buttons[SIZE_SHRINK_CENTER]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenter"))); - preset_buttons[SIZE_SHRINK_END]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenterBottom"))); + preset_buttons[SIZE_SHRINK_BEGIN]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterTop"))); + preset_buttons[SIZE_SHRINK_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter"))); + preset_buttons[SIZE_SHRINK_END]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterBottom"))); - preset_buttons[SIZE_FILL]->set_icon(get_editor_theme_icon(SNAME("ControlAlignVCenterWide"))); + preset_buttons[SIZE_FILL]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignVCenterWide"))); } else { - preset_buttons[SIZE_SHRINK_BEGIN]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenterLeft"))); - preset_buttons[SIZE_SHRINK_CENTER]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenter"))); - preset_buttons[SIZE_SHRINK_END]->set_icon(get_editor_theme_icon(SNAME("ControlAlignCenterRight"))); + preset_buttons[SIZE_SHRINK_BEGIN]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterLeft"))); + preset_buttons[SIZE_SHRINK_CENTER]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenter"))); + preset_buttons[SIZE_SHRINK_END]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignCenterRight"))); - preset_buttons[SIZE_FILL]->set_icon(get_editor_theme_icon(SNAME("ControlAlignHCenterWide"))); + preset_buttons[SIZE_FILL]->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignHCenterWide"))); } } break; } @@ -1051,9 +1050,9 @@ void ControlEditorToolbar::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - anchors_button->set_icon(get_editor_theme_icon(SNAME("ControlLayout"))); - anchor_mode_button->set_icon(get_editor_theme_icon(SNAME("Anchor"))); - containers_button->set_icon(get_editor_theme_icon(SNAME("ContainerLayout"))); + anchors_button->set_button_icon(get_editor_theme_icon(SNAME("ControlLayout"))); + anchor_mode_button->set_button_icon(get_editor_theme_icon(SNAME("Anchor"))); + containers_button->set_button_icon(get_editor_theme_icon(SNAME("ContainerLayout"))); } break; } } diff --git a/editor/plugins/control_editor_plugin.h b/editor/plugins/control_editor_plugin.h index 2672e8ef97..56e9f6ced4 100644 --- a/editor/plugins/control_editor_plugin.h +++ b/editor/plugins/control_editor_plugin.h @@ -241,7 +241,7 @@ protected: static ControlEditorToolbar *singleton; public: - bool is_anchors_mode_enabled() { return anchors_mode; }; + bool is_anchors_mode_enabled() { return anchors_mode; } static ControlEditorToolbar *get_singleton() { return singleton; } diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index e518cf7815..67006af44b 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -972,7 +972,7 @@ void CurveEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { spacing = Math::round(BASE_SPACING * get_theme_default_base_scale()); - snap_button->set_icon(get_editor_theme_icon(SNAME("SnapGrid"))); + snap_button->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); PopupMenu *p = presets_button->get_popup(); p->clear(); p->add_icon_item(get_editor_theme_icon(SNAME("CurveConstant")), TTR("Constant"), CurveEdit::PRESET_CONSTANT); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 9a53f07a3f..3618c0e6d3 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/io/file_access_memory.h" +#include "core/io/image.h" #include "core/io/resource_loader.h" #include "core/object/script_language.h" #include "core/os/os.h" diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp index 6366d20539..7cf0b2d2ac 100644 --- a/editor/plugins/font_config_plugin.cpp +++ b/editor/plugins/font_config_plugin.cpp @@ -149,7 +149,7 @@ void EditorPropertyFontMetaOverride::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (button_add) { - button_add->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_add->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } } break; } @@ -295,7 +295,7 @@ void EditorPropertyFontMetaOverride::update_property() { hbox->add_child(prop); prop->set_h_size_flags(SIZE_EXPAND_FILL); Button *remove = memnew(Button); - remove->set_icon(get_editor_theme_icon(SNAME("Remove"))); + remove->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); hbox->add_child(remove); remove->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyFontMetaOverride::_remove).bind(remove, name)); @@ -470,7 +470,7 @@ void EditorPropertyOTVariation::update_property() { Vector3i range = supported.get_value_at_index(i); EditorPropertyInteger *prop = memnew(EditorPropertyInteger); - prop->setup(range.x, range.y, false, 1, false, false); + prop->setup(range.x, range.y, false, true, false, false); prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag)); String name = TS->tag_to_name(name_tag); @@ -552,7 +552,7 @@ void EditorPropertyOTFeatures::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (button_add) { - button_add->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_add->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } } break; } @@ -789,7 +789,7 @@ void EditorPropertyOTFeatures::update_property() { hbox->add_child(prop); prop->set_h_size_flags(SIZE_EXPAND_FILL); Button *remove = memnew(Button); - remove->set_icon(get_editor_theme_icon(SNAME("Remove"))); + remove->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); hbox->add_child(remove); remove->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyOTFeatures::_remove).bind(remove, name_tag)); @@ -798,7 +798,7 @@ void EditorPropertyOTFeatures::update_property() { } button_add = EditorInspector::create_inspector_action_button(TTR("Add Feature")); - button_add->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_add->set_button_icon(get_editor_theme_icon(SNAME("Add"))); button_add->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyOTFeatures::_add_menu)); property_vbox->add_child(button_add); diff --git a/editor/plugins/game_view_plugin.cpp b/editor/plugins/game_view_plugin.cpp new file mode 100644 index 0000000000..5c1f81ee94 --- /dev/null +++ b/editor/plugins/game_view_plugin.cpp @@ -0,0 +1,487 @@ +/**************************************************************************/ +/* game_view_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 "game_view_plugin.h" + +#include "core/debugger/debugger_marshalls.h" +#include "editor/editor_main_screen.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "editor/themes/editor_scale.h" +#include "scene/gui/button.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" +#include "scene/gui/separator.h" + +void GameViewDebugger::_session_started(Ref<EditorDebuggerSession> p_session) { + Array setup_data; + Dictionary settings; + settings["editors/panning/2d_editor_panning_scheme"] = EDITOR_GET("editors/panning/2d_editor_panning_scheme"); + settings["editors/panning/simple_panning"] = EDITOR_GET("editors/panning/simple_panning"); + settings["editors/panning/warped_mouse_panning"] = EDITOR_GET("editors/panning/warped_mouse_panning"); + settings["editors/panning/2d_editor_pan_speed"] = EDITOR_GET("editors/panning/2d_editor_pan_speed"); + settings["canvas_item_editor/pan_view"] = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("canvas_item_editor/pan_view")); + setup_data.append(settings); + p_session->send_message("scene:runtime_node_select_setup", setup_data); + + Array type; + type.append(node_type); + p_session->send_message("scene:runtime_node_select_set_type", type); + Array visible; + visible.append(selection_visible); + p_session->send_message("scene:runtime_node_select_set_visible", visible); + Array mode; + mode.append(select_mode); + p_session->send_message("scene:runtime_node_select_set_mode", mode); + + emit_signal(SNAME("session_started")); +} + +void GameViewDebugger::_session_stopped() { + emit_signal(SNAME("session_stopped")); +} + +void GameViewDebugger::set_suspend(bool p_enabled) { + Array message; + message.append(p_enabled); + + for (Ref<EditorDebuggerSession> &I : sessions) { + if (I->is_active()) { + I->send_message("scene:suspend_changed", message); + } + } +} + +void GameViewDebugger::next_frame() { + for (Ref<EditorDebuggerSession> &I : sessions) { + if (I->is_active()) { + I->send_message("scene:next_frame", Array()); + } + } +} + +void GameViewDebugger::set_node_type(int p_type) { + node_type = p_type; + + Array message; + message.append(p_type); + + for (Ref<EditorDebuggerSession> &I : sessions) { + if (I->is_active()) { + I->send_message("scene:runtime_node_select_set_type", message); + } + } +} + +void GameViewDebugger::set_selection_visible(bool p_visible) { + selection_visible = p_visible; + + Array message; + message.append(p_visible); + + for (Ref<EditorDebuggerSession> &I : sessions) { + if (I->is_active()) { + I->send_message("scene:runtime_node_select_set_visible", message); + } + } +} + +void GameViewDebugger::set_select_mode(int p_mode) { + select_mode = p_mode; + + Array message; + message.append(p_mode); + + for (Ref<EditorDebuggerSession> &I : sessions) { + if (I->is_active()) { + I->send_message("scene:runtime_node_select_set_mode", message); + } + } +} + +void GameViewDebugger::set_camera_override(bool p_enabled) { + EditorDebuggerNode::get_singleton()->set_camera_override(p_enabled ? camera_override_mode : EditorDebuggerNode::OVERRIDE_NONE); +} + +void GameViewDebugger::set_camera_manipulate_mode(EditorDebuggerNode::CameraOverride p_mode) { + camera_override_mode = p_mode; + + if (EditorDebuggerNode::get_singleton()->get_camera_override() != EditorDebuggerNode::OVERRIDE_NONE) { + set_camera_override(true); + } +} + +void GameViewDebugger::reset_camera_2d_position() { + for (Ref<EditorDebuggerSession> &I : sessions) { + if (I->is_active()) { + I->send_message("scene:runtime_node_select_reset_camera_2d", Array()); + } + } +} + +void GameViewDebugger::reset_camera_3d_position() { + for (Ref<EditorDebuggerSession> &I : sessions) { + if (I->is_active()) { + I->send_message("scene:runtime_node_select_reset_camera_3d", Array()); + } + } +} + +void GameViewDebugger::setup_session(int p_session_id) { + Ref<EditorDebuggerSession> session = get_session(p_session_id); + ERR_FAIL_COND(session.is_null()); + + sessions.append(session); + + session->connect("started", callable_mp(this, &GameViewDebugger::_session_started).bind(session)); + session->connect("stopped", callable_mp(this, &GameViewDebugger::_session_stopped)); +} + +void GameViewDebugger::_bind_methods() { + ADD_SIGNAL(MethodInfo("session_started")); + ADD_SIGNAL(MethodInfo("session_stopped")); +} + +/////// + +void GameView::_sessions_changed() { + // The debugger session's `session_started/stopped` signal can be unreliable, so count it manually. + active_sessions = 0; + Array sessions = debugger->get_sessions(); + for (int i = 0; i < sessions.size(); i++) { + if (Object::cast_to<EditorDebuggerSession>(sessions[i])->is_active()) { + active_sessions++; + } + } + + _update_debugger_buttons(); +} + +void GameView::_update_debugger_buttons() { + bool empty = active_sessions == 0; + + suspend_button->set_disabled(empty); + camera_override_button->set_disabled(empty); + + PopupMenu *menu = camera_override_menu->get_popup(); + + bool disable_camera_reset = empty || !camera_override_button->is_pressed() || !menu->is_item_checked(menu->get_item_index(CAMERA_MODE_INGAME)); + menu->set_item_disabled(CAMERA_RESET_2D, disable_camera_reset); + menu->set_item_disabled(CAMERA_RESET_3D, disable_camera_reset); + + if (empty) { + suspend_button->set_pressed(false); + camera_override_button->set_pressed(false); + } + next_frame_button->set_disabled(!suspend_button->is_pressed()); +} + +void GameView::_suspend_button_toggled(bool p_pressed) { + _update_debugger_buttons(); + + debugger->set_suspend(p_pressed); +} + +void GameView::_node_type_pressed(int p_option) { + RuntimeNodeSelect::NodeType type = (RuntimeNodeSelect::NodeType)p_option; + for (int i = 0; i < RuntimeNodeSelect::NODE_TYPE_MAX; i++) { + node_type_button[i]->set_pressed_no_signal(i == type); + } + + _update_debugger_buttons(); + + debugger->set_node_type(type); +} + +void GameView::_select_mode_pressed(int p_option) { + RuntimeNodeSelect::SelectMode mode = (RuntimeNodeSelect::SelectMode)p_option; + for (int i = 0; i < RuntimeNodeSelect::SELECT_MODE_MAX; i++) { + select_mode_button[i]->set_pressed_no_signal(i == mode); + } + + debugger->set_select_mode(mode); +} + +void GameView::_hide_selection_toggled(bool p_pressed) { + hide_selection->set_button_icon(get_editor_theme_icon(p_pressed ? SNAME("GuiVisibilityHidden") : SNAME("GuiVisibilityVisible"))); + + debugger->set_selection_visible(!p_pressed); +} + +void GameView::_camera_override_button_toggled(bool p_pressed) { + _update_debugger_buttons(); + + debugger->set_camera_override(p_pressed); +} + +void GameView::_camera_override_menu_id_pressed(int p_id) { + PopupMenu *menu = camera_override_menu->get_popup(); + if (p_id != CAMERA_RESET_2D && p_id != CAMERA_RESET_3D) { + for (int i = 0; i < menu->get_item_count(); i++) { + menu->set_item_checked(i, false); + } + } + + switch (p_id) { + case CAMERA_RESET_2D: { + debugger->reset_camera_2d_position(); + } break; + case CAMERA_RESET_3D: { + debugger->reset_camera_3d_position(); + } break; + case CAMERA_MODE_INGAME: { + debugger->set_camera_manipulate_mode(EditorDebuggerNode::OVERRIDE_INGAME); + menu->set_item_checked(menu->get_item_index(p_id), true); + + _update_debugger_buttons(); + } break; + case CAMERA_MODE_EDITORS: { + debugger->set_camera_manipulate_mode(EditorDebuggerNode::OVERRIDE_EDITORS); + menu->set_item_checked(menu->get_item_index(p_id), true); + + _update_debugger_buttons(); + } break; + } +} + +void GameView::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + suspend_button->set_button_icon(get_editor_theme_icon(SNAME("Pause"))); + next_frame_button->set_button_icon(get_editor_theme_icon(SNAME("NextFrame"))); + + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_button_icon(get_editor_theme_icon(SNAME("InputEventJoypadMotion"))); + node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_button_icon(get_editor_theme_icon(SNAME("2DNodes"))); +#ifndef _3D_DISABLED + node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_button_icon(get_editor_theme_icon(SNAME("Node3D"))); +#endif // _3D_DISABLED + + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect"))); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_button_icon(get_editor_theme_icon(SNAME("ListSelect"))); + + hide_selection->set_button_icon(get_editor_theme_icon(hide_selection->is_pressed() ? SNAME("GuiVisibilityHidden") : SNAME("GuiVisibilityVisible"))); + + camera_override_button->set_button_icon(get_editor_theme_icon(SNAME("Camera"))); + camera_override_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + } break; + } +} + +void GameView::set_state(const Dictionary &p_state) { + if (p_state.has("hide_selection")) { + hide_selection->set_pressed(p_state["hide_selection"]); + _hide_selection_toggled(hide_selection->is_pressed()); + } + if (p_state.has("select_mode")) { + _select_mode_pressed(p_state["select_mode"]); + } + if (p_state.has("camera_override_mode")) { + _camera_override_menu_id_pressed(p_state["camera_override_mode"]); + } +} + +Dictionary GameView::get_state() const { + Dictionary d; + d["hide_selection"] = hide_selection->is_pressed(); + + for (int i = 0; i < RuntimeNodeSelect::SELECT_MODE_MAX; i++) { + if (select_mode_button[i]->is_pressed()) { + d["select_mode"] = i; + break; + } + } + + PopupMenu *menu = camera_override_menu->get_popup(); + for (int i = CAMERA_MODE_INGAME; i < CAMERA_MODE_EDITORS + 1; i++) { + if (menu->is_item_checked(menu->get_item_index(i))) { + d["camera_override_mode"] = i; + break; + } + } + + return d; +} + +GameView::GameView(Ref<GameViewDebugger> p_debugger) { + debugger = p_debugger; + + // Add some margin to the sides for better aesthetics. + // This prevents the first button's hover/pressed effect from "touching" the panel's border, + // which looks ugly. + MarginContainer *toolbar_margin = memnew(MarginContainer); + toolbar_margin->add_theme_constant_override("margin_left", 4 * EDSCALE); + toolbar_margin->add_theme_constant_override("margin_right", 4 * EDSCALE); + add_child(toolbar_margin); + + HBoxContainer *main_menu_hbox = memnew(HBoxContainer); + toolbar_margin->add_child(main_menu_hbox); + + suspend_button = memnew(Button); + main_menu_hbox->add_child(suspend_button); + suspend_button->set_toggle_mode(true); + suspend_button->set_theme_type_variation("FlatButton"); + suspend_button->connect(SceneStringName(toggled), callable_mp(this, &GameView::_suspend_button_toggled)); + suspend_button->set_tooltip_text(TTR("Suspend")); + + next_frame_button = memnew(Button); + main_menu_hbox->add_child(next_frame_button); + next_frame_button->set_theme_type_variation("FlatButton"); + next_frame_button->connect(SceneStringName(pressed), callable_mp(*debugger, &GameViewDebugger::next_frame)); + next_frame_button->set_tooltip_text(TTR("Next Frame")); + + main_menu_hbox->add_child(memnew(VSeparator)); + + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE] = memnew(Button); + main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]); + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_text(TTR("Input")); + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_toggle_mode(true); + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_pressed(true); + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_theme_type_variation("FlatButton"); + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_NONE)); + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_tooltip_text(TTR("Allow game input.")); + + node_type_button[RuntimeNodeSelect::NODE_TYPE_2D] = memnew(Button); + main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]); + node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_text(TTR("2D")); + node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_toggle_mode(true); + node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_theme_type_variation("FlatButton"); + node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_2D)); + node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_tooltip_text(TTR("Disable game input and allow to select Node2Ds, Controls, and manipulate the 2D camera.")); + +#ifndef _3D_DISABLED + node_type_button[RuntimeNodeSelect::NODE_TYPE_3D] = memnew(Button); + main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]); + node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_text(TTR("3D")); + node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_toggle_mode(true); + node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_theme_type_variation("FlatButton"); + node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_3D)); + node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_tooltip_text(TTR("Disable game input and allow to select Node3Ds and manipulate the 3D camera.")); +#endif // _3D_DISABLED + + main_menu_hbox->add_child(memnew(VSeparator)); + + hide_selection = memnew(Button); + main_menu_hbox->add_child(hide_selection); + hide_selection->set_toggle_mode(true); + hide_selection->set_theme_type_variation("FlatButton"); + hide_selection->connect(SceneStringName(toggled), callable_mp(this, &GameView::_hide_selection_toggled)); + hide_selection->set_tooltip_text(TTR("Toggle Selection Visibility")); + + main_menu_hbox->add_child(memnew(VSeparator)); + + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE] = memnew(Button); + main_menu_hbox->add_child(select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_toggle_mode(true); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_pressed(true); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_theme_type_variation("FlatButton"); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_select_mode_pressed).bind(RuntimeNodeSelect::SELECT_MODE_SINGLE)); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), Key::Q)); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_shortcut_context(this); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Alt+RMB: Show list of all nodes at position clicked.")); + + select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST] = memnew(Button); + main_menu_hbox->add_child(select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_toggle_mode(true); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_theme_type_variation("FlatButton"); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_select_mode_pressed).bind(RuntimeNodeSelect::SELECT_MODE_LIST)); + select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_tooltip_text(TTR("Show list of selectable nodes at position clicked.")); + + main_menu_hbox->add_child(memnew(VSeparator)); + + camera_override_button = memnew(Button); + main_menu_hbox->add_child(camera_override_button); + camera_override_button->set_toggle_mode(true); + camera_override_button->set_theme_type_variation("FlatButton"); + camera_override_button->connect(SceneStringName(toggled), callable_mp(this, &GameView::_camera_override_button_toggled)); + camera_override_button->set_tooltip_text(TTR("Override the in-game camera.")); + + camera_override_menu = memnew(MenuButton); + main_menu_hbox->add_child(camera_override_menu); + camera_override_menu->set_flat(false); + camera_override_menu->set_theme_type_variation("FlatMenuButton"); + camera_override_menu->set_h_size_flags(SIZE_SHRINK_END); + camera_override_menu->set_tooltip_text(TTR("Camera Override Options")); + + PopupMenu *menu = camera_override_menu->get_popup(); + menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_camera_override_menu_id_pressed)); + menu->add_item(TTR("Reset 2D Camera"), CAMERA_RESET_2D); + menu->add_item(TTR("Reset 3D Camera"), CAMERA_RESET_3D); + menu->add_separator(); + menu->add_radio_check_item(TTR("Manipulate In-Game"), CAMERA_MODE_INGAME); + menu->set_item_checked(menu->get_item_index(CAMERA_MODE_INGAME), true); + menu->add_radio_check_item(TTR("Manipulate From Editors"), CAMERA_MODE_EDITORS); + + _update_debugger_buttons(); + + panel = memnew(Panel); + add_child(panel); + panel->set_theme_type_variation("GamePanel"); + panel->set_v_size_flags(SIZE_EXPAND_FILL); + + p_debugger->connect("session_started", callable_mp(this, &GameView::_sessions_changed)); + p_debugger->connect("session_stopped", callable_mp(this, &GameView::_sessions_changed)); +} + +/////// + +void GameViewPlugin::make_visible(bool p_visible) { + game_view->set_visible(p_visible); +} + +void GameViewPlugin::set_state(const Dictionary &p_state) { + game_view->set_state(p_state); +} + +Dictionary GameViewPlugin::get_state() const { + return game_view->get_state(); +} + +void GameViewPlugin::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + add_debugger_plugin(debugger); + } break; + case NOTIFICATION_EXIT_TREE: { + remove_debugger_plugin(debugger); + } break; + } +} + +GameViewPlugin::GameViewPlugin() { + debugger.instantiate(); + + game_view = memnew(GameView(debugger)); + game_view->set_v_size_flags(Control::SIZE_EXPAND_FILL); + EditorNode::get_singleton()->get_editor_main_screen()->get_control()->add_child(game_view); + game_view->hide(); +} + +GameViewPlugin::~GameViewPlugin() { +} diff --git a/editor/plugins/game_view_plugin.h b/editor/plugins/game_view_plugin.h new file mode 100644 index 0000000000..f8701c3e76 --- /dev/null +++ b/editor/plugins/game_view_plugin.h @@ -0,0 +1,152 @@ +/**************************************************************************/ +/* game_view_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 GAME_VIEW_PLUGIN_H +#define GAME_VIEW_PLUGIN_H + +#include "editor/debugger/editor_debugger_node.h" +#include "editor/plugins/editor_debugger_plugin.h" +#include "editor/plugins/editor_plugin.h" +#include "scene/debugger/scene_debugger.h" +#include "scene/gui/box_container.h" + +class GameViewDebugger : public EditorDebuggerPlugin { + GDCLASS(GameViewDebugger, EditorDebuggerPlugin); + +private: + Vector<Ref<EditorDebuggerSession>> sessions; + + int node_type = RuntimeNodeSelect::NODE_TYPE_NONE; + bool selection_visible = true; + int select_mode = RuntimeNodeSelect::SELECT_MODE_SINGLE; + EditorDebuggerNode::CameraOverride camera_override_mode = EditorDebuggerNode::OVERRIDE_INGAME; + + void _session_started(Ref<EditorDebuggerSession> p_session); + void _session_stopped(); + +protected: + static void _bind_methods(); + +public: + void set_suspend(bool p_enabled); + void next_frame(); + + void set_node_type(int p_type); + void set_select_mode(int p_mode); + + void set_selection_visible(bool p_visible); + + void set_camera_override(bool p_enabled); + void set_camera_manipulate_mode(EditorDebuggerNode::CameraOverride p_mode); + + void reset_camera_2d_position(); + void reset_camera_3d_position(); + + virtual void setup_session(int p_session_id) override; + + GameViewDebugger() {} +}; + +class GameView : public VBoxContainer { + GDCLASS(GameView, VBoxContainer); + + enum { + CAMERA_RESET_2D, + CAMERA_RESET_3D, + CAMERA_MODE_INGAME, + CAMERA_MODE_EDITORS, + }; + + Ref<GameViewDebugger> debugger; + + int active_sessions = 0; + + Button *suspend_button = nullptr; + Button *next_frame_button = nullptr; + + Button *node_type_button[RuntimeNodeSelect::NODE_TYPE_MAX]; + Button *select_mode_button[RuntimeNodeSelect::SELECT_MODE_MAX]; + + Button *hide_selection = nullptr; + + Button *camera_override_button = nullptr; + MenuButton *camera_override_menu = nullptr; + + Panel *panel = nullptr; + + void _sessions_changed(); + + void _update_debugger_buttons(); + + void _suspend_button_toggled(bool p_pressed); + + void _node_type_pressed(int p_option); + void _select_mode_pressed(int p_option); + + void _hide_selection_toggled(bool p_pressed); + + void _camera_override_button_toggled(bool p_pressed); + void _camera_override_menu_id_pressed(int p_id); + +protected: + void _notification(int p_what); + +public: + void set_state(const Dictionary &p_state); + Dictionary get_state() const; + + GameView(Ref<GameViewDebugger> p_debugger); +}; + +class GameViewPlugin : public EditorPlugin { + GDCLASS(GameViewPlugin, EditorPlugin); + + GameView *game_view = nullptr; + + Ref<GameViewDebugger> debugger; + +protected: + void _notification(int p_what); + +public: + virtual String get_name() const override { return "Game"; } + bool has_main_screen() const override { return true; } + virtual void edit(Object *p_object) override {} + virtual bool handles(Object *p_object) const override { return false; } + virtual void make_visible(bool p_visible) override; + + virtual void set_state(const Dictionary &p_state) override; + virtual Dictionary get_state() const override; + + GameViewPlugin(); + ~GameViewPlugin(); +}; + +#endif // GAME_VIEW_PLUGIN_H diff --git a/editor/plugins/gdextension_export_plugin.h b/editor/plugins/gdextension_export_plugin.h index ad6b534235..c8ed05c6b7 100644 --- a/editor/plugins/gdextension_export_plugin.h +++ b/editor/plugins/gdextension_export_plugin.h @@ -77,12 +77,14 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p HashSet<String> archs; HashSet<String> features_wo_arch; + Vector<String> features_vector; for (const String &tag : p_features) { if (all_archs.has(tag)) { archs.insert(tag); } else { features_wo_arch.insert(tag); } + features_vector.append(tag); } if (archs.is_empty()) { @@ -90,11 +92,22 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p } HashSet<String> libs_added; + struct FoundLibInfo { + int count = 0; + Vector<String> libs; + }; + HashMap<String, FoundLibInfo> libs_found; + for (const String &arch_tag : archs) { + if (arch_tag != "universal") { + libs_found[arch_tag] = FoundLibInfo(); + } + } for (const String &arch_tag : archs) { PackedStringArray tags; String library_path = GDExtensionLibraryLoader::find_extension_library( p_path, config, [features_wo_arch, arch_tag](const String &p_feature) { return features_wo_arch.has(p_feature) || (p_feature == arch_tag); }, &tags); + if (libs_added.has(library_path)) { continue; // Universal library, already added for another arch, do not duplicate. } @@ -122,15 +135,19 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p String linker_flags = "-Wl,-U,_" + entry_symbol; add_ios_linker_flags(linker_flags); } - } else { - Vector<String> features_vector; - for (const String &E : p_features) { - features_vector.append(E); - } - if (get_export_platform().is_valid()) { - get_export_platform()->add_message(EditorExportPlatform::EXPORT_MESSAGE_WARNING, TTR("GDExtension"), vformat(TTR("No suitable library found for GDExtension: \"%s\". Possible feature flags for your platform: %s"), p_path, String(", ").join(features_vector))); + + // Update found library info. + if (arch_tag == "universal") { + for (const String &sub_arch_tag : archs) { + if (sub_arch_tag != "universal") { + libs_found[sub_arch_tag].count++; + libs_found[sub_arch_tag].libs.push_back(library_path); + } + } + } else { + libs_found[arch_tag].count++; + libs_found[arch_tag].libs.push_back(library_path); } - return; } Vector<SharedObject> dependencies_shared_objects = GDExtensionLibraryLoader::find_extension_dependencies(p_path, config, [p_features](String p_feature) { return p_features.has(p_feature); }); @@ -138,6 +155,18 @@ void GDExtensionExportPlugin::_export_file(const String &p_path, const String &p _add_shared_object(shared_object); } } + + for (const KeyValue<String, FoundLibInfo> &E : libs_found) { + if (E.value.count == 0) { + if (get_export_platform().is_valid()) { + get_export_platform()->add_message(EditorExportPlatform::EXPORT_MESSAGE_WARNING, TTR("GDExtension"), vformat(TTR("No \"%s\" library found for GDExtension: \"%s\". Possible feature flags for your platform: %s"), E.key, p_path, String(", ").join(features_vector))); + } + } else if (E.value.count > 1) { + if (get_export_platform().is_valid()) { + get_export_platform()->add_message(EditorExportPlatform::EXPORT_MESSAGE_WARNING, TTR("GDExtension"), vformat(TTR("Multiple \"%s\" libraries found for GDExtension: \"%s\": \"%s\"."), E.key, p_path, String(", ").join(E.value.libs))); + } + } + } } #endif // GDEXTENSION_EXPORT_PLUGIN_H diff --git a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp index 01492c1dd0..573c686d57 100644 --- a/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_shape_3d_gizmo_plugin.cpp @@ -93,7 +93,7 @@ String CollisionShape3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_g } if (Object::cast_to<CylinderShape3D>(*s)) { - return p_id == 0 ? "Radius" : "Height"; + return helper->cylinder_get_handle_name(p_id); } if (Object::cast_to<SeparationRayShape3D>(*s)) { @@ -219,25 +219,15 @@ void CollisionShape3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, i } if (Object::cast_to<CylinderShape3D>(*s)) { - Vector3 axis; - axis[p_id == 0 ? 0 : 1] = 1.0; Ref<CylinderShape3D> cs2 = s; - Vector3 ra, rb; - Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb); - float d = axis.dot(ra); - if (Node3DEditor::get_singleton()->is_snap_enabled()) { - d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); - } - - if (d < 0.001) { - d = 0.001; - } - if (p_id == 0) { - cs2->set_radius(d); - } else if (p_id == 1) { - cs2->set_height(d * 2.0); - } + real_t height = cs2->get_height(); + real_t radius = cs2->get_radius(); + Vector3 position; + helper->cylinder_set_handle(sg, p_id, height, radius, position); + cs2->set_height(height); + cs2->set_radius(radius); + cs->set_global_position(position); } } @@ -293,31 +283,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo if (Object::cast_to<CylinderShape3D>(*s)) { Ref<CylinderShape3D> ss = s; - if (p_cancel) { - if (p_id == 0) { - ss->set_radius(p_restore); - } else { - ss->set_height(p_restore); - } - return; - } - - EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - if (p_id == 0) { - ur->create_action(TTR("Change Cylinder Shape Radius")); - ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); - ur->add_undo_method(ss.ptr(), "set_radius", p_restore); - } else { - ur->create_action( - /// - - //////// - TTR("Change Cylinder Shape Height")); - ur->add_do_method(ss.ptr(), "set_height", ss->get_height()); - ur->add_undo_method(ss.ptr(), "set_height", p_restore); - } - - ur->commit_action(); + helper->cylinder_commit_handle(p_id, TTR("Change Cylinder Shape Radius"), TTR("Change Cylinder Shape Height"), p_cancel, cs, *ss, *ss); } if (Object::cast_to<SeparationRayShape3D>(*s)) { @@ -534,10 +500,7 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->add_collision_segments(collision_segments); - Vector<Vector3> handles = { - Vector3(cs2->get_radius(), 0, 0), - Vector3(0, cs2->get_height() * 0.5, 0) - }; + Vector<Vector3> handles = helper->cylinder_get_handles(cs2->get_height(), cs2->get_radius()); p_gizmo->add_handles(handles, handles_material); } @@ -575,20 +538,19 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (Object::cast_to<ConvexPolygonShape3D>(*s)) { Vector<Vector3> points = Object::cast_to<ConvexPolygonShape3D>(*s)->get_points(); - if (points.size() > 3) { + if (points.size() > 1) { // Need at least 2 points for a line. Vector<Vector3> varr = Variant(points); Geometry3D::MeshData md; Error err = ConvexHullComputer::convex_hull(varr, md); if (err == OK) { - Vector<Vector3> points2; - points2.resize(md.edges.size() * 2); + Vector<Vector3> lines; + lines.resize(md.edges.size() * 2); for (uint32_t i = 0; i < md.edges.size(); i++) { - points2.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a]; - points2.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b]; + lines.write[i * 2 + 0] = md.vertices[md.edges[i].vertex_a]; + lines.write[i * 2 + 1] = md.vertices[md.edges[i].vertex_b]; } - - p_gizmo->add_lines(points2, material); - p_gizmo->add_collision_segments(points2); + p_gizmo->add_lines(lines, material); + p_gizmo->add_collision_segments(lines); } } } diff --git a/editor/plugins/gizmos/gizmo_3d_helper.cpp b/editor/plugins/gizmos/gizmo_3d_helper.cpp index 1226be90cb..ff1b67aa4a 100644 --- a/editor/plugins/gizmos/gizmo_3d_helper.cpp +++ b/editor/plugins/gizmos/gizmo_3d_helper.cpp @@ -139,3 +139,98 @@ void Gizmo3DHelper::box_commit_handle(const String &p_action_name, bool p_cancel ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin()); ur->commit_action(); } + +Vector<Vector3> Gizmo3DHelper::cylinder_get_handles(real_t p_height, real_t p_radius) { + Vector<Vector3> handles; + handles.push_back(Vector3(p_radius, 0, 0)); + handles.push_back(Vector3(0, p_height * 0.5, 0)); + handles.push_back(Vector3(0, p_height * -0.5, 0)); + return handles; +} + +String Gizmo3DHelper::cylinder_get_handle_name(int p_id) const { + if (p_id == 0) { + return "Radius"; + } else { + return "Height"; + } +} + +void Gizmo3DHelper::cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position) { + int sign = p_id == 2 ? -1 : 1; + int axis = p_id == 0 ? 0 : 1; + + Vector3 axis_vector; + axis_vector[axis] = sign; + Vector3 ra, rb; + Geometry3D::get_closest_points_between_segments(axis_vector * -4096, axis_vector * 4096, p_segment[0], p_segment[1], ra, rb); + float d = axis_vector.dot(ra); + + // Snap to grid. + if (Node3DEditor::get_singleton()->is_snap_enabled()) { + d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap()); + } + + if (p_id == 0) { + // Adjust radius. + if (d < 0.001) { + d = 0.001; + } + r_radius = d; + r_cylinder_position = initial_transform.get_origin(); + } else if (p_id == 1 || p_id == 2) { + real_t initial_height = initial_value; + + // Adjust height. + if (Input::get_singleton()->is_key_pressed(Key::ALT)) { + r_height = d * 2.0; + } else { + r_height = (initial_height * 0.5) + d; + } + + if (r_height < 0.001) { + r_height = 0.001; + } + + // Adjust position. + if (Input::get_singleton()->is_key_pressed(Key::ALT)) { + r_cylinder_position = initial_transform.get_origin(); + } else { + Vector3 offset; + offset[axis] = (r_height - initial_height) * 0.5 * sign; + r_cylinder_position = initial_transform.xform(offset); + } + } +} + +void Gizmo3DHelper::cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object, Object *p_radius_object, const StringName &p_position_property, const StringName &p_height_property, const StringName &p_radius_property) { + if (!p_height_object) { + p_height_object = p_position_object; + } + if (!p_radius_object) { + p_radius_object = p_position_object; + } + + if (p_cancel) { + if (p_id == 0) { + p_radius_object->set(p_radius_property, initial_value); + } else { + p_height_object->set(p_height_property, initial_value); + } + p_position_object->set(p_position_property, initial_transform.get_origin()); + return; + } + + EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); + ur->create_action(p_id == 0 ? p_radius_action_name : p_height_action_name); + if (p_id == 0) { + ur->add_do_property(p_radius_object, p_radius_property, p_radius_object->get(p_radius_property)); + ur->add_undo_property(p_radius_object, p_radius_property, initial_value); + } else { + ur->add_do_property(p_height_object, p_height_property, p_height_object->get(p_height_property)); + ur->add_do_property(p_position_object, p_position_property, p_position_object->get(p_position_property)); + ur->add_undo_property(p_height_object, p_height_property, initial_value); + ur->add_undo_property(p_position_object, p_position_property, initial_transform.get_origin()); + } + ur->commit_action(); +} diff --git a/editor/plugins/gizmos/gizmo_3d_helper.h b/editor/plugins/gizmos/gizmo_3d_helper.h index 387ea020b8..6d27e54770 100644 --- a/editor/plugins/gizmos/gizmo_3d_helper.h +++ b/editor/plugins/gizmos/gizmo_3d_helper.h @@ -50,6 +50,11 @@ public: String box_get_handle_name(int p_id) const; void box_set_handle(const Vector3 p_segment[2], int p_id, Vector3 &r_box_size, Vector3 &r_box_position); void box_commit_handle(const String &p_action_name, bool p_cancel, Object *p_position_object, Object *p_size_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_size_property = "size"); + + Vector<Vector3> cylinder_get_handles(real_t p_height, real_t p_radius); + String cylinder_get_handle_name(int p_id) const; + void cylinder_set_handle(const Vector3 p_segment[2], int p_id, real_t &r_height, real_t &r_radius, Vector3 &r_cylinder_position); + void cylinder_commit_handle(int p_id, const String &p_radius_action_name, const String &p_height_action_name, bool p_cancel, Object *p_position_object, Object *p_height_object = nullptr, Object *p_radius_object = nullptr, const StringName &p_position_property = "global_position", const StringName &p_height_property = "height", const StringName &p_radius_property = "radius"); }; #endif // GIZMO_3D_HELPER_H diff --git a/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp index 39ae020d53..5a6527f876 100644 --- a/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp @@ -36,7 +36,7 @@ #include "scene/3d/marker_3d.h" Marker3DGizmoPlugin::Marker3DGizmoPlugin() { - pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); + pos3d_mesh.instantiate(); Vector<Vector3> cursor_points; Vector<Color> cursor_colors; diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp index c21a1b5dd6..4c2a3b60f8 100644 --- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp @@ -181,7 +181,7 @@ GPUParticlesCollisionSDF3DEditorPlugin::GPUParticlesCollisionSDF3DEditorPlugin() bake_hb->hide(); bake = memnew(Button); bake->set_theme_type_variation("FlatButton"); - bake->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons))); + bake->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons))); bake->set_text(TTR("Bake SDF")); bake->connect(SceneStringName(pressed), callable_mp(this, &GPUParticlesCollisionSDF3DEditorPlugin::_bake)); bake_hb->add_child(bake); diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index 1300394ca3..291ffc24d2 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -604,8 +604,8 @@ void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) { void GradientEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - reverse_button->set_icon(get_editor_theme_icon(SNAME("ReverseGradient"))); - snap_button->set_icon(get_editor_theme_icon(SNAME("SnapGrid"))); + reverse_button->set_button_icon(get_editor_theme_icon(SNAME("ReverseGradient"))); + snap_button->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); } break; case NOTIFICATION_READY: { Ref<Gradient> gradient = gradient_editor_rect->get_gradient(); diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.cpp b/editor/plugins/gradient_texture_2d_editor_plugin.cpp index 5bf1422780..46c9f4501b 100644 --- a/editor/plugins/gradient_texture_2d_editor_plugin.cpp +++ b/editor/plugins/gradient_texture_2d_editor_plugin.cpp @@ -262,8 +262,8 @@ void GradientTexture2DEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - reverse_button->set_icon(get_editor_theme_icon(SNAME("ReverseGradient"))); - snap_button->set_icon(get_editor_theme_icon(SNAME("SnapGrid"))); + reverse_button->set_button_icon(get_editor_theme_icon(SNAME("ReverseGradient"))); + snap_button->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); } break; case NOTIFICATION_READY: { if (texture.is_valid()) { diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp index 30debfc14f..0c3ceff977 100644 --- a/editor/plugins/input_event_editor_plugin.cpp +++ b/editor/plugins/input_event_editor_plugin.cpp @@ -37,7 +37,7 @@ void InputEventConfigContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - open_config_button->set_icon(get_editor_theme_icon(SNAME("Edit"))); + open_config_button->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); } break; } } diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp index 429add4540..e3b59f9bfb 100644 --- a/editor/plugins/light_occluder_2d_editor_plugin.cpp +++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp @@ -35,8 +35,8 @@ Ref<OccluderPolygon2D> LightOccluder2DEditor::_ensure_occluder() const { Ref<OccluderPolygon2D> occluder = node->get_occluder_polygon(); - if (!occluder.is_valid()) { - occluder = Ref<OccluderPolygon2D>(memnew(OccluderPolygon2D)); + if (occluder.is_null()) { + occluder.instantiate(); node->set_occluder_polygon(occluder); } return occluder; diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp index 854ab7de8f..3f21d5d11c 100644 --- a/editor/plugins/lightmap_gi_editor_plugin.cpp +++ b/editor/plugins/lightmap_gi_editor_plugin.cpp @@ -93,10 +93,14 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) { file_dialog->popup_file_dialog(); } break; 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.")); + EditorNode::get_singleton()->show_warning( + TTR("No meshes with lightmapping support to bake. Make sure they contain UV2 data and their Global Illumination property is set to Static.") + + String::utf8("\n\n• ") + TTR("To import a scene with lightmapping support, set Meshes > Light Baking to Static Lightmaps in the Import dock.") + + String::utf8("\n• ") + TTR("To enable lightmapping support on a primitive mesh, edit the PrimitiveMesh resource in the inspector and check Add UV2.") + + String::utf8("\n• ") + TTR("To enable lightmapping support on a CSG mesh, select the root CSG node and choose CSG > Bake Mesh Instance at the top of the 3D editor viewport.\nSelect the generated MeshInstance3D node and choose Mesh > Unwrap UV2 for Lightmap/AO at the top of the 3D editor viewport.")); } break; case LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE: { - EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable.")); + EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure the lightmap destination path is writable.")); } break; case LightmapGI::BAKE_ERROR_NO_SCENE_ROOT: { EditorNode::get_singleton()->show_warning(TTR("No editor scene root found.")); @@ -108,7 +112,7 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) { EditorNode::get_singleton()->show_warning(TTR("Maximum texture size is too small for the lightmap images.\nWhile this can be fixed by increasing the maximum texture size, it is recommended you split the scene into more objects instead.")); } break; case LightmapGI::BAKE_ERROR_LIGHTMAP_TOO_SMALL: { - EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes selected to bake have `lightmap_size_hint` value set high enough, and `texel_scale` value of LightmapGI is not too low.")); + EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes to bake have the Lightmap Size Hint property set high enough, and the LightmapGI's Texel Scale value is not too low.")); } break; case LightmapGI::BAKE_ERROR_ATLAS_TOO_SMALL: { EditorNode::get_singleton()->show_warning(TTR("Failed fitting a lightmap image into an atlas. This should never happen and should be reported.")); @@ -148,7 +152,7 @@ EditorProgress *LightmapGIEditorPlugin::tmp_progress = nullptr; bool LightmapGIEditorPlugin::bake_func_step(float p_progress, const String &p_description, void *, bool p_refresh) { if (!tmp_progress) { - tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), 1000, false)); + tmp_progress = memnew(EditorProgress("bake_lightmaps", TTR("Bake Lightmaps"), 1000, true)); ERR_FAIL_NULL_V(tmp_progress, false); } return tmp_progress->step(p_description, p_progress * 1000, p_refresh); @@ -177,7 +181,7 @@ LightmapGIEditorPlugin::LightmapGIEditorPlugin() { bake->set_theme_type_variation("FlatButton"); // TODO: Rework this as a dedicated toolbar control so we can hook into theme changes and update it // when the editor theme updates. - bake->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons))); + bake->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons))); bake->set_text(TTR("Bake Lightmaps")); #ifdef MODULE_LIGHTMAPPER_RD_ENABLED diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 2702b6c909..8bdc763ebe 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -56,9 +56,15 @@ void MaterialEditor::gui_input(const Ref<InputEvent> &p_event) { if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { rot.x -= mm->get_relative().y * 0.01; rot.y -= mm->get_relative().x * 0.01; - - rot.x = CLAMP(rot.x, -Math_PI / 2, Math_PI / 2); + if (quad_instance->is_visible()) { + // Clamp rotation so the quad is always visible. + const real_t limit = Math::deg_to_rad(80.0); + rot = rot.clampf(-limit, limit); + } else { + rot.x = CLAMP(rot.x, -Math_PI / 2, Math_PI / 2); + } _update_rotation(); + _store_rotation_metadata(); } } @@ -70,6 +76,7 @@ void MaterialEditor::_update_theme_item_cache() { theme_cache.sphere_icon = get_editor_theme_icon(SNAME("MaterialPreviewSphere")); theme_cache.box_icon = get_editor_theme_icon(SNAME("MaterialPreviewCube")); + theme_cache.quad_icon = get_editor_theme_icon(SNAME("MaterialPreviewQuad")); theme_cache.checkerboard = get_editor_theme_icon(SNAME("Checkerboard")); } @@ -77,11 +84,12 @@ void MaterialEditor::_update_theme_item_cache() { void MaterialEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - light_1_switch->set_icon(theme_cache.light_1_icon); - light_2_switch->set_icon(theme_cache.light_2_icon); + light_1_switch->set_button_icon(theme_cache.light_1_icon); + light_2_switch->set_button_icon(theme_cache.light_2_icon); - sphere_switch->set_icon(theme_cache.sphere_icon); - box_switch->set_icon(theme_cache.box_icon); + sphere_switch->set_button_icon(theme_cache.sphere_icon); + box_switch->set_button_icon(theme_cache.box_icon); + quad_switch->set_button_icon(theme_cache.quad_icon); error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); } break; @@ -95,6 +103,18 @@ void MaterialEditor::_notification(int p_what) { } } +void MaterialEditor::_set_rotation(real_t p_x_degrees, real_t p_y_degrees) { + rot.x = Math::deg_to_rad(p_x_degrees); + rot.y = Math::deg_to_rad(p_y_degrees); + _update_rotation(); +} + +// Store the rotation so it can persist when switching between materials. +void MaterialEditor::_store_rotation_metadata() { + Vector2 rotation_degrees = Vector2(Math::rad_to_deg(rot.x), Math::rad_to_deg(rot.y)); + EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_rotation", rotation_degrees); +} + void MaterialEditor::_update_rotation() { Transform3D t; t.basis.rotate(Vector3(0, 1, 0), -rot.y); @@ -124,6 +144,7 @@ void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_en vc->show(); sphere_instance->set_material_override(material); box_instance->set_material_override(material); + quad_instance->set_material_override(material); break; default: layout_error->show(); @@ -136,10 +157,6 @@ void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_en } else { hide(); } - - rot.x = Math::deg_to_rad(-15.0); - rot.y = Math::deg_to_rad(30.0); - _update_rotation(); } void MaterialEditor::_on_light_1_switch_pressed() { @@ -151,19 +168,36 @@ void MaterialEditor::_on_light_2_switch_pressed() { } void MaterialEditor::_on_sphere_switch_pressed() { - box_instance->hide(); sphere_instance->show(); + box_instance->hide(); + quad_instance->hide(); box_switch->set_pressed(false); - sphere_switch->set_pressed(true); - EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", true); + quad_switch->set_pressed(false); + _set_rotation(-15.0, 30.0); + _store_rotation_metadata(); + EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_mesh", "sphere"); } void MaterialEditor::_on_box_switch_pressed() { + sphere_instance->hide(); box_instance->show(); + quad_instance->hide(); + sphere_switch->set_pressed(false); + quad_switch->set_pressed(false); + _set_rotation(-15.0, 30.0); + _store_rotation_metadata(); + EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_mesh", "box"); +} + +void MaterialEditor::_on_quad_switch_pressed() { sphere_instance->hide(); - box_switch->set_pressed(true); + box_instance->hide(); + quad_instance->show(); sphere_switch->set_pressed(false); - EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", false); + box_switch->set_pressed(false); + _set_rotation(0.0, 0.0); + _store_rotation_metadata(); + EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_mesh", "quad"); } MaterialEditor::MaterialEditor() { @@ -213,7 +247,7 @@ MaterialEditor::MaterialEditor() { viewport = memnew(SubViewport); Ref<World3D> world_3d; world_3d.instantiate(); - viewport->set_world_3d(world_3d); //use own world + viewport->set_world_3d(world_3d); // Use own world. vc->add_child(viewport); viewport->set_disable_input(true); viewport->set_transparent_background(true); @@ -221,7 +255,7 @@ MaterialEditor::MaterialEditor() { camera = memnew(Camera3D); camera->set_transform(Transform3D(Basis(), Vector3(0, 0, 1.1))); - // Use low field of view so the sphere/box is fully encompassed within the preview, + // Use low field of view so the sphere/box/quad is fully encompassed within the preview, // without much distortion. camera->set_perspective(20, 0.1, 10); camera->make_current(); @@ -249,13 +283,19 @@ MaterialEditor::MaterialEditor() { box_instance = memnew(MeshInstance3D); rotation->add_child(box_instance); - box_instance->set_transform(Transform3D() * 0.25); + quad_instance = memnew(MeshInstance3D); + rotation->add_child(quad_instance); + sphere_instance->set_transform(Transform3D() * 0.375); + box_instance->set_transform(Transform3D() * 0.25); + quad_instance->set_transform(Transform3D() * 0.375); sphere_mesh.instantiate(); sphere_instance->set_mesh(sphere_mesh); box_mesh.instantiate(); box_instance->set_mesh(box_mesh); + quad_mesh.instantiate(); + quad_instance->set_mesh(quad_mesh); set_custom_minimum_size(Size2(1, 150) * EDSCALE); @@ -269,17 +309,21 @@ MaterialEditor::MaterialEditor() { sphere_switch = memnew(Button); sphere_switch->set_theme_type_variation("PreviewLightButton"); sphere_switch->set_toggle_mode(true); - sphere_switch->set_pressed(true); vb_shape->add_child(sphere_switch); sphere_switch->connect(SceneStringName(pressed), callable_mp(this, &MaterialEditor::_on_sphere_switch_pressed)); box_switch = memnew(Button); box_switch->set_theme_type_variation("PreviewLightButton"); box_switch->set_toggle_mode(true); - box_switch->set_pressed(false); vb_shape->add_child(box_switch); box_switch->connect(SceneStringName(pressed), callable_mp(this, &MaterialEditor::_on_box_switch_pressed)); + quad_switch = memnew(Button); + quad_switch->set_theme_type_variation("PreviewLightButton"); + quad_switch->set_toggle_mode(true); + vb_shape->add_child(quad_switch); + quad_switch->connect(SceneStringName(pressed), callable_mp(this, &MaterialEditor::_on_quad_switch_pressed)); + layout_3d->add_spacer(); VBoxContainer *vb_light = memnew(VBoxContainer); @@ -299,14 +343,23 @@ MaterialEditor::MaterialEditor() { vb_light->add_child(light_2_switch); light_2_switch->connect(SceneStringName(pressed), callable_mp(this, &MaterialEditor::_on_light_2_switch_pressed)); - if (EditorSettings::get_singleton()->get_project_metadata("inspector_options", "material_preview_on_sphere", true)) { + String shape = EditorSettings::get_singleton()->get_project_metadata("inspector_options", "material_preview_mesh", "sphere"); + if (shape == "sphere") { box_instance->hide(); + quad_instance->hide(); + sphere_switch->set_pressed_no_signal(true); + } else if (shape == "box") { + sphere_instance->hide(); + quad_instance->hide(); + box_switch->set_pressed_no_signal(true); } else { - box_instance->show(); sphere_instance->hide(); - box_switch->set_pressed(true); - sphere_switch->set_pressed(false); + box_instance->hide(); + quad_switch->set_pressed_no_signal(true); } + + Vector2 stored_rot = EditorSettings::get_singleton()->get_project_metadata("inspector_options", "material_preview_rotation", Vector2()); + _set_rotation(stored_rot.x, stored_rot.y); } /////////////////////// diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index 28c59d27db..c1b37a5831 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -62,6 +62,7 @@ class MaterialEditor : public Control { Node3D *rotation = nullptr; MeshInstance3D *sphere_instance = nullptr; MeshInstance3D *box_instance = nullptr; + MeshInstance3D *quad_instance = nullptr; DirectionalLight3D *light1 = nullptr; DirectionalLight3D *light2 = nullptr; Camera3D *camera = nullptr; @@ -69,6 +70,7 @@ class MaterialEditor : public Control { Ref<SphereMesh> sphere_mesh; Ref<BoxMesh> box_mesh; + Ref<QuadMesh> quad_mesh; VBoxContainer *layout_error = nullptr; Label *error_label = nullptr; @@ -80,6 +82,7 @@ class MaterialEditor : public Control { Button *sphere_switch = nullptr; Button *box_switch = nullptr; + Button *quad_switch = nullptr; Button *light_1_switch = nullptr; Button *light_2_switch = nullptr; @@ -88,6 +91,7 @@ class MaterialEditor : public Control { Ref<Texture2D> light_2_icon; Ref<Texture2D> sphere_icon; Ref<Texture2D> box_icon; + Ref<Texture2D> quad_icon; Ref<Texture2D> checkerboard; } theme_cache; @@ -95,11 +99,14 @@ class MaterialEditor : public Control { void _on_light_2_switch_pressed(); void _on_sphere_switch_pressed(); void _on_box_switch_pressed(); + void _on_quad_switch_pressed(); protected: virtual void _update_theme_item_cache() override; void _notification(int p_what); void gui_input(const Ref<InputEvent> &p_event) override; + void _set_rotation(real_t p_x_degrees, real_t p_y_degrees); + void _store_rotation_metadata(); void _update_rotation(); public: diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index c8eda600b8..645dd1784e 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -58,8 +58,8 @@ void MeshEditor::_update_theme_item_cache() { void MeshEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - light_1_switch->set_icon(theme_cache.light_1_icon); - light_2_switch->set_icon(theme_cache.light_2_icon); + light_1_switch->set_button_icon(theme_cache.light_1_icon); + light_2_switch->set_button_icon(theme_cache.light_2_icon); } break; } } diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp index eda6cdffb1..fdc222e64f 100644 --- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp @@ -40,6 +40,7 @@ #include "scene/3d/physics/collision_shape_3d.h" #include "scene/3d/physics/physics_body_3d.h" #include "scene/3d/physics/static_body_3d.h" +#include "scene/gui/aspect_ratio_container.h" #include "scene/gui/box_container.h" #include "scene/gui/dialogs.h" #include "scene/gui/menu_button.h" @@ -445,10 +446,43 @@ void MeshInstance3DEditor::_debug_uv_draw() { } debug_uv->set_clip_contents(true); - debug_uv->draw_rect(Rect2(Vector2(), debug_uv->get_size()), get_theme_color(SNAME("dark_color_3"), EditorStringName(Editor))); + debug_uv->draw_rect( + Rect2(Vector2(), debug_uv->get_size()), + get_theme_color(SNAME("dark_color_3"), EditorStringName(Editor))); + + // Draw an outline to represent the UV2's beginning and end area (useful on Black OLED theme). + // Top-left coordinate needs to be `(1, 1)` to prevent `clip_contents` from clipping the top and left lines. + debug_uv->draw_rect( + Rect2(Vector2(1, 1), debug_uv->get_size() - Vector2(1, 1)), + get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125), + false, + Math::round(EDSCALE)); + + for (int x = 1; x <= 7; x++) { + debug_uv->draw_line( + Vector2(debug_uv->get_size().x * 0.125 * x, 0), + Vector2(debug_uv->get_size().x * 0.125 * x, debug_uv->get_size().y), + get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125), + Math::round(EDSCALE)); + } + + for (int y = 1; y <= 7; y++) { + debug_uv->draw_line( + Vector2(0, debug_uv->get_size().y * 0.125 * y), + Vector2(debug_uv->get_size().x, debug_uv->get_size().y * 0.125 * y), + get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.125), + Math::round(EDSCALE)); + } + debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); + // Use a translucent color to allow overlapping triangles to be visible. - debug_uv->draw_multiline(uv_lines, get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.5)); + // Divide line width by the drawing scale set above, so that line width is consistent regardless of dialog size. + // Aspect ratio is preserved by the parent AspectRatioContainer, so we only need to check the X size which is always equal to Y. + debug_uv->draw_multiline( + uv_lines, + get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.5), + Math::round(EDSCALE) / debug_uv->get_size().x); } void MeshInstance3DEditor::_create_navigation_mesh() { @@ -527,7 +561,7 @@ void MeshInstance3DEditor::_create_outline_mesh() { void MeshInstance3DEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - options->set_icon(get_editor_theme_icon(SNAME("MeshInstance3D"))); + options->set_button_icon(get_editor_theme_icon(SNAME("MeshInstance3D"))); } break; } } @@ -613,10 +647,14 @@ MeshInstance3DEditor::MeshInstance3DEditor() { debug_uv_dialog = memnew(AcceptDialog); debug_uv_dialog->set_title(TTR("UV Channel Debug")); add_child(debug_uv_dialog); + + debug_uv_arc = memnew(AspectRatioContainer); + debug_uv_dialog->add_child(debug_uv_arc); + debug_uv = memnew(Control); debug_uv->set_custom_minimum_size(Size2(600, 600) * EDSCALE); debug_uv->connect(SceneStringName(draw), callable_mp(this, &MeshInstance3DEditor::_debug_uv_draw)); - debug_uv_dialog->add_child(debug_uv); + debug_uv_arc->add_child(debug_uv); navigation_mesh_dialog = memnew(ConfirmationDialog); navigation_mesh_dialog->set_title(TTR("Create NavigationMesh")); diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.h b/editor/plugins/mesh_instance_3d_editor_plugin.h index c982df9c5f..569ecd4fff 100644 --- a/editor/plugins/mesh_instance_3d_editor_plugin.h +++ b/editor/plugins/mesh_instance_3d_editor_plugin.h @@ -36,6 +36,7 @@ #include "scene/gui/option_button.h" class AcceptDialog; +class AspectRatioContainer; class ConfirmationDialog; class MenuButton; class SpinBox; @@ -79,6 +80,7 @@ class MeshInstance3DEditor : public Control { AcceptDialog *err_dialog = nullptr; AcceptDialog *debug_uv_dialog = nullptr; + AspectRatioContainer *debug_uv_arc = nullptr; Control *debug_uv = nullptr; Vector<Vector2> uv_lines; diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index d6650bd08f..6f79ab0529 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -260,7 +260,7 @@ MeshLibraryEditor::MeshLibraryEditor() { Node3DEditor::get_singleton()->add_control_to_menu_panel(menu); menu->set_position(Point2(1, 1)); menu->set_text(TTR("MeshLibrary")); - menu->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MeshLibrary"), EditorStringName(EditorIcons))); + menu->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MeshLibrary"), EditorStringName(EditorIcons))); menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM); menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM); menu->get_popup()->add_separator(); diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index 729ceccd25..9e686dd689 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -272,7 +272,7 @@ MultiMeshEditor::MultiMeshEditor() { Node3DEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text("MultiMesh"); - options->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MultiMeshInstance3D"), EditorStringName(EditorIcons))); + options->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MultiMeshInstance3D"), EditorStringName(EditorIcons))); options->get_popup()->add_item(TTR("Populate Surface")); options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &MultiMeshEditor::_menu_option)); diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp index 9629a673e9..efa98d8e4d 100644 --- a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp +++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp @@ -30,144 +30,506 @@ #include "navigation_obstacle_3d_editor_plugin.h" -#include "canvas_item_editor_plugin.h" -#include "core/input/input.h" -#include "core/io/file_access.h" +#include "core/config/project_settings.h" #include "core/math/geometry_2d.h" -#include "core/os/keyboard.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" -#include "node_3d_editor_plugin.h" -#include "scene/3d/camera_3d.h" -#include "scene/gui/separator.h" +#include "editor/plugins/node_3d_editor_plugin.h" +#include "scene/3d/navigation_obstacle_3d.h" +#include "scene/gui/button.h" +#include "scene/gui/dialogs.h" +#include "servers/navigation_server_3d.h" + +bool NavigationObstacle3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to<NavigationObstacle3D>(p_spatial) != nullptr; +} + +String NavigationObstacle3DGizmoPlugin::get_gizmo_name() const { + return "NavigationObstacle3D"; +} + +void NavigationObstacle3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + p_gizmo->clear(); + + if (!p_gizmo->is_selected() && get_state() == HIDDEN) { + return; + } + + NavigationObstacle3D *obstacle = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); + + if (!obstacle) { + return; + } + + const Vector<Vector3> &vertices = obstacle->get_vertices(); + if (vertices.is_empty()) { + return; + } + + float height = obstacle->get_height(); + + const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle->get_global_rotation().y, obstacle->get_global_basis().get_scale().abs().maxf(0.001)); + const Basis gbi = obstacle->get_global_basis().inverse(); + const Basis safe_global_basis = gbi * safe_basis; + const int vertex_count = vertices.size(); + + Vector<Vector3> lines_mesh_vertices; + lines_mesh_vertices.resize(vertex_count * 8); + Vector3 *lines_mesh_vertices_ptrw = lines_mesh_vertices.ptrw(); + + int vertex_index = 0; + + for (int i = 0; i < vertex_count; i++) { + Vector3 point = vertices[i]; + Vector3 next_point = vertices[(i + 1) % vertex_count]; + + Vector3 direction = safe_basis.xform(next_point.direction_to(point)); + Vector3 arrow_dir = direction.cross(Vector3(0.0, 1.0, 0.0)); + Vector3 edge_middle = point + ((next_point - point) * 0.5); + + // Ensure vector stays perpendicular even when scaled non-uniformly. + lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(edge_middle); + lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(edge_middle) + gbi.xform(arrow_dir) * 0.5; + + lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(point); + lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(next_point); + + lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(point.x, height, point.z)); + lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(next_point.x, height, next_point.z)); + + lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(point); + lines_mesh_vertices_ptrw[vertex_index++] = safe_global_basis.xform(Vector3(point.x, height, point.z)); + } + + Vector<Vector2> polygon_2d_vertices; + polygon_2d_vertices.resize(vertex_count); + for (int i = 0; i < vertex_count; i++) { + const Vector3 &vert = vertices[i]; + polygon_2d_vertices.write[i] = Vector2(vert.x, vert.z); + } + Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(polygon_2d_vertices); + + NavigationServer3D *ns3d = NavigationServer3D::get_singleton(); + + if (triangulated_polygon_2d_indices.is_empty()) { + p_gizmo->add_lines(lines_mesh_vertices, ns3d->get_debug_navigation_avoidance_static_obstacle_pushin_edge_material()); + } else { + p_gizmo->add_lines(lines_mesh_vertices, ns3d->get_debug_navigation_avoidance_static_obstacle_pushout_edge_material()); + } + p_gizmo->add_collision_segments(lines_mesh_vertices); + + if (p_gizmo->is_selected()) { + NavigationObstacle3DEditorPlugin::singleton->redraw(); + } +} + +bool NavigationObstacle3DGizmoPlugin::can_be_hidden() const { + return true; +} + +int NavigationObstacle3DGizmoPlugin::get_priority() const { + return -1; +} + +int NavigationObstacle3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const { + if (NavigationObstacle3DEditorPlugin::singleton->get_mode() != 1) { // MODE_EDIT + return -1; + } + + NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); + ERR_FAIL_NULL_V(obstacle_node, -1); + + const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); + const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); + const Vector<Vector3> &vertices = obstacle_node->get_vertices(); + + for (int idx = 0; idx < vertices.size(); ++idx) { + Vector3 pos = gt.xform(vertices[idx]); + if (p_camera->unproject_position(pos).distance_to(p_point) < 20) { + return idx; + } + } + + return -1; +} + +Vector<int> NavigationObstacle3DGizmoPlugin::subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const { + Vector<int> contained_points; + if (NavigationObstacle3DEditorPlugin::singleton->get_mode() != 1) { // MODE_EDIT + return contained_points; + } + + NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); + ERR_FAIL_NULL_V(obstacle_node, contained_points); + + const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); + const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); + const Vector<Vector3> &vertices = obstacle_node->get_vertices(); + + for (int idx = 0; idx < vertices.size(); ++idx) { + Vector3 pos = gt.xform(vertices[idx]); + bool is_contained_in_frustum = true; + for (int i = 0; i < p_frustum.size(); ++i) { + if (p_frustum[i].distance_to(pos) > 0) { + is_contained_in_frustum = false; + break; + } + } + + if (is_contained_in_frustum) { + contained_points.push_back(idx); + } + } + + return contained_points; +} + +Transform3D NavigationObstacle3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const { + NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); + ERR_FAIL_NULL_V(obstacle_node, Transform3D()); + + const Vector<Vector3> &vertices = obstacle_node->get_vertices(); + ERR_FAIL_INDEX_V(p_id, vertices.size(), Transform3D()); + + const Basis safe_basis_inverse = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001)).inverse(); + Transform3D subgizmo_transform = Transform3D(Basis(), safe_basis_inverse.xform(vertices[p_id])); + return subgizmo_transform; +} + +void NavigationObstacle3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) { + NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); + ERR_FAIL_NULL(obstacle_node); + + const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001)); + Vector3 new_vertex_pos = p_transform.origin; + + Vector<Vector3> vertices = obstacle_node->get_vertices(); + ERR_FAIL_INDEX(p_id, vertices.size()); + + Vector3 vertex = safe_basis.xform(new_vertex_pos); + vertex.y = 0.0; + vertices.write[p_id] = vertex; -void NavigationObstacle3DEditor::_notification(int p_what) { + obstacle_node->set_vertices(vertices); +} + +void NavigationObstacle3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) { + NavigationObstacle3D *obstacle_node = Object::cast_to<NavigationObstacle3D>(p_gizmo->get_node_3d()); + ERR_FAIL_NULL(obstacle_node); + + const Basis safe_basis = Basis(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y, obstacle_node->get_global_basis().get_scale().abs().maxf(0.001)); + + Vector<Vector3> vertices = obstacle_node->get_vertices(); + Vector<Vector3> restore_vertices = vertices; + + for (int i = 0; i < p_ids.size(); ++i) { + const int idx = p_ids[i]; + Vector3 vertex = safe_basis.xform(p_restore[i].origin); + vertex.y = 0.0; + restore_vertices.write[idx] = vertex; + } + + if (p_cancel) { + obstacle_node->set_vertices(restore_vertices); + return; + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Set Obstacle Vertices")); + undo_redo->add_do_method(obstacle_node, "set_vertices", vertices); + undo_redo->add_undo_method(obstacle_node, "set_vertices", restore_vertices); + undo_redo->commit_action(); +} + +NavigationObstacle3DGizmoPlugin::NavigationObstacle3DGizmoPlugin() { + current_state = VISIBLE; +} + +void NavigationObstacle3DEditorPlugin::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + _update_theme(); + } break; + case NOTIFICATION_READY: { - button_create->set_icon(get_editor_theme_icon(SNAME("Edit"))); - button_edit->set_icon(get_editor_theme_icon(SNAME("MovePoint"))); + _update_theme(); button_edit->set_pressed(true); - get_tree()->connect("node_removed", callable_mp(this, &NavigationObstacle3DEditor::_node_removed)); + get_tree()->connect("node_removed", callable_mp(this, &NavigationObstacle3DEditorPlugin::_node_removed)); + EditorNode::get_singleton()->get_gui_base()->connect(SceneStringName(theme_changed), callable_mp(this, &NavigationObstacle3DEditorPlugin::_update_theme)); + } break; + case NOTIFICATION_EXIT_TREE: { + get_tree()->disconnect("node_removed", callable_mp(this, &NavigationObstacle3DEditorPlugin::_node_removed)); + EditorNode::get_singleton()->get_gui_base()->disconnect(SceneStringName(theme_changed), callable_mp(this, &NavigationObstacle3DEditorPlugin::_update_theme)); } break; } } -void NavigationObstacle3DEditor::_node_removed(Node *p_node) { - if (p_node == obstacle_node) { - obstacle_node = nullptr; - if (point_lines_meshinstance->get_parent() == p_node) { - p_node->remove_child(point_lines_meshinstance); +void NavigationObstacle3DEditorPlugin::edit(Object *p_object) { + obstacle_node = Object::cast_to<NavigationObstacle3D>(p_object); + + RenderingServer *rs = RenderingServer::get_singleton(); + + if (obstacle_node) { + if (obstacle_node->get_vertices().is_empty()) { + set_mode(MODE_CREATE); + } else { + set_mode(MODE_EDIT); } - hide(); + wip_vertices.clear(); + wip_active = false; + edited_point = -1; + + rs->instance_set_scenario(point_lines_instance_rid, obstacle_node->get_world_3d()->get_scenario()); + rs->instance_set_scenario(point_handles_instance_rid, obstacle_node->get_world_3d()->get_scenario()); + + redraw(); + + } else { + obstacle_node = nullptr; + + rs->mesh_clear(point_lines_mesh_rid); + rs->mesh_clear(point_handle_mesh_rid); + rs->instance_set_scenario(point_lines_instance_rid, RID()); + rs->instance_set_scenario(point_handles_instance_rid, RID()); } } -void NavigationObstacle3DEditor::_menu_option(int p_option) { - switch (p_option) { - case MODE_CREATE: { - mode = MODE_CREATE; - button_create->set_pressed(true); - button_edit->set_pressed(false); - } break; - case MODE_EDIT: { - mode = MODE_EDIT; - button_create->set_pressed(false); - button_edit->set_pressed(true); - } break; +bool NavigationObstacle3DEditorPlugin::handles(Object *p_object) const { + return Object::cast_to<NavigationObstacle3D>(p_object); +} + +void NavigationObstacle3DEditorPlugin::make_visible(bool p_visible) { + if (p_visible) { + obstacle_editor->show(); + } else { + obstacle_editor->hide(); + edit(nullptr); } } -void NavigationObstacle3DEditor::_wip_close() { - ERR_FAIL_NULL_MSG(obstacle_node, "Edited NavigationObstacle3D is not valid."); +void NavigationObstacle3DEditorPlugin::action_flip_vertices() { + if (!obstacle_node) { + return; + } + + Vector<Vector3> flipped_vertices = obstacle_node->get_vertices(); + flipped_vertices.reverse(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Set NavigationObstacle3D Vertices")); + undo_redo->create_action(TTR("Edit Obstacle (Flip Winding)")); + undo_redo->add_do_method(obstacle_node, "set_vertices", flipped_vertices); undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); + undo_redo->commit_action(); - PackedVector3Array polygon_3d_vertices; - Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(wip); + obstacle_node->update_gizmos(); +} - if (!triangulated_polygon_2d_indices.is_empty()) { - polygon_3d_vertices.resize(wip.size()); - Vector3 *polygon_3d_vertices_ptr = polygon_3d_vertices.ptrw(); - for (int i = 0; i < wip.size(); i++) { - const Vector2 &vert = wip[i]; - polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y); - } +void NavigationObstacle3DEditorPlugin::action_clear_vertices() { + if (!obstacle_node) { + return; } - undo_redo->add_do_method(obstacle_node, "set_vertices", polygon_3d_vertices); - undo_redo->add_do_method(this, "_polygon_draw"); - undo_redo->add_undo_method(this, "_polygon_draw"); - wip.clear(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Edit Obstacle (Clear Vertices)")); + undo_redo->add_do_method(obstacle_node, "set_vertices", Vector<Vector3>()); + undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); + undo_redo->commit_action(); + + obstacle_node->update_gizmos(); + edit(obstacle_node); +} + +void NavigationObstacle3DEditorPlugin::_update_theme() { + button_create->set_tooltip_text(TTR("Add Vertex")); + button_edit->set_tooltip_text(TTR("Edit Vertex")); + button_delete->set_tooltip_text(TTR("Delete Vertex")); + button_flip->set_tooltip_text(TTR("Flip Winding")); + button_clear->set_tooltip_text(TTR("Clear Vertices")); + button_create->set_button_icon(button_create->get_editor_theme_icon(SNAME("CurveCreate"))); + button_edit->set_button_icon(button_edit->get_editor_theme_icon(SNAME("CurveEdit"))); + button_delete->set_button_icon(button_delete->get_editor_theme_icon(SNAME("CurveDelete"))); + button_flip->set_button_icon(button_flip->get_editor_theme_icon(SNAME("FlipWinding"))); + button_clear->set_button_icon(button_clear->get_editor_theme_icon(SNAME("Clear"))); +} + +void NavigationObstacle3DEditorPlugin::_node_removed(Node *p_node) { + if (obstacle_node == p_node) { + obstacle_node = nullptr; + + RenderingServer *rs = RenderingServer::get_singleton(); + rs->mesh_clear(point_lines_mesh_rid); + rs->mesh_clear(point_handle_mesh_rid); + + obstacle_editor->hide(); + } +} + +void NavigationObstacle3DEditorPlugin::set_mode(int p_option) { + if (p_option == NavigationObstacle3DEditorPlugin::ACTION_FLIP) { + button_flip->set_pressed(false); + action_flip_vertices(); + return; + } + + if (p_option == NavigationObstacle3DEditorPlugin::ACTION_CLEAR) { + button_clear->set_pressed(false); + button_clear_dialog->reset_size(); + button_clear_dialog->popup_centered(); + return; + } + + mode = p_option; + + button_create->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_CREATE); + button_edit->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_EDIT); + button_delete->set_pressed(p_option == NavigationObstacle3DEditorPlugin::MODE_DELETE); + button_flip->set_pressed(false); + button_clear->set_pressed(false); +} + +void NavigationObstacle3DEditorPlugin::_wip_cancel() { + wip_vertices.clear(); wip_active = false; - mode = MODE_EDIT; - button_edit->set_pressed(true); - button_create->set_pressed(false); + edited_point = -1; - undo_redo->commit_action(); + + redraw(); } -EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) { +void NavigationObstacle3DEditorPlugin::_wip_close() { + ERR_FAIL_NULL_MSG(obstacle_node, "Edited NavigationObstacle3D is not valid."); + + Vector<Vector2> wip_2d_vertices; + wip_2d_vertices.resize(wip_vertices.size()); + for (int i = 0; i < wip_vertices.size(); i++) { + const Vector3 &vert = wip_vertices[i]; + wip_2d_vertices.write[i] = Vector2(vert.x, vert.z); + } + Vector<int> triangulated_polygon_2d_indices = Geometry2D::triangulate_polygon(wip_2d_vertices); + + if (!triangulated_polygon_2d_indices.is_empty()) { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Set Obstacle Vertices")); + undo_redo->add_do_method(obstacle_node, "set_vertices", wip_vertices); + undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); + undo_redo->commit_action(); + + wip_vertices.clear(); + wip_active = false; + //mode = MODE_EDIT; + NavigationObstacle3DEditorPlugin::singleton->set_mode(NavigationObstacle3DEditorPlugin::MODE_EDIT); + button_edit->set_pressed(true); + button_create->set_pressed(false); + edited_point = -1; + } +} + +EditorPlugin::AfterGUIInput NavigationObstacle3DEditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) { if (!obstacle_node) { return EditorPlugin::AFTER_GUI_INPUT_PASS; } - Transform3D gt = obstacle_node->get_global_transform(); - Transform3D gi = gt.affine_inverse(); - Plane p(Vector3(0.0, 1.0, 0.0), gt.origin); + if (!obstacle_node->is_visible_in_tree()) { + return EditorPlugin::AFTER_GUI_INPUT_PASS; + } + + Ref<InputEventMouse> mouse_event = p_event; + + if (mouse_event.is_null()) { + return EditorPlugin::AFTER_GUI_INPUT_PASS; + } Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - Vector2 gpoint = mb->get_position(); - Vector3 ray_from = p_camera->project_ray_origin(gpoint); - Vector3 ray_dir = p_camera->project_ray_normal(gpoint); + Vector2 mouse_position = mb->get_position(); + Vector3 ray_from = p_camera->project_ray_origin(mouse_position); + Vector3 ray_dir = p_camera->project_ray_normal(mouse_position); + + const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); + const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); + Transform3D gi = gt.affine_inverse(); + Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin); Vector3 spoint; - if (!p.intersects_ray(ray_from, ray_dir, &spoint)) { + if (!projection_plane.intersects_ray(ray_from, ray_dir, &spoint)) { return EditorPlugin::AFTER_GUI_INPUT_PASS; } spoint = gi.xform(spoint); - Vector2 cpoint(spoint.x, spoint.z); - - //DO NOT snap here, it's confusing in 3D for adding points. - //Let the snap happen when the point is being moved, instead. - //cpoint = CanvasItemEditor::get_singleton()->snap_point(cpoint); - - PackedVector2Array poly = _get_polygon(); + Vector3 cpoint = Vector3(spoint.x, 0.0, spoint.z); + Vector<Vector3> obstacle_vertices = obstacle_node->get_vertices(); - //first check if a point is to be added (segment split) real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius"); switch (mode) { case MODE_CREATE: { if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { + if (obstacle_vertices.size() >= 3) { + int closest_idx = -1; + Vector2 closest_edge_point; + real_t closest_dist = 1e10; + for (int i = 0; i < obstacle_vertices.size(); i++) { + Vector2 points[2] = { + p_camera->unproject_position(gt.xform(obstacle_vertices[i])), + p_camera->unproject_position(gt.xform(obstacle_vertices[(i + 1) % obstacle_vertices.size()])) + }; + + Vector2 cp = Geometry2D::get_closest_point_to_segment(mouse_position, points); + if (cp.distance_squared_to(points[0]) < grab_threshold || cp.distance_squared_to(points[1]) < grab_threshold) { + continue; // Skip edge as clicked point is too close to existing vertex. + } + + real_t d = cp.distance_to(mouse_position); + if (d < closest_dist && d < grab_threshold) { + closest_dist = d; + closest_edge_point = cp; + closest_idx = i; + } + } + if (closest_idx >= 0) { + edited_point = -1; + Vector3 _ray_from = p_camera->project_ray_origin(closest_edge_point); + Vector3 _ray_dir = p_camera->project_ray_normal(closest_edge_point); + Vector3 edge_intersection_point; + if (projection_plane.intersects_ray(_ray_from, _ray_dir, &edge_intersection_point)) { + edge_intersection_point = gi.xform(edge_intersection_point); + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Edit Obstacle (Add Vertex)")); + undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_vertices); + obstacle_vertices.insert(closest_idx + 1, edge_intersection_point); + undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices); + undo_redo->commit_action(); + redraw(); + return EditorPlugin::AFTER_GUI_INPUT_STOP; + } + } + } if (!wip_active) { - wip.clear(); - wip.push_back(cpoint); + wip_vertices.clear(); + wip_vertices.push_back(cpoint); wip_active = true; edited_point_pos = cpoint; snap_ignore = false; - _polygon_draw(); + redraw(); edited_point = 1; return EditorPlugin::AFTER_GUI_INPUT_STOP; } else { - if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, 0.0, wip[0].y))).distance_to(gpoint) < grab_threshold) { - //wip closed + if (wip_vertices.size() > 1 && p_camera->unproject_position(gt.xform(wip_vertices[0])).distance_to(mouse_position) < grab_threshold) { _wip_close(); return EditorPlugin::AFTER_GUI_INPUT_STOP; } else { - wip.push_back(cpoint); - edited_point = wip.size(); + wip_vertices.push_back(cpoint); + edited_point = wip_vertices.size(); snap_ignore = false; - _polygon_draw(); + redraw(); return EditorPlugin::AFTER_GUI_INPUT_STOP; } } @@ -181,13 +543,11 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { if (mb->is_ctrl_pressed()) { - if (poly.size() < 3) { + if (obstacle_vertices.size() < 3) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Edit Vertices")); + undo_redo->create_action(TTR("Edit Obstacle (Add Vertex)")); undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); - poly.push_back(cpoint); - undo_redo->add_do_method(this, "_polygon_draw"); - undo_redo->add_undo_method(this, "_polygon_draw"); + obstacle_vertices.push_back(cpoint); undo_redo->commit_action(); return EditorPlugin::AFTER_GUI_INPUT_STOP; } @@ -196,18 +556,18 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam int closest_idx = -1; Vector2 closest_pos; real_t closest_dist = 1e10; - for (int i = 0; i < poly.size(); i++) { + for (int i = 0; i < obstacle_vertices.size(); i++) { Vector2 points[2] = { - p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))), - p_camera->unproject_position(gt.xform(Vector3(poly[(i + 1) % poly.size()].x, 0.0, poly[(i + 1) % poly.size()].y))) + p_camera->unproject_position(gt.xform(obstacle_vertices[i])), + p_camera->unproject_position(gt.xform(obstacle_vertices[(i + 1) % obstacle_vertices.size()])) }; - Vector2 cp = Geometry2D::get_closest_point_to_segment(gpoint, points); + Vector2 cp = Geometry2D::get_closest_point_to_segment(mouse_position, points); if (cp.distance_squared_to(points[0]) < CMP_EPSILON2 || cp.distance_squared_to(points[1]) < CMP_EPSILON2) { continue; //not valid to reuse point } - real_t d = cp.distance_to(gpoint); + real_t d = cp.distance_to(mouse_position); if (d < closest_dist && d < grab_threshold) { closest_dist = d; closest_pos = cp; @@ -216,26 +576,24 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam } if (closest_idx >= 0) { - pre_move_edit = poly; - poly.insert(closest_idx + 1, cpoint); + pre_move_edit = obstacle_vertices; + obstacle_vertices.insert(closest_idx + 1, cpoint); edited_point = closest_idx + 1; edited_point_pos = cpoint; - _set_polygon(poly); - _polygon_draw(); + obstacle_node->set_vertices(obstacle_vertices); + redraw(); snap_ignore = true; return EditorPlugin::AFTER_GUI_INPUT_STOP; } } else { - //look for points to move - int closest_idx = -1; Vector2 closest_pos; real_t closest_dist = 1e10; - for (int i = 0; i < poly.size(); i++) { - Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))); + for (int i = 0; i < obstacle_vertices.size(); i++) { + Vector2 cp = p_camera->unproject_position(gt.xform(obstacle_vertices[i])); - real_t d = cp.distance_to(gpoint); + real_t d = cp.distance_to(mouse_position); if (d < closest_dist && d < grab_threshold) { closest_dist = d; closest_pos = cp; @@ -244,10 +602,10 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam } if (closest_idx >= 0) { - pre_move_edit = poly; + pre_move_edit = obstacle_vertices; edited_point = closest_idx; - edited_point_pos = poly[closest_idx]; - _polygon_draw(); + edited_point_pos = obstacle_vertices[closest_idx]; + redraw(); snap_ignore = false; return EditorPlugin::AFTER_GUI_INPUT_STOP; } @@ -256,16 +614,13 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam snap_ignore = false; if (edited_point != -1) { - //apply + ERR_FAIL_INDEX_V(edited_point, obstacle_vertices.size(), EditorPlugin::AFTER_GUI_INPUT_PASS); + obstacle_vertices.write[edited_point] = edited_point_pos; - ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS); - poly.write[edited_point] = edited_point_pos; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Edit Poly")); - //undo_redo->add_do_method(obj, "set_polygon", poly); - //undo_redo->add_undo_method(obj, "set_polygon", pre_move_edit); - undo_redo->add_do_method(this, "_polygon_draw"); - undo_redo->add_undo_method(this, "_polygon_draw"); + undo_redo->create_action(TTR("Edit Obstacle (Move Vertex)")); + undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_node->get_vertices()); + undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices); undo_redo->commit_action(); edited_point = -1; @@ -273,30 +628,31 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam } } } - if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && edited_point == -1) { + + } break; + + case MODE_DELETE: { + if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) { int closest_idx = -1; - Vector2 closest_pos; real_t closest_dist = 1e10; - for (int i = 0; i < poly.size(); i++) { - Vector2 cp = p_camera->unproject_position(gt.xform(Vector3(poly[i].x, 0.0, poly[i].y))); - - real_t d = cp.distance_to(gpoint); + for (int i = 0; i < obstacle_vertices.size(); i++) { + Vector2 point = p_camera->unproject_position(gt.xform(obstacle_vertices[i])); + real_t d = point.distance_to(mouse_position); if (d < closest_dist && d < grab_threshold) { closest_dist = d; - closest_pos = cp; closest_idx = i; } } if (closest_idx >= 0) { + edited_point = -1; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Edit Poly (Remove Point)")); - //undo_redo->add_undo_method(obj, "set_polygon", poly); - poly.remove_at(closest_idx); - //undo_redo->add_do_method(obj, "set_polygon", poly); - undo_redo->add_do_method(this, "_polygon_draw"); - undo_redo->add_undo_method(this, "_polygon_draw"); + undo_redo->create_action(TTR("Edit Obstacle (Remove Vertex)")); + undo_redo->add_undo_method(obstacle_node, "set_vertices", obstacle_vertices); + obstacle_vertices.remove_at(closest_idx); + undo_redo->add_do_method(obstacle_node, "set_vertices", obstacle_vertices); undo_redo->commit_action(); + redraw(); return EditorPlugin::AFTER_GUI_INPUT_STOP; } } @@ -309,20 +665,25 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam if (mm.is_valid()) { if (edited_point != -1 && (wip_active || mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) { - Vector2 gpoint = mm->get_position(); + Vector2 mouse_position = mm->get_position(); - Vector3 ray_from = p_camera->project_ray_origin(gpoint); - Vector3 ray_dir = p_camera->project_ray_normal(gpoint); + Vector3 ray_from = p_camera->project_ray_origin(mouse_position); + Vector3 ray_dir = p_camera->project_ray_normal(mouse_position); - Vector3 spoint; + const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); + const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); + Transform3D gi = gt.affine_inverse(); + Plane projection_plane(Vector3(0.0, 1.0, 0.0), gt.origin); - if (!p.intersects_ray(ray_from, ray_dir, &spoint)) { + Vector3 intersection_point; + + if (!projection_plane.intersects_ray(ray_from, ray_dir, &intersection_point)) { return EditorPlugin::AFTER_GUI_INPUT_PASS; } - spoint = gi.xform(spoint); + intersection_point = gi.xform(intersection_point); - Vector2 cpoint(spoint.x, spoint.z); + Vector2 cpoint(intersection_point.x, intersection_point.z); if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CTRL)) { snap_ignore = false; @@ -331,272 +692,219 @@ EditorPlugin::AfterGUIInput NavigationObstacle3DEditor::forward_3d_gui_input(Cam if (!snap_ignore && Node3DEditor::get_singleton()->is_snap_enabled()) { cpoint = cpoint.snappedf(Node3DEditor::get_singleton()->get_translate_snap()); } - edited_point_pos = cpoint; + edited_point_pos = Vector3(cpoint.x, 0.0, cpoint.y); - _polygon_draw(); + redraw(); } } - return EditorPlugin::AFTER_GUI_INPUT_PASS; -} + Ref<InputEventKey> k = p_event; -PackedVector2Array NavigationObstacle3DEditor::_get_polygon() { - ERR_FAIL_NULL_V_MSG(obstacle_node, PackedVector2Array(), "Edited object is not valid."); - return PackedVector2Array(obstacle_node->call("get_polygon")); -} + if (k.is_valid() && k->is_pressed()) { + if (wip_active && k->get_keycode() == Key::ENTER) { + _wip_close(); + } else if (wip_active && k->get_keycode() == Key::ESCAPE) { + _wip_cancel(); + } + } -void NavigationObstacle3DEditor::_set_polygon(const PackedVector2Array &p_poly) { - ERR_FAIL_NULL_MSG(obstacle_node, "Edited object is not valid."); - obstacle_node->call("set_polygon", p_poly); + return EditorPlugin::AFTER_GUI_INPUT_PASS; } -void NavigationObstacle3DEditor::_polygon_draw() { +void NavigationObstacle3DEditorPlugin::redraw() { if (!obstacle_node) { return; } + RenderingServer *rs = RenderingServer::get_singleton(); + + rs->mesh_clear(point_lines_mesh_rid); + rs->mesh_clear(point_handle_mesh_rid); + + if (!obstacle_node->is_visible_in_tree()) { + return; + } - PackedVector2Array poly; - PackedVector3Array polygon_3d_vertices; + Vector<Vector3> edited_vertices; if (wip_active) { - poly = wip; + edited_vertices = wip_vertices; } else { - poly = _get_polygon(); + edited_vertices = obstacle_node->get_vertices(); } - polygon_3d_vertices.resize(poly.size()); - Vector3 *polygon_3d_vertices_ptr = polygon_3d_vertices.ptrw(); - for (int i = 0; i < poly.size(); i++) { - const Vector2 &vert = poly[i]; - polygon_3d_vertices_ptr[i] = Vector3(vert.x, 0.0, vert.y); + if (edited_vertices.is_empty()) { + return; } - point_handle_mesh->clear_surfaces(); - point_lines_mesh->clear_surfaces(); - point_lines_meshinstance->set_material_override(line_material); - point_lines_mesh->surface_begin(Mesh::PRIMITIVE_LINES); + Array point_lines_mesh_array; + point_lines_mesh_array.resize(Mesh::ARRAY_MAX); - Rect2 rect; + Vector<Vector3> point_lines_mesh_vertices; + point_lines_mesh_vertices.resize(edited_vertices.size() * 2); + Vector3 *point_lines_mesh_vertices_ptr = point_lines_mesh_vertices.ptrw(); - for (int i = 0; i < poly.size(); i++) { - Vector2 p, p2; - if (i == edited_point) { - p = edited_point_pos; - } else { - p = poly[i]; - } + int vertex_index = 0; - if ((wip_active && i == poly.size() - 1) || (((i + 1) % poly.size()) == edited_point)) { - p2 = edited_point_pos; + for (int i = 0; i < edited_vertices.size(); i++) { + Vector3 point, next_point; + if (i == edited_point) { + point = edited_point_pos; } else { - p2 = poly[(i + 1) % poly.size()]; + point = edited_vertices[i]; } - if (i == 0) { - rect.position = p; + if ((wip_active && i == edited_vertices.size() - 1) || (((i + 1) % edited_vertices.size()) == edited_point)) { + next_point = edited_point_pos; } else { - rect.expand_to(p); + next_point = edited_vertices[(i + 1) % edited_vertices.size()]; } - Vector3 point = Vector3(p.x, 0.0, p.y); - Vector3 next_point = Vector3(p2.x, 0.0, p2.y); - - point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8)); - point_lines_mesh->surface_add_vertex(point); - point_lines_mesh->surface_set_color(Color(1, 0.3, 0.1, 0.8)); - point_lines_mesh->surface_add_vertex(next_point); - - //Color col=Color(1,0.3,0.1,0.8); - //vpc->draw_line(point,next_point,col,2); - //vpc->draw_texture(handle,point-handle->get_size()*0.5); - } - - rect = rect.grow(1); - - AABB r; - r.position.x = rect.position.x; - r.position.y = 0.0; - r.position.z = rect.position.y; - r.size.x = rect.size.x; - r.size.y = 0; - r.size.z = rect.size.y; - - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0.3, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0.0, 0.3, 0)); - - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) - Vector3(0.3, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(r.size.x, 0, 0) + Vector3(0, 0.3, 0)); - - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) - Vector3(0, 0.3, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + Vector3(0, r.size.y, 0) + Vector3(0.3, 0, 0)); - - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + r.size); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.3, 0, 0)); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + r.size); - point_lines_mesh->surface_set_color(Color(0.8, 0.8, 0.8, 0.2)); - point_lines_mesh->surface_add_vertex(r.position + r.size - Vector3(0.0, 0.3, 0)); - - point_lines_mesh->surface_end(); - - if (poly.size() == 0) { - return; + point_lines_mesh_vertices_ptr[vertex_index++] = point; + point_lines_mesh_vertices_ptr[vertex_index++] = next_point; } + point_lines_mesh_array[Mesh::ARRAY_VERTEX] = point_lines_mesh_vertices; + + rs->mesh_add_surface_from_arrays(point_lines_mesh_rid, RS::PRIMITIVE_LINES, point_lines_mesh_array); + rs->instance_set_surface_override_material(point_lines_instance_rid, 0, line_material->get_rid()); + const Vector3 safe_scale = obstacle_node->get_global_basis().get_scale().abs().maxf(0.001); + const Transform3D gt = Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle_node->get_global_rotation().y), obstacle_node->get_global_position()); + rs->instance_set_transform(point_lines_instance_rid, gt); + Array point_handle_mesh_array; point_handle_mesh_array.resize(Mesh::ARRAY_MAX); Vector<Vector3> point_handle_mesh_vertices; - point_handle_mesh_vertices.resize(poly.size()); + point_handle_mesh_vertices.resize(edited_vertices.size()); Vector3 *point_handle_mesh_vertices_ptr = point_handle_mesh_vertices.ptrw(); - for (int i = 0; i < poly.size(); i++) { - Vector2 point_2d; - Vector2 p2; + for (int i = 0; i < edited_vertices.size(); i++) { + Vector3 point_handle_3d; if (i == edited_point) { - point_2d = edited_point_pos; + point_handle_3d = edited_point_pos; } else { - point_2d = poly[i]; + point_handle_3d = edited_vertices[i]; } - Vector3 point_handle_3d = Vector3(point_2d.x, 0.0, point_2d.y); point_handle_mesh_vertices_ptr[i] = point_handle_3d; } point_handle_mesh_array[Mesh::ARRAY_VERTEX] = point_handle_mesh_vertices; - point_handle_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_POINTS, point_handle_mesh_array); - point_handle_mesh->surface_set_material(0, handle_material); -} - -void NavigationObstacle3DEditor::edit(Node *p_node) { - obstacle_node = Object::cast_to<NavigationObstacle3D>(p_node); - if (obstacle_node) { - //Enable the pencil tool if the polygon is empty - if (_get_polygon().is_empty()) { - _menu_option(MODE_CREATE); - } - wip.clear(); - wip_active = false; - edited_point = -1; - if (point_lines_meshinstance->get_parent()) { - point_lines_meshinstance->reparent(p_node, false); - } else { - p_node->add_child(point_lines_meshinstance); - } - _polygon_draw(); - - } else { - obstacle_node = nullptr; - - if (point_lines_meshinstance->get_parent()) { - point_lines_meshinstance->get_parent()->remove_child(point_lines_meshinstance); - } - } + rs->mesh_add_surface_from_arrays(point_handle_mesh_rid, RS::PRIMITIVE_POINTS, point_handle_mesh_array); + rs->instance_set_surface_override_material(point_handles_instance_rid, 0, handle_material->get_rid()); + rs->instance_set_transform(point_handles_instance_rid, gt); } -void NavigationObstacle3DEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_polygon_draw"), &NavigationObstacle3DEditor::_polygon_draw); -} - -NavigationObstacle3DEditor::NavigationObstacle3DEditor() { - obstacle_node = nullptr; - - button_create = memnew(Button); - button_create->set_theme_type_variation("FlatButton"); - add_child(button_create); - button_create->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditor::_menu_option).bind(MODE_CREATE)); - button_create->set_toggle_mode(true); - - button_edit = memnew(Button); - button_edit->set_theme_type_variation("FlatButton"); - add_child(button_edit); - button_edit->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditor::_menu_option).bind(MODE_EDIT)); - button_edit->set_toggle_mode(true); +NavigationObstacle3DEditorPlugin *NavigationObstacle3DEditorPlugin::singleton = nullptr; - mode = MODE_EDIT; - wip_active = false; - point_lines_meshinstance = memnew(MeshInstance3D); - point_lines_mesh.instantiate(); - point_lines_meshinstance->set_mesh(point_lines_mesh); - point_lines_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001))); +NavigationObstacle3DEditorPlugin::NavigationObstacle3DEditorPlugin() { + singleton = this; line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); - line_material->set_albedo(Color(1, 1, 1)); + line_material->set_albedo(Color(1, 0.3, 0.1, 0.8)); + line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons)); handle_material->set_point_size(handle->get_width()); handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle); + handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true); - point_handles_meshinstance = memnew(MeshInstance3D); - point_lines_meshinstance->add_child(point_handles_meshinstance); - point_handle_mesh.instantiate(); - point_handles_meshinstance->set_mesh(point_handle_mesh); - point_handles_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001))); + RenderingServer *rs = RenderingServer::get_singleton(); - snap_ignore = false; -} + point_lines_mesh_rid = rs->mesh_create(); + point_handle_mesh_rid = rs->mesh_create(); -NavigationObstacle3DEditor::~NavigationObstacle3DEditor() { - memdelete(point_lines_meshinstance); -} + point_lines_instance_rid = rs->instance_create(); + point_handles_instance_rid = rs->instance_create(); -void NavigationObstacle3DEditorPlugin::edit(Object *p_object) { - obstacle_editor->edit(Object::cast_to<Node>(p_object)); -} + rs->instance_set_base(point_lines_instance_rid, point_lines_mesh_rid); + rs->instance_set_base(point_handles_instance_rid, point_handle_mesh_rid); -bool NavigationObstacle3DEditorPlugin::handles(Object *p_object) const { - return Object::cast_to<NavigationObstacle3D>(p_object); -} + obstacle_editor = memnew(HBoxContainer); + obstacle_editor->hide(); -void NavigationObstacle3DEditorPlugin::make_visible(bool p_visible) { - if (p_visible) { - obstacle_editor->show(); - } else { - obstacle_editor->hide(); - obstacle_editor->edit(nullptr); - } -} + Ref<ButtonGroup> bg; + bg.instantiate(); + + button_create = memnew(Button); + button_create->set_theme_type_variation("FlatButton"); + obstacle_editor->add_child(button_create); + button_create->set_tooltip_text(TTR("Add Vertex")); + button_create->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_CREATE)); + button_create->set_toggle_mode(true); + button_create->set_button_group(bg); + + button_edit = memnew(Button); + button_edit->set_theme_type_variation("FlatButton"); + obstacle_editor->add_child(button_edit); + button_edit->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_EDIT)); + button_edit->set_toggle_mode(true); + button_edit->set_button_group(bg); + + button_delete = memnew(Button); + button_delete->set_theme_type_variation("FlatButton"); + obstacle_editor->add_child(button_delete); + button_delete->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::MODE_DELETE)); + button_delete->set_toggle_mode(true); + button_delete->set_button_group(bg); + + button_flip = memnew(Button); + button_flip->set_theme_type_variation("FlatButton"); + obstacle_editor->add_child(button_flip); + button_flip->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::ACTION_FLIP)); + button_flip->set_toggle_mode(true); + + button_clear = memnew(Button); + button_clear->set_theme_type_variation("FlatButton"); + obstacle_editor->add_child(button_clear); + button_clear->connect(SceneStringName(pressed), callable_mp(this, &NavigationObstacle3DEditorPlugin::set_mode).bind(NavigationObstacle3DEditorPlugin::ACTION_CLEAR)); + button_clear->set_toggle_mode(true); + + button_clear_dialog = memnew(ConfirmationDialog); + button_clear_dialog->set_title(TTR("Please Confirm...")); + button_clear_dialog->set_text(TTR("Remove all vertices?")); + button_clear_dialog->connect(SceneStringName(confirmed), callable_mp(NavigationObstacle3DEditorPlugin::singleton, &NavigationObstacle3DEditorPlugin::action_clear_vertices)); + obstacle_editor->add_child(button_clear_dialog); -NavigationObstacle3DEditorPlugin::NavigationObstacle3DEditorPlugin() { - obstacle_editor = memnew(NavigationObstacle3DEditor); Node3DEditor::get_singleton()->add_control_to_menu_panel(obstacle_editor); - obstacle_editor->hide(); + Ref<NavigationObstacle3DGizmoPlugin> gizmo_plugin = memnew(NavigationObstacle3DGizmoPlugin()); + obstacle_3d_gizmo_plugin = gizmo_plugin; + Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin); } NavigationObstacle3DEditorPlugin::~NavigationObstacle3DEditorPlugin() { + RenderingServer *rs = RenderingServer::get_singleton(); + ERR_FAIL_NULL(rs); + + if (point_lines_instance_rid.is_valid()) { + rs->free(point_lines_instance_rid); + point_lines_instance_rid = RID(); + } + if (point_lines_mesh_rid.is_valid()) { + rs->free(point_lines_mesh_rid); + point_lines_mesh_rid = RID(); + } + + if (point_handles_instance_rid.is_valid()) { + rs->free(point_handles_instance_rid); + point_handles_instance_rid = RID(); + } + if (point_handle_mesh_rid.is_valid()) { + rs->free(point_handle_mesh_rid); + point_handle_mesh_rid = RID(); + } } diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.h b/editor/plugins/navigation_obstacle_3d_editor_plugin.h index c62a5a281b..b6f3a11cf6 100644 --- a/editor/plugins/navigation_obstacle_3d_editor_plugin.h +++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.h @@ -32,79 +32,99 @@ #define NAVIGATION_OBSTACLE_3D_EDITOR_PLUGIN_H #include "editor/plugins/editor_plugin.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/physics/collision_polygon_3d.h" +#include "editor/plugins/node_3d_editor_gizmos.h" #include "scene/gui/box_container.h" -#include "scene/resources/immediate_mesh.h" -#include "scene/3d/navigation_obstacle_3d.h" +class Button; +class ConfirmationDialog; +class NavigationObstacle3D; -class CanvasItemEditor; -class MenuButton; +class NavigationObstacle3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(NavigationObstacle3DGizmoPlugin, EditorNode3DGizmoPlugin); -class NavigationObstacle3DEditor : public HBoxContainer { - GDCLASS(NavigationObstacle3DEditor, HBoxContainer); +public: + virtual bool has_gizmo(Node3D *p_spatial) override; + virtual String get_gizmo_name() const override; - enum Mode { - MODE_CREATE, - MODE_EDIT, + virtual void redraw(EditorNode3DGizmo *p_gizmo) override; - }; + bool can_be_hidden() const override; + int get_priority() const override; - Mode mode; + virtual int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const override; + virtual Vector<int> subgizmos_intersect_frustum(const EditorNode3DGizmo *p_gizmo, const Camera3D *p_camera, const Vector<Plane> &p_frustum) const override; + virtual Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const override; + virtual void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) override; + virtual void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel = false) override; - Button *button_create = nullptr; - Button *button_edit = nullptr; + NavigationObstacle3DGizmoPlugin(); +}; + +class NavigationObstacle3DEditorPlugin : public EditorPlugin { + GDCLASS(NavigationObstacle3DEditorPlugin, EditorPlugin); + + Ref<NavigationObstacle3DGizmoPlugin> obstacle_3d_gizmo_plugin; + + NavigationObstacle3D *obstacle_node = nullptr; Ref<StandardMaterial3D> line_material; Ref<StandardMaterial3D> handle_material; - Panel *panel = nullptr; - NavigationObstacle3D *obstacle_node = nullptr; - Ref<ImmediateMesh> point_lines_mesh; - MeshInstance3D *point_lines_meshinstance = nullptr; - MeshInstance3D *point_handles_meshinstance = nullptr; - Ref<ArrayMesh> point_handle_mesh; + RID point_lines_mesh_rid; + RID point_lines_instance_rid; + RID point_handle_mesh_rid; + RID point_handles_instance_rid; - MenuButton *options = nullptr; +public: + enum Mode { + MODE_CREATE = 0, + MODE_EDIT, + MODE_DELETE, + ACTION_FLIP, + ACTION_CLEAR, + }; - int edited_point = 0; - Vector2 edited_point_pos; - PackedVector2Array pre_move_edit; - PackedVector2Array wip; - bool wip_active; - bool snap_ignore; +private: + int mode = MODE_EDIT; - float prev_depth = 0.0f; + int edited_point = 0; + Vector3 edited_point_pos; + Vector<Vector3> pre_move_edit; + Vector<Vector3> wip_vertices; + bool wip_active = false; + bool snap_ignore = false; void _wip_close(); - void _polygon_draw(); - void _menu_option(int p_option); + void _wip_cancel(); + void _update_theme(); + + Button *button_create = nullptr; + Button *button_edit = nullptr; + Button *button_delete = nullptr; + Button *button_flip = nullptr; + Button *button_clear = nullptr; - PackedVector2Array _get_polygon(); - void _set_polygon(const PackedVector2Array &p_poly); + ConfirmationDialog *button_clear_dialog = nullptr; protected: void _notification(int p_what); void _node_removed(Node *p_node); - static void _bind_methods(); public: - virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event); - void edit(Node *p_node); - NavigationObstacle3DEditor(); - ~NavigationObstacle3DEditor(); -}; + HBoxContainer *obstacle_editor = nullptr; + static NavigationObstacle3DEditorPlugin *singleton; -class NavigationObstacle3DEditorPlugin : public EditorPlugin { - GDCLASS(NavigationObstacle3DEditorPlugin, EditorPlugin); + void redraw(); - NavigationObstacle3DEditor *obstacle_editor = nullptr; + void set_mode(int p_mode); + int get_mode() { return mode; } -public: - virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return obstacle_editor->forward_3d_gui_input(p_camera, p_event); } + void action_flip_vertices(); + void action_clear_vertices(); + + virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override; - virtual String get_name() const override { return "NavigationObstacle3DEditor"; } + virtual String get_name() const override { return "NavigationObstacle3D"; } bool has_main_screen() const override { return false; } virtual void edit(Object *p_object) override; virtual bool handles(Object *p_object) const override; diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp index ec59bbb543..c11a7cf20e 100644 --- a/editor/plugins/navigation_polygon_editor_plugin.cpp +++ b/editor/plugins/navigation_polygon_editor_plugin.cpp @@ -38,8 +38,8 @@ Ref<NavigationPolygon> NavigationPolygonEditor::_ensure_navpoly() const { Ref<NavigationPolygon> navpoly = node->get_navigation_polygon(); - if (!navpoly.is_valid()) { - navpoly = Ref<NavigationPolygon>(memnew(NavigationPolygon)); + if (navpoly.is_null()) { + navpoly.instantiate(); node->set_navigation_polygon(navpoly); } return navpoly; @@ -177,8 +177,8 @@ NavigationPolygonEditor::NavigationPolygonEditor() { void NavigationPolygonEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - button_bake->set_icon(get_editor_theme_icon(SNAME("Bake"))); - button_reset->set_icon(get_editor_theme_icon(SNAME("Reload"))); + button_bake->set_button_icon(get_editor_theme_icon(SNAME("Bake"))); + button_reset->set_button_icon(get_editor_theme_icon(SNAME("Reload"))); } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { if (rebake_timer) { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 0df0274495..daede895b5 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1692,7 +1692,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> b = p_event; if (b.is_valid()) { - emit_signal(SNAME("clicked"), this); + emit_signal(SNAME("clicked")); ViewportNavMouseButton orbit_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/orbit_mouse_button").operator int(); ViewportNavMouseButton pan_mouse_preference = (ViewportNavMouseButton)EDITOR_GET("editors/3d/navigation/pan_mouse_button").operator int(); @@ -3139,8 +3139,8 @@ void Node3DEditorViewport::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - view_menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); - preview_camera->set_icon(get_editor_theme_icon(SNAME("Camera3D"))); + view_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + preview_camera->set_button_icon(get_editor_theme_icon(SNAME("Camera3D"))); Control *gui_base = EditorNode::get_singleton()->get_gui_base(); const Ref<StyleBox> &information_3d_stylebox = gui_base->get_theme_stylebox(SNAME("Information3dViewport"), EditorStringName(EditorStyles)); @@ -3265,7 +3265,7 @@ void Node3DEditorViewport::_draw() { if (message_time > 0) { Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label")); int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label")); - Point2 msgpos = Point2(5, get_size().y - 20); + Point2 msgpos = Point2(10 * EDSCALE, get_size().y - 14 * EDSCALE); font->draw_string(ci, msgpos + Point2(1, 1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8)); font->draw_string(ci, msgpos + Point2(-1, -1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8)); font->draw_string(ci, msgpos, message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1)); @@ -4210,7 +4210,7 @@ Dictionary Node3DEditorViewport::get_state() const { void Node3DEditorViewport::_bind_methods() { ADD_SIGNAL(MethodInfo("toggle_maximize_view", PropertyInfo(Variant::OBJECT, "viewport"))); - ADD_SIGNAL(MethodInfo("clicked", PropertyInfo(Variant::OBJECT, "viewport"))); + ADD_SIGNAL(MethodInfo("clicked")); } void Node3DEditorViewport::reset() { @@ -6572,18 +6572,6 @@ void Node3DEditor::_menu_item_toggled(bool pressed, int p_option) { tool_option_button[TOOL_OPT_USE_SNAP]->set_pressed(pressed); snap_enabled = pressed; } break; - - case MENU_TOOL_OVERRIDE_CAMERA: { - EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton(); - - using Override = EditorDebuggerNode::CameraOverride; - if (pressed) { - debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id)); - } else { - debugger->set_camera_override(Override::OVERRIDE_NONE); - } - - } break; } } @@ -6610,36 +6598,6 @@ void Node3DEditor::_menu_gizmo_toggled(int p_option) { update_all_gizmos(); } -void Node3DEditor::_update_camera_override_button(bool p_game_running) { - Button *const button = tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]; - - if (p_game_running) { - button->set_disabled(false); - button->set_tooltip_text(TTR("Project Camera Override\nOverrides the running project's camera with the editor viewport camera.")); - } else { - button->set_disabled(true); - button->set_pressed(false); - button->set_tooltip_text(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature.")); - } -} - -void Node3DEditor::_update_camera_override_viewport(Object *p_viewport) { - Node3DEditorViewport *current_viewport = Object::cast_to<Node3DEditorViewport>(p_viewport); - - if (!current_viewport) { - return; - } - - EditorDebuggerNode *const debugger = EditorDebuggerNode::get_singleton(); - - camera_override_viewport_id = current_viewport->index; - if (debugger->get_camera_override() >= EditorDebuggerNode::OVERRIDE_3D_1) { - using Override = EditorDebuggerNode::CameraOverride; - - debugger->set_camera_override((Override)(Override::OVERRIDE_3D_1 + camera_override_viewport_id)); - } -} - void Node3DEditor::_menu_item_pressed(int p_option) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); switch (p_option) { @@ -6670,6 +6628,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) { } break; case MENU_VIEW_USE_1_VIEWPORT: { viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_1_VIEWPORT); + if (last_used_viewport > 0) { + last_used_viewport = 0; + } view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), true); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); @@ -6681,6 +6642,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) { } break; case MENU_VIEW_USE_2_VIEWPORTS: { viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS); + if (last_used_viewport > 1) { + last_used_viewport = 0; + } view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), true); @@ -6692,6 +6656,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) { } break; case MENU_VIEW_USE_2_VIEWPORTS_ALT: { viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_2_VIEWPORTS_ALT); + if (last_used_viewport > 1) { + last_used_viewport = 0; + } view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); @@ -6703,6 +6670,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) { } break; case MENU_VIEW_USE_3_VIEWPORTS: { viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS); + if (last_used_viewport > 2) { + last_used_viewport = 0; + } view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); @@ -6714,6 +6684,9 @@ void Node3DEditor::_menu_item_pressed(int p_option) { } break; case MENU_VIEW_USE_3_VIEWPORTS_ALT: { viewport_base->set_view(Node3DEditorViewportContainer::VIEW_USE_3_VIEWPORTS_ALT); + if (last_used_viewport > 2) { + last_used_viewport = 0; + } view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), false); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), false); @@ -7077,12 +7050,12 @@ void fragment() { col.a = EDITOR_GET("editors/3d/manipulator_gizmo_opacity"); - move_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - move_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - rotate_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - scale_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - scale_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); - axis_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh)); + move_gizmo[i].instantiate(); + move_plane_gizmo[i].instantiate(); + rotate_gizmo[i].instantiate(); + scale_gizmo[i].instantiate(); + scale_plane_gizmo[i].instantiate(); + axis_gizmo[i].instantiate(); Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D); mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); @@ -7313,7 +7286,7 @@ void fragment() { border_mat->set_shader(border_shader); border_mat->set_shader_parameter("albedo", Color(0.75, 0.75, 0.75, col.a / 3.0)); - rotate_gizmo[3] = Ref<ArrayMesh>(memnew(ArrayMesh)); + rotate_gizmo[3].instantiate(); rotate_gizmo[3]->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); rotate_gizmo[3]->surface_set_material(0, border_mat); } @@ -8021,19 +7994,18 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) { } void Node3DEditor::_update_theme() { - tool_button[TOOL_MODE_SELECT]->set_icon(get_editor_theme_icon(SNAME("ToolSelect"))); - tool_button[TOOL_MODE_MOVE]->set_icon(get_editor_theme_icon(SNAME("ToolMove"))); - tool_button[TOOL_MODE_ROTATE]->set_icon(get_editor_theme_icon(SNAME("ToolRotate"))); - tool_button[TOOL_MODE_SCALE]->set_icon(get_editor_theme_icon(SNAME("ToolScale"))); - tool_button[TOOL_MODE_LIST_SELECT]->set_icon(get_editor_theme_icon(SNAME("ListSelect"))); - tool_button[TOOL_LOCK_SELECTED]->set_icon(get_editor_theme_icon(SNAME("Lock"))); - tool_button[TOOL_UNLOCK_SELECTED]->set_icon(get_editor_theme_icon(SNAME("Unlock"))); - tool_button[TOOL_GROUP_SELECTED]->set_icon(get_editor_theme_icon(SNAME("Group"))); - tool_button[TOOL_UNGROUP_SELECTED]->set_icon(get_editor_theme_icon(SNAME("Ungroup"))); - - tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_icon(get_editor_theme_icon(SNAME("Object"))); - tool_option_button[TOOL_OPT_USE_SNAP]->set_icon(get_editor_theme_icon(SNAME("Snap"))); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_icon(get_editor_theme_icon(SNAME("Camera3D"))); + tool_button[TOOL_MODE_SELECT]->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect"))); + tool_button[TOOL_MODE_MOVE]->set_button_icon(get_editor_theme_icon(SNAME("ToolMove"))); + tool_button[TOOL_MODE_ROTATE]->set_button_icon(get_editor_theme_icon(SNAME("ToolRotate"))); + tool_button[TOOL_MODE_SCALE]->set_button_icon(get_editor_theme_icon(SNAME("ToolScale"))); + tool_button[TOOL_MODE_LIST_SELECT]->set_button_icon(get_editor_theme_icon(SNAME("ListSelect"))); + tool_button[TOOL_LOCK_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Lock"))); + tool_button[TOOL_UNLOCK_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Unlock"))); + tool_button[TOOL_GROUP_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Group"))); + tool_button[TOOL_UNGROUP_SELECTED]->set_button_icon(get_editor_theme_icon(SNAME("Ungroup"))); + + tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_button_icon(get_editor_theme_icon(SNAME("Object"))); + tool_option_button[TOOL_OPT_USE_SNAP]->set_button_icon(get_editor_theme_icon(SNAME("Snap"))); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_editor_theme_icon(SNAME("Panels1"))); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_editor_theme_icon(SNAME("Panels2"))); @@ -8042,9 +8014,9 @@ void Node3DEditor::_update_theme() { view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_editor_theme_icon(SNAME("Panels3Alt"))); view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_editor_theme_icon(SNAME("Panels4"))); - sun_button->set_icon(get_editor_theme_icon(SNAME("PreviewSun"))); - environ_button->set_icon(get_editor_theme_icon(SNAME("PreviewEnvironment"))); - sun_environ_settings->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + sun_button->set_button_icon(get_editor_theme_icon(SNAME("PreviewSun"))); + environ_button->set_button_icon(get_editor_theme_icon(SNAME("PreviewEnvironment"))); + sun_environ_settings->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); sun_title->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("title_font"), SNAME("Window"))); environ_title->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("title_font"), SNAME("Window"))); @@ -8068,9 +8040,6 @@ void Node3DEditor::_notification(int p_what) { SceneTreeDock::get_singleton()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons)); editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_selection_changed)); - EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(false)); - EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(true)); - _update_preview_environment(); sun_state->set_custom_minimum_size(sun_vb->get_combined_minimum_size()); @@ -8106,15 +8075,6 @@ void Node3DEditor::_notification(int p_what) { } } break; - case NOTIFICATION_VISIBILITY_CHANGED: { - if (!is_visible() && tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->is_pressed()) { - EditorDebuggerNode *debugger = EditorDebuggerNode::get_singleton(); - - debugger->set_camera_override(EditorDebuggerNode::OVERRIDE_NONE); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_pressed(false); - } - } break; - case NOTIFICATION_PHYSICS_PROCESS: { if (do_snap_selected_nodes_to_floor) { _snap_selected_nodes_to_floor(); @@ -8216,6 +8176,10 @@ VSplitContainer *Node3DEditor::get_shader_split() { return shader_split; } +Node3DEditorViewport *Node3DEditor::get_last_used_viewport() { + return viewports[last_used_viewport]; +} + void Node3DEditor::add_control_to_left_panel(Control *p_control) { left_panel_split->add_child(p_control); left_panel_split->move_child(p_control, 0); @@ -8393,6 +8357,10 @@ void Node3DEditor::_toggle_maximize_view(Object *p_viewport) { } } +void Node3DEditor::_viewport_clicked(int p_viewport_idx) { + last_used_viewport = p_viewport_idx; +} + void Node3DEditor::_node_added(Node *p_node) { if (EditorNode::get_singleton()->get_scene_root()->is_ancestor_of(p_node)) { if (Object::cast_to<WorldEnvironment>(p_node)) { @@ -8512,7 +8480,7 @@ void Node3DEditor::clear() { } for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { - viewports[i]->view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(Node3DEditorViewport::VIEW_AUDIO_LISTENER), i == 0); + viewports[i]->view_menu->get_popup()->set_item_checked(viewports[i]->view_menu->get_popup()->get_item_index(Node3DEditorViewport::VIEW_AUDIO_LISTENER), i == 0); viewports[i]->viewport->set_as_audio_listener_3d(i == 0); } @@ -8671,7 +8639,7 @@ Node3DEditor::Node3DEditor() { gizmo.visible = true; gizmo.scale = 1.0; - viewport_environment = Ref<Environment>(memnew(Environment)); + viewport_environment.instantiate(); VBoxContainer *vbc = this; custom_camera = nullptr; @@ -8684,8 +8652,6 @@ Node3DEditor::Node3DEditor() { snap_key_enabled = false; tool_mode = TOOL_MODE_SELECT; - camera_override_viewport_id = 0; - // Add some margin to the sides for better aesthetics. // This prevents the first button's hover/pressed effect from "touching" the panel's border, // which looks ugly. @@ -8803,16 +8769,6 @@ Node3DEditor::Node3DEditor() { tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut_context(this); main_menu_hbox->add_child(memnew(VSeparator)); - - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA] = memnew(Button); - main_menu_hbox->add_child(tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_toggle_mode(true); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_theme_type_variation("FlatButton"); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_disabled(true); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->connect(SceneStringName(toggled), callable_mp(this, &Node3DEditor::_menu_item_toggled).bind(MENU_TOOL_OVERRIDE_CAMERA)); - _update_camera_override_button(false); - - main_menu_hbox->add_child(memnew(VSeparator)); sun_button = memnew(Button); sun_button->set_tooltip_text(TTR("Toggle preview sunlight.\nIf a DirectionalLight3D node is added to the scene, preview sunlight is disabled.")); sun_button->set_toggle_mode(true); @@ -8955,7 +8911,7 @@ Node3DEditor::Node3DEditor() { for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { viewports[i] = memnew(Node3DEditorViewport(this, i)); viewports[i]->connect("toggle_maximize_view", callable_mp(this, &Node3DEditor::_toggle_maximize_view)); - viewports[i]->connect("clicked", callable_mp(this, &Node3DEditor::_update_camera_override_viewport)); + viewports[i]->connect("clicked", callable_mp(this, &Node3DEditor::_viewport_clicked).bind(i)); viewports[i]->assign_pending_data_pointers(preview_node, &preview_bounds, accept); viewport_base->add_child(viewports[i]); } diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 1b03362606..d35fcb7653 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -622,7 +622,6 @@ public: enum ToolOptions { TOOL_OPT_LOCAL_COORDS, TOOL_OPT_USE_SNAP, - TOOL_OPT_OVERRIDE_CAMERA, TOOL_OPT_MAX }; @@ -632,6 +631,8 @@ private: Node3DEditorViewportContainer *viewport_base = nullptr; Node3DEditorViewport *viewports[VIEWPORTS_COUNT]; + int last_used_viewport = 0; + VSplitContainer *shader_split = nullptr; HSplitContainer *left_panel_split = nullptr; HSplitContainer *right_panel_split = nullptr; @@ -704,7 +705,6 @@ private: MENU_TOOL_LIST_SELECT, MENU_TOOL_LOCAL_COORDS, MENU_TOOL_USE_SNAP, - MENU_TOOL_OVERRIDE_CAMERA, MENU_TRANSFORM_CONFIGURE_SNAP, MENU_TRANSFORM_DIALOG, MENU_VIEW_USE_1_VIEWPORT, @@ -759,8 +759,6 @@ private: void _menu_item_pressed(int p_option); void _menu_item_toggled(bool pressed, int p_option); void _menu_gizmo_toggled(int p_option); - void _update_camera_override_button(bool p_game_running); - void _update_camera_override_viewport(Object *p_viewport); // Used for secondary menu items which are displayed depending on the currently selected node // (such as MeshInstance's "Mesh" menu). PanelContainer *context_toolbar_panel = nullptr; @@ -771,8 +769,6 @@ private: void _generate_selection_boxes(); - int camera_override_viewport_id; - void _init_indicators(); void _update_gizmos_menu(); void _update_gizmos_menu_theme(); @@ -781,6 +777,7 @@ private: void _finish_grid(); void _toggle_maximize_view(Object *p_viewport); + void _viewport_clicked(int p_viewport_idx); Node *custom_camera = nullptr; @@ -967,6 +964,7 @@ public: ERR_FAIL_INDEX_V(p_idx, static_cast<int>(VIEWPORTS_COUNT), nullptr); return viewports[p_idx]; } + Node3DEditorViewport *get_last_used_viewport(); void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin); void remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin); diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.cpp b/editor/plugins/occluder_instance_3d_editor_plugin.cpp index c14d9e02aa..b9c884178f 100644 --- a/editor/plugins/occluder_instance_3d_editor_plugin.cpp +++ b/editor/plugins/occluder_instance_3d_editor_plugin.cpp @@ -105,7 +105,7 @@ void OccluderInstance3DEditorPlugin::_bind_methods() { OccluderInstance3DEditorPlugin::OccluderInstance3DEditorPlugin() { bake = memnew(Button); bake->set_theme_type_variation("FlatButton"); - bake->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons))); + bake->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons))); bake->set_text(TTR("Bake Occluders")); bake->hide(); bake->connect(SceneStringName(pressed), Callable(this, "_bake")); diff --git a/editor/plugins/packed_scene_editor_plugin.cpp b/editor/plugins/packed_scene_editor_plugin.cpp index c2d9851c17..4684c5a456 100644 --- a/editor/plugins/packed_scene_editor_plugin.cpp +++ b/editor/plugins/packed_scene_editor_plugin.cpp @@ -43,7 +43,7 @@ void PackedSceneEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - open_scene_button->set_icon(get_editor_theme_icon(SNAME("PackedScene"))); + open_scene_button->set_button_icon(get_editor_theme_icon(SNAME("PackedScene"))); } break; } } diff --git a/editor/plugins/parallax_background_editor_plugin.cpp b/editor/plugins/parallax_background_editor_plugin.cpp index 6c55fd2753..e802f42596 100644 --- a/editor/plugins/parallax_background_editor_plugin.cpp +++ b/editor/plugins/parallax_background_editor_plugin.cpp @@ -119,7 +119,7 @@ void ParallaxBackgroundEditorPlugin::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &ParallaxBackgroundEditorPlugin::_menu_callback)); - menu->set_icon(menu->get_editor_theme_icon(SNAME("ParallaxBackground"))); + menu->set_button_icon(menu->get_editor_theme_icon(SNAME("ParallaxBackground"))); } break; } } diff --git a/editor/plugins/particle_process_material_editor_plugin.cpp b/editor/plugins/particle_process_material_editor_plugin.cpp index 67c9403aaf..7b46653ac7 100644 --- a/editor/plugins/particle_process_material_editor_plugin.cpp +++ b/editor/plugins/particle_process_material_editor_plugin.cpp @@ -346,7 +346,7 @@ float ParticleProcessMaterialMinMaxPropertyEditor::_get_max_spread() const { void ParticleProcessMaterialMinMaxPropertyEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - toggle_mode_button->set_icon(get_editor_theme_icon(SNAME("Anchor"))); + toggle_mode_button->set_button_icon(get_editor_theme_icon(SNAME("Anchor"))); range_slider_left_icon = get_editor_theme_icon(SNAME("RangeSliderLeft")); range_slider_right_icon = get_editor_theme_icon(SNAME("RangeSliderRight")); diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 34f5dcf963..36a2d7acad 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -60,7 +60,7 @@ void ParticlesEditorPlugin::_notification(int p_what) { DEV_ASSERT(false); } - menu->set_icon(menu->get_editor_theme_icon(handled_type)); + menu->set_button_icon(menu->get_editor_theme_icon(handled_type)); menu->set_text(handled_type); PopupMenu *popup = menu->get_popup(); diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index c96f23869e..96e022e230 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -43,14 +43,14 @@ void Path2DEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - curve_edit->set_icon(get_editor_theme_icon(SNAME("CurveEdit"))); - curve_edit_curve->set_icon(get_editor_theme_icon(SNAME("CurveCurve"))); - curve_create->set_icon(get_editor_theme_icon(SNAME("CurveCreate"))); - curve_del->set_icon(get_editor_theme_icon(SNAME("CurveDelete"))); - curve_close->set_icon(get_editor_theme_icon(SNAME("CurveClose"))); - curve_clear_points->set_icon(get_editor_theme_icon(SNAME("Clear"))); - - create_curve_button->set_icon(get_editor_theme_icon(SNAME("Curve2D"))); + curve_edit->set_button_icon(get_editor_theme_icon(SNAME("CurveEdit"))); + curve_edit_curve->set_button_icon(get_editor_theme_icon(SNAME("CurveCurve"))); + curve_create->set_button_icon(get_editor_theme_icon(SNAME("CurveCreate"))); + curve_del->set_button_icon(get_editor_theme_icon(SNAME("CurveDelete"))); + curve_close->set_button_icon(get_editor_theme_icon(SNAME("CurveClose"))); + curve_clear_points->set_button_icon(get_editor_theme_icon(SNAME("Clear"))); + + create_curve_button->set_button_icon(get_editor_theme_icon(SNAME("Curve2D"))); } break; } } diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 4fdcb79696..de4ab828bc 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -277,8 +277,15 @@ void Path3DGizmo::redraw() { Ref<StandardMaterial3D> path_tilt_material = gizmo_plugin->get_material("path_tilt_material", this); Ref<StandardMaterial3D> path_tilt_muted_material = gizmo_plugin->get_material("path_tilt_muted_material", this); Ref<StandardMaterial3D> handles_material = gizmo_plugin->get_material("handles"); + Ref<StandardMaterial3D> first_pt_handle_material = gizmo_plugin->get_material("first_pt_handle"); + Ref<StandardMaterial3D> last_pt_handle_material = gizmo_plugin->get_material("last_pt_handle"); + Ref<StandardMaterial3D> closed_pt_handle_material = gizmo_plugin->get_material("closed_pt_handle"); Ref<StandardMaterial3D> sec_handles_material = gizmo_plugin->get_material("sec_handles"); + first_pt_handle_material->set_albedo(Color(0.2, 1.0, 0.0)); + last_pt_handle_material->set_albedo(Color(1.0, 0.2, 0.0)); + closed_pt_handle_material->set_albedo(Color(1.0, 0.8, 0.0)); + Ref<Curve3D> c = path->get_curve(); if (c.is_null()) { return; @@ -369,7 +376,7 @@ void Path3DGizmo::redraw() { info.point_idx = idx; // Collect in-handles except for the first point. - if (idx > 0 && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) { + if (idx > (c->is_closed() ? -1 : 0) && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) { const Vector3 in = c->get_point_in(idx); info.type = HandleType::HANDLE_TYPE_IN; @@ -383,7 +390,7 @@ void Path3DGizmo::redraw() { } // Collect out-handles except for the last point. - if (idx < c->get_point_count() - 1 && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) { + if (idx < (c->is_closed() ? c->get_point_count() : c->get_point_count() - 1) && Path3DEditorPlugin::singleton->curve_edit_curve->is_pressed()) { const Vector3 out = c->get_point_out(idx); info.type = HandleType::HANDLE_TYPE_OUT; @@ -441,7 +448,42 @@ void Path3DGizmo::redraw() { } if (!Path3DEditorPlugin::singleton->curve_edit->is_pressed() && primary_handle_points.size()) { - add_handles(primary_handle_points, handles_material); + // Need to define indices separately. + // Point count. + const int pc = primary_handle_points.size(); + Vector<int> idx; + idx.resize(pc); + int *idx_ptr = idx.ptrw(); + for (int j = 0; j < pc; j++) { + idx_ptr[j] = j; + } + + // Initialize arrays for first point. + PackedVector3Array first_pt_handle_point; + Vector<int> first_pt_id; + first_pt_handle_point.append(primary_handle_points[0]); + first_pt_id.append(idx[0]); + + // Initialize arrays and add handle for last point if needed. + if (pc > 1) { + PackedVector3Array last_pt_handle_point; + Vector<int> last_pt_id; + last_pt_handle_point.append(primary_handle_points[pc - 1]); + last_pt_id.append(idx[pc - 1]); + primary_handle_points.remove_at(pc - 1); + idx.remove_at(pc - 1); + add_handles(last_pt_handle_point, c->is_closed() ? handles_material : last_pt_handle_material, last_pt_id); + } + + // Add handle for first point. + primary_handle_points.remove_at(0); + idx.remove_at(0); + add_handles(first_pt_handle_point, c->is_closed() ? closed_pt_handle_material : first_pt_handle_material, first_pt_id); + + // Add handles for remaining intermediate points. + if (!primary_handle_points.is_empty()) { + add_handles(primary_handle_points, handles_material, idx); + } } if (secondary_handle_points.size()) { add_handles(secondary_handle_points, sec_handles_material, collected_secondary_handle_ids, false, true); @@ -469,7 +511,7 @@ Path3DGizmo::Path3DGizmo(Path3D *p_path, float p_disk_size) { Path3DEditorPlugin::singleton->curve_edit_curve->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw)); Path3DEditorPlugin::singleton->curve_create->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw)); Path3DEditorPlugin::singleton->curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw)); - Path3DEditorPlugin::singleton->curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw)); + Path3DEditorPlugin::singleton->curve_closed->connect(SceneStringName(pressed), callable_mp(this, &Path3DGizmo::redraw)); } EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) { @@ -696,7 +738,7 @@ void Path3DEditorPlugin::_mode_changed(int p_mode) { Node3DEditor::get_singleton()->clear_subgizmo_selection(); } -void Path3DEditorPlugin::_close_curve() { +void Path3DEditorPlugin::_toggle_closed_curve() { Ref<Curve3D> c = path->get_curve(); if (c.is_null()) { return; @@ -704,13 +746,10 @@ void Path3DEditorPlugin::_close_curve() { if (c->get_point_count() < 2) { return; } - if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) { - return; - } EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton(); - ur->create_action(TTR("Close Curve")); - ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1); - ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count()); + ur->create_action(TTR("Toggle Open/Closed Curve")); + ur->add_do_method(c.ptr(), "set_closed", !c.ptr()->is_closed()); + ur->add_undo_method(c.ptr(), "set_closed", c.ptr()->is_closed()); ur->commit_action(); } @@ -771,6 +810,7 @@ void Path3DEditorPlugin::_clear_curve_points() { return; } Ref<Curve3D> curve = path->get_curve(); + curve->set_closed(false); curve->clear_points(); } @@ -790,14 +830,14 @@ void Path3DEditorPlugin::_restore_curve_points(const PackedVector3Array &p_point } void Path3DEditorPlugin::_update_theme() { - curve_edit->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveEdit"))); - curve_edit_curve->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveCurve"))); - curve_edit_tilt->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveTilt"))); - curve_create->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveCreate"))); - curve_del->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveDelete"))); - curve_close->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveClose"))); - curve_clear_points->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("Clear"))); - create_curve_button->set_icon(topmenu_bar->get_editor_theme_icon(SNAME("Curve3D"))); + curve_edit->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveEdit"))); + curve_edit_curve->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveCurve"))); + curve_edit_tilt->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveTilt"))); + curve_create->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveCreate"))); + curve_del->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveDelete"))); + curve_closed->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("CurveClose"))); + curve_clear_points->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("Clear"))); + create_curve_button->set_button_icon(topmenu_bar->get_editor_theme_icon(SNAME("Curve3D"))); } void Path3DEditorPlugin::_update_toolbar() { @@ -872,12 +912,12 @@ Path3DEditorPlugin::Path3DEditorPlugin() { toolbar->add_child(curve_del); curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(MODE_DELETE)); - curve_close = memnew(Button); - curve_close->set_theme_type_variation("FlatButton"); - curve_close->set_focus_mode(Control::FOCUS_NONE); - curve_close->set_tooltip_text(TTR("Close Curve")); - toolbar->add_child(curve_close); - curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_close_curve)); + curve_closed = memnew(Button); + curve_closed->set_theme_type_variation("FlatButton"); + curve_closed->set_focus_mode(Control::FOCUS_NONE); + curve_closed->set_tooltip_text(TTR("Close Curve")); + toolbar->add_child(curve_closed); + curve_closed->connect(SceneStringName(pressed), callable_mp(this, &Path3DEditorPlugin::_toggle_closed_curve)); curve_clear_points = memnew(Button); curve_clear_points->set_theme_type_variation("FlatButton"); @@ -922,7 +962,7 @@ Ref<EditorNode3DGizmo> Path3DGizmoPlugin::create_gizmo(Node3D *p_spatial) { Path3D *path = Object::cast_to<Path3D>(p_spatial); if (path) { - ref = Ref<Path3DGizmo>(memnew(Path3DGizmo(path, disk_size))); + ref.instantiate(path, disk_size); } return ref; @@ -943,6 +983,14 @@ void Path3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Ref<Curve3D> curve = path->get_curve(); Ref<StandardMaterial3D> handle_material = get_material("handles", p_gizmo); + Ref<StandardMaterial3D> first_pt_handle_material = get_material("first_pt_handle", p_gizmo); + Ref<StandardMaterial3D> last_pt_handle_material = get_material("last_pt_handle", p_gizmo); + Ref<StandardMaterial3D> closed_pt_handle_material = get_material("closed_pt_handle", p_gizmo); + + first_pt_handle_material->set_albedo(Color(0.2, 1.0, 0.0)); + last_pt_handle_material->set_albedo(Color(1.0, 0.2, 0.0)); + closed_pt_handle_material->set_albedo(Color(1.0, 0.8, 0.0)); + PackedVector3Array handles; if (Path3DEditorPlugin::singleton->curve_edit->is_pressed()) { @@ -955,7 +1003,37 @@ void Path3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { } if (handles.size()) { - p_gizmo->add_vertices(handles, handle_material, Mesh::PRIMITIVE_POINTS); + // Point count. + const int pc = handles.size(); + + // Initialize arrays for first point. + PackedVector3Array first_pt; + first_pt.append(handles[0]); + + // Initialize arrays and add handle for last point if needed. + if (pc > 1) { + PackedVector3Array last_pt; + last_pt.append(handles[handles.size() - 1]); + handles.remove_at(handles.size() - 1); + if (curve->is_closed()) { + p_gizmo->add_vertices(last_pt, handle_material, Mesh::PRIMITIVE_POINTS); + } else { + p_gizmo->add_vertices(last_pt, last_pt_handle_material, Mesh::PRIMITIVE_POINTS); + } + } + + // Add handle for first point. + handles.remove_at(0); + if (curve->is_closed()) { + p_gizmo->add_vertices(first_pt, closed_pt_handle_material, Mesh::PRIMITIVE_POINTS); + } else { + p_gizmo->add_vertices(first_pt, first_pt_handle_material, Mesh::PRIMITIVE_POINTS); + } + + // Add handles for remaining intermediate points. + if (!handles.is_empty()) { + p_gizmo->add_vertices(handles, handle_material, Mesh::PRIMITIVE_POINTS); + } } } @@ -1072,5 +1150,8 @@ Path3DGizmoPlugin::Path3DGizmoPlugin(float p_disk_size) { create_material("path_tilt_material", path_tilt_color); create_material("path_tilt_muted_material", path_tilt_color * 0.7); create_handle_material("handles", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons))); + create_handle_material("first_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons))); + create_handle_material("last_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons))); + create_handle_material("closed_pt_handle", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorPathSmoothHandle"), EditorStringName(EditorIcons))); create_handle_material("sec_handles", false, EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("EditorCurveHandle"), EditorStringName(EditorIcons))); } diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h index 60cb7f940f..3e45c2718f 100644 --- a/editor/plugins/path_3d_editor_plugin.h +++ b/editor/plugins/path_3d_editor_plugin.h @@ -120,7 +120,7 @@ class Path3DEditorPlugin : public EditorPlugin { Button *curve_edit_curve = nullptr; Button *curve_edit_tilt = nullptr; Button *curve_del = nullptr; - Button *curve_close = nullptr; + Button *curve_closed = nullptr; Button *curve_clear_points = nullptr; MenuButton *handle_menu = nullptr; @@ -144,7 +144,7 @@ class Path3DEditorPlugin : public EditorPlugin { void _update_toolbar(); void _mode_changed(int p_mode); - void _close_curve(); + void _toggle_closed_curve(); void _handle_option_pressed(int p_option); bool handle_clicked = false; bool mirror_handle_angle = true; diff --git a/editor/plugins/physical_bone_3d_editor_plugin.cpp b/editor/plugins/physical_bone_3d_editor_plugin.cpp index c858fa8606..15a957d5c4 100644 --- a/editor/plugins/physical_bone_3d_editor_plugin.cpp +++ b/editor/plugins/physical_bone_3d_editor_plugin.cpp @@ -60,7 +60,7 @@ PhysicalBone3DEditor::PhysicalBone3DEditor() { button_transform_joint->set_text(TTR("Move Joint")); // TODO: Rework this as a dedicated toolbar control so we can hook into theme changes and update it // when the editor theme updates. - button_transform_joint->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("PhysicalBone3D"), EditorStringName(EditorIcons))); + button_transform_joint->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("PhysicalBone3D"), EditorStringName(EditorIcons))); button_transform_joint->set_toggle_mode(true); button_transform_joint->connect(SceneStringName(toggled), callable_mp(this, &PhysicalBone3DEditor::_on_toggle_button_transform_joint)); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 842142db79..8ab08ff28f 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -111,22 +111,22 @@ void Polygon2DEditor::_notification(int p_what) { } break; case NOTIFICATION_READY: { - button_uv->set_icon(get_editor_theme_icon(SNAME("Uv"))); - - uv_button[UV_MODE_CREATE]->set_icon(get_editor_theme_icon(SNAME("Edit"))); - uv_button[UV_MODE_CREATE_INTERNAL]->set_icon(get_editor_theme_icon(SNAME("EditInternal"))); - uv_button[UV_MODE_REMOVE_INTERNAL]->set_icon(get_editor_theme_icon(SNAME("RemoveInternal"))); - uv_button[UV_MODE_EDIT_POINT]->set_icon(get_editor_theme_icon(SNAME("ToolSelect"))); - uv_button[UV_MODE_MOVE]->set_icon(get_editor_theme_icon(SNAME("ToolMove"))); - uv_button[UV_MODE_ROTATE]->set_icon(get_editor_theme_icon(SNAME("ToolRotate"))); - uv_button[UV_MODE_SCALE]->set_icon(get_editor_theme_icon(SNAME("ToolScale"))); - uv_button[UV_MODE_ADD_POLYGON]->set_icon(get_editor_theme_icon(SNAME("Edit"))); - uv_button[UV_MODE_REMOVE_POLYGON]->set_icon(get_editor_theme_icon(SNAME("Close"))); - uv_button[UV_MODE_PAINT_WEIGHT]->set_icon(get_editor_theme_icon(SNAME("Bucket"))); - uv_button[UV_MODE_CLEAR_WEIGHT]->set_icon(get_editor_theme_icon(SNAME("Clear"))); - - b_snap_grid->set_icon(get_editor_theme_icon(SNAME("Grid"))); - b_snap_enable->set_icon(get_editor_theme_icon(SNAME("SnapGrid"))); + button_uv->set_button_icon(get_editor_theme_icon(SNAME("Uv"))); + + uv_button[UV_MODE_CREATE]->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); + uv_button[UV_MODE_CREATE_INTERNAL]->set_button_icon(get_editor_theme_icon(SNAME("EditInternal"))); + uv_button[UV_MODE_REMOVE_INTERNAL]->set_button_icon(get_editor_theme_icon(SNAME("RemoveInternal"))); + uv_button[UV_MODE_EDIT_POINT]->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect"))); + uv_button[UV_MODE_MOVE]->set_button_icon(get_editor_theme_icon(SNAME("ToolMove"))); + uv_button[UV_MODE_ROTATE]->set_button_icon(get_editor_theme_icon(SNAME("ToolRotate"))); + uv_button[UV_MODE_SCALE]->set_button_icon(get_editor_theme_icon(SNAME("ToolScale"))); + uv_button[UV_MODE_ADD_POLYGON]->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); + uv_button[UV_MODE_REMOVE_POLYGON]->set_button_icon(get_editor_theme_icon(SNAME("Close"))); + uv_button[UV_MODE_PAINT_WEIGHT]->set_button_icon(get_editor_theme_icon(SNAME("Bucket"))); + uv_button[UV_MODE_CLEAR_WEIGHT]->set_button_icon(get_editor_theme_icon(SNAME("Clear"))); + + b_snap_grid->set_button_icon(get_editor_theme_icon(SNAME("Grid"))); + b_snap_enable->set_button_icon(get_editor_theme_icon(SNAME("SnapGrid"))); uv_vscroll->set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE); uv_hscroll->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE); @@ -275,7 +275,11 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { uv_button[UV_MODE_REMOVE_POLYGON]->hide(); uv_button[UV_MODE_PAINT_WEIGHT]->hide(); uv_button[UV_MODE_CLEAR_WEIGHT]->hide(); - _uv_mode(UV_MODE_EDIT_POINT); + if (node->get_polygon().is_empty()) { + _uv_mode(UV_MODE_CREATE); + } else { + _uv_mode(UV_MODE_EDIT_POINT); + } bone_scroll_main_vb->hide(); bone_paint_strength->hide(); @@ -317,9 +321,16 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) { uv_edit_draw->queue_redraw(); } +void Polygon2DEditor::_uv_edit_popup_show() { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->connect("version_changed", callable_mp(this, &Polygon2DEditor::_update_available_modes)); +} + void Polygon2DEditor::_uv_edit_popup_hide() { EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "uv_editor", Rect2(uv_edit->get_position(), uv_edit->get_size())); _cancel_editing(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->disconnect("version_changed", callable_mp(this, &Polygon2DEditor::_update_available_modes)); } void Polygon2DEditor::_menu_option(int p_option) { @@ -346,6 +357,7 @@ void Polygon2DEditor::_menu_option(int p_option) { uv_edit->popup_centered_ratio(0.85); } _update_bone_list(); + _update_available_modes(); get_tree()->connect("process_frame", callable_mp(this, &Polygon2DEditor::_center_view), CONNECT_ONE_SHOT); } break; case UVEDIT_POLYGON_TO_UV: { @@ -408,6 +420,7 @@ void Polygon2DEditor::_cancel_editing() { node->set_polygons(polygons_prev); _update_polygon_editing_state(); + _update_available_modes(); } else if (uv_drag) { uv_drag = false; if (uv_edit_mode[0]->is_pressed()) { // Edit UV. @@ -566,6 +579,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_drag = false; uv_create = false; + _update_available_modes(); _uv_mode(UV_MODE_EDIT_POINT); _menu_option(MODE_EDIT); } else { @@ -973,6 +987,23 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } } +void Polygon2DEditor::_update_available_modes() { + // Force point editing mode if there's no polygon yet. + if (node->get_polygon().is_empty()) { + if (!uv_edit_mode[1]->is_pressed()) { + uv_edit_mode[1]->set_pressed(true); + _uv_edit_mode_select(1); + } + uv_edit_mode[0]->set_disabled(true); + uv_edit_mode[2]->set_disabled(true); + uv_edit_mode[3]->set_disabled(true); + } else { + uv_edit_mode[0]->set_disabled(false); + uv_edit_mode[2]->set_disabled(false); + uv_edit_mode[3]->set_disabled(false); + } +} + void Polygon2DEditor::_center_view() { Size2 texture_size; if (node->get_texture().is_valid()) { @@ -1324,6 +1355,7 @@ Polygon2DEditor::Polygon2DEditor() { add_child(uv_edit); uv_edit->connect(SceneStringName(confirmed), callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide)); uv_edit->connect("canceled", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide)); + uv_edit->connect("about_to_popup", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_show)); VBoxContainer *uv_main_vb = memnew(VBoxContainer); uv_edit->add_child(uv_main_vb); diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index 164aa3eccc..4e1cd7172e 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -142,6 +142,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _cancel_editing(); void _update_polygon_editing_state(); + void _update_available_modes(); void _center_view(); void _update_zoom_and_pan(bool p_zoom_at_center); @@ -157,6 +158,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { void _set_snap_step_y(real_t p_val); void _uv_edit_mode_select(int p_mode); + void _uv_edit_popup_show(); void _uv_edit_popup_hide(); void _bone_paint_selected(int p_index); @@ -168,7 +170,7 @@ protected: virtual Vector2 _get_offset(int p_idx) const override; - virtual bool _has_uv() const override { return true; }; + virtual bool _has_uv() const override { return true; } virtual void _commit_action() override; void _notification(int p_what); diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp index 56baa4a839..017504f0d6 100644 --- a/editor/plugins/polygon_3d_editor_plugin.cpp +++ b/editor/plugins/polygon_3d_editor_plugin.cpp @@ -46,8 +46,8 @@ void Polygon3DEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - button_create->set_icon(get_editor_theme_icon(SNAME("Edit"))); - button_edit->set_icon(get_editor_theme_icon(SNAME("MovePoint"))); + button_create->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); + button_edit->set_button_icon(get_editor_theme_icon(SNAME("MovePoint"))); button_edit->set_pressed(true); get_tree()->connect("node_removed", callable_mp(this, &Polygon3DEditor::_node_removed)); @@ -554,7 +554,7 @@ Polygon3DEditor::Polygon3DEditor() { imgeom->set_mesh(imesh); imgeom->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001))); - line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + line_material.instantiate(); line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); @@ -562,7 +562,7 @@ Polygon3DEditor::Polygon3DEditor() { line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); line_material->set_albedo(Color(1, 1, 1)); - handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); + handle_material.instantiate(); handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true); handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index ba6699fcc4..bb0607a3c6 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -45,7 +45,7 @@ void ResourcePreloaderEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - load->set_icon(get_editor_theme_icon(SNAME("Folder"))); + load->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); } break; } } diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index cd422fc291..0a4e3d78c8 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -169,13 +169,13 @@ void EditorPropertyRootMotion::update_property() { NodePath p = get_edited_property_value(); assign->set_tooltip_text(p); if (p == NodePath()) { - assign->set_icon(Ref<Texture2D>()); + assign->set_button_icon(Ref<Texture2D>()); assign->set_text(TTR("Assign...")); assign->set_flat(false); return; } - assign->set_icon(Ref<Texture2D>()); + assign->set_button_icon(Ref<Texture2D>()); assign->set_text(p); } @@ -188,7 +188,7 @@ void EditorPropertyRootMotion::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { Ref<Texture2D> t = get_editor_theme_icon(SNAME("Clear")); - clear->set_icon(t); + clear->set_button_icon(t); } break; } } diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 7e0331d15c..49ecbac751 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -261,6 +261,52 @@ Ref<EditorSyntaxHighlighter> EditorJSONSyntaxHighlighter::_create() const { return syntax_highlighter; } +//// + +void EditorMarkdownSyntaxHighlighter::_update_cache() { + highlighter->set_text_edit(text_edit); + highlighter->clear_keyword_colors(); + highlighter->clear_member_keyword_colors(); + highlighter->clear_color_regions(); + + // Disable automatic symbolic highlights, as these don't make sense for prose. + highlighter->set_symbol_color(EDITOR_GET("text_editor/theme/highlighting/text_color")); + highlighter->set_number_color(EDITOR_GET("text_editor/theme/highlighting/text_color")); + highlighter->set_member_variable_color(EDITOR_GET("text_editor/theme/highlighting/text_color")); + highlighter->set_function_color(EDITOR_GET("text_editor/theme/highlighting/text_color")); + + // Headings (any level). + const Color function_color = EDITOR_GET("text_editor/theme/highlighting/function_color"); + highlighter->add_color_region("#", "", function_color); + + // Bold. + highlighter->add_color_region("**", "**", function_color); + // `__bold__` syntax is not supported as color regions must begin with a symbol, + // not a character that is valid in an identifier. + + // Code (both inline code and triple-backticks code blocks). + const Color code_color = EDITOR_GET("text_editor/theme/highlighting/engine_type_color"); + highlighter->add_color_region("`", "`", code_color); + + // Link (both references and inline links with URLs). The URL is not highlighted. + const Color link_color = EDITOR_GET("text_editor/theme/highlighting/keyword_color"); + highlighter->add_color_region("[", "]", link_color); + + // Quote. + const Color quote_color = EDITOR_GET("text_editor/theme/highlighting/string_color"); + highlighter->add_color_region(">", "", quote_color, true); + + // HTML comment, which is also supported in Markdown. + const Color comment_color = EDITOR_GET("text_editor/theme/highlighting/comment_color"); + highlighter->add_color_region("<!--", "-->", comment_color); +} + +Ref<EditorSyntaxHighlighter> EditorMarkdownSyntaxHighlighter::_create() const { + Ref<EditorMarkdownSyntaxHighlighter> syntax_highlighter; + syntax_highlighter.instantiate(); + return syntax_highlighter; +} + //////////////////////////////////////////////////////////////////////////////// /*** SCRIPT EDITOR ****/ @@ -1734,18 +1780,18 @@ void ScriptEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { tab_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("ScriptEditor"), EditorStringName(EditorStyles))); - help_search->set_icon(get_editor_theme_icon(SNAME("HelpSearch"))); - site_search->set_icon(get_editor_theme_icon(SNAME("ExternalLink"))); + help_search->set_button_icon(get_editor_theme_icon(SNAME("HelpSearch"))); + site_search->set_button_icon(get_editor_theme_icon(SNAME("ExternalLink"))); if (is_layout_rtl()) { - script_forward->set_icon(get_editor_theme_icon(SNAME("Back"))); - script_back->set_icon(get_editor_theme_icon(SNAME("Forward"))); + script_forward->set_button_icon(get_editor_theme_icon(SNAME("Back"))); + script_back->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); } else { - script_forward->set_icon(get_editor_theme_icon(SNAME("Forward"))); - script_back->set_icon(get_editor_theme_icon(SNAME("Back"))); + script_forward->set_button_icon(get_editor_theme_icon(SNAME("Forward"))); + script_back->set_button_icon(get_editor_theme_icon(SNAME("Back"))); } - members_overview_alphabeta_sort_button->set_icon(get_editor_theme_icon(SNAME("Sort"))); + members_overview_alphabeta_sort_button->set_button_icon(get_editor_theme_icon(SNAME("Sort"))); filter_scripts->set_right_icon(get_editor_theme_icon(SNAME("Search"))); filter_methods->set_right_icon(get_editor_theme_icon(SNAME("Search"))); @@ -2139,8 +2185,6 @@ void ScriptEditor::_update_script_colors() { continue; } - script_list->set_item_custom_bg_color(i, Color(0, 0, 0, 0)); - if (script_temperature_enabled) { int pass = n->get_meta("__editor_pass", -1); if (pass < 0) { @@ -2166,7 +2210,7 @@ void ScriptEditor::_update_script_names() { HashSet<Ref<Script>> used; Node *edited = EditorNode::get_singleton()->get_edited_scene(); - if (edited) { + if (edited && EDITOR_GET("text_editor/script_list/highlight_scene_scripts")) { _find_scripts(edited, edited, used); } @@ -2336,7 +2380,7 @@ void ScriptEditor::_update_script_names() { script_list->set_item_tooltip(index, sedata_filtered[i].tooltip); script_list->set_item_metadata(index, sedata_filtered[i].index); /* Saving as metadata the script's index in the tab container and not the filtered one */ if (sedata_filtered[i].used) { - script_list->set_item_custom_bg_color(index, Color(88 / 255.0, 88 / 255.0, 60 / 255.0)); + script_list->set_item_custom_bg_color(index, Color(.5, .5, .5, .125)); } if (tab_container->get_current_tab() == sedata_filtered[i].index) { script_list->select(index); @@ -4347,28 +4391,28 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { disk_changed = memnew(ConfirmationDialog); { - disk_changed->set_title(TTR("Files have been modified on disk")); + disk_changed->set_title(TTR("Files have been modified outside Godot")); VBoxContainer *vbc = memnew(VBoxContainer); disk_changed->add_child(vbc); Label *files_are_newer_label = memnew(Label); - files_are_newer_label->set_text(TTR("The following files are newer on disk.")); + files_are_newer_label->set_text(TTR("The following files are newer on disk:")); vbc->add_child(files_are_newer_label); - Label *what_action_label = memnew(Label); - what_action_label->set_text(TTR("What action should be taken?:")); - vbc->add_child(what_action_label); - disk_changed_list = memnew(Tree); vbc->add_child(disk_changed_list); disk_changed_list->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL); + Label *what_action_label = memnew(Label); + what_action_label->set_text(TTR("What action should be taken?")); + vbc->add_child(what_action_label); + disk_changed->connect(SceneStringName(confirmed), callable_mp(this, &ScriptEditor::reload_scripts).bind(false)); - disk_changed->set_ok_button_text(TTR("Discard local changes and reload")); + disk_changed->set_ok_button_text(TTR("Reload from disk")); - disk_changed->add_button(TTR("Keep local changes and overwrite"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); + disk_changed->add_button(TTR("Ignore external changes"), !DisplayServer::get_singleton()->get_swap_cancel_ok(), "resave"); disk_changed->connect("custom_action", callable_mp(this, &ScriptEditor::_resave_scripts)); } @@ -4414,6 +4458,10 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) { json_syntax_highlighter.instantiate(); register_syntax_highlighter(json_syntax_highlighter); + Ref<EditorMarkdownSyntaxHighlighter> markdown_syntax_highlighter; + markdown_syntax_highlighter.instantiate(); + register_syntax_highlighter(markdown_syntax_highlighter); + _update_online_doc(); } diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 8e82d60605..5de0aaa1e9 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -120,6 +120,24 @@ public: EditorJSONSyntaxHighlighter() { highlighter.instantiate(); } }; +class EditorMarkdownSyntaxHighlighter : public EditorSyntaxHighlighter { + GDCLASS(EditorMarkdownSyntaxHighlighter, EditorSyntaxHighlighter) + +private: + Ref<CodeHighlighter> highlighter; + +public: + virtual void _update_cache() override; + virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override { return highlighter->get_line_syntax_highlighting(p_line); } + + virtual PackedStringArray _get_supported_languages() const override { return PackedStringArray{ "md", "markdown" }; } + virtual String _get_name() const override { return TTR("Markdown"); } + + virtual Ref<EditorSyntaxHighlighter> _create() const override; + + EditorMarkdownSyntaxHighlighter() { highlighter.instantiate(); } +}; + /////////////////////////////////////////////////////////////////////////////// class ScriptEditorQuickOpen : public ConfirmationDialog { diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index b45b30b52e..cf586c792e 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -282,7 +282,7 @@ void ScriptTextEditor::_warning_clicked(const Variant &p_line) { 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 int closing_bracket_idx = prev_line.find_char(')'); const String text_to_insert = ", " + code.quote(quote_style); text_editor->insert_text(text_to_insert, line - 1, closing_bracket_idx); } else { @@ -951,7 +951,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c } else if (p_symbol.is_resource_file() || p_symbol.begins_with("uid://")) { String symbol = p_symbol; if (symbol.begins_with("uid://")) { - symbol = ResourceUID::get_singleton()->get_id_path(ResourceUID::get_singleton()->text_to_id(symbol)); + symbol = ResourceUID::uid_to_path(symbol); } List<String> scene_extensions; @@ -1205,7 +1205,7 @@ void ScriptTextEditor::_update_connected_methods() { // Account for inner classes by stripping the class names from the method, // starting from the right since our inner class might be inside of another inner class. - int pos = raw_name.rfind("."); + int pos = raw_name.rfind_char('.'); if (pos != -1) { name = raw_name.substr(pos + 1); } diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 6b00a2c9c5..5166619f90 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -396,6 +396,7 @@ void ShaderEditorPlugin::_setup_popup_menu(PopupMenuType p_type, PopupMenu *p_me if (p_type == FILE) { p_menu->add_separator(); p_menu->add_item(TTR("Open File in Inspector"), FILE_INSPECT); + p_menu->add_item(TTR("Inspect Native Shader Code..."), FILE_INSPECT_NATIVE_SHADER_CODE); p_menu->add_separator(); p_menu->add_shortcut(ED_SHORTCUT("shader_editor/close_file", TTR("Close File"), KeyModifierMask::CMD_OR_CTRL | Key::W), FILE_CLOSE); } else { @@ -554,6 +555,12 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) { EditorNode::get_singleton()->push_item(edited_shaders[index].shader_inc.ptr()); } } break; + case FILE_INSPECT_NATIVE_SHADER_CODE: { + int index = shader_tabs->get_current_tab(); + if (edited_shaders[index].shader.is_valid()) { + edited_shaders[index].shader->inspect_native_shader_code(); + } + } break; case FILE_CLOSE: { _close_shader(shader_tabs->get_current_tab()); } break; @@ -754,6 +761,7 @@ void ShaderEditorPlugin::_set_file_specific_items_disabled(bool p_disabled) { file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_SAVE), p_disabled); file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_SAVE_AS), p_disabled); file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_INSPECT), p_disabled); + file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_INSPECT_NATIVE_SHADER_CODE), p_disabled); file_popup_menu->set_item_disabled(file_popup_menu->get_item_index(FILE_CLOSE), p_disabled); } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 43e6af79fa..19e43921c3 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -69,6 +69,7 @@ class ShaderEditorPlugin : public EditorPlugin { FILE_SAVE, FILE_SAVE_AS, FILE_INSPECT, + FILE_INSPECT_NATIVE_SHADER_CODE, FILE_CLOSE, CLOSE_ALL, CLOSE_OTHER_TABS, diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp index d732d51f69..d2fd9b1cc0 100644 --- a/editor/plugins/shader_file_editor_plugin.cpp +++ b/editor/plugins/shader_file_editor_plugin.cpp @@ -63,7 +63,7 @@ void ShaderFileEditor::_version_selected(int p_option) { for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) { if (bytecode->get_stage_bytecode(RD::ShaderStage(i)).is_empty() && bytecode->get_stage_compile_error(RD::ShaderStage(i)) == String()) { - stages[i]->set_icon(Ref<Texture2D>()); + stages[i]->set_button_icon(Ref<Texture2D>()); continue; } @@ -73,7 +73,7 @@ void ShaderFileEditor::_version_selected(int p_option) { } else { icon = get_editor_theme_icon(SNAME("ImportCheck")); } - stages[i]->set_icon(icon); + stages[i]->set_button_icon(icon); if (first_found == -1) { first_found = i; diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp index 8f54641dcb..97ad0ff640 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.cpp +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -96,7 +96,7 @@ Skeleton2DEditor::Skeleton2DEditor() { CanvasItemEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text(TTR("Skeleton2D")); - options->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Skeleton2D"), EditorStringName(EditorIcons))); + options->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Skeleton2D"), EditorStringName(EditorIcons))); options->get_popup()->add_item(TTR("Reset to Rest Pose"), MENU_OPTION_SET_REST); options->get_popup()->add_separator(); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index 81beaf6f91..369a1fc864 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -123,7 +123,7 @@ void BonePropertiesEditor::_notification(int p_what) { const Color section_color = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)); section->set_bg_color(section_color); rest_section->set_bg_color(section_color); - add_metadata_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + add_metadata_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } break; } } @@ -351,12 +351,12 @@ void Skeleton3DEditor::set_keyable(const bool p_keyable) { } else { animation_hb->hide(); } -}; +} void Skeleton3DEditor::set_bone_options_enabled(const bool p_bone_options_enabled) { skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_RESET_SELECTED_POSES, !p_bone_options_enabled); skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_SELECTED_POSES_TO_RESTS, !p_bone_options_enabled); -}; +} void Skeleton3DEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("update_all"), &Skeleton3DEditor::update_all); @@ -1136,13 +1136,13 @@ void Skeleton3DEditor::_notification(int p_what) { add_theme_constant_override("separation", 0); } break; case NOTIFICATION_THEME_CHANGED: { - skeleton_options->set_icon(get_editor_theme_icon(SNAME("Skeleton3D"))); - edit_mode_button->set_icon(get_editor_theme_icon(SNAME("ToolBoneSelect"))); - key_loc_button->set_icon(get_editor_theme_icon(SNAME("KeyPosition"))); - key_rot_button->set_icon(get_editor_theme_icon(SNAME("KeyRotation"))); - key_scale_button->set_icon(get_editor_theme_icon(SNAME("KeyScale"))); - key_insert_button->set_icon(get_editor_theme_icon(SNAME("Key"))); - key_insert_all_button->set_icon(get_editor_theme_icon(SNAME("NewKey"))); + skeleton_options->set_button_icon(get_editor_theme_icon(SNAME("Skeleton3D"))); + edit_mode_button->set_button_icon(get_editor_theme_icon(SNAME("ToolBoneSelect"))); + key_loc_button->set_button_icon(get_editor_theme_icon(SNAME("KeyPosition"))); + key_rot_button->set_button_icon(get_editor_theme_icon(SNAME("KeyRotation"))); + key_scale_button->set_button_icon(get_editor_theme_icon(SNAME("KeyScale"))); + key_insert_button->set_button_icon(get_editor_theme_icon(SNAME("Key"))); + key_insert_all_button->set_button_icon(get_editor_theme_icon(SNAME("NewKey"))); bones_section->set_bg_color(get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor))); update_joint_tree(); @@ -1186,8 +1186,8 @@ Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, Skel singleton = this; // Handle. - handle_material = Ref<ShaderMaterial>(memnew(ShaderMaterial)); - handle_shader = Ref<Shader>(memnew(Shader)); + handle_material.instantiate(); + handle_shader.instantiate(); handle_shader->set_code(R"( // Skeleton 3D gizmo handle shader. diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index 0265183dfa..c6f8c5d357 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -228,14 +228,14 @@ public: void move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx); - Skeleton3D *get_skeleton() const { return skeleton; }; + Skeleton3D *get_skeleton() const { return skeleton; } bool is_edit_mode() const { return edit_mode; } void update_bone_original(); - Vector3 get_bone_original_position() const { return bone_original_position; }; - Quaternion get_bone_original_rotation() const { return bone_original_rotation; }; - Vector3 get_bone_original_scale() const { return bone_original_scale; }; + Vector3 get_bone_original_position() const { return bone_original_position; } + Quaternion get_bone_original_rotation() const { return bone_original_rotation; } + Vector3 get_bone_original_scale() const { return bone_original_scale; } Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, Skeleton3D *skeleton); ~Skeleton3DEditor(); diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp index 9b98b6ffa2..d0c8744dc2 100644 --- a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp @@ -75,7 +75,7 @@ void SkeletonIK3DEditorPlugin::make_visible(bool p_visible) { SkeletonIK3DEditorPlugin::SkeletonIK3DEditorPlugin() { play_btn = memnew(Button); - play_btn->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Play"), EditorStringName(EditorIcons))); + play_btn->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Play"), EditorStringName(EditorIcons))); play_btn->set_text(TTR("Play IK")); play_btn->set_toggle_mode(true); play_btn->hide(); diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index c7db243662..c365ad6133 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -560,7 +560,7 @@ void Sprite2DEditor::_notification(int p_what) { } break; case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - options->set_icon(get_editor_theme_icon(SNAME("Sprite2D"))); + options->set_button_icon(get_editor_theme_icon(SNAME("Sprite2D"))); options->get_popup()->set_item_icon(MENU_OPTION_CONVERT_TO_MESH_2D, get_editor_theme_icon(SNAME("MeshInstance2D"))); options->get_popup()->set_item_icon(MENU_OPTION_CONVERT_TO_POLYGON_2D, get_editor_theme_icon(SNAME("Polygon2D"))); @@ -593,12 +593,12 @@ Sprite2DEditor::Sprite2DEditor() { add_child(err_dialog); debug_uv_dialog = memnew(ConfirmationDialog); + debug_uv_dialog->set_size(Size2(960, 540) * EDSCALE); VBoxContainer *vb = memnew(VBoxContainer); debug_uv_dialog->add_child(vb); debug_uv = memnew(Panel); debug_uv->connect(SceneStringName(gui_input), callable_mp(this, &Sprite2DEditor::_debug_uv_input)); debug_uv->connect(SceneStringName(draw), callable_mp(this, &Sprite2DEditor::_debug_uv_draw)); - debug_uv->set_custom_minimum_size(Size2(800, 500) * EDSCALE); debug_uv->set_clip_contents(true); vb->add_margin_child(TTR("Preview:"), debug_uv, true); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index 168a3b3ac2..f29ace1d15 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -498,9 +498,9 @@ void SpriteFramesEditor::_toggle_show_settings() { void SpriteFramesEditor::_update_show_settings() { if (is_layout_rtl()) { - toggle_settings_button->set_icon(get_editor_theme_icon(split_sheet_settings_vb->is_visible() ? SNAME("Back") : SNAME("Forward"))); + toggle_settings_button->set_button_icon(get_editor_theme_icon(split_sheet_settings_vb->is_visible() ? SNAME("Back") : SNAME("Forward"))); } else { - toggle_settings_button->set_icon(get_editor_theme_icon(split_sheet_settings_vb->is_visible() ? SNAME("Forward") : SNAME("Back"))); + toggle_settings_button->set_button_icon(get_editor_theme_icon(split_sheet_settings_vb->is_visible() ? SNAME("Forward") : SNAME("Back"))); } } @@ -639,32 +639,32 @@ void SpriteFramesEditor::_notification(int p_what) { pause_icon = get_editor_theme_icon(SNAME("Pause")); _update_stop_icon(); - autoplay->set_icon(get_editor_theme_icon(SNAME("AutoPlay"))); - anim_loop->set_icon(get_editor_theme_icon(SNAME("Loop"))); - play->set_icon(get_editor_theme_icon(SNAME("PlayStart"))); - play_from->set_icon(get_editor_theme_icon(SNAME("Play"))); - play_bw->set_icon(get_editor_theme_icon(SNAME("PlayStartBackwards"))); - play_bw_from->set_icon(get_editor_theme_icon(SNAME("PlayBackwards"))); - - load->set_icon(get_editor_theme_icon(SNAME("Load"))); - load_sheet->set_icon(get_editor_theme_icon(SNAME("SpriteSheet"))); - copy->set_icon(get_editor_theme_icon(SNAME("ActionCopy"))); - paste->set_icon(get_editor_theme_icon(SNAME("ActionPaste"))); - empty_before->set_icon(get_editor_theme_icon(SNAME("InsertBefore"))); - empty_after->set_icon(get_editor_theme_icon(SNAME("InsertAfter"))); - move_up->set_icon(get_editor_theme_icon(SNAME("MoveLeft"))); - move_down->set_icon(get_editor_theme_icon(SNAME("MoveRight"))); - delete_frame->set_icon(get_editor_theme_icon(SNAME("Remove"))); - zoom_out->set_icon(get_editor_theme_icon(SNAME("ZoomLess"))); - zoom_reset->set_icon(get_editor_theme_icon(SNAME("ZoomReset"))); - zoom_in->set_icon(get_editor_theme_icon(SNAME("ZoomMore"))); - add_anim->set_icon(get_editor_theme_icon(SNAME("New"))); - duplicate_anim->set_icon(get_editor_theme_icon(SNAME("Duplicate"))); - delete_anim->set_icon(get_editor_theme_icon(SNAME("Remove"))); + autoplay->set_button_icon(get_editor_theme_icon(SNAME("AutoPlay"))); + anim_loop->set_button_icon(get_editor_theme_icon(SNAME("Loop"))); + play->set_button_icon(get_editor_theme_icon(SNAME("PlayStart"))); + play_from->set_button_icon(get_editor_theme_icon(SNAME("Play"))); + play_bw->set_button_icon(get_editor_theme_icon(SNAME("PlayStartBackwards"))); + play_bw_from->set_button_icon(get_editor_theme_icon(SNAME("PlayBackwards"))); + + load->set_button_icon(get_editor_theme_icon(SNAME("Load"))); + load_sheet->set_button_icon(get_editor_theme_icon(SNAME("SpriteSheet"))); + copy->set_button_icon(get_editor_theme_icon(SNAME("ActionCopy"))); + paste->set_button_icon(get_editor_theme_icon(SNAME("ActionPaste"))); + empty_before->set_button_icon(get_editor_theme_icon(SNAME("InsertBefore"))); + empty_after->set_button_icon(get_editor_theme_icon(SNAME("InsertAfter"))); + move_up->set_button_icon(get_editor_theme_icon(SNAME("MoveLeft"))); + move_down->set_button_icon(get_editor_theme_icon(SNAME("MoveRight"))); + delete_frame->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); + zoom_out->set_button_icon(get_editor_theme_icon(SNAME("ZoomLess"))); + zoom_reset->set_button_icon(get_editor_theme_icon(SNAME("ZoomReset"))); + zoom_in->set_button_icon(get_editor_theme_icon(SNAME("ZoomMore"))); + add_anim->set_button_icon(get_editor_theme_icon(SNAME("New"))); + duplicate_anim->set_button_icon(get_editor_theme_icon(SNAME("Duplicate"))); + delete_anim->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); anim_search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - split_sheet_zoom_out->set_icon(get_editor_theme_icon(SNAME("ZoomLess"))); - split_sheet_zoom_reset->set_icon(get_editor_theme_icon(SNAME("ZoomReset"))); - split_sheet_zoom_in->set_icon(get_editor_theme_icon(SNAME("ZoomMore"))); + split_sheet_zoom_out->set_button_icon(get_editor_theme_icon(SNAME("ZoomLess"))); + split_sheet_zoom_reset->set_button_icon(get_editor_theme_icon(SNAME("ZoomReset"))); + split_sheet_zoom_in->set_button_icon(get_editor_theme_icon(SNAME("ZoomMore"))); split_sheet_scroll->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); _update_show_settings(); @@ -1740,9 +1740,9 @@ void SpriteFramesEditor::_update_stop_icon() { is_playing = animated_sprite->call("is_playing"); } if (is_playing) { - stop->set_icon(pause_icon); + stop->set_button_icon(pause_icon); } else { - stop->set_icon(stop_icon); + stop->set_button_icon(stop_icon); } } diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index 0b53c10fab..2d71c617de 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -58,7 +58,7 @@ void StyleBoxPreview::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { set_texture(get_editor_theme_icon(SNAME("Checkerboard"))); - grid_preview->set_icon(get_editor_theme_icon(SNAME("StyleBoxGrid"))); + grid_preview->set_button_icon(get_editor_theme_icon(SNAME("StyleBoxGrid"))); } break; case NOTIFICATION_DRAW: { _redraw(); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index a812633480..bd653e4eed 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -845,9 +845,9 @@ void TextureRegionEditor::_notification(int p_what) { texture_preview->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("TextureRegionPreviewBG"), EditorStringName(EditorStyles))); texture_overlay->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("TextureRegionPreviewFG"), EditorStringName(EditorStyles))); - zoom_out->set_icon(get_editor_theme_icon(SNAME("ZoomLess"))); - zoom_reset->set_icon(get_editor_theme_icon(SNAME("ZoomReset"))); - zoom_in->set_icon(get_editor_theme_icon(SNAME("ZoomMore"))); + zoom_out->set_button_icon(get_editor_theme_icon(SNAME("ZoomLess"))); + zoom_reset->set_button_icon(get_editor_theme_icon(SNAME("ZoomReset"))); + zoom_in->set_button_icon(get_editor_theme_icon(SNAME("ZoomMore"))); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -1270,7 +1270,7 @@ bool EditorInspectorPluginTextureRegion::parse_property(Object *p_object, const if ((p_type == Variant::RECT2 || p_type == Variant::RECT2I)) { if (((Object::cast_to<Sprite2D>(p_object) || Object::cast_to<Sprite3D>(p_object) || Object::cast_to<NinePatchRect>(p_object) || Object::cast_to<StyleBoxTexture>(p_object)) && p_path == "region_rect") || (Object::cast_to<AtlasTexture>(p_object) && p_path == "region")) { Button *button = EditorInspector::create_inspector_action_button(TTR("Edit Region")); - button->set_icon(texture_region_editor->get_editor_theme_icon(SNAME("RegionEdit"))); + button->set_button_icon(texture_region_editor->get_editor_theme_icon(SNAME("RegionEdit"))); button->connect(SceneStringName(pressed), callable_mp(this, &EditorInspectorPluginTextureRegion::_region_edit).bind(p_object)); add_property_editor(p_path, button, true); } diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index cc488ff340..de75ed8c12 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -861,43 +861,43 @@ void ThemeItemImportTree::_notification(int p_what) { import_items_filter->set_right_icon(get_editor_theme_icon(SNAME("Search"))); // Bottom panel buttons. - import_collapse_types_button->set_icon(get_editor_theme_icon(SNAME("CollapseTree"))); - import_expand_types_button->set_icon(get_editor_theme_icon(SNAME("ExpandTree"))); + import_collapse_types_button->set_button_icon(get_editor_theme_icon(SNAME("CollapseTree"))); + import_expand_types_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandTree"))); - import_select_all_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); - import_select_full_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); - import_deselect_all_button->set_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); + import_select_all_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); + import_select_full_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); + import_deselect_all_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); // Side panel buttons. select_colors_icon->set_texture(get_editor_theme_icon(SNAME("Color"))); - deselect_all_colors_button->set_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); - select_all_colors_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); - select_full_colors_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); + deselect_all_colors_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); + select_all_colors_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); + select_full_colors_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); select_constants_icon->set_texture(get_editor_theme_icon(SNAME("MemberConstant"))); - deselect_all_constants_button->set_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); - select_all_constants_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); - select_full_constants_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); + deselect_all_constants_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); + select_all_constants_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); + select_full_constants_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); select_fonts_icon->set_texture(get_editor_theme_icon(SNAME("FontItem"))); - deselect_all_fonts_button->set_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); - select_all_fonts_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); - select_full_fonts_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); + deselect_all_fonts_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); + select_all_fonts_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); + select_full_fonts_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); select_font_sizes_icon->set_texture(get_editor_theme_icon(SNAME("FontSize"))); - deselect_all_font_sizes_button->set_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); - select_all_font_sizes_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); - select_full_font_sizes_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); + deselect_all_font_sizes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); + select_all_font_sizes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); + select_full_font_sizes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); select_icons_icon->set_texture(get_editor_theme_icon(SNAME("ImageTexture"))); - deselect_all_icons_button->set_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); - select_all_icons_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); - select_full_icons_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); + deselect_all_icons_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); + select_all_icons_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); + select_full_icons_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); select_styleboxes_icon->set_texture(get_editor_theme_icon(SNAME("StyleBoxFlat"))); - deselect_all_styleboxes_button->set_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); - select_all_styleboxes_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); - select_full_styleboxes_button->set_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); + deselect_all_styleboxes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeDeselectAll"))); + select_all_styleboxes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectAll"))); + select_full_styleboxes_button->set_button_icon(get_editor_theme_icon(SNAME("ThemeSelectFull"))); } break; } } @@ -1877,20 +1877,20 @@ void ThemeItemEditorDialog::_notification(int p_what) { [[fallthrough]]; } case NOTIFICATION_THEME_CHANGED: { - edit_items_add_color->set_icon(get_editor_theme_icon(SNAME("Color"))); - edit_items_add_constant->set_icon(get_editor_theme_icon(SNAME("MemberConstant"))); - edit_items_add_font->set_icon(get_editor_theme_icon(SNAME("FontItem"))); - edit_items_add_font_size->set_icon(get_editor_theme_icon(SNAME("FontSize"))); - edit_items_add_icon->set_icon(get_editor_theme_icon(SNAME("ImageTexture"))); - edit_items_add_stylebox->set_icon(get_editor_theme_icon(SNAME("StyleBoxFlat"))); + edit_items_add_color->set_button_icon(get_editor_theme_icon(SNAME("Color"))); + edit_items_add_constant->set_button_icon(get_editor_theme_icon(SNAME("MemberConstant"))); + edit_items_add_font->set_button_icon(get_editor_theme_icon(SNAME("FontItem"))); + edit_items_add_font_size->set_button_icon(get_editor_theme_icon(SNAME("FontSize"))); + edit_items_add_icon->set_button_icon(get_editor_theme_icon(SNAME("ImageTexture"))); + edit_items_add_stylebox->set_button_icon(get_editor_theme_icon(SNAME("StyleBoxFlat"))); - edit_items_remove_class->set_icon(get_editor_theme_icon(SNAME("Control"))); - edit_items_remove_custom->set_icon(get_editor_theme_icon(SNAME("ThemeRemoveCustomItems"))); - edit_items_remove_all->set_icon(get_editor_theme_icon(SNAME("ThemeRemoveAllItems"))); + edit_items_remove_class->set_button_icon(get_editor_theme_icon(SNAME("Control"))); + edit_items_remove_custom->set_button_icon(get_editor_theme_icon(SNAME("ThemeRemoveCustomItems"))); + edit_items_remove_all->set_button_icon(get_editor_theme_icon(SNAME("ThemeRemoveAllItems"))); - edit_add_type_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + edit_add_type_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); - import_another_theme_button->set_icon(get_editor_theme_icon(SNAME("Folder"))); + import_another_theme_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); } break; } } @@ -2481,21 +2481,21 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_ item_name_edit->hide(); Button *item_rename_button = memnew(Button); - item_rename_button->set_icon(get_editor_theme_icon(SNAME("Edit"))); + item_rename_button->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); item_rename_button->set_tooltip_text(TTR("Rename Item")); item_rename_button->set_flat(true); item_name_container->add_child(item_rename_button); item_rename_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_item_rename_cbk).bind(p_data_type, p_item_name, item_name_container)); Button *item_remove_button = memnew(Button); - item_remove_button->set_icon(get_editor_theme_icon(SNAME("Remove"))); + item_remove_button->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); item_remove_button->set_tooltip_text(TTR("Remove Item")); item_remove_button->set_flat(true); item_name_container->add_child(item_remove_button); item_remove_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_item_remove_cbk).bind(p_data_type, p_item_name)); Button *item_rename_confirm_button = memnew(Button); - item_rename_confirm_button->set_icon(get_editor_theme_icon(SNAME("ImportCheck"))); + item_rename_confirm_button->set_button_icon(get_editor_theme_icon(SNAME("ImportCheck"))); item_rename_confirm_button->set_tooltip_text(TTR("Confirm Item Rename")); item_rename_confirm_button->set_flat(true); item_name_container->add_child(item_rename_confirm_button); @@ -2503,7 +2503,7 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_ item_rename_confirm_button->hide(); Button *item_rename_cancel_button = memnew(Button); - item_rename_cancel_button->set_icon(get_editor_theme_icon(SNAME("ImportFail"))); + item_rename_cancel_button->set_button_icon(get_editor_theme_icon(SNAME("ImportFail"))); item_rename_cancel_button->set_tooltip_text(TTR("Cancel Item Rename")); item_rename_cancel_button->set_flat(true); item_name_container->add_child(item_rename_cancel_button); @@ -2513,7 +2513,7 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_ item_name->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("font_disabled_color"), EditorStringName(Editor))); Button *item_override_button = memnew(Button); - item_override_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + item_override_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); item_override_button->set_tooltip_text(TTR("Override Item")); item_override_button->set_flat(true); item_name_container->add_child(item_override_button); @@ -2722,7 +2722,7 @@ void ThemeTypeEditor::_update_type_items() { pin_leader_button->set_flat(true); pin_leader_button->set_toggle_mode(true); pin_leader_button->set_pressed(true); - pin_leader_button->set_icon(get_editor_theme_icon(SNAME("Pin"))); + pin_leader_button->set_button_icon(get_editor_theme_icon(SNAME("Pin"))); pin_leader_button->set_tooltip_text(TTR("Unpin this StyleBox as a main style.")); item_control->add_child(pin_leader_button); pin_leader_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_on_unpin_leader_button_pressed)); @@ -2765,7 +2765,7 @@ void ThemeTypeEditor::_update_type_items() { Button *pin_leader_button = memnew(Button); pin_leader_button->set_flat(true); pin_leader_button->set_toggle_mode(true); - pin_leader_button->set_icon(get_editor_theme_icon(SNAME("Pin"))); + pin_leader_button->set_button_icon(get_editor_theme_icon(SNAME("Pin"))); pin_leader_button->set_tooltip_text(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type.")); item_control->add_child(pin_leader_button); pin_leader_button->connect(SceneStringName(pressed), callable_mp(this, &ThemeTypeEditor::_on_pin_leader_button_pressed).bind(item_editor, E.key)); @@ -3371,7 +3371,7 @@ void ThemeTypeEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - add_type_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + add_type_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); data_type_tabs->set_tab_icon(0, get_editor_theme_icon(SNAME("Color"))); data_type_tabs->set_tab_icon(1, get_editor_theme_icon(SNAME("MemberConstant"))); @@ -3381,7 +3381,7 @@ void ThemeTypeEditor::_notification(int p_what) { data_type_tabs->set_tab_icon(5, get_editor_theme_icon(SNAME("StyleBoxFlat"))); data_type_tabs->set_tab_icon(6, get_editor_theme_icon(SNAME("Tools"))); - type_variation_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + type_variation_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } break; } } @@ -3695,7 +3695,7 @@ void ThemeEditor::_notification(int p_what) { preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox(SNAME("ThemeEditorPreviewBG"), EditorStringName(EditorStyles))); preview_tabs_content->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainerOdd"))); - add_preview_button->set_icon(get_editor_theme_icon(SNAME("Add"))); + add_preview_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } break; } } diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index 2426cec521..ea5e8a8ad7 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -211,7 +211,7 @@ void ThemeEditorPreview::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - picker_button->set_icon(get_editor_theme_icon(SNAME("ColorPick"))); + picker_button->set_button_icon(get_editor_theme_icon(SNAME("ColorPick"))); theme_cache.preview_picker_overlay = get_theme_stylebox(SNAME("preview_picker_overlay"), SNAME("ThemeEditor")); theme_cache.preview_picker_overlay_color = get_theme_color(SNAME("preview_picker_overlay_color"), SNAME("ThemeEditor")); @@ -489,7 +489,7 @@ void SceneThemeEditorPreview::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - reload_scene_button->set_icon(get_editor_theme_icon(SNAME("Reload"))); + reload_scene_button->set_button_icon(get_editor_theme_icon(SNAME("Reload"))); } break; } } diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index b806d1e042..bafacf8e36 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -496,13 +496,13 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_ float TileAtlasView::get_zoom() const { return zoom_widget->get_zoom(); -}; +} void TileAtlasView::set_transform(float p_zoom, Vector2i p_panning) { zoom_widget->set_zoom(p_zoom); panning = p_panning; _update_zoom_and_panning(); -}; +} void TileAtlasView::set_padding(Side p_side, int p_padding) { ERR_FAIL_COND(p_padding < 0); @@ -618,7 +618,7 @@ void TileAtlasView::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - button_center_view->set_icon(theme_cache.center_view_icon); + button_center_view->set_button_icon(theme_cache.center_view_icon); } break; } } diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index 8fcf942056..025df4fda0 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -135,8 +135,8 @@ public: void set_padding(Side p_side, int p_padding); // Left side. - void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); }; - void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); }; + void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); } + void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); } Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos, bool p_clamp = false) const; @@ -148,7 +148,7 @@ public: } p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); - }; + } // Right side. Vector3i get_alternative_tile_at_pos(const Vector2 p_pos) const; @@ -162,7 +162,7 @@ public: } p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); - }; + } // Redraw everything. void queue_redraw(); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index c6921699a4..bf53a9dfba 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -722,7 +722,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) void GenericTilePolygonEditor::_set_snap_option(int p_index) { current_snap_option = p_index; - button_pixel_snap->set_icon(button_pixel_snap->get_popup()->get_item_icon(p_index)); + button_pixel_snap->set_button_icon(button_pixel_snap->get_popup()->get_item_icon(p_index)); snap_subdivision->set_visible(p_index == SNAP_GRID); if (initializing) { @@ -880,16 +880,16 @@ void GenericTilePolygonEditor::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - button_expand->set_icon(get_editor_theme_icon(SNAME("DistractionFree"))); - button_create->set_icon(get_editor_theme_icon(SNAME("CurveCreate"))); - button_edit->set_icon(get_editor_theme_icon(SNAME("CurveEdit"))); - button_delete->set_icon(get_editor_theme_icon(SNAME("CurveDelete"))); - button_center_view->set_icon(get_editor_theme_icon(SNAME("CenterView"))); - button_advanced_menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + button_expand->set_button_icon(get_editor_theme_icon(SNAME("DistractionFree"))); + button_create->set_button_icon(get_editor_theme_icon(SNAME("CurveCreate"))); + button_edit->set_button_icon(get_editor_theme_icon(SNAME("CurveEdit"))); + button_delete->set_button_icon(get_editor_theme_icon(SNAME("CurveDelete"))); + button_center_view->set_button_icon(get_editor_theme_icon(SNAME("CenterView"))); + button_advanced_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); button_pixel_snap->get_popup()->set_item_icon(0, get_editor_theme_icon(SNAME("SnapDisable"))); button_pixel_snap->get_popup()->set_item_icon(1, get_editor_theme_icon(SNAME("Snap"))); button_pixel_snap->get_popup()->set_item_icon(2, get_editor_theme_icon(SNAME("SnapGrid"))); - button_pixel_snap->set_icon(button_pixel_snap->get_popup()->get_item_icon(current_snap_option)); + button_pixel_snap->set_button_icon(button_pixel_snap->get_popup()->get_item_icon(current_snap_option)); PopupMenu *p = button_advanced_menu->get_popup(); p->set_item_icon(p->get_item_index(ROTATE_RIGHT), get_editor_theme_icon(SNAME("RotateRight"))); @@ -1100,11 +1100,10 @@ void TileDataDefaultEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas_ } p_canvas_item->draw_set_transform_matrix(Transform2D()); } -}; +} void TileDataDefaultEditor::forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) { - -}; +} void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_set_atlas_source, const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> mm = p_event; @@ -1362,7 +1361,7 @@ void TileDataDefaultEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - picker_button->set_icon(get_editor_theme_icon(SNAME("ColorPick"))); + picker_button->set_button_icon(get_editor_theme_icon(SNAME("ColorPick"))); tile_bool_checked = get_editor_theme_icon(SNAME("TileChecked")); tile_bool_unchecked = get_editor_theme_icon(SNAME("TileUnchecked")); } break; @@ -2868,7 +2867,7 @@ void TileDataTerrainsEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - picker_button->set_icon(get_editor_theme_icon(SNAME("ColorPick"))); + picker_button->set_button_icon(get_editor_theme_icon(SNAME("ColorPick"))); } break; } } diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index 1426bb4c2f..312eb724ed 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -62,7 +62,7 @@ public: void set_tile_set(Ref<TileSet> p_tile_set); // Input to handle painting. - virtual Control *get_toolbar() { return nullptr; }; + virtual Control *get_toolbar() { return nullptr; } virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) {} virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) {} virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) {} @@ -238,7 +238,7 @@ protected: virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, const Variant &p_new_value); public: - virtual Control *get_toolbar() override { return toolbar; }; + virtual Control *get_toolbar() override { return toolbar; } virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override; @@ -375,7 +375,7 @@ protected: void _notification(int p_what); public: - virtual Control *get_toolbar() override { return toolbar; }; + virtual Control *get_toolbar() override { return toolbar; } virtual void forward_draw_over_atlas(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; virtual void forward_draw_over_alternatives(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, CanvasItem *p_canvas_item, Transform2D p_transform) override; virtual void forward_painting_atlas_gui_input(TileAtlasView *p_tile_atlas_view, TileSetAtlasSource *p_tile_atlas_source, const Ref<InputEvent> &p_event) override; diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp index dcfd92f6f9..16d4ee6f68 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.cpp +++ b/editor/plugins/tiles/tile_map_layer_editor.cpp @@ -503,21 +503,21 @@ void TileMapLayerEditorTilesPlugin::_scenes_list_lmb_empty_clicked(const Vector2 } void TileMapLayerEditorTilesPlugin::_update_theme() { - source_sort_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Sort"))); - select_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("ToolSelect"))); - paint_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Edit"))); - line_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Line"))); - rect_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Rectangle"))); - bucket_tool_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Bucket"))); - - picker_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("ColorPick"))); - erase_button->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Eraser"))); - random_tile_toggle->set_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("RandomNumberGenerator"))); - - transform_button_rotate_left->set_icon(tiles_bottom_panel->get_editor_theme_icon("RotateLeft")); - transform_button_rotate_right->set_icon(tiles_bottom_panel->get_editor_theme_icon("RotateRight")); - transform_button_flip_h->set_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorX")); - transform_button_flip_v->set_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorY")); + source_sort_button->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Sort"))); + select_tool_button->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("ToolSelect"))); + paint_tool_button->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Edit"))); + line_tool_button->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Line"))); + rect_tool_button->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Rectangle"))); + bucket_tool_button->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Bucket"))); + + picker_button->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("ColorPick"))); + erase_button->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("Eraser"))); + random_tile_toggle->set_button_icon(tiles_bottom_panel->get_editor_theme_icon(SNAME("RandomNumberGenerator"))); + + transform_button_rotate_left->set_button_icon(tiles_bottom_panel->get_editor_theme_icon("RotateLeft")); + transform_button_rotate_right->set_button_icon(tiles_bottom_panel->get_editor_theme_icon("RotateRight")); + transform_button_flip_h->set_button_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorX")); + transform_button_flip_v->set_button_icon(tiles_bottom_panel->get_editor_theme_icon("MirrorY")); missing_atlas_texture_icon = tiles_bottom_panel->get_editor_theme_icon(SNAME("TileSet")); _update_tile_set_sources_list(); @@ -1550,6 +1550,7 @@ int TileMapLayerEditorTilesPlugin::_get_transformed_alternative(int p_alternativ case TRANSFORM_ROTATE_RIGHT: { // A matrix with every possible flip/transpose combination, sorted by what comes next when you rotate. const LocalVector<bool> rotation_matrix = { + // NOLINTBEGIN(modernize-use-bool-literals) 0, 0, 0, 0, 1, 1, 1, 1, 0, @@ -1558,6 +1559,7 @@ int TileMapLayerEditorTilesPlugin::_get_transformed_alternative(int p_alternativ 0, 0, 1, 0, 1, 0, 1, 1, 1 + // NOLINTEND(modernize-use-bool-literals) }; for (int i = 0; i < 8; i++) { @@ -3486,13 +3488,13 @@ void TileMapLayerEditorTerrainsPlugin::_update_tiles_list() { } void TileMapLayerEditorTerrainsPlugin::_update_theme() { - paint_tool_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Edit"))); - line_tool_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Line"))); - rect_tool_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Rectangle"))); - bucket_tool_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Bucket"))); + paint_tool_button->set_button_icon(main_vbox_container->get_editor_theme_icon(SNAME("Edit"))); + line_tool_button->set_button_icon(main_vbox_container->get_editor_theme_icon(SNAME("Line"))); + rect_tool_button->set_button_icon(main_vbox_container->get_editor_theme_icon(SNAME("Rectangle"))); + bucket_tool_button->set_button_icon(main_vbox_container->get_editor_theme_icon(SNAME("Bucket"))); - picker_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("ColorPick"))); - erase_button->set_icon(main_vbox_container->get_editor_theme_icon(SNAME("Eraser"))); + picker_button->set_button_icon(main_vbox_container->get_editor_theme_icon(SNAME("ColorPick"))); + erase_button->set_button_icon(main_vbox_container->get_editor_theme_icon(SNAME("Eraser"))); _update_tiles_list(); } @@ -3689,12 +3691,12 @@ void TileMapLayerEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { missing_tile_texture = get_editor_theme_icon(SNAME("StatusWarning")); warning_pattern_texture = get_editor_theme_icon(SNAME("WarningPattern")); - advanced_menu_button->set_icon(get_editor_theme_icon(SNAME("Tools"))); - select_previous_layer->set_icon(get_editor_theme_icon(SNAME("MoveUp"))); - select_next_layer->set_icon(get_editor_theme_icon(SNAME("MoveDown"))); - select_all_layers->set_icon(get_editor_theme_icon(SNAME("FileList"))); - toggle_grid_button->set_icon(get_editor_theme_icon(SNAME("Grid"))); - toggle_highlight_selected_layer_button->set_icon(get_editor_theme_icon(SNAME("TileMapHighlightSelected"))); + advanced_menu_button->set_button_icon(get_editor_theme_icon(SNAME("Tools"))); + select_previous_layer->set_button_icon(get_editor_theme_icon(SNAME("MoveUp"))); + select_next_layer->set_button_icon(get_editor_theme_icon(SNAME("MoveDown"))); + select_all_layers->set_button_icon(get_editor_theme_icon(SNAME("FileList"))); + toggle_grid_button->set_button_icon(get_editor_theme_icon(SNAME("Grid"))); + toggle_highlight_selected_layer_button->set_button_icon(get_editor_theme_icon(SNAME("TileMapHighlightSelected"))); } break; case NOTIFICATION_INTERNAL_PROCESS: { diff --git a/editor/plugins/tiles/tile_map_layer_editor.h b/editor/plugins/tiles/tile_map_layer_editor.h index 805af7b58e..bcba34299d 100644 --- a/editor/plugins/tiles/tile_map_layer_editor.h +++ b/editor/plugins/tiles/tile_map_layer_editor.h @@ -63,9 +63,9 @@ public: virtual Vector<TabData> get_tabs() const { return Vector<TabData>(); - }; + } - virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; }; + virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; } virtual void forward_canvas_draw_over_viewport(Control *p_overlay) {} virtual void tile_set_changed() {} virtual void edit(ObjectID p_tile_map_layer_id) {} diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index b1417b2878..941d44c85e 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -996,7 +996,7 @@ void TileSetAtlasSourceEditor::_update_atlas_view() { // Create and position the button. Button *button = memnew(Button); button->set_flat(true); - button->set_icon(get_editor_theme_icon(SNAME("Add"))); + button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); button->add_theme_style_override(CoreStringName(normal), memnew(StyleBoxEmpty)); button->add_theme_style_override("hover", memnew(StyleBoxEmpty)); button->add_theme_style_override("focus", memnew(StyleBoxEmpty)); @@ -1699,7 +1699,7 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) { void TileSetAtlasSourceEditor::shortcut_input(const Ref<InputEvent> &p_event) { // Check for shortcuts. - if (ED_IS_SHORTCUT("tiles_editor/delete_tile", p_event)) { + if (ED_IS_SHORTCUT("tiles_editor/delete", p_event)) { if (tools_button_group->get_pressed_button() == tool_select_button && !selection.is_empty()) { _menu_option(TILE_DELETE); accept_event(); @@ -2441,12 +2441,12 @@ void TileSetAtlasSourceEditor::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - tool_setup_atlas_source_button->set_icon(get_editor_theme_icon(SNAME("Tools"))); - tool_select_button->set_icon(get_editor_theme_icon(SNAME("ToolSelect"))); - tool_paint_button->set_icon(get_editor_theme_icon(SNAME("Paint"))); + tool_setup_atlas_source_button->set_button_icon(get_editor_theme_icon(SNAME("Tools"))); + tool_select_button->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect"))); + tool_paint_button->set_button_icon(get_editor_theme_icon(SNAME("Paint"))); - tools_settings_erase_button->set_icon(get_editor_theme_icon(SNAME("Eraser"))); - tool_advanced_menu_button->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + tools_settings_erase_button->set_button_icon(get_editor_theme_icon(SNAME("Eraser"))); + tool_advanced_menu_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); outside_tiles_warning->set_texture(get_editor_theme_icon(SNAME("StatusWarning"))); resize_handle = get_editor_theme_icon(SNAME("EditorHandle")); @@ -2711,7 +2711,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_atlas_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); alternative_tile_popup_menu = memnew(PopupMenu); - alternative_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete_tile", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE); + alternative_tile_popup_menu->add_shortcut(ED_GET_SHORTCUT("tiles_editor/delete"), TILE_DELETE); alternative_tile_popup_menu->connect(SceneStringName(id_pressed), callable_mp(this, &TileSetAtlasSourceEditor::_menu_option)); tile_atlas_view->add_child(alternative_tile_popup_menu); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index c1a8338f81..f8b65bd675 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -80,7 +80,7 @@ public: int get_id() const; void edit(Ref<TileSet> p_tile_set, Ref<TileSetAtlasSource> p_tile_set_atlas_source, int p_source_id); - Ref<TileSetAtlasSource> get_edited() { return tile_set_atlas_source; }; + Ref<TileSetAtlasSource> get_edited() { return tile_set_atlas_source; } }; // -- Proxy object for a tile, needed by the inspector -- @@ -91,7 +91,7 @@ public: TileSetAtlasSourceEditor *tiles_set_atlas_source_editor = nullptr; Ref<TileSetAtlasSource> tile_set_atlas_source; - RBSet<TileSelection> tiles = RBSet<TileSelection>(); + RBSet<TileSelection> tiles; protected: bool _set(const StringName &p_name, const Variant &p_value); @@ -101,8 +101,8 @@ public: static void _bind_methods(); public: - Ref<TileSetAtlasSource> get_edited_tile_set_atlas_source() const { return tile_set_atlas_source; }; - RBSet<TileSelection> get_edited_tiles() const { return tiles; }; + Ref<TileSetAtlasSource> get_edited_tile_set_atlas_source() const { return tile_set_atlas_source; } + RBSet<TileSelection> get_edited_tiles() const { return tiles; } // Update the proxyed object. void edit(Ref<TileSetAtlasSource> p_tile_set_atlas_source, const RBSet<TileSelection> &p_tiles = RBSet<TileSelection>()); diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 7e5336ce06..6f473d1b60 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -366,10 +366,10 @@ void TileSetEditor::_set_source_sort(int p_sort) { void TileSetEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - sources_delete_button->set_icon(get_editor_theme_icon(SNAME("Remove"))); - sources_add_button->set_icon(get_editor_theme_icon(SNAME("Add"))); - source_sort_button->set_icon(get_editor_theme_icon(SNAME("Sort"))); - sources_advanced_menu_button->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + sources_delete_button->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); + sources_add_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + source_sort_button->set_button_icon(get_editor_theme_icon(SNAME("Sort"))); + sources_advanced_menu_button->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); missing_texture_texture = get_editor_theme_icon(SNAME("TileSet")); expanded_area->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), "Tree")); _update_sources_list(); 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 305407efdb..e2b3c451b0 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -367,8 +367,8 @@ void TileSetScenesCollectionSourceEditor::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - scene_tile_add_button->set_icon(get_editor_theme_icon(SNAME("Add"))); - scene_tile_delete_button->set_icon(get_editor_theme_icon(SNAME("Remove"))); + scene_tile_add_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + scene_tile_delete_button->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); _update_scenes_list(); } break; diff --git a/editor/plugins/tool_button_editor_plugin.cpp b/editor/plugins/tool_button_editor_plugin.cpp index d9852c8694..d9a15d9a23 100644 --- a/editor/plugins/tool_button_editor_plugin.cpp +++ b/editor/plugins/tool_button_editor_plugin.cpp @@ -33,7 +33,7 @@ #include "scene/gui/button.h" void EditorInspectorToolButtonPlugin::_update_action_icon(Button *p_action_button, const String &p_action_icon) { - p_action_button->set_icon(p_action_button->get_editor_theme_icon(p_action_icon)); + p_action_button->set_button_icon(p_action_button->get_editor_theme_icon(p_action_icon)); } void EditorInspectorToolButtonPlugin::_call_action(const Variant &p_object, const StringName &p_property) { diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index 4f0df1d5fc..815664c608 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -1081,7 +1081,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_ssh_public_key_input_hbc->add_child(set_up_ssh_public_key_file_dialog); Button *select_public_path_button = memnew(Button); - select_public_path_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_editor_theme_icon("Folder")); + select_public_path_button->set_button_icon(EditorNode::get_singleton()->get_gui_base()->get_editor_theme_icon("Folder")); select_public_path_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_popup_file_dialog).bind(set_up_ssh_public_key_file_dialog)); select_public_path_button->set_tooltip_text(TTR("Select SSH public key path")); set_up_ssh_public_key_input_hbc->add_child(select_public_path_button); @@ -1114,7 +1114,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { set_up_ssh_private_key_input_hbc->add_child(set_up_ssh_private_key_file_dialog); Button *select_private_path_button = memnew(Button); - select_private_path_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_editor_theme_icon("Folder")); + select_private_path_button->set_button_icon(EditorNode::get_singleton()->get_gui_base()->get_editor_theme_icon("Folder")); select_private_path_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_popup_file_dialog).bind(set_up_ssh_private_key_file_dialog)); select_private_path_button->set_tooltip_text(TTR("Select SSH private key path")); set_up_ssh_private_key_input_hbc->add_child(select_private_path_button); @@ -1159,7 +1159,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { refresh_button = memnew(Button); refresh_button->set_tooltip_text(TTR("Detect new changes")); refresh_button->set_theme_type_variation("FlatButton"); - refresh_button->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Reload"), EditorStringName(EditorIcons))); + refresh_button->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Reload"), EditorStringName(EditorIcons))); refresh_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area)); refresh_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_refresh_commit_list)); refresh_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_refresh_branch_list)); @@ -1179,14 +1179,14 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { discard_all_button = memnew(Button); discard_all_button->set_tooltip_text(TTR("Discard all changes")); - discard_all_button->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Close"), EditorStringName(EditorIcons))); + discard_all_button->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Close"), EditorStringName(EditorIcons))); discard_all_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_confirm_discard_all)); discard_all_button->set_theme_type_variation("FlatButton"); unstage_title->add_child(discard_all_button); stage_all_button = memnew(Button); stage_all_button->set_theme_type_variation("FlatButton"); - stage_all_button->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MoveDown"), EditorStringName(EditorIcons))); + stage_all_button->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MoveDown"), EditorStringName(EditorIcons))); stage_all_button->set_tooltip_text(TTR("Stage all changes")); unstage_title->add_child(stage_all_button); @@ -1216,7 +1216,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { unstage_all_button = memnew(Button); unstage_all_button->set_theme_type_variation("FlatButton"); - unstage_all_button->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MoveUp"), EditorStringName(EditorIcons))); + unstage_all_button->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MoveUp"), EditorStringName(EditorIcons))); unstage_all_button->set_tooltip_text(TTR("Unstage all changes")); stage_title->add_child(unstage_all_button); @@ -1411,26 +1411,26 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { fetch_button = memnew(Button); fetch_button->set_theme_type_variation("FlatButton"); fetch_button->set_tooltip_text(TTR("Fetch")); - fetch_button->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Reload"), EditorStringName(EditorIcons))); + fetch_button->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Reload"), EditorStringName(EditorIcons))); fetch_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_fetch)); menu_bar->add_child(fetch_button); pull_button = memnew(Button); pull_button->set_theme_type_variation("FlatButton"); pull_button->set_tooltip_text(TTR("Pull")); - pull_button->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MoveDown"), EditorStringName(EditorIcons))); + pull_button->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MoveDown"), EditorStringName(EditorIcons))); pull_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_pull)); menu_bar->add_child(pull_button); push_button = memnew(Button); push_button->set_theme_type_variation("FlatButton"); push_button->set_tooltip_text(TTR("Push")); - push_button->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MoveUp"), EditorStringName(EditorIcons))); + push_button->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("MoveUp"), EditorStringName(EditorIcons))); push_button->connect(SceneStringName(pressed), callable_mp(this, &VersionControlEditorPlugin::_push)); menu_bar->add_child(push_button); extra_options = memnew(MenuButton); - extra_options->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GuiTabMenuHl"), EditorStringName(EditorIcons))); + extra_options->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("GuiTabMenuHl"), EditorStringName(EditorIcons))); extra_options->get_popup()->connect(SNAME("about_to_popup"), callable_mp(this, &VersionControlEditorPlugin::_update_extra_options)); extra_options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &VersionControlEditorPlugin::_extra_option_selected)); menu_bar->add_child(extra_options); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index ede8351e41..31e158bba7 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -258,7 +258,7 @@ void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p vbox->add_child(offset); VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview); - port_preview->setup(visual_shader, editor->preview_material, visual_shader->get_shader_type(), p_node_id, p_port_id, p_is_valid); + port_preview->setup(visual_shader, editor->preview_material, visual_shader->get_shader_type(), links[p_node_id].output_ports[p_port_id].type == VisualShaderNode::PORT_TYPE_VECTOR_4D, p_node_id, p_port_id, p_is_valid); port_preview->set_h_size_flags(Control::SIZE_SHRINK_CENTER); vbox->add_child(port_preview); link.preview_visible = true; @@ -554,8 +554,8 @@ void VisualShaderGraphPlugin::register_link(VisualShader::Type p_type, int p_id, links.insert(p_id, { p_type, p_visual_node, p_graph_element, p_visual_node->get_output_port_for_preview() != -1, -1, HashMap<int, InputPort>(), HashMap<int, Port>(), nullptr, nullptr, nullptr, { nullptr, nullptr, nullptr } }); } -void VisualShaderGraphPlugin::register_output_port(int p_node_id, int p_port, TextureButton *p_button) { - links[p_node_id].output_ports.insert(p_port, { p_button }); +void VisualShaderGraphPlugin::register_output_port(int p_node_id, int p_port, VisualShaderNode::PortType p_port_type, TextureButton *p_button) { + links[p_node_id].output_ports.insert(p_port, { p_port_type, p_button }); } void VisualShaderGraphPlugin::register_parameter_name(int p_node_id, LineEdit *p_parameter_name) { @@ -1138,7 +1138,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool name_box->connect(SceneStringName(focus_exited), callable_mp(editor, &VisualShaderEditor::_port_name_focus_out).bind(name_box, p_id, j, false), CONNECT_DEFERRED); Button *remove_btn = memnew(Button); - remove_btn->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Remove"), EditorStringName(EditorIcons))); + remove_btn->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Remove"), EditorStringName(EditorIcons))); remove_btn->set_tooltip_text(TTR("Remove") + " " + name_left); remove_btn->connect(SceneStringName(pressed), callable_mp(editor, &VisualShaderEditor::_remove_input_port).bind(p_id, j), CONNECT_DEFERRED); hb->add_child(remove_btn); @@ -1166,7 +1166,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool if (valid_right) { if (is_group) { Button *remove_btn = memnew(Button); - remove_btn->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Remove"), EditorStringName(EditorIcons))); + remove_btn->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Remove"), EditorStringName(EditorIcons))); remove_btn->set_tooltip_text(TTR("Remove") + " " + name_left); remove_btn->connect(SceneStringName(pressed), callable_mp(editor, &VisualShaderEditor::_remove_output_port).bind(p_id, i), CONNECT_DEFERRED); hb->add_child(remove_btn); @@ -1220,7 +1220,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id, bool preview->set_texture_pressed(editor->get_editor_theme_icon(SNAME("GuiVisibilityVisible"))); preview->set_v_size_flags(Control::SIZE_SHRINK_CENTER); - register_output_port(p_id, j, preview); + register_output_port(p_id, j, port_right, preview); preview->connect(SceneStringName(pressed), callable_mp(editor, &VisualShaderEditor::_preview_select_port).bind(p_id, j), CONNECT_DEFERRED); hb->add_child(preview); @@ -1472,7 +1472,7 @@ void VisualShaderGraphPlugin::disconnect_nodes(VisualShader::Type p_type, int p_ if (visual_shader.is_valid() && visual_shader->get_shader_type() == p_type) { graph->disconnect_node(itos(p_from_node), p_from_port, itos(p_to_node), p_to_port); - for (const List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) { + for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) { if (E->get().from_node == p_from_node && E->get().from_port == p_from_port && E->get().to_node == p_to_node && E->get().to_port == p_to_port) { connections.erase(E); break; @@ -1985,6 +1985,67 @@ bool VisualShaderEditor::_update_preview_parameter_tree() { return found; } +void VisualShaderEditor::_preview_tools_menu_option(int p_idx) { + ShaderMaterial *src_mat = nullptr; + + if (p_idx == COPY_PARAMS_FROM_MATERIAL || p_idx == PASTE_PARAMS_TO_MATERIAL) { + for (int i = EditorNode::get_singleton()->get_editor_selection_history()->get_path_size() - 1; i >= 0; i--) { + Object *object = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_path_object(i)); + ShaderMaterial *src_mat2; + if (!object) { + continue; + } + if (object->has_method("get_material_override")) { // Trying to get material from MeshInstance. + src_mat2 = Object::cast_to<ShaderMaterial>(object->call("get_material_override")); + } else if (object->has_method("get_material")) { // From CanvasItem/Node2D. + src_mat2 = Object::cast_to<ShaderMaterial>(object->call("get_material")); + } else { + src_mat2 = Object::cast_to<ShaderMaterial>(object); + } + + if (src_mat2 && src_mat2->get_shader().is_valid() && src_mat2->get_shader() == visual_shader) { + src_mat = src_mat2; + break; + } + } + } + + switch (p_idx) { + case COPY_PARAMS_FROM_MATERIAL: + if (src_mat) { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Copy Preview Shader Parameters From Material")); + + List<PropertyInfo> params; + preview_material->get_shader()->get_shader_uniform_list(¶ms); + for (const PropertyInfo &E : params) { + undo_redo->add_do_method(visual_shader.ptr(), "_set_preview_shader_parameter", E.name, src_mat->get_shader_parameter(E.name)); + undo_redo->add_undo_method(visual_shader.ptr(), "_set_preview_shader_parameter", E.name, preview_material->get_shader_parameter(E.name)); + } + + undo_redo->commit_action(); + } + break; + case PASTE_PARAMS_TO_MATERIAL: + if (src_mat) { + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + undo_redo->create_action(TTR("Paste Preview Shader Parameters To Material")); + + List<PropertyInfo> params; + preview_material->get_shader()->get_shader_uniform_list(¶ms); + for (const PropertyInfo &E : params) { + undo_redo->add_do_method(src_mat, "set_shader_parameter", E.name, preview_material->get_shader_parameter(E.name)); + undo_redo->add_undo_method(src_mat, "set_shader_parameter", E.name, src_mat->get_shader_parameter(E.name)); + } + + undo_redo->commit_action(); + } + break; + default: + break; + } +} + void VisualShaderEditor::_clear_preview_param() { selected_param_id = ""; current_prop = nullptr; @@ -2128,12 +2189,11 @@ void VisualShaderEditor::_update_nodes() { } } - Array keys = added.keys(); - keys.sort(); - - for (int i = 0; i < keys.size(); i++) { - const Variant &key = keys.get(i); + List<Variant> keys; + added.get_key_list(&keys); + keys.sort_custom<StringLikeVariantOrder>(); + for (const Variant &key : keys) { const Dictionary &value = (Dictionary)added[key]; add_custom_type(value["name"], value["type"], value["script"], value["description"], value["return_icon_type"], value["category"], value["highend"]); @@ -4065,6 +4125,7 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in int from = p_from.to_int(); int to = p_to.to_int(); + bool swap = last_to_node != -1 && Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL); if (!visual_shader->can_connect_nodes(type, from, p_from_index, to, p_to_index)) { return; @@ -4082,6 +4143,14 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, E.from_node, E.from_port, E.to_node, E.to_port); + + if (swap) { + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, E.from_node, E.from_port, last_to_node, last_to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, E.from_node, E.from_port, last_to_node, last_to_port); + undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, E.from_node, E.from_port, last_to_node, last_to_port); + undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, E.from_node, E.from_port, last_to_node, last_to_port); + } + break; } } @@ -4095,6 +4164,9 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in undo_redo->add_do_method(graph_plugin.ptr(), "update_node", (int)type, to); undo_redo->add_undo_method(graph_plugin.ptr(), "update_node", (int)type, to); undo_redo->commit_action(); + + last_to_node = -1; + last_to_port = -1; } void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) { @@ -4105,6 +4177,11 @@ void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from int from = p_from.to_int(); int to = p_to.to_int(); + last_to_node = to; + last_to_port = p_to_index; + + info_label->show(); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Nodes Disconnected")); undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index); @@ -4116,6 +4193,10 @@ void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from undo_redo->commit_action(); } +void VisualShaderEditor::_connection_drag_ended() { + info_label->hide(); +} + void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) { from_node = p_from.to_int(); from_slot = p_from_slot; @@ -5020,8 +5101,11 @@ void VisualShaderEditor::_param_property_changed(const String &p_property, const void VisualShaderEditor::_update_current_param() { if (current_prop != nullptr) { String name = current_prop->get_meta("id"); - preview_material->set("shader_parameter/" + name, visual_shader->_get_preview_shader_parameter(name)); - + if (visual_shader->_has_preview_shader_parameter(name)) { + preview_material->set("shader_parameter/" + name, visual_shader->_get_preview_shader_parameter(name)); + } else { + preview_material->set("shader_parameter/" + name, Variant()); + } current_prop->update_property(); current_prop->update_editor_property_status(); current_prop->update_cache(); @@ -5107,8 +5191,8 @@ void VisualShaderEditor::_notification(int p_what) { param_filter->set_right_icon(Control::get_editor_theme_icon(SNAME("Search"))); node_filter->set_right_icon(Control::get_editor_theme_icon(SNAME("Search"))); - code_preview_button->set_icon(Control::get_editor_theme_icon(SNAME("Shader"))); - shader_preview_button->set_icon(Control::get_editor_theme_icon(SNAME("SubViewport"))); + code_preview_button->set_button_icon(Control::get_editor_theme_icon(SNAME("Shader"))); + shader_preview_button->set_button_icon(Control::get_editor_theme_icon(SNAME("SubViewport"))); { Color background_color = EDITOR_GET("text_editor/theme/highlighting/background_color"); @@ -5159,7 +5243,8 @@ void VisualShaderEditor::_notification(int p_what) { error_label->end_bulk_theme_override(); } - tools->set_icon(get_editor_theme_icon(SNAME("Tools"))); + tools->set_button_icon(get_editor_theme_icon(SNAME("Tools"))); + preview_tools->set_button_icon(get_editor_theme_icon(SNAME("Tools"))); if (is_visible_in_tree()) { _update_graph(); @@ -6306,6 +6391,7 @@ VisualShaderEditor::VisualShaderEditor() { graph->connect(SceneStringName(gui_input), callable_mp(this, &VisualShaderEditor::_graph_gui_input)); graph->connect("connection_to_empty", callable_mp(this, &VisualShaderEditor::_connection_to_empty)); graph->connect("connection_from_empty", callable_mp(this, &VisualShaderEditor::_connection_from_empty)); + graph->connect("connection_drag_ended", callable_mp(this, &VisualShaderEditor::_connection_drag_ended)); graph->connect(SceneStringName(visibility_changed), callable_mp(this, &VisualShaderEditor::_visibility_changed)); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR_INT); @@ -6366,6 +6452,13 @@ VisualShaderEditor::VisualShaderEditor() { graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SAMPLER, VisualShaderNode::PORT_TYPE_SAMPLER); + info_label = memnew(Label); + info_label->set_text(vformat(TTR("Hold %s Key To Swap Connections"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL))); + info_label->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_WIDE, PRESET_MODE_MINSIZE, 20); + info_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); + info_label->hide(); + graph->get_top_layer()->add_child(info_label); + PanelContainer *toolbar_panel = static_cast<PanelContainer *>(graph->get_menu_hbox()->get_parent()); toolbar_panel->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE, PRESET_MODE_MINSIZE, 10); toolbar_panel->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); @@ -6556,11 +6649,21 @@ VisualShaderEditor::VisualShaderEditor() { VBoxContainer *params_vbox = memnew(VBoxContainer); preview_split->add_child(params_vbox); + HBoxContainer *filter_hbox = memnew(HBoxContainer); + params_vbox->add_child(filter_hbox); + param_filter = memnew(LineEdit); + filter_hbox->add_child(param_filter); param_filter->connect(SceneStringName(text_changed), callable_mp(this, &VisualShaderEditor::_param_filter_changed)); param_filter->set_h_size_flags(SIZE_EXPAND_FILL); param_filter->set_placeholder(TTR("Filter Parameters")); - params_vbox->add_child(param_filter); + + preview_tools = memnew(MenuButton); + filter_hbox->add_child(preview_tools); + preview_tools->set_tooltip_text(TTR("Options")); + preview_tools->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &VisualShaderEditor::_preview_tools_menu_option)); + preview_tools->get_popup()->add_item(TTR("Copy Parameters From Material"), COPY_PARAMS_FROM_MATERIAL); + preview_tools->get_popup()->add_item(TTR("Paste Parameters To Material"), PASTE_PARAMS_TO_MATERIAL); ScrollContainer *sc = memnew(ScrollContainer); sc->set_v_size_flags(SIZE_EXPAND_FILL); @@ -6776,8 +6879,10 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("Grayscale", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), { VisualShaderNodeColorFunc::FUNC_GRAYSCALE }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("HSV2RGB", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Converts HSV vector to RGB equivalent."), { VisualShaderNodeColorFunc::FUNC_HSV2RGB, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("LinearToSRGB", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Converts color from linear to sRGB color space."), { VisualShaderNodeColorFunc::FUNC_LINEAR_TO_SRGB }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("RGB2HSV", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Converts RGB vector to HSV equivalent."), { VisualShaderNodeColorFunc::FUNC_RGB2HSV, VisualShaderNodeVectorFunc::OP_TYPE_VECTOR_3D }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Sepia", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), { VisualShaderNodeColorFunc::FUNC_SEPIA }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("SRGBToLinear", "Color/Functions", "VisualShaderNodeColorFunc", TTR("Converts color from sRGB to linear color space."), { VisualShaderNodeColorFunc::FUNC_SRGB_TO_LINEAR }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Burn", "Color/Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), { VisualShaderNodeColorOp::OP_BURN }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); add_options.push_back(AddOption("Darken", "Color/Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), { VisualShaderNodeColorOp::OP_DARKEN }, VisualShaderNode::PORT_TYPE_VECTOR_3D)); @@ -6923,7 +7028,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("NodePositionView", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_view", "NODE_POSITION_VIEW"), { "node_position_view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("NodePositionWorld", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "node_position_world", "NODE_POSITION_WORLD"), { "node_position_world" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("PointCoord", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "point_coord", "POINT_COORD"), { "point_coord" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); - add_options.push_back(AddOption("ScreenUV", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_shader_mode, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ScreenUV", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Tangent", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "tangent", "TANGENT"), { "tangent" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Vertex", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_vertex_and_fragment_shader_modes, "vertex", "VERTEX"), { "vertex" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("View", "Input/Fragment", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view", "VIEW"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); @@ -6941,6 +7046,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("LightIsDirectional", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "light_is_directional", "LIGHT_IS_DIRECTIONAL"), { "light_is_directional" }, VisualShaderNode::PORT_TYPE_BOOLEAN, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Metallic", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "metallic", "METALLIC"), { "metallic" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Roughness", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "roughness", "ROUGHNESS"), { "roughness" }, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ScreenUV", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "screen_uv", "SCREEN_UV"), { "screen_uv" }, VisualShaderNode::PORT_TYPE_VECTOR_2D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("Specular", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_light_shader_mode, "specular", "SPECULAR_LIGHT"), { "specular" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); add_options.push_back(AddOption("View", "Input/Light", "VisualShaderNodeInput", vformat(input_param_for_fragment_and_light_shader_modes, "view", "VIEW"), { "view" }, VisualShaderNode::PORT_TYPE_VECTOR_3D, TYPE_FLAGS_LIGHT, Shader::MODE_SPATIAL)); @@ -8032,7 +8138,15 @@ void VisualShaderNodePortPreview::_shader_changed() { set_material(mat); } -void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, Ref<ShaderMaterial> &p_preview_material, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid) { +void VisualShaderNodePortPreview::setup(const Ref<VisualShader> &p_shader, Ref<ShaderMaterial> &p_preview_material, VisualShader::Type p_type, bool p_has_transparency, int p_node, int p_port, bool p_is_valid) { + if (p_has_transparency) { + checkerboard = memnew(TextureRect); + checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE); + checkerboard->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + checkerboard->set_draw_behind_parent(true); + add_child(checkerboard); + } + shader = p_shader; shader->connect_changed(callable_mp(this, &VisualShaderNodePortPreview::_shader_changed), CONNECT_DEFERRED); preview_mat = p_preview_material; @@ -8051,6 +8165,11 @@ Size2 VisualShaderNodePortPreview::get_minimum_size() const { void VisualShaderNodePortPreview::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + if (checkerboard != nullptr) { + checkerboard->set_texture(get_theme_icon(SNAME("GuiMiniCheckerboard"), EditorStringName(EditorIcons))); + } + } break; case NOTIFICATION_DRAW: { Vector<Vector2> points = { Vector2(), diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 3b2ad33304..6b7c07e5a7 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -107,6 +107,7 @@ private: }; struct Port { + VisualShaderNode::PortType type = VisualShaderNode::PORT_TYPE_SCALAR; TextureButton *preview_button = nullptr; }; @@ -141,7 +142,7 @@ public: void register_shader(VisualShader *p_visual_shader); void set_connections(const List<VisualShader::Connection> &p_connections); void register_link(VisualShader::Type p_type, int p_id, VisualShaderNode *p_visual_node, GraphElement *p_graph_element); - void register_output_port(int p_id, int p_port, TextureButton *p_button); + void register_output_port(int p_id, int p_port, VisualShaderNode::PortType p_port_type, TextureButton *p_button); void register_parameter_name(int p_id, LineEdit *p_parameter_name); void register_default_input_button(int p_node_id, int p_port_id, Button *p_button); void register_expression_edit(int p_node_id, CodeEdit *p_expression_edit); @@ -220,6 +221,10 @@ class VisualShaderEditor : public ShaderEditor { Button *code_preview_button = nullptr; Button *shader_preview_button = nullptr; + int last_to_node = -1; + int last_to_port = -1; + Label *info_label = nullptr; + OptionButton *edit_type = nullptr; OptionButton *edit_type_standard = nullptr; OptionButton *edit_type_particles = nullptr; @@ -275,6 +280,7 @@ class VisualShaderEditor : public ShaderEditor { bool shader_preview_showed = true; LineEdit *param_filter = nullptr; + MenuButton *preview_tools = nullptr; String selected_param_id; Tree *parameters = nullptr; HashMap<String, PropertyInfo> parameter_props; @@ -317,6 +323,11 @@ class VisualShaderEditor : public ShaderEditor { COLLAPSE_ALL }; + enum PreviewToolsMenuOptions { + COPY_PARAMS_FROM_MATERIAL, + PASTE_PARAMS_TO_MATERIAL, + }; + #ifdef MINGW_ENABLED #undef DELETE #endif @@ -366,6 +377,7 @@ class VisualShaderEditor : public ShaderEditor { void _show_add_varying_dialog(); void _show_remove_varying_dialog(); + void _preview_tools_menu_option(int p_idx); void _clear_preview_param(); void _update_preview_parameter_list(); bool _update_preview_parameter_tree(); @@ -494,6 +506,7 @@ class VisualShaderEditor : public ShaderEditor { void _unlink_node_from_parent_frame(int p_node_id); + void _connection_drag_ended(); void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position); void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position); bool _check_node_drop_on_connection(const Vector2 &p_position, Ref<GraphEdit::Connection> *r_closest_connection, int *r_node_id = nullptr, int *r_to_port = nullptr); @@ -680,6 +693,7 @@ public: class VisualShaderNodePortPreview : public Control { GDCLASS(VisualShaderNodePortPreview, Control); + TextureRect *checkerboard = nullptr; Ref<VisualShader> shader; Ref<ShaderMaterial> preview_mat; VisualShader::Type type = VisualShader::Type::TYPE_MAX; @@ -692,7 +706,7 @@ protected: public: virtual Size2 get_minimum_size() const override; - void setup(const Ref<VisualShader> &p_shader, Ref<ShaderMaterial> &p_preview_material, VisualShader::Type p_type, int p_node, int p_port, bool p_is_valid); + void setup(const Ref<VisualShader> &p_shader, Ref<ShaderMaterial> &p_preview_material, VisualShader::Type p_type, bool p_has_transparency, int p_node, int p_port, bool p_is_valid); }; class VisualShaderConversionPlugin : public EditorResourceConversionPlugin { diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp index 3e835d5cb6..68fe013c08 100644 --- a/editor/plugins/voxel_gi_editor_plugin.cpp +++ b/editor/plugins/voxel_gi_editor_plugin.cpp @@ -185,7 +185,7 @@ VoxelGIEditorPlugin::VoxelGIEditorPlugin() { bake->set_theme_type_variation("FlatButton"); // TODO: Rework this as a dedicated toolbar control so we can hook into theme changes and update it // when the editor theme updates. - bake->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons))); + bake->set_button_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Bake"), EditorStringName(EditorIcons))); bake->set_text(TTR("Bake VoxelGI")); bake->connect(SceneStringName(pressed), callable_mp(this, &VoxelGIEditorPlugin::_bake)); bake_hb->add_child(bake); diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp index 76b6593f1d..3598a29fec 100644 --- a/editor/pot_generator.cpp +++ b/editor/pot_generator.cpp @@ -69,11 +69,16 @@ void POTGenerator::generate_pot(const String &p_file) { for (int i = 0; i < files.size(); i++) { Vector<String> msgids; Vector<Vector<String>> msgids_context_plural; + + Vector<String> msgids_comment; + Vector<String> msgids_context_plural_comment; + const String &file_path = files[i]; String file_extension = file_path.get_extension(); if (EditorTranslationParser::get_singleton()->can_parse(file_extension)) { EditorTranslationParser::get_singleton()->get_parser(file_extension)->parse_file(file_path, &msgids, &msgids_context_plural); + EditorTranslationParser::get_singleton()->get_parser(file_extension)->get_comments(&msgids_comment, &msgids_context_plural_comment); } else { ERR_PRINT("Unrecognized file extension " + file_extension + " in generate_pot()"); return; @@ -81,16 +86,18 @@ void POTGenerator::generate_pot(const String &p_file) { for (int j = 0; j < msgids_context_plural.size(); j++) { const Vector<String> &entry = msgids_context_plural[j]; - _add_new_msgid(entry[0], entry[1], entry[2], file_path); + const String &comment = (j < msgids_context_plural_comment.size()) ? msgids_context_plural_comment[j] : String(); + _add_new_msgid(entry[0], entry[1], entry[2], file_path, comment); } for (int j = 0; j < msgids.size(); j++) { - _add_new_msgid(msgids[j], "", "", file_path); + const String &comment = (j < msgids_comment.size()) ? msgids_comment[j] : String(); + _add_new_msgid(msgids[j], "", "", file_path, comment); } } if (GLOBAL_GET("internationalization/locale/translation_add_builtin_strings_to_pot")) { for (const Vector<String> &extractable_msgids : get_extractable_message_list()) { - _add_new_msgid(extractable_msgids[0], extractable_msgids[1], extractable_msgids[2], ""); + _add_new_msgid(extractable_msgids[0], extractable_msgids[1], extractable_msgids[2], "", ""); } } @@ -136,15 +143,25 @@ void POTGenerator::_write_to_pot(const String &p_file) { String context = v_msgid_data[i].ctx; String plural = v_msgid_data[i].plural; const HashSet<String> &locations = v_msgid_data[i].locations; + const HashSet<String> &comments = v_msgid_data[i].comments; // Put the blank line at the start, to avoid a double at the end when closing the file. file->store_line(""); + // Write comments. + bool is_first_comment = true; + for (const String &E : comments) { + if (is_first_comment) { + file->store_line("#. TRANSLATORS: " + E.replace("\n", "\n#. ")); + } else { + file->store_line("#. " + E.replace("\n", "\n#. ")); + } + is_first_comment = false; + } + // Write file locations. for (const String &E : locations) { - if (!E.is_empty()) { - file->store_line("#: " + E.trim_prefix("res://").replace("\n", "\\n")); - } + file->store_line("#: " + E.trim_prefix("res://").replace("\n", "\\n")); } // Write context. @@ -199,7 +216,7 @@ void POTGenerator::_write_msgid(Ref<FileAccess> r_file, const String &p_id, bool } } -void POTGenerator::_add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location) { +void POTGenerator::_add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location, const String &p_comment) { // Insert new location if msgid under same context exists already. if (all_translation_strings.has(p_msgid)) { Vector<MsgidData> &v_mdata = all_translation_strings[p_msgid]; @@ -208,18 +225,27 @@ void POTGenerator::_add_new_msgid(const String &p_msgid, const String &p_context if (!v_mdata[i].plural.is_empty() && !p_plural.is_empty() && v_mdata[i].plural != p_plural) { WARN_PRINT("Redefinition of plural message (msgid_plural), under the same message (msgid) and context (msgctxt)"); } - v_mdata.write[i].locations.insert(p_location); + if (!p_location.is_empty()) { + v_mdata.write[i].locations.insert(p_location); + } + if (!p_comment.is_empty()) { + v_mdata.write[i].comments.insert(p_comment); + } return; } } } - // Add a new entry of msgid, context, plural and location - context and plural might be empty if the inserted msgid doesn't associated - // context or plurals. + // Add a new entry. MsgidData mdata; mdata.ctx = p_context; mdata.plural = p_plural; - mdata.locations.insert(p_location); + if (!p_location.is_empty()) { + mdata.locations.insert(p_location); + } + if (!p_comment.is_empty()) { + mdata.comments.insert(p_comment); + } all_translation_strings[p_msgid].push_back(mdata); } diff --git a/editor/pot_generator.h b/editor/pot_generator.h index 8bcb2e5cac..54f4aa0652 100644 --- a/editor/pot_generator.h +++ b/editor/pot_generator.h @@ -44,13 +44,14 @@ class POTGenerator { String ctx; String plural; HashSet<String> locations; + HashSet<String> comments; }; // Store msgid as key and the additional data around the msgid - if it's under a context, has plurals and its file locations. HashMap<String, Vector<MsgidData>> all_translation_strings; void _write_to_pot(const String &p_file); void _write_msgid(Ref<FileAccess> r_file, const String &p_id, bool p_plural); - void _add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location); + void _add_new_msgid(const String &p_msgid, const String &p_context, const String &p_plural, const String &p_location, const String &p_comment); #ifdef DEBUG_POT void _print_all_translation_strings(); diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index b295e5733e..edf3ff7296 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -716,8 +716,9 @@ Vector<String> ProjectConverter3To4::check_for_files() { directories_to_check.append(current_dir.path_join(file_name) + "/"); } else { bool proper_extension = false; - if (file_name.ends_with(".gd") || file_name.ends_with(".shader") || file_name.ends_with(".gdshader") || file_name.ends_with(".tscn") || file_name.ends_with(".tres") || file_name.ends_with(".godot") || file_name.ends_with(".cs") || file_name.ends_with(".csproj") || file_name.ends_with(".import")) + if (file_name.ends_with(".gd") || file_name.ends_with(".shader") || file_name.ends_with(".gdshader") || file_name.ends_with(".tscn") || file_name.ends_with(".tres") || file_name.ends_with(".godot") || file_name.ends_with(".cs") || file_name.ends_with(".csproj") || file_name.ends_with(".import")) { proper_extension = true; + } if (proper_extension) { collected_files.append(current_dir.path_join(file_name)); @@ -1270,7 +1271,7 @@ bool ProjectConverter3To4::test_single_array(const char *p_array[][2], bool p_ig } } return valid; -}; +} // Returns arguments from given function execution, this cannot be really done as regex. // `abc(d,e(f,g),h)` -> [d], [e(f,g)], [h] @@ -1321,8 +1322,9 @@ Vector<String> ProjectConverter3To4::parse_arguments(const String &line) { break; }; case '"': { - if (previous_character != '\\') + if (previous_character != '\\') { is_inside_string = !is_inside_string; + } } } previous_character = character; @@ -1469,7 +1471,7 @@ void ProjectConverter3To4::rename_colors(Vector<SourceLine> &source_lines, const } } } -}; +} // Convert hexadecimal colors from ARGB to RGBA void ProjectConverter3To4::convert_hexadecimal_colors(Vector<SourceLine> &source_lines, const RegExContainer ®_container) { @@ -1566,7 +1568,7 @@ void ProjectConverter3To4::rename_classes(Vector<SourceLine> &source_lines, cons } } } -}; +} Vector<String> ProjectConverter3To4::check_for_rename_classes(Vector<String> &lines, const RegExContainer ®_container) { Vector<String> found_renames; @@ -1618,7 +1620,7 @@ void ProjectConverter3To4::rename_gdscript_functions(Vector<SourceLine> &source_ process_gdscript_line(line, reg_container, builtin); } } -}; +} Vector<String> ProjectConverter3To4::check_for_rename_gdscript_functions(Vector<String> &lines, const RegExContainer ®_container, bool builtin) { int current_line = 1; @@ -1968,7 +1970,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai // -- func c(var a, var b) -> func c(a, b) if (line.contains("func ") && line.contains("var ")) { int start = line.find("func "); - start = line.substr(start).find("(") + start; + start = line.substr(start).find_char('(') + start; int end = get_end_parenthesis(line.substr(start)) + 1; if (end > -1) { Vector<String> parts = parse_arguments(line.substr(start, end)); @@ -2118,12 +2120,12 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai } } // -- func _init(p_x:int).(p_x): -> func _init(p_x:int):\n\tsuper(p_x) Object # https://github.com/godotengine/godot/issues/70542 - if (line.contains(" _init(") && line.rfind(":") > 0) { + if (line.contains(" _init(") && line.rfind_char(':') > 0) { // func _init(p_arg1).(super4, super5, super6)->void: // ^--^indent ^super_start super_end^ int indent = line.count("\t", 0, line.find("func")); int super_start = line.find(".("); - int super_end = line.rfind(")"); + int super_end = line.rfind_char(')'); if (super_start > 0 && super_end > super_start) { line = line.substr(0, super_start) + line.substr(super_end + 1) + "\n" + String("\t").repeat(indent + 1) + "super" + line.substr(super_start + 1, super_end - super_start); } @@ -2438,7 +2440,7 @@ void ProjectConverter3To4::rename_csharp_functions(Vector<SourceLine> &source_li process_csharp_line(line, reg_container); } } -}; +} Vector<String> ProjectConverter3To4::check_for_rename_csharp_functions(Vector<String> &lines, const RegExContainer ®_container) { int current_line = 1; @@ -2847,7 +2849,7 @@ void ProjectConverter3To4::custom_rename(Vector<SourceLine> &source_lines, const line = reg.sub(line, to, true); } } -}; +} Vector<String> ProjectConverter3To4::check_for_custom_rename(Vector<String> &lines, const String &from, const String &to) { Vector<String> found_renames; diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 30878a2488..eb5e8d2a72 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -34,8 +34,6 @@ #include "core/io/config_file.h" #include "core/io/dir_access.h" #include "core/io/file_access.h" -#include "core/io/resource_saver.h" -#include "core/io/stream_peer_tls.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/version.h" @@ -51,12 +49,9 @@ #include "editor/project_manager/project_list.h" #include "editor/project_manager/project_tag.h" #include "editor/project_manager/quick_settings_dialog.h" -#include "editor/themes/editor_icons.h" #include "editor/themes/editor_scale.h" #include "editor/themes/editor_theme_manager.h" #include "main/main.h" -#include "scene/gui/check_box.h" -#include "scene/gui/color_rect.h" #include "scene/gui/flow_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" @@ -64,7 +59,6 @@ #include "scene/gui/panel_container.h" #include "scene/gui/rich_text_label.h" #include "scene/gui/separator.h" -#include "scene/gui/texture_rect.h" #include "scene/main/window.h" #include "scene/theme/theme_db.h" #include "servers/display_server.h" @@ -224,7 +218,7 @@ void ProjectManager::_update_theme(bool p_skip_creation) { background_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("Background"), EditorStringName(EditorStyles))); main_view_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("TabContainer"))); - title_bar_logo->set_icon(get_editor_theme_icon(SNAME("TitleBarLogo"))); + title_bar_logo->set_button_icon(get_editor_theme_icon(SNAME("TitleBarLogo"))); _set_main_view_icon(MAIN_VIEW_PROJECTS, get_editor_theme_icon(SNAME("ProjectList"))); _set_main_view_icon(MAIN_VIEW_ASSETLIB, get_editor_theme_icon(SNAME("AssetLib"))); @@ -234,28 +228,28 @@ void ProjectManager::_update_theme(bool p_skip_creation) { loading_label->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("bold"), EditorStringName(EditorFonts))); project_list_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("project_list"), SNAME("ProjectManager"))); - empty_list_create_project->set_icon(get_editor_theme_icon(SNAME("Add"))); - empty_list_import_project->set_icon(get_editor_theme_icon(SNAME("Load"))); - empty_list_open_assetlib->set_icon(get_editor_theme_icon(SNAME("AssetLib"))); + empty_list_create_project->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + empty_list_import_project->set_button_icon(get_editor_theme_icon(SNAME("Load"))); + empty_list_open_assetlib->set_button_icon(get_editor_theme_icon(SNAME("AssetLib"))); empty_list_online_warning->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("italic"), EditorStringName(EditorFonts))); empty_list_online_warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("font_placeholder_color"), EditorStringName(Editor))); // Top bar. search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - quick_settings_button->set_icon(get_editor_theme_icon(SNAME("Tools"))); + quick_settings_button->set_button_icon(get_editor_theme_icon(SNAME("Tools"))); // Sidebar. - create_btn->set_icon(get_editor_theme_icon(SNAME("Add"))); - import_btn->set_icon(get_editor_theme_icon(SNAME("Load"))); - scan_btn->set_icon(get_editor_theme_icon(SNAME("Search"))); - open_btn->set_icon(get_editor_theme_icon(SNAME("Edit"))); - run_btn->set_icon(get_editor_theme_icon(SNAME("Play"))); - rename_btn->set_icon(get_editor_theme_icon(SNAME("Rename"))); - manage_tags_btn->set_icon(get_editor_theme_icon("Script")); - erase_btn->set_icon(get_editor_theme_icon(SNAME("Remove"))); - erase_missing_btn->set_icon(get_editor_theme_icon(SNAME("Clear"))); - create_tag_btn->set_icon(get_editor_theme_icon("Add")); + create_btn->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + import_btn->set_button_icon(get_editor_theme_icon(SNAME("Load"))); + scan_btn->set_button_icon(get_editor_theme_icon(SNAME("Search"))); + open_btn->set_button_icon(get_editor_theme_icon(SNAME("Edit"))); + run_btn->set_button_icon(get_editor_theme_icon(SNAME("Play"))); + rename_btn->set_button_icon(get_editor_theme_icon(SNAME("Rename"))); + manage_tags_btn->set_button_icon(get_editor_theme_icon("Script")); + erase_btn->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); + erase_missing_btn->set_button_icon(get_editor_theme_icon(SNAME("Clear"))); + create_tag_btn->set_button_icon(get_editor_theme_icon("Add")); tag_error->add_theme_color_override(SceneStringName(font_color), get_theme_color("error_color", EditorStringName(Editor))); tag_edit_error->add_theme_color_override(SceneStringName(font_color), get_theme_color("error_color", EditorStringName(Editor))); @@ -310,17 +304,17 @@ void ProjectManager::_set_main_view_icon(MainViewTab p_id, const Ref<Texture2D> Button *toggle_button = main_view_toggle_map[p_id]; - Ref<Texture2D> old_icon = toggle_button->get_icon(); + Ref<Texture2D> old_icon = toggle_button->get_button_icon(); if (old_icon.is_valid()) { old_icon->disconnect_changed(callable_mp((Control *)toggle_button, &Control::update_minimum_size)); } if (p_icon.is_valid()) { - toggle_button->set_icon(p_icon); + toggle_button->set_button_icon(p_icon); // Make sure the control is updated if the icon is reimported. p_icon->connect_changed(callable_mp((Control *)toggle_button, &Control::update_minimum_size)); } else { - toggle_button->set_icon(Ref<Texture2D>()); + toggle_button->set_button_icon(Ref<Texture2D>()); } } @@ -1300,15 +1294,10 @@ ProjectManager::ProjectManager() { filter_option->connect(SceneStringName(item_selected), callable_mp(this, &ProjectManager::_on_order_option_changed)); hb->add_child(filter_option); - Vector<String> sort_filter_titles; - sort_filter_titles.push_back(TTR("Last Edited")); - sort_filter_titles.push_back(TTR("Name")); - sort_filter_titles.push_back(TTR("Path")); - sort_filter_titles.push_back(TTR("Tags")); - - for (int i = 0; i < sort_filter_titles.size(); i++) { - filter_option->add_item(sort_filter_titles[i]); - } + filter_option->add_item(TTR("Last Edited")); + filter_option->add_item(TTR("Name")); + filter_option->add_item(TTR("Path")); + filter_option->add_item(TTR("Tags")); } // Project list and its sidebar. diff --git a/editor/project_manager/project_dialog.cpp b/editor/project_manager/project_dialog.cpp index 7aadb9ac3c..8750ae8714 100644 --- a/editor/project_manager/project_dialog.cpp +++ b/editor/project_manager/project_dialog.cpp @@ -825,9 +825,9 @@ void ProjectDialog::show_dialog(bool p_reset_name) { void ProjectDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - create_dir->set_icon(get_editor_theme_icon(SNAME("FolderCreate"))); - project_browse->set_icon(get_editor_theme_icon(SNAME("FolderBrowse"))); - install_browse->set_icon(get_editor_theme_icon(SNAME("FolderBrowse"))); + create_dir->set_button_icon(get_editor_theme_icon(SNAME("FolderCreate"))); + project_browse->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse"))); + install_browse->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse"))); } break; case NOTIFICATION_READY: { fdialog_project = memnew(EditorFileDialog); diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp index 541ab01e62..27f04c0d0d 100644 --- a/editor/project_manager/project_list.cpp +++ b/editor/project_manager/project_list.cpp @@ -67,9 +67,9 @@ void ProjectListItemControl::_notification(int p_what) { favorite_button->set_texture_normal(get_editor_theme_icon(SNAME("Favorites"))); if (project_is_missing) { - explore_button->set_icon(get_editor_theme_icon(SNAME("FileBroken"))); + explore_button->set_button_icon(get_editor_theme_icon(SNAME("FileBroken"))); } else { - explore_button->set_icon(get_editor_theme_icon(SNAME("Load"))); + explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load"))); } } break; @@ -88,7 +88,7 @@ void ProjectListItemControl::_notification(int p_what) { draw_style_box(get_theme_stylebox(SNAME("selected"), SNAME("Tree")), Rect2(Point2(), get_size())); } if (is_hovering) { - draw_style_box(get_theme_stylebox(SNAME("hover"), SNAME("Tree")), Rect2(Point2(), get_size())); + draw_style_box(get_theme_stylebox(SNAME("hovered"), SNAME("Tree")), Rect2(Point2(), get_size())); } draw_line(Point2(0, get_size().y + 1), Point2(get_size().x, get_size().y + 1), get_theme_color(SNAME("guide_color"), SNAME("Tree"))); @@ -196,12 +196,12 @@ void ProjectListItemControl::set_is_missing(bool p_missing) { if (project_is_missing) { project_icon->set_modulate(Color(1, 1, 1, 0.5)); - explore_button->set_icon(get_editor_theme_icon(SNAME("FileBroken"))); + explore_button->set_button_icon(get_editor_theme_icon(SNAME("FileBroken"))); explore_button->set_tooltip_text(TTR("Error: Project is missing on the filesystem.")); } else { project_icon->set_modulate(Color(1, 1, 1, 1.0)); - explore_button->set_icon(get_editor_theme_icon(SNAME("Load"))); + explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load"))); #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED) explore_button->set_tooltip_text(TTR("Show in File Manager")); #else diff --git a/editor/project_manager/project_tag.cpp b/editor/project_manager/project_tag.cpp index 618b6555d4..e66969333c 100644 --- a/editor/project_manager/project_tag.cpp +++ b/editor/project_manager/project_tag.cpp @@ -36,7 +36,7 @@ void ProjectTag::_notification(int p_what) { if (display_close && p_what == NOTIFICATION_THEME_CHANGED) { - button->set_icon(get_theme_icon(SNAME("close"), SNAME("TabBar"))); + button->set_button_icon(get_theme_icon(SNAME("close"), SNAME("TabBar"))); } } diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index f973367bed..97f1d5d641 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -31,6 +31,7 @@ #include "project_settings_editor.h" #include "core/config/project_settings.h" +#include "core/input/input_map.h" #include "editor/editor_inspector.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" @@ -390,7 +391,7 @@ void ProjectSettingsEditor::_action_added(const String &p_name) { Dictionary action; action["events"] = Array(); - action["deadzone"] = 0.2f; + action["deadzone"] = InputMap::DEFAULT_DEADZONE; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add Input Action")); @@ -576,10 +577,10 @@ void ProjectSettingsEditor::_update_action_map_editor() { } void ProjectSettingsEditor::_update_theme() { - add_button->set_icon(get_editor_theme_icon(SNAME("Add"))); - del_button->set_icon(get_editor_theme_icon(SNAME("Remove"))); + add_button->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + del_button->set_button_icon(get_editor_theme_icon(SNAME("Remove"))); search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); - restart_close_button->set_icon(get_editor_theme_icon(SNAME("Close"))); + restart_close_button->set_button_icon(get_editor_theme_icon(SNAME("Close"))); restart_container->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SceneStringName(panel), SNAME("Tree"))); restart_icon->set_texture(get_editor_theme_icon(SNAME("StatusWarning"))); restart_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index c4ebca7308..2d3cbfb1e3 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -52,6 +52,7 @@ #include "editor/filesystem_dock.h" #include "editor/gui/editor_file_dialog.h" #include "editor/gui/editor_spin_slider.h" +#include "editor/gui/editor_toaster.h" #include "editor/import/3d/resource_importer_obj.h" #include "editor/import/3d/resource_importer_scene.h" #include "editor/import/editor_import_plugin.h" @@ -146,6 +147,7 @@ void register_editor_types() { GDREGISTER_CLASS(EditorSelection); GDREGISTER_CLASS(EditorFileDialog); GDREGISTER_CLASS(EditorSettings); + GDREGISTER_ABSTRACT_CLASS(EditorToaster); GDREGISTER_CLASS(EditorNode3DGizmo); GDREGISTER_CLASS(EditorNode3DGizmoPlugin); GDREGISTER_ABSTRACT_CLASS(EditorResourcePreview); diff --git a/editor/rename_dialog.h b/editor/rename_dialog.h index 37d159b4e2..5c2ac2558d 100644 --- a/editor/rename_dialog.h +++ b/editor/rename_dialog.h @@ -49,7 +49,7 @@ class TabContainer; class RenameDialog : public ConfirmationDialog { GDCLASS(RenameDialog, ConfirmationDialog); - virtual void ok_pressed() override { rename(); }; + virtual void ok_pressed() override { rename(); } void _cancel_pressed() {} void _features_toggled(bool pressed); void _insert_text(const String &text); diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp index 90e4d74fcb..dc1198167d 100644 --- a/editor/scene_create_dialog.cpp +++ b/editor/scene_create_dialog.cpp @@ -48,10 +48,10 @@ void SceneCreateDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - select_node_button->set_icon(get_editor_theme_icon(SNAME("ClassList"))); - node_type_2d->set_icon(get_editor_theme_icon(SNAME("Node2D"))); - node_type_3d->set_icon(get_editor_theme_icon(SNAME("Node3D"))); - node_type_gui->set_icon(get_editor_theme_icon(SNAME("Control"))); + select_node_button->set_button_icon(get_editor_theme_icon(SNAME("ClassList"))); + node_type_2d->set_button_icon(get_editor_theme_icon(SNAME("Node2D"))); + node_type_3d->set_button_icon(get_editor_theme_icon(SNAME("Node3D"))); + node_type_gui->set_button_icon(get_editor_theme_icon(SNAME("Control"))); node_type_other->add_theme_icon_override(SNAME("icon"), get_editor_theme_icon(SNAME("Node"))); } break; @@ -121,9 +121,9 @@ void SceneCreateDialog::update_dialog() { const StringName root_type_name = StringName(other_type_display->get_text()); if (has_theme_icon(root_type_name, EditorStringName(EditorIcons))) { - node_type_other->set_icon(get_editor_theme_icon(root_type_name)); + node_type_other->set_button_icon(get_editor_theme_icon(root_type_name)); } else { - node_type_other->set_icon(nullptr); + node_type_other->set_button_icon(nullptr); } root_name = root_name_edit->get_text().strip_edges(); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index bcab0c2883..87ba2e6875 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -126,7 +126,8 @@ void SceneTreeDock::input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT)) { - if (mb->is_pressed() && scene_tree->get_rect().has_point(scene_tree->get_local_mouse_position())) { + Tree *tree = scene_tree->get_scene_tree(); + if (mb->is_pressed() && tree->get_rect().has_point(tree->get_local_mouse_position())) { tree_clicked = true; } else if (!mb->is_pressed()) { tree_clicked = false; @@ -156,11 +157,12 @@ void SceneTreeDock::shortcut_input(const Ref<InputEvent> &p_event) { } if (ED_IS_SHORTCUT("scene_tree/rename", p_event)) { - // Prevent renaming if a button is focused - // to avoid conflict with Enter shortcut on macOS - if (!focus_owner || !Object::cast_to<BaseButton>(focus_owner)) { - _tool_selected(TOOL_RENAME); + // Prevent renaming if a button or a range is focused + // to avoid conflict with Enter shortcut on macOS. + if (focus_owner && (Object::cast_to<BaseButton>(focus_owner) || Object::cast_to<Range>(focus_owner))) { + return; } + _tool_selected(TOOL_RENAME); #ifdef MODULE_REGEX_ENABLED } else if (ED_IS_SHORTCUT("scene_tree/batch_rename", p_event)) { _tool_selected(TOOL_BATCH_RENAME); @@ -1589,7 +1591,7 @@ void SceneTreeDock::_notification(int p_what) { node_shortcuts_toggle = memnew(Button); node_shortcuts_toggle->set_flat(true); - node_shortcuts_toggle->set_icon(get_editor_theme_icon(SNAME("Favorites"))); + node_shortcuts_toggle->set_button_icon(get_editor_theme_icon(SNAME("Favorites"))); node_shortcuts_toggle->set_toggle_mode(true); node_shortcuts_toggle->set_tooltip_text(TTR("Toggle the display of favorite nodes.")); node_shortcuts_toggle->set_pressed(EDITOR_GET("_use_favorites_root_selection")); @@ -1614,19 +1616,19 @@ void SceneTreeDock::_notification(int p_what) { button_2d = memnew(Button); beginner_node_shortcuts->add_child(button_2d); button_2d->set_text(TTR("2D Scene")); - button_2d->set_icon(get_editor_theme_icon(SNAME("Node2D"))); + button_2d->set_button_icon(get_editor_theme_icon(SNAME("Node2D"))); button_2d->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_CREATE_2D_SCENE, false)); button_3d = memnew(Button); beginner_node_shortcuts->add_child(button_3d); button_3d->set_text(TTR("3D Scene")); - button_3d->set_icon(get_editor_theme_icon(SNAME("Node3D"))); + button_3d->set_button_icon(get_editor_theme_icon(SNAME("Node3D"))); button_3d->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_CREATE_3D_SCENE, false)); button_ui = memnew(Button); beginner_node_shortcuts->add_child(button_ui); button_ui->set_text(TTR("User Interface")); - button_ui->set_icon(get_editor_theme_icon(SNAME("Control"))); + button_ui->set_button_icon(get_editor_theme_icon(SNAME("Control"))); button_ui->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_CREATE_USER_INTERFACE, false)); favorite_node_shortcuts = memnew(VBoxContainer); @@ -1635,13 +1637,13 @@ void SceneTreeDock::_notification(int p_what) { button_custom = memnew(Button); node_shortcuts->add_child(button_custom); button_custom->set_text(TTR("Other Node")); - button_custom->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_custom->set_button_icon(get_editor_theme_icon(SNAME("Add"))); button_custom->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_NEW, false)); button_clipboard = memnew(Button); node_shortcuts->add_child(button_clipboard); button_clipboard->set_text(TTR("Paste From Clipboard")); - button_clipboard->set_icon(get_editor_theme_icon(SNAME("ActionPaste"))); + button_clipboard->set_button_icon(get_editor_theme_icon(SNAME("ActionPaste"))); button_clipboard->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_PASTE, false)); _update_create_root_dialog(true); @@ -1663,11 +1665,12 @@ void SceneTreeDock::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - button_add->set_icon(get_editor_theme_icon(SNAME("Add"))); - button_instance->set_icon(get_editor_theme_icon(SNAME("Instance"))); - button_create_script->set_icon(get_editor_theme_icon(SNAME("ScriptCreate"))); - button_detach_script->set_icon(get_editor_theme_icon(SNAME("ScriptRemove"))); - button_tree_menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + button_add->set_button_icon(get_editor_theme_icon(SNAME("Add"))); + button_instance->set_button_icon(get_editor_theme_icon(SNAME("Instance"))); + button_create_script->set_button_icon(get_editor_theme_icon(SNAME("ScriptCreate"))); + button_detach_script->set_button_icon(get_editor_theme_icon(SNAME("ScriptRemove"))); + button_extend_script->set_button_icon(get_editor_theme_icon(SNAME("ScriptExtend"))); + button_tree_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); filter->set_right_icon(get_editor_theme_icon(SNAME("Search"))); @@ -1678,19 +1681,19 @@ void SceneTreeDock::_notification(int p_what) { // These buttons are created on READY, because reasons... if (button_2d) { - button_2d->set_icon(get_editor_theme_icon(SNAME("Node2D"))); + button_2d->set_button_icon(get_editor_theme_icon(SNAME("Node2D"))); } if (button_3d) { - button_3d->set_icon(get_editor_theme_icon(SNAME("Node3D"))); + button_3d->set_button_icon(get_editor_theme_icon(SNAME("Node3D"))); } if (button_ui) { - button_ui->set_icon(get_editor_theme_icon(SNAME("Control"))); + button_ui->set_button_icon(get_editor_theme_icon(SNAME("Control"))); } if (button_custom) { - button_custom->set_icon(get_editor_theme_icon(SNAME("Add"))); + button_custom->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } if (button_clipboard) { - button_clipboard->set_icon(get_editor_theme_icon(SNAME("ActionPaste"))); + button_clipboard->set_button_icon(get_editor_theme_icon(SNAME("ActionPaste"))); } menu_subresources->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor))); @@ -2784,33 +2787,49 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { } void SceneTreeDock::_update_script_button() { - if (!profile_allow_script_editing) { - button_create_script->hide(); - button_detach_script->hide(); - } else if (editor_selection->get_selection().size() == 0) { - button_create_script->hide(); - button_detach_script->hide(); - } else if (editor_selection->get_selection().size() == 1) { - Node *n = editor_selection->get_selected_node_list().front()->get(); - if (n->get_script().is_null()) { - button_create_script->show(); - button_detach_script->hide(); - } else { - button_create_script->hide(); - button_detach_script->show(); - } - } else { - button_create_script->hide(); + bool can_create_script = false; + bool can_detach_script = false; + bool can_extend_script = false; + + if (profile_allow_script_editing) { Array selection = editor_selection->get_selected_nodes(); + for (int i = 0; i < selection.size(); i++) { Node *n = Object::cast_to<Node>(selection[i]); - if (!n->get_script().is_null()) { - button_detach_script->show(); - return; + Ref<Script> s = n->get_script(); + Ref<Script> cts; + + if (n->has_meta(SceneStringName(_custom_type_script))) { + cts = n->get_meta(SceneStringName(_custom_type_script)); + } + + if (selection.size() == 1) { + if (s.is_valid()) { + if (cts.is_valid() && s == cts) { + can_extend_script = true; + } + } else { + can_create_script = true; + } + } + + if (s.is_valid()) { + if (cts.is_valid()) { + if (s != cts) { + can_detach_script = true; + break; + } + } else { + can_detach_script = true; + break; + } } } - button_detach_script->hide(); } + + button_create_script->set_visible(can_create_script); + button_detach_script->set_visible(can_detach_script); + button_extend_script->set_visible(can_extend_script); } void SceneTreeDock::_selection_changed() { @@ -3057,7 +3076,28 @@ void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_pro Node *newnode = p_by_node; if (p_keep_properties) { - Node *default_oldnode = Object::cast_to<Node>(ClassDB::instantiate(oldnode->get_class())); + Node *default_oldnode = nullptr; + + // If we're dealing with a custom node type, we need to create a default instance of the custom type instead of the native type for property comparison. + if (oldnode->has_meta(SceneStringName(_custom_type_script))) { + Ref<Script> cts = oldnode->get_meta(SceneStringName(_custom_type_script)); + default_oldnode = Object::cast_to<Node>(get_editor_data()->script_class_instance(cts->get_global_name())); + if (default_oldnode) { + default_oldnode->set_name(cts->get_global_name()); + get_editor_data()->instantiate_object_properties(default_oldnode); + } else { + // Legacy custom type, registered with "add_custom_type()". + // TODO: Should probably be deprecated in 4.x. + const EditorData::CustomType *custom_type = get_editor_data()->get_custom_type_by_path(cts->get_path()); + if (custom_type) { + default_oldnode = Object::cast_to<Node>(get_editor_data()->instantiate_custom_type(custom_type->name, cts->get_instance_base_type())); + } + } + } + + if (!default_oldnode) { + default_oldnode = Object::cast_to<Node>(ClassDB::instantiate(oldnode->get_class())); + } List<PropertyInfo> pinfo; oldnode->get_property_list(&pinfo); @@ -3542,6 +3582,27 @@ void SceneTreeDock::_script_dropped(const String &p_file, NodePath p_to) { undo_redo->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(n)).path_join(new_node->get_name()))); undo_redo->commit_action(); } else { + // Check if dropped script is compatible. + if (n->has_meta(SceneStringName(_custom_type_script))) { + Ref<Script> ct_scr = n->get_meta(SceneStringName(_custom_type_script)); + if (!scr->inherits_script(ct_scr)) { + String custom_type_name = ct_scr->get_global_name(); + + // Legacy custom type, registered with "add_custom_type()". + if (custom_type_name.is_empty()) { + const EditorData::CustomType *custom_type = get_editor_data()->get_custom_type_by_path(ct_scr->get_path()); + if (custom_type) { + custom_type_name = custom_type->name; + } else { + custom_type_name = TTR("<unknown>"); + } + } + + WARN_PRINT_ED(vformat("Script does not extend type: '%s'.", custom_type_name)); + return; + } + } + undo_redo->create_action(TTR("Attach Script"), UndoRedo::MERGE_DISABLE, n); undo_redo->add_do_method(InspectorDock::get_singleton(), "store_script_properties", n); undo_redo->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", n); @@ -3649,6 +3710,7 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { Ref<Script> existing_script; bool existing_script_removable = true; + bool allow_attach_new_script = true; if (selection.size() == 1) { Node *selected = selection.front()->get(); @@ -3672,6 +3734,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (EditorNode::get_singleton()->get_object_custom_type_base(selected) == existing_script) { existing_script_removable = false; } + + if (selected->has_meta(SceneStringName(_custom_type_script))) { + allow_attach_new_script = false; + } } if (profile_allow_editing) { @@ -3692,7 +3758,10 @@ void SceneTreeDock::_tree_rmb(const Vector2 &p_menu_pos) { if (full_selection.size() == 1) { add_separator = true; - menu->add_icon_shortcut(get_editor_theme_icon(SNAME("ScriptCreate")), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + if (allow_attach_new_script) { + menu->add_icon_shortcut(get_editor_theme_icon(SNAME("ScriptCreate")), ED_GET_SHORTCUT("scene_tree/attach_script"), TOOL_ATTACH_SCRIPT); + } + if (existing_script.is_valid()) { menu->add_icon_shortcut(get_editor_theme_icon(SNAME("ScriptExtend")), ED_GET_SHORTCUT("scene_tree/extend_script"), TOOL_EXTEND_SCRIPT); } @@ -4269,7 +4338,7 @@ void SceneTreeDock::_update_create_root_dialog(bool p_initializing) { if (ScriptServer::is_global_class(name)) { name = ScriptServer::get_global_class_native_base(name); } - button->set_icon(EditorNode::get_singleton()->get_class_icon(name)); + button->set_button_icon(EditorNode::get_singleton()->get_class_icon(name)); button->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_favorite_root_selected).bind(l)); } } @@ -4601,6 +4670,14 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec filter_hbc->add_child(button_detach_script); button_detach_script->hide(); + button_extend_script = memnew(Button); + button_extend_script->set_flat(true); + button_extend_script->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_EXTEND_SCRIPT, false)); + button_extend_script->set_tooltip_text(TTR("Extend the script of the selected node.")); + button_extend_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/extend_script")); + filter_hbc->add_child(button_extend_script); + button_extend_script->hide(); + button_tree_menu = memnew(MenuButton); button_tree_menu->set_flat(false); button_tree_menu->set_theme_type_variation("FlatMenuButton"); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 05ad0f36e4..8cee2870f6 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -115,6 +115,7 @@ class SceneTreeDock : public VBoxContainer { Button *button_instance = nullptr; Button *button_create_script = nullptr; Button *button_detach_script = nullptr; + Button *button_extend_script = nullptr; MenuButton *button_tree_menu = nullptr; Button *node_shortcuts_toggle = nullptr; diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 0cb4952b04..8615836ddd 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -135,16 +135,16 @@ void ScriptCreateDialog::_notification(int p_what) { } } - path_button->set_icon(get_editor_theme_icon(SNAME("Folder"))); - parent_browse_button->set_icon(get_editor_theme_icon(SNAME("Folder"))); - parent_search_button->set_icon(get_editor_theme_icon(SNAME("ClassList"))); + path_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); + parent_browse_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); + parent_search_button->set_button_icon(get_editor_theme_icon(SNAME("ClassList"))); } break; } } void ScriptCreateDialog::_path_hbox_sorted() { if (is_visible()) { - int filename_start_pos = file_path->get_text().rfind("/") + 1; + int filename_start_pos = file_path->get_text().rfind_char('/') + 1; int filename_end_pos = file_path->get_text().get_basename().length(); if (!is_built_in) { @@ -359,6 +359,7 @@ void ScriptCreateDialog::_create_new() { alert->popup_centered(); return; } + EditorNode::get_singleton()->ensure_uid_file(lpath); } emit_signal(SNAME("script_created"), scr); diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp index 846e8867a1..33da3dd10c 100644 --- a/editor/shader_create_dialog.cpp +++ b/editor/shader_create_dialog.cpp @@ -31,6 +31,7 @@ #include "shader_create_dialog.h" #include "core/config/project_settings.h" +#include "editor/editor_node.h" #include "editor/gui/editor_file_dialog.h" #include "editor/gui/editor_validation_panel.h" #include "editor/themes/editor_scale.h" @@ -74,7 +75,7 @@ void ShaderCreateDialog::_notification(int p_what) { } } - path_button->set_icon(get_editor_theme_icon(SNAME("Folder"))); + path_button->set_button_icon(get_editor_theme_icon(SNAME("Folder"))); } break; } } @@ -102,7 +103,7 @@ void ShaderCreateDialog::_update_language_info() { void ShaderCreateDialog::_path_hbox_sorted() { if (is_visible()) { - int filename_start_pos = initial_base_path.rfind("/") + 1; + int filename_start_pos = initial_base_path.rfind_char('/') + 1; int filename_end_pos = initial_base_path.length(); if (!is_built_in) { @@ -240,6 +241,7 @@ void fog() { alert->popup_centered(); return; } + EditorNode::get_singleton()->ensure_uid_file(lpath); emit_signal(SNAME("shader_include_created"), shader_inc); } else { @@ -258,6 +260,7 @@ void fog() { alert->popup_centered(); return; } + EditorNode::get_singleton()->ensure_uid_file(lpath); } emit_signal(SNAME("shader_created"), shader); diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index 85e5cd6ea0..a53b621097 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -438,7 +438,7 @@ void ShaderGlobalsEditor::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - variable_add->set_icon(get_editor_theme_icon(SNAME("Add"))); + variable_add->set_button_icon(get_editor_theme_icon(SNAME("Add"))); } break; case NOTIFICATION_PREDELETE: { diff --git a/editor/surface_upgrade_tool.h b/editor/surface_upgrade_tool.h index 59745250e4..130c49dcac 100644 --- a/editor/surface_upgrade_tool.h +++ b/editor/surface_upgrade_tool.h @@ -54,9 +54,9 @@ protected: static void _bind_methods(); public: - static SurfaceUpgradeTool *get_singleton() { return singleton; }; + static SurfaceUpgradeTool *get_singleton() { return singleton; } - bool is_show_requested() const { return show_requested; }; + bool is_show_requested() const { return show_requested; } void show_popup() { _show_popup(); } void prepare_upgrade(); diff --git a/editor/themes/editor_color_map.cpp b/editor/themes/editor_color_map.cpp index 9046a8b688..3c3d755586 100644 --- a/editor/themes/editor_color_map.cpp +++ b/editor/themes/editor_color_map.cpp @@ -173,6 +173,8 @@ void EditorColorMap::create() { add_conversion_exception("OverbrightIndicator"); add_conversion_exception("MaterialPreviewCube"); add_conversion_exception("MaterialPreviewSphere"); + add_conversion_exception("MaterialPreviewQuad"); + add_conversion_exception("MaterialPreviewLight1"); add_conversion_exception("MaterialPreviewLight2"); diff --git a/editor/themes/editor_color_map.h b/editor/themes/editor_color_map.h index c1176749f2..fba39f249e 100644 --- a/editor/themes/editor_color_map.h +++ b/editor/themes/editor_color_map.h @@ -50,8 +50,8 @@ public: static void add_conversion_color_pair(const String &p_from_color, const String &p_to_color); static void add_conversion_exception(const StringName &p_icon_name); - static HashMap<Color, Color> &get_color_conversion_map() { return color_conversion_map; }; - static HashSet<StringName> &get_color_conversion_exceptions() { return color_conversion_exceptions; }; + static HashMap<Color, Color> &get_color_conversion_map() { return color_conversion_map; } + static HashSet<StringName> &get_color_conversion_exceptions() { return color_conversion_exceptions; } static void create(); static void finish(); diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp index 17bcbacfc2..a4251bfd29 100644 --- a/editor/themes/editor_theme_manager.cpp +++ b/editor/themes/editor_theme_manager.cpp @@ -633,6 +633,16 @@ void EditorThemeManager::_create_shared_styles(const Ref<EditorTheme> &p_theme, // in 4.0, and even if it was, it may not always work in practice (e.g. running with compositing disabled). p_config.popup_style->set_corner_radius_all(0); + p_config.popup_border_style = p_config.popup_style->duplicate(); + p_config.popup_border_style->set_content_margin_all(MAX(Math::round(EDSCALE), p_config.border_width) + 2 + (p_config.base_margin * 1.5) * EDSCALE); + // Always display a border for popups like PopupMenus so they can be distinguished from their background. + p_config.popup_border_style->set_border_width_all(MAX(Math::round(EDSCALE), p_config.border_width)); + if (p_config.draw_extra_borders) { + p_config.popup_border_style->set_border_color(p_config.extra_border_color_2); + } else { + p_config.popup_border_style->set_border_color(p_config.dark_color_2); + } + p_config.window_style = p_config.popup_style->duplicate(); p_config.window_style->set_border_color(p_config.base_color); p_config.window_style->set_border_width(SIDE_TOP, 24 * EDSCALE); @@ -707,7 +717,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the } // PopupPanel - p_theme->set_stylebox(SceneStringName(panel), "PopupPanel", p_config.popup_style); + p_theme->set_stylebox(SceneStringName(panel), "PopupPanel", p_config.popup_border_style); } // Buttons. @@ -945,6 +955,8 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the p_theme->set_color("custom_button_font_highlight", "Tree", p_config.font_hover_color); p_theme->set_color(SceneStringName(font_color), "Tree", p_config.font_color); + p_theme->set_color("font_hovered_color", "Tree", p_config.mono_color); + p_theme->set_color("font_hovered_dimmed_color", "Tree", p_config.font_color); p_theme->set_color("font_selected_color", "Tree", p_config.mono_color); p_theme->set_color("font_disabled_color", "Tree", p_config.font_disabled_color); p_theme->set_color("font_outline_color", "Tree", p_config.font_outline_color); @@ -997,7 +1009,13 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the Ref<StyleBoxFlat> style_tree_hover = p_config.base_style->duplicate(); style_tree_hover->set_bg_color(p_config.highlight_color * Color(1, 1, 1, 0.4)); style_tree_hover->set_border_width_all(0); - p_theme->set_stylebox("hover", "Tree", style_tree_hover); + p_theme->set_stylebox("hovered", "Tree", style_tree_hover); + p_theme->set_stylebox("button_hover", "Tree", style_tree_hover); + + Ref<StyleBoxFlat> style_tree_hover_dimmed = p_config.base_style->duplicate(); + style_tree_hover_dimmed->set_bg_color(p_config.highlight_color * Color(1, 1, 1, 0.2)); + style_tree_hover_dimmed->set_border_width_all(0); + p_theme->set_stylebox("hovered_dimmed", "Tree", style_tree_hover_dimmed); p_theme->set_stylebox("selected_focus", "Tree", style_tree_focus); p_theme->set_stylebox("selected", "Tree", style_tree_selected); @@ -1306,18 +1324,11 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the // PopupMenu. { - Ref<StyleBoxFlat> style_popup_menu = p_config.popup_style->duplicate(); + Ref<StyleBoxFlat> style_popup_menu = p_config.popup_border_style->duplicate(); // 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_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 (p_config.draw_extra_borders) { - style_popup_menu->set_border_color(p_config.extra_border_color_2); - } else { - style_popup_menu->set_border_color(p_config.dark_color_2); - } + style_popup_menu->set_content_margin_individual(Math::round(EDSCALE), 2 * EDSCALE, Math::round(EDSCALE), 2 * EDSCALE); p_theme->set_stylebox(SceneStringName(panel), "PopupMenu", style_popup_menu); Ref<StyleBoxFlat> style_menu_hover = p_config.button_style_hover->duplicate(); @@ -1327,17 +1338,17 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the Ref<StyleBoxLine> style_popup_separator(memnew(StyleBoxLine)); style_popup_separator->set_color(p_config.separator_color); - style_popup_separator->set_grow_begin(p_config.popup_margin - MAX(Math::round(EDSCALE), p_config.border_width)); - style_popup_separator->set_grow_end(p_config.popup_margin - MAX(Math::round(EDSCALE), p_config.border_width)); + style_popup_separator->set_grow_begin(Math::round(EDSCALE) - MAX(Math::round(EDSCALE), p_config.border_width)); + style_popup_separator->set_grow_end(Math::round(EDSCALE) - MAX(Math::round(EDSCALE), p_config.border_width)); style_popup_separator->set_thickness(MAX(Math::round(EDSCALE), p_config.border_width)); Ref<StyleBoxLine> style_popup_labeled_separator_left(memnew(StyleBoxLine)); - style_popup_labeled_separator_left->set_grow_begin(p_config.popup_margin - MAX(Math::round(EDSCALE), p_config.border_width)); + style_popup_labeled_separator_left->set_grow_begin(Math::round(EDSCALE) - MAX(Math::round(EDSCALE), p_config.border_width)); style_popup_labeled_separator_left->set_color(p_config.separator_color); style_popup_labeled_separator_left->set_thickness(MAX(Math::round(EDSCALE), p_config.border_width)); Ref<StyleBoxLine> style_popup_labeled_separator_right(memnew(StyleBoxLine)); - style_popup_labeled_separator_right->set_grow_end(p_config.popup_margin - MAX(Math::round(EDSCALE), p_config.border_width)); + style_popup_labeled_separator_right->set_grow_end(Math::round(EDSCALE) - MAX(Math::round(EDSCALE), p_config.border_width)); style_popup_labeled_separator_right->set_color(p_config.separator_color); style_popup_labeled_separator_right->set_thickness(MAX(Math::round(EDSCALE), p_config.border_width)); @@ -1846,6 +1857,12 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme p_theme->set_stylebox("ScriptEditorPanelFloating", EditorStringName(EditorStyles), make_empty_stylebox(0, 0, 0, 0)); p_theme->set_stylebox("ScriptEditor", EditorStringName(EditorStyles), make_empty_stylebox(0, 0, 0, 0)); + // Game view. + p_theme->set_type_variation("GamePanel", "Panel"); + Ref<StyleBoxFlat> game_panel = p_theme->get_stylebox(SNAME("panel"), SNAME("Panel"))->duplicate(); + game_panel->set_corner_radius_all(0); + p_theme->set_stylebox(SceneStringName(panel), "GamePanel", game_panel); + // Main menu. Ref<StyleBoxFlat> menu_transparent_style = p_config.button_style->duplicate(); menu_transparent_style->set_bg_color(Color(1, 1, 1, 0)); @@ -2115,21 +2132,6 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme // EditorValidationPanel. p_theme->set_stylebox(SceneStringName(panel), "EditorValidationPanel", p_config.tree_panel_style); - - // ControlEditor. - { - p_theme->set_type_variation("ControlEditorPopupPanel", "PopupPanel"); - - Ref<StyleBoxFlat> control_editor_popup_style = p_config.popup_style->duplicate(); - control_editor_popup_style->set_shadow_size(0); - control_editor_popup_style->set_content_margin(SIDE_LEFT, p_config.base_margin * EDSCALE); - control_editor_popup_style->set_content_margin(SIDE_TOP, p_config.base_margin * EDSCALE); - control_editor_popup_style->set_content_margin(SIDE_RIGHT, p_config.base_margin * EDSCALE); - control_editor_popup_style->set_content_margin(SIDE_BOTTOM, p_config.base_margin * EDSCALE); - control_editor_popup_style->set_border_width_all(0); - - p_theme->set_stylebox(SceneStringName(panel), "ControlEditorPopupPanel", control_editor_popup_style); - } } // Editor inspector. @@ -2504,6 +2506,7 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme p_theme->set_color("transition_icon_disabled_color", "GraphStateMachine", Color(1, 1, 1, 0.2)); p_theme->set_color("highlight_color", "GraphStateMachine", p_config.accent_color); p_theme->set_color("highlight_disabled_color", "GraphStateMachine", p_config.accent_color * Color(1, 1, 1, 0.6)); + p_theme->set_color("focus_color", "GraphStateMachine", p_config.accent_color); p_theme->set_color("guideline_color", "GraphStateMachine", p_config.font_color * Color(1, 1, 1, 0.3)); p_theme->set_color("playback_color", "GraphStateMachine", p_config.font_color); diff --git a/editor/themes/editor_theme_manager.h b/editor/themes/editor_theme_manager.h index 5e7bd00083..ca5e1a4e2d 100644 --- a/editor/themes/editor_theme_manager.h +++ b/editor/themes/editor_theme_manager.h @@ -135,6 +135,7 @@ class EditorThemeManager { Ref<StyleBoxFlat> button_style_hover; Ref<StyleBoxFlat> popup_style; + Ref<StyleBoxFlat> popup_border_style; Ref<StyleBoxFlat> window_style; Ref<StyleBoxFlat> dialog_style; Ref<StyleBoxFlat> panel_container_style; diff --git a/editor/window_wrapper.cpp b/editor/window_wrapper.cpp index 9496ba016c..3256680416 100644 --- a/editor/window_wrapper.cpp +++ b/editor/window_wrapper.cpp @@ -390,8 +390,7 @@ void ScreenSelect::_notification(int p_what) { connect(SceneStringName(gui_input), callable_mp(this, &ScreenSelect::_handle_mouse_shortcut)); } break; case NOTIFICATION_THEME_CHANGED: { - set_icon(get_editor_theme_icon("MakeFloating")); - popup_background->add_theme_style_override(SceneStringName(panel), get_theme_stylebox("PanelForeground", EditorStringName(EditorStyles))); + set_button_icon(get_editor_theme_icon("MakeFloating")); const real_t popup_height = real_t(get_theme_font_size(SceneStringName(font_size))) * 2.0; popup->set_min_size(Size2(0, popup_height * 3)); @@ -454,14 +453,10 @@ ScreenSelect::ScreenSelect() { // Create the popup. const Size2 borders = Size2(4, 4) * EDSCALE; - popup = memnew(Popup); + popup = memnew(PopupPanel); popup->connect("popup_hide", callable_mp(static_cast<BaseButton *>(this), &ScreenSelect::set_pressed).bind(false)); add_child(popup); - popup_background = memnew(Panel); - popup_background->set_anchors_and_offsets_preset(PRESET_FULL_RECT); - popup->add_child(popup_background); - MarginContainer *popup_root = memnew(MarginContainer); popup_root->add_theme_constant_override("margin_right", borders.width); popup_root->add_theme_constant_override("margin_top", borders.height); diff --git a/editor/window_wrapper.h b/editor/window_wrapper.h index a07e95f09e..3597276de9 100644 --- a/editor/window_wrapper.h +++ b/editor/window_wrapper.h @@ -88,7 +88,6 @@ class ScreenSelect : public Button { GDCLASS(ScreenSelect, Button); Popup *popup = nullptr; - Panel *popup_background = nullptr; HBoxContainer *screen_list = nullptr; void _build_advanced_menu(); |