diff options
Diffstat (limited to 'editor')
237 files changed, 6445 insertions, 3326 deletions
diff --git a/editor/action_map_editor.cpp b/editor/action_map_editor.cpp index 462f314471..c8ab98a80f 100644 --- a/editor/action_map_editor.cpp +++ b/editor/action_map_editor.cpp @@ -577,7 +577,6 @@ String InputEventConfigurationDialog::_get_device_string(int p_device) const { void InputEventConfigurationDialog::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { input_list_search->set_right_icon(input_list_search->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); @@ -732,9 +731,9 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { store_command_checkbox->set_pressed(true); store_command_checkbox->set_text(TTR("Store Command")); #ifdef APPLE_STYLE_KEYS - store_command_checkbox->set_tooltip(TTR("Toggles between serializing 'command' and 'meta'. Used for compatibility with Windows/Linux style keyboard.")); + store_command_checkbox->set_tooltip_text(TTR("Toggles between serializing 'command' and 'meta'. Used for compatibility with Windows/Linux style keyboard.")); #else - store_command_checkbox->set_tooltip(TTR("Toggles between serializing 'command' and 'control'. Used for compatibility with Apple Style keyboards.")); + store_command_checkbox->set_tooltip_text(TTR("Toggles between serializing 'command' and 'control'. Used for compatibility with Apple Style keyboards.")); #endif mod_container->add_child(store_command_checkbox); @@ -745,7 +744,7 @@ InputEventConfigurationDialog::InputEventConfigurationDialog() { physical_key_checkbox = memnew(CheckBox); physical_key_checkbox->set_text(TTR("Use Physical Keycode")); - physical_key_checkbox->set_tooltip(TTR("Stores the physical position of the key on the keyboard rather than the key's value. Used for compatibility with non-latin layouts.\nThis should generally be enabled for most game shortcuts, but not in non-game applications.")); + physical_key_checkbox->set_tooltip_text(TTR("Stores the physical position of the key on the keyboard rather than the key's value. Used for compatibility with non-latin layouts.\nThis should generally be enabled for most game shortcuts, but not in non-game applications.")); physical_key_checkbox->connect("toggled", callable_mp(this, &InputEventConfigurationDialog::_physical_keycode_toggled)); physical_key_checkbox->hide(); additional_options_container->add_child(physical_key_checkbox); @@ -805,7 +804,7 @@ String ActionMapEditor::_check_new_action_name(const String &p_name) { void ActionMapEditor::_add_edit_text_changed(const String &p_name) { String error = _check_new_action_name(p_name); - add_button->set_tooltip(error); + add_button->set_tooltip_text(error); add_button->set_disabled(!error.is_empty()); } @@ -1058,9 +1057,11 @@ void ActionMapEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, void ActionMapEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { action_list_search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + if (!actions_cache.is_empty()) { + update_action_list(); + } } break; } } diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index e10ed7e976..c121ab42d3 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/view_panner.h" #include "scene/resources/text_line.h" @@ -40,7 +41,7 @@ float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) { float h = p_h; h = (h - v_scroll) / v_zoom; - h = (get_size().height / 2) - h; + h = (get_size().height / 2.0) - h; return h; } @@ -51,10 +52,10 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) { int right_limit = get_size().width; //selection may have altered the order of keys - RBMap<float, int> key_order; + RBMap<real_t, int> key_order; for (int i = 0; i < animation->track_get_key_count(p_track); i++) { - float ofs = animation->track_get_key_time(p_track, i); + real_t ofs = animation->track_get_key_time(p_track, i); if (moving_selection && selection.has(IntPair(p_track, i))) { ofs += moving_selection_offset.x; } @@ -62,7 +63,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) { key_order[ofs] = i; } - for (RBMap<float, int>::Element *E = key_order.front(); E; E = E->next()) { + for (RBMap<real_t, int>::Element *E = key_order.front(); E; E = E->next()) { int i = E->get(); if (!E->next()) { @@ -74,7 +75,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) { float offset = animation->track_get_key_time(p_track, i); float height = animation->bezier_track_get_key_value(p_track, i); Vector2 out_handle = animation->bezier_track_get_key_out_handle(p_track, i); - if (p_track == moving_handle_track && moving_handle != 0 && moving_handle_key == i) { + if (p_track == moving_handle_track && (moving_handle == -1 || moving_handle == 1) && moving_handle_key == i) { out_handle = moving_handle_right; } @@ -88,7 +89,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) { float offset_n = animation->track_get_key_time(p_track, i_n); float height_n = animation->bezier_track_get_key_value(p_track, i_n); Vector2 in_handle = animation->bezier_track_get_key_in_handle(p_track, i_n); - if (p_track == moving_handle_track && moving_handle != 0 && moving_handle_key == i_n) { + if (p_track == moving_handle_track && (moving_handle == -1 || moving_handle == 1) && moving_handle_key == i_n) { in_handle = moving_handle_left; } @@ -138,7 +139,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) { //narrow high and low as much as possible for (int k = 0; k < iterations; k++) { - float middle = (low + high) / 2; + float middle = (low + high) / 2.0; Vector2 interp = start.bezier_interpolate(out_handle, in_handle, end, middle); @@ -215,8 +216,8 @@ void AnimationBezierTrackEdit::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning"))); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { bezier_icon = get_theme_icon(SNAME("KeyBezierPoint"), SNAME("EditorIcons")); bezier_handle_icon = get_theme_icon(SNAME("KeyBezierHandle"), SNAME("EditorIcons")); @@ -315,7 +316,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { int h = MAX(text_buf.get_size().y, icon->get_height()); - draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2)); + draw_texture(icon, Point2(ofs, vofs + int(h - icon->get_height()) / 2.0)); ofs += icon->get_width(); margin = icon->get_width(); @@ -402,29 +403,29 @@ void AnimationBezierTrackEdit::_notification(int p_what) { Vector2 string_pos = Point2(margin, vofs); text_buf.draw(get_canvas_item(), string_pos, cc); - float icon_start_height = vofs + rect.size.y / 2; - Rect2 remove_rect = Rect2(remove_hpos, icon_start_height - remove->get_height() / 2, remove->get_width(), remove->get_height()); + float icon_start_height = vofs + rect.size.y / 2.0; + Rect2 remove_rect = Rect2(remove_hpos, icon_start_height - remove->get_height() / 2.0, remove->get_width(), remove->get_height()); if (read_only) { draw_texture(remove, remove_rect.position, dc); } else { draw_texture(remove, remove_rect.position); } - Rect2 lock_rect = Rect2(lock_hpos, icon_start_height - lock->get_height() / 2, lock->get_width(), lock->get_height()); + Rect2 lock_rect = Rect2(lock_hpos, icon_start_height - lock->get_height() / 2.0, lock->get_width(), lock->get_height()); if (locked_tracks.has(current_track)) { draw_texture(lock, lock_rect.position); } else { draw_texture(unlock, lock_rect.position); } - Rect2 visible_rect = Rect2(visibility_hpos, icon_start_height - visible->get_height() / 2, visible->get_width(), visible->get_height()); + Rect2 visible_rect = Rect2(visibility_hpos, icon_start_height - visible->get_height() / 2.0, visible->get_width(), visible->get_height()); if (hidden_tracks.has(current_track)) { draw_texture(hidden, visible_rect.position); } else { draw_texture(visible, visible_rect.position); } - Rect2 solo_rect = Rect2(solo_hpos, icon_start_height - solo->get_height() / 2, solo->get_width(), solo->get_height()); + Rect2 solo_rect = Rect2(solo_hpos, icon_start_height - solo->get_height() / 2.0, solo->get_width(), solo->get_height()); draw_texture(solo, solo_rect.position); RBMap<int, Rect2> track_icons; @@ -455,7 +456,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { bool first = true; int prev_iv = 0; for (int i = font->get_height(font_size); i < get_size().height; i++) { - float ofs = get_size().height / 2 - i; + float ofs = get_size().height / 2.0 - i; ofs *= v_zoom; ofs += v_scroll; @@ -494,7 +495,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value)); if (pos.x >= limit && pos.x <= right_limit) { - draw_texture(point, pos - point->get_size() / 2, E.value); + draw_texture(point, pos - point->get_size() / 2.0, E.value); } } } @@ -546,14 +547,15 @@ void AnimationBezierTrackEdit::_notification(int p_what) { Vector2 pos((offset - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value)); Vector2 in_vec = animation->bezier_track_get_key_in_handle(i, j); - if (moving_handle != 0 && moving_handle_track == i && moving_handle_key == j) { + + if ((moving_handle == 1 || moving_handle == -1) && moving_handle_track == i && moving_handle_key == j) { in_vec = moving_handle_left; } Vector2 pos_in(((offset + in_vec.x) - timeline->get_value()) * scale + limit, _bezier_h_to_pixel(value + in_vec.y)); Vector2 out_vec = animation->bezier_track_get_key_out_handle(i, j); - if (moving_handle != 0 && moving_handle_track == i && moving_handle_key == j) { + if ((moving_handle == 1 || moving_handle == -1) && moving_handle_track == i && moving_handle_key == j) { out_vec = moving_handle_right; } @@ -568,7 +570,7 @@ void AnimationBezierTrackEdit::_notification(int p_what) { ep.track = i; ep.key = j; if (pos.x >= limit && pos.x <= right_limit) { - ep.point_rect.position = (pos - bezier_icon->get_size() / 2).floor(); + ep.point_rect.position = (pos - bezier_icon->get_size() / 2.0).floor(); ep.point_rect.size = bezier_icon->get_size(); if (selection.has(IntPair(i, j))) { draw_texture(selected_icon, ep.point_rect.position); @@ -583,18 +585,22 @@ void AnimationBezierTrackEdit::_notification(int p_what) { } ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5); } + ep.point_rect = ep.point_rect.grow(ep.point_rect.size.width * 0.5); + if (i == selected_track || selection.has(IntPair(i, j))) { - if (pos_in.x >= limit && pos_in.x <= right_limit) { - ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2).floor(); - ep.in_rect.size = bezier_handle_icon->get_size(); - draw_texture(bezier_handle_icon, ep.in_rect.position); - ep.in_rect = ep.in_rect.grow(ep.in_rect.size.width * 0.5); - } - if (pos_out.x >= limit && pos_out.x <= right_limit) { - ep.out_rect.position = (pos_out - bezier_handle_icon->get_size() / 2).floor(); - ep.out_rect.size = bezier_handle_icon->get_size(); - draw_texture(bezier_handle_icon, ep.out_rect.position); - ep.out_rect = ep.out_rect.grow(ep.out_rect.size.width * 0.5); + if (animation->bezier_track_get_key_handle_mode(i, j) != Animation::HANDLE_MODE_LINEAR) { + if (pos_in.x >= limit && pos_in.x <= right_limit) { + ep.in_rect.position = (pos_in - bezier_handle_icon->get_size() / 2.0).floor(); + ep.in_rect.size = bezier_handle_icon->get_size(); + draw_texture(bezier_handle_icon, ep.in_rect.position); + ep.in_rect = ep.in_rect.grow(ep.in_rect.size.width * 0.5); + } + if (pos_out.x >= limit && pos_out.x <= right_limit) { + ep.out_rect.position = (pos_out - bezier_handle_icon->get_size() / 2.0).floor(); + ep.out_rect.size = bezier_handle_icon->get_size(); + draw_texture(bezier_handle_icon, ep.out_rect.position); + ep.out_rect = ep.out_rect.grow(ep.out_rect.size.width * 0.5); + } } } if (!locked_tracks.has(i)) { @@ -649,7 +655,7 @@ Size2 AnimationBezierTrackEdit::get_minimum_size() const { return Vector2(1, 1); } -void AnimationBezierTrackEdit::set_undo_redo(UndoRedo *p_undo_redo) { +void AnimationBezierTrackEdit::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { undo_redo = p_undo_redo; } @@ -663,7 +669,6 @@ void AnimationBezierTrackEdit::set_editor(AnimationTrackEditor *p_editor) { editor = p_editor; connect("clear_selection", Callable(editor, "_clear_selection").bind(false)); connect("select_key", Callable(editor, "_key_selected"), CONNECT_DEFERRED); - connect("deselect_key", Callable(editor, "_key_deselected"), CONNECT_DEFERRED); } void AnimationBezierTrackEdit::_play_position_draw() { @@ -684,7 +689,7 @@ void AnimationBezierTrackEdit::_play_position_draw() { } } -void AnimationBezierTrackEdit::set_play_position(float p_pos) { +void AnimationBezierTrackEdit::set_play_position(real_t p_pos) { play_position_pos = p_pos; play_position->update(); } @@ -785,13 +790,14 @@ void AnimationBezierTrackEdit::_clear_selection() { update(); } -void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::HandleMode p_mode) { +void AnimationBezierTrackEdit::_change_selected_keys_handle_mode(Animation::HandleMode p_mode, bool p_auto) { undo_redo->create_action(TTR("Update Selected Key Handles")); - double ratio = timeline->get_zoom_scale() * v_zoom; - for (const IntPair &E : selection) { - const IntPair track_key_pair = E; - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_handle_mode(track_key_pair.first, track_key_pair.second), ratio); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track_key_pair.first, track_key_pair.second, p_mode, ratio); + for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { + const IntPair track_key_pair = E->get(); + undo_redo->add_undo_method(editor, "_bezier_track_set_key_handle_mode", animation.ptr(), track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_handle_mode(track_key_pair.first, track_key_pair.second), Animation::HANDLE_SET_MODE_NONE); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_in_handle(track_key_pair.first, track_key_pair.second)); + undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track_key_pair.first, track_key_pair.second, animation->bezier_track_get_key_out_handle(track_key_pair.first, track_key_pair.second)); + undo_redo->add_do_method(editor, "_bezier_track_set_key_handle_mode", animation.ptr(), track_key_pair.first, track_key_pair.second, p_mode, p_auto ? Animation::HANDLE_SET_MODE_AUTO : Animation::HANDLE_SET_MODE_RESET); } undo_redo->commit_action(); } @@ -803,7 +809,7 @@ void AnimationBezierTrackEdit::_clear_selection_for_anim(const Ref<Animation> &p _clear_selection(); } -void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos) { +void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int p_track, real_t p_pos) { if (!(animation == p_anim)) { return; } @@ -812,7 +818,7 @@ void AnimationBezierTrackEdit::_select_at_anim(const Ref<Animation> &p_anim, int ERR_FAIL_COND(idx < 0); selection.insert(IntPair(p_track, idx)); - emit_signal(SNAME("select_key"), p_track, idx, true); + emit_signal(SNAME("select_key"), idx, true, p_track); update(); } @@ -868,16 +874,16 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { return; } - float minimum_time = INFINITY; - float maximum_time = -INFINITY; - float minimum_value = INFINITY; - float maximum_value = -INFINITY; + real_t minimum_time = INFINITY; + real_t maximum_time = -INFINITY; + real_t minimum_value = INFINITY; + real_t maximum_value = -INFINITY; for (const IntPair &E : selection) { IntPair key_pair = E; - float time = animation->track_get_key_time(key_pair.first, key_pair.second); - float value = animation->bezier_track_get_key_value(key_pair.first, key_pair.second); + real_t time = animation->track_get_key_time(key_pair.first, key_pair.second); + real_t value = animation->bezier_track_get_key_value(key_pair.first, key_pair.second); minimum_time = MIN(time, minimum_time); maximum_time = MAX(time, maximum_time); @@ -887,8 +893,8 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { float width = get_size().width - timeline->get_name_limit() - timeline->get_buttons_width(); float padding = width * 0.1; - float desired_scale = (width - padding / 2) / (maximum_time - minimum_time); - minimum_time = MAX(0, minimum_time - (padding / 2) / desired_scale); + float desired_scale = (width - padding / 2.0) / (maximum_time - minimum_time); + minimum_time = MAX(0, minimum_time - (padding / 2.0) / desired_scale); float zv = Math::pow(100 / desired_scale, 0.125f); if (zv < 1) { @@ -942,7 +948,12 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete Selected Key(s)"), MENU_KEY_DELETE); menu->add_separator(); menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesFree"), SNAME("EditorIcons")), TTR("Make Handles Free"), MENU_KEY_SET_HANDLE_FREE); + menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesLinear"), SNAME("EditorIcons")), TTR("Make Handles Linear"), MENU_KEY_SET_HANDLE_LINEAR); menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesBalanced"), SNAME("EditorIcons")), TTR("Make Handles Balanced"), MENU_KEY_SET_HANDLE_BALANCED); + menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesMirror"), SNAME("EditorIcons")), TTR("Make Handles Mirrored"), MENU_KEY_SET_HANDLE_MIRRORED); + menu->add_separator(); + menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesBalanced"), SNAME("EditorIcons")), TTR("Make Handles Balanced (Auto Tangent)"), MENU_KEY_SET_HANDLE_AUTO_BALANCED); + menu->add_icon_item(get_theme_icon(SNAME("BezierHandlesMirror"), SNAME("EditorIcons")), TTR("Make Handles Mirrored (Auto Tangent)"), MENU_KEY_SET_HANDLE_AUTO_MIRRORED); } if (menu->get_item_count()) { @@ -984,9 +995,10 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { for (int i = 0; i < animation->track_get_key_count(track); ++i) { undo_redo->add_undo_method( - animation.ptr(), - "bezier_track_insert_key", - track, animation->track_get_key_time(track, i), + this, + "_bezier_track_insert_key", + track, + animation->track_get_key_time(track, i), animation->bezier_track_get_key_value(track, i), animation->bezier_track_get_key_in_handle(track, i), animation->bezier_track_get_key_out_handle(track, i), @@ -1093,6 +1105,9 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { moving_selection = false; moving_selection_from_key = pair.second; moving_selection_from_track = pair.first; + moving_handle_track = pair.first; + moving_handle_left = animation->bezier_track_get_key_in_handle(pair.first, pair.second); + moving_handle_right = animation->bezier_track_get_key_out_handle(pair.first, pair.second); moving_selection_offset = Vector2(); select_single_attempt = pair; update(); @@ -1102,10 +1117,12 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { moving_selection_from_key = pair.second; moving_selection_from_track = pair.first; moving_selection_offset = Vector2(); - set_animation_and_track(animation, pair.first, read_only); + moving_handle_track = pair.first; + moving_handle_left = animation->bezier_track_get_key_in_handle(pair.first, pair.second); + moving_handle_right = animation->bezier_track_get_key_out_handle(pair.first, pair.second); selection.clear(); selection.insert(pair); - update(); + set_animation_and_track(animation, pair.first, read_only); } return; } @@ -1137,24 +1154,23 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { //insert new point if (mb->get_position().x >= limit && mb->get_position().x < get_size().width && mb->is_command_pressed()) { Array new_point; - new_point.resize(6); + new_point.resize(5); - float h = (get_size().height / 2 - mb->get_position().y) * v_zoom + v_scroll; + float h = (get_size().height / 2.0 - mb->get_position().y) * v_zoom + v_scroll; new_point[0] = h; new_point[1] = -0.25; new_point[2] = 0; new_point[3] = 0.25; new_point[4] = 0; - new_point[5] = 0; - float time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); + real_t time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); while (animation->track_find_key(selected_track, time, true) != -1) { time += 0.001; } undo_redo->create_action(TTR("Add Bezier Point")); - undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point); + undo_redo->add_do_method(animation.ptr(), "bezier_track_insert_key", selected_track, time, new_point); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time); undo_redo->commit_action(); @@ -1218,10 +1234,10 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { //select by clicking on curve int track_count = animation->get_track_count(); - float animation_length = animation->get_length(); + real_t animation_length = animation->get_length(); animation->set_length(real_t(INT_MAX)); //bezier_track_interpolate doesn't find keys if they exist beyond anim length - float time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); + real_t time = ((mb->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); for (int i = 0; i < track_count; ++i) { if (animation->track_get_type(i) != Animation::TrackType::TYPE_BEZIER || hidden_tracks.has(i) || locked_tracks.has(i)) { @@ -1245,20 +1261,6 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { update(); } - if (moving_handle != 0 && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { - if (!read_only) { - undo_redo->create_action(TTR("Move Bezier Points")); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", selected_track, moving_handle_key, moving_handle_left); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", selected_track, moving_handle_key, moving_handle_right); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", selected_track, moving_handle_key, animation->bezier_track_get_key_in_handle(selected_track, moving_handle_key)); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", selected_track, moving_handle_key, animation->bezier_track_get_key_out_handle(selected_track, moving_handle_key)); - undo_redo->commit_action(); - - moving_handle = 0; - update(); - } - } - if (moving_selection_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { if (!read_only) { if (moving_selection) { @@ -1267,13 +1269,14 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { undo_redo->create_action(TTR("Move Bezier Points")); List<AnimMoveRestore> to_restore; + List<Animation::HandleMode> to_restore_handle_modes; // 1-remove the keys for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { undo_redo->add_do_method(animation.ptr(), "track_remove_key", E->get().first, E->get().second); } // 2- remove overlapped keys for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { - float newtime = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); + real_t newtime = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); int idx = animation->track_find_key(E->get().first, newtime, true); if (idx == -1) { @@ -1292,33 +1295,62 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { amr.time = newtime; to_restore.push_back(amr); + to_restore_handle_modes.push_back(animation->bezier_track_get_key_handle_mode(E->get().first, idx)); } // 3-move the keys (re insert them) for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { - float newpos = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); + real_t newpos = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); Array key = animation->track_get_key_value(E->get().first, E->get().second); - float h = key[0]; + real_t h = key[0]; h += moving_selection_offset.y; key[0] = h; - undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, newpos, key, 1); + undo_redo->add_do_method( + this, + "_bezier_track_insert_key", + E->get().first, + newpos, + key[0], + Vector2(key[1], key[2]), + Vector2(key[3], key[4]), + animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second)); } // 4-(undo) remove inserted keys for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { - float newpos = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); + real_t newpos = editor->snap_time(animation->track_get_key_time(E->get().first, E->get().second) + moving_selection_offset.x); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos); } // 5-(undo) reinsert keys for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { - float oldpos = animation->track_get_key_time(E->get().first, E->get().second); - undo_redo->add_undo_method(animation.ptr(), "track_insert_key", E->get().first, oldpos, animation->track_get_key_value(E->get().first, E->get().second), 1); + real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second); + Array key = animation->track_get_key_value(E->get().first, E->get().second); + undo_redo->add_undo_method( + this, + "_bezier_track_insert_key", + E->get().first, + oldpos, + key[0], + Vector2(key[1], key[2]), + Vector2(key[3], key[4]), + animation->bezier_track_get_key_handle_mode(E->get().first, E->get().second)); } // 6-(undo) reinsert overlapped keys - for (const AnimMoveRestore &amr : to_restore) { + for (int i = 0; i < to_restore.size(); i++) { + const AnimMoveRestore &amr = to_restore[i]; + Array key = amr.key; undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1); + undo_redo->add_undo_method( + this, + "_bezier_track_insert_key", + amr.track, + amr.time, + key[0], + Vector2(key[1], key[2]), + Vector2(key[3], key[4]), + to_restore_handle_modes[i]); } undo_redo->add_do_method(this, "_clear_selection_for_anim", animation); @@ -1327,8 +1359,8 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { // 7-reselect for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { - float oldpos = animation->track_get_key_time(E->get().first, E->get().second); - float newpos = editor->snap_time(oldpos + moving_selection_offset.x); + real_t oldpos = animation->track_get_key_time(E->get().first, E->get().second); + real_t newpos = editor->snap_time(oldpos + moving_selection_offset.x); undo_redo->add_do_method(this, "_select_at_anim", animation, E->get().first, newpos); undo_redo->add_undo_method(this, "_select_at_anim", animation, E->get().first, oldpos); @@ -1355,12 +1387,16 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { select_single_attempt = IntPair(-1, -1); } - float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + v_scroll; + float y = (get_size().height / 2.0 - mm->get_position().y) * v_zoom + v_scroll; float x = editor->snap_time(((mm->get_position().x - limit) / timeline->get_zoom_scale()) + timeline->get_value()); if (!read_only) { moving_selection_offset = Vector2(x - animation->track_get_key_time(moving_selection_from_track, moving_selection_from_key), y - animation->bezier_track_get_key_value(moving_selection_from_track, moving_selection_from_key)); } + + additional_moving_handle_lefts.clear(); + additional_moving_handle_rights.clear(); + update(); } @@ -1379,8 +1415,8 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { update(); } - if (moving_handle != 0 && mm.is_valid()) { - float y = (get_size().height / 2 - mm->get_position().y) * v_zoom + v_scroll; + if ((moving_handle == 1 || moving_handle == -1) && mm.is_valid()) { + float y = (get_size().height / 2.0 - mm->get_position().y) * v_zoom + v_scroll; float x = editor->snap_time((mm->get_position().x - timeline->get_name_limit()) / timeline->get_zoom_scale()) + timeline->get_value(); Vector2 key_pos = Vector2(animation->track_get_key_time(selected_track, moving_handle_key), animation->bezier_track_get_key_value(selected_track, moving_handle_key)); @@ -1393,8 +1429,10 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { if (moving_handle == -1) { moving_handle_left = moving_handle_value; - if (animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) { - double ratio = timeline->get_zoom_scale() * v_zoom; + Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key); + + if (handle_mode == Animation::HANDLE_MODE_BALANCED) { + real_t ratio = timeline->get_zoom_scale() * v_zoom; Transform2D xform; xform.set_scale(Vector2(1.0, 1.0 / ratio)); @@ -1402,12 +1440,16 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { Vector2 vec_in = xform.xform(moving_handle_left); moving_handle_right = xform.affine_inverse().xform(-vec_in.normalized() * vec_out.length()); + } else if (handle_mode == Animation::HANDLE_MODE_MIRRORED) { + moving_handle_right = -moving_handle_left; } } else if (moving_handle == 1) { moving_handle_right = moving_handle_value; - if (animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key) == Animation::HANDLE_MODE_BALANCED) { - double ratio = timeline->get_zoom_scale() * v_zoom; + Animation::HandleMode handle_mode = animation->bezier_track_get_key_handle_mode(moving_handle_track, moving_handle_key); + + if (handle_mode == Animation::HANDLE_MODE_BALANCED) { + real_t ratio = timeline->get_zoom_scale() * v_zoom; Transform2D xform; xform.set_scale(Vector2(1.0, 1.0 / ratio)); @@ -1415,26 +1457,26 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) { Vector2 vec_out = xform.xform(moving_handle_right); moving_handle_left = xform.affine_inverse().xform(-vec_out.normalized() * vec_in.length()); + } else if (handle_mode == Animation::HANDLE_MODE_MIRRORED) { + moving_handle_left = -moving_handle_right; } } update(); } - bool is_finishing_key_handle_drag = moving_handle != 0 && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT; - if (is_finishing_key_handle_drag) { + if ((moving_handle == -1 || moving_handle == 1) && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { if (!read_only) { undo_redo->create_action(TTR("Move Bezier Points")); if (moving_handle == -1) { - double ratio = timeline->get_zoom_scale() * v_zoom; + real_t ratio = timeline->get_zoom_scale() * v_zoom; undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", moving_handle_track, moving_handle_key, moving_handle_left, ratio); undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_in_handle(moving_handle_track, moving_handle_key), ratio); } else if (moving_handle == 1) { - double ratio = timeline->get_zoom_scale() * v_zoom; + real_t ratio = timeline->get_zoom_scale() * v_zoom; undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, moving_handle_right, ratio); undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", moving_handle_track, moving_handle_key, animation->bezier_track_get_key_out_handle(moving_handle_track, moving_handle_key), ratio); } undo_redo->commit_action(); - moving_handle = 0; update(); } @@ -1468,7 +1510,7 @@ void AnimationBezierTrackEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_or timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() * 1.05); } } - v_scroll = v_scroll + (p_origin.y - get_size().y / 2) * (v_zoom - v_zoom_orig); + v_scroll = v_scroll + (p_origin.y - get_size().y / 2.0) * (v_zoom - v_zoom_orig); update(); } @@ -1477,20 +1519,19 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { case MENU_KEY_INSERT: { if (animation->get_track_count() > 0) { Array new_point; - new_point.resize(6); + new_point.resize(5); - float h = (get_size().height / 2 - menu_insert_key.y) * v_zoom + v_scroll; + float h = (get_size().height / 2.0 - menu_insert_key.y) * v_zoom + v_scroll; new_point[0] = h; new_point[1] = -0.25; new_point[2] = 0; new_point[3] = 0.25; new_point[4] = 0; - new_point[5] = Animation::HANDLE_MODE_BALANCED; int limit = timeline->get_name_limit(); - float time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); + real_t time = ((menu_insert_key.x - limit) / timeline->get_zoom_scale()) + timeline->get_value(); while (animation->track_find_key(selected_track, time, true) != -1) { time += 0.001; @@ -1500,8 +1541,8 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { undo_redo->add_do_method(animation.ptr(), "track_insert_key", selected_track, time, new_point); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", selected_track, time); undo_redo->commit_action(); + update(); } - } break; case MENU_KEY_DUPLICATE: { duplicate_selection(); @@ -1512,9 +1553,21 @@ void AnimationBezierTrackEdit::_menu_selected(int p_index) { case MENU_KEY_SET_HANDLE_FREE: { _change_selected_keys_handle_mode(Animation::HANDLE_MODE_FREE); } break; + case MENU_KEY_SET_HANDLE_LINEAR: { + _change_selected_keys_handle_mode(Animation::HANDLE_MODE_LINEAR); + } break; case MENU_KEY_SET_HANDLE_BALANCED: { _change_selected_keys_handle_mode(Animation::HANDLE_MODE_BALANCED); } break; + case MENU_KEY_SET_HANDLE_MIRRORED: { + _change_selected_keys_handle_mode(Animation::HANDLE_MODE_MIRRORED); + } break; + case MENU_KEY_SET_HANDLE_AUTO_BALANCED: { + _change_selected_keys_handle_mode(Animation::HANDLE_MODE_BALANCED, true); + } break; + case MENU_KEY_SET_HANDLE_AUTO_MIRRORED: { + _change_selected_keys_handle_mode(Animation::HANDLE_MODE_MIRRORED, true); + } break; } } @@ -1523,9 +1576,9 @@ void AnimationBezierTrackEdit::duplicate_selection() { return; } - float top_time = 1e10; + real_t top_time = 1e10; for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { - float t = animation->track_get_key_time(E->get().first, E->get().second); + real_t t = animation->track_get_key_time(E->get().first, E->get().second); if (t < top_time) { top_time = t; } @@ -1533,17 +1586,17 @@ void AnimationBezierTrackEdit::duplicate_selection() { undo_redo->create_action(TTR("Anim Duplicate Keys")); - List<Pair<int, float>> new_selection_values; + List<Pair<int, real_t>> new_selection_values; for (SelectionSet::Element *E = selection.back(); E; E = E->prev()) { - float t = animation->track_get_key_time(E->get().first, E->get().second); - float dst_time = t + (timeline->get_play_position() - top_time); + real_t t = animation->track_get_key_time(E->get().first, E->get().second); + real_t dst_time = t + (timeline->get_play_position() - top_time); int existing_idx = animation->track_find_key(E->get().first, dst_time, true); undo_redo->add_do_method(animation.ptr(), "track_insert_key", E->get().first, dst_time, animation->track_get_key_value(E->get().first, E->get().second), animation->track_get_key_transition(E->get().first, E->get().second)); undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, dst_time); - Pair<int, float> p; + Pair<int, real_t> p; p.first = E->get().first; p.second = dst_time; new_selection_values.push_back(p); @@ -1558,9 +1611,9 @@ void AnimationBezierTrackEdit::duplicate_selection() { //reselect duplicated selection.clear(); - for (const Pair<int, float> &E : new_selection_values) { + for (const Pair<int, real_t> &E : new_selection_values) { int track = E.first; - float time = E.second; + real_t time = E.second; int existing_idx = animation->track_find_key(track, time, true); @@ -1590,18 +1643,24 @@ void AnimationBezierTrackEdit::delete_selection() { } } +void AnimationBezierTrackEdit::_bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode) { + ERR_FAIL_COND(animation.is_null()); + int idx = animation->bezier_track_insert_key(p_track, p_time, p_value, p_in_handle, p_out_handle); + animation->bezier_track_set_key_handle_mode(p_track, idx, p_handle_mode); +} + void AnimationBezierTrackEdit::_bind_methods() { - ClassDB::bind_method("_clear_selection", &AnimationBezierTrackEdit::_clear_selection); - ClassDB::bind_method("_clear_selection_for_anim", &AnimationBezierTrackEdit::_clear_selection_for_anim); - ClassDB::bind_method("_select_at_anim", &AnimationBezierTrackEdit::_select_at_anim); - ClassDB::bind_method("_update_hidden_tracks_after", &AnimationBezierTrackEdit::_update_hidden_tracks_after); - ClassDB::bind_method("_update_locked_tracks_after", &AnimationBezierTrackEdit::_update_locked_tracks_after); + ClassDB::bind_method(D_METHOD("_clear_selection"), &AnimationBezierTrackEdit::_clear_selection); + ClassDB::bind_method(D_METHOD("_clear_selection_for_anim"), &AnimationBezierTrackEdit::_clear_selection_for_anim); + ClassDB::bind_method(D_METHOD("_select_at_anim"), &AnimationBezierTrackEdit::_select_at_anim); + ClassDB::bind_method(D_METHOD("_update_hidden_tracks_after"), &AnimationBezierTrackEdit::_update_hidden_tracks_after); + ClassDB::bind_method(D_METHOD("_update_locked_tracks_after"), &AnimationBezierTrackEdit::_update_locked_tracks_after); + ClassDB::bind_method(D_METHOD("_bezier_track_insert_key"), &AnimationBezierTrackEdit::_bezier_track_insert_key); ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag"))); ADD_SIGNAL(MethodInfo("remove_request", PropertyInfo(Variant::INT, "track"))); ADD_SIGNAL(MethodInfo("insert_key", PropertyInfo(Variant::FLOAT, "offset"))); - ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "track"), PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single"))); - ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "track"), PropertyInfo(Variant::INT, "index"))); + ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single"), PropertyInfo(Variant::INT, "track"))); ADD_SIGNAL(MethodInfo("clear_selection")); ADD_SIGNAL(MethodInfo("close_request")); diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h index 070a6589ad..beb7a5e9c6 100644 --- a/editor/animation_bezier_editor.h +++ b/editor/animation_bezier_editor.h @@ -32,8 +32,9 @@ #define ANIMATION_BEZIER_EDITOR_H #include "animation_track_editor.h" -#include "core/templates/rb_set.h" +#include "core/templates/hashfuncs.h" +class EditorUndoRedoManager; class ViewPanner; class AnimationBezierTrackEdit : public Control { @@ -44,14 +45,18 @@ class AnimationBezierTrackEdit : public Control { MENU_KEY_DUPLICATE, MENU_KEY_DELETE, MENU_KEY_SET_HANDLE_FREE, + MENU_KEY_SET_HANDLE_LINEAR, MENU_KEY_SET_HANDLE_BALANCED, + MENU_KEY_SET_HANDLE_MIRRORED, + MENU_KEY_SET_HANDLE_AUTO_BALANCED, + MENU_KEY_SET_HANDLE_AUTO_MIRRORED, }; AnimationTimelineEdit *timeline = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; Node *root = nullptr; Control *play_position = nullptr; //separate control used to draw so updates for only position changed are much faster - float play_position_pos = 0; + real_t play_position_pos = 0; Ref<Animation> animation; bool read_only = false; @@ -111,25 +116,37 @@ class AnimationBezierTrackEdit : public Control { Vector2 box_selection_from; Vector2 box_selection_to; - int moving_handle = 0; //0 no move -1 or +1 out + int moving_handle = 0; //0 no move -1 or +1 out, 2 both (drawing only) int moving_handle_key = 0; int moving_handle_track = 0; Vector2 moving_handle_left; Vector2 moving_handle_right; int moving_handle_mode = 0; // value from Animation::HandleMode + struct PairHasher { + static _FORCE_INLINE_ uint32_t hash(const Pair<int, int> &p_value) { + int32_t hash = 23; + hash = hash * 31 * hash_one_uint64(p_value.first); + hash = hash * 31 * hash_one_uint64(p_value.second); + return hash; + } + }; + + HashMap<Pair<int, int>, Vector2, PairHasher> additional_moving_handle_lefts; + HashMap<Pair<int, int>, Vector2, PairHasher> additional_moving_handle_rights; + void _clear_selection(); void _clear_selection_for_anim(const Ref<Animation> &p_anim); - void _select_at_anim(const Ref<Animation> &p_anim, int p_track, float p_pos); - void _change_selected_keys_handle_mode(Animation::HandleMode p_mode); + void _select_at_anim(const Ref<Animation> &p_anim, int p_track, real_t p_pos); + void _change_selected_keys_handle_mode(Animation::HandleMode p_mode, bool p_auto = false); Vector2 menu_insert_key; struct AnimMoveRestore { int track = 0; - float time = 0; + double time = 0; Variant key; - float transition = 0; + real_t transition = 0; }; AnimationTrackEditor *editor = nullptr; @@ -144,7 +161,7 @@ class AnimationBezierTrackEdit : public Control { Vector<EditPoint> edit_points; - struct SelectionCompare { + struct PairCompare { bool operator()(const IntPair &lh, const IntPair &rh) { if (lh.first == rh.first) { return lh.second < rh.second; @@ -154,7 +171,7 @@ class AnimationBezierTrackEdit : public Control { } }; - typedef RBSet<IntPair, SelectionCompare> SelectionSet; + typedef RBSet<IntPair, PairCompare> SelectionSet; SelectionSet selection; @@ -180,18 +197,20 @@ public: void set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only); virtual Size2 get_minimum_size() const override; - void set_undo_redo(UndoRedo *p_undo_redo); + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void set_timeline(AnimationTimelineEdit *p_timeline); void set_editor(AnimationTrackEditor *p_editor); void set_root(Node *p_root); void set_filtered(bool p_filtered); - void set_play_position(float p_pos); + void set_play_position(real_t p_pos); void update_play_position(); void duplicate_selection(); void delete_selection(); + void _bezier_track_insert_key(int p_track, double p_time, real_t p_value, const Vector2 &p_in_handle, const Vector2 &p_out_handle, const Animation::HandleMode p_handle_mode); + AnimationBezierTrackEdit(); }; diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index 0db82551cb..379e2cb510 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -35,8 +35,10 @@ #include "editor/animation_bezier_editor.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "scene/animation/animation_player.h" +#include "scene/animation/tween.h" #include "scene/gui/separator.h" #include "scene/gui/view_panner.h" #include "scene/main/window.h" @@ -58,17 +60,17 @@ public: return true; } - bool _read_only() { + bool _is_read_only() { return animation_read_only; } static void _bind_methods() { - ClassDB::bind_method("_update_obj", &AnimationTrackKeyEdit::_update_obj); - ClassDB::bind_method("_key_ofs_changed", &AnimationTrackKeyEdit::_key_ofs_changed); - ClassDB::bind_method("_hide_script_from_inspector", &AnimationTrackKeyEdit::_hide_script_from_inspector); - ClassDB::bind_method("get_root_path", &AnimationTrackKeyEdit::get_root_path); - ClassDB::bind_method("_dont_undo_redo", &AnimationTrackKeyEdit::_dont_undo_redo); - ClassDB::bind_method("_read_only", &AnimationTrackKeyEdit::_read_only); + ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj); + ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationTrackKeyEdit::_key_ofs_changed); + ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationTrackKeyEdit::_hide_script_from_inspector); + ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationTrackKeyEdit::get_root_path); + ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationTrackKeyEdit::_dont_undo_redo); + ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationTrackKeyEdit::_is_read_only); } void _fix_node_path(Variant &value) { @@ -349,8 +351,8 @@ public: setting = true; undo_redo->create_action(TTR("Anim Change Keyframe Value"), UndoRedo::MERGE_ENDS); int prev = animation->bezier_track_get_key_handle_mode(track, key); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key, prev); + undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); undo_redo->add_do_method(this, "_update_obj", animation); undo_redo->add_undo_method(this, "_update_obj", animation); undo_redo->commit_action(); @@ -635,10 +637,16 @@ public: } break; case Animation::TYPE_BEZIER: { + Animation::HandleMode hm = animation->bezier_track_get_key_handle_mode(track, key); p_list->push_back(PropertyInfo(Variant::FLOAT, PNAME("value"))); - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"))); - p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"))); - p_list->push_back(PropertyInfo(Variant::INT, PNAME("handle_mode"), PROPERTY_HINT_ENUM, "Free,Balanced")); + if (hm == Animation::HANDLE_MODE_LINEAR) { + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY)); + } else { + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("in_handle"))); + p_list->push_back(PropertyInfo(Variant::VECTOR2, PNAME("out_handle"))); + } + p_list->push_back(PropertyInfo(Variant::INT, PNAME("handle_mode"), PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); } break; case Animation::TYPE_AUDIO: { @@ -680,7 +688,7 @@ public: } } - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; Ref<Animation> animation; int track = -1; float key_ofs = 0; @@ -719,17 +727,17 @@ public: return true; } - bool _read_only() { + bool _is_read_only() { return animation_read_only; } static void _bind_methods() { - ClassDB::bind_method("_update_obj", &AnimationMultiTrackKeyEdit::_update_obj); - ClassDB::bind_method("_key_ofs_changed", &AnimationMultiTrackKeyEdit::_key_ofs_changed); - ClassDB::bind_method("_hide_script_from_inspector", &AnimationMultiTrackKeyEdit::_hide_script_from_inspector); - ClassDB::bind_method("get_root_path", &AnimationMultiTrackKeyEdit::get_root_path); - ClassDB::bind_method("_dont_undo_redo", &AnimationMultiTrackKeyEdit::_dont_undo_redo); - ClassDB::bind_method("_read_only", &AnimationMultiTrackKeyEdit::_read_only); + ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationMultiTrackKeyEdit::_update_obj); + ClassDB::bind_method(D_METHOD("_key_ofs_changed"), &AnimationMultiTrackKeyEdit::_key_ofs_changed); + ClassDB::bind_method(D_METHOD("_hide_script_from_inspector"), &AnimationMultiTrackKeyEdit::_hide_script_from_inspector); + ClassDB::bind_method(D_METHOD("get_root_path"), &AnimationMultiTrackKeyEdit::get_root_path); + ClassDB::bind_method(D_METHOD("_dont_undo_redo"), &AnimationMultiTrackKeyEdit::_dont_undo_redo); + ClassDB::bind_method(D_METHOD("_is_read_only"), &AnimationMultiTrackKeyEdit::_is_read_only); } void _fix_node_path(Variant &value, NodePath &base) { @@ -970,8 +978,8 @@ public: undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } Vector2 prev = animation->bezier_track_get_key_in_handle(track, key); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_in_handle", track, key, prev); + undo_redo->add_do_method(this, "_bezier_track_set_key_in_handle", track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_in_handle", track, key, prev); update_obj = true; } else if (name == "out_handle") { const Variant &value = p_value; @@ -981,8 +989,8 @@ public: undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } Vector2 prev = animation->bezier_track_get_key_out_handle(track, key); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_out_handle", track, key, prev); + undo_redo->add_do_method(this, "_bezier_track_set_key_out_handle", track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_out_handle", track, key, prev); update_obj = true; } else if (name == "handle_mode") { const Variant &value = p_value; @@ -992,8 +1000,8 @@ public: undo_redo->create_action(TTR("Anim Multi Change Keyframe Value"), UndoRedo::MERGE_ENDS); } int prev = animation->bezier_track_get_key_handle_mode(track, key); - undo_redo->add_do_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key, value); - undo_redo->add_undo_method(animation.ptr(), "bezier_track_set_key_handle_mode", track, key, prev); + undo_redo->add_do_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, value); + undo_redo->add_undo_method(this, "_bezier_track_set_key_handle_mode", animation.ptr(), track, key, prev); update_obj = true; } } break; @@ -1324,7 +1332,7 @@ public: p_list->push_back(PropertyInfo(Variant::FLOAT, "value")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "in_handle")); p_list->push_back(PropertyInfo(Variant::VECTOR2, "out_handle")); - p_list->push_back(PropertyInfo(Variant::INT, "handle_mode", PROPERTY_HINT_ENUM, "Free,Balanced")); + p_list->push_back(PropertyInfo(Variant::INT, "handle_mode", PROPERTY_HINT_ENUM, "Free,Linear,Balanced,Mirrored")); } break; case Animation::TYPE_AUDIO: { p_list->push_back(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream")); @@ -1374,7 +1382,7 @@ public: bool use_fps = false; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; void notify_change() { notify_property_list_changed(); @@ -1481,7 +1489,6 @@ int AnimationTimelineEdit::get_name_limit() const { void AnimationTimelineEdit::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning"))); add_track->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); @@ -1708,7 +1715,7 @@ Size2 AnimationTimelineEdit::get_minimum_size() const { return ms; } -void AnimationTimelineEdit::set_undo_redo(UndoRedo *p_undo_redo) { +void AnimationTimelineEdit::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { undo_redo = p_undo_redo; } @@ -1743,13 +1750,13 @@ void AnimationTimelineEdit::update_values() { if (use_fps && animation->get_step() > 0) { length->set_value(animation->get_length() / animation->get_step()); length->set_step(1); - length->set_tooltip(TTR("Animation length (frames)")); - time_icon->set_tooltip(TTR("Animation length (frames)")); + length->set_tooltip_text(TTR("Animation length (frames)")); + time_icon->set_tooltip_text(TTR("Animation length (frames)")); } else { length->set_value(animation->get_length()); length->set_step(0.001); - length->set_tooltip(TTR("Animation length (seconds)")); - time_icon->set_tooltip(TTR("Animation length (seconds)")); + length->set_tooltip_text(TTR("Animation length (seconds)")); + time_icon->set_tooltip_text(TTR("Animation length (seconds)")); } switch (animation->get_loop_mode()) { @@ -1934,7 +1941,7 @@ AnimationTimelineEdit::AnimationTimelineEdit() { len_hb->add_child(expander); time_icon = memnew(TextureRect); time_icon->set_v_size_flags(SIZE_SHRINK_CENTER); - time_icon->set_tooltip(TTR("Animation length (seconds)")); + time_icon->set_tooltip_text(TTR("Animation length (seconds)")); len_hb->add_child(time_icon); length = memnew(EditorSpinSlider); length->set_min(0.001); @@ -1943,12 +1950,12 @@ AnimationTimelineEdit::AnimationTimelineEdit() { length->set_allow_greater(true); length->set_custom_minimum_size(Vector2(70 * EDSCALE, 0)); length->set_hide_slider(true); - length->set_tooltip(TTR("Animation length (seconds)")); + length->set_tooltip_text(TTR("Animation length (seconds)")); length->connect("value_changed", callable_mp(this, &AnimationTimelineEdit::_anim_length_changed)); len_hb->add_child(length); loop = memnew(Button); loop->set_flat(true); - loop->set_tooltip(TTR("Animation Looping")); + loop->set_tooltip_text(TTR("Animation Looping")); loop->connect("pressed", callable_mp(this, &AnimationTimelineEdit::_anim_loop_pressed)); loop->set_toggle_mode(true); len_hb->add_child(loop); @@ -2114,11 +2121,12 @@ void AnimationTrackEdit::_notification(int p_what) { get_theme_icon(SNAME("InterpWrapClamp"), SNAME("EditorIcons")), get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")), }; - - Ref<Texture2D> interp_icon[3] = { + Ref<Texture2D> interp_icon[5] = { get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")), get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")), - get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")) + get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")), + get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")), + get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")), }; Ref<Texture2D> cont_icon[4] = { get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), @@ -2507,10 +2515,14 @@ Size2 AnimationTrackEdit::get_minimum_size() const { return Vector2(1, max_h + separation); } -void AnimationTrackEdit::set_undo_redo(UndoRedo *p_undo_redo) { +void AnimationTrackEdit::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { undo_redo = p_undo_redo; } +Ref<EditorUndoRedoManager> AnimationTrackEdit::get_undo_redo() const { + return undo_redo; +} + void AnimationTrackEdit::set_timeline(AnimationTimelineEdit *p_timeline) { timeline = p_timeline; timeline->set_track_edit(this); @@ -2720,9 +2732,15 @@ String AnimationTrackEdit::get_tooltip(const Point2 &p_pos) const { case Animation::HANDLE_MODE_FREE: { text += TTR("Handle mode: Free\n"); } break; + case Animation::HANDLE_MODE_LINEAR: { + text += TTR("Handle mode: Linear\n"); + } break; case Animation::HANDLE_MODE_BALANCED: { text += TTR("Handle mode: Balanced\n"); } break; + case Animation::HANDLE_MODE_MIRRORED: { + text += TTR("Handle mode: Mirrored\n"); + } break; } } break; case Animation::TYPE_AUDIO: { @@ -2831,6 +2849,23 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) { menu->add_icon_item(get_theme_icon(SNAME("InterpRaw"), SNAME("EditorIcons")), TTR("Nearest"), MENU_INTERPOLATION_NEAREST); menu->add_icon_item(get_theme_icon(SNAME("InterpLinear"), SNAME("EditorIcons")), TTR("Linear"), MENU_INTERPOLATION_LINEAR); menu->add_icon_item(get_theme_icon(SNAME("InterpCubic"), SNAME("EditorIcons")), TTR("Cubic"), MENU_INTERPOLATION_CUBIC); + // Check is angle property. + AnimationPlayerEditor *ape = AnimationPlayerEditor::get_singleton(); + if (ape) { + AnimationPlayer *ap = ape->get_player(); + if (ap) { + NodePath path = animation->track_get_path(track); + Node *nd = ap->get_node(ap->get_root())->get_node(NodePath(path.get_concatenated_names())); + StringName prop = path.get_concatenated_subnames(); + PropertyInfo prop_info; + ClassDB::get_property_info(nd->get_class(), prop, &prop_info); + bool is_angle = prop_info.type == Variant::FLOAT && prop_info.hint_string.find("radians") != -1; + if (is_angle) { + menu->add_icon_item(get_theme_icon(SNAME("InterpLinearAngle"), SNAME("EditorIcons")), TTR("Linear Angle"), MENU_INTERPOLATION_LINEAR_ANGLE); + menu->add_icon_item(get_theme_icon(SNAME("InterpCubicAngle"), SNAME("EditorIcons")), TTR("Cubic Angle"), MENU_INTERPOLATION_CUBIC_ANGLE); + } + } + } menu->reset_size(); Vector2 popup_pos = get_screen_position() + interp_mode_rect.position + Vector2(0, interp_mode_rect.size.height); @@ -3171,7 +3206,9 @@ void AnimationTrackEdit::_menu_selected(int p_index) { } break; case MENU_INTERPOLATION_NEAREST: case MENU_INTERPOLATION_LINEAR: - case MENU_INTERPOLATION_CUBIC: { + case MENU_INTERPOLATION_CUBIC: + case MENU_INTERPOLATION_LINEAR_ANGLE: + case MENU_INTERPOLATION_CUBIC_ANGLE: { Animation::InterpolationType interp_mode = Animation::InterpolationType(p_index - MENU_INTERPOLATION_NEAREST); undo_redo->create_action(TTR("Change Animation Interpolation Mode")); undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", track, interp_mode); @@ -3251,7 +3288,6 @@ void AnimationTrackEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("insert_key", PropertyInfo(Variant::FLOAT, "offset"))); ADD_SIGNAL(MethodInfo("select_key", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "single"))); ADD_SIGNAL(MethodInfo("deselect_key", PropertyInfo(Variant::INT, "index"))); - ADD_SIGNAL(MethodInfo("bezier_edit")); ADD_SIGNAL(MethodInfo("move_selection_begin")); ADD_SIGNAL(MethodInfo("move_selection", PropertyInfo(Variant::FLOAT, "offset"))); @@ -3414,7 +3450,8 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re track_edits[_get_track_selected()]->release_focus(); } if (animation.is_valid()) { - animation->disconnect("changed", callable_mp(this, &AnimationTrackEditor::_animation_changed)); + animation->disconnect("tracks_changed", callable_mp(this, &AnimationTrackEditor::_animation_changed)); + animation->disconnect("changed", callable_mp(this, &AnimationTrackEditor::_sync_animation_change)); _clear_selection(); } animation = p_anim; @@ -3425,7 +3462,8 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re _update_tracks(); if (animation.is_valid()) { - animation->connect("changed", callable_mp(this, &AnimationTrackEditor::_animation_changed)); + animation->connect("tracks_changed", callable_mp(this, &AnimationTrackEditor::_animation_changed), CONNECT_DEFERRED); + animation->connect("changed", callable_mp(this, &AnimationTrackEditor::_sync_animation_change), CONNECT_DEFERRED); hscroll->show(); edit->set_disabled(read_only); @@ -4340,13 +4378,12 @@ AnimationTrackEditor::TrackIndices AnimationTrackEditor::_confirm_insert(InsertD } break; case Animation::TYPE_BEZIER: { Array array; - array.resize(6); + array.resize(5); array[0] = p_id.value; array[1] = -0.25; array[2] = 0; array[3] = 0.25; array[4] = 0; - array[5] = Animation::HANDLE_MODE_BALANCED; value = array; bezier_edit_icon->set_disabled(false); @@ -4562,7 +4599,7 @@ void AnimationTrackEditor::_update_tracks() { g->set_type_and_name(icon, name, animation->track_get_path(i)); g->set_root(root); - g->set_tooltip(tooltip); + g->set_tooltip_text(tooltip); g->set_timeline(timeline); groups.push_back(g); VBoxContainer *vb = memnew(VBoxContainer); @@ -4609,11 +4646,19 @@ void AnimationTrackEditor::_update_tracks() { } } +void AnimationTrackEditor::_sync_animation_change() { + bezier_edit->update(); +} + void AnimationTrackEditor::_animation_changed() { if (animation_changing_awaiting_update) { return; // All will be updated, don't bother with anything. } + if (key_edit) { + _update_key_edit(); + } + if (key_edit && key_edit->setting) { // If editing a key, just update the edited track, makes refresh less costly. if (key_edit->track < track_edits.size()) { @@ -4713,8 +4758,8 @@ void AnimationTrackEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { panner->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/animation_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning"))); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { zoom_icon->set_texture(get_theme_icon(SNAME("Zoom"), SNAME("EditorIcons"))); bezier_edit_icon->set_icon(get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons"))); @@ -5073,13 +5118,12 @@ void AnimationTrackEditor::_insert_key_from_track(float p_ofs, int p_track) { Variant value; _find_hint_for_track(p_track, bp, &value); Array arr; - arr.resize(6); + arr.resize(5); arr[0] = value; arr[1] = -0.25; arr[2] = 0; arr[3] = 0.25; arr[4] = 0; - arr[5] = 0; undo_redo->create_action(TTR("Add Track Key")); undo_redo->add_do_method(animation.ptr(), "track_insert_key", p_track, p_ofs, arr); @@ -5558,6 +5602,13 @@ void AnimationTrackEditor::_bezier_edit(int p_for_track) { // Search everything within the track and curve - edit it. } +void AnimationTrackEditor::_bezier_track_set_key_handle_mode(Animation *p_anim, int p_track, int p_index, Animation::HandleMode p_mode, Animation::HandleSetMode p_set_mode) { + if (!p_anim) { + return; + } + p_anim->bezier_track_set_key_handle_mode(p_track, p_index, p_mode, p_set_mode); +} + void AnimationTrackEditor::_anim_duplicate_keys(bool transpose) { // Duplicait! if (selection.size() && animation.is_valid() && (!transpose || (_get_track_selected() >= 0 && _get_track_selected() < animation->get_track_count()))) { @@ -5963,6 +6014,98 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { #undef NEW_POS undo_redo->commit_action(); } break; + + case EDIT_EASE_SELECTION: { + ease_dialog->popup_centered(Size2(200, 100) * EDSCALE); + } break; + case EDIT_EASE_CONFIRM: { + undo_redo->create_action(TTR("Make Easing Keys")); + + Tween::TransitionType transition_type = static_cast<Tween::TransitionType>(transition_selection->get_selected_id()); + Tween::EaseType ease_type = static_cast<Tween::EaseType>(ease_selection->get_selected_id()); + float fps = ease_fps->get_value(); + double dur_step = 1.0 / fps; + + // Organize track and key. + HashMap<int, Vector<int>> keymap; + Vector<int> tracks; + for (const KeyValue<SelectedKey, KeyInfo> &E : selection) { + if (!tracks.has(E.key.track)) { + tracks.append(E.key.track); + } + } + for (int i = 0; i < tracks.size(); i++) { + switch (animation->track_get_type(tracks[i])) { + case Animation::TYPE_VALUE: + case Animation::TYPE_POSITION_3D: + case Animation::TYPE_ROTATION_3D: + case Animation::TYPE_SCALE_3D: + case Animation::TYPE_BLEND_SHAPE: { + Vector<int> keys; + for (const KeyValue<SelectedKey, KeyInfo> &E : selection) { + if (E.key.track == tracks[i]) { + keys.append(E.key.key); + } + } + keys.sort(); + keymap.insert(tracks[i], keys); + } break; + default: { + } break; + } + } + + // Make easing. + HashMap<int, Vector<int>>::Iterator E = keymap.begin(); + while (E) { + int track = E->key; + Vector<int> keys = E->value; + int len = keys.size() - 1; + + // Special case for angle interpolation. + bool is_using_angle = animation->track_get_interpolation_type(track) == Animation::INTERPOLATION_LINEAR_ANGLE || animation->track_get_interpolation_type(track) == Animation::INTERPOLATION_CUBIC_ANGLE; + + // Make insert queue. + Vector<Pair<double, Variant>> insert_queue; + for (int i = 0; i < len; i++) { + // Check neighboring keys. + if (keys[i] + 1 == keys[i + 1]) { + double from_t = animation->track_get_key_time(track, keys[i]); + double to_t = animation->track_get_key_time(track, keys[i + 1]); + Variant from_v = animation->track_get_key_value(track, keys[i]); + Variant to_v = animation->track_get_key_value(track, keys[i + 1]); + if (is_using_angle) { + real_t a = from_v; + real_t b = to_v; + real_t to_diff = fmod(b - a, Math_TAU); + to_v = a + fmod(2.0 * to_diff, Math_TAU) - to_diff; + } + Variant delta_v; + Variant::sub(to_v, from_v, delta_v); + double duration = to_t - from_t; + double fixed_duration = duration - 0.01; // Prevent to overwrap keys... + for (double delta_t = dur_step; delta_t < fixed_duration; delta_t += dur_step) { + Pair<double, Variant> keydata; + keydata.first = from_t + delta_t; + keydata.second = Tween::interpolate_variant(from_v, delta_v, delta_t, duration, transition_type, ease_type); + insert_queue.append(keydata); + } + } + } + + // Do insertion. + for (int i = 0; i < insert_queue.size(); i++) { + undo_redo->add_do_method(animation.ptr(), "track_insert_key", track, insert_queue[i].first, insert_queue[i].second); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", track, insert_queue[i].first); + } + + ++E; + } + + undo_redo->commit_action(); + + } break; + case EDIT_DUPLICATE_SELECTION: { if (bezier_edit->is_visible()) { bezier_edit->duplicate_selection(); @@ -6052,18 +6195,131 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { case EDIT_GOTO_PREV_STEP: { goto_prev_step(false); } break; + + case EDIT_BAKE_TRACK: { + bake_dialog->popup_centered(Size2(200, 100) * EDSCALE); + } break; + case EDIT_BAKE_TRACK_CONFIRM: { + undo_redo->create_action(TTR("Bake Track as Linear keys.")); + + int track_len = animation->get_track_count(); + bool b_trs = bake_trs->is_pressed(); + bool b_bs = bake_blendshape->is_pressed(); + bool b_v = bake_value->is_pressed(); + + double anim_len = animation->get_length() + CMP_EPSILON; // For end key. + float fps = bake_fps->get_value(); + double dur_step = 1.0 / fps; + + for (int i = 0; i < track_len; i++) { + bool do_bake = false; + Animation::TrackType type = animation->track_get_type(i); + do_bake |= b_trs && (type == Animation::TYPE_POSITION_3D || type == Animation::TYPE_ROTATION_3D || type == Animation::TYPE_SCALE_3D); + do_bake |= b_bs && type == Animation::TYPE_BLEND_SHAPE; + do_bake |= b_v && type == Animation::TYPE_VALUE; + if (do_bake && !animation->track_is_compressed(i)) { + Animation::InterpolationType it = animation->track_get_interpolation_type(i); + if (it == Animation::INTERPOLATION_NEAREST) { + continue; // Nearest and Angle interpolation cannot be baked. + } + + // Special case for angle interpolation. + bool is_using_angle = it == Animation::INTERPOLATION_LINEAR_ANGLE || it == Animation::INTERPOLATION_CUBIC_ANGLE; + + // Make insert queue. + Vector<Pair<double, Variant>> insert_queue; + + switch (type) { + case Animation::TYPE_POSITION_3D: { + for (double delta_t = 0.0; delta_t <= anim_len; delta_t += dur_step) { + Pair<double, Variant> keydata; + keydata.first = delta_t; + Vector3 v; + animation->position_track_interpolate(i, delta_t, &v); + keydata.second = v; + insert_queue.append(keydata); + } + } break; + case Animation::TYPE_ROTATION_3D: { + for (double delta_t = 0.0; delta_t <= anim_len; delta_t += dur_step) { + Pair<double, Variant> keydata; + keydata.first = delta_t; + Quaternion v; + animation->rotation_track_interpolate(i, delta_t, &v); + keydata.second = v; + insert_queue.append(keydata); + } + } break; + case Animation::TYPE_SCALE_3D: { + for (double delta_t = 0.0; delta_t <= anim_len; delta_t += dur_step) { + Pair<double, Variant> keydata; + keydata.first = delta_t; + Vector3 v; + animation->scale_track_interpolate(i, delta_t, &v); + keydata.second = v; + insert_queue.append(keydata); + } + } break; + case Animation::TYPE_BLEND_SHAPE: { + for (double delta_t = 0.0; delta_t <= anim_len; delta_t += dur_step) { + Pair<double, Variant> keydata; + keydata.first = delta_t; + float v; + animation->blend_shape_track_interpolate(i, delta_t, &v); + keydata.second = v; + insert_queue.append(keydata); + } + } break; + case Animation::TYPE_VALUE: { + for (double delta_t = 0.0; delta_t < anim_len; delta_t += dur_step) { + Pair<double, Variant> keydata; + keydata.first = delta_t; + keydata.second = animation->value_track_interpolate(i, delta_t); + insert_queue.append(keydata); + } + } break; + default: { + } break; + } + + // Cleanup keys. + int key_len = animation->track_get_key_count(i); + for (int j = key_len - 1; j >= 0; j--) { + undo_redo->add_do_method(animation.ptr(), "track_remove_key", i, j); + } + + // Insert keys. + undo_redo->add_do_method(animation.ptr(), "track_set_interpolation_type", i, is_using_angle ? Animation::INTERPOLATION_LINEAR_ANGLE : Animation::INTERPOLATION_LINEAR); + for (int j = insert_queue.size() - 1; j >= 0; j--) { + undo_redo->add_do_method(animation.ptr(), "track_insert_key", i, insert_queue[j].first, insert_queue[j].second); + undo_redo->add_undo_method(animation.ptr(), "track_remove_key", i, j); + } + + // Undo methods. + undo_redo->add_undo_method(animation.ptr(), "track_set_interpolation_type", i, animation->track_get_interpolation_type(i)); + for (int j = key_len - 1; j >= 0; j--) { + undo_redo->add_undo_method(animation.ptr(), "track_insert_key", i, animation->track_get_key_time(i, j), animation->track_get_key_value(i, j), animation->track_get_key_transition(i, j)); + } + } + } + + undo_redo->commit_action(); + + } break; + case EDIT_APPLY_RESET: { AnimationPlayerEditor::get_singleton()->get_player()->apply_reset(true); - } break; + case EDIT_OPTIMIZE_ANIMATION: { optimize_dialog->popup_centered(Size2(250, 180) * EDSCALE); } break; case EDIT_OPTIMIZE_ANIMATION_CONFIRM: { - animation->optimize(optimize_linear_error->get_value(), optimize_angular_error->get_value(), optimize_max_angle->get_value()); + animation->optimize(optimize_velocity_error->get_value(), optimize_angular_error->get_value(), optimize_precision_error->get_value()); _update_tracks(); - undo_redo->clear_history(); + undo_redo->clear_history(true, undo_redo->get_history_for_object(animation.ptr()).id); + undo_redo->clear_history(true, undo_redo->get_history_for_object(this).id); } break; case EDIT_CLEAN_UP_ANIMATION: { @@ -6131,7 +6387,8 @@ void AnimationTrackEditor::_cleanup_animation(Ref<Animation> p_animation) { } } - undo_redo->clear_history(); + undo_redo->clear_history(true, undo_redo->get_history_for_object(animation.ptr()).id); + undo_redo->clear_history(true, undo_redo->get_history_for_object(this).id); _update_tracks(); } @@ -6218,15 +6475,17 @@ void AnimationTrackEditor::_select_all_tracks_for_copy() { } void AnimationTrackEditor::_bind_methods() { - ClassDB::bind_method("_animation_update", &AnimationTrackEditor::_animation_update); - ClassDB::bind_method("_track_grab_focus", &AnimationTrackEditor::_track_grab_focus); - ClassDB::bind_method("_update_tracks", &AnimationTrackEditor::_update_tracks); - ClassDB::bind_method("_clear_selection_for_anim", &AnimationTrackEditor::_clear_selection_for_anim); - ClassDB::bind_method("_select_at_anim", &AnimationTrackEditor::_select_at_anim); + ClassDB::bind_method(D_METHOD("_animation_update"), &AnimationTrackEditor::_animation_update); + ClassDB::bind_method(D_METHOD("_track_grab_focus"), &AnimationTrackEditor::_track_grab_focus); + ClassDB::bind_method(D_METHOD("_update_tracks"), &AnimationTrackEditor::_update_tracks); + ClassDB::bind_method(D_METHOD("_clear_selection_for_anim"), &AnimationTrackEditor::_clear_selection_for_anim); + ClassDB::bind_method(D_METHOD("_select_at_anim"), &AnimationTrackEditor::_select_at_anim); - ClassDB::bind_method("_key_selected", &AnimationTrackEditor::_key_selected); // Still used by some connect_compat. - ClassDB::bind_method("_key_deselected", &AnimationTrackEditor::_key_deselected); // Still used by some connect_compat. - ClassDB::bind_method("_clear_selection", &AnimationTrackEditor::_clear_selection); // Still used by some connect_compat. + ClassDB::bind_method(D_METHOD("_key_selected"), &AnimationTrackEditor::_key_selected); // Still used by some connect_compat. + ClassDB::bind_method(D_METHOD("_key_deselected"), &AnimationTrackEditor::_key_deselected); // Still used by some connect_compat. + ClassDB::bind_method(D_METHOD("_clear_selection"), &AnimationTrackEditor::_clear_selection); // Still used by some connect_compat. + + ClassDB::bind_method(D_METHOD("_bezier_track_set_key_handle_mode", "animation", "track_idx", "key_idx", "key_handle_mode", "key_handle_set_mode"), &AnimationTrackEditor::_bezier_track_set_key_handle_mode, DEFVAL(Animation::HANDLE_SET_MODE_NONE)); ADD_SIGNAL(MethodInfo("timeline_changed", PropertyInfo(Variant::FLOAT, "position"), PropertyInfo(Variant::BOOL, "drag"), PropertyInfo(Variant::BOOL, "timeline_only"))); ADD_SIGNAL(MethodInfo("keying_changed")); @@ -6301,7 +6560,7 @@ void AnimationTrackEditor::_pick_track_filter_input(const Ref<InputEvent> &p_ie) } AnimationTrackEditor::AnimationTrackEditor() { - undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo = EditorNode::get_undo_redo(); main_panel = memnew(PanelContainer); main_panel->set_focus_mode(FOCUS_ALL); // Allow panel to have focus so that shortcuts work as expected. @@ -6378,7 +6637,7 @@ AnimationTrackEditor::AnimationTrackEditor() { imported_anim_warning = memnew(Button); imported_anim_warning->hide(); imported_anim_warning->set_text(TTR("Imported Scene")); - imported_anim_warning->set_tooltip(TTR("Warning: Editing imported animation")); + imported_anim_warning->set_tooltip_text(TTR("Warning: Editing imported animation")); imported_anim_warning->connect("pressed", callable_mp(this, &AnimationTrackEditor::_show_imported_anim_warning)); bottom_hb->add_child(imported_anim_warning); @@ -6389,7 +6648,7 @@ AnimationTrackEditor::AnimationTrackEditor() { bezier_edit_icon->set_disabled(true); bezier_edit_icon->set_toggle_mode(true); bezier_edit_icon->connect("pressed", callable_mp(this, &AnimationTrackEditor::_toggle_bezier_edit)); - bezier_edit_icon->set_tooltip(TTR("Toggle between the bezier curve editor and track editor.")); + bezier_edit_icon->set_tooltip_text(TTR("Toggle between the bezier curve editor and track editor.")); bottom_hb->add_child(bezier_edit_icon); @@ -6397,7 +6656,7 @@ AnimationTrackEditor::AnimationTrackEditor() { selected_filter->set_flat(true); selected_filter->connect("pressed", callable_mp(this, &AnimationTrackEditor::_view_group_toggle)); // Same function works the same. selected_filter->set_toggle_mode(true); - selected_filter->set_tooltip(TTR("Only show tracks from nodes selected in tree.")); + selected_filter->set_tooltip_text(TTR("Only show tracks from nodes selected in tree.")); bottom_hb->add_child(selected_filter); @@ -6405,7 +6664,7 @@ AnimationTrackEditor::AnimationTrackEditor() { view_group->set_flat(true); view_group->connect("pressed", callable_mp(this, &AnimationTrackEditor::_view_group_toggle)); view_group->set_toggle_mode(true); - view_group->set_tooltip(TTR("Group tracks by node or display them as plain list.")); + view_group->set_tooltip_text(TTR("Group tracks by node or display them as plain list.")); bottom_hb->add_child(view_group); bottom_hb->add_child(memnew(VSeparator)); @@ -6424,7 +6683,7 @@ AnimationTrackEditor::AnimationTrackEditor() { step->set_step(0.001); step->set_hide_slider(true); step->set_custom_minimum_size(Size2(100, 0) * EDSCALE); - step->set_tooltip(TTR("Animation step value.")); + step->set_tooltip_text(TTR("Animation step value.")); bottom_hb->add_child(step); step->connect("value_changed", callable_mp(this, &AnimationTrackEditor::_update_step)); step->set_read_only(true); @@ -6456,13 +6715,15 @@ AnimationTrackEditor::AnimationTrackEditor() { edit->set_text(TTR("Edit")); edit->set_flat(false); edit->set_disabled(true); - edit->set_tooltip(TTR("Animation properties.")); + edit->set_tooltip_text(TTR("Animation properties.")); edit->get_popup()->add_item(TTR("Copy Tracks"), EDIT_COPY_TRACKS); edit->get_popup()->add_item(TTR("Paste Tracks"), EDIT_PASTE_TRACKS); edit->get_popup()->add_separator(); edit->get_popup()->add_item(TTR("Scale Selection"), EDIT_SCALE_SELECTION); edit->get_popup()->add_item(TTR("Scale From Cursor"), EDIT_SCALE_FROM_CURSOR); edit->get_popup()->add_separator(); + edit->get_popup()->add_item(TTR("Make Easing Selection"), EDIT_EASE_SELECTION); + edit->get_popup()->add_separator(); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CMD | Key::D), EDIT_DUPLICATE_SELECTION); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/duplicate_selection_transposed", TTR("Duplicate Transposed"), KeyModifierMask::SHIFT | KeyModifierMask::CMD | Key::D), EDIT_DUPLICATE_TRANSPOSED); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/add_reset_value", TTR("Add RESET Value(s)"))); @@ -6473,10 +6734,12 @@ AnimationTrackEditor::AnimationTrackEditor() { edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_next_step", TTR("Go to Next Step"), KeyModifierMask::CMD | Key::RIGHT), EDIT_GOTO_NEXT_STEP); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/goto_prev_step", TTR("Go to Previous Step"), KeyModifierMask::CMD | Key::LEFT), EDIT_GOTO_PREV_STEP); edit->get_popup()->add_separator(); + edit->get_popup()->add_item(TTR("Bake Track"), EDIT_BAKE_TRACK); + edit->get_popup()->add_separator(); edit->get_popup()->add_shortcut(ED_SHORTCUT("animation_editor/apply_reset", TTR("Apply Reset")), EDIT_APPLY_RESET); edit->get_popup()->add_separator(); - edit->get_popup()->add_item(TTR("Optimize Animation"), EDIT_OPTIMIZE_ANIMATION); - edit->get_popup()->add_item(TTR("Clean-Up Animation"), EDIT_CLEAN_UP_ANIMATION); + edit->get_popup()->add_item(TTR("Optimize Animation (no undo)"), EDIT_OPTIMIZE_ANIMATION); + edit->get_popup()->add_item(TTR("Clean-Up Animation (no undo)"), EDIT_CLEAN_UP_ANIMATION); edit->get_popup()->connect("id_pressed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed)); edit->get_popup()->connect("about_to_popup", callable_mp(this, &AnimationTrackEditor::_edit_menu_about_to_popup)); @@ -6536,25 +6799,24 @@ AnimationTrackEditor::AnimationTrackEditor() { VBoxContainer *optimize_vb = memnew(VBoxContainer); optimize_dialog->add_child(optimize_vb); - optimize_linear_error = memnew(SpinBox); - optimize_linear_error->set_max(1.0); - optimize_linear_error->set_min(0.001); - optimize_linear_error->set_step(0.001); - optimize_linear_error->set_value(0.05); - optimize_vb->add_margin_child(TTR("Max. Linear Error:"), optimize_linear_error); + optimize_velocity_error = memnew(SpinBox); + optimize_velocity_error->set_max(1.0); + optimize_velocity_error->set_min(0.001); + optimize_velocity_error->set_step(0.001); + optimize_velocity_error->set_value(0.01); + optimize_vb->add_margin_child(TTR("Max. Velocity Error:"), optimize_velocity_error); optimize_angular_error = memnew(SpinBox); optimize_angular_error->set_max(1.0); optimize_angular_error->set_min(0.001); optimize_angular_error->set_step(0.001); optimize_angular_error->set_value(0.01); - optimize_vb->add_margin_child(TTR("Max. Angular Error:"), optimize_angular_error); - optimize_max_angle = memnew(SpinBox); - optimize_vb->add_margin_child(TTR("Max Optimizable Angle:"), optimize_max_angle); - optimize_max_angle->set_max(360.0); - optimize_max_angle->set_min(0.0); - optimize_max_angle->set_step(0.1); - optimize_max_angle->set_value(22); + optimize_precision_error = memnew(SpinBox); + optimize_precision_error->set_max(6); + optimize_precision_error->set_min(1); + optimize_precision_error->set_step(1); + optimize_precision_error->set_value(3); + optimize_vb->add_margin_child(TTR("Max. Precision Error:"), optimize_precision_error); optimize_dialog->set_ok_button_text(TTR("Optimize")); optimize_dialog->connect("confirmed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_OPTIMIZE_ANIMATION_CONFIRM)); @@ -6598,6 +6860,88 @@ AnimationTrackEditor::AnimationTrackEditor() { scale_dialog->connect("confirmed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_SCALE_CONFIRM)); add_child(scale_dialog); + // + ease_dialog = memnew(ConfirmationDialog); + ease_dialog->set_title(TTR("Select Transition and Easing")); + ease_dialog->connect("confirmed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_EASE_CONFIRM)); + add_child(ease_dialog); + GridContainer *ease_grid = memnew(GridContainer); + ease_grid->set_columns(2); + ease_dialog->add_child(ease_grid); + transition_selection = memnew(OptionButton); + transition_selection->add_item("Linear", Tween::TRANS_LINEAR); + transition_selection->add_item("Sine", Tween::TRANS_SINE); + transition_selection->add_item("Quint", Tween::TRANS_QUINT); + transition_selection->add_item("Quart", Tween::TRANS_QUART); + transition_selection->add_item("Quad", Tween::TRANS_QUAD); + transition_selection->add_item("Expo", Tween::TRANS_EXPO); + transition_selection->add_item("Elastic", Tween::TRANS_ELASTIC); + transition_selection->add_item("Cubic", Tween::TRANS_CUBIC); + transition_selection->add_item("Circ", Tween::TRANS_CIRC); + transition_selection->add_item("Bounce", Tween::TRANS_BOUNCE); + transition_selection->add_item("Back", Tween::TRANS_BACK); + transition_selection->select(Tween::TRANS_LINEAR); // Default + ease_selection = memnew(OptionButton); + ease_selection->add_item("In", Tween::EASE_IN); + ease_selection->add_item("Out", Tween::EASE_OUT); + ease_selection->add_item("InOut", Tween::EASE_IN_OUT); + ease_selection->add_item("OutIn", Tween::EASE_OUT_IN); + ease_selection->select(Tween::EASE_IN_OUT); // Default + ease_fps = memnew(SpinBox); + ease_fps->set_min(1); + ease_fps->set_max(999); + ease_fps->set_step(1); + ease_fps->set_value(30); // Default + Label *ease_label1 = memnew(Label); + Label *ease_label2 = memnew(Label); + Label *ease_label3 = memnew(Label); + ease_label1->set_text("Transition Type:"); + ease_label2->set_text("Ease Type:"); + ease_label3->set_text("FPS:"); + ease_grid->add_child(ease_label1); + ease_grid->add_child(transition_selection); + ease_grid->add_child(ease_label2); + ease_grid->add_child(ease_selection); + ease_grid->add_child(ease_label3); + ease_grid->add_child(ease_fps); + + // + bake_dialog = memnew(ConfirmationDialog); + bake_dialog->set_title(TTR("Track Baker")); + bake_dialog->connect("confirmed", callable_mp(this, &AnimationTrackEditor::_edit_menu_pressed).bind(EDIT_BAKE_TRACK_CONFIRM)); + add_child(bake_dialog); + GridContainer *bake_grid = memnew(GridContainer); + bake_grid->set_columns(2); + bake_dialog->add_child(bake_grid); + bake_trs = memnew(CheckBox); + bake_trs->set_pressed(true); + bake_blendshape = memnew(CheckBox); + bake_blendshape->set_pressed(true); + bake_value = memnew(CheckBox); + bake_value->set_pressed(true); + bake_fps = memnew(SpinBox); + bake_fps->set_min(1); + bake_fps->set_max(999); + bake_fps->set_step(1); + bake_fps->set_value(30); // Default + Label *bake_label1 = memnew(Label); + Label *bake_label2 = memnew(Label); + Label *bake_label3 = memnew(Label); + Label *bake_label4 = memnew(Label); + bake_label1->set_text("Pos/Rot/Scl3D Track:"); + bake_label2->set_text("Blendshape Track:"); + bake_label3->set_text("Value Track:"); + bake_label4->set_text("FPS:"); + bake_grid->add_child(bake_label1); + bake_grid->add_child(bake_trs); + bake_grid->add_child(bake_label2); + bake_grid->add_child(bake_blendshape); + bake_grid->add_child(bake_label3); + bake_grid->add_child(bake_value); + bake_grid->add_child(bake_label4); + bake_grid->add_child(bake_fps); + + // track_copy_dialog = memnew(ConfirmationDialog); add_child(track_copy_dialog); track_copy_dialog->set_title(TTR("Select Tracks to Copy")); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index b0553c54a5..a17ee65eab 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -32,6 +32,7 @@ #define ANIMATION_TRACK_EDITOR_H #include "editor/editor_data.h" +#include "editor/editor_properties.h" #include "editor/editor_spin_slider.h" #include "editor/property_selector.h" @@ -78,7 +79,7 @@ class AnimationTimelineEdit : public Range { void _anim_loop_pressed(); void _play_position_draw(); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; Rect2 hsize_rect; bool editing = false; @@ -112,7 +113,7 @@ public: void set_track_edit(AnimationTrackEdit *p_track_edit); void set_zoom(Range *p_zoom); Range *get_zoom() const { return zoom; } - void set_undo_redo(UndoRedo *p_undo_redo); + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void set_play_position(float p_pos); float get_play_position() const; @@ -143,6 +144,8 @@ class AnimationTrackEdit : public Control { MENU_INTERPOLATION_NEAREST, MENU_INTERPOLATION_LINEAR, MENU_INTERPOLATION_CUBIC, + MENU_INTERPOLATION_LINEAR_ANGLE, + MENU_INTERPOLATION_CUBIC_ANGLE, MENU_LOOP_WRAP, MENU_LOOP_CLAMP, MENU_KEY_INSERT, @@ -152,7 +155,7 @@ class AnimationTrackEdit : public Control { }; AnimationTimelineEdit *timeline = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; Popup *path_popup = nullptr; LineEdit *path = nullptr; Node *root = nullptr; @@ -233,12 +236,12 @@ public: Ref<Animation> get_animation() const; AnimationTimelineEdit *get_timeline() const { return timeline; } AnimationTrackEditor *get_editor() const { return editor; } - UndoRedo *get_undo_redo() const { return undo_redo; } + Ref<EditorUndoRedoManager> get_undo_redo() const; NodePath get_path() const; void set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only); virtual Size2 get_minimum_size() const override; - void set_undo_redo(UndoRedo *p_undo_redo); + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void set_timeline(AnimationTimelineEdit *p_timeline); void set_editor(AnimationTrackEditor *p_editor); void set_root(Node *p_root); @@ -322,8 +325,9 @@ class AnimationTrackEditor : public VBoxContainer { Vector<AnimationTrackEditGroup *> groups; bool animation_changing_awaiting_update = false; - void _animation_update(); + void _animation_update(); // Updated by AnimationTrackEditor(this) int _get_track_selected(); + void _sync_animation_change(); void _animation_changed(); void _update_tracks(); @@ -333,7 +337,7 @@ class AnimationTrackEditor : public VBoxContainer { void _animation_track_remove_request(int p_track, Ref<Animation> p_from_animation); void _track_grab_focus(int p_track); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; void _update_scroll(double); void _update_step(double p_new_step); @@ -447,13 +451,20 @@ class AnimationTrackEditor : public VBoxContainer { void _toggle_bezier_edit(); void _cancel_bezier_edit(); void _bezier_edit(int p_for_track); + void _bezier_track_set_key_handle_mode(Animation *p_anim, int p_track, int p_index, Animation::HandleMode p_mode, Animation::HandleSetMode p_set_mode = Animation::HANDLE_SET_MODE_NONE); ////////////// edit menu stuff + ConfirmationDialog *bake_dialog = nullptr; + CheckBox *bake_trs = nullptr; + CheckBox *bake_blendshape = nullptr; + CheckBox *bake_value = nullptr; + SpinBox *bake_fps = nullptr; + ConfirmationDialog *optimize_dialog = nullptr; - SpinBox *optimize_linear_error = nullptr; + SpinBox *optimize_velocity_error = nullptr; SpinBox *optimize_angular_error = nullptr; - SpinBox *optimize_max_angle = nullptr; + SpinBox *optimize_precision_error = nullptr; ConfirmationDialog *cleanup_dialog = nullptr; CheckBox *cleanup_keys = nullptr; @@ -463,6 +474,11 @@ class AnimationTrackEditor : public VBoxContainer { ConfirmationDialog *scale_dialog = nullptr; SpinBox *scale = nullptr; + ConfirmationDialog *ease_dialog = nullptr; + OptionButton *transition_selection = nullptr; + OptionButton *ease_selection = nullptr; + SpinBox *ease_fps = nullptr; + void _select_all_tracks_for_copy(); void _edit_menu_about_to_popup(); @@ -486,9 +502,9 @@ class AnimationTrackEditor : public VBoxContainer { NodePath full_path; NodePath base_path; Animation::TrackType track_type = Animation::TYPE_ANIMATION; - Animation::InterpolationType interp_type = Animation::INTERPOLATION_CUBIC; + Animation::InterpolationType interp_type = Animation::INTERPOLATION_CUBIC_ANGLE; Animation::UpdateMode update_mode = Animation::UPDATE_CAPTURE; - Animation::LoopMode loop_mode = Animation::LOOP_LINEAR; + Animation::LoopMode loop_mode = Animation::LOOP_PINGPONG; bool loop_wrap = false; bool enabled = false; @@ -520,6 +536,8 @@ public: EDIT_SCALE_SELECTION, EDIT_SCALE_FROM_CURSOR, EDIT_SCALE_CONFIRM, + EDIT_EASE_SELECTION, + EDIT_EASE_CONFIRM, EDIT_DUPLICATE_SELECTION, EDIT_DUPLICATE_TRANSPOSED, EDIT_ADD_RESET_KEY, @@ -527,6 +545,8 @@ public: EDIT_GOTO_NEXT_STEP, EDIT_GOTO_NEXT_STEP_TIMELINE_ONLY, // Next step without updating animation. EDIT_GOTO_PREV_STEP, + EDIT_BAKE_TRACK, + EDIT_BAKE_TRACK_CONFIRM, EDIT_APPLY_RESET, EDIT_OPTIMIZE_ANIMATION, EDIT_OPTIMIZE_ANIMATION_CONFIRM, diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp index cd40b53919..ab64aaa24d 100644 --- a/editor/animation_track_editor_plugins.cpp +++ b/editor/animation_track_editor_plugins.cpp @@ -33,6 +33,7 @@ #include "editor/audio_stream_preview.h" #include "editor/editor_resource_preview.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/2d/animated_sprite_2d.h" #include "scene/2d/sprite_2d.h" #include "scene/3d/sprite_3d.h" diff --git a/editor/array_property_edit.cpp b/editor/array_property_edit.cpp index 58527ee4d1..ab0e801c88 100644 --- a/editor/array_property_edit.cpp +++ b/editor/array_property_edit.cpp @@ -32,6 +32,7 @@ #include "core/io/marshalls.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #define ITEMS_PER_PAGE 100 @@ -87,7 +88,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) { return true; } - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Resize Array")); ur->add_do_method(this, "_set_size", newsize); ur->add_undo_method(this, "_set_size", size); @@ -134,7 +135,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) { Callable::CallError ce; Variant new_value; Variant::construct(Variant::Type(type), new_value, nullptr, 0, ce); - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Array Value Type")); ur->add_do_method(this, "_set_value", idx, new_value); @@ -150,7 +151,7 @@ bool ArrayPropertyEdit::_set(const StringName &p_name, const Variant &p_value) { Variant arr = get_array(); Variant value = arr.get(idx); - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Array Value")); ur->add_do_method(this, "_set_value", idx, p_value); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index 9e72c8ec10..9dddcbbeba 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -1740,10 +1740,6 @@ void CodeTextEditor::_update_status_bar_theme() { void CodeTextEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - _update_status_bar_theme(); - } break; - case NOTIFICATION_THEME_CHANGED: { _update_status_bar_theme(); if (toggle_scripts_button->is_visible()) { @@ -1789,7 +1785,7 @@ void CodeTextEditor::toggle_bookmark() { } void CodeTextEditor::goto_next_bookmark() { - Array bmarks = text_editor->get_bookmarked_lines(); + PackedInt32Array bmarks = text_editor->get_bookmarked_lines(); if (bmarks.size() <= 0) { return; } @@ -1813,7 +1809,7 @@ void CodeTextEditor::goto_next_bookmark() { } void CodeTextEditor::goto_prev_bookmark() { - Array bmarks = text_editor->get_bookmarked_lines(); + PackedInt32Array bmarks = text_editor->get_bookmarked_lines(); if (bmarks.size() <= 0) { return; } @@ -1862,7 +1858,7 @@ void CodeTextEditor::update_toggle_scripts_button() { } else { toggle_scripts_button->set_icon(get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"), SNAME("EditorIcons"))); } - toggle_scripts_button->set_tooltip(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text())); + toggle_scripts_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text())); } CodeTextEditor::CodeTextEditor() { @@ -1955,7 +1951,7 @@ CodeTextEditor::CodeTextEditor() { error_button->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER); error_button->set_default_cursor_shape(CURSOR_POINTING_HAND); error_button->connect("pressed", callable_mp(this, &CodeTextEditor::_error_button_pressed)); - error_button->set_tooltip(TTR("Errors")); + error_button->set_tooltip_text(TTR("Errors")); set_error_count(0); // Warnings @@ -1965,14 +1961,14 @@ CodeTextEditor::CodeTextEditor() { warning_button->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER); warning_button->set_default_cursor_shape(CURSOR_POINTING_HAND); warning_button->connect("pressed", callable_mp(this, &CodeTextEditor::_warning_button_pressed)); - warning_button->set_tooltip(TTR("Warnings")); + warning_button->set_tooltip_text(TTR("Warnings")); set_warning_count(0); // Line and column line_and_col_txt = memnew(Label); status_bar->add_child(line_and_col_txt); line_and_col_txt->set_v_size_flags(SIZE_EXPAND | SIZE_SHRINK_CENTER); - line_and_col_txt->set_tooltip(TTR("Line and column numbers.")); + line_and_col_txt->set_tooltip_text(TTR("Line and column numbers.")); line_and_col_txt->set_mouse_filter(MOUSE_FILTER_STOP); text_editor->connect("gui_input", callable_mp(this, &CodeTextEditor::_text_editor_gui_input)); diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 6fdd9563fb..bfa9081af2 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -34,6 +34,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/scene_tree_dock.h" #include "plugins/script_editor_plugin.h" @@ -229,9 +230,8 @@ void ConnectDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { bind_editor->edit(cdbinds); + } break; - [[fallthrough]]; - } case NOTIFICATION_THEME_CHANGED: { for (int i = 0; i < type_list->get_item_count(); i++) { String type_name = Variant::get_type_name((Variant::Type)type_list->get_item_id(i)); @@ -459,7 +459,7 @@ ConnectDialog::ConnectDialog() { vbc_right->add_margin_child(TTR("Extra Call Arguments:"), bind_editor, true); unbind_count = memnew(SpinBox); - unbind_count->set_tooltip(TTR("Allows to drop arguments sent by signal emitter.")); + unbind_count->set_tooltip_text(TTR("Allows to drop arguments sent by signal emitter.")); unbind_count->connect("value_changed", callable_mp(this, &ConnectDialog::_unbind_count_changed)); vbc_right->add_margin_child(TTR("Unbind Signal Arguments:"), unbind_count); @@ -480,13 +480,13 @@ ConnectDialog::ConnectDialog() { deferred = memnew(CheckBox); deferred->set_h_size_flags(0); deferred->set_text(TTR("Deferred")); - deferred->set_tooltip(TTR("Defers the signal, storing it in a queue and only firing it at idle time.")); + deferred->set_tooltip_text(TTR("Defers the signal, storing it in a queue and only firing it at idle time.")); vbc_right->add_child(deferred); oneshot = memnew(CheckBox); oneshot->set_h_size_flags(0); oneshot->set_text(TTR("Oneshot")); - oneshot->set_tooltip(TTR("Disconnects the signal after its first emission.")); + oneshot->set_tooltip_text(TTR("Disconnects the signal after its first emission.")); vbc_right->add_child(oneshot); cdbinds = memnew(ConnectDialogBinds); @@ -839,6 +839,9 @@ void ConnectionsDock::_handle_signal_menu_option(int p_option) { disconnect_all_dialog->set_text(vformat(TTR("Are you sure you want to remove all connections from the \"%s\" signal?"), signal_name)); disconnect_all_dialog->popup_centered(); } break; + case COPY_NAME: { + DisplayServer::get_singleton()->clipboard_set(item->get_metadata(0).operator Dictionary()["name"]); + } break; } } @@ -909,7 +912,6 @@ void ConnectionsDock::_connect_pressed() { void ConnectionsDock::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); } break; @@ -924,6 +926,10 @@ void ConnectionsDock::_bind_methods() { ClassDB::bind_method("update_tree", &ConnectionsDock::update_tree); } +void ConnectionsDock::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + void ConnectionsDock::set_node(Node *p_node) { selected_node = p_node; update_tree(); @@ -1159,6 +1165,7 @@ ConnectionsDock::ConnectionsDock() { signal_menu->connect("id_pressed", callable_mp(this, &ConnectionsDock::_handle_signal_menu_option)); signal_menu->add_item(TTR("Connect..."), CONNECT); signal_menu->add_item(TTR("Disconnect All"), DISCONNECT_ALL); + signal_menu->add_item(TTR("Copy Name"), COPY_NAME); slot_menu = memnew(PopupMenu); add_child(slot_menu); diff --git a/editor/connections_dialog.h b/editor/connections_dialog.h index d141d1a880..352c5c99d6 100644 --- a/editor/connections_dialog.h +++ b/editor/connections_dialog.h @@ -31,7 +31,6 @@ #ifndef CONNECTIONS_DIALOG_H #define CONNECTIONS_DIALOG_H -#include "core/object/undo_redo.h" #include "editor/editor_inspector.h" #include "editor/scene_tree_editor.h" #include "scene/gui/button.h" @@ -48,6 +47,7 @@ #include "scene/gui/tree.h" class ConnectDialogBinds; +class EditorUndoRedoManager; class ConnectDialog : public ConfirmationDialog { GDCLASS(ConnectDialog, ConfirmationDialog); @@ -177,13 +177,14 @@ class ConnectionsDock : public VBoxContainer { //Right-click Pop-up Menu Options. enum SignalMenuOption { CONNECT, - DISCONNECT_ALL + DISCONNECT_ALL, + COPY_NAME, }; enum SlotMenuOption { EDIT, GO_TO_SCRIPT, - DISCONNECT + DISCONNECT, }; Node *selected_node = nullptr; @@ -194,7 +195,7 @@ class ConnectionsDock : public VBoxContainer { Button *connect_button = nullptr; PopupMenu *signal_menu = nullptr; PopupMenu *slot_menu = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; LineEdit *search_box = nullptr; HashMap<StringName, HashMap<StringName, String>> descr_cache; @@ -225,7 +226,7 @@ protected: static void _bind_methods(); public: - void set_undoredo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void set_node(Node *p_node); void update_tree(); diff --git a/editor/create_dialog.cpp b/editor/create_dialog.cpp index e6168f4924..02ce081c02 100644 --- a/editor/create_dialog.cpp +++ b/editor/create_dialog.cpp @@ -38,7 +38,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" -void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const String &p_select_type) { +void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const String &p_select_type, const String &p_select_name) { _fill_type_list(); icon_fallback = search_options->has_theme_icon(base_type, SNAME("EditorIcons")) ? base_type : "Object"; @@ -57,7 +57,11 @@ void CreateDialog::popup_create(bool p_dont_clear, bool p_replace_mode, const St _update_search(); if (p_replace_mode) { - set_title(vformat(TTR("Change %s Type"), base_type)); + if (!p_select_name.is_empty()) { + set_title(vformat(TTR("Convert %s from %s"), p_select_name, p_select_type)); + } else { + set_title(vformat(TTR("Convert %s"), p_select_type)); + } set_ok_button_text(TTR("Change")); } else { set_title(vformat(TTR("Create New %s"), base_type)); @@ -419,16 +423,10 @@ void CreateDialog::_sbox_input(const Ref<InputEvent> &p_ie) { } } -void CreateDialog::_update_theme() { - search_box->set_right_icon(search_options->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); - favorite->set_icon(search_options->get_theme_icon(SNAME("Favorites"), SNAME("EditorIcons"))); -} - void CreateDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { connect("confirmed", callable_mp(this, &CreateDialog::_confirmed)); - _update_theme(); } break; case NOTIFICATION_EXIT_TREE: { @@ -445,7 +443,8 @@ void CreateDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - _update_theme(); + search_box->set_right_icon(search_options->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + favorite->set_icon(search_options->get_theme_icon(SNAME("Favorites"), SNAME("EditorIcons"))); } break; } } @@ -776,7 +775,7 @@ CreateDialog::CreateDialog() { favorite = memnew(Button); favorite->set_toggle_mode(true); - favorite->set_tooltip(TTR("(Un)favorite selected item.")); + favorite->set_tooltip_text(TTR("(Un)favorite selected item.")); favorite->connect("pressed", callable_mp(this, &CreateDialog::_favorite_toggled)); search_hb->add_child(favorite); vbc->add_margin_child(TTR("Search:"), search_hb); diff --git a/editor/create_dialog.h b/editor/create_dialog.h index 04094108ad..9a17993e11 100644 --- a/editor/create_dialog.h +++ b/editor/create_dialog.h @@ -101,8 +101,6 @@ class CreateDialog : public ConfirmationDialog { bool _is_class_disabled_by_feature_profile(const StringName &p_class) const; void _load_favorites_and_history(); - void _update_theme(); - protected: void _notification(int p_what); static void _bind_methods(); @@ -120,7 +118,7 @@ public: void set_preferred_search_result_type(const String &p_preferred_type) { preferred_search_result_type = p_preferred_type; } String get_preferred_search_result_type() { return preferred_search_result_type; } - void popup_create(bool p_dont_clear, bool p_replace_mode = false, const String &p_select_type = "Node"); + void popup_create(bool p_dont_clear, bool p_replace_mode = false, const String &p_select_type = "Node", const String &p_select_name = ""); CreateDialog(); }; diff --git a/editor/debugger/editor_debugger_inspector.cpp b/editor/debugger/editor_debugger_inspector.cpp index 58206efc20..6c0ba55ec8 100644 --- a/editor/debugger/editor_debugger_inspector.cpp +++ b/editor/debugger/editor_debugger_inspector.cpp @@ -85,6 +85,7 @@ void EditorDebuggerRemoteObject::_bind_methods() { ClassDB::bind_method(D_METHOD("get_variant"), &EditorDebuggerRemoteObject::get_variant); ClassDB::bind_method(D_METHOD("clear"), &EditorDebuggerRemoteObject::clear); ClassDB::bind_method(D_METHOD("get_remote_object_id"), &EditorDebuggerRemoteObject::get_remote_object_id); + ClassDB::bind_method(D_METHOD("_is_read_only"), &EditorDebuggerRemoteObject::_is_read_only); ADD_SIGNAL(MethodInfo("value_edited", PropertyInfo(Variant::INT, "object_id"), PropertyInfo(Variant::STRING, "property"), PropertyInfo("value"))); } diff --git a/editor/debugger/editor_debugger_inspector.h b/editor/debugger/editor_debugger_inspector.h index 5aac4dbf11..c595e0acaa 100644 --- a/editor/debugger/editor_debugger_inspector.h +++ b/editor/debugger/editor_debugger_inspector.h @@ -50,6 +50,7 @@ public: HashMap<StringName, Variant> prop_values; ObjectID get_remote_object_id() { return remote_object_id; }; + bool _is_read_only() { return true; }; String get_title(); Variant get_variant(const StringName &p_name); diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index 472e53c0e8..9fd7fa578f 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -30,6 +30,7 @@ #include "editor_debugger_node.h" +#include "core/object/undo_redo.h" #include "editor/debugger/editor_debugger_tree.h" #include "editor/debugger/script_editor_debugger.h" #include "editor/editor_log.h" @@ -83,8 +84,6 @@ EditorDebuggerNode::EditorDebuggerNode() { inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2); EditorNode *editor = EditorNode::get_singleton(); - editor->get_undo_redo()->set_method_notify_callback(_method_changeds, this); - editor->get_undo_redo()->set_property_notify_callback(_property_changeds, this); editor->get_pause_button()->connect("pressed", callable_mp(this, &EditorDebuggerNode::_paused)); } @@ -181,6 +180,11 @@ void EditorDebuggerNode::_bind_methods() { ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"))); } +void EditorDebuggerNode::register_undo_redo(UndoRedo *p_undo_redo) { + p_undo_redo->set_method_notify_callback(_method_changeds, this); + p_undo_redo->set_property_notify_callback(_property_changeds, this); +} + EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() { return Object::cast_to<EditorDebuggerRemoteObject>(ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_current())); } diff --git a/editor/debugger/editor_debugger_node.h b/editor/debugger/editor_debugger_node.h index 4c9ad49ac4..e79e60b180 100644 --- a/editor/debugger/editor_debugger_node.h +++ b/editor/debugger/editor_debugger_node.h @@ -41,6 +41,7 @@ class EditorDebuggerRemoteObject; class MenuButton; class ScriptEditorDebugger; class TabContainer; +class UndoRedo; class EditorDebuggerNode : public MarginContainer { GDCLASS(EditorDebuggerNode, MarginContainer); @@ -152,6 +153,7 @@ protected: public: static EditorDebuggerNode *get_singleton() { return singleton; } + void register_undo_redo(UndoRedo *p_undo_redo); ScriptEditorDebugger *get_current_debugger() const; ScriptEditorDebugger *get_default_debugger() const; diff --git a/editor/debugger/editor_network_profiler.cpp b/editor/debugger/editor_network_profiler.cpp index 8c18eba71d..185553cac0 100644 --- a/editor/debugger/editor_network_profiler.cpp +++ b/editor/debugger/editor_network_profiler.cpp @@ -40,7 +40,6 @@ void EditorNetworkProfiler::_bind_methods() { void EditorNetworkProfiler::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); diff --git a/editor/debugger/editor_profiler.cpp b/editor/debugger/editor_profiler.cpp index 28b5d4be15..30197e6d8b 100644 --- a/editor/debugger/editor_profiler.cpp +++ b/editor/debugger/editor_profiler.cpp @@ -394,10 +394,9 @@ void EditorProfiler::_clear_pressed() { void EditorProfiler::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_TRANSLATION_CHANGED: { + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_THEME_CHANGED: { activate->set_icon(get_theme_icon(SNAME("Play"), SNAME("EditorIcons"))); clear_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); } break; @@ -607,7 +606,7 @@ EditorProfiler::EditorProfiler() { display_time = memnew(OptionButton); display_time->add_item(TTR("Inclusive")); display_time->add_item(TTR("Self")); - display_time->set_tooltip(TTR("Inclusive: Includes time from other functions called by this function.\nUse this to spot bottlenecks.\n\nSelf: Only count the time spent in the function itself, not in other functions called by that function.\nUse this to find individual functions to optimize.")); + display_time->set_tooltip_text(TTR("Inclusive: Includes time from other functions called by this function.\nUse this to spot bottlenecks.\n\nSelf: Only count the time spent in the function itself, not in other functions called by that function.\nUse this to find individual functions to optimize.")); display_time->connect("item_selected", callable_mp(this, &EditorProfiler::_combo_changed)); hb->add_child(display_time); diff --git a/editor/debugger/editor_visual_profiler.cpp b/editor/debugger/editor_visual_profiler.cpp index 6f3dd1793c..df1a59cc99 100644 --- a/editor/debugger/editor_visual_profiler.cpp +++ b/editor/debugger/editor_visual_profiler.cpp @@ -424,10 +424,9 @@ void EditorVisualProfiler::_clear_pressed() { void EditorVisualProfiler::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_TRANSLATION_CHANGED: { + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_THEME_CHANGED: { if (is_layout_rtl()) { activate->set_icon(get_theme_icon(SNAME("PlayBackwards"), SNAME("EditorIcons"))); } else { diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index ac2e958c5b..1036561a47 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -369,7 +369,7 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_da } } - vmem_total->set_tooltip(TTR("Bytes:") + " " + itos(total)); + vmem_total->set_tooltip_text(TTR("Bytes:") + " " + itos(total)); vmem_total->set_text(String::humanize_size(total)); } else if (p_msg == "stack_dump") { @@ -776,7 +776,7 @@ void ScriptEditorDebugger::_set_reason_text(const String &p_reason, MessageType reason->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); } reason->set_text(p_reason); - reason->set_tooltip(p_reason.word_wrap(80)); + reason->set_tooltip_text(p_reason.word_wrap(80)); } void ScriptEditorDebugger::_notification(int p_what) { @@ -787,8 +787,8 @@ void ScriptEditorDebugger::_notification(int p_what) { error_tree->connect("item_selected", callable_mp(this, &ScriptEditorDebugger::_error_selected)); error_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_error_activated)); breakpoints_tree->connect("item_activated", callable_mp(this, &ScriptEditorDebugger::_breakpoint_tree_clicked)); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { skip_breakpoints->set_icon(get_theme_icon(skip_breakpoints_value ? SNAME("DebugSkipBreakpointsOn") : SNAME("DebugSkipBreakpointsOff"), SNAME("EditorIcons"))); copy->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); @@ -964,7 +964,7 @@ void ScriptEditorDebugger::stop() { peer->close(); peer.unref(); reason->set_text(""); - reason->set_tooltip(""); + reason->set_tooltip_text(""); } node_path_cache.clear(); @@ -1706,7 +1706,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { skip_breakpoints = memnew(Button); skip_breakpoints->set_flat(true); hbc->add_child(skip_breakpoints); - skip_breakpoints->set_tooltip(TTR("Skip Breakpoints")); + skip_breakpoints->set_tooltip_text(TTR("Skip Breakpoints")); skip_breakpoints->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_skip_breakpoints)); hbc->add_child(memnew(VSeparator)); @@ -1714,7 +1714,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { copy = memnew(Button); copy->set_flat(true); hbc->add_child(copy); - copy->set_tooltip(TTR("Copy Error")); + copy->set_tooltip_text(TTR("Copy Error")); copy->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_copy)); hbc->add_child(memnew(VSeparator)); @@ -1722,14 +1722,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() { step = memnew(Button); step->set_flat(true); hbc->add_child(step); - step->set_tooltip(TTR("Step Into")); + step->set_tooltip_text(TTR("Step Into")); step->set_shortcut(ED_GET_SHORTCUT("debugger/step_into")); step->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_step)); next = memnew(Button); next->set_flat(true); hbc->add_child(next); - next->set_tooltip(TTR("Step Over")); + next->set_tooltip_text(TTR("Step Over")); next->set_shortcut(ED_GET_SHORTCUT("debugger/step_over")); next->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_next)); @@ -1738,14 +1738,14 @@ ScriptEditorDebugger::ScriptEditorDebugger() { dobreak = memnew(Button); dobreak->set_flat(true); hbc->add_child(dobreak); - dobreak->set_tooltip(TTR("Break")); + dobreak->set_tooltip_text(TTR("Break")); dobreak->set_shortcut(ED_GET_SHORTCUT("debugger/break")); dobreak->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_break)); docontinue = memnew(Button); docontinue->set_flat(true); hbc->add_child(docontinue); - docontinue->set_tooltip(TTR("Continue")); + docontinue->set_tooltip_text(TTR("Continue")); docontinue->set_shortcut(ED_GET_SHORTCUT("debugger/continue")); docontinue->connect("pressed", callable_mp(this, &ScriptEditorDebugger::debug_continue)); @@ -1917,7 +1917,7 @@ ScriptEditorDebugger::ScriptEditorDebugger() { vmem_hb->add_child(vmem_refresh); vmem_export = memnew(Button); vmem_export->set_flat(true); - vmem_export->set_tooltip(TTR("Export list to a CSV file")); + vmem_export->set_tooltip_text(TTR("Export list to a CSV file")); vmem_hb->add_child(vmem_export); vmem_vb->add_child(vmem_hb); vmem_refresh->connect("pressed", callable_mp(this, &ScriptEditorDebugger::_video_mem_request)); @@ -1959,15 +1959,18 @@ ScriptEditorDebugger::ScriptEditorDebugger() { info_left->set_columns(2); misc->add_child(info_left); clicked_ctrl = memnew(LineEdit); + clicked_ctrl->set_editable(false); clicked_ctrl->set_h_size_flags(SIZE_EXPAND_FILL); info_left->add_child(memnew(Label(TTR("Clicked Control:")))); info_left->add_child(clicked_ctrl); clicked_ctrl_type = memnew(LineEdit); + clicked_ctrl_type->set_editable(false); info_left->add_child(memnew(Label(TTR("Clicked Control Type:")))); info_left->add_child(clicked_ctrl_type); scene_tree = memnew(SceneDebuggerTree); live_edit_root = memnew(LineEdit); + live_edit_root->set_editable(false); live_edit_root->set_h_size_flags(SIZE_EXPAND_FILL); { diff --git a/editor/dictionary_property_edit.cpp b/editor/dictionary_property_edit.cpp index 630265e268..f16c5402ad 100644 --- a/editor/dictionary_property_edit.cpp +++ b/editor/dictionary_property_edit.cpp @@ -30,6 +30,7 @@ #include "dictionary_property_edit.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" void DictionaryPropertyEdit::_notif_change() { notify_property_list_changed(); @@ -118,7 +119,7 @@ bool DictionaryPropertyEdit::_set(const StringName &p_name, const Variant &p_val int index = pn.substr(0, slash).to_int(); if (type == "key" && index < keys.size()) { const Variant &key = keys[index]; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Dictionary Key")); ur->add_do_method(this, "_set_key", key, p_value); @@ -130,7 +131,7 @@ bool DictionaryPropertyEdit::_set(const StringName &p_name, const Variant &p_val const Variant &key = keys[index]; if (dict.has(key)) { Variant value = dict[key]; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Dictionary Value")); ur->add_do_method(this, "_set_value", key, p_value); diff --git a/editor/editor_about.cpp b/editor/editor_about.cpp index 17aa67cd45..cdf300e288 100644 --- a/editor/editor_about.cpp +++ b/editor/editor_about.cpp @@ -38,22 +38,18 @@ // The metadata key used to store and retrieve the version text to copy to the clipboard. static const String META_TEXT_TO_COPY = "text_to_copy"; -void EditorAbout::_theme_changed() { - const Ref<Font> font = get_theme_font(SNAME("source"), SNAME("EditorFonts")); - const int font_size = get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts")); - _tpl_text->add_theme_font_override("normal_font", font); - _tpl_text->add_theme_font_size_override("normal_font_size", font_size); - _tpl_text->add_theme_constant_override("line_separation", 4 * EDSCALE); - _license_text->add_theme_font_override("normal_font", font); - _license_text->add_theme_font_size_override("normal_font_size", font_size); - _license_text->add_theme_constant_override("line_separation", 4 * EDSCALE); - _logo->set_texture(get_theme_icon(SNAME("Logo"), SNAME("EditorIcons"))); -} - void EditorAbout::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - _theme_changed(); + case NOTIFICATION_THEME_CHANGED: { + const Ref<Font> font = get_theme_font(SNAME("source"), SNAME("EditorFonts")); + const int font_size = get_theme_font_size(SNAME("source_size"), SNAME("EditorFonts")); + _tpl_text->add_theme_font_override("normal_font", font); + _tpl_text->add_theme_font_size_override("normal_font_size", font_size); + _tpl_text->add_theme_constant_override("line_separation", 4 * EDSCALE); + _license_text->add_theme_font_override("normal_font", font); + _license_text->add_theme_font_size_override("normal_font_size", font_size); + _license_text->add_theme_constant_override("line_separation", 4 * EDSCALE); + _logo->set_texture(get_theme_icon(SNAME("Logo"), SNAME("EditorIcons"))); } break; } } @@ -120,7 +116,6 @@ EditorAbout::EditorAbout() { set_hide_on_ok(true); VBoxContainer *vbc = memnew(VBoxContainer); - vbc->connect("theme_changed", callable_mp(this, &EditorAbout::_theme_changed)); HBoxContainer *hbc = memnew(HBoxContainer); hbc->set_h_size_flags(Control::SIZE_EXPAND_FILL); hbc->set_alignment(BoxContainer::ALIGNMENT_CENTER); @@ -147,7 +142,7 @@ EditorAbout::EditorAbout() { // Set the text to copy in metadata as it slightly differs from the button's text. version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash); version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); - version_btn->set_tooltip(TTR("Click to copy.")); + version_btn->set_tooltip_text(TTR("Click to copy.")); version_btn->connect("pressed", callable_mp(this, &EditorAbout::_version_button_pressed)); version_info_vbc->add_child(version_btn); diff --git a/editor/editor_about.h b/editor/editor_about.h index 971843f6d2..b152f1b4d3 100644 --- a/editor/editor_about.h +++ b/editor/editor_about.h @@ -62,8 +62,6 @@ private: RichTextLabel *_tpl_text = nullptr; TextureRect *_logo = nullptr; - void _theme_changed(); - protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index b6d7bbc45f..cc65cddfb2 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -38,6 +38,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "filesystem_dock.h" #include "scene/resources/font.h" #include "servers/audio_server.h" @@ -65,7 +66,6 @@ void EditorAudioBus::_update_visible_channels() { void EditorAudioBus::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { for (int i = 0; i < CHANNELS_MAX; i++) { channel[i].vu_l->set_under_texture(get_theme_icon(SNAME("BusVuEmpty"), SNAME("EditorIcons"))); @@ -280,7 +280,7 @@ void EditorAudioBus::_name_changed(const String &p_new_name) { } updating_bus = true; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); StringName current = AudioServer::get_singleton()->get_bus_name(get_index()); ur->create_action(TTR("Rename Audio Bus")); @@ -321,7 +321,7 @@ void EditorAudioBus::_volume_changed(float p_normalized) { slider->set_value(_scaled_db_to_normalized_volume(Math::round(p_db))); } - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Audio Bus Volume"), UndoRedo::MERGE_ENDS); ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), p_db); ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", get_index(), AudioServer::get_singleton()->get_bus_volume_db(get_index())); @@ -394,7 +394,7 @@ void EditorAudioBus::_show_value(float slider_value) { // Also set the preview text as a standard Control tooltip. // This way, it can be seen when the slider is merely hovered (instead of dragged). - slider->set_tooltip(text); + slider->set_tooltip_text(text); audio_value_preview_label->set_text(text); const Vector2 slider_size = slider->get_size(); const Vector2 slider_position = slider->get_global_position(); @@ -415,7 +415,7 @@ void EditorAudioBus::_hide_value_preview() { void EditorAudioBus::_solo_toggled() { updating_bus = true; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Toggle Audio Bus Solo")); ur->add_do_method(AudioServer::get_singleton(), "set_bus_solo", get_index(), solo->is_pressed()); ur->add_undo_method(AudioServer::get_singleton(), "set_bus_solo", get_index(), AudioServer::get_singleton()->is_bus_solo(get_index())); @@ -429,7 +429,7 @@ void EditorAudioBus::_solo_toggled() { void EditorAudioBus::_mute_toggled() { updating_bus = true; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Toggle Audio Bus Mute")); ur->add_do_method(AudioServer::get_singleton(), "set_bus_mute", get_index(), mute->is_pressed()); ur->add_undo_method(AudioServer::get_singleton(), "set_bus_mute", get_index(), AudioServer::get_singleton()->is_bus_mute(get_index())); @@ -443,7 +443,7 @@ void EditorAudioBus::_mute_toggled() { void EditorAudioBus::_bypass_toggled() { updating_bus = true; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Toggle Audio Bus Bypass Effects")); ur->add_do_method(AudioServer::get_singleton(), "set_bus_bypass_effects", get_index(), bypass->is_pressed()); ur->add_undo_method(AudioServer::get_singleton(), "set_bus_bypass_effects", get_index(), AudioServer::get_singleton()->is_bus_bypassing_effects(get_index())); @@ -457,7 +457,7 @@ void EditorAudioBus::_bypass_toggled() { void EditorAudioBus::_send_selected(int p_which) { updating_bus = true; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Select Audio Bus Send")); ur->add_do_method(AudioServer::get_singleton(), "set_bus_send", get_index(), send->get_item_text(p_which)); ur->add_undo_method(AudioServer::get_singleton(), "set_bus_send", get_index(), AudioServer::get_singleton()->get_bus_send(get_index())); @@ -507,7 +507,7 @@ void EditorAudioBus::_effect_edited() { int index = effect->get_metadata(0); updating_bus = true; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Select Audio Bus Send")); ur->add_do_method(AudioServer::get_singleton(), "set_bus_effect_enabled", get_index(), index, effect->is_checked(0)); ur->add_undo_method(AudioServer::get_singleton(), "set_bus_effect_enabled", get_index(), index, AudioServer::get_singleton()->is_bus_effect_enabled(get_index(), index)); @@ -534,7 +534,7 @@ void EditorAudioBus::_effect_add(int p_which) { afxr->set_name(effect_options->get_item_text(p_which)); - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Add Audio Bus Effect")); ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), afxr, -1); ur->add_undo_method(AudioServer::get_singleton(), "remove_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect_count(get_index())); @@ -688,7 +688,7 @@ void EditorAudioBus::drop_data_fw(const Point2 &p_point, const Variant &p_data, bool enabled = AudioServer::get_singleton()->is_bus_effect_enabled(bus, effect); - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Move Bus Effect")); ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect", bus, effect); ur->add_do_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect(bus, effect), paste_at); @@ -730,7 +730,7 @@ void EditorAudioBus::_delete_effect_pressed(int p_option) { int index = item->get_metadata(0); - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Delete Bus Effect")); ur->add_do_method(AudioServer::get_singleton(), "remove_bus_effect", get_index(), index); ur->add_undo_method(AudioServer::get_singleton(), "add_bus_effect", get_index(), AudioServer::get_singleton()->get_bus_effect(get_index(), index), index); @@ -778,7 +778,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { buses = p_buses; is_master = p_is_master; - set_tooltip(TTR("Drag & drop to rearrange.")); + set_tooltip_text(TTR("Drag & drop to rearrange.")); VBoxContainer *vb = memnew(VBoxContainer); add_child(vb); @@ -795,21 +795,21 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { solo = memnew(Button); solo->set_flat(true); solo->set_toggle_mode(true); - solo->set_tooltip(TTR("Solo")); + solo->set_tooltip_text(TTR("Solo")); solo->set_focus_mode(FOCUS_NONE); solo->connect("pressed", callable_mp(this, &EditorAudioBus::_solo_toggled)); hbc->add_child(solo); mute = memnew(Button); mute->set_flat(true); mute->set_toggle_mode(true); - mute->set_tooltip(TTR("Mute")); + mute->set_tooltip_text(TTR("Mute")); mute->set_focus_mode(FOCUS_NONE); mute->connect("pressed", callable_mp(this, &EditorAudioBus::_mute_toggled)); hbc->add_child(mute); bypass = memnew(Button); bypass->set_flat(true); bypass->set_toggle_mode(true); - bypass->set_tooltip(TTR("Bypass")); + bypass->set_tooltip_text(TTR("Bypass")); bypass->set_focus_mode(FOCUS_NONE); bypass->connect("pressed", callable_mp(this, &EditorAudioBus::_bypass_toggled)); hbc->add_child(bypass); @@ -935,7 +935,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) { bus_options->set_shortcut_context(this); bus_options->set_h_size_flags(SIZE_SHRINK_END); bus_options->set_anchor(SIDE_RIGHT, 0.0); - bus_options->set_tooltip(TTR("Bus Options")); + bus_options->set_tooltip_text(TTR("Bus Options")); hbc->add_child(bus_options); bus_popup = bus_options->get_popup(); @@ -1024,7 +1024,6 @@ EditorAudioBuses *EditorAudioBuses::register_editor() { void EditorAudioBuses::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { bus_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); } break; @@ -1063,7 +1062,7 @@ void EditorAudioBuses::_notification(int p_what) { } void EditorAudioBuses::_add_bus() { - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Add Audio Bus")); ur->add_do_method(AudioServer::get_singleton(), "set_bus_count", AudioServer::get_singleton()->get_bus_count() + 1); @@ -1095,7 +1094,7 @@ void EditorAudioBuses::_delete_bus(Object *p_which) { return; } - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Delete Audio Bus")); ur->add_do_method(AudioServer::get_singleton(), "remove_bus", index); @@ -1117,7 +1116,7 @@ void EditorAudioBuses::_delete_bus(Object *p_which) { void EditorAudioBuses::_duplicate_bus(int p_which) { int add_at_pos = p_which + 1; - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Duplicate Audio Bus")); ur->add_do_method(AudioServer::get_singleton(), "add_bus", add_at_pos); ur->add_do_method(AudioServer::get_singleton(), "set_bus_name", add_at_pos, AudioServer::get_singleton()->get_bus_name(p_which) + " Copy"); @@ -1140,7 +1139,7 @@ void EditorAudioBuses::_reset_bus_volume(Object *p_which) { EditorAudioBus *bus = Object::cast_to<EditorAudioBus>(p_which); int index = bus->get_index(); - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Reset Bus Volume")); ur->add_do_method(AudioServer::get_singleton(), "set_bus_volume_db", index, 0.f); ur->add_undo_method(AudioServer::get_singleton(), "set_bus_volume_db", index, AudioServer::get_singleton()->get_bus_volume_db(index)); @@ -1160,7 +1159,7 @@ void EditorAudioBuses::_request_drop_end() { } void EditorAudioBuses::_drop_at_index(int p_bus, int p_index) { - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Move Audio Bus")); ur->add_do_method(AudioServer::get_singleton(), "move_bus", p_bus, p_index); @@ -1219,7 +1218,7 @@ void EditorAudioBuses::_load_default_layout() { file->set_text(String(TTR("Layout:")) + " " + layout_path.get_file()); AudioServer::get_singleton()->set_bus_layout(state); _update_buses(); - EditorNode::get_singleton()->get_undo_redo()->clear_history(); + EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY); call_deferred(SNAME("_select_layout")); } @@ -1235,7 +1234,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) { file->set_text(String(TTR("Layout:")) + " " + p_string.get_file()); AudioServer::get_singleton()->set_bus_layout(state); _update_buses(); - EditorNode::get_singleton()->get_undo_redo()->clear_history(); + EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY); call_deferred(SNAME("_select_layout")); } else if (file_dialog->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) { @@ -1255,7 +1254,7 @@ void EditorAudioBuses::_file_dialog_callback(const String &p_string) { edited_path = p_string; file->set_text(String(TTR("Layout:")) + " " + p_string.get_file()); _update_buses(); - EditorNode::get_singleton()->get_undo_redo()->clear_history(); + EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY); call_deferred(SNAME("_select_layout")); } } @@ -1281,7 +1280,7 @@ EditorAudioBuses::EditorAudioBuses() { add = memnew(Button); top_hb->add_child(add); add->set_text(TTR("Add Bus")); - add->set_tooltip(TTR("Add a new Audio Bus to this layout.")); + add->set_tooltip_text(TTR("Add a new Audio Bus to this layout.")); add->connect("pressed", callable_mp(this, &EditorAudioBuses::_add_bus)); VSeparator *separator = memnew(VSeparator); @@ -1289,25 +1288,25 @@ EditorAudioBuses::EditorAudioBuses() { load = memnew(Button); load->set_text(TTR("Load")); - load->set_tooltip(TTR("Load an existing Bus Layout.")); + load->set_tooltip_text(TTR("Load an existing Bus Layout.")); top_hb->add_child(load); load->connect("pressed", callable_mp(this, &EditorAudioBuses::_load_layout)); save_as = memnew(Button); save_as->set_text(TTR("Save As")); - save_as->set_tooltip(TTR("Save this Bus Layout to a file.")); + save_as->set_tooltip_text(TTR("Save this Bus Layout to a file.")); top_hb->add_child(save_as); save_as->connect("pressed", callable_mp(this, &EditorAudioBuses::_save_as_layout)); _default = memnew(Button); _default->set_text(TTR("Load Default")); - _default->set_tooltip(TTR("Load the default Bus Layout.")); + _default->set_tooltip_text(TTR("Load the default Bus Layout.")); top_hb->add_child(_default); _default->connect("pressed", callable_mp(this, &EditorAudioBuses::_load_default_layout)); _new = memnew(Button); _new->set_text(TTR("Create")); - _new->set_tooltip(TTR("Create a new Bus Layout.")); + _new->set_tooltip_text(TTR("Create a new Bus Layout.")); top_hb->add_child(_new); _new->connect("pressed", callable_mp(this, &EditorAudioBuses::_new_layout)); @@ -1354,7 +1353,7 @@ void EditorAudioBuses::open_layout(const String &p_path) { file->set_text(p_path.get_file()); AudioServer::get_singleton()->set_bus_layout(state); _update_buses(); - EditorNode::get_singleton()->get_undo_redo()->clear_history(); + EditorNode::get_undo_redo()->clear_history(true, EditorUndoRedoManager::GLOBAL_HISTORY); call_deferred(SNAME("_select_layout")); } diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp index ee4955d0a0..7dff0d54f7 100644 --- a/editor/editor_autoload_settings.cpp +++ b/editor/editor_autoload_settings.cpp @@ -35,6 +35,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/filesystem_dock.h" #include "project_settings_editor.h" #include "scene/main/window.h" @@ -58,7 +59,6 @@ void EditorAutoloadSettings::_notification(int p_what) { get_tree()->get_root()->call_deferred(SNAME("add_child"), info.node); } } - browse_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); } break; case NOTIFICATION_THEME_CHANGED: { @@ -193,7 +193,7 @@ void EditorAutoloadSettings::_autoload_edited() { TreeItem *ti = tree->get_edited(); int column = tree->get_edited_column(); - UndoRedo *undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); if (column == 0) { String name = ti->get_text(0); @@ -288,7 +288,7 @@ void EditorAutoloadSettings::_autoload_button_pressed(Object *p_item, int p_colu String name = "autoload/" + ti->get_text(0); - UndoRedo *undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); switch (p_button) { case BUTTON_OPEN: { @@ -713,7 +713,7 @@ void EditorAutoloadSettings::drop_data_fw(const Point2 &p_point, const Variant & orders.sort(); - UndoRedo *undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Rearrange Autoloads")); @@ -757,7 +757,7 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_ name = "autoload/" + name; - UndoRedo *undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Add Autoload")); // Singleton autoloads are represented with a leading "*" in their path. @@ -783,7 +783,7 @@ bool EditorAutoloadSettings::autoload_add(const String &p_name, const String &p_ void EditorAutoloadSettings::autoload_remove(const String &p_name) { String name = "autoload/" + p_name; - UndoRedo *undo_redo = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); int order = ProjectSettings::get_singleton()->get_order(name); diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp index 2d4945db14..231ae198d2 100644 --- a/editor/editor_data.cpp +++ b/editor/editor_data.cpp @@ -35,6 +35,7 @@ #include "core/io/resource_loader.h" #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/script_editor_plugin.h" #include "scene/resources/packed_scene.h" @@ -364,13 +365,13 @@ void EditorData::restore_editor_global_states() { void EditorData::paste_object_params(Object *p_object) { ERR_FAIL_NULL(p_object); - undo_redo.create_action(TTR("Paste Params")); + undo_redo_manager->create_action(TTR("Paste Params")); for (const PropertyData &E : clipboard) { String name = E.name; - undo_redo.add_do_property(p_object, name, E.value); - undo_redo.add_undo_property(p_object, name, p_object->get(name)); + undo_redo_manager->add_do_property(p_object, name, E.value); + undo_redo_manager->add_undo_property(p_object, name, p_object->get(name)); } - undo_redo.commit_action(); + undo_redo_manager->commit_action(); } bool EditorData::call_build() { @@ -383,8 +384,49 @@ bool EditorData::call_build() { return result; } -UndoRedo &EditorData::get_undo_redo() { - return undo_redo; +void EditorData::set_scene_as_saved(int p_idx) { + if (p_idx == -1) { + p_idx = current_edited_scene; + } + ERR_FAIL_INDEX(p_idx, edited_scene.size()); + + get_undo_redo()->set_history_as_saved(edited_scene[p_idx].history_id); +} + +bool EditorData::is_scene_changed(int p_idx) { + if (p_idx == -1) { + p_idx = current_edited_scene; + } + ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), false); + + uint64_t current_scene_version = get_undo_redo()->get_or_create_history(edited_scene[p_idx].history_id).undo_redo->get_version(); + bool is_changed = edited_scene[p_idx].last_checked_version != current_scene_version; + edited_scene.write[p_idx].last_checked_version = current_scene_version; + return is_changed; +} + +int EditorData::get_scene_history_id_from_path(const String &p_path) const { + for (const EditedScene &E : edited_scene) { + if (E.path == p_path) { + return E.history_id; + } + } + return 0; +} + +int EditorData::get_current_edited_scene_history_id() const { + if (current_edited_scene != -1) { + return edited_scene[current_edited_scene].history_id; + } + return 0; +} + +int EditorData::get_scene_history_id(int p_idx) const { + return edited_scene[p_idx].history_id; +} + +Ref<EditorUndoRedoManager> &EditorData::get_undo_redo() { + return undo_redo_manager; } void EditorData::add_undo_redo_inspector_hook_callback(Callable p_callable) { @@ -415,12 +457,12 @@ Callable EditorData::get_move_array_element_function(const StringName &p_class) } void EditorData::remove_editor_plugin(EditorPlugin *p_plugin) { - p_plugin->undo_redo = nullptr; + p_plugin->undo_redo = Ref<EditorUndoRedoManager>(); editor_plugins.erase(p_plugin); } void EditorData::add_editor_plugin(EditorPlugin *p_plugin) { - p_plugin->undo_redo = &undo_redo; + p_plugin->undo_redo = undo_redo_manager; editor_plugins.push_back(p_plugin); } @@ -505,8 +547,8 @@ int EditorData::add_edited_scene(int p_at_pos) { es.path = String(); es.file_modified_time = 0; es.history_current = -1; - es.version = 0; es.live_edit_root = NodePath(String("/root")); + es.history_id = last_created_scene++; if (p_at_pos == edited_scene.size()) { edited_scene.push_back(es); @@ -547,6 +589,7 @@ void EditorData::remove_scene(int p_idx) { ScriptEditor::get_singleton()->close_builtin_scripts_from_scene(edited_scene[p_idx].path); } + undo_redo_manager->discard_history(edited_scene[p_idx].history_id); edited_scene.remove_at(p_idx); } @@ -679,26 +722,10 @@ Vector<EditorData::EditedScene> EditorData::get_edited_scenes() const { return out_edited_scenes_list; } -void EditorData::set_edited_scene_version(uint64_t version, int p_scene_idx) { - ERR_FAIL_INDEX(current_edited_scene, edited_scene.size()); - if (p_scene_idx < 0) { - edited_scene.write[current_edited_scene].version = version; - } else { - ERR_FAIL_INDEX(p_scene_idx, edited_scene.size()); - edited_scene.write[p_scene_idx].version = version; - } -} - -uint64_t EditorData::get_scene_version(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, edited_scene.size(), 0); - return edited_scene[p_idx].version; -} - void EditorData::set_scene_modified_time(int p_idx, uint64_t p_time) { if (p_idx == -1) { p_idx = current_edited_scene; } - ERR_FAIL_INDEX(p_idx, edited_scene.size()); edited_scene.write[p_idx].file_modified_time = p_time; @@ -991,6 +1018,7 @@ void EditorData::script_class_load_icon_paths() { EditorData::EditorData() { current_edited_scene = -1; + undo_redo_manager.instantiate(); script_class_load_icon_paths(); } @@ -1115,8 +1143,8 @@ void EditorSelection::_emit_change() { emitted = false; } -Array EditorSelection::_get_transformable_selected_nodes() { - Array ret; +TypedArray<Node> EditorSelection::_get_transformable_selected_nodes() { + TypedArray<Node> ret; for (const Node *E : selected_node_list) { ret.push_back(E); diff --git a/editor/editor_data.h b/editor/editor_data.h index 351c63f4b9..1da188c546 100644 --- a/editor/editor_data.h +++ b/editor/editor_data.h @@ -31,12 +31,12 @@ #ifndef EDITOR_DATA_H #define EDITOR_DATA_H -#include "core/object/undo_redo.h" #include "core/templates/list.h" #include "scene/resources/texture.h" class ConfigFile; class EditorPlugin; +class EditorUndoRedoManager; /** * Stores the history of objects which have been selected for editing in the Editor & the Inspector. @@ -118,8 +118,9 @@ public: Vector<EditorSelectionHistory::HistoryElement> history_stored; int history_current = 0; Dictionary custom_state; - uint64_t version = 0; NodePath live_edit_root; + int history_id = 0; + uint64_t last_checked_version = 0; }; private: @@ -132,12 +133,13 @@ private: HashMap<String, Vector<CustomType>> custom_types; List<PropertyData> clipboard; - UndoRedo undo_redo; + Ref<EditorUndoRedoManager> undo_redo_manager; Vector<Callable> undo_redo_callbacks; HashMap<StringName, Callable> move_element_functions; Vector<EditedScene> edited_scene; - int current_edited_scene; + int current_edited_scene = -1; + int last_created_scene = 1; bool _find_updated_instances(Node *p_root, Node *p_node, HashSet<String> &checked_paths); @@ -166,7 +168,7 @@ public: int get_editor_plugin_count() const; EditorPlugin *get_editor_plugin(int p_idx); - UndoRedo &get_undo_redo(); + Ref<EditorUndoRedoManager> &get_undo_redo(); void add_undo_redo_inspector_hook_callback(Callable p_callable); // Callbacks should have this signature: void (Object* undo_redo, Object *modified_object, String property, Variant new_value) void remove_undo_redo_inspector_hook_callback(Callable p_callable); const Vector<Callable> get_undo_redo_inspector_hook_callback(); @@ -200,7 +202,6 @@ public: void set_scene_path(int p_idx, const String &p_path); Ref<Script> get_scene_root_script(int p_idx) const; void set_edited_scene_version(uint64_t version, int p_scene_idx = -1); - uint64_t get_scene_version(int p_idx) const; void set_scene_modified_time(int p_idx, uint64_t p_time); uint64_t get_scene_modified_time(int p_idx) const; void clear_edited_scenes(); @@ -210,6 +211,13 @@ public: void move_edited_scene_to_index(int p_idx); bool call_build(); + void set_scene_as_saved(int p_idx); + bool is_scene_changed(int p_idx); + + int get_scene_history_id_from_path(const String &p_path) const; + int get_current_edited_scene_history_id() const; + int get_scene_history_id(int p_idx) const; + void set_plugin_window_layout(Ref<ConfigFile> p_layout); void get_plugin_window_layout(Ref<ConfigFile> p_layout); @@ -263,7 +271,7 @@ class EditorSelection : public Object { List<Node *> selected_node_list; void _update_node_list(); - Array _get_transformable_selected_nodes(); + TypedArray<Node> _get_transformable_selected_nodes(); void _emit_change(); protected: diff --git a/editor/editor_file_dialog.cpp b/editor/editor_file_dialog.cpp index 2f1134e8ef..cc0f60fee5 100644 --- a/editor/editor_file_dialog.cpp +++ b/editor/editor_file_dialog.cpp @@ -70,7 +70,6 @@ VBoxContainer *EditorFileDialog::get_vbox() { void EditorFileDialog::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: case NOTIFICATION_THEME_CHANGED: case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { @@ -1625,13 +1624,13 @@ EditorFileDialog::EditorFileDialog() { dir_prev = memnew(Button); dir_prev->set_flat(true); - dir_prev->set_tooltip(TTR("Go to previous folder.")); + dir_prev->set_tooltip_text(TTR("Go to previous folder.")); dir_next = memnew(Button); dir_next->set_flat(true); - dir_next->set_tooltip(TTR("Go to next folder.")); + dir_next->set_tooltip_text(TTR("Go to next folder.")); dir_up = memnew(Button); dir_up->set_flat(true); - dir_up->set_tooltip(TTR("Go to parent folder.")); + dir_up->set_tooltip_text(TTR("Go to parent folder.")); pathhb->add_child(dir_prev); pathhb->add_child(dir_next); @@ -1655,14 +1654,14 @@ EditorFileDialog::EditorFileDialog() { refresh = memnew(Button); refresh->set_flat(true); - refresh->set_tooltip(TTR("Refresh files.")); + refresh->set_tooltip_text(TTR("Refresh files.")); refresh->connect("pressed", callable_mp(this, &EditorFileDialog::update_file_list)); pathhb->add_child(refresh); favorite = memnew(Button); favorite->set_flat(true); favorite->set_toggle_mode(true); - favorite->set_tooltip(TTR("(Un)favorite current folder.")); + favorite->set_tooltip_text(TTR("(Un)favorite current folder.")); favorite->connect("pressed", callable_mp(this, &EditorFileDialog::_favorite_pressed)); pathhb->add_child(favorite); @@ -1670,7 +1669,7 @@ EditorFileDialog::EditorFileDialog() { show_hidden->set_flat(true); show_hidden->set_toggle_mode(true); show_hidden->set_pressed(is_showing_hidden_files()); - show_hidden->set_tooltip(TTR("Toggle the visibility of hidden files.")); + show_hidden->set_tooltip_text(TTR("Toggle the visibility of hidden files.")); show_hidden->connect("toggled", callable_mp(this, &EditorFileDialog::set_show_hidden_files)); pathhb->add_child(show_hidden); @@ -1685,7 +1684,7 @@ EditorFileDialog::EditorFileDialog() { mode_thumbnails->set_toggle_mode(true); mode_thumbnails->set_pressed(display_mode == DISPLAY_THUMBNAILS); mode_thumbnails->set_button_group(view_mode_group); - mode_thumbnails->set_tooltip(TTR("View items as a grid of thumbnails.")); + mode_thumbnails->set_tooltip_text(TTR("View items as a grid of thumbnails.")); pathhb->add_child(mode_thumbnails); mode_list = memnew(Button); @@ -1694,7 +1693,7 @@ EditorFileDialog::EditorFileDialog() { mode_list->set_toggle_mode(true); mode_list->set_pressed(display_mode == DISPLAY_LIST); mode_list->set_button_group(view_mode_group); - mode_list->set_tooltip(TTR("View items as a list.")); + mode_list->set_tooltip_text(TTR("View items as a list.")); pathhb->add_child(mode_list); shortcuts_container = memnew(HBoxContainer); diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp index 1a105c7fe8..bda2e283ef 100644 --- a/editor/editor_file_system.cpp +++ b/editor/editor_file_system.cpp @@ -1192,11 +1192,6 @@ void EditorFileSystem::scan_changes() { void EditorFileSystem::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - call_deferred(SNAME("scan")); //this should happen after every editor node entered the tree - - } break; - case NOTIFICATION_EXIT_TREE: { Thread &active_thread = thread.is_started() ? thread : thread_sources; if (use_threads && active_thread.is_started()) { diff --git a/editor/editor_fonts.cpp b/editor/editor_fonts.cpp index a02051c8ee..fffe77f1c4 100644 --- a/editor/editor_fonts.cpp +++ b/editor/editor_fonts.cpp @@ -37,7 +37,7 @@ #include "scene/resources/default_theme/default_theme.h" #include "scene/resources/font.h" -Ref<FontFile> load_external_font(const String &p_path, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning, bool p_msdf = false, TypedArray<Font> *r_fallbacks = nullptr) { +Ref<FontFile> load_external_font(const String &p_path, TextServer::Hinting p_hinting, TextServer::FontAntialiasing p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning, bool p_msdf = false, TypedArray<Font> *r_fallbacks = nullptr) { Ref<FontFile> font; font.instantiate(); @@ -45,7 +45,7 @@ Ref<FontFile> load_external_font(const String &p_path, TextServer::Hinting p_hin font->set_data(data); font->set_multichannel_signed_distance_field(p_msdf); - font->set_antialiased(p_aa); + font->set_antialiasing(p_aa); font->set_hinting(p_hinting); font->set_force_autohinter(p_autohint); font->set_subpixel_positioning(p_font_subpixel_positioning); @@ -57,13 +57,13 @@ Ref<FontFile> load_external_font(const String &p_path, TextServer::Hinting p_hin return font; } -Ref<FontFile> load_internal_font(const uint8_t *p_data, size_t p_size, TextServer::Hinting p_hinting, bool p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning, bool p_msdf = false, TypedArray<Font> *r_fallbacks = nullptr) { +Ref<FontFile> load_internal_font(const uint8_t *p_data, size_t p_size, TextServer::Hinting p_hinting, TextServer::FontAntialiasing p_aa, bool p_autohint, TextServer::SubpixelPositioning p_font_subpixel_positioning, bool p_msdf = false, TypedArray<Font> *r_fallbacks = nullptr) { Ref<FontFile> font; font.instantiate(); font->set_data_ptr(p_data, p_size); font->set_multichannel_signed_distance_field(p_msdf); - font->set_antialiased(p_aa); + font->set_antialiasing(p_aa); font->set_hinting(p_hinting); font->set_force_autohinter(p_autohint); font->set_subpixel_positioning(p_font_subpixel_positioning); @@ -91,7 +91,7 @@ Ref<FontVariation> make_bold_font(const Ref<Font> &p_font, double p_embolden, Ty void editor_register_fonts(Ref<Theme> p_theme) { Ref<DirAccess> dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); - bool font_antialiased = (bool)EditorSettings::get_singleton()->get("interface/editor/font_antialiased"); + TextServer::FontAntialiasing font_antialiasing = (TextServer::FontAntialiasing)(int)EditorSettings::get_singleton()->get("interface/editor/font_antialiasing"); int font_hinting_setting = (int)EditorSettings::get_singleton()->get("interface/editor/font_hinting"); TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)EditorSettings::get_singleton()->get("interface/editor/font_subpixel_positioning"); @@ -123,47 +123,47 @@ void editor_register_fonts(Ref<Theme> p_theme) { const int default_font_size = int(EDITOR_GET("interface/editor/main_font_size")) * EDSCALE; const float embolden_strength = 0.6; - Ref<Font> default_font = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false); - Ref<Font> default_font_msdf = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, true); + Ref<Font> default_font = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false); + Ref<Font> default_font_msdf = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, true); TypedArray<Font> fallbacks; - Ref<FontFile> arabic_font = load_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> bengali_font = load_internal_font(_font_NotoSansBengaliUI_Regular, _font_NotoSansBengaliUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> devanagari_font = load_internal_font(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> georgian_font = load_internal_font(_font_NotoSansGeorgian_Regular, _font_NotoSansGeorgian_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> hebrew_font = load_internal_font(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> malayalam_font = load_internal_font(_font_NotoSansMalayalamUI_Regular, _font_NotoSansMalayalamUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> oriya_font = load_internal_font(_font_NotoSansOriyaUI_Regular, _font_NotoSansOriyaUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> sinhala_font = load_internal_font(_font_NotoSansSinhalaUI_Regular, _font_NotoSansSinhalaUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> tamil_font = load_internal_font(_font_NotoSansTamilUI_Regular, _font_NotoSansTamilUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> telugu_font = load_internal_font(_font_NotoSansTeluguUI_Regular, _font_NotoSansTeluguUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> thai_font = load_internal_font(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> fallback_font = load_internal_font(_font_DroidSansFallback, _font_DroidSansFallback_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); - Ref<FontFile> japanese_font = load_internal_font(_font_DroidSansJapanese, _font_DroidSansJapanese_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> arabic_font = load_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> bengali_font = load_internal_font(_font_NotoSansBengaliUI_Regular, _font_NotoSansBengaliUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> devanagari_font = load_internal_font(_font_NotoSansDevanagariUI_Regular, _font_NotoSansDevanagariUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> georgian_font = load_internal_font(_font_NotoSansGeorgian_Regular, _font_NotoSansGeorgian_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> hebrew_font = load_internal_font(_font_NotoSansHebrew_Regular, _font_NotoSansHebrew_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> malayalam_font = load_internal_font(_font_NotoSansMalayalamUI_Regular, _font_NotoSansMalayalamUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> oriya_font = load_internal_font(_font_NotoSansOriyaUI_Regular, _font_NotoSansOriyaUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> sinhala_font = load_internal_font(_font_NotoSansSinhalaUI_Regular, _font_NotoSansSinhalaUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> tamil_font = load_internal_font(_font_NotoSansTamilUI_Regular, _font_NotoSansTamilUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> telugu_font = load_internal_font(_font_NotoSansTeluguUI_Regular, _font_NotoSansTeluguUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> thai_font = load_internal_font(_font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> fallback_font = load_internal_font(_font_DroidSansFallback, _font_DroidSansFallback_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); + Ref<FontFile> japanese_font = load_internal_font(_font_DroidSansJapanese, _font_DroidSansJapanese_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks); default_font->set_fallbacks(fallbacks); default_font_msdf->set_fallbacks(fallbacks); - Ref<FontFile> default_font_bold = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false); - Ref<FontFile> default_font_bold_msdf = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, true); + Ref<FontFile> default_font_bold = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false); + Ref<FontFile> default_font_bold_msdf = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, true); TypedArray<Font> fallbacks_bold; - Ref<FontFile> arabic_font_bold = load_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> bengali_font_bold = load_internal_font(_font_NotoSansBengaliUI_Bold, _font_NotoSansBengaliUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> devanagari_font_bold = load_internal_font(_font_NotoSansDevanagariUI_Bold, _font_NotoSansDevanagariUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> georgian_font_bold = load_internal_font(_font_NotoSansGeorgian_Bold, _font_NotoSansGeorgian_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> hebrew_font_bold = load_internal_font(_font_NotoSansHebrew_Bold, _font_NotoSansHebrew_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> malayalam_font_bold = load_internal_font(_font_NotoSansMalayalamUI_Bold, _font_NotoSansMalayalamUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> oriya_font_bold = load_internal_font(_font_NotoSansOriyaUI_Bold, _font_NotoSansOriyaUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> sinhala_font_bold = load_internal_font(_font_NotoSansSinhalaUI_Bold, _font_NotoSansSinhalaUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> tamil_font_bold = load_internal_font(_font_NotoSansTamilUI_Bold, _font_NotoSansTamilUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> telugu_font_bold = load_internal_font(_font_NotoSansTeluguUI_Bold, _font_NotoSansTeluguUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); - Ref<FontFile> thai_font_bold = load_internal_font(_font_NotoSansThaiUI_Bold, _font_NotoSansThaiUI_Bold_size, font_hinting, font_antialiased, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> arabic_font_bold = load_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> bengali_font_bold = load_internal_font(_font_NotoSansBengaliUI_Bold, _font_NotoSansBengaliUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> devanagari_font_bold = load_internal_font(_font_NotoSansDevanagariUI_Bold, _font_NotoSansDevanagariUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> georgian_font_bold = load_internal_font(_font_NotoSansGeorgian_Bold, _font_NotoSansGeorgian_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> hebrew_font_bold = load_internal_font(_font_NotoSansHebrew_Bold, _font_NotoSansHebrew_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> malayalam_font_bold = load_internal_font(_font_NotoSansMalayalamUI_Bold, _font_NotoSansMalayalamUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> oriya_font_bold = load_internal_font(_font_NotoSansOriyaUI_Bold, _font_NotoSansOriyaUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> sinhala_font_bold = load_internal_font(_font_NotoSansSinhalaUI_Bold, _font_NotoSansSinhalaUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> tamil_font_bold = load_internal_font(_font_NotoSansTamilUI_Bold, _font_NotoSansTamilUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> telugu_font_bold = load_internal_font(_font_NotoSansTeluguUI_Bold, _font_NotoSansTeluguUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); + Ref<FontFile> thai_font_bold = load_internal_font(_font_NotoSansThaiUI_Bold, _font_NotoSansThaiUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, false, &fallbacks_bold); Ref<FontVariation> fallback_font_bold = make_bold_font(fallback_font, embolden_strength, &fallbacks_bold); Ref<FontVariation> japanese_font_bold = make_bold_font(japanese_font, embolden_strength, &fallbacks_bold); default_font_bold->set_fallbacks(fallbacks_bold); default_font_bold_msdf->set_fallbacks(fallbacks_bold); - Ref<FontFile> default_font_mono = load_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_hinting, font_antialiased, true, font_subpixel_positioning); + Ref<FontFile> default_font_mono = load_internal_font(_font_JetBrainsMono_Regular, _font_JetBrainsMono_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning); default_font_mono->set_fallbacks(fallbacks); // Init base font configs and load custom fonts. @@ -174,7 +174,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<FontVariation> default_fc; default_fc.instantiate(); if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiased, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font); @@ -191,7 +191,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<FontVariation> default_fc_msdf; default_fc_msdf.instantiate(); if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiased, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_msdf); @@ -208,7 +208,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<FontVariation> bold_fc; bold_fc.instantiate(); if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiased, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_bold); @@ -216,7 +216,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { } bold_fc->set_base_font(custom_font); } else if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiased, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_bold); @@ -234,7 +234,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<FontVariation> bold_fc_msdf; bold_fc_msdf.instantiate(); if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiased, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_bold_msdf); @@ -242,7 +242,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { } bold_fc_msdf->set_base_font(custom_font); } else if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiased, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_bold_msdf); @@ -260,7 +260,7 @@ void editor_register_fonts(Ref<Theme> p_theme) { Ref<FontVariation> mono_fc; mono_fc.instantiate(); if (custom_font_path_source.length() > 0 && dir->file_exists(custom_font_path_source)) { - Ref<FontFile> custom_font = load_external_font(custom_font_path_source, font_hinting, font_antialiased, true, font_subpixel_positioning); + Ref<FontFile> custom_font = load_external_font(custom_font_path_source, font_hinting, font_antialiasing, true, font_subpixel_positioning); { TypedArray<Font> fallback_custom; fallback_custom.push_back(default_font_mono); diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 8d58469684..29a0781e96 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -44,29 +44,6 @@ DocTools *EditorHelp::doc = nullptr; -void EditorHelp::_update_theme() { - text_color = get_theme_color(SNAME("text_color"), SNAME("EditorHelp")); - title_color = get_theme_color(SNAME("title_color"), SNAME("EditorHelp")); - headline_color = get_theme_color(SNAME("headline_color"), SNAME("EditorHelp")); - comment_color = get_theme_color(SNAME("comment_color"), SNAME("EditorHelp")); - symbol_color = get_theme_color(SNAME("symbol_color"), SNAME("EditorHelp")); - value_color = get_theme_color(SNAME("value_color"), SNAME("EditorHelp")); - qualifier_color = get_theme_color(SNAME("qualifier_color"), SNAME("EditorHelp")); - type_color = get_theme_color(SNAME("type_color"), SNAME("EditorHelp")); - - class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("selection_color"), SNAME("EditorHelp"))); - class_desc->add_theme_constant_override("line_separation", get_theme_constant(SNAME("line_separation"), SNAME("EditorHelp"))); - class_desc->add_theme_constant_override("table_h_separation", get_theme_constant(SNAME("table_h_separation"), SNAME("EditorHelp"))); - class_desc->add_theme_constant_override("table_v_separation", get_theme_constant(SNAME("table_v_separation"), SNAME("EditorHelp"))); - - doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts")); - doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts")); - doc_title_font = get_theme_font(SNAME("doc_title"), SNAME("EditorFonts")); - doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts")); - - doc_title_font_size = get_theme_font_size(SNAME("doc_title_size"), SNAME("EditorFonts")); -} - void EditorHelp::_search(bool p_search_previous) { if (p_search_previous) { find_bar->search_prev(); @@ -239,6 +216,27 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) { class_desc->pop(); } +void EditorHelp::_add_type_icon(const String &p_type, int p_size) { + Ref<Texture2D> icon; + if (has_theme_icon(p_type, SNAME("EditorIcons"))) { + icon = get_theme_icon(p_type, SNAME("EditorIcons")); + } else if (ClassDB::class_exists(p_type) && ClassDB::is_parent_class(p_type, "Object")) { + icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons")); + } else { + icon = get_theme_icon(SNAME("ArrowRight"), SNAME("EditorIcons")); + } + + Vector2i size = Vector2i(icon->get_width(), icon->get_height()); + if (p_size > 0) { + // Ensures icon scales proportionally on both axis, based on icon height. + float ratio = p_size / float(size.height); + size.width *= ratio; + size.height *= ratio; + } + + class_desc->add_image(icon, size.width, size.height); +} + String EditorHelp::_fix_constant(const String &p_constant) const { if (p_constant.strip_edges() == "4294967295") { return "0xFFFFFFFF"; @@ -525,27 +523,17 @@ void EditorHelp::_update_doc() { method_line.clear(); section_line.clear(); - _update_theme(); String link_color_text = title_color.to_html(false); DocData::ClassDoc cd = doc->class_list[edited_class]; // Make a copy, so we can sort without worrying. - Ref<Texture2D> icon; - if (has_theme_icon(edited_class, SNAME("EditorIcons"))) { - icon = get_theme_icon(edited_class, SNAME("EditorIcons")); - } else if (ClassDB::class_exists(edited_class) && ClassDB::is_parent_class(edited_class, "Object")) { - icon = get_theme_icon(SNAME("Object"), SNAME("EditorIcons")); - } else { - icon = get_theme_icon(SNAME("ArrowRight"), SNAME("EditorIcons")); - } - // Class name section_line.push_back(Pair<String, int>(TTR("Top"), 0)); class_desc->push_font(doc_title_font); class_desc->push_font_size(doc_title_font_size); class_desc->push_color(title_color); class_desc->add_text(TTR("Class:") + " "); - class_desc->add_image(icon, icon->get_width(), icon->get_height()); + _add_type_icon(edited_class, doc_title_font_size); class_desc->add_text(" "); class_desc->push_color(headline_color); _add_text(edited_class); @@ -566,6 +554,8 @@ void EditorHelp::_update_doc() { String inherits = cd.inherits; while (!inherits.is_empty()) { + _add_type_icon(inherits); + class_desc->add_text(" "); // Extra space, otherwise icon borrows hyperlink from _add_type(). _add_type(inherits); inherits = doc->class_list[inherits].inherits; @@ -597,7 +587,8 @@ void EditorHelp::_update_doc() { if (prev) { class_desc->add_text(" , "); } - + _add_type_icon(E.value.name); + class_desc->add_text(" "); // Extra space, otherwise icon borrows hyperlink from _add_type(). _add_type(E.value.name); prev = true; } @@ -1785,7 +1776,7 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) { p_rt->add_text("["); pos = brk_pos + 1; - } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("theme_item ")) { + } else if (tag.begins_with("method ") || tag.begins_with("member ") || tag.begins_with("signal ") || tag.begins_with("enum ") || tag.begins_with("constant ") || tag.begins_with("annotation ") || tag.begins_with("theme_item ")) { const int tag_end = tag.find(" "); const String link_tag = tag.substr(0, tag_end); const String link_target = tag.substr(tag_end + 1, tag.length()).lstrip(" "); @@ -1994,10 +1985,29 @@ void EditorHelp::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - if (is_inside_tree()) { - _class_desc_resized(true); - } + _class_desc_resized(true); update_toggle_scripts_button(); + + text_color = get_theme_color(SNAME("text_color"), SNAME("EditorHelp")); + title_color = get_theme_color(SNAME("title_color"), SNAME("EditorHelp")); + headline_color = get_theme_color(SNAME("headline_color"), SNAME("EditorHelp")); + comment_color = get_theme_color(SNAME("comment_color"), SNAME("EditorHelp")); + symbol_color = get_theme_color(SNAME("symbol_color"), SNAME("EditorHelp")); + value_color = get_theme_color(SNAME("value_color"), SNAME("EditorHelp")); + qualifier_color = get_theme_color(SNAME("qualifier_color"), SNAME("EditorHelp")); + type_color = get_theme_color(SNAME("type_color"), SNAME("EditorHelp")); + + class_desc->add_theme_color_override("selection_color", get_theme_color(SNAME("selection_color"), SNAME("EditorHelp"))); + class_desc->add_theme_constant_override("line_separation", get_theme_constant(SNAME("line_separation"), SNAME("EditorHelp"))); + class_desc->add_theme_constant_override("table_h_separation", get_theme_constant(SNAME("table_h_separation"), SNAME("EditorHelp"))); + class_desc->add_theme_constant_override("table_v_separation", get_theme_constant(SNAME("table_v_separation"), SNAME("EditorHelp"))); + + doc_font = get_theme_font(SNAME("doc"), SNAME("EditorFonts")); + doc_bold_font = get_theme_font(SNAME("doc_bold"), SNAME("EditorFonts")); + doc_title_font = get_theme_font(SNAME("doc_title"), SNAME("EditorFonts")); + doc_code_font = get_theme_font(SNAME("doc_source"), SNAME("EditorFonts")); + + doc_title_font_size = get_theme_font_size(SNAME("doc_title_size"), SNAME("EditorFonts")); } break; case NOTIFICATION_VISIBILITY_CHANGED: { @@ -2078,7 +2088,7 @@ void EditorHelp::update_toggle_scripts_button() { } else { toggle_scripts_button->set_icon(get_theme_icon(ScriptEditor::get_singleton()->is_scripts_panel_toggled() ? SNAME("Back") : SNAME("Forward"), SNAME("EditorIcons"))); } - toggle_scripts_button->set_tooltip(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text())); + toggle_scripts_button->set_tooltip_text(vformat("%s (%s)", TTR("Toggle Scripts Panel"), ED_GET_SHORTCUT("script_editor/toggle_scripts_panel")->get_as_text())); } void EditorHelp::_bind_methods() { @@ -2175,7 +2185,6 @@ void EditorHelpBit::_bind_methods() { void EditorHelpBit::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { rich_text->add_theme_color_override("selection_color", get_theme_color(SNAME("selection_color"), SNAME("EditorHelp"))); rich_text->clear(); @@ -2257,7 +2266,6 @@ void FindBar::popup_search() { void FindBar::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { find_prev->set_icon(get_theme_icon(SNAME("MoveUp"), SNAME("EditorIcons"))); find_next->set_icon(get_theme_icon(SNAME("MoveDown"), SNAME("EditorIcons"))); diff --git a/editor/editor_help.h b/editor/editor_help.h index 9f8da938f1..20eb7f78fc 100644 --- a/editor/editor_help.h +++ b/editor/editor_help.h @@ -145,7 +145,6 @@ class EditorHelp : public VBoxContainer { int scroll_to = -1; - void _update_theme(); void _help_callback(const String &p_topic); void _add_text(const String &p_bbcode); @@ -153,6 +152,7 @@ class EditorHelp : public VBoxContainer { //void _button_pressed(int p_idx); void _add_type(const String &p_type, const String &p_enum = String()); + void _add_type_icon(const String &p_type, int p_size = 0); void _add_method(const DocData::MethodDoc &p_method, bool p_overview = true); void _add_bulletpoint(); diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index 297d1226e3..b4678d8363 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -206,7 +206,7 @@ EditorHelpSearch::EditorHelpSearch() { case_sensitive_button = memnew(Button); case_sensitive_button->set_flat(true); - case_sensitive_button->set_tooltip(TTR("Case Sensitive")); + case_sensitive_button->set_tooltip_text(TTR("Case Sensitive")); case_sensitive_button->connect("pressed", callable_mp(this, &EditorHelpSearch::_update_results)); case_sensitive_button->set_toggle_mode(true); case_sensitive_button->set_focus_mode(Control::FOCUS_NONE); @@ -214,7 +214,7 @@ EditorHelpSearch::EditorHelpSearch() { hierarchy_button = memnew(Button); hierarchy_button->set_flat(true); - hierarchy_button->set_tooltip(TTR("Show Hierarchy")); + hierarchy_button->set_tooltip_text(TTR("Show Hierarchy")); hierarchy_button->connect("pressed", callable_mp(this, &EditorHelpSearch::_update_results)); hierarchy_button->set_toggle_mode(true); hierarchy_button->set_pressed(true); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index b4e36d568d..21dbb5ab97 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -340,6 +340,8 @@ void EditorProperty::_notification(int p_what) { draw_string(font, Point2(ofs, v_ofs + font->get_ascent(font_size)), label, HORIZONTAL_ALIGNMENT_LEFT, text_limit, font_size, color); } + ofs = size.width; + if (keying) { Ref<Texture2D> key; @@ -349,7 +351,7 @@ void EditorProperty::_notification(int p_what) { key = get_theme_icon(SNAME("Key"), SNAME("EditorIcons")); } - ofs = size.width - key->get_width() - get_theme_constant(SNAME("hseparator"), SNAME("Tree")); + ofs -= key->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); Color color2(1, 1, 1); if (keying_hover) { @@ -373,7 +375,7 @@ void EditorProperty::_notification(int p_what) { close = get_theme_icon(SNAME("Close"), SNAME("EditorIcons")); - ofs = size.width - close->get_width() - get_theme_constant(SNAME("hseparator"), SNAME("Tree")); + ofs -= close->get_width() + get_theme_constant(SNAME("hseparator"), SNAME("Tree")); Color color2(1, 1, 1); if (delete_hover) { @@ -432,11 +434,11 @@ bool EditorProperty::is_read_only() const { } Variant EditorPropertyRevert::get_property_revert_value(Object *p_object, const StringName &p_property, bool *r_is_valid) { - if (p_object->has_method("property_can_revert") && p_object->call("property_can_revert", p_property)) { + if (p_object->property_can_revert(p_property)) { if (r_is_valid) { *r_is_valid = true; } - return p_object->call("property_get_revert", p_property); + return p_object->property_get_revert(p_property); } return PropertyUtils::get_property_default_value(p_object, p_property, r_is_valid); @@ -894,14 +896,9 @@ static Control *make_help_bit(const String &p_text, bool p_property) { } Control *EditorProperty::make_custom_tooltip(const String &p_text) const { - tooltip_text = p_text; return make_help_bit(p_text, true); } -String EditorProperty::get_tooltip_text() const { - return tooltip_text; -} - void EditorProperty::menu_option(int p_option) { switch (p_option) { case MENU_COPY_PROPERTY: { @@ -949,7 +946,6 @@ void EditorProperty::_bind_methods() { ClassDB::bind_method(D_METHOD("get_edited_property"), &EditorProperty::get_edited_property); ClassDB::bind_method(D_METHOD("get_edited_object"), &EditorProperty::get_edited_object); - ClassDB::bind_method(D_METHOD("get_tooltip_text"), &EditorProperty::get_tooltip_text); ClassDB::bind_method(D_METHOD("update_property"), &EditorProperty::update_property); ClassDB::bind_method(D_METHOD("add_focusable", "control"), &EditorProperty::add_focusable); @@ -1127,7 +1123,6 @@ void EditorInspectorCategory::_notification(int p_what) { } Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) const { - tooltip_text = p_text; return make_help_bit(p_text, false); } @@ -1146,14 +1141,6 @@ Size2 EditorInspectorCategory::get_minimum_size() const { return ms; } -void EditorInspectorCategory::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_tooltip_text"), &EditorInspectorCategory::get_tooltip_text); -} - -String EditorInspectorCategory::get_tooltip_text() const { - return tooltip_text; -} - EditorInspectorCategory::EditorInspectorCategory() { } @@ -1701,7 +1688,7 @@ void EditorInspectorArray::_move_element(int p_element_index, int p_to_pos) { // Call the function. Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name()); if (move_function.is_valid()) { - Variant args[] = { (Object *)undo_redo, object, array_element_prefix, p_element_index, p_to_pos }; + Variant args[] = { undo_redo.ptr(), object, array_element_prefix, p_element_index, p_to_pos }; const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] }; Variant return_value; Callable::CallError call_error; @@ -1845,7 +1832,7 @@ void EditorInspectorArray::_clear_array() { // Call the function. Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name()); if (move_function.is_valid()) { - Variant args[] = { (Object *)undo_redo, object, array_element_prefix, i, -1 }; + Variant args[] = { undo_redo.ptr(), object, array_element_prefix, i, -1 }; const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] }; Variant return_value; Callable::CallError call_error; @@ -1898,7 +1885,7 @@ void EditorInspectorArray::_resize_array(int p_size) { // Call the function. Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name()); if (move_function.is_valid()) { - Variant args[] = { (Object *)undo_redo, object, array_element_prefix, -1, -1 }; + Variant args[] = { undo_redo.ptr(), object, array_element_prefix, -1, -1 }; const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] }; Variant return_value; Callable::CallError call_error; @@ -1917,7 +1904,7 @@ void EditorInspectorArray::_resize_array(int p_size) { // Call the function. Callable move_function = EditorNode::get_singleton()->get_editor_data().get_move_array_element_function(object->get_class_name()); if (move_function.is_valid()) { - Variant args[] = { (Object *)undo_redo, object, array_element_prefix, i, -1 }; + Variant args[] = { undo_redo.ptr(), object, array_element_prefix, i, -1 }; const Variant *args_p[] = { &args[0], &args[1], &args[2], &args[3], &args[4] }; Variant return_value; Callable::CallError call_error; @@ -2064,7 +2051,7 @@ void EditorInspectorArray::_setup() { ae.panel->set_mouse_filter(MOUSE_FILTER_PASS); ae.panel->set_drag_forwarding(this); ae.panel->set_meta("index", begin_array_index + i); - ae.panel->set_tooltip(vformat(TTR("Element %d: %s%d*"), i, array_element_prefix, i)); + ae.panel->set_tooltip_text(vformat(TTR("Element %d: %s%d*"), i, array_element_prefix, i)); ae.panel->connect("focus_entered", callable_mp((CanvasItem *)ae.panel, &PanelContainer::update)); ae.panel->connect("focus_exited", callable_mp((CanvasItem *)ae.panel, &PanelContainer::update)); ae.panel->connect("draw", callable_mp(this, &EditorInspectorArray::_panel_draw).bind(i)); @@ -2188,7 +2175,6 @@ bool EditorInspectorArray::can_drop_data_fw(const Point2 &p_point, const Variant void EditorInspectorArray::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { Color color = get_theme_color(SNAME("dark_color_1"), SNAME("Editor")); odd_style->set_bg_color(color.darkened(-0.08)); @@ -2240,7 +2226,7 @@ void EditorInspectorArray::_bind_methods() { ADD_SIGNAL(MethodInfo("page_change_request")); } -void EditorInspectorArray::set_undo_redo(UndoRedo *p_undo_redo) { +void EditorInspectorArray::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { undo_redo = p_undo_redo; } @@ -2382,7 +2368,6 @@ void EditorPaginator::update(int p_page, int p_max_page) { void EditorPaginator::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { first_page_button->set_icon(get_theme_icon(SNAME("PageFirst"), SNAME("EditorIcons"))); prev_page_button->set_icon(get_theme_icon(SNAME("PagePrevious"), SNAME("EditorIcons"))); @@ -2502,7 +2487,7 @@ Button *EditorInspector::create_inspector_action_button(const String &p_text) { return button; } -void EditorInspector::set_undo_redo(UndoRedo *p_undo_redo) { +void EditorInspector::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { undo_redo = p_undo_redo; } @@ -2628,15 +2613,28 @@ void EditorInspector::update_tree() { valid_plugins.push_back(inspector_plugins[i]); } - // Decide if properties should be drawn with the warning color (yellow). + // Decide if properties should be drawn with the warning color (yellow), + // or if the whole object should be considered read-only. bool draw_warning = false; + bool all_read_only = false; if (is_inside_tree()) { + if (object->has_method("_is_read_only")) { + all_read_only = object->call("_is_read_only"); + } + Node *nod = Object::cast_to<Node>(object); Node *es = EditorNode::get_singleton()->get_edited_scene(); if (nod && es != nod && nod->get_owner() != es) { // Draw in warning color edited nodes that are not in the currently edited scene, // as changes may be lost in the future. draw_warning = true; + } else { + if (!all_read_only) { + Resource *res = Object::cast_to<Resource>(object); + if (res) { + all_read_only = EditorNode::get_singleton()->is_resource_read_only(res); + } + } } } @@ -2788,7 +2786,7 @@ void EditorInspector::update_tree() { class_descr_cache[doc_name] = descr; } - category->set_tooltip(p.name + "::" + (class_descr_cache[doc_name].is_empty() ? "" : class_descr_cache[doc_name])); + category->set_tooltip_text(p.name + "::" + (class_descr_cache[doc_name].is_empty() ? "" : class_descr_cache[doc_name])); } // Add editors at the start of a category. @@ -2981,7 +2979,7 @@ void EditorInspector::update_tree() { Color c = sscolor; c.a /= level; section->setup(acc_path, label, object, c, use_folding, section_depth); - section->set_tooltip(tooltip); + section->set_tooltip_text(tooltip); // Add editors at the start of a group. for (Ref<EditorInspectorPlugin> &ped : valid_plugins) { @@ -3179,7 +3177,6 @@ void EditorInspector::update_tree() { ep->property_usage = p.usage; //and set label? } - if (!editors[i].label.is_empty()) { ep->set_label(editors[i].label); } else { @@ -3206,7 +3203,7 @@ void EditorInspector::update_tree() { ep->set_checkable(checkable); ep->set_checked(checked); ep->set_keying(keying); - ep->set_read_only(property_read_only); + ep->set_read_only(property_read_only || all_read_only); ep->set_deletable(deletable_properties || p.name.begins_with("metadata/")); } @@ -3226,9 +3223,9 @@ void EditorInspector::update_tree() { ep->connect("resource_selected", callable_mp(this, &EditorInspector::_resource_selected), CONNECT_DEFERRED); ep->connect("object_id_selected", callable_mp(this, &EditorInspector::_object_id_selected), CONNECT_DEFERRED); if (!doc_info.description.is_empty()) { - ep->set_tooltip(property_prefix + p.name + "::" + doc_info.description); + ep->set_tooltip_text(property_prefix + p.name + "::" + doc_info.description); } else { - ep->set_tooltip(property_prefix + p.name); + ep->set_tooltip_text(property_prefix + p.name); } ep->set_doc_path(doc_info.path); ep->update_property(); @@ -3253,6 +3250,9 @@ void EditorInspector::update_tree() { add_md->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); add_md->connect(SNAME("pressed"), callable_mp(this, &EditorInspector::_show_add_meta_dialog)); main_vbox->add_child(add_md); + if (all_read_only) { + add_md->set_disabled(true); + } } // Get the lists of to add at the end. @@ -3525,7 +3525,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo } } - if (!undo_redo || bool(object->call("_dont_undo_redo"))) { + if (!undo_redo.is_valid() || bool(object->call("_dont_undo_redo"))) { object->set(p_name, p_value); if (p_refresh_all) { _edit_request_change(object, ""); @@ -3568,7 +3568,7 @@ void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bo } } - Variant v_undo_redo = (Object *)undo_redo; + Variant v_undo_redo = undo_redo; Variant v_object = object; Variant v_name = p_name; for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_undo_redo_inspector_hook_callback().size(); i++) { @@ -3744,7 +3744,7 @@ void EditorInspector::_property_pinned(const String &p_path, bool p_pinned) { Node *node = Object::cast_to<Node>(object); ERR_FAIL_COND(!node); - if (undo_redo) { + if (undo_redo.is_valid()) { undo_redo->create_action(vformat(p_pinned ? TTR("Pinned %s") : TTR("Unpinned %s"), p_path)); undo_redo->add_do_method(node, "_set_property_pinned", p_path, p_pinned); undo_redo->add_undo_method(node, "_set_property_pinned", p_path, !p_pinned); @@ -4026,7 +4026,6 @@ void EditorInspector::_bind_methods() { EditorInspector::EditorInspector() { object = nullptr; - undo_redo = nullptr; main_vbox = memnew(VBoxContainer); main_vbox->set_h_size_flags(SIZE_EXPAND_FILL); main_vbox->add_theme_constant_override("separation", 0); diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h index baba9ec1f4..474078853a 100644 --- a/editor/editor_inspector.h +++ b/editor/editor_inspector.h @@ -31,6 +31,7 @@ #ifndef EDITOR_INSPECTOR_H #define EDITOR_INSPECTOR_H +#include "editor/editor_undo_redo_manager.h" #include "editor_property_name_processor.h" #include "scene/gui/box_container.h" #include "scene/gui/button.h" @@ -42,8 +43,6 @@ #include "scene/gui/spin_box.h" #include "scene/gui/texture_rect.h" -class UndoRedo; - class EditorPropertyRevert { public: static bool get_instantiated_node_original_property(Node *p_node, const StringName &p_prop, Variant &value, bool p_check_class_default = true); @@ -118,8 +117,6 @@ private: Control *bottom_editor = nullptr; PopupMenu *menu = nullptr; - mutable String tooltip_text; - HashMap<StringName, Variant> cache; GDVIRTUAL0(_update_property) @@ -200,8 +197,6 @@ public: void set_object_and_property(Object *p_object, const StringName &p_property); virtual Control *make_custom_tooltip(const String &p_text) const override; - String get_tooltip_text() const; - void set_draw_top_bg(bool p_draw) { draw_top_bg = p_draw; } bool can_revert_to_default() const { return can_revert; } @@ -255,18 +250,13 @@ class EditorInspectorCategory : public Control { Ref<Texture2D> icon; String label; - mutable String tooltip_text; - protected: void _notification(int p_what); - static void _bind_methods(); public: virtual Size2 get_minimum_size() const override; virtual Control *make_custom_tooltip(const String &p_text) const override; - String get_tooltip_text() const; - EditorInspectorCategory(); }; @@ -313,7 +303,7 @@ public: class EditorInspectorArray : public EditorInspectorSection { GDCLASS(EditorInspectorArray, EditorInspectorSection); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; enum Mode { MODE_NONE, @@ -408,7 +398,7 @@ protected: static void _bind_methods(); public: - void set_undo_redo(UndoRedo *p_undo_redo); + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void setup_with_move_element_function(Object *p_object, String p_label, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = ""); void setup_with_count_property(Object *p_object, String p_label, const StringName &p_count_property, const StringName &p_array_element_prefix, int p_page, const Color &p_bg_color, bool p_foldable, bool p_movable = true, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = ""); @@ -448,7 +438,7 @@ public: class EditorInspector : public ScrollContainer { GDCLASS(EditorInspector, ScrollContainer); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; enum { MAX_PLUGINS = 1024 }; @@ -562,7 +552,7 @@ public: static EditorProperty *instantiate_property_editor(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false); - void set_undo_redo(UndoRedo *p_undo_redo); + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); String get_selected_path() const; diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp index cd8150d235..87da67fb05 100644 --- a/editor/editor_locale_dialog.cpp +++ b/editor/editor_locale_dialog.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/check_button.h" #include "scene/gui/line_edit.h" #include "scene/gui/option_button.h" @@ -492,7 +493,7 @@ EditorLocaleDialog::EditorLocaleDialog() { { lang_code = memnew(LineEdit); lang_code->set_max_length(3); - lang_code->set_tooltip("Language"); + lang_code->set_tooltip_text("Language"); vb_language->add_child(lang_code); } hb_locale->add_child(vb_language); @@ -508,7 +509,7 @@ EditorLocaleDialog::EditorLocaleDialog() { { script_code = memnew(LineEdit); script_code->set_max_length(4); - script_code->set_tooltip("Script"); + script_code->set_tooltip_text("Script"); vb_script->add_child(script_code); } hb_locale->add_child(vb_script); @@ -524,7 +525,7 @@ EditorLocaleDialog::EditorLocaleDialog() { { country_code = memnew(LineEdit); country_code->set_max_length(2); - country_code->set_tooltip("Country"); + country_code->set_tooltip_text("Country"); vb_country->add_child(country_code); } hb_locale->add_child(vb_country); @@ -541,7 +542,7 @@ EditorLocaleDialog::EditorLocaleDialog() { variant_code = memnew(LineEdit); variant_code->set_h_size_flags(Control::SIZE_EXPAND_FILL); variant_code->set_placeholder("Variant"); - variant_code->set_tooltip("Variant"); + variant_code->set_tooltip_text("Variant"); vb_variant->add_child(variant_code); } hb_locale->add_child(vb_variant); diff --git a/editor/editor_locale_dialog.h b/editor/editor_locale_dialog.h index 7a4828e83a..8ac642a038 100644 --- a/editor/editor_locale_dialog.h +++ b/editor/editor_locale_dialog.h @@ -40,7 +40,7 @@ class VBoxContainer; class LineEdit; class Tree; class OptionButton; -class UndoRedo; +class EditorUndoRedoManager; class EditorLocaleDialog : public ConfirmationDialog { GDCLASS(EditorLocaleDialog, ConfirmationDialog); @@ -63,7 +63,7 @@ class EditorLocaleDialog : public ConfirmationDialog { Tree *script_list = nullptr; Tree *cnt_list = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; bool locale_set = false; bool updating_lists = false; diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp index 8aa099ddff..dbb6706cb3 100644 --- a/editor/editor_log.cpp +++ b/editor/editor_log.cpp @@ -64,52 +64,48 @@ void EditorLog::_error_handler(void *p_self, const char *p_func, const char *p_f } } -void EditorLog::_update_theme() { - Ref<Font> normal_font = get_theme_font(SNAME("output_source"), SNAME("EditorFonts")); - if (normal_font.is_valid()) { - log->add_theme_font_override("normal_font", normal_font); - } - - log->add_theme_font_size_override("normal_font_size", get_theme_font_size(SNAME("output_source_size"), SNAME("EditorFonts"))); - log->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); - - Ref<Font> bold_font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); - if (bold_font.is_valid()) { - log->add_theme_font_override("bold_font", bold_font); - } - - type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_theme_icon(SNAME("Popup"), SNAME("EditorIcons"))); - type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons"))); - type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); - type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); - - 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_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); - copy_button->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); - collapse_button->set_icon(get_theme_icon(SNAME("CombineLines"), SNAME("EditorIcons"))); - show_search_button->set_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); - search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); - - theme_cache.error_color = get_theme_color(SNAME("error_color"), SNAME("Editor")); - theme_cache.error_icon = get_theme_icon(SNAME("Error"), SNAME("EditorIcons")); - theme_cache.warning_color = get_theme_color(SNAME("warning_color"), SNAME("Editor")); - theme_cache.warning_icon = get_theme_icon(SNAME("Warning"), SNAME("EditorIcons")); - theme_cache.message_color = get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.6); -} - void EditorLog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - _update_theme(); _load_state(); } break; case NOTIFICATION_THEME_CHANGED: { - _update_theme(); + Ref<Font> normal_font = get_theme_font(SNAME("output_source"), SNAME("EditorFonts")); + if (normal_font.is_valid()) { + log->add_theme_font_override("normal_font", normal_font); + } + + log->add_theme_font_size_override("normal_font_size", get_theme_font_size(SNAME("output_source_size"), SNAME("EditorFonts"))); + log->add_theme_color_override("selection_color", get_theme_color(SNAME("accent_color"), SNAME("Editor")) * Color(1, 1, 1, 0.4)); + + Ref<Font> bold_font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); + if (bold_font.is_valid()) { + log->add_theme_font_override("bold_font", bold_font); + } + + type_filter_map[MSG_TYPE_STD]->toggle_button->set_icon(get_theme_icon(SNAME("Popup"), SNAME("EditorIcons"))); + type_filter_map[MSG_TYPE_ERROR]->toggle_button->set_icon(get_theme_icon(SNAME("StatusError"), SNAME("EditorIcons"))); + type_filter_map[MSG_TYPE_WARNING]->toggle_button->set_icon(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); + type_filter_map[MSG_TYPE_EDITOR]->toggle_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + + 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_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); + copy_button->set_icon(get_theme_icon(SNAME("ActionCopy"), SNAME("EditorIcons"))); + collapse_button->set_icon(get_theme_icon(SNAME("CombineLines"), SNAME("EditorIcons"))); + show_search_button->set_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + + theme_cache.error_color = get_theme_color(SNAME("error_color"), SNAME("Editor")); + theme_cache.error_icon = get_theme_icon(SNAME("Error"), SNAME("EditorIcons")); + theme_cache.warning_color = get_theme_color(SNAME("warning_color"), SNAME("Editor")); + theme_cache.warning_icon = get_theme_icon(SNAME("Warning"), SNAME("EditorIcons")); + theme_cache.message_color = get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.6); + _rebuild_log(); } break; } @@ -224,6 +220,10 @@ void EditorLog::set_tool_button(Button *p_tool_button) { tool_button = p_tool_button; } +void EditorLog::register_undo_redo(UndoRedo *p_undo_redo) { + p_undo_redo->set_commit_notify_callback(_undo_redo_cbk, this); +} + void EditorLog::_undo_redo_cbk(void *p_self, const String &p_name) { EditorLog *self = static_cast<EditorLog *>(p_self); self->add_message(p_name, EditorLog::MSG_TYPE_EDITOR); @@ -248,6 +248,11 @@ void EditorLog::_rebuild_log() { } void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) { + if (!is_inside_tree()) { + // The log will be built all at once when it enters the tree and has its theme items. + return; + } + // Only add the message to the log if it passes the filters. bool filter_active = type_filter_map[p_message.type]->is_active(); String search_text = search_box->get_text(); @@ -410,7 +415,7 @@ EditorLog::EditorLog() { collapse_button = memnew(Button); collapse_button->set_flat(true); collapse_button->set_focus_mode(FOCUS_NONE); - collapse_button->set_tooltip(TTR("Collapse duplicate messages into one log entry. Shows number of occurrences.")); + collapse_button->set_tooltip_text(TTR("Collapse duplicate messages into one log entry. Shows number of occurrences.")); collapse_button->set_toggle_mode(true); collapse_button->set_pressed(false); collapse_button->connect("toggled", callable_mp(this, &EditorLog::_set_collapse)); @@ -458,8 +463,6 @@ EditorLog::EditorLog() { add_error_handler(&eh); current = Thread::get_caller_id(); - - EditorNode::get_undo_redo()->set_commit_notify_callback(_undo_redo_cbk, this); } void EditorLog::deinit() { diff --git a/editor/editor_log.h b/editor/editor_log.h index c225e6d8c5..0e17eb7949 100644 --- a/editor/editor_log.h +++ b/editor/editor_log.h @@ -41,6 +41,8 @@ #include "scene/gui/texture_button.h" #include "scene/gui/texture_rect.h" +class UndoRedo; + class EditorLog : public HBoxContainer { GDCLASS(EditorLog, HBoxContainer); @@ -93,7 +95,7 @@ private: toggle_button->set_toggle_mode(true); toggle_button->set_pressed(true); toggle_button->set_text(itos(message_count)); - toggle_button->set_tooltip(TTR(p_tooltip)); + toggle_button->set_tooltip_text(TTR(p_tooltip)); // Don't tint the icon even when in "pressed" state. toggle_button->add_theme_color_override("icon_color_pressed", Color(1, 1, 1, 1)); toggle_button->set_focus_mode(FOCUS_NONE); @@ -173,8 +175,6 @@ private: void _save_state(); void _load_state(); - void _update_theme(); - protected: static void _bind_methods(); void _notification(int p_what); @@ -182,6 +182,7 @@ protected: public: void add_message(const String &p_msg, MessageType p_type = MSG_TYPE_STD); void set_tool_button(Button *p_tool_button); + void register_undo_redo(UndoRedo *p_undo_redo); void deinit(); void clear(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index e7946f56da..9a188bbe38 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -54,6 +54,7 @@ #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" #include "scene/gui/link_button.h" +#include "scene/gui/menu_bar.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/gui/panel_container.h" @@ -104,6 +105,7 @@ #include "editor/editor_themes.h" #include "editor/editor_toaster.h" #include "editor/editor_translation_parser.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/export/editor_export.h" #include "editor/export/export_template_manager.h" #include "editor/export/project_export.h" @@ -348,8 +350,7 @@ void EditorNode::_update_scene_tabs() { icon = EditorNode::get_singleton()->get_object_icon(type_node, "Node"); } - int current = editor_data.get_edited_scene(); - bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0; + bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(i)); scene_tabs->add_tab(disambiguated_scene_names[i] + (unsaved ? "(*)" : ""), icon); if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { @@ -596,15 +597,15 @@ void EditorNode::_notification(int p_what) { opening_prev = false; } - bool unsaved_cache_changed = false; - if (unsaved_cache != (saved_version != editor_data.get_undo_redo().get_version())) { - unsaved_cache = (saved_version != editor_data.get_undo_redo().get_version()); - unsaved_cache_changed = true; + bool global_unsaved = get_undo_redo()->is_history_unsaved(EditorUndoRedoManager::GLOBAL_HISTORY); + bool scene_or_global_unsaved = global_unsaved || get_undo_redo()->is_history_unsaved(editor_data.get_current_edited_scene_history_id()); + if (unsaved_cache != scene_or_global_unsaved) { + unsaved_cache = scene_or_global_unsaved; + _update_title(); } - if (last_checked_version != editor_data.get_undo_redo().get_version()) { + if (editor_data.is_scene_changed(-1)) { _update_scene_tabs(); - last_checked_version = editor_data.get_undo_redo().get_version(); } // Update the animation frame of the update spinner. @@ -630,7 +631,7 @@ void EditorNode::_notification(int p_what) { ResourceImporterTexture::get_singleton()->update_imports(); - if (settings_changed || unsaved_cache_changed) { + if (settings_changed) { _update_title(); } @@ -660,6 +661,7 @@ void EditorNode::_notification(int p_what) { command_palette->register_shortcuts_as_command(); + MessageQueue::get_singleton()->push_callable(callable_mp(this, &EditorNode::_begin_first_scan)); /* DO NOT LOAD SCENES HERE, WAIT FOR FILE SCANNING AND REIMPORT TO COMPLETE */ } break; @@ -751,11 +753,7 @@ void EditorNode::_notification(int p_what) { scene_tabs->add_theme_style_override("tab_selected", gui_base->get_theme_stylebox(SNAME("SceneTabFG"), SNAME("EditorStyles"))); scene_tabs->add_theme_style_override("tab_unselected", gui_base->get_theme_stylebox(SNAME("SceneTabBG"), SNAME("EditorStyles"))); - file_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - project_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - debug_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - settings_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - help_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); + main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); } scene_tabs->set_max_tab_width(int(EDITOR_GET("interface/scene_tabs/maximum_width")) * EDSCALE); @@ -803,16 +801,15 @@ void EditorNode::_notification(int p_what) { dock_tab_move_right->set_icon(theme->get_icon(SNAME("Forward"), SNAME("EditorIcons"))); } - PopupMenu *p = help_menu->get_popup(); - p->set_item_icon(p->get_item_index(HELP_SEARCH), gui_base->get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons"))); - p->set_item_icon(p->get_item_index(HELP_DOCS), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); - p->set_item_icon(p->get_item_index(HELP_QA), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); - p->set_item_icon(p->get_item_index(HELP_REPORT_A_BUG), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); - p->set_item_icon(p->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); - p->set_item_icon(p->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); - p->set_item_icon(p->get_item_index(HELP_COMMUNITY), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); - p->set_item_icon(p->get_item_index(HELP_ABOUT), gui_base->get_theme_icon(SNAME("Godot"), SNAME("EditorIcons"))); - p->set_item_icon(p->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), gui_base->get_theme_icon(SNAME("Heart"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), gui_base->get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_DOCS), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_QA), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_REPORT_A_BUG), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_SUGGEST_A_FEATURE), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_SEND_DOCS_FEEDBACK), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_COMMUNITY), gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_ABOUT), gui_base->get_theme_icon(SNAME("Godot"), SNAME("EditorIcons"))); + help_menu->set_item_icon(help_menu->get_item_index(HELP_SUPPORT_GODOT_DEVELOPMENT), gui_base->get_theme_icon(SNAME("Heart"), SNAME("EditorIcons"))); for (int i = 0; i < main_editor_buttons.size(); i++) { main_editor_buttons.write[i]->add_theme_font_override("font", gui_base->get_theme_font(SNAME("main_button_font"), SNAME("EditorFonts"))); @@ -848,7 +845,7 @@ void EditorNode::_update_update_spinner() { update_popup->set_item_checked(update_popup->get_item_index(SETTINGS_UPDATE_WHEN_CHANGED), !update_continuously); if (update_continuously) { - update_spinner->set_tooltip(TTR("Spins when the editor window redraws.\nUpdate Continuously is enabled, which can increase power usage. Click to disable it.")); + update_spinner->set_tooltip_text(TTR("Spins when the editor window redraws.\nUpdate Continuously is enabled, which can increase power usage. Click to disable it.")); // Use a different color for the update spinner when Update Continuously is enabled, // as this feature should only be enabled for troubleshooting purposes. @@ -858,7 +855,7 @@ void EditorNode::_update_update_spinner() { update_spinner->set_self_modulate( gui_base->get_theme_color(SNAME("error_color"), SNAME("Editor")) * (dark_theme ? Color(1.1, 1.1, 1.1) : Color(4.25, 4.25, 4.25))); } else { - update_spinner->set_tooltip(TTR("Spins when the editor window redraws.")); + update_spinner->set_tooltip_text(TTR("Spins when the editor window redraws.")); update_spinner->set_self_modulate(Color(1, 1, 1)); } @@ -1047,6 +1044,8 @@ void EditorNode::_sources_changed(bool p_exist) { if (waiting_for_first_scan) { waiting_for_first_scan = false; + Engine::get_singleton()->startup_benchmark_end_measure(); // editor_scan_and_reimport + // Reload the global shader variables, but this time // loading textures, as they are now properly imported. RenderingServer::get_singleton()->global_shader_uniforms_load_settings(true); @@ -1059,8 +1058,16 @@ void EditorNode::_sources_changed(bool p_exist) { _load_docks(); if (!defer_load_scene.is_empty()) { + Engine::get_singleton()->startup_benchmark_begin_measure("editor_load_scene"); load_scene(defer_load_scene); defer_load_scene = ""; + Engine::get_singleton()->startup_benchmark_end_measure(); + + if (use_startup_benchmark) { + Engine::get_singleton()->startup_dump(startup_benchmark_file); + startup_benchmark_file = String(); + use_startup_benchmark = false; + } } } } @@ -1131,7 +1138,6 @@ void EditorNode::_reload_modified_scenes() { } } - get_undo_redo()->clear_history(false); set_current_scene(current_idx); _update_scene_tabs(); disk_changed->hide(); @@ -1244,7 +1250,9 @@ void EditorNode::save_resource_in_path(const Ref<Resource> &p_resource, const St } void EditorNode::save_resource(const Ref<Resource> &p_resource) { - if (p_resource->get_path().is_resource_file()) { + // If the resource has been imported, ask the user to use a different path in order to save it. + String path = p_resource->get_path(); + if (path.is_resource_file() && !FileAccess::exists(path + ".import")) { save_resource_in_path(p_resource, p_resource->get_path()); } else { save_resource_as(p_resource); @@ -1254,11 +1262,18 @@ void EditorNode::save_resource(const Ref<Resource> &p_resource) { void EditorNode::save_resource_as(const Ref<Resource> &p_resource, const String &p_at_path) { { String path = p_resource->get_path(); - int srpos = path.find("::"); - if (srpos != -1) { - String base = path.substr(0, srpos); - if (!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base) { - show_warning(TTR("This resource can't be saved because it does not belong to the edited scene. Make it unique first.")); + if (!path.is_resource_file()) { + int srpos = path.find("::"); + if (srpos != -1) { + String base = path.substr(0, srpos); + if (!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base) { + show_warning(TTR("This resource can't be saved because it does not belong to the edited scene. Make it unique first.")); + return; + } + } + } else { + if (FileAccess::exists(path + ".import")) { + show_warning(TTR("This resource can't be saved because it was imported from another file. Make it unique first.")); return; } } @@ -1685,6 +1700,8 @@ int EditorNode::_save_external_resources() { saved++; } + get_undo_redo()->set_history_as_saved(EditorUndoRedoManager::GLOBAL_HISTORY); + return saved; } @@ -1766,11 +1783,7 @@ void EditorNode::_save_scene(String p_file, int idx) { if (err == OK) { scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(p_file)); - if (idx < 0 || idx == editor_data.get_edited_scene()) { - set_current_version(editor_data.get_undo_redo().get_version()); - } else { - editor_data.set_edited_scene_version(0, idx); - } + editor_data.set_scene_as_saved(idx); editor_data.set_scene_modified_time(idx, FileAccess::get_modified_time(p_file)); editor_folding.save_scene_folding(scene, p_file); @@ -1815,15 +1828,15 @@ void EditorNode::restart_editor() { List<String> args; + for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) { + args.push_back(a); + } + args.push_back("--path"); args.push_back(ProjectSettings::get_singleton()->get_resource_path()); args.push_back("-e"); - if (OS::get_singleton()->is_disable_crash_handler()) { - args.push_back("--disable-crash-handler"); - } - if (!to_reopen.is_empty()) { args.push_back(to_reopen); } @@ -1862,12 +1875,9 @@ void EditorNode::_mark_unsaved_scenes() { } String path = node->get_scene_file_path(); - if (!(path.is_empty() || FileAccess::exists(path))) { - if (i == editor_data.get_edited_scene()) { - set_current_version(-1); - } else { - editor_data.set_edited_scene_version(-1, i); - } + if (!path.is_empty() && !FileAccess::exists(path)) { + // Mark scene tab as unsaved if the file is gone. + get_undo_redo()->set_history_as_unsaved(editor_data.get_scene_history_id(i)); } } @@ -1942,6 +1952,21 @@ void EditorNode::_dialog_action(String p_file) { } } break; + case FILE_SAVE_AND_RUN_MAIN_SCENE: { + ProjectSettings::get_singleton()->set("application/run/main_scene", p_file); + ProjectSettings::get_singleton()->save(); + + if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) { + _save_default_environment(); + _save_scene_with_preview(p_file); + if ((bool)pick_main_scene->get_meta("from_native", false)) { + run_native->resume_run_native(); + } else { + _run(false, p_file); + } + } + } break; + case FILE_EXPORT_MESH_LIBRARY: { Ref<MeshLibrary> ml; if (file_export_lib_merge->is_pressed() && FileAccess::exists(p_file)) { @@ -2207,7 +2232,14 @@ void EditorNode::_edit_current(bool p_skip_foreign) { bool stay_in_script_editor_on_node_selected = bool(EDITOR_GET("text_editor/behavior/navigation/stay_in_script_editor_on_node_selected")); bool skip_main_plugin = false; - String editable_warning; // None by default. + String editable_info; // None by default. + bool info_is_warning = false; + + if (current_obj->has_method("_is_read_only")) { + if (current_obj->call("_is_read_only")) { + editable_info = TTR("This object is marked as read-only, so it's not editable."); + } + } if (is_resource) { Resource *current_res = Object::cast_to<Resource>(current_obj); @@ -2221,16 +2253,25 @@ void EditorNode::_edit_current(bool p_skip_foreign) { int subr_idx = current_res->get_path().find("::"); if (subr_idx != -1) { String base_path = current_res->get_path().substr(0, subr_idx); - if (FileAccess::exists(base_path + ".import")) { - editable_warning = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); + if (!base_path.is_resource_file()) { + if (FileAccess::exists(base_path + ".import")) { + if (get_edited_scene() && get_edited_scene()->get_scene_file_path() == base_path) { + info_is_warning = true; + } + editable_info = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); + } else { + if ((!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base_path) && ResourceLoader::get_resource_type(base_path) == "PackedScene") { + editable_info = TTR("This resource belongs to a scene that was instantiated or inherited.\nChanges to it must be made inside the original scene."); + } + } } else { - if ((!get_edited_scene() || get_edited_scene()->get_scene_file_path() != base_path) && ResourceLoader::get_resource_type(base_path) == "PackedScene") { - editable_warning = TTR("This resource belongs to a scene that was instantiated or inherited.\nChanges to it won't be kept when saving the current scene."); + if (FileAccess::exists(base_path + ".import")) { + editable_info = TTR("This resource belongs to a scene that was imported, so it's not editable.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); } } } else if (current_res->get_path().is_resource_file()) { if (FileAccess::exists(current_res->get_path() + ".import")) { - editable_warning = TTR("This resource was imported, so it's not editable. Change its settings in the import panel and then re-import."); + editable_info = TTR("This resource was imported, so it's not editable. Change its settings in the import panel and then re-import."); } } } else if (is_node) { @@ -2254,7 +2295,8 @@ void EditorNode::_edit_current(bool p_skip_foreign) { if (get_edited_scene() && !get_edited_scene()->get_scene_file_path().is_empty()) { String source_scene = get_edited_scene()->get_scene_file_path(); if (FileAccess::exists(source_scene + ".import")) { - editable_warning = TTR("This scene was imported, so changes to it won't be kept.\nInstancing it or inheriting will allow making changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); + editable_info = TTR("This scene was imported, so changes to it won't be kept.\nInstancing it or inheriting will allow making changes to it.\nPlease read the documentation relevant to importing scenes to better understand this workflow."); + info_is_warning = true; } } @@ -2262,7 +2304,7 @@ void EditorNode::_edit_current(bool p_skip_foreign) { Node *selected_node = nullptr; if (current_obj->is_class("EditorDebuggerRemoteObject")) { - editable_warning = TTR("This is a remote object, so changes to it won't be kept.\nPlease read the documentation relevant to debugging to better understand this workflow."); + editable_info = TTR("This is a remote object, so it's not editable.\nPlease read the documentation relevant to debugging to better understand this workflow."); disable_folding = true; } else if (current_obj->is_class("MultiNodeEdit")) { Node *scene = get_edited_scene(); @@ -2297,7 +2339,10 @@ void EditorNode::_edit_current(bool p_skip_foreign) { InspectorDock::get_inspector_singleton()->update_tree(); } - InspectorDock::get_singleton()->set_warning(editable_warning); + InspectorDock::get_singleton()->set_info( + info_is_warning ? TTR("Changes may be lost!") : TTR("This object is read-only."), + editable_info, + info_is_warning); if (InspectorDock::get_inspector_singleton()->is_using_folding() == disable_folding) { InspectorDock::get_inspector_singleton()->set_use_folding(!disable_folding); @@ -2387,7 +2432,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { String run_filename; - if (p_current || (editor_data.get_edited_scene_root() && !p_custom.is_empty() && p_custom == editor_data.get_edited_scene_root()->get_scene_file_path())) { + if ((p_current && p_custom.is_empty()) || (editor_data.get_edited_scene_root() && !p_custom.is_empty() && p_custom == editor_data.get_edited_scene_root()->get_scene_file_path())) { Node *scene = editor_data.get_edited_scene_root(); if (!scene) { @@ -2396,10 +2441,8 @@ void EditorNode::_run(bool p_current, const String &p_custom) { } if (scene->get_scene_file_path().is_empty()) { - current_menu_option = -1; - _menu_option(FILE_SAVE_AS_SCENE); - // Set the option to save and run so when the dialog is accepted, the scene runs. current_menu_option = FILE_SAVE_AND_RUN; + _menu_option_confirm(FILE_SAVE_AS_SCENE, true); file->set_title(TTR("Save scene before running...")); return; } @@ -2414,6 +2457,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) { if (!ensure_main_scene(false)) { return; } + run_filename = GLOBAL_DEF_BASIC("application/run/main_scene", ""); } if (bool(EDITOR_GET("run/auto_save/save_before_running"))) { @@ -2450,15 +2494,19 @@ void EditorNode::_run(bool p_current, const String &p_custom) { emit_signal(SNAME("play_pressed")); if (p_current) { + run_current_filename = run_filename; play_scene_button->set_pressed(true); play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); + play_scene_button->set_tooltip_text(TTR("Reload the played scene.")); } else if (!p_custom.is_empty()) { run_custom_filename = p_custom; play_custom_scene_button->set_pressed(true); play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); + play_custom_scene_button->set_tooltip_text(TTR("Reload the played scene.")); } else { play_button->set_pressed(true); play_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); + play_button->set_tooltip_text(TTR("Reload the played scene.")); } stop_button->set_disabled(false); @@ -2483,9 +2531,22 @@ void EditorNode::_run_native(const Ref<EditorExportPreset> &p_preset) { } } +void EditorNode::_reset_play_buttons() { + play_button->set_pressed(false); + play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); + play_button->set_tooltip_text(TTR("Play the project.")); + play_scene_button->set_pressed(false); + play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons"))); + play_scene_button->set_tooltip_text(TTR("Play the edited scene.")); + play_custom_scene_button->set_pressed(false); + play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons"))); + play_custom_scene_button->set_tooltip_text(TTR("Play a custom scene.")); +} + void EditorNode::_android_build_source_selected(const String &p_file) { export_template_manager->install_android_template_from_file(p_file); } + void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { if (!p_confirmed) { // FIXME: this may be a hack. current_menu_option = (MenuOptions)p_option; @@ -2711,9 +2772,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) { log->add_message(TTR("Can't undo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR); } else { - String action = editor_data.get_undo_redo().get_current_action_name(); + String action = editor_data.get_undo_redo()->get_current_action_name(); - if (!editor_data.get_undo_redo().undo()) { + if (!editor_data.get_undo_redo()->undo()) { log->add_message(TTR("Nothing to undo."), EditorLog::MSG_TYPE_EDITOR); } else if (!action.is_empty()) { log->add_message(vformat(TTR("Undo: %s"), action), EditorLog::MSG_TYPE_EDITOR); @@ -2724,10 +2785,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { if ((int)Input::get_singleton()->get_mouse_button_mask() & 0x7) { log->add_message(TTR("Can't redo while mouse buttons are pressed."), EditorLog::MSG_TYPE_EDITOR); } else { - if (!editor_data.get_undo_redo().redo()) { + if (!editor_data.get_undo_redo()->redo()) { log->add_message(TTR("Nothing to redo."), EditorLog::MSG_TYPE_EDITOR); } else { - String action = editor_data.get_undo_redo().get_current_action_name(); + String action = editor_data.get_undo_redo()->get_current_action_name(); log->add_message(vformat(TTR("Redo: %s"), action), EditorLog::MSG_TYPE_EDITOR); } } @@ -2762,7 +2823,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { ERR_PRINT("Failed to load scene"); } editor_data.move_edited_scene_to_index(cur_idx); - get_undo_redo()->clear_history(false); + get_undo_redo()->clear_history(false, editor_data.get_current_edited_scene_history_id()); scene_tabs->set_current_tab(cur_idx); } break; @@ -2777,7 +2838,7 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { quick_run->set_title(TTR("Quick Run Scene...")); play_custom_scene_button->set_pressed(false); } else { - String last_custom_scene = run_custom_filename; + String last_custom_scene = run_custom_filename; // This is necessary to have a copy of the string. run_play_custom(last_custom_scene); } @@ -2789,13 +2850,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { editor_run.stop(); run_custom_filename.clear(); - play_button->set_pressed(false); - play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); - play_scene_button->set_pressed(false); - play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons"))); - play_custom_scene_button->set_pressed(false); - play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons"))); + run_current_filename.clear(); stop_button->set_disabled(true); + _reset_play_buttons(); if (bool(EDITOR_GET("run/output/always_close_output_on_stop"))) { for (int i = 0; i < bottom_panel_items.size(); i++) { @@ -2818,7 +2875,12 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case RUN_PLAY_SCENE: { - run_play_current(); + if (run_current_filename.is_empty() || editor_run.get_status() == EditorRun::STATUS_STOP) { + run_play_current(); + } else { + String last_current_scene = run_current_filename; // This is necessary to have a copy of the string. + run_play_custom(last_current_scene); + } } break; case RUN_SETTINGS: { @@ -3075,8 +3137,7 @@ int EditorNode::_next_unsaved_scene(bool p_valid_filename, int p_start) { if (!editor_data.get_edited_scene_root(i)) { continue; } - int current = editor_data.get_edited_scene(); - bool unsaved = (i == current) ? saved_version != editor_data.get_undo_redo().get_version() : editor_data.get_scene_version(i) != 0; + bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(i)); if (unsaved) { String scene_filename = editor_data.get_edited_scene_root(i)->get_scene_file_path(); if (p_valid_filename && scene_filename.length() == 0) { @@ -3162,6 +3223,9 @@ void EditorNode::_discard_changes(const String &p_str) { String exec = OS::get_singleton()->get_executable_path(); List<String> args; + for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) { + args.push_back(a); + } args.push_back("--path"); args.push_back(exec.get_base_dir()); args.push_back("--project-manager"); @@ -3181,17 +3245,15 @@ void EditorNode::_update_file_menu_opened() { Ref<Shortcut> reopen_closed_scene_sc = ED_GET_SHORTCUT("editor/reopen_closed_scene"); reopen_closed_scene_sc->set_name(TTR("Reopen Closed Scene")); - PopupMenu *pop = file_menu->get_popup(); - pop->set_item_disabled(pop->get_item_index(FILE_OPEN_PREV), previous_scenes.is_empty()); + file_menu->set_item_disabled(file_menu->get_item_index(FILE_OPEN_PREV), previous_scenes.is_empty()); - const UndoRedo &undo_redo = editor_data.get_undo_redo(); - pop->set_item_disabled(pop->get_item_index(EDIT_UNDO), !undo_redo.has_undo()); - pop->set_item_disabled(pop->get_item_index(EDIT_REDO), !undo_redo.has_redo()); + Ref<EditorUndoRedoManager> undo_redo = editor_data.get_undo_redo(); + file_menu->set_item_disabled(file_menu->get_item_index(EDIT_UNDO), !undo_redo->has_undo()); + file_menu->set_item_disabled(file_menu->get_item_index(EDIT_REDO), !undo_redo->has_redo()); } void EditorNode::_update_file_menu_closed() { - PopupMenu *pop = file_menu->get_popup(); - pop->set_item_disabled(pop->get_item_index(FILE_OPEN_PREV), false); + file_menu->set_item_disabled(file_menu->get_item_index(FILE_OPEN_PREV), false); } Control *EditorNode::get_main_control() { @@ -3443,7 +3505,6 @@ void EditorNode::_remove_edited_scene(bool p_change_tab) { _scene_tab_changed(new_index); } editor_data.remove_scene(old_index); - editor_data.get_undo_redo().clear_history(false); _update_title(); _update_scene_tabs(); } @@ -3499,7 +3560,6 @@ Dictionary EditorNode::_get_main_scene_state() { state["main_tab"] = _get_current_main_editor(); state["scene_tree_offset"] = SceneTreeDock::get_singleton()->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value(); state["property_edit_offset"] = InspectorDock::get_inspector_singleton()->get_scroll_offset(); - state["saved_version"] = saved_version; state["node_filter"] = SceneTreeDock::get_singleton()->get_filter(); return state; } @@ -3559,11 +3619,6 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) { editor_data.notify_edited_scene_changed(); } -void EditorNode::set_current_version(uint64_t p_version) { - saved_version = p_version; - editor_data.set_edited_scene_version(p_version); -} - bool EditorNode::is_changing_scene() const { return changing_scene; } @@ -3583,7 +3638,7 @@ void EditorNode::set_current_scene(int p_idx) { editor_folding.load_scene_folding(editor_data.get_edited_scene_root(p_idx), editor_data.get_scene_path(p_idx)); } - call_deferred(SNAME("_clear_undo_history")); + get_undo_redo()->clear_history(false, editor_data.get_scene_history_id(p_idx)); } changing_scene = true; @@ -3600,8 +3655,8 @@ void EditorNode::set_current_scene(int p_idx) { Node *new_scene = editor_data.get_edited_scene_root(); - if (Object::cast_to<Popup>(new_scene)) { - Object::cast_to<Popup>(new_scene)->show(); + if (Popup *p = Object::cast_to<Popup>(new_scene)) { + p->show(); } SceneTreeDock::get_singleton()->set_edited_scene(new_scene); @@ -3619,6 +3674,7 @@ void EditorNode::set_current_scene(int p_idx) { _edit_current(true); _update_title(); + _update_scene_tabs(); call_deferred(SNAME("_set_main_scene_state"), state, get_edited_scene()); // Do after everything else is done setting up. } @@ -3646,6 +3702,18 @@ void EditorNode::fix_dependencies(const String &p_for_file) { int EditorNode::new_scene() { int idx = editor_data.add_edited_scene(-1); + // Remove placeholder empty scene. + if (editor_data.get_edited_scene_count() > 1) { + for (int i = 0; i < editor_data.get_edited_scene_count() - 1; i++) { + bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(i)); + if (!unsaved && editor_data.get_scene_path(i).is_empty()) { + editor_data.remove_scene(i); + idx--; + } + } + } + idx = MAX(idx, 0); + _scene_tab_changed(idx); editor_data.clear_editor_states(); _update_scene_tabs(); @@ -3777,7 +3845,6 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b set_edited_scene(new_scene); _get_scene_metadata(p_scene); - saved_version = editor_data.get_undo_redo().get_version(); _update_title(); _update_scene_tabs(); _add_to_recent_scenes(lpath); @@ -3820,6 +3887,37 @@ void EditorNode::edit_foreign_resource(Ref<Resource> p_resource) { InspectorDock::get_singleton()->call_deferred("edit_resource", p_resource); } +bool EditorNode::is_resource_read_only(Ref<Resource> p_resource) { + ERR_FAIL_COND_V(p_resource.is_null(), false); + + String path = p_resource->get_path(); + if (!path.is_resource_file()) { + // If the resource name contains '::', that means it is a subresource embedded in another resource. + int srpos = path.find("::"); + if (srpos != -1) { + String base = path.substr(0, srpos); + // If the base resource is a packed scene, we treat it as read-only if it is not the currently edited scene. + if (ResourceLoader::get_resource_type(base) == "PackedScene") { + if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) { + return true; + } + } else { + // If a corresponding .import file exists for the base file, we assume it to be imported and should therefore treated as read-only. + if (FileAccess::exists(base + ".import")) { + return true; + } + } + } + } else { + // The resource is not a subresource, but if it has an .import file, it's imported so treat it as read only. + if (FileAccess::exists(path + ".import")) { + return true; + } + } + + return false; +} + void EditorNode::request_instance_scene(const String &p_path) { SceneTreeDock::get_singleton()->instantiate(p_path); } @@ -3828,6 +3926,10 @@ void EditorNode::request_instantiate_scenes(const Vector<String> &p_files) { SceneTreeDock::get_singleton()->instantiate_scenes(p_files); } +Ref<EditorUndoRedoManager> &EditorNode::get_undo_redo() { + return singleton->editor_data.get_undo_redo(); +} + void EditorNode::_inherit_request(String p_file) { current_menu_option = FILE_NEW_INHERITED_SCENE; _dialog_action(p_file); @@ -3988,6 +4090,7 @@ void EditorNode::register_editor_types() { GDREGISTER_CLASS(EditorSpinSlider); GDREGISTER_CLASS(EditorResourcePicker); GDREGISTER_CLASS(EditorScriptPicker); + GDREGISTER_ABSTRACT_CLASS(EditorUndoRedoManager); GDREGISTER_ABSTRACT_CLASS(FileSystemDock); GDREGISTER_VIRTUAL_CLASS(EditorFileSystemImportFormatSupportQuery); @@ -4102,8 +4205,15 @@ void EditorNode::_pick_main_scene_custom_action(const String &p_custom_action_na } pick_main_scene->hide(); - current_menu_option = SETTINGS_PICK_MAIN_SCENE; - _dialog_action(scene->get_scene_file_path()); + + if (!FileAccess::exists(scene->get_scene_file_path())) { + current_menu_option = FILE_SAVE_AND_RUN_MAIN_SCENE; + _menu_option_confirm(FILE_SAVE_AS_SCENE, true); + file->set_title(TTR("Save scene before running...")); + } else { + current_menu_option = SETTINGS_PICK_MAIN_SCENE; + _dialog_action(scene->get_scene_file_path()); + } } } @@ -4303,6 +4413,15 @@ void EditorNode::_editor_file_dialog_unregister(EditorFileDialog *p_dialog) { Vector<EditorNodeInitCallback> EditorNode::_init_callbacks; +void EditorNode::_begin_first_scan() { + Engine::get_singleton()->startup_benchmark_begin_measure("editor_scan_and_import"); + EditorFileSystem::get_singleton()->scan(); +} +void EditorNode::set_use_startup_benchmark(bool p_use_startup_benchmark, const String &p_startup_benchmark_file) { + use_startup_benchmark = p_use_startup_benchmark; + startup_benchmark_file = p_startup_benchmark_file; +} + Error EditorNode::export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only) { export_defer.preset = p_preset; export_defer.path = p_path; @@ -4977,8 +5096,9 @@ void EditorNode::run_play_current() { } void EditorNode::run_play_custom(const String &p_custom) { + bool is_current = !run_current_filename.is_empty(); _menu_option_confirm(RUN_STOP, true); - _run(false, p_custom); + _run(is_current, p_custom); } void EditorNode::run_stop() { @@ -5113,9 +5233,7 @@ void EditorNode::_scene_tab_closed(int p_tab, int option) { return; } - bool unsaved = (p_tab == editor_data.get_edited_scene()) - ? saved_version != editor_data.get_undo_redo().get_version() - : editor_data.get_scene_version(p_tab) != 0; + bool unsaved = get_undo_redo()->is_history_unsaved(editor_data.get_scene_history_id(p_tab)); if (unsaved) { save_confirmation->set_ok_button_text(TTR("Save & Close")); save_confirmation->set_text(vformat(TTR("Save changes to '%s' before closing?"), !scene->get_scene_file_path().is_empty() ? scene->get_scene_file_path() : "unsaved scene")); @@ -5227,23 +5345,10 @@ void EditorNode::_thumbnail_done(const String &p_path, const Ref<Texture2D> &p_p void EditorNode::_scene_tab_changed(int p_tab) { tab_preview_panel->hide(); - bool unsaved = (saved_version != editor_data.get_undo_redo().get_version()); - if (p_tab == editor_data.get_edited_scene()) { return; // Pointless. } - - uint64_t next_scene_version = editor_data.get_scene_version(p_tab); - - editor_data.get_undo_redo().create_action(TTR("Switch Scene Tab")); - editor_data.get_undo_redo().add_do_method(this, "set_current_version", unsaved ? saved_version : 0); - editor_data.get_undo_redo().add_do_method(this, "set_current_scene", p_tab); - editor_data.get_undo_redo().add_do_method(this, "set_current_version", next_scene_version == 0 ? editor_data.get_undo_redo().get_version() + 1 : next_scene_version); - - editor_data.get_undo_redo().add_undo_method(this, "set_current_version", next_scene_version); - editor_data.get_undo_redo().add_undo_method(this, "set_current_scene", editor_data.get_edited_scene()); - editor_data.get_undo_redo().add_undo_method(this, "set_current_version", saved_version); - editor_data.get_undo_redo().commit_action(); + set_current_scene(p_tab); } Button *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) { @@ -5629,7 +5734,7 @@ void EditorNode::reload_scene(const String &p_path) { if (scene_idx == -1) { if (get_edited_scene()) { // Scene is not open, so at it might be instantiated. We'll refresh the whole scene later. - editor_data.get_undo_redo().clear_history(); + editor_data.get_undo_redo()->clear_history(false, editor_data.get_current_edited_scene_history_id()); } return; } @@ -5645,7 +5750,7 @@ void EditorNode::reload_scene(const String &p_path) { // Adjust index so tab is back a the previous position. editor_data.move_edited_scene_to_index(scene_idx); - get_undo_redo()->clear_history(); + get_undo_redo()->clear_history(false, editor_data.get_scene_history_id(scene_idx)); // Recover the tab. scene_tabs->set_current_tab(current_tab); @@ -5823,13 +5928,13 @@ void EditorNode::_bind_methods() { ClassDB::bind_method("set_edited_scene", &EditorNode::set_edited_scene); ClassDB::bind_method("open_request", &EditorNode::open_request); ClassDB::bind_method("edit_foreign_resource", &EditorNode::edit_foreign_resource); + ClassDB::bind_method("is_resource_read_only", &EditorNode::is_resource_read_only); ClassDB::bind_method("_close_messages", &EditorNode::_close_messages); ClassDB::bind_method("_show_messages", &EditorNode::_show_messages); ClassDB::bind_method("stop_child_process", &EditorNode::stop_child_process); ClassDB::bind_method("set_current_scene", &EditorNode::set_current_scene); - ClassDB::bind_method("set_current_version", &EditorNode::set_current_version); ClassDB::bind_method("_thumbnail_done", &EditorNode::_thumbnail_done); ClassDB::bind_method("_set_main_scene_state", &EditorNode::_set_main_scene_state); ClassDB::bind_method("_update_recent_scenes", &EditorNode::_update_recent_scenes); @@ -6130,7 +6235,7 @@ EditorNode::EditorNode() { rmp.instantiate(); EditorInspector::add_inspector_plugin(rmp); - Ref<EditorInspectorShaderModePlugin> smp; + Ref<EditorInspectorVisualShaderModePlugin> smp; smp.instantiate(); EditorInspector::add_inspector_plugin(smp); } @@ -6153,6 +6258,7 @@ EditorNode::EditorNode() { add_child(editor_export); // Exporters might need the theme. + EditorColorMap::create(); theme = create_custom_theme(); register_exporters(); @@ -6220,7 +6326,7 @@ EditorNode::EditorNode() { main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 8); main_vbox->add_theme_constant_override("separation", 8 * EDSCALE); - menu_hb = memnew(HBoxContainer); + menu_hb = memnew(EditorTitleBar); main_vbox->add_child(menu_hb); left_l_hsplit = memnew(HSplitContainer); @@ -6409,7 +6515,7 @@ EditorNode::EditorNode() { scene_tab_add = memnew(Button); scene_tab_add->set_flat(true); - scene_tab_add->set_tooltip(TTR("Add a new scene.")); + scene_tab_add->set_tooltip_text(TTR("Add a new scene.")); scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); scene_tab_add->add_theme_color_override("icon_normal_color", Color(0.6f, 0.6f, 0.6f, 0.8f)); scene_tabs->add_child(scene_tab_add); @@ -6425,7 +6531,7 @@ EditorNode::EditorNode() { ED_SHORTCUT_AND_COMMAND("editor/distraction_free_mode", TTR("Distraction Free Mode"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::F11); ED_SHORTCUT_OVERRIDE("editor/distraction_free_mode", "macos", KeyModifierMask::CMD | KeyModifierMask::CTRL | Key::D); distraction_free->set_shortcut(ED_GET_SHORTCUT("editor/distraction_free_mode")); - distraction_free->set_tooltip(TTR("Toggle distraction-free mode.")); + distraction_free->set_tooltip_text(TTR("Toggle distraction-free mode.")); distraction_free->connect("pressed", callable_mp(this, &EditorNode::_toggle_distraction_free_mode)); distraction_free->set_icon(gui_base->get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons"))); distraction_free->set_toggle_mode(true); @@ -6450,20 +6556,34 @@ EditorNode::EditorNode() { main_control->add_theme_constant_override("separation", 0); scene_root_parent->add_child(main_control); - HBoxContainer *left_menu_hb = memnew(HBoxContainer); - menu_hb->add_child(left_menu_hb); + bool global_menu = !bool(EDITOR_GET("interface/editor/use_embedded_menu")) && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU); + bool can_expand = bool(EDITOR_GET("interface/editor/expand_to_title")) && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_EXTEND_TO_TITLE); - file_menu = memnew(MenuButton); - file_menu->set_flat(false); - file_menu->set_switch_on_hover(true); - file_menu->set_text(TTR("Scene")); - file_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - left_menu_hb->add_child(file_menu); + if (can_expand) { + // Add spacer to avoid other controls under window minimize/maximize/close buttons (left side). + Control *menu_spacer = memnew(Control); + menu_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS); + menu_spacer->set_custom_minimum_size(Size2(DisplayServer::get_singleton()->window_get_safe_title_margins(DisplayServer::MAIN_WINDOW_ID).x, 0)); + menu_hb->add_child(menu_spacer); + } + + main_menu = memnew(MenuBar); + menu_hb->add_child(main_menu); + main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); + main_menu->set_flat(true); + main_menu->set_start_index(0); // Main menu, add to the start of global menu. + main_menu->set_prefer_global_menu(global_menu); + main_menu->set_switch_on_hover(true); + + file_menu = memnew(PopupMenu); + file_menu->set_name(TTR("Scene")); + main_menu->add_child(file_menu); + main_menu->set_menu_tooltip(0, TTR("Operations with scene files.")); prev_scene = memnew(Button); prev_scene->set_flat(true); prev_scene->set_icon(gui_base->get_theme_icon(SNAME("PrevScene"), SNAME("EditorIcons"))); - prev_scene->set_tooltip(TTR("Go to previously opened scene.")); + prev_scene->set_tooltip_text(TTR("Go to previously opened scene.")); prev_scene->set_disabled(true); prev_scene->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(FILE_OPEN_PREV)); gui_base->add_child(prev_scene); @@ -6528,84 +6648,75 @@ EditorNode::EditorNode() { command_palette->set_title(TTR("Command Palette")); gui_base->add_child(command_palette); - PopupMenu *p; - - file_menu->set_tooltip(TTR("Operations with scene files.")); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/new_scene", TTR("New Scene"), KeyModifierMask::CMD + Key::N), FILE_NEW_SCENE); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/new_inherited_scene", TTR("New Inherited Scene..."), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::N), FILE_NEW_INHERITED_SCENE); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/open_scene", TTR("Open Scene..."), KeyModifierMask::CMD + Key::O), FILE_OPEN_SCENE); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reopen_closed_scene", TTR("Reopen Closed Scene"), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::T), FILE_OPEN_PREV); + file_menu->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT); - p = file_menu->get_popup(); + file_menu->add_separator(); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_scene", TTR("Save Scene"), KeyModifierMask::CMD + Key::S), FILE_SAVE_SCENE); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_scene_as", TTR("Save Scene As..."), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::S), FILE_SAVE_AS_SCENE); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_all_scenes", TTR("Save All Scenes"), KeyModifierMask::CMD + KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::S), FILE_SAVE_ALL_SCENES); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/new_scene", TTR("New Scene"), KeyModifierMask::CMD + Key::N), FILE_NEW_SCENE); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/new_inherited_scene", TTR("New Inherited Scene..."), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::N), FILE_NEW_INHERITED_SCENE); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/open_scene", TTR("Open Scene..."), KeyModifierMask::CMD + Key::O), FILE_OPEN_SCENE); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reopen_closed_scene", TTR("Reopen Closed Scene"), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::T), FILE_OPEN_PREV); - p->add_submenu_item(TTR("Open Recent"), "RecentScenes", FILE_OPEN_RECENT); + file_menu->add_separator(); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_scene", TTR("Save Scene"), KeyModifierMask::CMD + Key::S), FILE_SAVE_SCENE); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_scene_as", TTR("Save Scene As..."), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::S), FILE_SAVE_AS_SCENE); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/save_all_scenes", TTR("Save All Scenes"), KeyModifierMask::CMD + KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::S), FILE_SAVE_ALL_SCENES); - - p->add_separator(); - - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open", TTR("Quick Open..."), KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::O), FILE_QUICK_OPEN); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open_scene", TTR("Quick Open Scene..."), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::O), FILE_QUICK_OPEN_SCENE); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open_script", TTR("Quick Open Script..."), KeyModifierMask::CMD + KeyModifierMask::ALT + Key::O), FILE_QUICK_OPEN_SCRIPT); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open", TTR("Quick Open..."), KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::O), FILE_QUICK_OPEN); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open_scene", TTR("Quick Open Scene..."), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::O), FILE_QUICK_OPEN_SCENE); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/quick_open_script", TTR("Quick Open Script..."), KeyModifierMask::CMD + KeyModifierMask::ALT + Key::O), FILE_QUICK_OPEN_SCRIPT); - p->add_separator(); + file_menu->add_separator(); export_as_menu = memnew(PopupMenu); export_as_menu->set_name("Export"); - p->add_child(export_as_menu); - p->add_submenu_item(TTR("Export As..."), "Export"); + file_menu->add_child(export_as_menu); + file_menu->add_submenu_item(TTR("Export As..."), "Export"); export_as_menu->add_shortcut(ED_SHORTCUT("editor/export_as_mesh_library", TTR("MeshLibrary...")), FILE_EXPORT_MESH_LIBRARY); export_as_menu->connect("index_pressed", callable_mp(this, &EditorNode::_export_as_menu_option)); - p->add_separator(); - p->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true); - p->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true); + file_menu->add_separator(); + file_menu->add_shortcut(ED_GET_SHORTCUT("ui_undo"), EDIT_UNDO, true); + file_menu->add_shortcut(ED_GET_SHORTCUT("ui_redo"), EDIT_REDO, true); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/close_scene", TTR("Close Scene"), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::W), FILE_CLOSE); + file_menu->add_separator(); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/reload_saved_scene", TTR("Reload Saved Scene")), EDIT_RELOAD_SAVED_SCENE); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/close_scene", TTR("Close Scene"), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::W), FILE_CLOSE); recent_scenes = memnew(PopupMenu); recent_scenes->set_name("RecentScenes"); - p->add_child(recent_scenes); + file_menu->add_child(recent_scenes); recent_scenes->connect("id_pressed", callable_mp(this, &EditorNode::_open_recent_scene)); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/file_quit", TTR("Quit"), KeyModifierMask::CMD + Key::Q), FILE_QUIT, true); - - project_menu = memnew(MenuButton); - project_menu->set_flat(false); - project_menu->set_switch_on_hover(true); - project_menu->set_tooltip(TTR("Miscellaneous project or scene-wide tools.")); - project_menu->set_text(TTR("Project")); - project_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - left_menu_hb->add_child(project_menu); + if (!global_menu || !OS::get_singleton()->has_feature("macos")) { + // On macOS "Quit" and "About" options are in the "app" menu. + file_menu->add_separator(); + file_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/file_quit", TTR("Quit"), KeyModifierMask::CMD + Key::Q), FILE_QUIT, true); + } - p = project_menu->get_popup(); + project_menu = memnew(PopupMenu); + project_menu->set_name(TTR("Project")); + main_menu->add_child(project_menu); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/project_settings", TTR("Project Settings..."), Key::NONE, TTR("Project Settings")), RUN_SETTINGS); - p->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); + project_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/project_settings", TTR("Project Settings..."), Key::NONE, TTR("Project Settings")), RUN_SETTINGS); + project_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); vcs_actions_menu = VersionControlEditorPlugin::get_singleton()->get_version_control_actions_panel(); vcs_actions_menu->set_name("Version Control"); vcs_actions_menu->connect("index_pressed", callable_mp(this, &EditorNode::_version_control_menu_option)); - p->add_separator(); - p->add_child(vcs_actions_menu); - p->add_submenu_item(TTR("Version Control"), "Version Control"); + project_menu->add_separator(); + project_menu->add_child(vcs_actions_menu); + project_menu->add_submenu_item(TTR("Version Control"), "Version Control"); vcs_actions_menu->add_item(TTR("Create Version Control Metadata"), RUN_VCS_METADATA); vcs_actions_menu->add_item(TTR("Set Up Version Control"), RUN_VCS_SETTINGS); vcs_actions_menu->add_item(TTR("Shut Down Version Control"), RUN_VCS_SHUT_DOWN); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/export", TTR("Export..."), Key::NONE, TTR("Export")), FILE_EXPORT_PROJECT); - p->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE); - p->add_item(TTR("Open User Data Folder"), RUN_USER_DATA_FOLDER); + project_menu->add_separator(); + project_menu->add_shortcut(ED_SHORTCUT_AND_COMMAND("editor/export", TTR("Export..."), Key::NONE, TTR("Export")), FILE_EXPORT_PROJECT); + project_menu->add_item(TTR("Install Android Build Template..."), FILE_INSTALL_ANDROID_SOURCE); + project_menu->add_item(TTR("Open User Data Folder"), RUN_USER_DATA_FOLDER); - p->add_separator(); - p->add_item(TTR("Customize Engine Build Configuration..."), TOOLS_BUILD_PROFILE_MANAGER); - p->add_separator(); + project_menu->add_separator(); + project_menu->add_item(TTR("Customize Engine Build Configuration..."), TOOLS_BUILD_PROFILE_MANAGER); + project_menu->add_separator(); plugin_config_dialog = memnew(PluginConfigDialog); plugin_config_dialog->connect("plugin_ready", callable_mp(this, &EditorNode::_on_plugin_ready)); @@ -6614,15 +6725,20 @@ EditorNode::EditorNode() { tool_menu = memnew(PopupMenu); tool_menu->set_name("Tools"); tool_menu->connect("index_pressed", callable_mp(this, &EditorNode::_tool_menu_option)); - p->add_child(tool_menu); - p->add_submenu_item(TTR("Tools"), "Tools"); + project_menu->add_child(tool_menu); + project_menu->add_submenu_item(TTR("Tools"), "Tools"); tool_menu->add_item(TTR("Orphan Resource Explorer..."), TOOLS_ORPHAN_RESOURCES); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("editor/reload_current_project", TTR("Reload Current Project")), RELOAD_CURRENT_PROJECT); + project_menu->add_separator(); + project_menu->add_shortcut(ED_SHORTCUT("editor/reload_current_project", TTR("Reload Current Project")), RELOAD_CURRENT_PROJECT); ED_SHORTCUT_AND_COMMAND("editor/quit_to_project_list", TTR("Quit to Project List"), KeyModifierMask::CMD + KeyModifierMask::SHIFT + Key::Q); ED_SHORTCUT_OVERRIDE("editor/quit_to_project_list", "macos", KeyModifierMask::SHIFT + KeyModifierMask::ALT + Key::Q); - p->add_shortcut(ED_GET_SHORTCUT("editor/quit_to_project_list"), RUN_PROJECT_MANAGER, true); + project_menu->add_shortcut(ED_GET_SHORTCUT("editor/quit_to_project_list"), RUN_PROJECT_MANAGER, true); + + // Spacer to center 2D / 3D / Script buttons. + Control *left_spacer = memnew(Control); + left_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS); + menu_hb->add_child(left_spacer); menu_hb->add_spacer(); @@ -6630,85 +6746,81 @@ EditorNode::EditorNode() { menu_hb->add_child(main_editor_button_vb); // Options are added and handled by DebuggerEditorPlugin. - debug_menu = memnew(MenuButton); - debug_menu->set_flat(false); - debug_menu->set_switch_on_hover(true); - debug_menu->set_text(TTR("Debug")); - debug_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - left_menu_hb->add_child(debug_menu); + debug_menu = memnew(PopupMenu); + debug_menu->set_name(TTR("Debug")); + main_menu->add_child(debug_menu); menu_hb->add_spacer(); - settings_menu = memnew(MenuButton); - settings_menu->set_flat(false); - settings_menu->set_switch_on_hover(true); - settings_menu->set_text(TTR("Editor")); - settings_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - left_menu_hb->add_child(settings_menu); - - p = settings_menu->get_popup(); + settings_menu = memnew(PopupMenu); + settings_menu->set_name(TTR("Editor")); + main_menu->add_child(settings_menu); ED_SHORTCUT_AND_COMMAND("editor/editor_settings", TTR("Editor Settings...")); ED_SHORTCUT_OVERRIDE("editor/editor_settings", "macos", KeyModifierMask::CMD + Key::COMMA); - p->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); - p->add_shortcut(ED_SHORTCUT("editor/command_palette", TTR("Command Palette..."), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::P), HELP_COMMAND_PALETTE); - p->add_separator(); + settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/editor_settings"), SETTINGS_PREFERENCES); + settings_menu->add_shortcut(ED_SHORTCUT("editor/command_palette", TTR("Command Palette..."), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::P), HELP_COMMAND_PALETTE); + settings_menu->add_separator(); editor_layouts = memnew(PopupMenu); editor_layouts->set_name("Layouts"); - p->add_child(editor_layouts); + settings_menu->add_child(editor_layouts); editor_layouts->connect("id_pressed", callable_mp(this, &EditorNode::_layout_menu_option)); - p->add_submenu_item(TTR("Editor Layout"), "Layouts"); - p->add_separator(); + settings_menu->add_submenu_item(TTR("Editor Layout"), "Layouts"); + settings_menu->add_separator(); ED_SHORTCUT_AND_COMMAND("editor/take_screenshot", TTR("Take Screenshot"), KeyModifierMask::CTRL | Key::F12); ED_SHORTCUT_OVERRIDE("editor/take_screenshot", "macos", KeyModifierMask::CMD | Key::F12); - p->add_shortcut(ED_GET_SHORTCUT("editor/take_screenshot"), EDITOR_SCREENSHOT); + settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/take_screenshot"), EDITOR_SCREENSHOT); - p->set_item_tooltip(-1, TTR("Screenshots are stored in the Editor Data/Settings Folder.")); + settings_menu->set_item_tooltip(-1, TTR("Screenshots are stored in the Editor Data/Settings Folder.")); ED_SHORTCUT_AND_COMMAND("editor/fullscreen_mode", TTR("Toggle Fullscreen"), KeyModifierMask::SHIFT | Key::F11); ED_SHORTCUT_OVERRIDE("editor/fullscreen_mode", "macos", KeyModifierMask::CMD | KeyModifierMask::CTRL | Key::F); - p->add_shortcut(ED_GET_SHORTCUT("editor/fullscreen_mode"), SETTINGS_TOGGLE_FULLSCREEN); + settings_menu->add_shortcut(ED_GET_SHORTCUT("editor/fullscreen_mode"), SETTINGS_TOGGLE_FULLSCREEN); - p->add_separator(); + settings_menu->add_separator(); if (OS::get_singleton()->get_data_path() == OS::get_singleton()->get_config_path()) { // Configuration and data folders are located in the same place (Windows/MacOS). - p->add_item(TTR("Open Editor Data/Settings Folder"), SETTINGS_EDITOR_DATA_FOLDER); + settings_menu->add_item(TTR("Open Editor Data/Settings Folder"), SETTINGS_EDITOR_DATA_FOLDER); } else { // Separate configuration and data folders (Linux). - p->add_item(TTR("Open Editor Data Folder"), SETTINGS_EDITOR_DATA_FOLDER); - p->add_item(TTR("Open Editor Settings Folder"), SETTINGS_EDITOR_CONFIG_FOLDER); + settings_menu->add_item(TTR("Open Editor Data Folder"), SETTINGS_EDITOR_DATA_FOLDER); + settings_menu->add_item(TTR("Open Editor Settings Folder"), SETTINGS_EDITOR_CONFIG_FOLDER); } - p->add_separator(); + settings_menu->add_separator(); - p->add_item(TTR("Manage Editor Features..."), SETTINGS_MANAGE_FEATURE_PROFILES); - p->add_item(TTR("Manage Export Templates..."), SETTINGS_MANAGE_EXPORT_TEMPLATES); + settings_menu->add_item(TTR("Manage Editor Features..."), SETTINGS_MANAGE_FEATURE_PROFILES); + settings_menu->add_item(TTR("Manage Export Templates..."), SETTINGS_MANAGE_EXPORT_TEMPLATES); - help_menu = memnew(MenuButton); - help_menu->set_flat(false); - help_menu->set_switch_on_hover(true); - help_menu->set_text(TTR("Help")); - help_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles"))); - left_menu_hb->add_child(help_menu); + help_menu = memnew(PopupMenu); + help_menu->set_name(TTR("Help")); + main_menu->add_child(help_menu); - p = help_menu->get_popup(); - p->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); + help_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); ED_SHORTCUT_AND_COMMAND("editor/editor_help", TTR("Search Help"), Key::F1); ED_SHORTCUT_OVERRIDE("editor/editor_help", "macos", KeyModifierMask::ALT | Key::SPACE); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons")), ED_GET_SHORTCUT("editor/editor_help"), HELP_SEARCH); - p->add_separator(); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTR("Online Documentation")), HELP_DOCS); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/q&a", TTR("Questions & Answers")), HELP_QA); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/community", TTR("Community")), HELP_COMMUNITY); - p->add_separator(); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Godot"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/about", TTR("About Godot")), HELP_ABOUT); - p->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Heart"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/support_development", TTR("Support Godot Development")), HELP_SUPPORT_GODOT_DEVELOPMENT); + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("HelpSearch"), SNAME("EditorIcons")), ED_GET_SHORTCUT("editor/editor_help"), HELP_SEARCH); + help_menu->add_separator(); + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/online_docs", TTR("Online Documentation")), HELP_DOCS); + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/q&a", TTR("Questions & Answers")), HELP_QA); + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/report_a_bug", TTR("Report a Bug")), HELP_REPORT_A_BUG); + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/suggest_a_feature", TTR("Suggest a Feature")), HELP_SUGGEST_A_FEATURE); + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/send_docs_feedback", TTR("Send Docs Feedback")), HELP_SEND_DOCS_FEEDBACK); + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/community", TTR("Community")), HELP_COMMUNITY); + help_menu->add_separator(); + if (!global_menu || !OS::get_singleton()->has_feature("macos")) { + // On macOS "Quit" and "About" options are in the "app" menu. + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Godot"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/about", TTR("About Godot")), HELP_ABOUT); + } + help_menu->add_icon_shortcut(gui_base->get_theme_icon(SNAME("Heart"), SNAME("EditorIcons")), ED_SHORTCUT_AND_COMMAND("editor/support_development", TTR("Support Godot Development")), HELP_SUPPORT_GODOT_DEVELOPMENT); + + // Spacer to center 2D / 3D / Script buttons. + Control *right_spacer = memnew(Control); + right_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS); + menu_hb->add_child(right_spacer); HBoxContainer *play_hb = memnew(HBoxContainer); menu_hb->add_child(play_hb); @@ -6717,10 +6829,8 @@ EditorNode::EditorNode() { play_button->set_flat(true); play_hb->add_child(play_button); play_button->set_toggle_mode(true); - play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); play_button->set_focus_mode(Control::FOCUS_NONE); play_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY)); - play_button->set_tooltip(TTR("Play the project.")); ED_SHORTCUT_AND_COMMAND("editor/play", TTR("Play"), Key::F5); ED_SHORTCUT_OVERRIDE("editor/play", "macos", KeyModifierMask::CMD | Key::B); @@ -6731,7 +6841,7 @@ EditorNode::EditorNode() { pause_button->set_toggle_mode(true); pause_button->set_icon(gui_base->get_theme_icon(SNAME("Pause"), SNAME("EditorIcons"))); pause_button->set_focus_mode(Control::FOCUS_NONE); - pause_button->set_tooltip(TTR("Pause the scene execution for debugging.")); + pause_button->set_tooltip_text(TTR("Pause the scene execution for debugging.")); pause_button->set_disabled(true); play_hb->add_child(pause_button); @@ -6745,7 +6855,7 @@ EditorNode::EditorNode() { stop_button->set_focus_mode(Control::FOCUS_NONE); stop_button->set_icon(gui_base->get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); stop_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_STOP)); - stop_button->set_tooltip(TTR("Stop the scene.")); + stop_button->set_tooltip_text(TTR("Stop the scene.")); stop_button->set_disabled(true); ED_SHORTCUT("editor/stop", TTR("Stop"), Key::F8); @@ -6761,9 +6871,7 @@ EditorNode::EditorNode() { play_hb->add_child(play_scene_button); play_scene_button->set_toggle_mode(true); play_scene_button->set_focus_mode(Control::FOCUS_NONE); - play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons"))); play_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY_SCENE)); - play_scene_button->set_tooltip(TTR("Play the edited scene.")); ED_SHORTCUT_AND_COMMAND("editor/play_scene", TTR("Play Scene"), Key::F6); ED_SHORTCUT_OVERRIDE("editor/play_scene", "macos", KeyModifierMask::CMD | Key::R); @@ -6774,9 +6882,9 @@ EditorNode::EditorNode() { play_hb->add_child(play_custom_scene_button); play_custom_scene_button->set_toggle_mode(true); play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE); - play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons"))); play_custom_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY_CUSTOM_SCENE)); - play_custom_scene_button->set_tooltip(TTR("Play custom scene")); + + _reset_play_buttons(); ED_SHORTCUT_AND_COMMAND("editor/play_custom_scene", TTR("Play Custom Scene"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::F5); ED_SHORTCUT_OVERRIDE("editor/play_custom_scene", "macos", KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::R); @@ -6789,7 +6897,7 @@ EditorNode::EditorNode() { write_movie_button->set_pressed(false); write_movie_button->set_icon(gui_base->get_theme_icon(SNAME("MainMovieWrite"), SNAME("EditorIcons"))); write_movie_button->set_focus_mode(Control::FOCUS_NONE); - write_movie_button->set_tooltip(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file.")); + write_movie_button->set_tooltip_text(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file.")); // This button behaves differently, so color it as such. write_movie_button->add_theme_color_override("icon_normal_color", Color(1, 1, 1, 0.7)); @@ -6813,6 +6921,14 @@ EditorNode::EditorNode() { right_menu_hb->add_child(rendering_driver); + if (can_expand) { + // Add spacer to avoid other controls under the window minimize/maximize/close buttons (right side). + Control *menu_spacer = memnew(Control); + menu_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS); + menu_spacer->set_custom_minimum_size(Size2(DisplayServer::get_singleton()->window_get_safe_title_margins(DisplayServer::MAIN_WINDOW_ID).y, 0)); + menu_hb->add_child(menu_spacer); + } + // Only display the render drivers that are available for this display driver. int display_driver_idx = OS::get_singleton()->get_display_driver_id(); Vector<String> render_drivers = DisplayServer::get_create_function_rendering_drivers(display_driver_idx); @@ -6857,7 +6973,7 @@ EditorNode::EditorNode() { right_menu_hb->add_child(update_spinner); update_spinner->set_icon(gui_base->get_theme_icon(SNAME("Progress1"), SNAME("EditorIcons"))); update_spinner->get_popup()->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); - p = update_spinner->get_popup(); + PopupMenu *p = update_spinner->get_popup(); p->add_radio_check_item(TTR("Update Continuously"), SETTINGS_UPDATE_CONTINUOUSLY); p->add_radio_check_item(TTR("Update When Changed"), SETTINGS_UPDATE_WHEN_CHANGED); p->add_separator(); @@ -6968,7 +7084,7 @@ EditorNode::EditorNode() { // Fade out the version label to be less prominent, but still readable. version_btn->set_self_modulate(Color(1, 1, 1, 0.65)); version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); - version_btn->set_tooltip(TTR("Click to copy.")); + version_btn->set_tooltip_text(TTR("Click to copy.")); version_btn->connect("pressed", callable_mp(this, &EditorNode::_version_button_pressed)); version_info_vbc->add_child(version_btn); @@ -7077,11 +7193,11 @@ EditorNode::EditorNode() { gui_base->add_child(file_script); file_script->connect("file_selected", callable_mp(this, &EditorNode::_dialog_action)); - file_menu->get_popup()->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); + file_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); file_menu->connect("about_to_popup", callable_mp(this, &EditorNode::_update_file_menu_opened)); - file_menu->get_popup()->connect("popup_hide", callable_mp(this, &EditorNode::_update_file_menu_closed)); + file_menu->connect("popup_hide", callable_mp(this, &EditorNode::_update_file_menu_closed)); - settings_menu->get_popup()->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); + settings_menu->connect("id_pressed", callable_mp(this, &EditorNode::_menu_option)); file->connect("file_selected", callable_mp(this, &EditorNode::_dialog_action)); file_templates->connect("file_selected", callable_mp(this, &EditorNode::_dialog_action)); @@ -7229,7 +7345,7 @@ EditorNode::EditorNode() { canvas_item_mat_convert.instantiate(); resource_conversion_plugins.push_back(canvas_item_mat_convert); - Ref<ParticlesMaterialConversionPlugin> particles_mat_convert; + Ref<ParticleProcessMaterialConversionPlugin> particles_mat_convert; particles_mat_convert.instantiate(); resource_conversion_plugins.push_back(particles_mat_convert); @@ -7364,11 +7480,22 @@ EditorNode::EditorNode() { screenshot_timer = memnew(Timer); screenshot_timer->set_one_shot(true); - screenshot_timer->set_wait_time(settings_menu->get_popup()->get_submenu_popup_delay() + 0.1f); + screenshot_timer->set_wait_time(settings_menu->get_submenu_popup_delay() + 0.1f); screenshot_timer->connect("timeout", callable_mp(this, &EditorNode::_request_screenshot)); add_child(screenshot_timer); screenshot_timer->set_owner(get_owner()); + // Adjust spacers to center 2D / 3D / Script buttons. + int max_w = MAX(play_hb->get_minimum_size().x + right_menu_hb->get_minimum_size().x, main_menu->get_minimum_size().x); + left_spacer->set_custom_minimum_size(Size2(MAX(0, max_w - main_menu->get_minimum_size().x), 0)); + right_spacer->set_custom_minimum_size(Size2(MAX(0, max_w - play_hb->get_minimum_size().x - right_menu_hb->get_minimum_size().x), 0)); + + // Extend menu bar to window title. + if (can_expand) { + DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE, true, DisplayServer::MAIN_WINDOW_ID); + menu_hb->set_can_move_window(true); + } + String exec = OS::get_singleton()->get_executable_path(); // Save editor executable path for third-party tools. EditorSettings::get_singleton()->set_project_metadata("editor_metadata", "executable_path", exec); diff --git a/editor/editor_node.h b/editor/editor_node.h index 0201e84eaf..792d2fc879 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -35,6 +35,7 @@ #include "editor/editor_folding.h" #include "editor/editor_native_shader_source_visualizer.h" #include "editor/editor_run.h" +#include "editor/editor_title_bar.h" #include "editor/export/editor_export.h" #include "editor/inspector_dock.h" @@ -72,12 +73,14 @@ class EditorRun; class EditorRunNative; class EditorSettingsDialog; class EditorToaster; +class EditorUndoRedoManager; class ExportTemplateManager; class FileDialog; class FileSystemDock; class HSplitContainer; class ImportDock; class LinkButton; +class MenuBar; class MenuButton; class NodeDock; class OrphanResourcesDialog; @@ -141,6 +144,7 @@ private: FILE_SAVE_AS_SCENE, FILE_SAVE_ALL_SCENES, FILE_SAVE_AND_RUN, + FILE_SAVE_AND_RUN_MAIN_SCENE, FILE_SHOW_IN_FILESYSTEM, FILE_EXPORT_PROJECT, FILE_EXPORT_MESH_LIBRARY, @@ -319,13 +323,14 @@ private: HBoxContainer *bottom_hb = nullptr; Control *vp_base = nullptr; - HBoxContainer *menu_hb = nullptr; + EditorTitleBar *menu_hb = nullptr; Control *main_control = nullptr; - MenuButton *file_menu = nullptr; - MenuButton *project_menu = nullptr; - MenuButton *debug_menu = nullptr; - MenuButton *settings_menu = nullptr; - MenuButton *help_menu = nullptr; + MenuBar *main_menu = nullptr; + PopupMenu *file_menu = nullptr; + PopupMenu *project_menu = nullptr; + PopupMenu *debug_menu = nullptr; + PopupMenu *settings_menu = nullptr; + PopupMenu *help_menu = nullptr; PopupMenu *tool_menu = nullptr; PopupMenu *export_as_menu = nullptr; Button *export_button = nullptr; @@ -466,10 +471,9 @@ private: String _tmp_import_path; String external_file; String open_navigate; - String run_custom_filename; - uint64_t saved_version = 1; - uint64_t last_checked_version = 0; + String run_custom_filename; + String run_current_filename; DynamicFontImportSettings *fontdata_import_settings = nullptr; SceneImportSettings *scene_import_settings = nullptr; @@ -579,6 +583,7 @@ private: void _run(bool p_current = false, const String &p_custom = ""); void _run_native(const Ref<EditorExportPreset> &p_preset); + void _reset_play_buttons(); void _add_to_recent_scenes(const String &p_scene); void _update_recent_scenes(); @@ -679,6 +684,10 @@ private: void _bottom_panel_switch(bool p_enable, int p_idx); void _bottom_panel_raise_toggled(bool); + void _begin_first_scan(); + bool use_startup_benchmark = false; + String startup_benchmark_file; + protected: friend class FileSystemDock; @@ -702,7 +711,7 @@ public: static EditorLog *get_log() { return singleton->log; } static EditorData &get_editor_data() { return singleton->editor_data; } static EditorFolding &get_editor_folding() { return singleton->editor_folding; } - static UndoRedo *get_undo_redo() { return &singleton->editor_data.get_undo_redo(); } + static Ref<EditorUndoRedoManager> &get_undo_redo(); static HBoxContainer *get_menu_hb() { return singleton->menu_hb; } static VSplitContainer *get_top_split() { return singleton->top_split; } @@ -771,6 +780,8 @@ public: void open_request(const String &p_path); void edit_foreign_resource(Ref<Resource> p_resource); + bool is_resource_read_only(Ref<Resource> p_resource); + bool is_changing_scene() const; Control *get_main_control(); @@ -786,7 +797,6 @@ public: bool is_scene_open(const String &p_path); - void set_current_version(uint64_t p_version); void set_current_scene(int p_idx); void setup_color_picker(ColorPicker *picker); @@ -813,6 +823,7 @@ public: void _copy_warning(const String &p_str); + void set_use_startup_benchmark(bool p_use_startup_benchmark, const String &p_startup_benchmark_file); Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only); Control *get_gui_base() { return gui_base; } diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp index dc77b5fea9..b8b6d57c08 100644 --- a/editor/editor_path.cpp +++ b/editor/editor_path.cpp @@ -72,7 +72,7 @@ void EditorPath::_add_children_to_popup(Object *p_obj, int p_depth) { int index = sub_objects_menu->get_item_count(); sub_objects_menu->add_icon_item(icon, proper_name, objects.size()); - sub_objects_menu->set_item_horizontal_offset(index, p_depth * 10 * EDSCALE); + sub_objects_menu->set_item_indent(index, p_depth); objects.push_back(obj->get_instance_id()); _add_children_to_popup(obj, p_depth + 1); @@ -80,6 +80,11 @@ void EditorPath::_add_children_to_popup(Object *p_obj, int p_depth) { } void EditorPath::_show_popup() { + if (sub_objects_menu->is_visible()) { + sub_objects_menu->hide(); + return; + } + sub_objects_menu->clear(); Size2 size = get_size(); @@ -145,14 +150,14 @@ void EditorPath::update_path() { } current_object_label->set_text(" " + name); // An extra space so the text is not too close of the icon. - set_tooltip(obj->get_class()); + set_tooltip_text(obj->get_class()); } } } void EditorPath::clear_path() { set_disabled(true); - set_tooltip(""); + set_tooltip_text(""); current_object_label->set_text(""); current_object_icon->set_texture(nullptr); @@ -177,7 +182,6 @@ void EditorPath::_id_pressed(int p_idx) { void EditorPath::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { update_path(); @@ -226,5 +230,5 @@ EditorPath::EditorPath(EditorSelectionHistory *p_history) { sub_objects_menu->connect("about_to_popup", callable_mp(this, &EditorPath::_about_to_show)); sub_objects_menu->connect("id_pressed", callable_mp(this, &EditorPath::_id_pressed)); - set_tooltip(TTR("Open a list of sub-resources.")); + set_tooltip_text(TTR("Open a list of sub-resources.")); } diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 566c22f5a9..b0bd500ef8 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -35,6 +35,7 @@ #include "editor/editor_paths.h" #include "editor/editor_resource_preview.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/export/editor_export.h" #include "editor/filesystem_dock.h" #include "editor/plugins/canvas_item_editor_plugin.h" @@ -47,7 +48,7 @@ #include "scene/gui/popup_menu.h" #include "servers/rendering_server.h" -Array EditorInterface::_make_mesh_previews(const Array &p_meshes, int p_preview_size) { +TypedArray<Texture2D> EditorInterface::_make_mesh_previews(const Array &p_meshes, int p_preview_size) { Vector<Ref<Mesh>> meshes; for (int i = 0; i < p_meshes.size(); i++) { @@ -55,7 +56,7 @@ Array EditorInterface::_make_mesh_previews(const Array &p_meshes, int p_preview_ } Vector<Ref<Texture2D>> textures = make_mesh_previews(meshes, nullptr, p_preview_size); - Array ret; + TypedArray<Texture2D> ret; for (int i = 0; i < textures.size(); i++) { ret.push_back(textures[i]); } @@ -215,8 +216,8 @@ Node *EditorInterface::get_edited_scene_root() { return EditorNode::get_singleton()->get_edited_scene(); } -Array EditorInterface::get_open_scenes() const { - Array ret; +PackedStringArray EditorInterface::get_open_scenes() const { + PackedStringArray ret; Vector<EditorData::EditedScene> scenes = EditorNode::get_editor_data().get_edited_scenes(); int scns_amount = scenes.size(); @@ -312,6 +313,13 @@ void EditorInterface::set_distraction_free_mode(bool p_enter) { EditorNode::get_singleton()->set_distraction_free_mode(p_enter); } +void EditorInterface::restart_editor(bool p_save) { + if (p_save) { + EditorNode::get_singleton()->save_all_scenes(); + } + EditorNode::get_singleton()->restart_editor(); +} + bool EditorInterface::is_distraction_free_mode_enabled() const { return EditorNode::get_singleton()->is_distraction_free_mode_enabled(); } @@ -360,6 +368,7 @@ void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("save_scene"), &EditorInterface::save_scene); ClassDB::bind_method(D_METHOD("save_scene_as", "path", "with_preview"), &EditorInterface::save_scene_as, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("restart_editor", "save"), &EditorInterface::restart_editor, DEFVAL(true)); ClassDB::bind_method(D_METHOD("set_main_screen_editor", "name"), &EditorInterface::set_main_screen_editor); ClassDB::bind_method(D_METHOD("set_distraction_free_mode", "enter"), &EditorInterface::set_distraction_free_mode); @@ -445,7 +454,7 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C CanvasItemEditor::get_singleton()->get_bottom_split()->add_child(p_control); } break; - case CONTAINER_PROPERTY_EDITOR_BOTTOM: { + case CONTAINER_INSPECTOR_BOTTOM: { InspectorDock::get_singleton()->get_addon_area()->add_child(p_control); } break; @@ -498,7 +507,7 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati CanvasItemEditor::get_singleton()->get_bottom_split()->remove_child(p_control); } break; - case CONTAINER_PROPERTY_EDITOR_BOTTOM: { + case CONTAINER_INSPECTOR_BOTTOM: { InspectorDock::get_singleton()->get_addon_area()->remove_child(p_control); } break; @@ -885,7 +894,7 @@ void EditorPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("make_bottom_panel_item_visible", "item"), &EditorPlugin::make_bottom_panel_item_visible); ClassDB::bind_method(D_METHOD("hide_bottom_panel"), &EditorPlugin::hide_bottom_panel); - ClassDB::bind_method(D_METHOD("get_undo_redo"), &EditorPlugin::_get_undo_redo); + ClassDB::bind_method(D_METHOD("get_undo_redo"), &EditorPlugin::get_undo_redo); ClassDB::bind_method(D_METHOD("add_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::add_undo_redo_inspector_hook_callback); ClassDB::bind_method(D_METHOD("remove_undo_redo_inspector_hook_callback", "callable"), &EditorPlugin::remove_undo_redo_inspector_hook_callback); ClassDB::bind_method(D_METHOD("queue_save_layout"), &EditorPlugin::queue_save_layout); @@ -950,7 +959,7 @@ void EditorPlugin::_bind_methods() { BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_SIDE_LEFT); BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_SIDE_RIGHT); BIND_ENUM_CONSTANT(CONTAINER_CANVAS_EDITOR_BOTTOM); - BIND_ENUM_CONSTANT(CONTAINER_PROPERTY_EDITOR_BOTTOM); + BIND_ENUM_CONSTANT(CONTAINER_INSPECTOR_BOTTOM); BIND_ENUM_CONSTANT(CONTAINER_PROJECT_SETTING_TAB_LEFT); BIND_ENUM_CONSTANT(CONTAINER_PROJECT_SETTING_TAB_RIGHT); @@ -965,6 +974,10 @@ void EditorPlugin::_bind_methods() { BIND_ENUM_CONSTANT(DOCK_SLOT_MAX); } +Ref<EditorUndoRedoManager> EditorPlugin::get_undo_redo() { + return undo_redo; +} + EditorPluginCreateFunc EditorPlugins::creation_funcs[MAX_CREATE_FUNCS]; int EditorPlugins::creation_func_count = 0; diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h index 84c63d1021..8357f0960a 100644 --- a/editor/editor_plugin.h +++ b/editor/editor_plugin.h @@ -53,6 +53,7 @@ class EditorImportPlugin; class EditorExportPlugin; class EditorNode3DGizmoPlugin; class EditorResourcePreview; +class EditorUndoRedoManager; class EditorFileSystem; class EditorToolAddons; class EditorPaths; @@ -66,7 +67,7 @@ protected: static void _bind_methods(); static EditorInterface *singleton; - Array _make_mesh_previews(const Array &p_meshes, int p_preview_size); + TypedArray<Texture2D> _make_mesh_previews(const Array &p_meshes, int p_preview_size); public: static EditorInterface *get_singleton() { return singleton; } @@ -86,7 +87,7 @@ public: String get_playing_scene() const; Node *get_edited_scene_root(); - Array get_open_scenes() const; + PackedStringArray get_open_scenes() const; ScriptEditor *get_script_editor(); EditorCommandPalette *get_command_palette() const; @@ -116,6 +117,7 @@ public: Error save_scene(); void save_scene_as(const String &p_scene, bool p_with_preview = true); + void restart_editor(bool p_save = true); Vector<Ref<Texture2D>> make_mesh_previews(const Vector<Ref<Mesh>> &p_meshes, Vector<Transform3D> *p_transforms, int p_preview_size); @@ -129,9 +131,7 @@ public: class EditorPlugin : public Node { GDCLASS(EditorPlugin, Node); friend class EditorData; - UndoRedo *undo_redo = nullptr; - - UndoRedo *_get_undo_redo() { return undo_redo; } + Ref<EditorUndoRedoManager> undo_redo; bool input_event_forwarding_always_enabled = false; bool force_draw_over_forwarding_enabled = false; @@ -144,7 +144,7 @@ protected: void _notification(int p_what); static void _bind_methods(); - UndoRedo &get_undo_redo() { return *undo_redo; } + Ref<EditorUndoRedoManager> get_undo_redo(); void add_custom_type(const String &p_type, const String &p_base, const Ref<Script> &p_script, const Ref<Texture2D> &p_icon); void remove_custom_type(const String &p_type); @@ -184,7 +184,7 @@ public: CONTAINER_CANVAS_EDITOR_SIDE_LEFT, CONTAINER_CANVAS_EDITOR_SIDE_RIGHT, CONTAINER_CANVAS_EDITOR_BOTTOM, - CONTAINER_PROPERTY_EDITOR_BOTTOM, + CONTAINER_INSPECTOR_BOTTOM, CONTAINER_PROJECT_SETTING_TAB_LEFT, CONTAINER_PROJECT_SETTING_TAB_RIGHT, }; diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index f434df3a1e..c3fd46c511 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -99,6 +99,10 @@ void EditorPropertyText::set_string_name(bool p_enabled) { string_name = p_enabled; } +void EditorPropertyText::set_secret(bool p_enabled) { + text->set_secret(p_enabled); +} + void EditorPropertyText::set_placeholder(const String &p_string) { text->set_placeholder(p_string); } @@ -163,8 +167,7 @@ void EditorPropertyMultilineText::update_property() { void EditorPropertyMultilineText::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_THEME_CHANGED: { Ref<Texture2D> df = get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons")); open_big_text->set_icon(df); @@ -318,7 +321,6 @@ void EditorPropertyTextEnum::_bind_methods() { void EditorPropertyTextEnum::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { edit_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); accept_button->set_icon(get_theme_icon(SNAME("ImportCheck"), SNAME("EditorIcons"))); @@ -392,7 +394,7 @@ void EditorPropertyLocale::_locale_pressed() { void EditorPropertyLocale::update_property() { String locale_code = get_edited_object()->get(get_edited_property()); locale->set_text(locale_code); - locale->set_tooltip(locale_code); + locale->set_tooltip_text(locale_code); } void EditorPropertyLocale::setup(const String &p_hint_text) { @@ -400,7 +402,6 @@ void EditorPropertyLocale::setup(const String &p_hint_text) { void EditorPropertyLocale::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { locale_edit->set_icon(get_theme_icon(SNAME("Translation"), SNAME("EditorIcons"))); } break; @@ -481,7 +482,7 @@ void EditorPropertyPath::_path_pressed() { void EditorPropertyPath::update_property() { String full_path = get_edited_object()->get(get_edited_property()); path->set_text(full_path); - path->set_tooltip(full_path); + path->set_tooltip_text(full_path); } void EditorPropertyPath::setup(const Vector<String> &p_extensions, bool p_folder, bool p_global) { @@ -496,7 +497,6 @@ void EditorPropertyPath::set_save_mode() { void EditorPropertyPath::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { path_edit->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); } break; @@ -1149,7 +1149,6 @@ void EditorPropertyLayersGrid::_bind_methods() { void EditorPropertyLayers::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { button->set_normal_texture(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); button->set_pressed_texture(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); @@ -1458,7 +1457,7 @@ void EditorPropertyFloat::_value_changed(double val) { } if (angle_in_radians) { - val = Math::deg2rad(val); + val = Math::deg_to_rad(val); } emit_changed(get_edited_property(), val); } @@ -1466,7 +1465,7 @@ void EditorPropertyFloat::_value_changed(double val) { void EditorPropertyFloat::update_property() { double val = get_edited_object()->get(get_edited_property()); if (angle_in_radians) { - val = Math::rad2deg(val); + val = Math::rad_to_deg(val); } setting = true; spin->set_value(val); @@ -1673,8 +1672,7 @@ void EditorPropertyEasing::setup(bool p_positive_only, bool p_flip) { void EditorPropertyEasing::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_THEME_CHANGED: { preset->clear(); preset->add_icon_item(get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")), "Linear", EASING_LINEAR); preset->add_icon_item(get_theme_icon(SNAME("CurveIn"), SNAME("EditorIcons")), "Ease In", EASING_IN); @@ -1771,7 +1769,6 @@ void EditorPropertyVector2::_update_ratio() { void EditorPropertyVector2::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { linked->set_normal_texture(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons"))); linked->set_pressed_texture(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons"))); @@ -1881,7 +1878,6 @@ void EditorPropertyRect2::update_property() { void EditorPropertyRect2::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 4; i++) { @@ -1988,9 +1984,9 @@ void EditorPropertyVector3::_value_changed(double val, const String &p_name) { v3.y = spin[1]->get_value(); v3.z = spin[2]->get_value(); if (angle_in_radians) { - v3.x = Math::deg2rad(v3.x); - v3.y = Math::deg2rad(v3.y); - v3.z = Math::deg2rad(v3.z); + v3.x = Math::deg_to_rad(v3.x); + v3.y = Math::deg_to_rad(v3.y); + v3.z = Math::deg_to_rad(v3.z); } emit_changed(get_edited_property(), v3, p_name); } @@ -2022,9 +2018,9 @@ void EditorPropertyVector3::_update_ratio() { void EditorPropertyVector3::update_using_vector(Vector3 p_vector) { if (angle_in_radians) { - p_vector.x = Math::rad2deg(p_vector.x); - p_vector.y = Math::rad2deg(p_vector.y); - p_vector.z = Math::rad2deg(p_vector.z); + p_vector.x = Math::rad_to_deg(p_vector.x); + p_vector.y = Math::rad_to_deg(p_vector.y); + p_vector.z = Math::rad_to_deg(p_vector.z); } setting = true; spin[0]->set_value(p_vector.x); @@ -2039,9 +2035,9 @@ Vector3 EditorPropertyVector3::get_vector() { v3.y = spin[1]->get_value(); v3.z = spin[2]->get_value(); if (angle_in_radians) { - v3.x = Math::deg2rad(v3.x); - v3.y = Math::deg2rad(v3.y); - v3.z = Math::deg2rad(v3.z); + v3.x = Math::deg_to_rad(v3.x); + v3.y = Math::deg_to_rad(v3.y); + v3.z = Math::deg_to_rad(v3.z); } return v3; @@ -2049,7 +2045,6 @@ Vector3 EditorPropertyVector3::get_vector() { void EditorPropertyVector3::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { linked->set_normal_texture(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons"))); linked->set_pressed_texture(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons"))); @@ -2184,7 +2179,6 @@ void EditorPropertyVector2i::_update_ratio() { void EditorPropertyVector2i::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { linked->set_normal_texture(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons"))); linked->set_pressed_texture(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons"))); @@ -2294,7 +2288,6 @@ void EditorPropertyRect2i::update_property() { void EditorPropertyRect2i::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 4; i++) { @@ -2435,7 +2428,6 @@ void EditorPropertyVector3i::_update_ratio() { void EditorPropertyVector3i::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { linked->set_normal_texture(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons"))); linked->set_pressed_texture(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons"))); @@ -2547,7 +2539,6 @@ void EditorPropertyPlane::update_property() { void EditorPropertyPlane::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 4; i++) { @@ -2613,8 +2604,47 @@ void EditorPropertyQuaternion::_set_read_only(bool p_read_only) { for (int i = 0; i < 4; i++) { spin[i]->set_read_only(p_read_only); } + for (int i = 0; i < 3; i++) { + euler[i]->set_read_only(p_read_only); + } }; +void EditorPropertyQuaternion::_edit_custom_value() { + if (edit_button->is_pressed()) { + edit_custom_bc->show(); + for (int i = 0; i < 3; i++) { + euler[i]->grab_focus(); + } + } else { + edit_custom_bc->hide(); + for (int i = 0; i < 4; i++) { + spin[i]->grab_focus(); + } + } + update_property(); +} + +void EditorPropertyQuaternion::_custom_value_changed(double val) { + if (setting) { + return; + } + + edit_euler.x = euler[0]->get_value(); + edit_euler.y = euler[1]->get_value(); + edit_euler.z = euler[2]->get_value(); + + Vector3 v; + v.x = Math::deg_to_rad(edit_euler.x); + v.y = Math::deg_to_rad(edit_euler.y); + v.z = Math::deg_to_rad(edit_euler.z); + + Quaternion temp_q = Quaternion(v); + spin[0]->set_value(temp_q.x); + spin[1]->set_value(temp_q.y); + spin[2]->set_value(temp_q.z); + spin[3]->set_value(temp_q.w); +} + void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) { if (setting) { return; @@ -2625,9 +2655,18 @@ void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) p.y = spin[1]->get_value(); p.z = spin[2]->get_value(); p.w = spin[3]->get_value(); + emit_changed(get_edited_property(), p, p_name); } +bool EditorPropertyQuaternion::is_grabbing_euler() { + bool is_grabbing = false; + for (int i = 0; i < 3; i++) { + is_grabbing |= euler[i]->is_grabbing(); + } + return is_grabbing; +} + void EditorPropertyQuaternion::update_property() { Quaternion val = get_edited_object()->get(get_edited_property()); setting = true; @@ -2635,17 +2674,36 @@ void EditorPropertyQuaternion::update_property() { spin[1]->set_value(val.y); spin[2]->set_value(val.z); spin[3]->set_value(val.w); + if (!is_grabbing_euler()) { + Vector3 v = val.normalized().get_euler_yxz(); + edit_euler.x = Math::rad_to_deg(v.x); + edit_euler.y = Math::rad_to_deg(v.y); + edit_euler.z = Math::rad_to_deg(v.z); + euler[0]->set_value(edit_euler.x); + euler[1]->set_value(edit_euler.y); + euler[2]->set_value(edit_euler.z); + } setting = false; } +void EditorPropertyQuaternion::_warning_pressed() { + warning_dialog->popup_centered(); +} + void EditorPropertyQuaternion::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 4; i++) { spin[i]->add_theme_color_override("label_color", colors[i]); } + for (int i = 0; i < 3; i++) { + euler[i]->add_theme_color_override("label_color", colors[i]); + } + edit_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); + euler_label->add_theme_color_override(SNAME("font_color"), get_theme_color(SNAME("property_color"), SNAME("Editor"))); + warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); + warning->add_theme_color_override(SNAME("font_color"), get_theme_color(SNAME("warning_color"), SNAME("Editor"))); } break; } } @@ -2653,7 +2711,7 @@ void EditorPropertyQuaternion::_notification(int p_what) { void EditorPropertyQuaternion::_bind_methods() { } -void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix) { +void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix, bool p_hide_editor) { for (int i = 0; i < 4; i++) { spin[i]->set_min(p_min); spin[i]->set_max(p_max); @@ -2665,28 +2723,50 @@ void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, // a generic way to store 4 values, so we'll still respect the suffix. spin[i]->set_suffix(p_suffix); } + + for (int i = 0; i < 3; i++) { + euler[i]->set_min(-360); + euler[i]->set_max(360); + euler[i]->set_step(0.1); + euler[i]->set_hide_slider(false); + euler[i]->set_allow_greater(true); + euler[i]->set_allow_lesser(true); + euler[i]->set_suffix(U"\u00B0"); + } + + if (p_hide_editor) { + edit_button->hide(); + } } EditorPropertyQuaternion::EditorPropertyQuaternion() { bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); - BoxContainer *bc; - + VBoxContainer *bc = memnew(VBoxContainer); + edit_custom_bc = memnew(VBoxContainer); + BoxContainer *edit_custom_layout; if (horizontal) { - bc = memnew(HBoxContainer); - add_child(bc); + default_layout = memnew(HBoxContainer); + edit_custom_layout = memnew(HBoxContainer); set_bottom_editor(bc); } else { - bc = memnew(VBoxContainer); - add_child(bc); + default_layout = memnew(VBoxContainer); + edit_custom_layout = memnew(VBoxContainer); } + edit_custom_bc->hide(); + add_child(bc); + edit_custom_bc->set_h_size_flags(SIZE_EXPAND_FILL); + default_layout->set_h_size_flags(SIZE_EXPAND_FILL); + edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL); + bc->add_child(default_layout); + bc->add_child(edit_custom_bc); static const char *desc[4] = { "x", "y", "z", "w" }; for (int i = 0; i < 4; i++) { spin[i] = memnew(EditorSpinSlider); spin[i]->set_flat(true); spin[i]->set_label(desc[i]); - bc->add_child(spin[i]); + default_layout->add_child(spin[i]); add_focusable(spin[i]); spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyQuaternion::_value_changed).bind(desc[i])); if (horizontal) { @@ -2694,6 +2774,41 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() { } } + warning = memnew(Button); + warning->set_text(TTR("Temporary Euler may be changed implicitly!")); + warning->set_clip_text(true); + warning->connect("pressed", callable_mp(this, &EditorPropertyQuaternion::_warning_pressed)); + warning_dialog = memnew(AcceptDialog); + add_child(warning_dialog); + warning_dialog->set_text(TTR("Temporary Euler will not be stored in the object with the original value. Instead, it will be stored as Quaternion with irreversible conversion.\nThis is due to the fact that the result of Euler->Quaternion can be determined uniquely, but the result of Quaternion->Euler can be multi-existent.")); + + euler_label = memnew(Label); + euler_label->set_text("Temporary Euler"); + + edit_custom_bc->add_child(warning); + edit_custom_bc->add_child(edit_custom_layout); + edit_custom_layout->add_child(euler_label); + + for (int i = 0; i < 3; i++) { + euler[i] = memnew(EditorSpinSlider); + euler[i]->set_flat(true); + euler[i]->set_label(desc[i]); + edit_custom_layout->add_child(euler[i]); + add_focusable(euler[i]); + euler[i]->connect("value_changed", callable_mp(this, &EditorPropertyQuaternion::_custom_value_changed)); + if (horizontal) { + euler[i]->set_h_size_flags(SIZE_EXPAND_FILL); + } + } + + edit_button = memnew(Button); + edit_button->set_flat(true); + edit_button->set_toggle_mode(true); + default_layout->add_child(edit_button); + edit_button->connect("pressed", callable_mp(this, &EditorPropertyQuaternion::_edit_custom_value)); + + add_focusable(edit_button); + if (!horizontal) { set_label_reference(spin[0]); //show text and buttons around this } @@ -2731,7 +2846,6 @@ void EditorPropertyVector4::update_property() { void EditorPropertyVector4::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 4; i++) { @@ -2823,7 +2937,6 @@ void EditorPropertyVector4i::update_property() { void EditorPropertyVector4i::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 4; i++) { @@ -2918,7 +3031,6 @@ void EditorPropertyAABB::update_property() { void EditorPropertyAABB::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 6; i++) { @@ -3001,7 +3113,6 @@ void EditorPropertyTransform2D::update_property() { void EditorPropertyTransform2D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 6; i++) { @@ -3098,7 +3209,6 @@ void EditorPropertyBasis::update_property() { void EditorPropertyBasis::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 9; i++) { @@ -3196,7 +3306,6 @@ void EditorPropertyTransform3D::update_using_transform(Transform3D p_transform) void EditorPropertyTransform3D::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 12; i++) { @@ -3302,7 +3411,6 @@ void EditorPropertyProjection::update_using_transform(Projection p_transform) { void EditorPropertyProjection::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { const Color *colors = _get_property_colors(); for (int i = 0; i < 16; i++) { @@ -3373,7 +3481,6 @@ void EditorPropertyColor::_picker_opening() { void EditorPropertyColor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { picker->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); } break; @@ -3386,14 +3493,14 @@ void EditorPropertyColor::update_property() { // Add a tooltip to display each channel's values without having to click the ColorPickerButton if (picker->is_editing_alpha()) { - picker->set_tooltip(vformat( + picker->set_tooltip_text(vformat( "R: %s\nG: %s\nB: %s\nA: %s", rtos(color.r).pad_decimals(2), rtos(color.g).pad_decimals(2), rtos(color.b).pad_decimals(2), rtos(color.a).pad_decimals(2))); } else { - picker->set_tooltip(vformat( + picker->set_tooltip_text(vformat( "R: %s\nG: %s\nB: %s", rtos(color.r).pad_decimals(2), rtos(color.g).pad_decimals(2), @@ -3551,7 +3658,7 @@ void EditorPropertyNodePath::update_property() { p = get_edited_object()->get(get_edited_property()); } - assign->set_tooltip(p); + assign->set_tooltip_text(p); if (p == NodePath()) { assign->set_icon(Ref<Texture2D>()); assign->set_text(TTR("Assign...")); @@ -3597,7 +3704,6 @@ void EditorPropertyNodePath::setup(const NodePath &p_base_hint, Vector<StringNam void EditorPropertyNodePath::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { Ref<Texture2D> t = get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")); clear->set_icon(t); @@ -3653,20 +3759,24 @@ void EditorPropertyResource::_set_read_only(bool p_read_only) { resource_picker->set_editable(!p_read_only); }; -void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_edit) { +void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) { if (p_resource->is_built_in() && !p_resource->get_path().is_empty()) { String parent = p_resource->get_path().get_slice("::", 0); List<String> extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions); - if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) { - // If the resource belongs to another scene, edit it in that scene instead. - EditorNode::get_singleton()->call_deferred("edit_foreign_resource", p_resource); - return; + if (p_inspect) { + if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) { + // If the resource belongs to another (non-imported) scene, edit it in that scene instead. + if (!FileAccess::exists(parent + ".import")) { + EditorNode::get_singleton()->call_deferred("edit_foreign_resource", p_resource); + return; + } + } } } - if (!p_edit && use_sub_inspector) { + if (!p_inspect && use_sub_inspector) { bool unfold = !get_edited_object()->editor_is_section_unfolded(get_edited_property()); get_edited_object()->editor_set_section_unfold(get_edited_property(), unfold); update_property(); @@ -4005,7 +4115,6 @@ void EditorPropertyResource::set_use_sub_inspector(bool p_enable) { void EditorPropertyResource::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (!updating_theme) { _update_property_bg(); @@ -4279,6 +4388,9 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ EditorPropertyText *editor = memnew(EditorPropertyText); if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { editor->set_placeholder(p_hint_text); + } else if (p_hint == PROPERTY_HINT_PASSWORD) { + editor->set_secret(true); + editor->set_placeholder(p_hint_text); } return editor; } @@ -4357,7 +4469,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ case Variant::QUATERNION: { EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion); EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step); - editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix); + editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix, p_hint == PROPERTY_HINT_HIDE_QUATERNION_EDIT); return editor; } break; case Variant::AABB: { @@ -4403,6 +4515,9 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_ EditorPropertyText *editor = memnew(EditorPropertyText); if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { editor->set_placeholder(p_hint_text); + } else if (p_hint == PROPERTY_HINT_PASSWORD) { + editor->set_secret(true); + editor->set_placeholder(p_hint_text); } editor->set_string_name(true); return editor; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index c1dfb5cb1e..d6c9510634 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -67,6 +67,7 @@ public: void set_string_name(bool p_enabled); virtual void update_property() override; void set_placeholder(const String &p_string); + void set_secret(bool p_enabled); EditorPropertyText(); }; @@ -625,9 +626,26 @@ public: class EditorPropertyQuaternion : public EditorProperty { GDCLASS(EditorPropertyQuaternion, EditorProperty); + BoxContainer *default_layout = nullptr; EditorSpinSlider *spin[4]; bool setting = false; + + Button *warning = nullptr; + AcceptDialog *warning_dialog = nullptr; + + Label *euler_label = nullptr; + VBoxContainer *edit_custom_bc = nullptr; + EditorSpinSlider *euler[3]; + Button *edit_button = nullptr; + + Vector3 edit_euler = Vector3(); + void _value_changed(double p_val, const String &p_name); + void _edit_custom_value(); + void _custom_value_changed(double p_val); + void _warning_pressed(); + + bool is_grabbing_euler(); protected: virtual void _set_read_only(bool p_read_only) override; @@ -636,7 +654,7 @@ protected: public: virtual void update_property() override; - void setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix = String()); + void setup(double p_min, double p_max, double p_step, bool p_no_slider, const String &p_suffix = String(), bool p_hide_editor = false); EditorPropertyQuaternion(); }; @@ -835,7 +853,7 @@ class EditorPropertyResource : public EditorProperty { bool updating_theme = false; bool opened_editor = false; - void _resource_selected(const Ref<Resource> &p_resource, bool p_edit); + void _resource_selected(const Ref<Resource> &p_resource, bool p_inspect); void _resource_changed(const Ref<Resource> &p_resource); void _viewport_selected(const NodePath &p_path); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 9a83082d1e..fa7e5cf4a9 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -485,8 +485,7 @@ void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_d void EditorPropertyArray::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_THEME_CHANGED: { change_type->clear(); for (int i = 0; i < Variant::VARIANT_MAX; i++) { if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) { @@ -1111,7 +1110,7 @@ void EditorPropertyDictionary::update_property() { if (i < amount) { String cs = key.get_construct_string(); prop->set_label(key.get_construct_string()); - prop->set_tooltip(cs); + prop->set_tooltip_text(cs); change_index = i + offset; } else if (i == amount) { prop->set_label(TTR("New Key:")); @@ -1166,8 +1165,7 @@ void EditorPropertyDictionary::_object_id_selected(const StringName &p_property, void EditorPropertyDictionary::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_THEME_CHANGED: { change_type->clear(); for (int i = 0; i < Variant::VARIANT_MAX; i++) { if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) { @@ -1361,7 +1359,7 @@ void EditorPropertyLocalizableString::update_property() { String cs = key.get_construct_string(); prop->set_label(cs); - prop->set_tooltip(cs); + prop->set_tooltip_text(cs); remove_index = i + offset; prop->set_selectable(false); @@ -1405,8 +1403,7 @@ void EditorPropertyLocalizableString::_object_id_selected(const StringName &p_pr void EditorPropertyLocalizableString::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_THEME_CHANGED: { if (Object::cast_to<Button>(button_add_item)) { button_add_item->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp index 6c713de94a..a2dfa4f80e 100644 --- a/editor/editor_property_name_processor.cpp +++ b/editor/editor_property_name_processor.cpp @@ -106,6 +106,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["aabb"] = "AABB"; capitalize_string_remaps["adb"] = "ADB"; capitalize_string_remaps["ao"] = "AO"; + capitalize_string_remaps["api"] = "API"; capitalize_string_remaps["apk"] = "APK"; capitalize_string_remaps["arm64-v8a"] = "arm64-v8a"; capitalize_string_remaps["armeabi-v7a"] = "armeabi-v7a"; @@ -172,6 +173,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["k1"] = "K1"; capitalize_string_remaps["k2"] = "K2"; capitalize_string_remaps["kb"] = "(KB)"; // Unit. + capitalize_string_remaps["lcd"] = "LCD"; capitalize_string_remaps["ldr"] = "LDR"; capitalize_string_remaps["lod"] = "LOD"; capitalize_string_remaps["lowpass"] = "Low-pass"; @@ -190,11 +192,14 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["opengl"] = "OpenGL"; capitalize_string_remaps["opentype"] = "OpenType"; capitalize_string_remaps["openxr"] = "OpenXR"; + capitalize_string_remaps["osslsigncode"] = "osslsigncode"; capitalize_string_remaps["pck"] = "PCK"; capitalize_string_remaps["png"] = "PNG"; capitalize_string_remaps["po2"] = "(Power of 2)"; // Unit. capitalize_string_remaps["pvrtc"] = "PVRTC"; capitalize_string_remaps["pvs"] = "PVS"; + capitalize_string_remaps["rcedit"] = "rcedit"; + capitalize_string_remaps["rcodesign"] = "rcodesign"; capitalize_string_remaps["rgb"] = "RGB"; capitalize_string_remaps["rid"] = "RID"; capitalize_string_remaps["rmb"] = "RMB"; @@ -204,6 +209,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["sdfgi"] = "SDFGI"; capitalize_string_remaps["sdk"] = "SDK"; capitalize_string_remaps["sec"] = "(sec)"; // Unit. + capitalize_string_remaps["signtool"] = "signtool"; capitalize_string_remaps["sms"] = "SMS"; capitalize_string_remaps["srgb"] = "sRGB"; capitalize_string_remaps["ssao"] = "SSAO"; @@ -236,6 +242,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["webp"] = "WebP"; capitalize_string_remaps["webrtc"] = "WebRTC"; capitalize_string_remaps["websocket"] = "WebSocket"; + capitalize_string_remaps["wine"] = "wine"; capitalize_string_remaps["wifi"] = "Wi-Fi"; capitalize_string_remaps["x86"] = "x86"; capitalize_string_remaps["xr"] = "XR"; diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index f606459bad..78ed71400c 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -62,7 +62,7 @@ void EditorResourcePicker::_update_resource() { if (edited_resource == Ref<Resource>()) { assign_button->set_icon(Ref<Texture2D>()); assign_button->set_text(TTR("[empty]")); - assign_button->set_tooltip(""); + assign_button->set_tooltip_text(""); } else { assign_button->set_icon(EditorNode::get_singleton()->get_object_icon(edited_resource.operator->(), "Object")); @@ -73,14 +73,16 @@ void EditorResourcePicker::_update_resource() { } else { assign_button->set_text(edited_resource->get_class()); } - assign_button->set_tooltip(resource_path + TTR("Type:") + " " + edited_resource->get_class()); + assign_button->set_tooltip_text(resource_path + TTR("Type:") + " " + edited_resource->get_class()); // Preview will override the above, so called at the end. EditorResourcePreview::get_singleton()->queue_edited_resource_preview(edited_resource, this, "_update_resource_preview", edited_resource->get_instance_id()); } } else if (edited_resource.is_valid()) { - assign_button->set_tooltip(resource_path + TTR("Type:") + " " + edited_resource->get_class()); + assign_button->set_tooltip_text(resource_path + TTR("Type:") + " " + edited_resource->get_class()); } + + assign_button->set_disabled(!editable && !edited_resource.is_valid()); } void EditorResourcePicker::_update_resource_preview(const String &p_path, const Ref<Texture2D> &p_preview, const Ref<Texture2D> &p_small_preview, ObjectID p_obj) { @@ -171,35 +173,50 @@ void EditorResourcePicker::_update_menu_items() { edit_menu->clear(); // Add options for creating specific subtypes of the base resource type. - set_create_options(edit_menu); + if (is_editable()) { + set_create_options(edit_menu); - // Add an option to load a resource from a file using the QuickOpen dialog. - edit_menu->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Quick Load"), OBJ_MENU_QUICKLOAD); + // Add an option to load a resource from a file using the QuickOpen dialog. + edit_menu->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Quick Load"), OBJ_MENU_QUICKLOAD); - // Add an option to load a resource from a file using the regular file dialog. - edit_menu->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Load"), OBJ_MENU_LOAD); + // Add an option to load a resource from a file using the regular file dialog. + edit_menu->add_icon_item(get_theme_icon(SNAME("Load"), SNAME("EditorIcons")), TTR("Load"), OBJ_MENU_LOAD); + } // Add options for changing existing value of the resource. if (edited_resource.is_valid()) { - edit_menu->add_icon_item(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")), TTR("Edit"), OBJ_MENU_EDIT); - edit_menu->add_icon_item(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")), TTR("Clear"), OBJ_MENU_CLEAR); - edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE); - - // Check whether the resource has subresources. - List<PropertyInfo> property_list; - edited_resource->get_property_list(&property_list); - bool has_subresources = false; - for (PropertyInfo &p : property_list) { - if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script")) { - has_subresources = true; - break; - } - } - if (has_subresources) { - edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique (Recursive)"), OBJ_MENU_MAKE_UNIQUE_RECURSIVE); + // Determine if the edited resource is part of another scene (foreign) which was imported + bool is_edited_resource_foreign_import = EditorNode::get_singleton()->is_resource_read_only(edited_resource); + + // If the resource is determined to be foreign and imported, change the menu entry's description to 'inspect' rather than 'edit' + // since will only be able to view its properties in read-only mode. + if (is_edited_resource_foreign_import) { + // The 'Search' icon is a magnifying glass, which seems appropriate, but maybe a bespoke icon is preferred here. + edit_menu->add_icon_item(get_theme_icon(SNAME("Search"), SNAME("EditorIcons")), TTR("Inspect"), OBJ_MENU_INSPECT); + } else { + edit_menu->add_icon_item(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")), TTR("Edit"), OBJ_MENU_INSPECT); } - edit_menu->add_icon_item(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")), TTR("Save"), OBJ_MENU_SAVE); + if (is_editable()) { + edit_menu->add_icon_item(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")), TTR("Clear"), OBJ_MENU_CLEAR); + edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE); + + // Check whether the resource has subresources. + List<PropertyInfo> property_list; + edited_resource->get_property_list(&property_list); + bool has_subresources = false; + for (PropertyInfo &p : property_list) { + if ((p.type == Variant::OBJECT) && (p.hint == PROPERTY_HINT_RESOURCE_TYPE) && (p.name != "script")) { + has_subresources = true; + break; + } + } + if (has_subresources) { + edit_menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Make Unique (Recursive)"), OBJ_MENU_MAKE_UNIQUE_RECURSIVE); + } + + edit_menu->add_icon_item(get_theme_icon(SNAME("Save"), SNAME("EditorIcons")), TTR("Save"), OBJ_MENU_SAVE); + } if (edited_resource->get_path().is_resource_file()) { edit_menu->add_separator(); @@ -210,14 +227,16 @@ void EditorResourcePicker::_update_menu_items() { // Add options to copy/paste resource. Ref<Resource> cb = EditorSettings::get_singleton()->get_resource_clipboard(); bool paste_valid = false; - if (cb.is_valid()) { - if (base_type.is_empty()) { - paste_valid = true; - } else { - for (int i = 0; i < base_type.get_slice_count(","); i++) { - if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) { - paste_valid = true; - break; + if (is_editable()) { + if (cb.is_valid()) { + if (base_type.is_empty()) { + paste_valid = true; + } else { + for (int i = 0; i < base_type.get_slice_count(","); i++) { + if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) { + paste_valid = true; + break; + } } } } @@ -236,7 +255,7 @@ void EditorResourcePicker::_update_menu_items() { } // Add options to convert existing resource to another type of resource. - if (edited_resource.is_valid()) { + if (is_editable() && edited_resource.is_valid()) { Vector<Ref<EditorResourceConversionPlugin>> conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(edited_resource); if (conversions.size()) { edit_menu->add_separator(); @@ -295,7 +314,7 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) { quick_open->set_title(TTR("Resource")); } break; - case OBJ_MENU_EDIT: { + case OBJ_MENU_INSPECT: { if (edited_resource.is_valid()) { emit_signal(SNAME("resource_selected"), edited_resource, true); } @@ -491,20 +510,21 @@ void EditorResourcePicker::_button_draw() { } void EditorResourcePicker::_button_input(const Ref<InputEvent> &p_event) { - if (!editable) { - return; - } - Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { if (mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) { - _update_menu_items(); - - Vector2 pos = get_screen_position() + mb->get_position(); - edit_menu->reset_size(); - edit_menu->set_position(pos); - edit_menu->popup(); + // Only attempt to update and show the menu if we have + // a valid resource or the Picker is editable, as + // there will otherwise be nothing to display. + if (edited_resource.is_valid() || is_editable()) { + _update_menu_items(); + + Vector2 pos = get_screen_position() + mb->get_position(); + edit_menu->reset_size(); + edit_menu->set_position(pos); + edit_menu->popup(); + } } } } @@ -734,7 +754,7 @@ void EditorResourcePicker::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); - ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::BOOL, "edit"))); + ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"), PropertyInfo(Variant::BOOL, "inspect"))); ADD_SIGNAL(MethodInfo("resource_changed", PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource"))); } @@ -742,8 +762,8 @@ void EditorResourcePicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { _update_resource(); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { edit_button->set_icon(get_theme_icon(SNAME("select_arrow"), SNAME("Tree"))); } break; @@ -866,7 +886,7 @@ void EditorResourcePicker::set_toggle_pressed(bool p_pressed) { void EditorResourcePicker::set_editable(bool p_editable) { editable = p_editable; - assign_button->set_disabled(!editable); + assign_button->set_disabled(!editable && !edited_resource.is_valid()); edit_button->set_visible(editable); } @@ -1018,7 +1038,6 @@ EditorShaderPicker::EditorShaderPicker() { void EditorAudioStreamPicker::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_READY: case NOTIFICATION_THEME_CHANGED: { _update_resource(); } break; diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 3a4d5985bd..3d6127e656 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -63,7 +63,7 @@ class EditorResourcePicker : public HBoxContainer { enum MenuOption { OBJ_MENU_LOAD, OBJ_MENU_QUICKLOAD, - OBJ_MENU_EDIT, + OBJ_MENU_INSPECT, OBJ_MENU_CLEAR, OBJ_MENU_MAKE_UNIQUE, OBJ_MENU_MAKE_UNIQUE_RECURSIVE, diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp index 6ce8625daa..3b828951e4 100644 --- a/editor/editor_run.cpp +++ b/editor/editor_run.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "main/main.h" #include "servers/display_server.h" EditorRun::Status EditorRun::get_status() const { @@ -46,6 +47,10 @@ String EditorRun::get_running_scene() const { Error EditorRun::run(const String &p_scene, const String &p_write_movie) { List<String> args; + for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_PROJECT)) { + args.push_back(a); + } + String resource_path = ProjectSettings::get_singleton()->get_resource_path(); if (!resource_path.is_empty()) { args.push_back("--path"); @@ -105,10 +110,6 @@ Error EditorRun::run(const String &p_scene, const String &p_write_movie) { screen -= 3; } - if (OS::get_singleton()->is_disable_crash_handler()) { - args.push_back("--disable-crash-handler"); - } - Rect2 screen_rect; screen_rect.position = DisplayServer::get_singleton()->screen_get_position(screen); screen_rect.size = DisplayServer::get_singleton()->screen_get_size(screen); diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index d8b738be17..3e8f17085d 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -77,9 +77,9 @@ void EditorRunNative::_notification(int p_what) { mb->get_popup()->clear(); mb->show(); if (dc == 1) { - mb->set_tooltip(eep->get_option_tooltip(0)); + mb->set_tooltip_text(eep->get_option_tooltip(0)); } else { - mb->set_tooltip(eep->get_options_tooltip()); + mb->set_tooltip_text(eep->get_options_tooltip()); for (int i = 0; i < dc; i++) { mb->get_popup()->add_icon_item(eep->get_option_icon(i), eep->get_option_label(i)); mb->get_popup()->set_item_tooltip(-1, eep->get_option_tooltip(i)); diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp index 801a1a4641..1faefb5af7 100644 --- a/editor/editor_sectioned_inspector.cpp +++ b/editor/editor_sectioned_inspector.cpp @@ -113,12 +113,12 @@ class SectionedInspectorFilter : public Object { } } - bool property_can_revert(const String &p_name) { - return edited->call("property_can_revert", section + "/" + p_name); + bool property_can_revert(const StringName &p_name) { + return edited->property_can_revert(section + "/" + p_name); } - Variant property_get_revert(const String &p_name) { - return edited->call("property_get_revert", section + "/" + p_name); + Variant property_get_revert(const StringName &p_name) { + return edited->property_get_revert(section + "/" + p_name); } protected: diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 80e77a1125..f5d3b4842d 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -406,6 +406,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { _initial_set("interface/editor/debug/enable_pseudolocalization", false); set_restart_if_changed("interface/editor/debug/enable_pseudolocalization", true); // Use pseudolocalization in editor. + EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/use_embedded_menu", false, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) + EDITOR_SETTING_USAGE(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/expand_to_title", true, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) EDITOR_SETTING_USAGE(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/editor/custom_display_scale", 1.0, "0.5,3,0.01", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED) EDITOR_SETTING(Variant::INT, PROPERTY_HINT_RANGE, "interface/editor/main_font_size", 14, "8,48,1") @@ -413,7 +415,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/code_font_contextual_ligatures", 0, "Default,Disable Contextual Alternates (Coding Ligatures),Use Custom OpenType Feature Set") _initial_set("interface/editor/code_font_custom_opentype_features", ""); _initial_set("interface/editor/code_font_custom_variations", ""); - _initial_set("interface/editor/font_antialiased", true); + EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/font_antialiasing", 1, "None,Grayscale,LCD sub-pixel") #ifdef MACOS_ENABLED EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/font_hinting", 0, "Auto (None),None,Light,Normal") #else @@ -962,8 +964,8 @@ void EditorSettings::save() { } } -Array EditorSettings::get_changed_settings() const { - Array arr; +PackedStringArray EditorSettings::get_changed_settings() const { + PackedStringArray arr; for (const String &setting : changed_settings) { arr.push_back(setting); } @@ -1073,24 +1075,25 @@ Variant _EDITOR_GET(const String &p_setting) { return EditorSettings::get_singleton()->get(p_setting); } -bool EditorSettings::property_can_revert(const String &p_setting) { - if (!props.has(p_setting)) { +bool EditorSettings::_property_can_revert(const StringName &p_name) const { + if (!props.has(p_name)) { return false; } - if (!props[p_setting].has_default_value) { + if (!props[p_name].has_default_value) { return false; } - return props[p_setting].initial != props[p_setting].variant; + return props[p_name].initial != props[p_name].variant; } -Variant EditorSettings::property_get_revert(const String &p_setting) { - if (!props.has(p_setting) || !props[p_setting].has_default_value) { - return Variant(); +bool EditorSettings::_property_get_revert(const StringName &p_name, Variant &r_property) const { + if (!props.has(p_name) || !props[p_name].has_default_value) { + return false; } - return props[p_setting].initial; + r_property = props[p_name].initial; + return true; } void EditorSettings::add_property_hint(const PropertyInfo &p_hint) { @@ -1474,8 +1477,11 @@ void ED_SHORTCUT_OVERRIDE_ARRAY(const String &p_path, const String &p_feature, c } } - // Directly override the existing shortcut. - sc->set_events(events); + // Override the existing shortcut only if it wasn't customized by the user (i.e. still "original"). + if (Shortcut::is_event_array_equal(sc->get_events(), sc->get_meta("original"))) { + sc->set_events(events); + } + sc->set_meta("original", events.duplicate(true)); } @@ -1621,8 +1627,6 @@ void EditorSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("get_setting", "name"), &EditorSettings::get_setting); ClassDB::bind_method(D_METHOD("erase", "property"), &EditorSettings::erase); ClassDB::bind_method(D_METHOD("set_initial_value", "name", "value", "update_current"), &EditorSettings::set_initial_value); - ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &EditorSettings::property_can_revert); - ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &EditorSettings::property_get_revert); ClassDB::bind_method(D_METHOD("add_property_info", "info"), &EditorSettings::_add_property_info_bind); ClassDB::bind_method(D_METHOD("set_project_metadata", "section", "key", "data"), &EditorSettings::set_project_metadata); diff --git a/editor/editor_settings.h b/editor/editor_settings.h index 5faeec88c8..09bc4caa22 100644 --- a/editor/editor_settings.h +++ b/editor/editor_settings.h @@ -100,6 +100,8 @@ private: void _initial_set(const StringName &p_name, const Variant &p_value); void _get_property_list(List<PropertyInfo> *p_list) const; void _add_property_info_bind(const Dictionary &p_info); + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; void _load_defaults(Ref<ConfigFile> p_extra_config = Ref<ConfigFile>()); void _load_godot2_text_editor_theme(); @@ -138,10 +140,8 @@ public: _set_only(p_setting, p_value); } } - bool property_can_revert(const String &p_setting); - Variant property_get_revert(const String &p_setting); void add_property_hint(const PropertyInfo &p_hint); - Array get_changed_settings() const; + PackedStringArray get_changed_settings() const; bool check_changed_settings_in_group(const String &p_setting_prefix) const; void mark_setting_changed(const String &p_setting); diff --git a/editor/editor_settings_dialog.cpp b/editor/editor_settings_dialog.cpp index 08ff63551f..67c602ad2d 100644 --- a/editor/editor_settings_dialog.cpp +++ b/editor/editor_settings_dialog.cpp @@ -40,6 +40,7 @@ #include "editor/editor_property_name_processor.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/margin_container.h" void EditorSettingsDialog::ok_pressed() { @@ -124,9 +125,9 @@ void EditorSettingsDialog::_notification(int p_what) { } break; case NOTIFICATION_READY: { - undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, nullptr); - undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, nullptr); - undo_redo->set_commit_notify_callback(_undo_redo_callback, this); + undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_method_notify_callback(EditorDebuggerNode::_method_changeds, nullptr); + undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_property_notify_callback(EditorDebuggerNode::_property_changeds, nullptr); + undo_redo->get_or_create_history(EditorUndoRedoManager::GLOBAL_HISTORY).undo_redo->set_commit_notify_callback(_undo_redo_callback, this); } break; case NOTIFICATION_ENTER_TREE: { @@ -680,7 +681,7 @@ void EditorSettingsDialog::_bind_methods() { EditorSettingsDialog::EditorSettingsDialog() { set_title(TTR("Editor Settings")); - undo_redo = memnew(UndoRedo); + undo_redo = EditorNode::get_undo_redo(); tabs = memnew(TabContainer); tabs->set_theme_type_variation("TabContainerOdd"); @@ -776,5 +777,4 @@ EditorSettingsDialog::EditorSettingsDialog() { } EditorSettingsDialog::~EditorSettingsDialog() { - memdelete(undo_redo); } diff --git a/editor/editor_settings_dialog.h b/editor/editor_settings_dialog.h index a1ea54c6fb..87ed6a77eb 100644 --- a/editor/editor_settings_dialog.h +++ b/editor/editor_settings_dialog.h @@ -40,6 +40,8 @@ #include "scene/gui/tab_container.h" #include "scene/gui/texture_rect.h" +class EditorUndoRedoManager; + class EditorSettingsDialog : public AcceptDialog { GDCLASS(EditorSettingsDialog, AcceptDialog); @@ -73,7 +75,7 @@ class EditorSettingsDialog : public AcceptDialog { Timer *timer = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; virtual void cancel_pressed() override; virtual void ok_pressed() override; diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 20e9d7a3df..128d90cd62 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -149,6 +149,10 @@ void EditorSpinSlider::gui_input(const Ref<InputEvent> &p_event) { } void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) { + if (read_only) { + return; + } + Ref<InputEventMouseButton> mb = p_event; if (grabbing_grabber) { @@ -427,7 +431,6 @@ void EditorSpinSlider::_draw_spin_slider() { void EditorSpinSlider::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { _update_value_input_stylebox(); } break; @@ -605,6 +608,10 @@ bool EditorSpinSlider::is_flat() const { return flat; } +bool EditorSpinSlider::is_grabbing() const { + return grabbing_grabber || grabbing_spinner; +} + void EditorSpinSlider::_focus_entered() { _ensure_input_popup(); Rect2 gr = get_screen_rect(); diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h index f0adf5b7a1..afcaa3e4b6 100644 --- a/editor/editor_spin_slider.h +++ b/editor/editor_spin_slider.h @@ -110,6 +110,8 @@ public: void set_flat(bool p_enable); bool is_flat() const; + bool is_grabbing() const; + void setup_and_show() { _focus_entered(); } LineEdit *get_line_edit(); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index c58d1263f3..827a657a31 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -32,7 +32,6 @@ #include "core/error/error_macros.h" #include "core/io/resource_loader.h" -#include "core/variant/dictionary.h" #include "editor/editor_fonts.h" #include "editor/editor_icons.gen.h" #include "editor/editor_scale.h" @@ -43,6 +42,118 @@ #include "modules/svg/image_loader_svg.h" #endif +HashMap<Color, Color> EditorColorMap::editor_color_map; + +void EditorColorMap::add_color_pair(const String p_from_color, const String p_to_color) { + editor_color_map[Color::html(p_from_color)] = Color::html(p_to_color); +} + +void EditorColorMap::create() { + // Some of the colors below are listed for completeness sake. + // This can be a basis for proper palette validation later. + + // Convert: FROM TO + add_color_pair("#478cbf", "#478cbf"); // Godot Blue + add_color_pair("#414042", "#414042"); // Godot Gray + + add_color_pair("#ffffff", "#414141"); // Pure white + add_color_pair("#000000", "#bfbfbf"); // Pure black + // Keep pure RGB colors as is, but list them for explicitly. + add_color_pair("#ff0000", "#ff0000"); // Pure red + add_color_pair("#00ff00", "#00ff00"); // Pure green + add_color_pair("#0000ff", "#0000ff"); // Pure blue + + // GUI Colors + add_color_pair("#e0e0e0", "#5a5a5a"); // Common icon color + add_color_pair("#fefefe", "#fefefe"); // Forced light color + add_color_pair("#808080", "#808080"); // GUI disabled color + add_color_pair("#b3b3b3", "#363636"); // GUI disabled light color + add_color_pair("#699ce8", "#699ce8"); // GUI highlight color + add_color_pair("#f9f9f9", "#606060"); // Scrollbar grabber highlight color + + add_color_pair("#c38ef1", "#a85de9"); // Animation + add_color_pair("#fc7f7f", "#cd3838"); // Spatial + add_color_pair("#8da5f3", "#3d64dd"); // 2D + add_color_pair("#4b70ea", "#1a3eac"); // 2D Dark + add_color_pair("#8eef97", "#2fa139"); // Control + + add_color_pair("#5fb2ff", "#0079f0"); // Selection (blue) + add_color_pair("#003e7a", "#2b74bb"); // Selection (darker blue) + add_color_pair("#f7f5cf", "#615f3a"); // Gizmo (yellow) + + // Rainbow + add_color_pair("#ff4545", "#ff2929"); // Red + add_color_pair("#ffe345", "#ffe337"); // Yellow + add_color_pair("#80ff45", "#74ff34"); // Green + add_color_pair("#45ffa2", "#2cff98"); // Aqua + add_color_pair("#45d7ff", "#22ccff"); // Blue + add_color_pair("#8045ff", "#702aff"); // Purple + add_color_pair("#ff4596", "#ff2781"); // Pink + + // Audio gradients + add_color_pair("#e1da5b", "#d6cf4b"); // Yellow + + add_color_pair("#62aeff", "#1678e0"); // Frozen gradient top + add_color_pair("#75d1e6", "#41acc5"); // Frozen gradient middle + add_color_pair("#84ffee", "#49ccba"); // Frozen gradient bottom + + add_color_pair("#f70000", "#c91616"); // Color track red + add_color_pair("#eec315", "#d58c0b"); // Color track orange + add_color_pair("#dbee15", "#b7d10a"); // Color track yellow + add_color_pair("#288027", "#218309"); // Color track green + + // Resource groups + add_color_pair("#ffca5f", "#fea900"); // Mesh resource (orange) + add_color_pair("#2998ff", "#68b6ff"); // Shape resource (blue) + add_color_pair("#a2d2ff", "#4998e3"); // Shape resource (light blue) + + // Animation editor tracks + // The property track icon color is set by the common icon color. + add_color_pair("#ea7940", "#bd5e2c"); // 3D Position track + add_color_pair("#ff2b88", "#bd165f"); // 3D Rotation track + add_color_pair("#eac840", "#bd9d1f"); // 3D Scale track + add_color_pair("#3cf34e", "#16a827"); // Call Method track + add_color_pair("#2877f6", "#236be6"); // Bezier Curve track + add_color_pair("#eae440", "#9f9722"); // Audio Playback track + add_color_pair("#a448f0", "#9853ce"); // Animation Playback track + add_color_pair("#5ad5c4", "#0a9c88"); // Blend Shape track + + // Control layouts + add_color_pair("#d6d6d6", "#474747"); // Highlighted part + add_color_pair("#474747", "#d6d6d6"); // Background part + add_color_pair("#919191", "#6e6e6e"); // Border part + + // TileSet editor icons + add_color_pair("#fce00e", "#aa8d24"); // New Single Tile + add_color_pair("#0e71fc", "#0350bd"); // New Autotile + add_color_pair("#c6ced4", "#828f9b"); // New Atlas + + // Visual script + add_color_pair("#41ecad", "#25e3a0"); // VisualScript variant + add_color_pair("#6f91f0", "#6d8eeb"); // VisualScript bool + add_color_pair("#5abbef", "#4fb2e9"); // VisualScript int + add_color_pair("#35d4f4", "#27ccf0"); // VisualScript float + add_color_pair("#4593ec", "#4690e7"); // VisualScript String + add_color_pair("#ac73f1", "#ad76ee"); // VisualScript Vector2 + add_color_pair("#f1738f", "#ee758e"); // VisualScript Rect2 + add_color_pair("#de66f0", "#dc6aed"); // VisualScript Vector3 + add_color_pair("#b9ec41", "#96ce1a"); // VisualScript Transform2D + add_color_pair("#f74949", "#f77070"); // VisualScript Plane + add_color_pair("#ec418e", "#ec69a3"); // VisualScript Quat + add_color_pair("#ee5677", "#ee7991"); // VisualScript AABB + add_color_pair("#e1ec41", "#b2bb19"); // VisualScript Basis + add_color_pair("#f68f45", "#f49047"); // VisualScript Transform + add_color_pair("#417aec", "#6993ec"); // VisualScript NodePath + add_color_pair("#41ec80", "#2ce573"); // VisualScript RID + add_color_pair("#55f3e3", "#12d5c3"); // VisualScript Object + add_color_pair("#54ed9e", "#57e99f"); // VisualScript Dictionary + // Visual shaders + add_color_pair("#77ce57", "#67c046"); // Vector funcs + add_color_pair("#ea686c", "#d95256"); // Vector transforms + add_color_pair("#eac968", "#d9b64f"); // Textures and cubemaps + add_color_pair("#cf68ea", "#c050dd"); // Functions and expressions +} + static Ref<StyleBoxTexture> make_stylebox(Ref<Texture2D> p_texture, float p_left, float p_top, float p_right, float p_bottom, float p_margin_left = -1, float p_margin_top = -1, float p_margin_right = -1, float p_margin_bottom = -1, bool p_draw_center = true) { Ref<StyleBoxTexture> style(memnew(StyleBoxTexture)); style->set_texture(p_texture); @@ -113,7 +224,7 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false, #ifdef MODULE_SVG_ENABLED // See also `generate_icon()` in `scene/resources/default_theme.cpp`. -static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, float p_scale = EDSCALE, float p_saturation = 1.0, Dictionary p_convert_colors = Dictionary()) { +static Ref<ImageTexture> editor_generate_icon(int p_index, float p_scale, float p_saturation, const HashMap<Color, Color> &p_convert_colors = HashMap<Color, Color>()) { Ref<Image> img = memnew(Image); // Upsample icon generation only if the editor scale isn't an integer multiplier. @@ -121,8 +232,7 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, // with integer editor scales. const bool upsample = !Math::is_equal_approx(Math::round(p_scale), p_scale); ImageLoaderSVG img_loader; - img_loader.set_replace_colors(p_convert_colors); - img_loader.create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_color); + img_loader.create_image_from_string(img, editor_icons_sources[p_index], p_scale, upsample, p_convert_colors); if (p_saturation != 1.0) { img->adjust_bcs(1.0, 1.0, p_saturation); } @@ -132,126 +242,18 @@ static Ref<ImageTexture> editor_generate_icon(int p_index, bool p_convert_color, } #endif -#ifndef ADD_CONVERT_COLOR -#define ADD_CONVERT_COLOR(dictionary, old_color, new_color) dictionary[Color::html(old_color)] = Color::html(new_color) -#endif - void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = true, int p_thumb_size = 32, bool p_only_thumbs = false, float p_icon_saturation = 1.0) { #ifdef MODULE_SVG_ENABLED - // The default icon theme is designed to be used for a dark theme. - // This dictionary stores Color values to convert to other colors - // for better readability on a light theme. - // Godot Color values are used to avoid the ambiguity of strings - // (where "#ffffff", "fff", and "white" are all equivalent). - Dictionary dark_icon_color_dictionary; + HashMap<Color, Color> icon_color_map; // The names of the icons to never convert, even if one of their colors // are contained in the dictionary above. HashSet<StringName> exceptions; - // Some of the colors below are listed for completeness sake. - // This can be a basis for proper palette validation later. if (!p_dark_theme) { - // Convert color: FROM TO - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#478cbf", "#478cbf"); // Godot Blue - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#414042", "#414042"); // Godot Gray - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffffff", "#414141"); // Pure white - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#000000", "#bfbfbf"); // Pure black - // Keep pure RGB colors as is, but list them for explicitly. - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff0000", "#ff0000"); // Pure red - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#00ff00", "#00ff00"); // Pure green - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#0000ff", "#0000ff"); // Pure blue - - // GUI Colors - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e0e0e0", "#5a5a5a"); // Common icon color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fefefe", "#fefefe"); // Forced light color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#808080", "#808080"); // GUI disabled color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#b3b3b3", "#363636"); // GUI disabled light color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#699ce8", "#699ce8"); // GUI highlight color - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f9f9f9", "#606060"); // Scrollbar grabber highlight color - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#c38ef1", "#a85de9"); // Animation - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fc7f7f", "#cd3838"); // Spatial - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8da5f3", "#3d64dd"); // 2D - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#4b70ea", "#1a3eac"); // 2D Dark - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8eef97", "#2fa139"); // Control - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5fb2ff", "#0079f0"); // Selection (blue) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#003e7a", "#2b74bb"); // Selection (darker blue) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f7f5cf", "#615f3a"); // Gizmo (yellow) - - // Rainbow - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff4545", "#ff2929"); // Red - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffe345", "#ffe337"); // Yellow - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#80ff45", "#74ff34"); // Green - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#45ffa2", "#2cff98"); // Aqua - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#45d7ff", "#22ccff"); // Blue - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#8045ff", "#702aff"); // Purple - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff4596", "#ff2781"); // Pink - - // Audio gradients - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e1da5b", "#d6cf4b"); // Yellow - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#62aeff", "#1678e0"); // Frozen gradient top - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#75d1e6", "#41acc5"); // Frozen gradient middle - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#84ffee", "#49ccba"); // Frozen gradient bottom - - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f70000", "#c91616"); // Color track red - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eec315", "#d58c0b"); // Color track orange - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#dbee15", "#b7d10a"); // Color track yellow - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#288027", "#218309"); // Color track green - - // Resource groups - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ffca5f", "#fea900"); // Mesh resource (orange) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#2998ff", "#68b6ff"); // Shape resource (blue) - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a2d2ff", "#4998e3"); // Shape resource (light blue) - - // Animation editor tracks - // The property track icon color is set by the common icon color. - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea7940", "#bd5e2c"); // 3D Position track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ff2b88", "#bd165f"); // 3D Rotation track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eac840", "#bd9d1f"); // 3D Scale track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#3cf34e", "#16a827"); // Call Method track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#2877f6", "#236be6"); // Bezier Curve track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eae440", "#9f9722"); // Audio Playback track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#a448f0", "#9853ce"); // Animation Playback track - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5ad5c4", "#0a9c88"); // Blend Shape track - - // Control layouts - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#d6d6d6", "#474747"); // Highlighted part - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#474747", "#d6d6d6"); // Background part - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#919191", "#6e6e6e"); // Border part - - // TileSet editor icons - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#fce00e", "#aa8d24"); // New Single Tile - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#0e71fc", "#0350bd"); // New Autotile - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#c6ced4", "#828f9b"); // New Atlas - - // Visual script - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#41ecad", "#25e3a0"); // VisualScript variant - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#6f91f0", "#6d8eeb"); // VisualScript bool - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#5abbef", "#4fb2e9"); // VisualScript int - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#35d4f4", "#27ccf0"); // VisualScript float - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#4593ec", "#4690e7"); // VisualScript String - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ac73f1", "#ad76ee"); // VisualScript Vector2 - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f1738f", "#ee758e"); // VisualScript Rect2 - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#de66f0", "#dc6aed"); // VisualScript Vector3 - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#b9ec41", "#96ce1a"); // VisualScript Transform2D - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f74949", "#f77070"); // VisualScript Plane - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ec418e", "#ec69a3"); // VisualScript Quat - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ee5677", "#ee7991"); // VisualScript AABB - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#e1ec41", "#b2bb19"); // VisualScript Basis - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#f68f45", "#f49047"); // VisualScript Transform - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#417aec", "#6993ec"); // VisualScript NodePath - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#41ec80", "#2ce573"); // VisualScript RID - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#55f3e3", "#12d5c3"); // VisualScript Object - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#54ed9e", "#57e99f"); // VisualScript Dictionary - // Visual shaders - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#77ce57", "#67c046"); // Vector funcs - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#ea686c", "#d95256"); // Vector transforms - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#eac968", "#d9b64f"); // Textures and cubemaps - ADD_CONVERT_COLOR(dark_icon_color_dictionary, "#cf68ea", "#c050dd"); // Functions and expressions + for (KeyValue<Color, Color> &E : EditorColorMap::get()) { + icon_color_map[E.key] = E.value; + } exceptions.insert("EditorPivot"); exceptions.insert("EditorHandle"); @@ -291,18 +293,18 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = const Color error_color = p_theme->get_color(SNAME("error_color"), SNAME("Editor")); const Color success_color = p_theme->get_color(SNAME("success_color"), SNAME("Editor")); const Color warning_color = p_theme->get_color(SNAME("warning_color"), SNAME("Editor")); - dark_icon_color_dictionary[Color::html("#ff5f5f")] = error_color; - dark_icon_color_dictionary[Color::html("#5fff97")] = success_color; - dark_icon_color_dictionary[Color::html("#ffdd65")] = warning_color; + icon_color_map[Color::html("#ff5f5f")] = error_color; + icon_color_map[Color::html("#5fff97")] = success_color; + icon_color_map[Color::html("#ffdd65")] = warning_color; // Use the accent color for some icons (checkbox, radio, toggle, etc.). - Dictionary accent_color_icon_color_dictionary; + HashMap<Color, Color> accent_color_map; HashSet<StringName> accent_color_icons; const Color accent_color = p_theme->get_color(SNAME("accent_color"), SNAME("Editor")); - accent_color_icon_color_dictionary[Color::html("699ce8")] = accent_color; + accent_color_map[Color::html("699ce8")] = accent_color; if (accent_color.get_luminance() > 0.75) { - accent_color_icon_color_dictionary[Color::html("ffffff")] = Color(0.2, 0.2, 0.2); + accent_color_map[Color::html("ffffff")] = Color(0.2, 0.2, 0.2); } accent_color_icons.insert("GuiChecked"); @@ -318,7 +320,7 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = Ref<ImageTexture> icon; if (accent_color_icons.has(editor_icons_names[i])) { - icon = editor_generate_icon(i, true, EDSCALE, 1.0, accent_color_icon_color_dictionary); + icon = editor_generate_icon(i, EDSCALE, 1.0, accent_color_map); } else { float saturation = p_icon_saturation; @@ -327,7 +329,11 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = } const int is_exception = exceptions.has(editor_icons_names[i]); - icon = editor_generate_icon(i, !is_exception, EDSCALE, saturation, dark_icon_color_dictionary); + if (is_exception) { + icon = editor_generate_icon(i, EDSCALE, saturation); + } else { + icon = editor_generate_icon(i, EDSCALE, saturation, icon_color_map); + } } p_theme->set_icon(editor_icons_names[i], SNAME("EditorIcons"), icon); @@ -342,7 +348,13 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = for (int i = 0; i < editor_bg_thumbs_count; i++) { const int index = editor_bg_thumbs_indices[i]; const int is_exception = exceptions.has(editor_icons_names[index]); - const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary); + + Ref<ImageTexture> icon; + if (!p_dark_theme && !is_exception) { + icon = editor_generate_icon(index, scale, force_filter, icon_color_map); + } else { + icon = editor_generate_icon(index, scale, force_filter); + } p_theme->set_icon(editor_icons_names[index], SNAME("EditorIcons"), icon); } @@ -351,7 +363,13 @@ void editor_register_and_generate_icons(Ref<Theme> p_theme, bool p_dark_theme = for (int i = 0; i < editor_md_thumbs_count; i++) { const int index = editor_md_thumbs_indices[i]; const bool is_exception = exceptions.has(editor_icons_names[index]); - const Ref<ImageTexture> icon = editor_generate_icon(index, !p_dark_theme && !is_exception, scale, force_filter, dark_icon_color_dictionary); + + Ref<ImageTexture> icon; + if (!p_dark_theme && !is_exception) { + icon = editor_generate_icon(index, scale, force_filter, icon_color_map); + } else { + icon = editor_generate_icon(index, scale, force_filter); + } p_theme->set_icon(editor_icons_names[index], SNAME("EditorIcons"), icon); } @@ -518,8 +536,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { Color warning_color = Color(1, 0.87, 0.4); Color error_color = Color(1, 0.47, 0.42); Color property_color = font_color.lerp(Color(0.5, 0.5, 0.5), 0.5); - Color readonly_color = property_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.5); - Color readonly_warning_color = error_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.5); + Color readonly_color = property_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.25); + Color readonly_warning_color = error_color.lerp(dark_theme ? Color(0, 0, 0) : Color(1, 1, 1), 0.25); if (!dark_theme) { // Darken some colors to be readable on a light background @@ -788,6 +806,25 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { editor_log_button_pressed->set_border_color(accent_color); theme->set_stylebox("pressed", "EditorLogFilterButton", editor_log_button_pressed); + // MenuBar + theme->set_stylebox("normal", "MenuBar", style_widget); + theme->set_stylebox("hover", "MenuBar", style_widget_hover); + theme->set_stylebox("pressed", "MenuBar", style_widget_pressed); + theme->set_stylebox("focus", "MenuBar", style_widget_focus); + theme->set_stylebox("disabled", "MenuBar", style_widget_disabled); + + theme->set_color("font_color", "MenuBar", font_color); + theme->set_color("font_hover_color", "MenuBar", font_hover_color); + theme->set_color("font_focus_color", "MenuBar", font_focus_color); + theme->set_color("font_pressed_color", "MenuBar", accent_color); + theme->set_color("font_disabled_color", "MenuBar", font_disabled_color); + + theme->set_color("icon_normal_color", "MenuBar", icon_normal_color); + theme->set_color("icon_hover_color", "MenuBar", icon_hover_color); + theme->set_color("icon_focus_color", "MenuBar", icon_focus_color); + theme->set_color("icon_pressed_color", "MenuBar", icon_pressed_color); + theme->set_color("icon_disabled_color", "MenuBar", icon_disabled_color); + // OptionButton Ref<StyleBoxFlat> style_option_button_focus = style_widget_focus->duplicate(); Ref<StyleBoxFlat> style_option_button_normal = style_widget->duplicate(); @@ -912,6 +949,9 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Always display a border for PopupMenus so they can be distinguished from their background. style_popup_menu->set_border_width_all(EDSCALE); style_popup_menu->set_border_color(dark_color_2); + // Popups are separate windows by default in the editor. Windows currently don't support per-pixel transparency + // in 4.0, and even if it was, it may not always work in practice (e.g. running with compositing disabled). + style_popup_menu->set_corner_radius_all(0); theme->set_stylebox("panel", "PopupMenu", style_popup_menu); Ref<StyleBoxFlat> style_menu_hover = style_widget_hover->duplicate(); @@ -1460,6 +1500,17 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // PopupPanel theme->set_stylebox("panel", "PopupPanel", style_popup); + Ref<StyleBoxFlat> control_editor_popup_style = style_popup->duplicate(); + control_editor_popup_style->set_shadow_size(0); + control_editor_popup_style->set_default_margin(SIDE_LEFT, default_margin_size * EDSCALE); + control_editor_popup_style->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE); + control_editor_popup_style->set_default_margin(SIDE_RIGHT, default_margin_size * EDSCALE); + control_editor_popup_style->set_default_margin(SIDE_BOTTOM, default_margin_size * EDSCALE); + control_editor_popup_style->set_border_width_all(0); + + theme->set_stylebox("panel", "ControlEditorPopupButton", control_editor_popup_style); + theme->set_type_variation("ControlEditorPopupButton", "PopupPanel"); + // SpinBox theme->set_icon("updown", "SpinBox", theme->get_icon(SNAME("GuiSpinboxUpdown"), SNAME("EditorIcons"))); theme->set_icon("updown_disabled", "SpinBox", theme->get_icon(SNAME("GuiSpinboxUpdownDisabled"), SNAME("EditorIcons"))); @@ -1816,14 +1867,14 @@ Ref<Theme> create_custom_theme(const Ref<Theme> p_theme) { return theme; } -Ref<ImageTexture> create_unscaled_default_project_icon() { -#ifdef MODULE_SVG_ENABLED +/** + * Returns the SVG code for the default project icon. + */ +String get_default_project_icon() { for (int i = 0; i < editor_icons_count; i++) { - // ESCALE should never affect size of the icon if (strcmp(editor_icons_names[i], "DefaultProjectIcon") == 0) { - return editor_generate_icon(i, false, 1.0); + return String(editor_icons_sources[i]); } } -#endif - return Ref<ImageTexture>(memnew(ImageTexture)); + return String(); } diff --git a/editor/editor_themes.h b/editor/editor_themes.h index 95184b9d4a..37db8160fa 100644 --- a/editor/editor_themes.h +++ b/editor/editor_themes.h @@ -34,10 +34,24 @@ #include "scene/resources/texture.h" #include "scene/resources/theme.h" +// The default icon theme is designed to be used for a dark theme. This map stores +// Color values to convert to other colors for better readability on a light theme. +class EditorColorMap { + // Godot Color values are used to avoid the ambiguity of strings + // (where "#ffffff", "fff", and "white" are all equivalent). + static HashMap<Color, Color> editor_color_map; + +public: + static void create(); + static void add_color_pair(const String p_from_color, const String p_to_color); + + static HashMap<Color, Color> &get() { return editor_color_map; }; +}; + Ref<Theme> create_editor_theme(Ref<Theme> p_theme = nullptr); Ref<Theme> create_custom_theme(Ref<Theme> p_theme = nullptr); -Ref<ImageTexture> create_unscaled_default_project_icon(); +String get_default_project_icon(); #endif // EDITOR_THEMES_H diff --git a/editor/editor_title_bar.cpp b/editor/editor_title_bar.cpp new file mode 100644 index 0000000000..06dcea1f8a --- /dev/null +++ b/editor/editor_title_bar.cpp @@ -0,0 +1,86 @@ +/*************************************************************************/ +/* editor_title_bar.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "editor/editor_title_bar.h" + +void EditorTitleBar::input(const Ref<InputEvent> &p_event) { + if (!can_move) { + return; + } + + Ref<InputEventMouseMotion> mm = p_event; + if (mm.is_valid() && moving) { + if ((mm->get_button_mask() & MouseButton::LEFT) == MouseButton::LEFT) { + Window *w = Object::cast_to<Window>(get_viewport()); + if (w) { + Point2 mouse = DisplayServer::get_singleton()->mouse_get_position(); + w->set_position(mouse - click_pos); + } + } else { + moving = false; + } + } + + Ref<InputEventMouseButton> mb = p_event; + if (mb.is_valid() && has_point(mb->get_position())) { + Window *w = Object::cast_to<Window>(get_viewport()); + if (w) { + if (mb->get_button_index() == MouseButton::LEFT) { + if (mb->is_pressed()) { + click_pos = DisplayServer::get_singleton()->mouse_get_position() - w->get_position(); + moving = true; + } else { + moving = false; + } + } + if (mb->get_button_index() == MouseButton::LEFT && mb->is_double_click() && mb->is_pressed()) { + if (DisplayServer::get_singleton()->window_maximize_on_title_dbl_click()) { + if (w->get_mode() == Window::MODE_WINDOWED) { + w->set_mode(Window::MODE_MAXIMIZED); + } else if (w->get_mode() == Window::MODE_MAXIMIZED) { + w->set_mode(Window::MODE_WINDOWED); + } + } else if (DisplayServer::get_singleton()->window_minimize_on_title_dbl_click()) { + w->set_mode(Window::MODE_MINIMIZED); + } + moving = false; + } + } + } +} + +void EditorTitleBar::set_can_move_window(bool p_enabled) { + can_move = p_enabled; + set_process_input(can_move); +} + +bool EditorTitleBar::get_can_move_window() const { + return can_move; +} diff --git a/editor/editor_title_bar.h b/editor/editor_title_bar.h new file mode 100644 index 0000000000..ad6ec37ac9 --- /dev/null +++ b/editor/editor_title_bar.h @@ -0,0 +1,53 @@ +/*************************************************************************/ +/* editor_title_bar.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 EDITOR_TITLE_BAR_H +#define EDITOR_TITLE_BAR_H + +#include "scene/gui/box_container.h" +#include "scene/main/window.h" + +class EditorTitleBar : public HBoxContainer { + GDCLASS(EditorTitleBar, HBoxContainer); + + Point2i click_pos; + bool moving = false; + bool can_move = false; + +protected: + virtual void input(const Ref<InputEvent> &p_event) override; + static void _bind_methods(){}; + +public: + void set_can_move_window(bool p_enabled); + bool get_can_move_window() const; +}; + +#endif // EDITOR_TITLE_BAR_H diff --git a/editor/editor_toaster.cpp b/editor/editor_toaster.cpp index 050cde6069..15a4464298 100644 --- a/editor/editor_toaster.cpp +++ b/editor/editor_toaster.cpp @@ -105,7 +105,6 @@ 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_theme_icon(SNAME("Notification"), SNAME("EditorIcons"))); @@ -236,11 +235,11 @@ void EditorToaster::_auto_hide_or_free_toasts() { } if (toasts.is_empty()) { - main_button->set_tooltip(TTR("No notifications.")); + main_button->set_tooltip_text(TTR("No notifications.")); main_button->set_modulate(Color(0.5, 0.5, 0.5)); main_button->set_disabled(true); } else { - main_button->set_tooltip(TTR("Show notifications.")); + main_button->set_tooltip_text(TTR("Show notifications.")); main_button->set_modulate(Color(1, 1, 1)); main_button->set_disabled(false); } @@ -341,7 +340,7 @@ void EditorToaster::_repop_old() { Control *EditorToaster::popup(Control *p_control, Severity p_severity, double p_time, String p_tooltip) { // Create the panel according to the severity. PanelContainer *panel = memnew(PanelContainer); - panel->set_tooltip(p_tooltip); + panel->set_tooltip_text(p_tooltip); switch (p_severity) { case SEVERITY_INFO: panel->add_theme_style_override("panel", info_panel_style_background); @@ -518,7 +517,7 @@ EditorToaster::EditorToaster() { // Main button. main_button = memnew(Button); - main_button->set_tooltip(TTR("No notifications.")); + main_button->set_tooltip_text(TTR("No notifications.")); main_button->set_modulate(Color(0.5, 0.5, 0.5)); main_button->set_disabled(true); main_button->set_flat(true); @@ -534,7 +533,7 @@ EditorToaster::EditorToaster() { add_child(disable_notifications_panel); disable_notifications_button = memnew(Button); - disable_notifications_button->set_tooltip(TTR("Silence the notifications.")); + disable_notifications_button->set_tooltip_text(TTR("Silence the notifications.")); disable_notifications_button->set_flat(true); disable_notifications_button->connect("pressed", callable_mp(this, &EditorToaster::_set_notifications_enabled).bind(false)); disable_notifications_panel->add_child(disable_notifications_button); diff --git a/editor/editor_undo_redo_manager.cpp b/editor/editor_undo_redo_manager.cpp new file mode 100644 index 0000000000..eca2b3143b --- /dev/null +++ b/editor/editor_undo_redo_manager.cpp @@ -0,0 +1,442 @@ +/*************************************************************************/ +/* editor_undo_redo_manager.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "editor_undo_redo_manager.h" + +#include "core/io/resource.h" +#include "core/os/os.h" +#include "core/templates/local_vector.h" +#include "editor/editor_log.h" +#include "editor/editor_node.h" +#include "scene/main/node.h" + +EditorUndoRedoManager::History &EditorUndoRedoManager::get_or_create_history(int p_idx) { + if (!history_map.has(p_idx)) { + History history; + history.undo_redo = memnew(UndoRedo); + history.id = p_idx; + history_map[p_idx] = history; + + EditorNode::get_singleton()->get_log()->register_undo_redo(history.undo_redo); + EditorDebuggerNode::get_singleton()->register_undo_redo(history.undo_redo); + } + return history_map[p_idx]; +} + +UndoRedo *EditorUndoRedoManager::get_history_undo_redo(int p_idx) const { + ERR_FAIL_COND_V(!history_map.has(p_idx), nullptr); + return history_map[p_idx].undo_redo; +} + +int EditorUndoRedoManager::get_history_id_for_object(Object *p_object) const { + int history_id = INVALID_HISTORY; + + if (Node *node = Object::cast_to<Node>(p_object)) { + Node *edited_scene = EditorNode::get_singleton()->get_edited_scene(); + + if (edited_scene && (node == edited_scene || edited_scene->is_ancestor_of(node))) { + int idx = EditorNode::get_singleton()->get_editor_data().get_current_edited_scene_history_id(); + if (idx > 0) { + history_id = idx; + } + } + } + + if (Resource *res = Object::cast_to<Resource>(p_object)) { + if (res->is_built_in()) { + if (res->get_path().is_empty()) { + int idx = EditorNode::get_singleton()->get_editor_data().get_current_edited_scene_history_id(); + if (idx > 0) { + history_id = idx; + } + } else { + int idx = EditorNode::get_singleton()->get_editor_data().get_scene_history_id_from_path(res->get_path().get_slice("::", 0)); + if (idx > 0) { + history_id = idx; + } + } + } + } + + if (history_id == INVALID_HISTORY) { + if (pending_action.history_id != INVALID_HISTORY) { + history_id = pending_action.history_id; + } else { + history_id = GLOBAL_HISTORY; + } + } + return history_id; +} + +EditorUndoRedoManager::History &EditorUndoRedoManager::get_history_for_object(Object *p_object) { + int history_id = get_history_id_for_object(p_object); + ERR_FAIL_COND_V_MSG(pending_action.history_id != INVALID_HISTORY && history_id != pending_action.history_id, get_or_create_history(pending_action.history_id), vformat("UndoRedo history mismatch: expected %d, got %d.", pending_action.history_id, history_id)); + + History &history = get_or_create_history(history_id); + if (pending_action.history_id == INVALID_HISTORY) { + pending_action.history_id = history_id; + history.undo_redo->create_action(pending_action.action_name, pending_action.merge_mode); + } + + return history; +} + +void EditorUndoRedoManager::create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode) { + pending_action.action_name = p_name; + pending_action.timestamp = OS::get_singleton()->get_unix_time(); + pending_action.merge_mode = p_mode; + + if (p_history_id != INVALID_HISTORY) { + pending_action.history_id = p_history_id; + History &history = get_or_create_history(p_history_id); + history.undo_redo->create_action(pending_action.action_name, pending_action.merge_mode); + } +} + +void EditorUndoRedoManager::create_action(const String &p_name, UndoRedo::MergeMode p_mode, Object *p_custom_context) { + create_action_for_history(p_name, INVALID_HISTORY, p_mode); + + if (p_custom_context) { + // This assigns context to pending action. + get_history_for_object(p_custom_context); + } +} + +void EditorUndoRedoManager::add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { + UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; + undo_redo->add_do_methodp(p_object, p_method, p_args, p_argcount); +} + +void EditorUndoRedoManager::add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount) { + UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; + undo_redo->add_undo_methodp(p_object, p_method, p_args, p_argcount); +} + +void EditorUndoRedoManager::_add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 0; + return; + } + + if (p_args[0]->get_type() != Variant::OBJECT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return; + } + + if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING_NAME; + return; + } + + r_error.error = Callable::CallError::CALL_OK; + + Object *object = *p_args[0]; + StringName method = *p_args[1]; + + add_do_methodp(object, method, p_args + 2, p_argcount - 2); +} + +void EditorUndoRedoManager::_add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { + if (p_argcount < 2) { + r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument = 0; + return; + } + + if (p_args[0]->get_type() != Variant::OBJECT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::OBJECT; + return; + } + + if (p_args[1]->get_type() != Variant::STRING_NAME && p_args[1]->get_type() != Variant::STRING) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::STRING_NAME; + return; + } + + r_error.error = Callable::CallError::CALL_OK; + + Object *object = *p_args[0]; + StringName method = *p_args[1]; + + add_undo_methodp(object, method, p_args + 2, p_argcount - 2); +} + +void EditorUndoRedoManager::add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value) { + UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; + undo_redo->add_do_property(p_object, p_property, p_value); +} + +void EditorUndoRedoManager::add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value) { + UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; + undo_redo->add_undo_property(p_object, p_property, p_value); +} + +void EditorUndoRedoManager::add_do_reference(Object *p_object) { + UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; + undo_redo->add_do_reference(p_object); +} + +void EditorUndoRedoManager::add_undo_reference(Object *p_object) { + UndoRedo *undo_redo = get_history_for_object(p_object).undo_redo; + undo_redo->add_undo_reference(p_object); +} + +void EditorUndoRedoManager::commit_action(bool p_execute) { + ERR_FAIL_COND(pending_action.history_id == INVALID_HISTORY); + is_committing = true; + + History &history = get_or_create_history(pending_action.history_id); + history.undo_redo->commit_action(p_execute); + history.redo_stack.clear(); + + if (!history.undo_stack.is_empty()) { + const Action &prev_action = history.undo_stack.back()->get(); + if (pending_action.merge_mode != UndoRedo::MERGE_DISABLE && pending_action.merge_mode == prev_action.merge_mode && pending_action.action_name == prev_action.action_name) { + // Discard action if it should be merged (UndoRedo handles merging internally). + pending_action = Action(); + is_committing = false; + return; + } + } + + history.undo_stack.push_back(pending_action); + pending_action = Action(); + is_committing = false; +} + +bool EditorUndoRedoManager::is_committing_action() const { + return is_committing; +} + +bool EditorUndoRedoManager::undo() { + if (!has_undo()) { + return false; + } + + History *selected_history = nullptr; + double global_timestamp = 0; + + // Pick the history with greatest last action timestamp (either global or current scene). + { + History &history = get_or_create_history(GLOBAL_HISTORY); + if (!history.undo_stack.is_empty()) { + selected_history = &history; + global_timestamp = history.undo_stack.back()->get().timestamp; + } + } + + { + History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); + if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { + selected_history = &history; + } + } + + if (selected_history) { + Action action = selected_history->undo_stack.back()->get(); + selected_history->undo_stack.pop_back(); + selected_history->redo_stack.push_back(action); + return selected_history->undo_redo->undo(); + } + return false; +} + +bool EditorUndoRedoManager::redo() { + if (!has_redo()) { + return false; + } + + History *selected_history = nullptr; + double global_timestamp = INFINITY; + + // Pick the history with lowest last action timestamp (either global or current scene). + { + History &history = get_or_create_history(GLOBAL_HISTORY); + if (!history.redo_stack.is_empty()) { + selected_history = &history; + global_timestamp = history.redo_stack.back()->get().timestamp; + } + } + + { + History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); + if (!history.redo_stack.is_empty() && history.redo_stack.back()->get().timestamp < global_timestamp) { + selected_history = &history; + } + } + + if (selected_history) { + Action action = selected_history->redo_stack.back()->get(); + selected_history->redo_stack.pop_back(); + selected_history->undo_stack.push_back(action); + return selected_history->undo_redo->redo(); + } + return false; +} + +void EditorUndoRedoManager::set_history_as_saved(int p_id) { + History &history = get_or_create_history(p_id); + history.saved_version = history.undo_redo->get_version(); +} + +void EditorUndoRedoManager::set_history_as_unsaved(int p_id) { + History &history = get_or_create_history(p_id); + history.saved_version = -1; +} + +bool EditorUndoRedoManager::is_history_unsaved(int p_id) { + History &history = get_or_create_history(p_id); + return history.undo_redo->get_version() != history.saved_version; +} + +bool EditorUndoRedoManager::has_undo() { + for (const KeyValue<int, History> &E : history_map) { + if ((E.key == GLOBAL_HISTORY || E.key == EditorNode::get_editor_data().get_current_edited_scene_history_id()) && !E.value.undo_stack.is_empty()) { + return true; + } + } + return false; +} + +bool EditorUndoRedoManager::has_redo() { + for (const KeyValue<int, History> &E : history_map) { + if ((E.key == GLOBAL_HISTORY || E.key == EditorNode::get_editor_data().get_current_edited_scene_history_id()) && !E.value.redo_stack.is_empty()) { + return true; + } + } + return false; +} + +void EditorUndoRedoManager::clear_history(bool p_increase_version, int p_idx) { + if (p_idx != INVALID_HISTORY) { + get_or_create_history(p_idx).undo_redo->clear_history(p_increase_version); + if (!p_increase_version) { + set_history_as_saved(p_idx); + } + return; + } + + for (const KeyValue<int, History> &E : history_map) { + E.value.undo_redo->clear_history(p_increase_version); + set_history_as_saved(E.key); + } +} + +String EditorUndoRedoManager::get_current_action_name() { + if (has_undo()) { + History *selected_history = nullptr; + double global_timestamp = 0; + + // Pick the history with greatest last action timestamp (either global or current scene). + { + History &history = get_or_create_history(GLOBAL_HISTORY); + if (!history.undo_stack.is_empty()) { + selected_history = &history; + global_timestamp = history.undo_stack.back()->get().timestamp; + } + } + + { + History &history = get_or_create_history(EditorNode::get_editor_data().get_current_edited_scene_history_id()); + if (!history.undo_stack.is_empty() && history.undo_stack.back()->get().timestamp > global_timestamp) { + selected_history = &history; + } + } + + if (selected_history) { + return selected_history->undo_redo->get_current_action_name(); + } + } + return ""; +} + +void EditorUndoRedoManager::discard_history(int p_idx, bool p_erase_from_map) { + ERR_FAIL_COND(!history_map.has(p_idx)); + History &history = history_map[p_idx]; + + if (history.undo_redo) { + memdelete(history.undo_redo); + history.undo_redo = nullptr; + } + + if (p_erase_from_map) { + history_map.erase(p_idx); + } +} + +void EditorUndoRedoManager::_bind_methods() { + ClassDB::bind_method(D_METHOD("create_action", "name", "merge_mode", "custom_context"), &EditorUndoRedoManager::create_action, DEFVAL(UndoRedo::MERGE_DISABLE), DEFVAL((Object *)nullptr)); + ClassDB::bind_method(D_METHOD("commit_action", "execute"), &EditorUndoRedoManager::commit_action, DEFVAL(true)); + ClassDB::bind_method(D_METHOD("is_committing_action"), &EditorUndoRedoManager::is_committing_action); + + { + MethodInfo mi; + mi.name = "add_do_method"; + mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); + mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method")); + + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_do_method", &EditorUndoRedoManager::_add_do_method, mi, varray(), false); + } + + { + MethodInfo mi; + mi.name = "add_undo_method"; + mi.arguments.push_back(PropertyInfo(Variant::OBJECT, "object")); + mi.arguments.push_back(PropertyInfo(Variant::STRING_NAME, "method")); + + ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "add_undo_method", &EditorUndoRedoManager::_add_undo_method, mi, varray(), false); + } + + ClassDB::bind_method(D_METHOD("add_do_property", "object", "property", "value"), &EditorUndoRedoManager::add_do_property); + ClassDB::bind_method(D_METHOD("add_undo_property", "object", "property", "value"), &EditorUndoRedoManager::add_undo_property); + ClassDB::bind_method(D_METHOD("add_do_reference", "object"), &EditorUndoRedoManager::add_do_reference); + ClassDB::bind_method(D_METHOD("add_undo_reference", "object"), &EditorUndoRedoManager::add_undo_reference); + + ClassDB::bind_method(D_METHOD("get_object_history_id", "object"), &EditorUndoRedoManager::get_history_id_for_object); + ClassDB::bind_method(D_METHOD("get_history_undo_redo", "id"), &EditorUndoRedoManager::get_history_undo_redo); + + BIND_ENUM_CONSTANT(GLOBAL_HISTORY); + BIND_ENUM_CONSTANT(INVALID_HISTORY); +} + +EditorUndoRedoManager::~EditorUndoRedoManager() { + for (const KeyValue<int, History> &E : history_map) { + discard_history(E.key, false); + } +} diff --git a/editor/editor_undo_redo_manager.h b/editor/editor_undo_redo_manager.h new file mode 100644 index 0000000000..c4d85daa22 --- /dev/null +++ b/editor/editor_undo_redo_manager.h @@ -0,0 +1,134 @@ +/*************************************************************************/ +/* editor_undo_redo_manager.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 EDITOR_UNDO_REDO_MANAGER_H +#define EDITOR_UNDO_REDO_MANAGER_H + +#include "core/object/class_db.h" +#include "core/object/ref_counted.h" +#include "core/object/undo_redo.h" + +class EditorUndoRedoManager : public RefCounted { + GDCLASS(EditorUndoRedoManager, RefCounted); + +public: + enum SpecialHistory { + GLOBAL_HISTORY = 0, + INVALID_HISTORY = -99, + }; + +private: + struct Action { + int history_id = INVALID_HISTORY; + double timestamp = 0; + String action_name; + UndoRedo::MergeMode merge_mode = UndoRedo::MERGE_DISABLE; + }; + + struct History { + int id = INVALID_HISTORY; + UndoRedo *undo_redo = nullptr; + uint64_t saved_version = 1; + List<Action> undo_stack; + List<Action> redo_stack; + }; + + HashMap<int, History> history_map; + Action pending_action; + + bool is_committing = false; + +protected: + static void _bind_methods(); + +public: + History &get_or_create_history(int p_idx); + UndoRedo *get_history_undo_redo(int p_idx) const; + int get_history_id_for_object(Object *p_object) const; + History &get_history_for_object(Object *p_object); + + void create_action_for_history(const String &p_name, int p_history_id, UndoRedo::MergeMode p_mode = UndoRedo::MERGE_DISABLE); + void create_action(const String &p_name = "", UndoRedo::MergeMode p_mode = UndoRedo::MERGE_DISABLE, Object *p_custom_context = nullptr); + + void add_do_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); + void add_undo_methodp(Object *p_object, const StringName &p_method, const Variant **p_args, int p_argcount); + + template <typename... VarArgs> + void add_do_method(Object *p_object, const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + + add_do_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + } + + template <typename... VarArgs> + void add_undo_method(Object *p_object, const StringName &p_method, VarArgs... p_args) { + Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. + const Variant *argptrs[sizeof...(p_args) + 1]; + for (uint32_t i = 0; i < sizeof...(p_args); i++) { + argptrs[i] = &args[i]; + } + + add_undo_methodp(p_object, p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); + } + + void _add_do_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + void _add_undo_method(const Variant **p_args, int p_argcount, Callable::CallError &r_error); + + void add_do_property(Object *p_object, const StringName &p_property, const Variant &p_value); + void add_undo_property(Object *p_object, const StringName &p_property, const Variant &p_value); + void add_do_reference(Object *p_object); + void add_undo_reference(Object *p_object); + + void commit_action(bool p_execute = true); + bool is_committing_action() const; + + bool undo(); + bool redo(); + void clear_history(bool p_increase_version = true, int p_idx = INVALID_HISTORY); + + void set_history_as_saved(int p_idx); + void set_history_as_unsaved(int p_idx); + bool is_history_unsaved(int p_idx); + bool has_undo(); + bool has_redo(); + + String get_current_action_name(); + + void discard_history(int p_idx, bool p_erase_from_map = true); + ~EditorUndoRedoManager(); +}; + +VARIANT_ENUM_CAST(EditorUndoRedoManager::SpecialHistory); + +#endif // EDITOR_UNDO_REDO_MANAGER_H diff --git a/editor/editor_vcs_interface.cpp b/editor/editor_vcs_interface.cpp index 3f2012cc16..cb188f9c3e 100644 --- a/editor/editor_vcs_interface.cpp +++ b/editor/editor_vcs_interface.cpp @@ -82,8 +82,8 @@ void EditorVCSInterface::_unstage_file(String p_file_path) { void EditorVCSInterface::_commit(String p_msg) { } -Array EditorVCSInterface::_get_file_diff(String p_file_path) { - return Array(); +TypedArray<Dictionary> EditorVCSInterface::_get_file_diff(String p_file_path) { + return TypedArray<Dictionary>(); } bool EditorVCSInterface::_shut_down() { @@ -133,11 +133,11 @@ void EditorVCSInterface::commit(String p_msg) { } } -Array EditorVCSInterface::get_file_diff(String p_file_path) { +TypedArray<Dictionary> EditorVCSInterface::get_file_diff(String p_file_path) { if (is_addon_ready()) { return call("_get_file_diff", p_file_path); } - return Array(); + return TypedArray<Dictionary>(); } bool EditorVCSInterface::shut_down() { diff --git a/editor/editor_vcs_interface.h b/editor/editor_vcs_interface.h index 6a6fca7eba..d6d7ffa0e9 100644 --- a/editor/editor_vcs_interface.h +++ b/editor/editor_vcs_interface.h @@ -52,7 +52,7 @@ protected: virtual void _stage_file(String p_file_path); virtual void _unstage_file(String p_file_path); virtual void _commit(String p_msg); - virtual Array _get_file_diff(String p_file_path); + virtual TypedArray<Dictionary> _get_file_diff(String p_file_path); virtual bool _shut_down(); virtual String _get_project_name(); virtual String _get_vcs_name(); @@ -76,7 +76,7 @@ public: void stage_file(String p_file_path); void unstage_file(String p_file_path); void commit(String p_msg); - Array get_file_diff(String p_file_path); + TypedArray<Dictionary> get_file_diff(String p_file_path); bool shut_down(); String get_project_name(); String get_vcs_name(); diff --git a/editor/editor_zoom_widget.cpp b/editor/editor_zoom_widget.cpp index e4beea5e5f..512fa1c86c 100644 --- a/editor/editor_zoom_widget.cpp +++ b/editor/editor_zoom_widget.cpp @@ -143,7 +143,6 @@ void EditorZoomWidget::set_zoom_by_increments(int p_increment_count, bool p_inte void EditorZoomWidget::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { zoom_minus->set_icon(get_theme_icon(SNAME("ZoomLess"), SNAME("EditorIcons"))); zoom_plus->set_icon(get_theme_icon(SNAME("ZoomMore"), SNAME("EditorIcons"))); diff --git a/editor/export/editor_export.cpp b/editor/export/editor_export.cpp index 31f408eedb..d291040bd2 100644 --- a/editor/export/editor_export.cpp +++ b/editor/export/editor_export.cpp @@ -312,12 +312,14 @@ void EditorExport::update_export_presets() { // Clear the preset properties and values prior to reloading preset->properties.clear(); preset->values.clear(); + preset->update_visibility.clear(); for (const EditorExportPlatform::ExportOption &E : options) { preset->properties.push_back(E.option); StringName option_name = E.option.name; preset->values[option_name] = previous_values.has(option_name) ? previous_values[option_name] : E.default_value; + preset->update_visibility[option_name] = E.update_visibility; } } } diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp index ab1586cb77..8283c24e61 100644 --- a/editor/export/editor_export_platform.cpp +++ b/editor/export/editor_export_platform.cpp @@ -323,6 +323,7 @@ Ref<EditorExportPreset> EditorExportPlatform::create_preset() { for (const ExportOption &E : options) { preset->properties.push_back(E.option); preset->values[E.option.name] = E.default_value; + preset->update_visibility[E.option.name] = E.update_visibility; } return preset; diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h index c870ee66aa..bbdb47e041 100644 --- a/editor/export/editor_export_platform.h +++ b/editor/export/editor_export_platform.h @@ -117,10 +117,12 @@ public: struct ExportOption { PropertyInfo option; Variant default_value; + bool update_visibility = false; - ExportOption(const PropertyInfo &p_info, const Variant &p_default) : + ExportOption(const PropertyInfo &p_info, const Variant &p_default, bool p_update_visibility = false) : option(p_info), - default_value(p_default) { + default_value(p_default), + update_visibility(p_update_visibility) { } ExportOption() {} }; @@ -136,13 +138,13 @@ public: messages.push_back(msg); switch (p_type) { case EXPORT_MESSAGE_INFO: { - print_line(vformat("%s: %s\n", msg.category, msg.text)); + print_line(vformat("%s: %s", msg.category, msg.text)); } break; case EXPORT_MESSAGE_WARNING: { - WARN_PRINT(vformat("%s: %s\n", msg.category, msg.text)); + WARN_PRINT(vformat("%s: %s", msg.category, msg.text)); } break; case EXPORT_MESSAGE_ERROR: { - ERR_PRINT(vformat("%s: %s\n", msg.category, msg.text)); + ERR_PRINT(vformat("%s: %s", msg.category, msg.text)); } break; default: break; @@ -170,7 +172,7 @@ public: virtual void get_export_options(List<ExportOption> *r_options) = 0; virtual bool should_update_export_options() { return false; } - virtual bool get_export_option_visibility(const String &p_option, const HashMap<StringName, Variant> &p_options) const { return true; } + virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option, const HashMap<StringName, Variant> &p_options) const { return true; } virtual String get_os_name() const = 0; virtual String get_name() const = 0; diff --git a/editor/export/editor_export_preset.cpp b/editor/export/editor_export_preset.cpp index cdf69e727d..4411ad11bc 100644 --- a/editor/export/editor_export_preset.cpp +++ b/editor/export/editor_export_preset.cpp @@ -34,6 +34,9 @@ bool EditorExportPreset::_set(const StringName &p_name, const Variant &p_value) if (values.has(p_name)) { values[p_name] = p_value; EditorExport::singleton->save_presets(); + if (update_visibility[p_name]) { + notify_property_list_changed(); + } return true; } @@ -51,7 +54,7 @@ bool EditorExportPreset::_get(const StringName &p_name, Variant &r_ret) const { void EditorExportPreset::_get_property_list(List<PropertyInfo> *p_list) const { for (const PropertyInfo &E : properties) { - if (platform->get_export_option_visibility(E.name, values)) { + if (platform->get_export_option_visibility(this, E.name, values)) { p_list->push_back(E); } } diff --git a/editor/export/editor_export_preset.h b/editor/export/editor_export_preset.h index 00109396b0..0c780c45cd 100644 --- a/editor/export/editor_export_preset.h +++ b/editor/export/editor_export_preset.h @@ -67,6 +67,7 @@ private: List<PropertyInfo> properties; HashMap<StringName, Variant> values; + HashMap<StringName, bool> update_visibility; String name; diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp index a7d9d7f068..ebcf60508a 100644 --- a/editor/export/export_template_manager.cpp +++ b/editor/export/export_template_manager.cpp @@ -754,7 +754,6 @@ Error ExportTemplateManager::install_android_template_from_file(const String &p_ void ExportTemplateManager::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { current_value->add_theme_font_override("font", get_theme_font(SNAME("main"), SNAME("EditorFonts"))); current_missing_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); @@ -868,13 +867,13 @@ ExportTemplateManager::ExportTemplateManager() { current_open_button = memnew(Button); current_open_button->set_text(TTR("Open Folder")); - current_open_button->set_tooltip(TTR("Open the folder containing installed templates for the current version.")); + current_open_button->set_tooltip_text(TTR("Open the folder containing installed templates for the current version.")); current_installed_hb->add_child(current_open_button); current_open_button->connect("pressed", callable_mp(this, &ExportTemplateManager::_open_template_folder).bind(VERSION_FULL_CONFIG)); current_uninstall_button = memnew(Button); current_uninstall_button->set_text(TTR("Uninstall")); - current_uninstall_button->set_tooltip(TTR("Uninstall templates for the current version.")); + current_uninstall_button->set_tooltip_text(TTR("Uninstall templates for the current version.")); current_installed_hb->add_child(current_uninstall_button); current_uninstall_button->connect("pressed", callable_mp(this, &ExportTemplateManager::_uninstall_template).bind(VERSION_FULL_CONFIG)); @@ -915,14 +914,14 @@ ExportTemplateManager::ExportTemplateManager() { Button *download_current_button = memnew(Button); download_current_button->set_text(TTR("Download and Install")); - download_current_button->set_tooltip(TTR("Download and install templates for the current version from the best possible mirror.")); + download_current_button->set_tooltip_text(TTR("Download and install templates for the current version from the best possible mirror.")); download_install_hb->add_child(download_current_button); download_current_button->connect("pressed", callable_mp(this, &ExportTemplateManager::_download_current)); // Update downloads buttons to prevent unsupported downloads. if (!downloads_available) { download_current_button->set_disabled(true); - download_current_button->set_tooltip(TTR("Official export templates aren't available for development builds.")); + download_current_button->set_tooltip_text(TTR("Official export templates aren't available for development builds.")); } HBoxContainer *install_file_hb = memnew(HBoxContainer); @@ -931,7 +930,7 @@ ExportTemplateManager::ExportTemplateManager() { install_file_button = memnew(Button); install_file_button->set_text(TTR("Install from File")); - install_file_button->set_tooltip(TTR("Install templates from a local file.")); + install_file_button->set_tooltip_text(TTR("Install templates from a local file.")); install_file_hb->add_child(install_file_button); install_file_button->connect("pressed", callable_mp(this, &ExportTemplateManager::_install_file)); @@ -956,7 +955,7 @@ ExportTemplateManager::ExportTemplateManager() { Button *download_cancel_button = memnew(Button); download_cancel_button->set_text(TTR("Cancel")); - download_cancel_button->set_tooltip(TTR("Cancel the download of the templates.")); + download_cancel_button->set_tooltip_text(TTR("Cancel the download of the templates.")); download_progress_hb->add_child(download_cancel_button); download_cancel_button->connect("pressed", callable_mp(this, &ExportTemplateManager::_cancel_template_download)); diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 76493d330f..00a0e08d3a 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -185,9 +185,9 @@ void ProjectExportDialog::_update_export_all() { export_all_button->set_disabled(!can_export); if (can_export) { - export_all_button->set_tooltip(TTR("Export the project for all the presets defined.")); + export_all_button->set_tooltip_text(TTR("Export the project for all the presets defined.")); } else { - export_all_button->set_tooltip(TTR("All presets must have an export path defined for Export All to work.")); + export_all_button->set_tooltip_text(TTR("All presets must have an export path defined for Export All to work.")); } } @@ -1020,12 +1020,12 @@ ProjectExportDialog::ProjectExportDialog() { mc->add_child(presets); presets->connect("item_selected", callable_mp(this, &ProjectExportDialog::_edit_preset)); duplicate_preset = memnew(Button); - duplicate_preset->set_tooltip(TTR("Duplicate")); + duplicate_preset->set_tooltip_text(TTR("Duplicate")); duplicate_preset->set_flat(true); preset_hb->add_child(duplicate_preset); duplicate_preset->connect("pressed", callable_mp(this, &ProjectExportDialog::_duplicate_preset)); delete_preset = memnew(Button); - delete_preset->set_tooltip(TTR("Delete")); + delete_preset->set_tooltip_text(TTR("Delete")); delete_preset->set_flat(true); preset_hb->add_child(delete_preset); delete_preset->connect("pressed", callable_mp(this, &ProjectExportDialog::_delete_preset)); @@ -1041,7 +1041,7 @@ ProjectExportDialog::ProjectExportDialog() { name->connect("text_changed", callable_mp(this, &ProjectExportDialog::_name_changed)); runnable = memnew(CheckButton); runnable->set_text(TTR("Runnable")); - runnable->set_tooltip(TTR("If checked, the preset will be available for use in one-click deploy.\nOnly one preset per platform may be marked as runnable.")); + runnable->set_tooltip_text(TTR("If checked, the preset will be available for use in one-click deploy.\nOnly one preset per platform may be marked as runnable.")); runnable->connect("pressed", callable_mp(this, &ProjectExportDialog::_runnable_pressed)); settings_vb->add_child(runnable); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index b823db68f0..a42433caee 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -614,11 +614,11 @@ 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_theme_icon(SNAME("FileThumbnail"), SNAME("EditorIcons"))); - button_file_list_display_mode->set_tooltip(TTR("View items as a grid of thumbnails.")); + 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_theme_icon(SNAME("FileList"), SNAME("EditorIcons"))); - button_file_list_display_mode->set_tooltip(TTR("View items as a list.")); + button_file_list_display_mode->set_tooltip_text(TTR("View items as a list.")); } _update_file_list(true); @@ -2977,7 +2977,7 @@ void FileSystemDock::_file_sort_popup(int p_id) { MenuButton *FileSystemDock::_create_file_menu_button() { MenuButton *button = memnew(MenuButton); button->set_flat(true); - button->set_tooltip(TTR("Sort files")); + button->set_tooltip_text(TTR("Sort files")); PopupMenu *p = button->get_popup(); p->connect("id_pressed", callable_mp(this, &FileSystemDock::_file_sort_popup)); @@ -3040,14 +3040,14 @@ FileSystemDock::FileSystemDock() { button_hist_prev->set_flat(true); button_hist_prev->set_disabled(true); button_hist_prev->set_focus_mode(FOCUS_NONE); - button_hist_prev->set_tooltip(TTR("Previous Folder/File")); + button_hist_prev->set_tooltip_text(TTR("Previous Folder/File")); toolbar_hbc->add_child(button_hist_prev); button_hist_next = memnew(Button); button_hist_next->set_flat(true); button_hist_next->set_disabled(true); button_hist_next->set_focus_mode(FOCUS_NONE); - button_hist_next->set_tooltip(TTR("Next Folder/File")); + button_hist_next->set_tooltip_text(TTR("Next Folder/File")); toolbar_hbc->add_child(button_hist_next); current_path = memnew(LineEdit); @@ -3059,7 +3059,7 @@ FileSystemDock::FileSystemDock() { button_reload = memnew(Button); button_reload->connect("pressed", callable_mp(this, &FileSystemDock::_rescan)); button_reload->set_focus_mode(FOCUS_NONE); - button_reload->set_tooltip(TTR("Re-Scan Filesystem")); + button_reload->set_tooltip_text(TTR("Re-Scan Filesystem")); button_reload->hide(); toolbar_hbc->add_child(button_reload); @@ -3067,7 +3067,7 @@ FileSystemDock::FileSystemDock() { button_toggle_display_mode->set_toggle_mode(true); button_toggle_display_mode->connect("toggled", callable_mp(this, &FileSystemDock::_toggle_split_mode)); button_toggle_display_mode->set_focus_mode(FOCUS_NONE); - button_toggle_display_mode->set_tooltip(TTR("Toggle Split Mode")); + button_toggle_display_mode->set_tooltip_text(TTR("Toggle Split Mode")); button_toggle_display_mode->set_flat(true); toolbar_hbc->add_child(button_toggle_display_mode); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 81b71b5609..56387e87f5 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -373,7 +373,7 @@ FindInFilesDialog::FindInFilesDialog() { Label *filter_label = memnew(Label); filter_label->set_text(TTR("Filters:")); - filter_label->set_tooltip(TTR("Include the files with the following extensions. Add or remove them in ProjectSettings.")); + filter_label->set_tooltip_text(TTR("Include the files with the following extensions. Add or remove them in ProjectSettings.")); gc->add_child(filter_label); _filters_container = memnew(HBoxContainer); diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index f16097f109..87721a2bbf 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/scene_tree_dock.h" #include "editor/scene_tree_editor.h" #include "scene/gui/box_container.h" @@ -379,7 +380,6 @@ void GroupDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_TRANSLATION_CHANGED: case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (is_layout_rtl()) { add_button->set_icon(groups->get_theme_icon(SNAME("Back"), SNAME("EditorIcons"))); @@ -397,6 +397,10 @@ void GroupDialog::_notification(int p_what) { } } +void GroupDialog::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + void GroupDialog::edit() { popup_centered(); @@ -696,6 +700,10 @@ void GroupsEditor::update_tree() { } } +void GroupsEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + void GroupsEditor::set_current(Node *p_node) { node = p_node; update_tree(); diff --git a/editor/groups_editor.h b/editor/groups_editor.h index fec8913e31..8bbea4e652 100644 --- a/editor/groups_editor.h +++ b/editor/groups_editor.h @@ -31,7 +31,6 @@ #ifndef GROUPS_EDITOR_H #define GROUPS_EDITOR_H -#include "core/object/undo_redo.h" #include "editor/scene_tree_editor.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" @@ -40,6 +39,8 @@ #include "scene/gui/popup.h" #include "scene/gui/tree.h" +class EditorUndoRedoManager; + class GroupDialog : public AcceptDialog { GDCLASS(GroupDialog, AcceptDialog); @@ -68,7 +69,7 @@ class GroupDialog : public AcceptDialog { String selected_group; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; void _group_selected(); @@ -103,7 +104,7 @@ public: }; void edit(); - void set_undo_redo(UndoRedo *p_undoredo) { undo_redo = p_undoredo; } + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); GroupDialog(); }; @@ -119,7 +120,7 @@ class GroupsEditor : public VBoxContainer { Button *add = nullptr; Tree *tree = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; void update_tree(); void _add_group(const String &p_group = ""); @@ -137,7 +138,7 @@ public: COPY_GROUP, }; - void set_undo_redo(UndoRedo *p_undoredo) { undo_redo = p_undoredo; } + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void set_current(Node *p_node); GroupsEditor(); diff --git a/editor/icons/BezierHandlesBalanced.svg b/editor/icons/BezierHandlesBalanced.svg index 911029e431..b1778b1a5e 100644 --- a/editor/icons/BezierHandlesBalanced.svg +++ b/editor/icons/BezierHandlesBalanced.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.7627119 13.627119s1.2881355-6.847458 6.5762712-8.1355935c5.0847459.9491522 5.9661009 8.1355925 5.9661009 8.1355925" fill="none" stroke="#5fb2ff" stroke-miterlimit="4.9" stroke-width="1.7"/><ellipse cx="1.898304" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><ellipse cx="14.237288" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><path d="m7.4559186 5.1473018-4.7355323 1.5541798" fill="none" stroke="#5fb2ff" stroke-width=".618"/><path d="m10.790357 4.2063094-2.5009748.9433136" fill="none" stroke="#5fb2ff" stroke-width=".614897"/><g fill="#e0e0e0"><ellipse cx="8.271187" cy="4.779661" rx="1.267586" ry="1.199789"/><path d="m1.7157324 5.8754878a1.2675855 1.1997888 0 0 0 -1.26757806 1.1992188 1.2675855 1.1997888 0 0 0 1.26757806 1.1992187 1.2675855 1.1997888 0 0 0 1.2675781-1.1992187 1.2675855 1.1997888 0 0 0 -1.2675781-1.1992188zm.00195.4238282a.84677333.80148375 0 0 1 .8476593.8007812.84677333.80148375 0 0 1 -.8476562.8007812.84677333.80148375 0 0 1 -.84765616-.8007812.84677333.80148375 0 0 1 .84765616-.8007812z"/><path d="m11.909414 2.4642073a1.2836218 1.231838 0 0 0 -1.283614 1.2312528 1.2836218 1.231838 0 0 0 1.283614 1.2312527 1.2836218 1.231838 0 0 0 1.283614-1.2312527 1.2836218 1.231838 0 0 0 -1.283614-1.2312528zm.002.4351497a.85748593.82289328 0 0 1 .858383.8221719.85748593.82289328 0 0 1 -.85838.822172.85748593.82289328 0 0 1 -.858379-.822172.85748593.82289328 0 0 1 .858379-.8221719z"/></g></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.7627119 13.627119s1.2881355-6.847458 6.5762712-8.1355935c5.0847459.9491522 5.9661009 8.1355925 5.9661009 8.1355925" fill="none" stroke="#87b1d7" stroke-miterlimit="4.9" stroke-width=".5"/><path d="m2.4962504 7.6963851 10.1811806-3.7166314" fill="none" stroke="#61b2ff" stroke-width="1.5"/><g fill="#e0e0e0"><ellipse cx="1.898304" cy="13.491526" rx="1.267586" ry="1.199789"/><ellipse cx="14.237288" cy="13.491526" rx="1.267586" ry="1.199789"/><ellipse cx="8.338983" cy="5.491526" rx="1.267586" ry="1.199789"/><path d="m1.6910776 6.7273a1.2675855 1.1997888 0 0 0 -1.26757808 1.1992188 1.2675855 1.1997888 0 0 0 1.26757808 1.1992187 1.2675855 1.1997888 0 0 0 1.2675781-1.1992187 1.2675855 1.1997888 0 0 0 -1.2675781-1.1992188zm.00195.4238282a.84677333.80148375 0 0 1 .8476593.8007812.84677333.80148375 0 0 1 -.8476562.8007812.84677333.80148375 0 0 1 -.84765618-.8007812.84677333.80148375 0 0 1 .84765618-.8007812z"/><path d="m13.40948 2.2963899a1.2836218 1.231838 0 0 0 -1.283614 1.2312528 1.2836218 1.231838 0 0 0 1.283614 1.2312526 1.2836218 1.231838 0 0 0 1.283614-1.2312526 1.2836218 1.231838 0 0 0 -1.283614-1.2312528zm.002.4351497a.85748593.82289328 0 0 1 .858383.8221719.85748593.82289328 0 0 1 -.85838.8221719.85748593.82289328 0 0 1 -.858379-.8221719.85748593.82289328 0 0 1 .858379-.8221719z"/></g></svg> diff --git a/editor/icons/BezierHandlesFree.svg b/editor/icons/BezierHandlesFree.svg index 6e91288c79..c7bff530ae 100644 --- a/editor/icons/BezierHandlesFree.svg +++ b/editor/icons/BezierHandlesFree.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.7627119 13.627119s1.2881355-6.847458 6.5762712-8.1355935c5.0847459.9491522 5.9661009 8.1355925 5.9661009 8.1355925" fill="none" stroke="#5fb2ff" stroke-miterlimit="4.9" stroke-width="1.7"/><ellipse cx="1.898304" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><ellipse cx="14.237288" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><path d="m7.6850253 4.7560401-3.776127.6607599" fill="none" stroke="#5fb2ff" stroke-width=".805138"/><path d="m11.695505 2.3941651-2.999121 2.2935078" fill="none" stroke="#5fb2ff" stroke-width=".730798"/><g fill="#e0e0e0"><ellipse cx="8.271187" cy="4.779661" rx="1.267586" ry="1.199789"/><path d="m2.4961199 4.3976698a1.1997888 1.2675855 80.074672 0 0 -1.0419038 1.3997559 1.1997888 1.2675855 80.074672 0 0 1.4553094.9627848 1.1997888 1.2675855 80.074672 0 0 1.0419037-1.3997558 1.1997888 1.2675855 80.074672 0 0 -1.4553093-.9627849zm.074974.4171488a.80148375.84677333 80.074672 0 1 .9729986.6426896.80148375.84677333 80.074672 0 1 -.6969432.934902.80148375.84677333 80.074672 0 1 -.9729958-.6426902.80148375.84677333 80.074672 0 1 .6969432-.934902z"/><path d="m11.838896.64428913a1.231838 1.2836218 52.593897 0 0 -.271701 1.75779027 1.231838 1.2836218 52.593897 0 0 1.767576.1983008 1.231838 1.2836218 52.593897 0 0 .271701-1.75779027 1.231838 1.2836218 52.593897 0 0 -1.767576-.1983008zm.265925.3444462a.82289328.85748593 52.593897 0 1 1.181294.13165847.82289328.85748593 52.593897 0 1 -.182417 1.1745241.82289328.85748593 52.593897 0 1 -1.181291-.1316609.82289328.85748593 52.593897 0 1 .182417-1.17452347z"/></g></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.7627119 13.627119s1.3064631-5.1979735 6.5945988-6.486109c5.0847463.9491522 5.9477733 6.486108 5.9477733 6.486108" fill="none" stroke="#87b1d7" stroke-miterlimit="4.9" stroke-width=".5"/><path d="m2.3554991 8.5165019 6.0018116-1.3754919 2.0717113-4.6377276" fill="none" stroke="#61b3ff" stroke-width="1.5"/><g fill="#e0e0e0"><ellipse cx="1.898304" cy="13.491526" rx="1.267586" ry="1.199789"/><ellipse cx="14.237288" cy="13.491526" rx="1.267586" ry="1.199789"/><ellipse cx="8.35731" cy="7.14101" rx="1.267586" ry="1.199789"/><path d="m1.3048251 7.4400522a1.1997888 1.2675855 80.074672 0 0 -1.04190379 1.3997559 1.1997888 1.2675855 80.074672 0 0 1.45530939.9627848 1.1997888 1.2675855 80.074672 0 0 1.0419037-1.3997558 1.1997888 1.2675855 80.074672 0 0 -1.4553093-.9627849zm.074974.4171488a.80148375.84677333 80.074672 0 1 .9729986.6426896.80148375.84677333 80.074672 0 1 -.6969432.934902.80148375.84677333 80.074672 0 1 -.97299579-.6426902.80148375.84677333 80.074672 0 1 .69694319-.934902z"/><path d="m10.024463.73592688a1.231838 1.2836218 52.593897 0 0 -.2717015 1.75779042 1.231838 1.2836218 52.593897 0 0 1.7675765.1983008 1.231838 1.2836218 52.593897 0 0 .271701-1.75779042 1.231838 1.2836218 52.593897 0 0 -1.767576-.1983008zm.265925.34444622a.82289328.85748593 52.593897 0 1 1.181294.1316585.82289328.85748593 52.593897 0 1 -.182417 1.1745242.82289328.85748593 52.593897 0 1 -1.181291-.1316609.82289328.85748593 52.593897 0 1 .182417-1.1745236z"/></g></svg> diff --git a/editor/icons/BezierHandlesLinear.svg b/editor/icons/BezierHandlesLinear.svg new file mode 100644 index 0000000000..2667779dcb --- /dev/null +++ b/editor/icons/BezierHandlesLinear.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8.2711868 4.7796612-6.3728828 8.7118648z" fill="none" stroke="#87b1d7" stroke-miterlimit="4.9" stroke-width=".5"/><ellipse cx="1.898304" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><path d="m14.237288 13.491526-5.9661012-8.7118648" fill="none" stroke="#87b1d7" stroke-miterlimit="4.9" stroke-width=".5"/><path d="m5.6316733 8.3879317 2.6395135-3.6082705 2.4416832 3.5654122" fill="none" stroke="#61b2ff" stroke-width="1.5"/><g fill="#e0e0e0"><ellipse cx="14.237288" cy="13.491526" rx="1.267586" ry="1.199789"/><ellipse cx="8.271187" cy="4.779661" rx="1.267586" ry="1.199789"/><path d="m5.0847454 7.9363749a1.2675855 1.1997888 0 0 0 -1.2675781 1.1992188 1.2675855 1.1997888 0 0 0 1.2675781 1.1992183 1.2675855 1.1997888 0 0 0 1.2675781-1.1992183 1.2675855 1.1997888 0 0 0 -1.2675781-1.1992188zm.00195.4238282a.84677333.80148375 0 0 1 .8476593.8007812.84677333.80148375 0 0 1 -.8476562.8007812.84677333.80148375 0 0 1 -.8476562-.8007812.84677333.80148375 0 0 1 .8476562-.8007812z"/><path d="m11.254237 7.9043407a1.2836218 1.231838 0 0 0 -1.2836135 1.2312528 1.2836218 1.231838 0 0 0 1.2836135 1.2312525 1.2836218 1.231838 0 0 0 1.283614-1.2312525 1.2836218 1.231838 0 0 0 -1.283614-1.2312528zm.002.4351497a.85748593.82289328 0 0 1 .858383.8221719.85748593.82289328 0 0 1 -.85838.822172.85748593.82289328 0 0 1 -.858379-.822172.85748593.82289328 0 0 1 .858379-.8221719z"/></g></svg> diff --git a/editor/icons/BezierHandlesMirror.svg b/editor/icons/BezierHandlesMirror.svg index 9180e31921..07817f7247 100644 --- a/editor/icons/BezierHandlesMirror.svg +++ b/editor/icons/BezierHandlesMirror.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.7627119 13.627119s1.2881355-6.847458 6.5762712-8.1355935c5.0847459.9491522 5.9661009 8.1355925 5.9661009 8.1355925" fill="none" stroke="#5fb2ff" stroke-miterlimit="4.9" stroke-width="1.7"/><ellipse cx="1.898304" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><ellipse cx="14.237288" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><path d="m8.2033896 4.6779662h-3.8335021" fill="none" stroke="#5fb2ff" stroke-width=".805138"/><path d="m11.931789 4.6440679h-3.7283994" fill="none" stroke="#5fb2ff" stroke-width=".716709"/><g fill="#e0e0e0"><ellipse cx="8.271187" cy="4.779661" rx="1.267586" ry="1.199789"/><path d="m3.1539157 3.4305762a1.2675855 1.1997888 0 0 0 -1.2675781 1.1992188 1.2675855 1.1997888 0 0 0 1.2675781 1.1992187 1.2675855 1.1997888 0 0 0 1.2675781-1.1992187 1.2675855 1.1997888 0 0 0 -1.2675781-1.1992188zm.00195.4238282a.84677333.80148375 0 0 1 .8476593.8007812.84677333.80148375 0 0 1 -.8476562.8007812.84677333.80148375 0 0 1 -.8476562-.8007812.84677333.80148375 0 0 1 .8476562-.8007812z"/><path d="m13.093969 3.3750567a1.2675855 1.1997888 0 0 0 -1.267578 1.1992188 1.2675855 1.1997888 0 0 0 1.267578 1.1992187 1.2675855 1.1997888 0 0 0 1.267578-1.1992187 1.2675855 1.1997888 0 0 0 -1.267578-1.1992188zm.002.4238282a.84677333.80148375 0 0 1 .847659.8007812.84677333.80148375 0 0 1 -.847656.8007812.84677333.80148375 0 0 1 -.847656-.8007812.84677333.80148375 0 0 1 .847656-.8007812z"/></g></svg> +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1.7627119 13.627119s1.2881355-6.847458 6.5762712-8.1355935c5.0847459.9491522 5.9661009 8.1355925 5.9661009 8.1355925" fill="none" stroke="#87b1d7" stroke-miterlimit="4.9" stroke-width=".5"/><ellipse cx="1.898304" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><ellipse cx="14.237288" cy="13.491526" fill="#e0e0e0" rx="1.267586" ry="1.199789"/><path d="m8.3389831 5.4915255-5.8519685.0395137" fill="none" stroke="#5fb2ff" stroke-width="1.5"/><path d="m13.814033 5.4419288-5.4750499.0156984" fill="none" stroke="#5fb2ff" stroke-width="1.5"/><g fill="#e0e0e0"><ellipse cx="8.40678" cy="5.593221" rx="1.267586" ry="1.199789"/><path d="m1.6400247 4.2441355a1.2675855 1.1997888 0 0 0 -1.26757814 1.1992188 1.2675855 1.1997888 0 0 0 1.26757814 1.1992187 1.2675855 1.1997888 0 0 0 1.2675781-1.1992187 1.2675855 1.1997888 0 0 0 -1.2675781-1.1992188zm.00195.4238282a.84677333.80148375 0 0 1 .8476593.8007812.84677333.80148375 0 0 1 -.8476562.8007812.84677333.80148375 0 0 1 -.84765624-.8007812.84677333.80148375 0 0 1 .84765624-.8007812z"/><path d="m14.659116 4.188616a1.2675855 1.1997888 0 0 0 -1.267578 1.1992188 1.2675855 1.1997888 0 0 0 1.267578 1.1992187 1.2675855 1.1997888 0 0 0 1.267578-1.1992187 1.2675855 1.1997888 0 0 0 -1.267578-1.1992188zm.002.4238282a.84677333.80148375 0 0 1 .847659.8007812.84677333.80148375 0 0 1 -.847656.8007812.84677333.80148375 0 0 1 -.847656-.8007812.84677333.80148375 0 0 1 .847656-.8007812z"/></g></svg> diff --git a/editor/icons/ContainerLayout.svg b/editor/icons/ContainerLayout.svg new file mode 100644 index 0000000000..feabc2c350 --- /dev/null +++ b/editor/icons/ContainerLayout.svg @@ -0,0 +1 @@ +<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m3 1c-1.105 0-2 .895-2 2h2zm2 0v2h2v-2zm4 0v2h2v-2zm4 0v2h2c0-1.105-.895-2-2-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4v2h2v-2zm12 0v2h2v-2zm-12 4c0 1.105.895 2 2 2v-2zm4 0v2h2v-2zm4 0v2h2v-2zm4 0v2c1.105 0 2-.895 2-2z" fill="#8eef97"/><path d="m7 7h4v4h-4z" fill="#d6d6d6"/></g></svg> diff --git a/editor/icons/ControlLayout.svg b/editor/icons/ControlLayout.svg index 11dd2554be..8503e3313c 100644 --- a/editor/icons/ControlLayout.svg +++ b/editor/icons/ControlLayout.svg @@ -1 +1 @@ -<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v14h14v-14zm2 2h3v3h-3zm5 0h5v3h-5zm-5 5h3v5h-3zm5 0h5v5h-5z" fill="#8eef97"/></svg> +<svg clip-rule="evenodd" fill-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="2" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero"><path d="m11.793 8v-2h-3.793v-2.113h-2v2.113h-2.142v2h2.142v3.967h2v-3.967z" fill="#d6d6d6"/><path d="m8 .345c-4.199 0-7.655 3.456-7.655 7.655s3.456 7.655 7.655 7.655 7.655-3.456 7.655-7.655-3.456-7.655-7.655-7.655zm0 1.999c3.103 0 5.656 2.553 5.656 5.656s-2.553 5.656-5.656 5.656-5.656-2.553-5.656-5.656 2.553-5.656 5.656-5.656z" fill="#8eef97"/></g></svg> diff --git a/editor/icons/CurveIn.svg b/editor/icons/CurveIn.svg index 2ad44dc654..fefad9ce6c 100644 --- a/editor/icons/CurveIn.svg +++ b/editor/icons/CurveIn.svg @@ -1 +1 @@ -<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c5 0 8-3 8-8" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> +<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c5 0 8-3 8-8" fill="none" stroke="#80ff45" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> diff --git a/editor/icons/CurveInOut.svg b/editor/icons/CurveInOut.svg index 292dac4573..f099cb83f1 100644 --- a/editor/icons/CurveInOut.svg +++ b/editor/icons/CurveInOut.svg @@ -1 +1 @@ -<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c5 0 3-8 8-8" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> +<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c5 0 3-8 8-8" fill="none" stroke="#45d7ff" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> diff --git a/editor/icons/CurveLinear.svg b/editor/icons/CurveLinear.svg index 3c1fb2a0e2..41d37c9329 100644 --- a/editor/icons/CurveLinear.svg +++ b/editor/icons/CurveLinear.svg @@ -1 +1 @@ -<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4 8-8" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> +<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4 8-8" fill="none" stroke="#ffe345" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> diff --git a/editor/icons/CurveOut.svg b/editor/icons/CurveOut.svg index dfa9a26144..19710aa38d 100644 --- a/editor/icons/CurveOut.svg +++ b/editor/icons/CurveOut.svg @@ -1 +1 @@ -<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c0-5 3-8 8-8" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> +<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c0-5 3-8 8-8" fill="none" stroke="#45ffa2" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> diff --git a/editor/icons/CurveOutIn.svg b/editor/icons/CurveOutIn.svg index 9a6463d0e9..7f200432bf 100644 --- a/editor/icons/CurveOutIn.svg +++ b/editor/icons/CurveOutIn.svg @@ -1 +1 @@ -<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c0-5 8-3 8-8" fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> +<svg height="12" viewBox="0 0 12 12" width="12" xmlns="http://www.w3.org/2000/svg"><path d="m2 1050.4c0-5 8-3 8-8" fill="none" stroke="#ff4596" stroke-linecap="round" stroke-width="2" transform="translate(0 -1040.4)"/></svg> diff --git a/editor/icons/DefaultProjectIcon.svg b/editor/icons/DefaultProjectIcon.svg index f81ba4d390..adc26df6c2 100644 --- a/editor/icons/DefaultProjectIcon.svg +++ b/editor/icons/DefaultProjectIcon.svg @@ -1 +1 @@ -<svg height="64" viewBox="0 0 64 64" width="64" xmlns="http://www.w3.org/2000/svg"><g stroke-linejoin="round"><path d="m8 0c-4.432 0-8 3.568-8 8v48c0 4.432 3.568 8 8 8h48c4.432 0 8-3.568 8-8v-48c0-4.432-3.568-8-8-8z" fill="#355570" stroke-linecap="round" stroke-width="2"/><path d="m8 0c-4.432 0-8 3.568-8 8v48c0 4.432 3.568 8 8 8h48c4.432 0 8-3.568 8-8v-48c0-4.432-3.568-8-8-8zm0 2h48c3.324 0 6 2.676 6 6v48c0 3.324-2.676 6-6 6h-48c-3.324 0-6-2.676-6-6v-48c0-3.324 2.676-6 6-6z" fill-opacity=".19608" stroke-linecap="round" stroke-width="2"/><path d="m27.254 10c-2.1314.47383-4.2401 1.134-6.2168 2.1289.04521 1.7455.15796 3.4164.38672 5.1152-.76768.4919-1.574.91443-2.291 1.4902-.72854.5604-1.4731 1.0965-2.1328 1.752-1.3179-.8716-2.7115-1.691-4.1484-2.4141-1.549 1.667-2.9985 3.4672-4.1816 5.4805.89011 1.4399 1.8209 2.7894 2.8242 4.0703h.027343v9.9453 1.2617 1.1504l-.009765 1.6309h-.001953c.0031.7321.011718 1.5356.011718 1.6953 0 7.1942 9.1264 10.652 20.465 10.691h.013672.013672c11.338-.04 20.461-3.4972 20.461-10.691 0-.1626.010282-.96271.013672-1.6953h-.001953l-.011719-1.6309v-.98633l.003907-.001953v-11.369h.027343c1.0035-1.2809 1.9337-2.6304 2.8242-4.0703-1.1827-2.0133-2.6327-3.8135-4.1816-5.4805-1.4366.7231-2.8325 1.5425-4.1504 2.4141-.65947-.6555-1.4013-1.1916-2.1309-1.752-.71682-.5758-1.5248-.99833-2.291-1.4902.22813-1.6988.3413-3.3697.38672-5.1152-1.977-.99494-4.0863-1.6551-6.2188-2.1289-.85139 1.4309-1.6285 2.9812-2.3066 4.4961-.80409-.1344-1.613-.18571-2.4219-.19531h-.015625-.015625c-.81037.01-1.6176.060513-2.4219.19531-.67768-1.5149-1.4559-3.0652-2.3086-4.4961z" fill="#fff" stroke="#fff" stroke-width="3"/></g><g stroke-width=".32031" transform="matrix(.050279 0 0 .050279 6.2574 1.18)"><path d="m0 0s-.325 1.994-.515 1.976l-36.182-3.491c-2.879-.278-5.115-2.574-5.317-5.459l-.994-14.247-27.992-1.997-1.904 12.912c-.424 2.872-2.932 5.037-5.835 5.037h-38.188c-2.902 0-5.41-2.165-5.834-5.037l-1.905-12.912-27.992 1.997-.994 14.247c-.202 2.886-2.438 5.182-5.317 5.46l-36.2 3.49c-.187.018-.324-1.978-.511-1.978l-.049-7.83 30.658-4.944 1.004-14.374c.203-2.91 2.551-5.263 5.463-5.472l38.551-2.75c.146-.01.29-.016.434-.016 2.897 0 5.401 2.166 5.825 5.038l1.959 13.286h28.005l1.959-13.286c.423-2.871 2.93-5.037 5.831-5.037.142 0 .284.005.423.015l38.556 2.75c2.911.209 5.26 2.562 5.463 5.472l1.003 14.374 30.645 4.966z" fill="#fff" transform="matrix(4.1626 0 0 -4.1626 919.24 771.67)"/><path d="m0 0v-59.041c.108-.001.216-.005.323-.015l36.196-3.49c1.896-.183 3.382-1.709 3.514-3.609l1.116-15.978 31.574-2.253 2.175 14.747c.282 1.912 1.922 3.329 3.856 3.329h38.188c1.933 0 3.573-1.417 3.855-3.329l2.175-14.747 31.575 2.253 1.115 15.978c.133 1.9 1.618 3.425 3.514 3.609l36.182 3.49c.107.01.214.014.322.015v4.711l.015.005v54.325h.134c4.795 6.12 9.232 12.569 13.487 19.449-5.651 9.62-12.575 18.217-19.976 26.182-6.864-3.455-13.531-7.369-19.828-11.534-3.151 3.132-6.7 5.694-10.186 8.372-3.425 2.751-7.285 4.768-10.946 7.118 1.09 8.117 1.629 16.108 1.846 24.448-9.446 4.754-19.519 7.906-29.708 10.17-4.068-6.837-7.788-14.241-11.028-21.479-3.842.642-7.702.88-11.567.926v.006c-.027 0-.052-.006-.075-.006-.024 0-.049.006-.073.006v-.006c-3.872-.046-7.729-.284-11.572-.926-3.238 7.238-6.956 14.642-11.03 21.479-10.184-2.264-20.258-5.416-29.703-10.17.216-8.34.755-16.331 1.848-24.448-3.668-2.35-7.523-4.367-10.949-7.118-3.481-2.678-7.036-5.24-10.188-8.372-6.297 4.165-12.962 8.079-19.828 11.534-7.401-7.965-14.321-16.562-19.974-26.182 4.253-6.88 8.693-13.329 13.487-19.449z" fill="#478cbf" transform="matrix(4.1626 0 0 -4.1626 104.7 525.91)"/><path d="m0 0-1.121-16.063c-.135-1.936-1.675-3.477-3.611-3.616l-38.555-2.751c-.094-.007-.188-.01-.281-.01-1.916 0-3.569 1.406-3.852 3.33l-2.211 14.994h-31.459l-2.211-14.994c-.297-2.018-2.101-3.469-4.133-3.32l-38.555 2.751c-1.936.139-3.476 1.68-3.611 3.616l-1.121 16.063-32.547 3.138c.015-3.498.06-7.33.06-8.093 0-34.374 43.605-50.896 97.781-51.086h.133c54.176.19 97.766 16.712 97.766 51.086 0 .777.047 4.593.063 8.093z" fill="#478cbf" transform="matrix(4.1626 0 0 -4.1626 784.07 817.24)"/><path d="m0 0c0-12.052-9.765-21.815-21.813-21.815-12.042 0-21.81 9.763-21.81 21.815 0 12.044 9.768 21.802 21.81 21.802 12.048 0 21.813-9.758 21.813-21.802" fill="#fff" transform="matrix(4.1626 0 0 -4.1626 389.21 625.67)"/><path d="m0 0c0-7.994-6.479-14.473-14.479-14.473-7.996 0-14.479 6.479-14.479 14.473s6.483 14.479 14.479 14.479c8 0 14.479-6.485 14.479-14.479" fill="#414042" transform="matrix(4.1626 0 0 -4.1626 367.37 631.06)"/><path d="m0 0c-3.878 0-7.021 2.858-7.021 6.381v20.081c0 3.52 3.143 6.381 7.021 6.381s7.028-2.861 7.028-6.381v-20.081c0-3.523-3.15-6.381-7.028-6.381" fill="#fff" transform="matrix(4.1626 0 0 -4.1626 511.99 724.74)"/><path d="m0 0c0-12.052 9.765-21.815 21.815-21.815 12.041 0 21.808 9.763 21.808 21.815 0 12.044-9.767 21.802-21.808 21.802-12.05 0-21.815-9.758-21.815-21.802" fill="#fff" transform="matrix(4.1626 0 0 -4.1626 634.79 625.67)"/><path d="m0 0c0-7.994 6.477-14.473 14.471-14.473 8.002 0 14.479 6.479 14.479 14.473s-6.477 14.479-14.479 14.479c-7.994 0-14.471-6.485-14.471-14.479" fill="#414042" transform="matrix(4.1626 0 0 -4.1626 656.64 631.06)"/></g></svg> +<svg height="128" width="128" xmlns="http://www.w3.org/2000/svg"><g transform="translate(32 32)"><path d="m-16-32c-8.86 0-16 7.13-16 15.99v95.98c0 8.86 7.13 15.99 16 15.99h96c8.86 0 16-7.13 16-15.99v-95.98c0-8.85-7.14-15.99-16-15.99z" fill="#363d52"/><path d="m-16-32c-8.86 0-16 7.13-16 15.99v95.98c0 8.86 7.13 15.99 16 15.99h96c8.86 0 16-7.13 16-15.99v-95.98c0-8.85-7.14-15.99-16-15.99zm0 4h96c6.64 0 12 5.35 12 11.99v95.98c0 6.64-5.35 11.99-12 11.99h-96c-6.64 0-12-5.35-12-11.99v-95.98c0-6.64 5.36-11.99 12-11.99z" fill-opacity=".4"/></g><g stroke-width="9.92746" transform="matrix(.10073078 0 0 .10073078 12.425923 2.256365)"><path d="m0 0s-.325 1.994-.515 1.976l-36.182-3.491c-2.879-.278-5.115-2.574-5.317-5.459l-.994-14.247-27.992-1.997-1.904 12.912c-.424 2.872-2.932 5.037-5.835 5.037h-38.188c-2.902 0-5.41-2.165-5.834-5.037l-1.905-12.912-27.992 1.997-.994 14.247c-.202 2.886-2.438 5.182-5.317 5.46l-36.2 3.49c-.187.018-.324-1.978-.511-1.978l-.049-7.83 30.658-4.944 1.004-14.374c.203-2.91 2.551-5.263 5.463-5.472l38.551-2.75c.146-.01.29-.016.434-.016 2.897 0 5.401 2.166 5.825 5.038l1.959 13.286h28.005l1.959-13.286c.423-2.871 2.93-5.037 5.831-5.037.142 0 .284.005.423.015l38.556 2.75c2.911.209 5.26 2.562 5.463 5.472l1.003 14.374 30.645 4.966z" fill="#fff" transform="matrix(4.162611 0 0 -4.162611 919.24059 771.67186)"/><path d="m0 0v-47.514-6.035-5.492c.108-.001.216-.005.323-.015l36.196-3.49c1.896-.183 3.382-1.709 3.514-3.609l1.116-15.978 31.574-2.253 2.175 14.747c.282 1.912 1.922 3.329 3.856 3.329h38.188c1.933 0 3.573-1.417 3.855-3.329l2.175-14.747 31.575 2.253 1.115 15.978c.133 1.9 1.618 3.425 3.514 3.609l36.182 3.49c.107.01.214.014.322.015v4.711l.015.005v54.325c5.09692 6.4164715 9.92323 13.494208 13.621 19.449-5.651 9.62-12.575 18.217-19.976 26.182-6.864-3.455-13.531-7.369-19.828-11.534-3.151 3.132-6.7 5.694-10.186 8.372-3.425 2.751-7.285 4.768-10.946 7.118 1.09 8.117 1.629 16.108 1.846 24.448-9.446 4.754-19.519 7.906-29.708 10.17-4.068-6.837-7.788-14.241-11.028-21.479-3.842.642-7.702.88-11.567.926v.006c-.027 0-.052-.006-.075-.006-.024 0-.049.006-.073.006v-.006c-3.872-.046-7.729-.284-11.572-.926-3.238 7.238-6.956 14.642-11.03 21.479-10.184-2.264-20.258-5.416-29.703-10.17.216-8.34.755-16.331 1.848-24.448-3.668-2.35-7.523-4.367-10.949-7.118-3.481-2.678-7.036-5.24-10.188-8.372-6.297 4.165-12.962 8.079-19.828 11.534-7.401-7.965-14.321-16.562-19.974-26.182 4.4426579-6.973692 9.2079702-13.9828876 13.621-19.449z" fill="#478cbf" transform="matrix(4.162611 0 0 -4.162611 104.69892 525.90697)"/><path d="m0 0-1.121-16.063c-.135-1.936-1.675-3.477-3.611-3.616l-38.555-2.751c-.094-.007-.188-.01-.281-.01-1.916 0-3.569 1.406-3.852 3.33l-2.211 14.994h-31.459l-2.211-14.994c-.297-2.018-2.101-3.469-4.133-3.32l-38.555 2.751c-1.936.139-3.476 1.68-3.611 3.616l-1.121 16.063-32.547 3.138c.015-3.498.06-7.33.06-8.093 0-34.374 43.605-50.896 97.781-51.086h.066.067c54.176.19 97.766 16.712 97.766 51.086 0 .777.047 4.593.063 8.093z" fill="#478cbf" transform="matrix(4.162611 0 0 -4.162611 784.07144 817.24284)"/><path d="m0 0c0-12.052-9.765-21.815-21.813-21.815-12.042 0-21.81 9.763-21.81 21.815 0 12.044 9.768 21.802 21.81 21.802 12.048 0 21.813-9.758 21.813-21.802" fill="#fff" transform="matrix(4.162611 0 0 -4.162611 389.21484 625.67104)"/><path d="m0 0c0-7.994-6.479-14.473-14.479-14.473-7.996 0-14.479 6.479-14.479 14.473s6.483 14.479 14.479 14.479c8 0 14.479-6.485 14.479-14.479" fill="#414042" transform="matrix(4.162611 0 0 -4.162611 367.36686 631.05679)"/><path d="m0 0c-3.878 0-7.021 2.858-7.021 6.381v20.081c0 3.52 3.143 6.381 7.021 6.381s7.028-2.861 7.028-6.381v-20.081c0-3.523-3.15-6.381-7.028-6.381" fill="#fff" transform="matrix(4.162611 0 0 -4.162611 511.99336 724.73954)"/><path d="m0 0c0-12.052 9.765-21.815 21.815-21.815 12.041 0 21.808 9.763 21.808 21.815 0 12.044-9.767 21.802-21.808 21.802-12.05 0-21.815-9.758-21.815-21.802" fill="#fff" transform="matrix(4.162611 0 0 -4.162611 634.78706 625.67104)"/><path d="m0 0c0-7.994 6.477-14.473 14.471-14.473 8.002 0 14.479 6.479 14.479 14.473s-6.477 14.479-14.479 14.479c-7.994 0-14.471-6.485-14.471-14.479" fill="#414042" transform="matrix(4.162611 0 0 -4.162611 656.64056 631.05679)"/></g></svg> diff --git a/editor/icons/InterpCubicAngle.svg b/editor/icons/InterpCubicAngle.svg new file mode 100644 index 0000000000..e302d556dc --- /dev/null +++ b/editor/icons/InterpCubicAngle.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 16 8" height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#5fff95" stroke-linecap="round"><path d="m2 6c5 0 3-4 6-4s1 4 6 4" stroke-width="2"/><circle cx="14" cy="2" r="1.5" stroke-linejoin="round"/></g></svg> diff --git a/editor/icons/InterpLinearAngle.svg b/editor/icons/InterpLinearAngle.svg new file mode 100644 index 0000000000..af4e87a6cb --- /dev/null +++ b/editor/icons/InterpLinearAngle.svg @@ -0,0 +1 @@ +<svg enable-background="new 0 0 16 8" height="8" viewBox="0 0 16 8" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" stroke="#fd995f" stroke-linecap="round" stroke-linejoin="round"><path d="m2 6 6-4 6 4" stroke-width="2"/><circle cx="14" cy="2" r="1.5"/></g></svg> diff --git a/editor/icons/Position2D.svg b/editor/icons/Marker2D.svg index 191f0b2a03..191f0b2a03 100644 --- a/editor/icons/Position2D.svg +++ b/editor/icons/Marker2D.svg diff --git a/editor/icons/Position3D.svg b/editor/icons/Marker3D.svg index 894b195589..894b195589 100644 --- a/editor/icons/Position3D.svg +++ b/editor/icons/Marker3D.svg diff --git a/editor/icons/MenuBar.svg b/editor/icons/MenuBar.svg new file mode 100644 index 0000000000..0a53f07f85 --- /dev/null +++ b/editor/icons/MenuBar.svg @@ -0,0 +1 @@ +<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 6a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1Zm1 2h10v2H3Zm0 3h10v2H3ZM1 1v4h6V1Zm1 1h4L4 4Z" fill="#8eef97"/></svg> diff --git a/editor/icons/NodeInfo.svg b/editor/icons/NodeInfo.svg new file mode 100644 index 0000000000..4e3f0c42d0 --- /dev/null +++ b/editor/icons/NodeInfo.svg @@ -0,0 +1 @@ +<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7-7 7 7 0 0 0 -7-7zm-1 3h2v2h-2zm0 3h2v5h-2z" fill="#fff"/></svg> diff --git a/editor/icons/ParticlesMaterial.svg b/editor/icons/ParticleProcessMaterial.svg index 33598980a5..33598980a5 100644 --- a/editor/icons/ParticlesMaterial.svg +++ b/editor/icons/ParticleProcessMaterial.svg diff --git a/editor/icons/RigidDynamicBody2D.svg b/editor/icons/RigidBody2D.svg index 5d08e991ae..5d08e991ae 100644 --- a/editor/icons/RigidDynamicBody2D.svg +++ b/editor/icons/RigidBody2D.svg diff --git a/editor/icons/RigidDynamicBody3D.svg b/editor/icons/RigidBody3D.svg index 7f5db4ce88..7f5db4ce88 100644 --- a/editor/icons/RigidDynamicBody3D.svg +++ b/editor/icons/RigidBody3D.svg diff --git a/editor/icons/SoftDynamicBody3D.svg b/editor/icons/SoftBody3D.svg index 7bc9a22c22..7bc9a22c22 100644 --- a/editor/icons/SoftDynamicBody3D.svg +++ b/editor/icons/SoftBody3D.svg diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp index f3709efab6..843c076642 100644 --- a/editor/import/audio_stream_import_settings.cpp +++ b/editor/import/audio_stream_import_settings.cpp @@ -42,8 +42,7 @@ void AudioStreamImportSettings::_notification(int p_what) { connect("confirmed", callable_mp(this, &AudioStreamImportSettings::_reimport)); } break; - case NOTIFICATION_THEME_CHANGED: - case NOTIFICATION_ENTER_TREE: { + case NOTIFICATION_THEME_CHANGED: { _play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons"))); _stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons"))); _preview->set_color(get_theme_color(SNAME("dark_color_2"), SNAME("Editor"))); @@ -526,7 +525,7 @@ AudioStreamImportSettings::AudioStreamImportSettings() { loop_hb->add_theme_constant_override("separation", 4 * EDSCALE); loop = memnew(CheckBox); loop->set_text(TTR("Enable")); - loop->set_tooltip(TTR("Enable looping.")); + loop->set_tooltip_text(TTR("Enable looping.")); loop->connect("toggled", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); loop_hb->add_child(loop); loop_hb->add_spacer(); @@ -535,7 +534,7 @@ AudioStreamImportSettings::AudioStreamImportSettings() { loop_offset->set_max(10000); loop_offset->set_step(0.001); loop_offset->set_suffix("sec"); - loop_offset->set_tooltip(TTR("Loop offset (from beginning). Note that if BPM is set, this setting will be ignored.")); + loop_offset->set_tooltip_text(TTR("Loop offset (from beginning). Note that if BPM is set, this setting will be ignored.")); loop_offset->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); loop_hb->add_child(loop_offset); main_vbox->add_margin_child(TTR("Loop:"), loop_hb); @@ -549,14 +548,14 @@ AudioStreamImportSettings::AudioStreamImportSettings() { bpm_edit = memnew(SpinBox); bpm_edit->set_max(400); bpm_edit->set_step(0.01); - bpm_edit->set_tooltip(TTR("Configure the Beats Per Measure (tempo) used for the interactive streams.\nThis is required in order to configure beat information.")); + bpm_edit->set_tooltip_text(TTR("Configure the Beats Per Measure (tempo) used for the interactive streams.\nThis is required in order to configure beat information.")); bpm_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(bpm_edit); interactive_hb->add_spacer(); bar_beats_label = memnew(Label(TTR("Beats/Bar:"))); interactive_hb->add_child(bar_beats_label); bar_beats_edit = memnew(SpinBox); - bar_beats_edit->set_tooltip(TTR("Configure the Beats Per Bar. This used for music-aware transitions between AudioStreams.")); + bar_beats_edit->set_tooltip_text(TTR("Configure the Beats Per Bar. This used for music-aware transitions between AudioStreams.")); bar_beats_edit->set_min(2); bar_beats_edit->set_max(32); bar_beats_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); @@ -567,7 +566,7 @@ AudioStreamImportSettings::AudioStreamImportSettings() { beats_enabled->connect("toggled", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(beats_enabled); beats_edit = memnew(SpinBox); - beats_edit->set_tooltip(TTR("Configure the amount of Beats used for music-aware looping. If zero, it will be autodetected from the length.\nIt is recommended to set this value (either manually or by clicking on a beat number in the preview) to ensure looping works properly.")); + beats_edit->set_tooltip_text(TTR("Configure the amount of Beats used for music-aware looping. If zero, it will be autodetected from the length.\nIt is recommended to set this value (either manually or by clicking on a beat number in the preview) to ensure looping works properly.")); beats_edit->set_max(99999); beats_edit->connect("value_changed", callable_mp(this, &AudioStreamImportSettings::_settings_changed).unbind(1)); interactive_hb->add_child(beats_edit); diff --git a/editor/import/collada.cpp b/editor/import/collada.cpp index f4d19fe8b6..5b9ed2c1d2 100644 --- a/editor/import/collada.cpp +++ b/editor/import/collada.cpp @@ -131,7 +131,7 @@ Transform3D Collada::Node::compute_transform(const Collada &state) const { switch (xf.op) { case XForm::OP_ROTATE: { if (xf.data.size() >= 4) { - xform_step.rotate(Vector3(xf.data[0], xf.data[1], xf.data[2]), Math::deg2rad(xf.data[3])); + xform_step.rotate(Vector3(xf.data[0], xf.data[1], xf.data[2]), Math::deg_to_rad(xf.data[3])); } } break; case XForm::OP_SCALE: { diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp index 043681aa87..ad80ac1ca9 100644 --- a/editor/import/dynamic_font_import_settings.cpp +++ b/editor/import/dynamic_font_import_settings.cpp @@ -449,8 +449,8 @@ void DynamicFontImportSettings::_main_prop_changed(const String &p_edited_proper // Update font preview. if (font_preview.is_valid()) { - if (p_edited_property == "antialiased") { - font_preview->set_antialiased(import_settings_data->get("antialiased")); + if (p_edited_property == "antialiasing") { + font_preview->set_antialiasing((TextServer::FontAntialiasing)import_settings_data->get("antialiasing").operator int()); } else if (p_edited_property == "generate_mipmaps") { font_preview->set_generate_mipmaps(import_settings_data->get("generate_mipmaps")); } else if (p_edited_property == "multichannel_signed_distance_field") { @@ -574,6 +574,12 @@ void DynamicFontImportSettings::_variations_validate() { } } } + if ((TextServer::FontAntialiasing)(int)import_settings_data->get("antialiasing") == TextServer::FONT_ANTIALIASING_LCD) { + warn += "\n" + TTR("Note: LCD sub-pixel anti-aliasing is selected, each of the glyphs will be pre-rendered for all supported sub-pixel layouts (5x)."); + } + if ((TextServer::SubpixelPositioning)(int)import_settings_data->get("subpixel_positioning") != TextServer::SUBPIXEL_POSITIONING_DISABLED) { + warn += "\n" + TTR("Note: Sub-pixel positioning is selected, each of the glyphs might be pre-rendered for multiple sub-pixel offsets (up to 4x)."); + } if (warn.is_empty()) { label_warn->set_text(""); label_warn->hide(); @@ -870,7 +876,6 @@ void DynamicFontImportSettings::_notification(int p_what) { connect("confirmed", callable_mp(this, &DynamicFontImportSettings::_re_import)); } break; - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { add_var->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); } break; @@ -881,7 +886,7 @@ void DynamicFontImportSettings::_re_import() { HashMap<StringName, Variant> main_settings; main_settings["face_index"] = import_settings_data->get("face_index"); - main_settings["antialiased"] = import_settings_data->get("antialiased"); + main_settings["antialiasing"] = import_settings_data->get("antialiasing"); main_settings["generate_mipmaps"] = import_settings_data->get("generate_mipmaps"); main_settings["multichannel_signed_distance_field"] = import_settings_data->get("multichannel_signed_distance_field"); main_settings["msdf_pixel_range"] = import_settings_data->get("msdf_pixel_range"); @@ -1079,7 +1084,7 @@ void DynamicFontImportSettings::open_settings(const String &p_path) { import_settings_data->notify_property_list_changed(); if (font_preview.is_valid()) { - font_preview->set_antialiased(import_settings_data->get("antialiased")); + font_preview->set_antialiasing((TextServer::FontAntialiasing)import_settings_data->get("antialiasing").operator int()); font_preview->set_multichannel_signed_distance_field(import_settings_data->get("multichannel_signed_distance_field")); font_preview->set_msdf_pixel_range(import_settings_data->get("msdf_pixel_range")); font_preview->set_msdf_size(import_settings_data->get("msdf_size")); @@ -1108,7 +1113,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() { options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant())); - options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "antialiased"), true)); + options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel"), 1)); options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "generate_mipmaps"), false)); options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); options_general.push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8)); @@ -1233,7 +1238,7 @@ DynamicFontImportSettings::DynamicFontImportSettings() { add_var = memnew(Button); page2_hb_vars->add_child(add_var); - add_var->set_tooltip(TTR("Add configuration")); + add_var->set_tooltip_text(TTR("Add configuration")); add_var->set_icon(add_var->get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); add_var->connect("pressed", callable_mp(this, &DynamicFontImportSettings::_variation_add)); diff --git a/editor/import/editor_import_plugin.cpp b/editor/import/editor_import_plugin.cpp index e822b4963a..3305f241c0 100644 --- a/editor/import/editor_import_plugin.cpp +++ b/editor/import/editor_import_plugin.cpp @@ -115,7 +115,7 @@ void EditorImportPlugin::get_import_options(const String &p_path, List<ResourceI Array needed; needed.push_back("name"); needed.push_back("default_value"); - Array options; + TypedArray<Dictionary> options; if (GDVIRTUAL_CALL(_get_import_options, p_path, p_preset, options)) { for (int i = 0; i < options.size(); i++) { Dictionary d = options[i]; diff --git a/editor/import/editor_import_plugin.h b/editor/import/editor_import_plugin.h index 4548513b6f..e9749c240f 100644 --- a/editor/import/editor_import_plugin.h +++ b/editor/import/editor_import_plugin.h @@ -32,6 +32,7 @@ #define EDITOR_IMPORT_PLUGIN_H #include "core/io/resource_importer.h" +#include "core/variant/typed_array.h" class EditorImportPlugin : public ResourceImporter { GDCLASS(EditorImportPlugin, ResourceImporter); @@ -44,7 +45,7 @@ protected: GDVIRTUAL0RC(int, _get_preset_count) GDVIRTUAL1RC(String, _get_preset_name, int) GDVIRTUAL0RC(Vector<String>, _get_recognized_extensions) - GDVIRTUAL2RC(Array, _get_import_options, String, int) + GDVIRTUAL2RC(TypedArray<Dictionary>, _get_import_options, String, int) GDVIRTUAL0RC(String, _get_save_extension) GDVIRTUAL0RC(String, _get_resource_type) GDVIRTUAL0RC(float, _get_priority) diff --git a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp index 4f00bd120a..6f775c7ea8 100644 --- a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/post_import_plugin_skeleton_rest_fixer.cpp @@ -38,16 +38,16 @@ void PostImportPluginSkeletonRestFixer::get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) { if (p_category == INTERNAL_IMPORT_CATEGORY_SKELETON_3D_NODE) { + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/apply_node_transforms"), true)); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/normalize_position_tracks"), true)); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/overwrite_axis"), true)); - r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::BOOL, "retarget/rest_fixer/fix_silhouette/enable"), false)); - r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/threshold"), 15)); - // TODO: PostImportPlugin need to be implemented such as validate_option(PropertyInfo &property, const Dictionary &p_options). // get_internal_option_visibility() is not sufficient because it can only retrieve options implemented in the core and can only read option values. // r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/filter", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::STRING_NAME, PROPERTY_HINT_ENUM, "Hips,Spine,Chest")), Array())); r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::ARRAY, "retarget/rest_fixer/fix_silhouette/filter", PROPERTY_HINT_ARRAY_TYPE, "StringName"), Array())); + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/threshold"), 15)); + r_options->push_back(ResourceImporter::ImportOption(PropertyInfo(Variant::FLOAT, "retarget/rest_fixer/fix_silhouette/base_height_adjustment", PROPERTY_HINT_RANGE, "-1,1,0.01"), 0.0)); } } @@ -67,6 +67,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory if (!src_skeleton) { return; } + bool is_renamed = bool(p_options["retarget/bone_renamer/rename_bones"]); Array filter = p_options["retarget/rest_fixer/fix_silhouette/filter"]; bool is_rest_changed = false; @@ -89,44 +90,96 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } - // Set motion scale to Skeleton if normalize position tracks. - if (bool(p_options["retarget/rest_fixer/normalize_position_tracks"])) { - int src_bone_idx = src_skeleton->find_bone(profile->get_scale_base_bone()); - if (src_bone_idx >= 0) { - real_t motion_scale = abs(src_skeleton->get_bone_global_rest(src_bone_idx).origin.y); - if (motion_scale > 0) { - src_skeleton->set_motion_scale(motion_scale); + // Apply node transforms. + if (bool(p_options["retarget/rest_fixer/apply_node_transforms"])) { + LocalVector<Transform3D> old_skeleton_rest; + LocalVector<Transform3D> old_skeleton_global_rest; + for (int i = 0; i < src_skeleton->get_bone_count(); i++) { + old_skeleton_rest.push_back(src_skeleton->get_bone_rest(i)); + old_skeleton_global_rest.push_back(src_skeleton->get_bone_global_rest(i)); + } + + Transform3D global_transform; + Node *pr = src_skeleton; + while (pr) { + Node3D *pr3d = Object::cast_to<Node3D>(pr); + if (pr3d) { + global_transform = pr3d->get_transform() * global_transform; + pr3d->set_transform(Transform3D()); } + pr = pr->get_parent(); } + Vector3 scl = global_transform.basis.get_scale_local(); - TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); - while (nodes.size()) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); - List<StringName> anims; - ap->get_animation_list(&anims); - for (const StringName &name : anims) { - Ref<Animation> anim = ap->get_animation(name); - int track_len = anim->get_track_count(); - for (int i = 0; i < track_len; i++) { - if (anim->track_get_path(i).get_subname_count() != 1 || anim->track_get_type(i) != Animation::TYPE_POSITION_3D) { - continue; - } + Vector<int> bones_to_process = src_skeleton->get_parentless_bones(); + for (int i = 0; i < bones_to_process.size(); i++) { + src_skeleton->set_bone_rest(bones_to_process[i], global_transform.orthonormalized() * src_skeleton->get_bone_rest(bones_to_process[i])); + } - if (anim->track_is_compressed(i)) { - continue; // Shouldn't occur in internal_process(). - } + while (bones_to_process.size() > 0) { + int src_idx = bones_to_process[0]; + bones_to_process.erase(src_idx); + Vector<int> src_children = src_skeleton->get_bone_children(src_idx); + for (int i = 0; i < src_children.size(); i++) { + bones_to_process.push_back(src_children[i]); + } + src_skeleton->set_bone_rest(src_idx, Transform3D(src_skeleton->get_bone_rest(src_idx).basis, src_skeleton->get_bone_rest(src_idx).origin * scl)); + } - String track_path = String(anim->track_get_path(i).get_concatenated_names()); - Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); - if (node) { - Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); - if (track_skeleton) { + // Fix animation. + bones_to_process = src_skeleton->get_parentless_bones(); + { + TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); + while (nodes.size()) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); + List<StringName> anims; + ap->get_animation_list(&anims); + for (const StringName &name : anims) { + Ref<Animation> anim = ap->get_animation(name); + int track_len = anim->get_track_count(); + for (int i = 0; i < track_len; i++) { + if (anim->track_get_path(i).get_subname_count() != 1 || !(anim->track_get_type(i) == Animation::TYPE_POSITION_3D || anim->track_get_type(i) == Animation::TYPE_ROTATION_3D || anim->track_get_type(i) == Animation::TYPE_SCALE_3D)) { + continue; + } + + if (anim->track_is_compressed(i)) { + continue; // Shouldn't occur in internal_process(). + } + + String track_path = String(anim->track_get_path(i).get_concatenated_names()); + Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); + if (node) { + Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); if (track_skeleton && track_skeleton == src_skeleton) { - real_t mlt = 1 / src_skeleton->get_motion_scale(); - int key_len = anim->track_get_key_count(i); - for (int j = 0; j < key_len; j++) { - Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); - anim->track_set_key_value(i, j, pos * mlt); + StringName bn = anim->track_get_path(i).get_subname(0); + if (bn) { + int bone_idx = src_skeleton->find_bone(bn); + int key_len = anim->track_get_key_count(i); + if (anim->track_get_type(i) == Animation::TYPE_POSITION_3D) { + if (bones_to_process.has(bone_idx)) { + for (int j = 0; j < key_len; j++) { + Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, global_transform.basis.xform(ps) + global_transform.origin); + } + } else { + for (int j = 0; j < key_len; j++) { + Vector3 ps = static_cast<Vector3>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, ps * scl); + } + } + } else if (bones_to_process.has(bone_idx)) { + if (anim->track_get_type(i) == Animation::TYPE_ROTATION_3D) { + for (int j = 0; j < key_len; j++) { + Quaternion qt = static_cast<Quaternion>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, global_transform.basis.get_rotation_quaternion() * qt); + } + } else { + for (int j = 0; j < key_len; j++) { + Basis sc = Basis().scaled(static_cast<Vector3>(anim->track_get_key_value(i, j))); + anim->track_set_key_value(i, j, (global_transform.basis * sc).get_scale()); + } + } + } } } } @@ -134,6 +187,8 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } } + + is_rest_changed = true; } // Complement Rotation track for compatibility between different rests. @@ -269,7 +324,7 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory Vector3 src_dir = src_tail - src_head; // Rotate rest. - if (Math::abs(Math::rad2deg(src_dir.angle_to(prof_dir))) > float(p_options["retarget/rest_fixer/fix_silhouette/threshold"])) { + if (Math::abs(Math::rad_to_deg(src_dir.angle_to(prof_dir))) > float(p_options["retarget/rest_fixer/fix_silhouette/threshold"])) { // Get rotation difference. Vector3 up_vec; // Need to rotate other than roll axis. switch (Vector3(abs(src_dir.x), abs(src_dir.y), abs(src_dir.z)).min_axis_index()) { @@ -303,6 +358,52 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory } } + // Adjust scale base bone height. + float base_adjustment = float(p_options["retarget/rest_fixer/fix_silhouette/base_height_adjustment"]); + if (!Math::is_zero_approx(base_adjustment)) { + StringName scale_base_bone_name = profile->get_scale_base_bone(); + int src_bone_idx = src_skeleton->find_bone(scale_base_bone_name); + Transform3D src_rest = src_skeleton->get_bone_rest(src_bone_idx); + src_skeleton->set_bone_rest(src_bone_idx, Transform3D(src_rest.basis, Vector3(src_rest.origin.x, src_rest.origin.y + base_adjustment, src_rest.origin.z))); + + TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); + while (nodes.size()) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); + List<StringName> anims; + ap->get_animation_list(&anims); + for (const StringName &name : anims) { + Ref<Animation> anim = ap->get_animation(name); + int track_len = anim->get_track_count(); + for (int i = 0; i < track_len; i++) { + if (anim->track_get_path(i).get_subname_count() != 1 || anim->track_get_type(i) != Animation::TYPE_POSITION_3D) { + continue; + } + + if (anim->track_is_compressed(i)) { + continue; // Shouldn't occur in internal_process(). + } + + String track_path = String(anim->track_get_path(i).get_concatenated_names()); + Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); + if (node) { + Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); + if (track_skeleton && track_skeleton == src_skeleton) { + StringName bn = anim->track_get_path(i).get_concatenated_subnames(); + if (bn == scale_base_bone_name) { + int key_len = anim->track_get_key_count(i); + for (int j = 0; j < key_len; j++) { + Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); + pos.y += base_adjustment; + anim->track_set_key_value(i, j, pos); + } + } + } + } + } + } + } + } + // For skin modification in overwrite rest. for (int i = 0; i < src_skeleton->get_bone_count(); i++) { silhouette_diff_w[i] = old_skeleton_global_rest[i] * src_skeleton->get_bone_global_rest(i).inverse(); @@ -311,6 +412,51 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory is_rest_changed = true; } + // Set motion scale to Skeleton if normalize position tracks. + if (bool(p_options["retarget/rest_fixer/normalize_position_tracks"])) { + int src_bone_idx = src_skeleton->find_bone(profile->get_scale_base_bone()); + if (src_bone_idx >= 0) { + real_t motion_scale = abs(src_skeleton->get_bone_global_rest(src_bone_idx).origin.y); + if (motion_scale > 0) { + src_skeleton->set_motion_scale(motion_scale); + } + } + + TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); + while (nodes.size()) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(nodes.pop_back()); + List<StringName> anims; + ap->get_animation_list(&anims); + for (const StringName &name : anims) { + Ref<Animation> anim = ap->get_animation(name); + int track_len = anim->get_track_count(); + for (int i = 0; i < track_len; i++) { + if (anim->track_get_path(i).get_subname_count() != 1 || anim->track_get_type(i) != Animation::TYPE_POSITION_3D) { + continue; + } + + if (anim->track_is_compressed(i)) { + continue; // Shouldn't occur in internal_process(). + } + + String track_path = String(anim->track_get_path(i).get_concatenated_names()); + Node *node = (ap->get_node(ap->get_root()))->get_node(NodePath(track_path)); + if (node) { + Skeleton3D *track_skeleton = Object::cast_to<Skeleton3D>(node); + if (track_skeleton && track_skeleton == src_skeleton) { + real_t mlt = 1 / src_skeleton->get_motion_scale(); + int key_len = anim->track_get_key_count(i); + for (int j = 0; j < key_len; j++) { + Vector3 pos = static_cast<Vector3>(anim->track_get_key_value(i, j)); + anim->track_set_key_value(i, j, pos * mlt); + } + } + } + } + } + } + } + // Overwrite axis. if (bool(p_options["retarget/rest_fixer/overwrite_axis"])) { LocalVector<Transform3D> old_skeleton_rest; @@ -367,32 +513,6 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory src_skeleton->set_bone_rest(src_idx, Transform3D(tgt_rot, diff.xform(src_skeleton->get_bone_rest(src_idx).origin))); } - // Fix skin. - { - TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); - while (nodes.size()) { - ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); - Ref<Skin> skin = mi->get_skin(); - if (skin.is_valid()) { - Node *node = mi->get_node(mi->get_skeleton_path()); - if (node) { - Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); - if (mesh_skeleton && node == src_skeleton) { - int skin_len = skin->get_bind_count(); - for (int i = 0; i < skin_len; i++) { - StringName bn = skin->get_bind_name(i); - int bone_idx = src_skeleton->find_bone(bn); - if (bone_idx >= 0) { - Transform3D new_rest = silhouette_diff[i] * src_skeleton->get_bone_global_rest(bone_idx); - skin->set_bind_pose(i, new_rest.inverse()); - } - } - } - } - } - } - } - // Fix animation. { TypedArray<Node> nodes = p_base_scene->find_children("*", "AnimationPlayer"); @@ -471,8 +591,34 @@ void PostImportPluginSkeletonRestFixer::internal_process(InternalImportCategory is_rest_changed = true; } - // Init skeleton pose to new rest. if (is_rest_changed) { + // Fix skin. + { + TypedArray<Node> nodes = p_base_scene->find_children("*", "ImporterMeshInstance3D"); + while (nodes.size()) { + ImporterMeshInstance3D *mi = Object::cast_to<ImporterMeshInstance3D>(nodes.pop_back()); + Ref<Skin> skin = mi->get_skin(); + if (skin.is_valid()) { + Node *node = mi->get_node(mi->get_skeleton_path()); + if (node) { + Skeleton3D *mesh_skeleton = Object::cast_to<Skeleton3D>(node); + if (mesh_skeleton && node == src_skeleton) { + int skin_len = skin->get_bind_count(); + for (int i = 0; i < skin_len; i++) { + StringName bn = skin->get_bind_name(i); + int bone_idx = src_skeleton->find_bone(bn); + if (bone_idx >= 0) { + Transform3D new_rest = silhouette_diff[i] * src_skeleton->get_bone_global_rest(bone_idx); + skin->set_bind_pose(i, new_rest.inverse()); + } + } + } + } + } + } + } + + // Init skeleton pose to new rest. for (int i = 0; i < src_skeleton->get_bone_count(); i++) { Transform3D fixed_rest = src_skeleton->get_bone_rest(i); src_skeleton->set_bone_pose_position(i, fixed_rest.origin); diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp index 32fd94b093..c822cd0fec 100644 --- a/editor/import/resource_importer_dynamic_font.cpp +++ b/editor/import/resource_importer_dynamic_font.cpp @@ -105,7 +105,7 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List< r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Rendering", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant())); - r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "antialiased"), true)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "antialiasing", PROPERTY_HINT_ENUM, "None,Grayscale,LCD sub-pixel"), 1)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "generate_mipmaps"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "multichannel_signed_distance_field", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), (msdf) ? true : false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "msdf_pixel_range", PROPERTY_HINT_RANGE, "1,100,1"), 8)); @@ -139,7 +139,7 @@ void ResourceImporterDynamicFont::show_advanced_options(const String &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) { print_verbose("Importing dynamic font from: " + p_source_file); - bool antialiased = p_options["antialiased"]; + int antialiasing = p_options["antialiasing"]; bool generate_mipmaps = p_options["generate_mipmaps"]; bool msdf = p_options["multichannel_signed_distance_field"]; int px_range = p_options["msdf_pixel_range"]; @@ -159,7 +159,7 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str Ref<FontFile> font; font.instantiate(); font->set_data(data); - font->set_antialiased(antialiased); + font->set_antialiasing((TextServer::FontAntialiasing)antialiasing); font->set_generate_mipmaps(generate_mipmaps); font->set_multichannel_signed_distance_field(msdf); font->set_msdf_pixel_range(px_range); diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp index 374cbe7ce2..58c2061051 100644 --- a/editor/import/resource_importer_imagefont.cpp +++ b/editor/import/resource_importer_imagefont.cpp @@ -99,7 +99,7 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin Ref<FontFile> font; font.instantiate(); - font->set_antialiased(false); + font->set_antialiasing(TextServer::FONT_ANTIALIASING_NONE); font->set_generate_mipmaps(false); font->set_multichannel_signed_distance_field(false); font->set_fixed_size(base_size); diff --git a/editor/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp index b301bbf0f9..ed83535421 100644 --- a/editor/import/resource_importer_layered_texture.cpp +++ b/editor/import/resource_importer_layered_texture.cpp @@ -327,7 +327,7 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const Ref<Image> image; image.instantiate(); - Error err = ImageLoader::load_image(p_source_file, image, nullptr, false, 1.0); + Error err = ImageLoader::load_image(p_source_file, image); if (err != OK) { return err; } diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/resource_importer_scene.cpp index 3c0de61d24..62cb6e4167 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/resource_importer_scene.cpp @@ -565,7 +565,7 @@ Node *ResourceImporterScene::_pre_fix_node(Node *p_node, Node *p_root, HashMap<R _pre_gen_shape_list(mesh, shapes, true); } - RigidDynamicBody3D *rigid_body = memnew(RigidDynamicBody3D); + RigidBody3D *rigid_body = memnew(RigidBody3D); rigid_body->set_name(_fixstr(name, "rigid_body")); p_node->replace_by(rigid_body); rigid_body->set_transform(mi->get_transform()); @@ -850,12 +850,12 @@ Node *ResourceImporterScene::_post_fix_animations(Node *p_node, Node *p_root, co AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); bool use_optimizer = node_settings["optimizer/enabled"]; - float anim_optimizer_linerr = node_settings["optimizer/max_linear_error"]; + float anim_optimizer_linerr = node_settings["optimizer/max_velocity_error"]; float anim_optimizer_angerr = node_settings["optimizer/max_angular_error"]; - float anim_optimizer_maxang = node_settings["optimizer/max_angle"]; + int anim_optimizer_preerr = node_settings["optimizer/max_precision_error"]; if (use_optimizer) { - _optimize_animations(ap, anim_optimizer_linerr, anim_optimizer_angerr, anim_optimizer_maxang); + _optimize_animations(ap, anim_optimizer_linerr, anim_optimizer_angerr, anim_optimizer_preerr); } bool use_compression = node_settings["compression/enabled"]; @@ -1060,7 +1060,7 @@ Node *ResourceImporterScene::_post_fix_node(Node *p_node, Node *p_root, HashMap< base = col; } break; case MESH_PHYSICS_RIGID_BODY_AND_MESH: { - RigidDynamicBody3D *rigid_body = memnew(RigidDynamicBody3D); + RigidBody3D *rigid_body = memnew(RigidBody3D); rigid_body->set_name(p_node->get_name()); p_node->replace_by(rigid_body); rigid_body->set_transform(mi->get_transform() * get_collision_shapes_transform(node_settings)); @@ -1386,12 +1386,12 @@ void ResourceImporterScene::_create_clips(AnimationPlayer *anim, const Array &p_ al->remove_animation("default"); // Remove default (no longer needed). } -void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_max_lin_error, float p_max_ang_error, float p_max_angle) { +void ResourceImporterScene::_optimize_animations(AnimationPlayer *anim, float p_max_vel_error, float p_max_ang_error, int p_prc_error) { List<StringName> anim_names; anim->get_animation_list(&anim_names); for (const StringName &E : anim_names) { Ref<Animation> a = anim->get_animation(E); - a->optimize(p_max_lin_error, p_max_ang_error, Math::deg2rad(p_max_angle)); + a->optimize(p_max_vel_error, p_max_ang_error, p_prc_error); } } @@ -1467,9 +1467,9 @@ void ResourceImporterScene::get_internal_import_options(InternalImportCategory p case INTERNAL_IMPORT_CATEGORY_ANIMATION_NODE: { r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "import/skip_import", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "optimizer/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), true)); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_linear_error"), 0.05)); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error"), 0.01)); - r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angle"), 22)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_velocity_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01)); + r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "optimizer/max_angular_error", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.01)); + r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "optimizer/max_precision_error", PROPERTY_HINT_NONE, "1,6,1"), 3)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "compression/enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compression/page_size", PROPERTY_HINT_RANGE, "4,512,1,suffix:kb"), 8)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "import_tracks/position", PROPERTY_HINT_ENUM, "IfPresent,IfPresentForAll,Never"), 1)); diff --git a/editor/import/resource_importer_scene.h b/editor/import/resource_importer_scene.h index b336931476..da37893cc5 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/resource_importer_scene.h @@ -280,7 +280,7 @@ public: Ref<Animation> _save_animation_to_file(Ref<Animation> anim, bool p_save_to_file, String p_save_to_path, bool p_keep_custom_tracks); void _create_clips(AnimationPlayer *anim, const Array &p_clips, bool p_bake_all); - void _optimize_animations(AnimationPlayer *anim, float p_max_lin_error, float p_max_ang_error, float p_max_angle); + void _optimize_animations(AnimationPlayer *anim, float p_max_vel_error, float p_max_ang_error, int p_prc_error); void _compress_animations(AnimationPlayer *anim, int p_page_size_kb); Node *pre_import(const String &p_source_file, const HashMap<StringName, Variant> &p_options); diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 0eed6184c0..17b94ec706 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -409,11 +409,26 @@ void ResourceImporterTexture::_save_ctex(const Ref<Image> &p_image, const String } Error ResourceImporterTexture::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) { + // Parse import options. + int32_t loader_flags = ImageFormatLoader::FLAG_NONE; + + // Compression. CompressMode compress_mode = CompressMode(int(p_options["compress/mode"])); const float lossy = p_options["compress/lossy_quality"]; const int pack_channels = p_options["compress/channel_pack"]; + const int normal = p_options["compress/normal_map"]; + const int hdr_compression = p_options["compress/hdr_compression"]; + const int bptc_ldr = p_options["compress/bptc_ldr"]; + + // Mipmaps. const bool mipmaps = p_options["mipmaps/generate"]; const uint32_t mipmap_limit = mipmaps ? uint32_t(p_options["mipmaps/limit"]) : uint32_t(-1); + + // Roughness. + const int roughness = p_options["roughness/mode"]; + const String normal_map = p_options["roughness/src_normal"]; + + // Processing. const bool fix_alpha_border = p_options["process/fix_alpha_border"]; const bool premult_alpha = p_options["process/premult_alpha"]; const bool normal_map_invert_y = p_options["process/normal_map_invert_y"]; @@ -421,29 +436,29 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String const bool stream = false; const int size_limit = p_options["process/size_limit"]; const bool hdr_as_srgb = p_options["process/hdr_as_srgb"]; + if (hdr_as_srgb) { + loader_flags |= ImageFormatLoader::FLAG_FORCE_LINEAR; + } const bool hdr_clamp_exposure = p_options["process/hdr_clamp_exposure"]; - const int normal = p_options["compress/normal_map"]; - const int hdr_compression = p_options["compress/hdr_compression"]; - const int bptc_ldr = p_options["compress/bptc_ldr"]; - const int roughness = p_options["roughness/mode"]; - const String normal_map = p_options["roughness/src_normal"]; + float scale = 1.0; + // SVG-specific options. if (p_options.has("svg/scale")) { scale = p_options["svg/scale"]; } Ref<Image> normal_image; Image::RoughnessChannel roughness_channel = Image::ROUGHNESS_CHANNEL_R; - if (mipmaps && roughness > 1 && FileAccess::exists(normal_map)) { normal_image.instantiate(); if (ImageLoader::load_image(normal_map, normal_image) == OK) { roughness_channel = Image::RoughnessChannel(roughness - 2); } } + Ref<Image> image; image.instantiate(); - Error err = ImageLoader::load_image(p_source_file, image, nullptr, hdr_as_srgb, scale); + Error err = ImageLoader::load_image(p_source_file, image, nullptr, loader_flags, scale); if (err != OK) { return err; } diff --git a/editor/import/resource_importer_texture_atlas.cpp b/editor/import/resource_importer_texture_atlas.cpp index bae1b903c6..9171f04f42 100644 --- a/editor/import/resource_importer_texture_atlas.cpp +++ b/editor/import/resource_importer_texture_atlas.cpp @@ -382,7 +382,6 @@ Error ResourceImporterTextureAtlas::import_group_file(const String &p_group_file mesh_texture->set_mesh(mesh); texture = mesh_texture; - //mesh } String save_path = p_base_paths[E.key] + ".res"; diff --git a/editor/import/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp index a1e00f7d30..1dcae2841b 100644 --- a/editor/import/resource_importer_wav.cpp +++ b/editor/import/resource_importer_wav.cpp @@ -390,7 +390,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s int first = 0; int last = (frames / format_channels) - 1; bool found = false; - float limit = Math::db2linear(TRIM_DB_LIMIT); + float limit = Math::db_to_linear(TRIM_DB_LIMIT); for (int i = 0; i < data.size() / format_channels; i++) { float ampChannelSum = 0; diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index 087ef48b56..02270e163f 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -584,12 +584,12 @@ void ImportDock::_set_dirty(bool p_dirty) { // Add a dirty marker to notify the user that they should reimport the selected resource to see changes. import->set_text(TTR("Reimport") + " (*)"); import->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); - import->set_tooltip(TTR("You have pending changes that haven't been applied yet. Click Reimport to apply changes made to the import options.\nSelecting another resource in the FileSystem dock without clicking Reimport first will discard changes made in the Import dock.")); + import->set_tooltip_text(TTR("You have pending changes that haven't been applied yet. Click Reimport to apply changes made to the import options.\nSelecting another resource in the FileSystem dock without clicking Reimport first will discard changes made in the Import dock.")); } else { // Remove the dirty marker on the Reimport button. import->set_text(TTR("Reimport")); import->remove_theme_color_override("font_color"); - import->set_tooltip(""); + import->set_tooltip_text(""); } } diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 79d94246ad..7af04e17c1 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -178,7 +178,8 @@ void InspectorDock::_menu_option_confirm(int p_option, bool p_confirmed) { } } - editor_data->get_undo_redo().clear_history(); + int history_id = editor_data->get_undo_redo()->get_history_for_object(current).id; + editor_data->get_undo_redo()->clear_history(true, history_id); EditorNode::get_singleton()->get_editor_plugins_over()->edit(nullptr); EditorNode::get_singleton()->get_editor_plugins_over()->edit(current); @@ -246,7 +247,7 @@ void InspectorDock::_resource_file_selected(String p_file) { } if (res.is_null()) { - warning_dialog->set_text(TTR("Failed to load resource.")); + info_dialog->set_text(TTR("Failed to load resource.")); return; }; @@ -408,8 +409,8 @@ void InspectorDock::_menu_expand_revertable() { inspector->expand_revertable(); } -void InspectorDock::_warning_pressed() { - warning_dialog->popup_centered(); +void InspectorDock::_info_pressed() { + info_dialog->popup_centered(); } Container *InspectorDock::get_addon_area() { @@ -445,8 +446,13 @@ void InspectorDock::_notification(int p_what) { history_menu->set_icon(get_theme_icon(SNAME("History"), SNAME("EditorIcons"))); object_menu->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); search->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); - warning->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); - warning->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + if (info_is_warning) { + info->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); + info->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + } else { + info->set_icon(get_theme_icon(SNAME("NodeInfo"), SNAME("EditorIcons"))); + info->add_theme_color_override("font_color", get_theme_color(SNAME("font_color"), SNAME("Editor"))); + } } break; } } @@ -475,11 +481,22 @@ void InspectorDock::open_resource(const String &p_type) { _load_resource(p_type); } -void InspectorDock::set_warning(const String &p_message) { - warning->hide(); - if (!p_message.is_empty()) { - warning->show(); - warning_dialog->set_text(p_message); +void InspectorDock::set_info(const String &p_button_text, const String &p_message, bool p_is_warning) { + info->hide(); + info_is_warning = p_is_warning; + + if (info_is_warning) { + info->set_icon(get_theme_icon(SNAME("NodeWarning"), SNAME("EditorIcons"))); + info->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + } else { + info->set_icon(get_theme_icon(SNAME("NodeInfo"), SNAME("EditorIcons"))); + info->add_theme_color_override("font_color", get_theme_color(SNAME("font_color"), SNAME("Editor"))); + } + + if (!p_button_text.is_empty() && !p_message.is_empty()) { + info->show(); + info->set_text(p_button_text); + info_dialog->set_text(p_message); } } @@ -514,7 +531,7 @@ void InspectorDock::update(Object *p_object) { resource_extra_popup->set_item_disabled(resource_extra_popup->get_item_index(RESOURCE_MAKE_BUILT_IN), !is_resource || is_text_file); if (!is_object || is_text_file) { - warning->hide(); + info->hide(); editor_path->clear_path(); return; } @@ -616,20 +633,20 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { resource_new_button = memnew(Button); resource_new_button->set_flat(true); - resource_new_button->set_tooltip(TTR("Create a new resource in memory and edit it.")); + resource_new_button->set_tooltip_text(TTR("Create a new resource in memory and edit it.")); general_options_hb->add_child(resource_new_button); resource_new_button->connect("pressed", callable_mp(this, &InspectorDock::_new_resource)); resource_new_button->set_focus_mode(Control::FOCUS_NONE); resource_load_button = memnew(Button); resource_load_button->set_flat(true); - resource_load_button->set_tooltip(TTR("Load an existing resource from disk and edit it.")); + resource_load_button->set_tooltip_text(TTR("Load an existing resource from disk and edit it.")); general_options_hb->add_child(resource_load_button); resource_load_button->connect("pressed", callable_mp(this, &InspectorDock::_open_resource_selector)); resource_load_button->set_focus_mode(Control::FOCUS_NONE); resource_save_button = memnew(MenuButton); - resource_save_button->set_tooltip(TTR("Save the currently edited resource.")); + resource_save_button->set_tooltip_text(TTR("Save the currently edited resource.")); general_options_hb->add_child(resource_save_button); resource_save_button->get_popup()->add_item(TTR("Save"), RESOURCE_SAVE); resource_save_button->get_popup()->add_item(TTR("Save As..."), RESOURCE_SAVE_AS); @@ -638,7 +655,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { resource_save_button->set_disabled(true); resource_extra_button = memnew(MenuButton); - resource_extra_button->set_tooltip(TTR("Extra resource options.")); + resource_extra_button->set_tooltip_text(TTR("Extra resource options.")); general_options_hb->add_child(resource_extra_button); resource_extra_button->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_resource_extra_popup)); resource_extra_button->get_popup()->add_icon_shortcut(get_theme_icon(SNAME("ActionPaste"), SNAME("EditorIcons")), ED_SHORTCUT("property_editor/paste_resource", TTR("Edit Resource from Clipboard")), RESOURCE_EDIT_CLIPBOARD); @@ -654,19 +671,19 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { backward_button = memnew(Button); backward_button->set_flat(true); general_options_hb->add_child(backward_button); - backward_button->set_tooltip(TTR("Go to the previous edited object in history.")); + backward_button->set_tooltip_text(TTR("Go to the previous edited object in history.")); backward_button->set_disabled(true); backward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_back)); forward_button = memnew(Button); forward_button->set_flat(true); general_options_hb->add_child(forward_button); - forward_button->set_tooltip(TTR("Go to the next edited object in history.")); + forward_button->set_tooltip_text(TTR("Go to the next edited object in history.")); forward_button->set_disabled(true); forward_button->connect("pressed", callable_mp(this, &InspectorDock::_edit_forward)); history_menu = memnew(MenuButton); - history_menu->set_tooltip(TTR("History of recently edited objects.")); + history_menu->set_tooltip_text(TTR("History of recently edited objects.")); general_options_hb->add_child(history_menu); history_menu->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_history)); history_menu->get_popup()->connect("id_pressed", callable_mp(this, &InspectorDock::_select_history)); @@ -680,7 +697,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { open_docs_button = memnew(Button); open_docs_button->set_flat(true); open_docs_button->set_disabled(true); - open_docs_button->set_tooltip(TTR("Open documentation for this object.")); + open_docs_button->set_tooltip_text(TTR("Open documentation for this object.")); open_docs_button->set_shortcut(ED_SHORTCUT("property_editor/open_help", TTR("Open Documentation"))); subresource_hb->add_child(open_docs_button); open_docs_button->connect("pressed", callable_mp(this, &InspectorDock::_menu_option).bind(OBJECT_REQUEST_HELP)); @@ -702,16 +719,15 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { object_menu = memnew(MenuButton); object_menu->set_shortcut_context(this); property_tools_hb->add_child(object_menu); - object_menu->set_tooltip(TTR("Manage object properties.")); + object_menu->set_tooltip_text(TTR("Manage object properties.")); object_menu->get_popup()->connect("about_to_popup", callable_mp(this, &InspectorDock::_prepare_menu)); object_menu->get_popup()->connect("id_pressed", callable_mp(this, &InspectorDock::_menu_option)); - warning = memnew(Button); - add_child(warning); - warning->set_text(TTR("Changes may be lost!")); - warning->set_clip_text(true); - warning->hide(); - warning->connect("pressed", callable_mp(this, &InspectorDock::_warning_pressed)); + info = memnew(Button); + add_child(info); + info->set_clip_text(true); + info->hide(); + info->connect("pressed", callable_mp(this, &InspectorDock::_info_pressed)); unique_resources_confirmation = memnew(ConfirmationDialog); add_child(unique_resources_confirmation); @@ -736,8 +752,8 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { unique_resources_confirmation->connect("confirmed", callable_mp(this, &InspectorDock::_menu_confirm_current)); - warning_dialog = memnew(AcceptDialog); - EditorNode::get_singleton()->get_gui_base()->add_child(warning_dialog); + info_dialog = memnew(AcceptDialog); + EditorNode::get_singleton()->get_gui_base()->add_child(info_dialog); load_resource_dialog = memnew(EditorFileDialog); add_child(load_resource_dialog); @@ -755,7 +771,7 @@ InspectorDock::InspectorDock(EditorData &p_editor_data) { inspector->set_property_name_style(EditorPropertyNameProcessor::get_default_inspector_style()); inspector->set_use_folding(!bool(EDITOR_GET("interface/inspector/disable_folding"))); inspector->register_text_enter(search); - inspector->set_undo_redo(&editor_data->get_undo_redo()); + inspector->set_undo_redo(editor_data->get_undo_redo()); inspector->set_use_filter(true); // TODO: check me diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index e32410151f..5ebcbf70c7 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -93,8 +93,9 @@ class InspectorDock : public VBoxContainer { MenuButton *object_menu = nullptr; EditorPath *editor_path = nullptr; - Button *warning = nullptr; - AcceptDialog *warning_dialog = nullptr; + bool info_is_warning = false; // Display in yellow and use warning icon if true. + Button *info = nullptr; + AcceptDialog *info_dialog = nullptr; int current_option = -1; ConfirmationDialog *unique_resources_confirmation = nullptr; @@ -118,7 +119,7 @@ class InspectorDock : public VBoxContainer { void _paste_resource(); void _prepare_resource_extra_popup(); - void _warning_pressed(); + void _info_pressed(); void _resource_created(); void _resource_selected(const Ref<Resource> &p_res, const String &p_property); void _edit_forward(); @@ -145,7 +146,7 @@ public: void edit_resource(const Ref<Resource> &p_resource); void open_resource(const String &p_type); void clear(); - void set_warning(const String &p_message); + void set_info(const String &p_button_text, const String &p_message, bool p_is_warning); void update(Object *p_object); Container *get_addon_area(); EditorInspector *get_inspector() { return inspector; } diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp index e8fb80eb57..77a1700ebf 100644 --- a/editor/localization_editor.cpp +++ b/editor/localization_editor.cpp @@ -36,6 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_translation_parser.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/filesystem_dock.h" #include "editor/pot_generator.h" #include "scene/gui/control.h" diff --git a/editor/localization_editor.h b/editor/localization_editor.h index 10ccdfdc13..ecac171fe3 100644 --- a/editor/localization_editor.h +++ b/editor/localization_editor.h @@ -56,7 +56,7 @@ class LocalizationEditor : public VBoxContainer { EditorFileDialog *pot_file_open_dialog = nullptr; EditorFileDialog *pot_generate_dialog = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; bool updating_translations = false; String localization_changed; diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp index a694b8d754..70cc54668d 100644 --- a/editor/multi_node_edit.cpp +++ b/editor/multi_node_edit.cpp @@ -32,6 +32,7 @@ #include "core/math/math_fieldwise.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" bool MultiNodeEdit::_set(const StringName &p_name, const Variant &p_value) { return _set_impl(p_name, p_value, ""); @@ -54,7 +55,7 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value, node_path_target = es->get_node(p_value); } - UndoRedo *ur = EditorNode::get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("MultiNode Set") + " " + String(name), UndoRedo::MERGE_ENDS); for (const NodePath &E : nodes) { diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp index 986370f537..e366f3ce3c 100644 --- a/editor/node_dock.cpp +++ b/editor/node_dock.cpp @@ -53,7 +53,6 @@ void NodeDock::_bind_methods() { void NodeDock::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { connections_button->set_icon(get_theme_icon(SNAME("Signals"), SNAME("EditorIcons"))); groups_button->set_icon(get_theme_icon(SNAME("Groups"), SNAME("EditorIcons"))); @@ -117,7 +116,7 @@ NodeDock::NodeDock() { groups_button->connect("pressed", callable_mp(this, &NodeDock::show_groups)); connections = memnew(ConnectionsDock); - connections->set_undoredo(EditorNode::get_undo_redo()); + connections->set_undo_redo(EditorNode::get_undo_redo()); add_child(connections); connections->set_v_size_flags(SIZE_EXPAND_FILL); connections->hide(); diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp index 6d323572e6..88aa6354e4 100644 --- a/editor/plugin_config_dialog.cpp +++ b/editor/plugin_config_dialog.cpp @@ -111,32 +111,32 @@ void PluginConfigDialog::_on_required_text_changed(const String &) { name_validation->set_texture(valid_icon); subfolder_validation->set_texture(valid_icon); script_validation->set_texture(valid_icon); - name_validation->set_tooltip(""); - subfolder_validation->set_tooltip(""); - script_validation->set_tooltip(""); + name_validation->set_tooltip_text(""); + subfolder_validation->set_tooltip_text(""); + script_validation->set_tooltip_text(""); // Change valid status to invalid depending on conditions. Vector<String> errors; if (name_edit->get_text().is_empty()) { is_valid = false; name_validation->set_texture(invalid_icon); - name_validation->set_tooltip(TTR("Plugin name cannot be blank.")); + name_validation->set_tooltip_text(TTR("Plugin name cannot be blank.")); } if ((!script_edit->get_text().get_extension().is_empty() && script_edit->get_text().get_extension() != ext) || script_edit->get_text().ends_with(".")) { is_valid = false; script_validation->set_texture(invalid_icon); - script_validation->set_tooltip(vformat(TTR("Script extension must match chosen language extension (.%s)."), ext)); + script_validation->set_tooltip_text(vformat(TTR("Script extension must match chosen language extension (.%s)."), ext)); } if (!subfolder_edit->get_text().is_empty() && !subfolder_edit->get_text().is_valid_filename()) { is_valid = false; subfolder_validation->set_texture(invalid_icon); - subfolder_validation->set_tooltip(TTR("Subfolder name is not a valid folder name.")); + subfolder_validation->set_tooltip_text(TTR("Subfolder name is not a valid folder name.")); } else { String path = "res://addons/" + _get_subfolder(); if (!_edit_mode && DirAccess::exists(path)) { // Only show this error if in "create" mode. is_valid = false; subfolder_validation->set_texture(invalid_icon); - subfolder_validation->set_tooltip(TTR("Subfolder cannot be one which already exists.")); + subfolder_validation->set_tooltip_text(TTR("Subfolder cannot be one which already exists.")); } } diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index a7d7c0145a..d17b88c2ce 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -36,6 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/separator.h" bool AbstractPolygon2DEditor::Vertex::operator==(const AbstractPolygon2DEditor::Vertex &p_vertex) const { @@ -150,7 +151,6 @@ void AbstractPolygon2DEditor::_menu_option(int p_option) { void AbstractPolygon2DEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { button_create->set_icon(get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons"))); button_edit->set_icon(get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons"))); @@ -234,13 +234,13 @@ void AbstractPolygon2DEditor::disable_polygon_editing(bool p_disable, String p_r button_delete->set_disabled(p_disable); if (p_disable) { - button_create->set_tooltip(p_reason); - button_edit->set_tooltip(p_reason); - button_delete->set_tooltip(p_reason); + button_create->set_tooltip_text(p_reason); + button_edit->set_tooltip_text(p_reason); + button_delete->set_tooltip_text(p_reason); } else { - button_create->set_tooltip(TTR("Create points.")); - button_edit->set_tooltip(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point")); - button_delete->set_tooltip(TTR("Erase points.")); + button_create->set_tooltip_text(TTR("Create points.")); + button_edit->set_tooltip_text(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point")); + button_delete->set_tooltip_text(TTR("Erase points.")); } } diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 696fd7b637..1fbbe67c8d 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -36,6 +36,7 @@ #include "scene/gui/box_container.h" class CanvasItemEditor; +class EditorUndoRedoManager; class AbstractPolygon2DEditor : public HBoxContainer { GDCLASS(AbstractPolygon2DEditor, HBoxContainer); @@ -99,7 +100,7 @@ protected: int mode = MODE_EDIT; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; virtual void _menu_option(int p_option); void _wip_changed(); diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 32d97c65a9..b8a1d2cb69 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -35,6 +35,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/animation/animation_blend_tree.h" StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { @@ -47,7 +48,9 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) { if (selected_point != -1) { - _erase_selected(); + if (!read_only) { + _erase_selected(); + } accept_event(); } } @@ -55,62 +58,64 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (mb->get_button_index() == MouseButton::LEFT && tool_create->is_pressed()))) { - menu->clear(); - animations_menu->clear(); - animations_to_add.clear(); + if (!read_only) { + menu->clear(); + animations_menu->clear(); + animations_to_add.clear(); - List<StringName> classes; - ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); - classes.sort_custom<StringName::AlphCompare>(); + List<StringName> classes; + ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); + classes.sort_custom<StringName::AlphCompare>(); - menu->add_submenu_item(TTR("Add Animation"), "animations"); + menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); - ERR_FAIL_COND(!gp); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); + ERR_FAIL_COND(!gp); - if (gp->has_node(gp->get_animation_player())) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); + if (gp->has_node(gp->get_animation_player())) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); - if (ap) { - List<StringName> names; - ap->get_animation_list(&names); + if (ap) { + List<StringName> names; + ap->get_animation_list(&names); - for (const StringName &E : names) { - animations_menu->add_icon_item(get_theme_icon(SNAME("Animation"), SNAME("EditorIcons")), E); - animations_to_add.push_back(E); + for (const StringName &E : names) { + animations_menu->add_icon_item(get_theme_icon(SNAME("Animation"), SNAME("EditorIcons")), E); + animations_to_add.push_back(E); + } } } - } - for (const StringName &E : classes) { - String name = String(E).replace_first("AnimationNode", ""); - if (name == "Animation" || name == "StartState" || name == "EndState") { - continue; - } + for (const StringName &E : classes) { + String name = String(E).replace_first("AnimationNode", ""); + if (name == "Animation" || name == "StartState" || name == "EndState") { + continue; + } - int idx = menu->get_item_count(); - menu->add_item(vformat(TTR("Add %s"), name), idx); - menu->set_item_metadata(idx, E); - } + int idx = menu->get_item_count(); + menu->add_item(vformat(TTR("Add %s"), name), idx); + menu->set_item_metadata(idx, E); + } - Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); - if (clipb.is_valid()) { + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } menu->add_separator(); - menu->add_item(TTR("Paste"), MENU_PASTE); - } - menu->add_separator(); - menu->add_item(TTR("Load..."), MENU_LOAD_FILE); + menu->add_item(TTR("Load..."), MENU_LOAD_FILE); - menu->set_position(blend_space_draw->get_screen_position() + mb->get_position()); - menu->reset_size(); - menu->popup(); + menu->set_position(blend_space_draw->get_screen_position() + mb->get_position()); + menu->reset_size(); + menu->popup(); - add_point_pos = (mb->get_position() / blend_space_draw->get_size()).x; - add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); - add_point_pos += blend_space->get_min_space(); + add_point_pos = (mb->get_position() / blend_space_draw->get_size()).x; + add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); + add_point_pos += blend_space->get_min_space(); - if (snap->is_pressed()) { - add_point_pos = Math::snapped(add_point_pos, blend_space->get_snap()); + if (snap->is_pressed()) { + add_point_pos = Math::snapped(add_point_pos, blend_space->get_snap()); + } } } @@ -137,31 +142,33 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven } if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) { - if (dragging_selected) { - // move - float point = blend_space->get_blend_point_position(selected_point); - point += drag_ofs.x; + if (!read_only) { + if (dragging_selected) { + // move + float point = blend_space->get_blend_point_position(selected_point); + point += drag_ofs.x; + + if (snap->is_pressed()) { + point = Math::snapped(point, blend_space->get_snap()); + } - if (snap->is_pressed()) { - point = Math::snapped(point, blend_space->get_snap()); + updating = true; + undo_redo->create_action(TTR("Move Node Point")); + undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); + undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->add_do_method(this, "_update_edited_point_pos"); + undo_redo->add_undo_method(this, "_update_edited_point_pos"); + undo_redo->commit_action(); + updating = false; + _update_edited_point_pos(); } - updating = true; - undo_redo->create_action(TTR("Move Node Point")); - undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); - undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->add_do_method(this, "_update_edited_point_pos"); - undo_redo->add_undo_method(this, "_update_edited_point_pos"); - undo_redo->commit_action(); - updating = false; - _update_edited_point_pos(); + dragging_selected_attempt = false; + dragging_selected = false; + blend_space_draw->update(); } - - dragging_selected_attempt = false; - dragging_selected = false; - blend_space_draw->update(); } // *set* the blend @@ -254,10 +261,12 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_draw() { for (int i = 0; i < blend_space->get_blend_point_count(); i++) { float point = blend_space->get_blend_point_position(i); - if (dragging_selected && selected_point == i) { - point += drag_ofs.x; - if (snap->is_pressed()) { - point = Math::snapped(point, blend_space->get_snap()); + if (!read_only) { + if (dragging_selected && selected_point == i) { + point += drag_ofs.x; + if (snap->is_pressed()) { + point = Math::snapped(point, blend_space->get_snap()); + } } } @@ -474,7 +483,7 @@ void AnimationNodeBlendSpace1DEditor::_update_edited_point_pos() { void AnimationNodeBlendSpace1DEditor::_update_tool_erase() { bool point_valid = selected_point >= 0 && selected_point < blend_space->get_blend_point_count(); - tool_erase->set_disabled(!point_valid); + tool_erase->set_disabled(!point_valid || read_only); if (point_valid) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); @@ -485,7 +494,11 @@ void AnimationNodeBlendSpace1DEditor::_update_tool_erase() { open_editor->hide(); } - edit_hb->show(); + if (!read_only) { + edit_hb->show(); + } else { + edit_hb->hide(); + } } else { edit_hb->hide(); } @@ -537,7 +550,6 @@ void AnimationNodeBlendSpace1DEditor::_open_editor() { void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); @@ -589,10 +601,20 @@ bool AnimationNodeBlendSpace1DEditor::can_edit(const Ref<AnimationNode> &p_node) void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) { blend_space = p_node; + read_only = false; if (!blend_space.is_null()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(blend_space); + _update_space(); } + + tool_create->set_disabled(read_only); + edit_value->set_editable(!read_only); + label_value->set_editable(!read_only); + min_value->set_editable(!read_only); + max_value->set_editable(!read_only); + sync->set_disabled(read_only); } AnimationNodeBlendSpace1DEditor *AnimationNodeBlendSpace1DEditor::singleton = nullptr; @@ -612,7 +634,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { tool_blend->set_button_group(bg); top_hb->add_child(tool_blend); tool_blend->set_pressed(true); - tool_blend->set_tooltip(TTR("Set the blending position within the space")); + tool_blend->set_tooltip_text(TTR("Set the blending position within the space")); tool_blend->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(3)); tool_select = memnew(Button); @@ -620,7 +642,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { tool_select->set_toggle_mode(true); tool_select->set_button_group(bg); top_hb->add_child(tool_select); - tool_select->set_tooltip(TTR("Select and move points, create points with RMB.")); + tool_select->set_tooltip_text(TTR("Select and move points, create points with RMB.")); tool_select->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(0)); tool_create = memnew(Button); @@ -628,7 +650,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { tool_create->set_toggle_mode(true); tool_create->set_button_group(bg); top_hb->add_child(tool_create); - tool_create->set_tooltip(TTR("Create points.")); + tool_create->set_tooltip_text(TTR("Create points.")); tool_create->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_tool_switch).bind(1)); tool_erase_sep = memnew(VSeparator); @@ -636,7 +658,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { tool_erase = memnew(Button); tool_erase->set_flat(true); top_hb->add_child(tool_erase); - tool_erase->set_tooltip(TTR("Erase points.")); + tool_erase->set_tooltip_text(TTR("Erase points.")); tool_erase->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_erase_selected)); top_hb->add_child(memnew(VSeparator)); @@ -646,7 +668,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { snap->set_toggle_mode(true); top_hb->add_child(snap); snap->set_pressed(true); - snap->set_tooltip(TTR("Enable snap and show grid.")); + snap->set_tooltip_text(TTR("Enable snap and show grid.")); snap->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace1DEditor::_snap_toggled)); snap_value = memnew(SpinBox); diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index 9b06f3248f..c8b01cb54b 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -40,10 +40,13 @@ #include "scene/gui/separator.h" #include "scene/gui/tree.h" +class EditorUndoRedoManager; + class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { GDCLASS(AnimationNodeBlendSpace1DEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendSpace1D> blend_space; + bool read_only = false; HBoxContainer *goto_parent_hb = nullptr; Button *goto_parent = nullptr; @@ -76,7 +79,7 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { bool updating = false; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; static AnimationNodeBlendSpace1DEditor *singleton; diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index dc764725dd..918fca0e1c 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -39,6 +39,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" @@ -59,11 +60,29 @@ void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) { blend_space->disconnect("triangles_updated", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_changed)); } blend_space = p_node; + read_only = false; if (!blend_space.is_null()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(blend_space); + blend_space->connect("triangles_updated", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_blend_space_changed)); _update_space(); } + + tool_create->set_disabled(read_only); + interpolation->set_disabled(read_only); + max_x_value->set_editable(!read_only); + min_x_value->set_editable(!read_only); + max_y_value->set_editable(!read_only); + min_y_value->set_editable(!read_only); + label_x->set_editable(!read_only); + label_y->set_editable(!read_only); + edit_x->set_editable(!read_only); + edit_y->set_editable(!read_only); + tool_triangle->set_disabled(read_only); + auto_triangles->set_disabled(read_only); + sync->set_disabled(read_only); + interpolation->set_disabled(read_only); } StringName AnimationNodeBlendSpace2DEditor::get_blend_position_path() const { @@ -75,7 +94,9 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven Ref<InputEventKey> k = p_event; if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) { if (selected_point != -1 || selected_triangle != -1) { - _erase_selected(); + if (!read_only) { + _erase_selected(); + } accept_event(); } } @@ -83,57 +104,59 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (mb->get_button_index() == MouseButton::LEFT && tool_create->is_pressed()))) { - menu->clear(); - animations_menu->clear(); - animations_to_add.clear(); - List<StringName> classes; - classes.sort_custom<StringName::AlphCompare>(); - - ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); - menu->add_submenu_item(TTR("Add Animation"), "animations"); - - AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); - ERR_FAIL_COND(!gp); - if (gp && gp->has_node(gp->get_animation_player())) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); - if (ap) { - List<StringName> names; - ap->get_animation_list(&names); - for (const StringName &E : names) { - animations_menu->add_icon_item(get_theme_icon(SNAME("Animation"), SNAME("EditorIcons")), E); - animations_to_add.push_back(E); + if (!read_only) { + menu->clear(); + animations_menu->clear(); + animations_to_add.clear(); + List<StringName> classes; + classes.sort_custom<StringName::AlphCompare>(); + + ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); + menu->add_submenu_item(TTR("Add Animation"), "animations"); + + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); + ERR_FAIL_COND(!gp); + if (gp && gp->has_node(gp->get_animation_player())) { + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); + if (ap) { + List<StringName> names; + ap->get_animation_list(&names); + for (const StringName &E : names) { + animations_menu->add_icon_item(get_theme_icon(SNAME("Animation"), SNAME("EditorIcons")), E); + animations_to_add.push_back(E); + } } } - } - for (const StringName &E : classes) { - String name = String(E).replace_first("AnimationNode", ""); - if (name == "Animation" || name == "StartState" || name == "EndState") { - continue; // nope + for (const StringName &E : classes) { + String name = String(E).replace_first("AnimationNode", ""); + if (name == "Animation" || name == "StartState" || name == "EndState") { + continue; // nope + } + int idx = menu->get_item_count(); + menu->add_item(vformat(TTR("Add %s"), name), idx); + menu->set_item_metadata(idx, E); } - int idx = menu->get_item_count(); - menu->add_item(vformat(TTR("Add %s"), name), idx); - menu->set_item_metadata(idx, E); - } - Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); - if (clipb.is_valid()) { + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } menu->add_separator(); - menu->add_item(TTR("Paste"), MENU_PASTE); - } - menu->add_separator(); - menu->add_item(TTR("Load..."), MENU_LOAD_FILE); - - menu->set_position(blend_space_draw->get_screen_position() + mb->get_position()); - menu->reset_size(); - menu->popup(); - add_point_pos = (mb->get_position() / blend_space_draw->get_size()); - add_point_pos.y = 1.0 - add_point_pos.y; - add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); - add_point_pos += blend_space->get_min_space(); - - if (snap->is_pressed()) { - add_point_pos = add_point_pos.snapped(blend_space->get_snap()); + menu->add_item(TTR("Load..."), MENU_LOAD_FILE); + + menu->set_position(blend_space_draw->get_screen_position() + mb->get_position()); + menu->reset_size(); + menu->popup(); + add_point_pos = (mb->get_position() / blend_space_draw->get_size()); + add_point_pos.y = 1.0 - add_point_pos.y; + add_point_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); + add_point_pos += blend_space->get_min_space(); + + if (snap->is_pressed()) { + add_point_pos = add_point_pos.snapped(blend_space->get_snap()); + } } } @@ -221,17 +244,19 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven point = point.snapped(blend_space->get_snap()); } - updating = true; - undo_redo->create_action(TTR("Move Node Point")); - undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); - undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); - undo_redo->add_do_method(this, "_update_space"); - undo_redo->add_undo_method(this, "_update_space"); - undo_redo->add_do_method(this, "_update_edited_point_pos"); - undo_redo->add_undo_method(this, "_update_edited_point_pos"); - undo_redo->commit_action(); - updating = false; - _update_edited_point_pos(); + if (!read_only) { + updating = true; + undo_redo->create_action(TTR("Move Node Point")); + undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); + undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); + undo_redo->add_do_method(this, "_update_space"); + undo_redo->add_undo_method(this, "_update_space"); + undo_redo->add_do_method(this, "_update_edited_point_pos"); + undo_redo->add_undo_method(this, "_update_edited_point_pos"); + undo_redo->commit_action(); + updating = false; + _update_edited_point_pos(); + } } dragging_selected_attempt = false; dragging_selected = false; @@ -258,7 +283,9 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven if (mm.is_valid() && dragging_selected_attempt) { dragging_selected = true; - drag_ofs = ((mm->get_position() - drag_from) / blend_space_draw->get_size()) * (blend_space->get_max_space() - blend_space->get_min_space()) * Vector2(1, -1); + if (!read_only) { + drag_ofs = ((mm->get_position() - drag_from) / blend_space_draw->get_size()) * (blend_space->get_max_space() - blend_space->get_min_space()) * Vector2(1, -1); + } blend_space_draw->update(); _update_edited_point_pos(); } @@ -354,7 +381,10 @@ void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) { } void AnimationNodeBlendSpace2DEditor::_update_tool_erase() { - tool_erase->set_disabled(!(selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) && !(selected_triangle >= 0 && selected_triangle < blend_space->get_triangle_count())); + tool_erase->set_disabled( + (!(selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) && !(selected_triangle >= 0 && selected_triangle < blend_space->get_triangle_count())) || + read_only); + if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); if (AnimationTreeEditor::get_singleton()->can_edit(an)) { @@ -362,7 +392,11 @@ void AnimationNodeBlendSpace2DEditor::_update_tool_erase() { } else { open_editor->hide(); } - edit_hb->show(); + if (!read_only) { + edit_hb->show(); + } else { + edit_hb->hide(); + } } else { edit_hb->hide(); } @@ -502,10 +536,12 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { points.clear(); for (int i = 0; i < blend_space->get_blend_point_count(); i++) { Vector2 point = blend_space->get_blend_point_position(i); - if (dragging_selected && selected_point == i) { - point += drag_ofs; - if (snap->is_pressed()) { - point = point.snapped(blend_space->get_snap()); + if (!read_only) { + if (dragging_selected && selected_point == i) { + point += drag_ofs; + if (snap->is_pressed()) { + point = point.snapped(blend_space->get_snap()); + } } } point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); @@ -736,7 +772,6 @@ void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) { void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); @@ -832,7 +867,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { tool_blend->set_button_group(bg); top_hb->add_child(tool_blend); tool_blend->set_pressed(true); - tool_blend->set_tooltip(TTR("Set the blending position within the space")); + tool_blend->set_tooltip_text(TTR("Set the blending position within the space")); tool_blend->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(3)); tool_select = memnew(Button); @@ -840,7 +875,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { tool_select->set_toggle_mode(true); tool_select->set_button_group(bg); top_hb->add_child(tool_select); - tool_select->set_tooltip(TTR("Select and move points, create points with RMB.")); + tool_select->set_tooltip_text(TTR("Select and move points, create points with RMB.")); tool_select->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(0)); tool_create = memnew(Button); @@ -848,7 +883,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { tool_create->set_toggle_mode(true); tool_create->set_button_group(bg); top_hb->add_child(tool_create); - tool_create->set_tooltip(TTR("Create points.")); + tool_create->set_tooltip_text(TTR("Create points.")); tool_create->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(1)); tool_triangle = memnew(Button); @@ -856,7 +891,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { tool_triangle->set_toggle_mode(true); tool_triangle->set_button_group(bg); top_hb->add_child(tool_triangle); - tool_triangle->set_tooltip(TTR("Create triangles by connecting points.")); + tool_triangle->set_tooltip_text(TTR("Create triangles by connecting points.")); tool_triangle->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_tool_switch).bind(2)); tool_erase_sep = memnew(VSeparator); @@ -864,7 +899,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { tool_erase = memnew(Button); tool_erase->set_flat(true); top_hb->add_child(tool_erase); - tool_erase->set_tooltip(TTR("Erase points and triangles.")); + tool_erase->set_tooltip_text(TTR("Erase points and triangles.")); tool_erase->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_erase_selected)); tool_erase->set_disabled(true); @@ -875,7 +910,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { top_hb->add_child(auto_triangles); auto_triangles->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled)); auto_triangles->set_toggle_mode(true); - auto_triangles->set_tooltip(TTR("Generate blend triangles automatically (instead of manually)")); + auto_triangles->set_tooltip_text(TTR("Generate blend triangles automatically (instead of manually)")); top_hb->add_child(memnew(VSeparator)); @@ -884,7 +919,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { snap->set_toggle_mode(true); top_hb->add_child(snap); snap->set_pressed(true); - snap->set_tooltip(TTR("Enable snap and show grid.")); + snap->set_tooltip_text(TTR("Enable snap and show grid.")); snap->connect("pressed", callable_mp(this, &AnimationNodeBlendSpace2DEditor::_snap_toggled)); snap_x = memnew(SpinBox); diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h index 26471df051..1f015a1804 100644 --- a/editor/plugins/animation_blend_space_2d_editor.h +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -40,10 +40,13 @@ #include "scene/gui/separator.h" #include "scene/gui/tree.h" +class EditorUndoRedoManager; + class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { GDCLASS(AnimationNodeBlendSpace2DEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendSpace2D> blend_space; + bool read_only = false; PanelContainer *panel = nullptr; Button *tool_blend = nullptr; @@ -82,7 +85,7 @@ class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { bool updating; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; static AnimationNodeBlendSpace2DEditor *singleton; diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 79be2d04b3..11054ee11e 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -39,6 +39,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" @@ -133,6 +134,8 @@ void AnimationNodeBlendTreeEditor::_update_graph() { GraphNode *node = memnew(GraphNode); graph->add_child(node); + node->set_draggable(!read_only); + Ref<AnimationNode> agnode = blend_tree->get_node(E); ERR_CONTINUE(!agnode.is_valid()); @@ -145,9 +148,10 @@ void AnimationNodeBlendTreeEditor::_update_graph() { if (String(E) != "output") { LineEdit *name = memnew(LineEdit); name->set_text(E); + name->set_editable(!read_only); name->set_expand_to_text_length_enabled(true); node->add_child(name); - node->set_slot(0, false, 0, Color(), true, 0, get_theme_color(SNAME("font_color"), SNAME("Label"))); + node->set_slot(0, false, 0, Color(), true, read_only ? -1 : 0, get_theme_color(SNAME("font_color"), SNAME("Label"))); name->connect("text_submitted", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed).bind(agnode), CONNECT_DEFERRED); name->connect("focus_exited", callable_mp(this, &AnimationNodeBlendTreeEditor::_node_renamed_focus_out).bind(name, agnode), CONNECT_DEFERRED); base = 1; @@ -159,7 +163,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { Label *in_name = memnew(Label); node->add_child(in_name); in_name->set_text(agnode->get_input_name(i)); - node->set_slot(base + i, true, 0, get_theme_color(SNAME("font_color"), SNAME("Label")), false, 0, Color()); + node->set_slot(base + i, true, read_only ? -1 : 0, get_theme_color(SNAME("font_color"), SNAME("Label")), false, 0, Color()); } List<PropertyInfo> pinfo; @@ -171,6 +175,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { String base_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E) + "/" + F.name; EditorProperty *prop = EditorInspector::instantiate_property_editor(AnimationTreeEditor::get_singleton()->get_tree(), F.type, base_path, F.hint, F.hint_string, F.usage); if (prop) { + prop->set_read_only(read_only); prop->set_object_and_property(AnimationTreeEditor::get_singleton()->get_tree(), base_path); prop->update_property(); prop->set_name_split_ratio(0); @@ -194,12 +199,16 @@ void AnimationNodeBlendTreeEditor::_update_graph() { if (agnode->has_filter()) { node->add_child(memnew(HSeparator)); - Button *edit_filters = memnew(Button); - edit_filters->set_text(TTR("Edit Filters")); - edit_filters->set_icon(get_theme_icon(SNAME("AnimationFilter"), SNAME("EditorIcons"))); - node->add_child(edit_filters); - edit_filters->connect("pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_edit_filters).bind(E), CONNECT_DEFERRED); - edit_filters->set_h_size_flags(SIZE_SHRINK_CENTER); + Button *inspect_filters = memnew(Button); + if (read_only) { + inspect_filters->set_text(TTR("Inspect Filters")); + } else { + inspect_filters->set_text(TTR("Edit Filters")); + } + inspect_filters->set_icon(get_theme_icon(SNAME("AnimationFilter"), SNAME("EditorIcons"))); + node->add_child(inspect_filters); + inspect_filters->connect("pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_inspect_filters).bind(E), CONNECT_DEFERRED); + inspect_filters->set_h_size_flags(SIZE_SHRINK_CENTER); } Ref<AnimationNodeAnimation> anim = agnode; @@ -207,6 +216,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { MenuButton *mb = memnew(MenuButton); mb->set_text(anim->get_animation()); mb->set_icon(get_theme_icon(SNAME("Animation"), SNAME("EditorIcons"))); + mb->set_disabled(read_only); Array options; node->add_child(memnew(HSeparator)); @@ -369,10 +379,18 @@ void AnimationNodeBlendTreeEditor::_popup(bool p_has_input_ports, const Vector2 } void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) { + if (read_only) { + return; + } + _popup(false, graph->get_screen_position() + graph->get_local_mouse_position(), p_position); } void AnimationNodeBlendTreeEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) { + if (read_only) { + return; + } + Ref<AnimationNode> node = blend_tree->get_node(p_from); if (node.is_valid()) { from_node = p_from; @@ -381,6 +399,10 @@ void AnimationNodeBlendTreeEditor::_connection_to_empty(const String &p_from, in } void AnimationNodeBlendTreeEditor::_connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position) { + if (read_only) { + return; + } + Ref<AnimationNode> node = blend_tree->get_node(p_to); if (node.is_valid()) { to_node = p_to; @@ -401,6 +423,10 @@ void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Ve } void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) { + if (read_only) { + return; + } + AnimationNodeBlendTree::ConnectionError err = blend_tree->can_connect_node(p_to, p_to_index, p_from); if (err != AnimationNodeBlendTree::CONNECTION_OK) { @@ -417,6 +443,10 @@ void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int } void AnimationNodeBlendTreeEditor::_disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) { + if (read_only) { + return; + } + graph->disconnect_node(p_from, p_from_index, p_to, p_to_index); updating = true; @@ -444,6 +474,10 @@ void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options, } void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { + if (read_only) { + return; + } + undo_redo->create_action(TTR("Delete Node")); undo_redo->add_do_method(blend_tree.ptr(), "remove_node", p_which); undo_redo->add_undo_method(blend_tree.ptr(), "add_node", p_which, blend_tree->get_node(p_which), blend_tree.ptr()->get_node_position(p_which)); @@ -463,6 +497,10 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { } void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<StringName> &p_nodes) { + if (read_only) { + return; + } + List<StringName> to_erase; if (p_nodes.is_empty()) { @@ -494,6 +532,10 @@ void AnimationNodeBlendTreeEditor::_delete_nodes_request(const TypedArray<String } void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) { + if (read_only) { + return; + } + GraphNode *gn = Object::cast_to<GraphNode>(p_node); ERR_FAIL_COND(!gn); @@ -678,7 +720,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano } } - ti->set_editable(0, true); + ti->set_editable(0, !read_only); ti->set_selectable(0, true); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, concat); @@ -691,7 +733,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano ti = filters->create_item(ti); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, concat); - ti->set_editable(0, true); + ti->set_editable(0, !read_only); ti->set_selectable(0, true); ti->set_checked(0, anode->is_path_filtered(path)); ti->set_metadata(0, path); @@ -713,7 +755,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano ti = filters->create_item(ti); ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); ti->set_text(0, types_text); - ti->set_editable(0, true); + ti->set_editable(0, !read_only); ti->set_selectable(0, true); ti->set_checked(0, anode->is_path_filtered(path)); ti->set_metadata(0, path); @@ -726,7 +768,15 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano return true; } -void AnimationNodeBlendTreeEditor::_edit_filters(const String &p_which) { +void AnimationNodeBlendTreeEditor::_inspect_filters(const String &p_which) { + if (read_only) { + filter_dialog->set_title(TTR("Inspect Filtered Tracks:")); + } else { + filter_dialog->set_title(TTR("Edit Filtered Tracks:")); + } + + filter_enabled->set_disabled(read_only); + Ref<AnimationNode> anode = blend_tree->get_node(p_which); ERR_FAIL_COND(!anode.is_valid()); @@ -749,16 +799,10 @@ void AnimationNodeBlendTreeEditor::_update_editor_settings() { graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning"))); } -void AnimationNodeBlendTreeEditor::_update_theme() { - error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); - error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); -} - void AnimationNodeBlendTreeEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { _update_editor_settings(); - _update_theme(); } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { @@ -766,7 +810,8 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - _update_theme(); + error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); if (is_visible_in_tree()) { _update_graph(); @@ -837,6 +882,10 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { } void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) { + if (read_only) { + return; + } + if (updating) { return; } @@ -943,13 +992,20 @@ void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { blend_tree = p_node; + read_only = false; + if (blend_tree.is_null()) { hide(); } else { + read_only = EditorNode::get_singleton()->is_resource_read_only(blend_tree); + blend_tree->connect("removed_from_graph", callable_mp(this, &AnimationNodeBlendTreeEditor::_removed_from_graph)); _update_graph(); } + + add_node->set_disabled(read_only); + graph->set_arrange_nodes_button_hidden(read_only); } AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { @@ -985,6 +1041,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { graph->get_zoom_hbox()->move_child(add_node, 0); add_node->get_popup()->connect("id_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_add_node)); add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu).bind(false)); + add_node->set_disabled(read_only); add_options.push_back(AddOption("Animation", "AnimationNodeAnimation")); add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot", 2)); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index 18199676b8..cdbf2975f2 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -41,11 +41,15 @@ class ProgressBar; class EditorFileDialog; +class EditorUndoRedoManager; class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendTree> blend_tree; + + bool read_only = false; + GraphEdit *graph = nullptr; MenuButton *add_node = nullptr; Vector2 position_from_popup_menu; @@ -54,7 +58,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { PanelContainer *error_panel = nullptr; Label *error_label = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; AcceptDialog *filter_dialog = nullptr; Tree *filters = nullptr; @@ -105,7 +109,7 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _delete_nodes_request(const TypedArray<StringName> &p_nodes); bool _update_filters(const Ref<AnimationNode> &anode); - void _edit_filters(const String &p_which); + void _inspect_filters(const String &p_which); void _filter_edited(); void _filter_toggled(); Ref<AnimationNode> _filter_edit; @@ -119,7 +123,6 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _removed_from_graph(); void _update_editor_settings(); - void _update_theme(); EditorFileDialog *open_file = nullptr; Ref<AnimationNode> file_loaded; diff --git a/editor/plugins/animation_library_editor.cpp b/editor/plugins/animation_library_editor.cpp index c36ae1c521..f9e5aa799a 100644 --- a/editor/plugins/animation_library_editor.cpp +++ b/editor/plugins/animation_library_editor.cpp @@ -32,6 +32,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" void AnimationLibraryEditor::set_animation_player(Object *p_player) { player = p_player; @@ -92,7 +93,7 @@ void AnimationLibraryEditor::_add_library_validate(const String &p_name) { void AnimationLibraryEditor::_add_library_confirm() { if (adding_animation) { String anim_name = add_library_name->get_text(); - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); Ref<AnimationLibrary> al = player->call("get_animation_library", adding_animation_to_library); ERR_FAIL_COND(!al.is_valid()); @@ -109,7 +110,7 @@ void AnimationLibraryEditor::_add_library_confirm() { } else { String lib_name = add_library_name->get_text(); - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); Ref<AnimationLibrary> al; al.instantiate(); @@ -203,7 +204,7 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) { // TODO: should probably make all foreign animations assigned to this library // unique too. - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Make Animation Library Unique: %s"), lib_name)); undo_redo->add_do_method(player, "remove_animation_library", lib_name); undo_redo->add_do_method(player, "add_animation_library", lib_name, ald); @@ -272,7 +273,7 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) { Ref<Animation> animd = anim->duplicate(); - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Make Animation Unique: %s"), anim_name)); undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name); undo_redo->add_do_method(al.ptr(), "add_animation", anim_name, animd); @@ -320,7 +321,7 @@ void AnimationLibraryEditor::_load_file(String p_path) { name = p_path.get_file().get_basename() + " " + itos(attempt); } - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Add Animation Library: %s"), name)); undo_redo->add_do_method(player, "add_animation_library", name, al); @@ -358,7 +359,7 @@ void AnimationLibraryEditor::_load_file(String p_path) { name = p_path.get_file().get_basename() + " " + itos(attempt); } - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Load Animation into Library: %s"), name)); undo_redo->add_do_method(al.ptr(), "add_animation", name, anim); @@ -374,7 +375,7 @@ void AnimationLibraryEditor::_load_file(String p_path) { EditorNode::get_singleton()->save_resource_in_path(al, p_path); if (al->get_path() != prev_path) { // Save successful. - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Save Animation library to File: %s"), file_dialog_library)); undo_redo->add_do_method(al.ptr(), "set_path", al->get_path()); @@ -395,7 +396,7 @@ void AnimationLibraryEditor::_load_file(String p_path) { String prev_path = anim->get_path(); EditorNode::get_singleton()->save_resource_in_path(anim, p_path); if (anim->get_path() != prev_path) { // Save successful. - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Save Animation to File: %s"), file_dialog_animation)); undo_redo->add_do_method(anim.ptr(), "set_path", anim->get_path()); @@ -413,7 +414,7 @@ void AnimationLibraryEditor::_item_renamed() { String text = ti->get_text(0); String old_text = ti->get_metadata(0); bool restore_text = false; - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); if (String(text).contains("/") || String(text).contains(":") || String(text).contains(",") || String(text).contains("[")) { restore_text = true; @@ -527,7 +528,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int name = base_name + " (" + itos(attempt) + ")"; } - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Add Animation to Library: %s"), name)); undo_redo->add_do_method(al.ptr(), "add_animation", name, anim); @@ -553,7 +554,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int file_dialog_library = lib_name; } break; case LIB_BUTTON_DELETE: { - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Remove Animation Library: %s"), lib_name)); undo_redo->add_do_method(player, "remove_animation_library", lib_name); undo_redo->add_undo_method(player, "add_animation_library", lib_name, al); @@ -594,7 +595,7 @@ void AnimationLibraryEditor::_button_pressed(TreeItem *p_item, int p_column, int } break; case ANIM_BUTTON_DELETE: { - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(vformat(TTR("Remove Animation from Library: %s"), anim_name)); undo_redo->add_do_method(al.ptr(), "remove_animation", anim_name); undo_redo->add_undo_method(al.ptr(), "add_animation", anim_name, anim); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index 516079673d..e47c5b1e4b 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -1045,6 +1045,10 @@ void AnimationPlayerEditor::_update_name_dialog_library_dropdown() { } } +void AnimationPlayerEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { if (player && pin->is_pressed()) { return; // Ignore, pinned. @@ -1642,28 +1646,28 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug play_bw_from = memnew(Button); play_bw_from->set_flat(true); - play_bw_from->set_tooltip(TTR("Play selected animation backwards from current pos. (A)")); + play_bw_from->set_tooltip_text(TTR("Play selected animation backwards from current pos. (A)")); hb->add_child(play_bw_from); play_bw = memnew(Button); play_bw->set_flat(true); - play_bw->set_tooltip(TTR("Play selected animation backwards from end. (Shift+A)")); + play_bw->set_tooltip_text(TTR("Play selected animation backwards from end. (Shift+A)")); hb->add_child(play_bw); stop = memnew(Button); stop->set_flat(true); stop->set_toggle_mode(true); hb->add_child(stop); - stop->set_tooltip(TTR("Stop animation playback. (S)")); + stop->set_tooltip_text(TTR("Stop animation playback. (S)")); play = memnew(Button); play->set_flat(true); - play->set_tooltip(TTR("Play selected animation from start. (Shift+D)")); + play->set_tooltip_text(TTR("Play selected animation from start. (Shift+D)")); hb->add_child(play); play_from = memnew(Button); play_from->set_flat(true); - play_from->set_tooltip(TTR("Play selected animation from current pos. (D)")); + play_from->set_tooltip_text(TTR("Play selected animation from current pos. (D)")); hb->add_child(play_from); frame = memnew(SpinBox); @@ -1671,7 +1675,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug frame->set_custom_minimum_size(Size2(80, 0) * EDSCALE); frame->set_stretch_ratio(2); frame->set_step(0.0001); - frame->set_tooltip(TTR("Animation position (in seconds).")); + frame->set_tooltip_text(TTR("Animation position (in seconds).")); hb->add_child(memnew(VSeparator)); @@ -1679,7 +1683,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug hb->add_child(scale); scale->set_h_size_flags(SIZE_EXPAND_FILL); scale->set_stretch_ratio(1); - scale->set_tooltip(TTR("Scale animation playback globally for the node.")); + scale->set_tooltip_text(TTR("Scale animation playback globally for the node.")); scale->hide(); delete_dialog = memnew(ConfirmationDialog); @@ -1689,7 +1693,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug tool_anim = memnew(MenuButton); tool_anim->set_shortcut_context(this); tool_anim->set_flat(false); - tool_anim->set_tooltip(TTR("Animation Tools")); + tool_anim->set_tooltip_text(TTR("Animation Tools")); tool_anim->set_text(TTR("Animation")); tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New")), TOOL_NEW_ANIM); tool_anim->get_popup()->add_separator(); @@ -1708,13 +1712,13 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug animation = memnew(OptionButton); hb->add_child(animation); animation->set_h_size_flags(SIZE_EXPAND_FILL); - animation->set_tooltip(TTR("Display list of animations in player.")); + animation->set_tooltip_text(TTR("Display list of animations in player.")); animation->set_clip_text(true); autoplay = memnew(Button); autoplay->set_flat(true); hb->add_child(autoplay); - autoplay->set_tooltip(TTR("Autoplay on Load")); + autoplay->set_tooltip_text(TTR("Autoplay on Load")); hb->add_child(memnew(VSeparator)); @@ -1727,12 +1731,12 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug onion_toggle = memnew(Button); onion_toggle->set_flat(true); onion_toggle->set_toggle_mode(true); - onion_toggle->set_tooltip(TTR("Enable Onion Skinning")); + onion_toggle->set_tooltip_text(TTR("Enable Onion Skinning")); onion_toggle->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_onion_skinning_menu).bind(ONION_SKINNING_ENABLE)); hb->add_child(onion_toggle); onion_skinning = memnew(MenuButton); - onion_skinning->set_tooltip(TTR("Onion Skinning Options")); + onion_skinning->set_tooltip_text(TTR("Onion Skinning Options")); onion_skinning->get_popup()->add_separator(TTR("Directions")); // TRANSLATORS: Opposite of "Future", refers to a direction in animation onion skinning. onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST); @@ -1755,7 +1759,7 @@ AnimationPlayerEditor::AnimationPlayerEditor(AnimationPlayerEditorPlugin *p_plug pin = memnew(Button); pin->set_flat(true); pin->set_toggle_mode(true); - pin->set_tooltip(TTR("Pin AnimationPlayer")); + pin->set_tooltip_text(TTR("Pin AnimationPlayer")); hb->add_child(pin); pin->connect("pressed", callable_mp(this, &AnimationPlayerEditor::_pin_pressed)); @@ -1925,7 +1929,7 @@ void AnimationPlayerEditorPlugin::_update_keying() { } void AnimationPlayerEditorPlugin::edit(Object *p_object) { - anim_editor->set_undo_redo(&get_undo_redo()); + anim_editor->set_undo_redo(get_undo_redo()); if (!p_object) { return; } diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 3b1de070fa..a37a9debef 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -41,6 +41,7 @@ #include "scene/gui/texture_button.h" #include "scene/gui/tree.h" +class EditorUndoRedoManager; class AnimationPlayerEditorPlugin; class AnimationPlayerEditor : public VBoxContainer { @@ -100,7 +101,7 @@ class AnimationPlayerEditor : public VBoxContainer { LineEdit *name = nullptr; OptionButton *library = nullptr; Label *name_title = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; Ref<Texture2D> autoplay_icon; Ref<Texture2D> reset_icon; @@ -233,7 +234,7 @@ public: void ensure_visibility(); - void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void edit(AnimationPlayer *p_player); void forward_force_draw_over_viewport(Control *p_overlay); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 473450b292..07d132229d 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -39,6 +39,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/animation/animation_blend_tree.h" #include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" @@ -55,7 +56,11 @@ bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) { state_machine = p_node; + read_only = false; + if (state_machine.is_valid()) { + read_only = EditorNode::get_singleton()->is_resource_read_only(state_machine); + selected_transition_from = StringName(); selected_transition_to = StringName(); selected_transition_index = -1; @@ -65,6 +70,9 @@ void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) { _update_mode(); _update_graph(); } + + tool_create->set_disabled(read_only); + tool_connect->set_disabled(read_only); } void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) { @@ -76,7 +84,9 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<InputEventKey> k = p_event; if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) { if (selected_node != StringName() || !selected_nodes.is_empty() || selected_transition_to != StringName() || selected_transition_from != StringName()) { - _erase_selected(); + if (!read_only) { + _erase_selected(); + } accept_event(); } } @@ -94,9 +104,11 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<InputEventMouseButton> mb = p_event; // Add new node - if (mb.is_valid() && mb->is_pressed() && !box_selecting && !connecting && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (tool_create->is_pressed() && mb->get_button_index() == MouseButton::LEFT))) { - connecting_from = StringName(); - _open_menu(mb->get_position()); + if (!read_only) { + if (mb.is_valid() && mb->is_pressed() && !box_selecting && !connecting && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (tool_create->is_pressed() && mb->get_button_index() == MouseButton::LEFT))) { + connecting_from = StringName(); + _open_menu(mb->get_position()); + } } // Select node or push a field inside @@ -120,22 +132,24 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv return; } - if (node_rects[i].name.has_point(mb->get_position()) && state_machine->can_edit_node(node_rects[i].node_name)) { // edit name - Ref<StyleBox> line_sb = get_theme_stylebox(SNAME("normal"), SNAME("LineEdit")); + if (!read_only) { + if (node_rects[i].name.has_point(mb->get_position()) && state_machine->can_edit_node(node_rects[i].node_name)) { // edit name + Ref<StyleBox> line_sb = get_theme_stylebox(SNAME("normal"), SNAME("LineEdit")); - Rect2 edit_rect = node_rects[i].name; - edit_rect.position -= line_sb->get_offset(); - edit_rect.size += line_sb->get_minimum_size(); + Rect2 edit_rect = node_rects[i].name; + edit_rect.position -= line_sb->get_offset(); + edit_rect.size += line_sb->get_minimum_size(); - name_edit_popup->set_position(state_machine_draw->get_screen_position() + edit_rect.position); - name_edit_popup->set_size(edit_rect.size); - name_edit->set_text(node_rects[i].node_name); - name_edit_popup->popup(); - name_edit->grab_focus(); - name_edit->select_all(); + name_edit_popup->set_position(state_machine_draw->get_screen_position() + edit_rect.position); + name_edit_popup->set_size(edit_rect.size); + name_edit->set_text(node_rects[i].node_name); + name_edit_popup->popup(); + name_edit->grab_focus(); + name_edit->select_all(); - prev_name = node_rects[i].node_name; - return; + prev_name = node_rects[i].node_name; + return; + } } if (node_rects[i].edit.has_point(mb->get_position())) { //edit name @@ -318,7 +332,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } // Move mouse while connecting - if (mm.is_valid() && connecting) { + if (mm.is_valid() && connecting && !read_only) { connecting_to = mm->get_position(); connecting_to_node = StringName(); state_machine_draw->update(); @@ -332,7 +346,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } // Move mouse while moving a node - if (mm.is_valid() && dragging_selected_attempt) { + if (mm.is_valid() && dragging_selected_attempt && !read_only) { dragging_selected = true; drag_ofs = mm->get_position() - drag_from; snap_x = StringName(); @@ -462,9 +476,9 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv to = String(transition_lines[closest].multi_transitions[i].to_node); tooltip += "\n" + from + " -> " + to; } - state_machine_draw->set_tooltip(tooltip); + state_machine_draw->set_tooltip_text(tooltip); } else { - state_machine_draw->set_tooltip(""); + state_machine_draw->set_tooltip_text(""); } } } @@ -477,17 +491,21 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } Control::CursorShape AnimationNodeStateMachineEditor::get_cursor_shape(const Point2 &p_pos) const { - // Put ibeam (text cursor) over names to make it clearer that they are editable. - Transform2D xform = panel->get_transform() * state_machine_draw->get_transform(); - Point2 pos = xform.xform_inv(p_pos); Control::CursorShape cursor_shape = get_default_cursor_shape(); - - for (int i = node_rects.size() - 1; i >= 0; i--) { // Inverse to draw order. - if (node_rects[i].node.has_point(pos)) { - if (node_rects[i].name.has_point(pos)) { - cursor_shape = Control::CURSOR_IBEAM; + if (!read_only) { + // Put ibeam (text cursor) over names to make it clearer that they are editable. + Transform2D xform = panel->get_transform() * state_machine_draw->get_transform(); + Point2 pos = xform.xform_inv(p_pos); + + for (int i = node_rects.size() - 1; i >= 0; i--) { // Inverse to draw order. + if (node_rects[i].node.has_point(pos)) { + if (node_rects[i].name.has_point(pos)) { + if (state_machine->can_edit_node(node_rects[i].node_name)) { + cursor_shape = Control::CURSOR_IBEAM; + } + } + break; } - break; } } return cursor_shape; @@ -1526,10 +1544,9 @@ void AnimationNodeStateMachineEditor::_update_graph() { void AnimationNodeStateMachineEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: - case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: - case NOTIFICATION_TRANSLATION_CHANGED: { + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_THEME_CHANGED: { error_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); error_label->add_theme_color_override("font_color", get_theme_color(SNAME("error_color"), SNAME("Editor"))); panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); @@ -1847,9 +1864,9 @@ void AnimationNodeStateMachineEditor::_update_mode() { tool_erase_hb->show(); bool nothing_selected = selected_nodes.is_empty() && selected_transition_from == StringName() && selected_transition_to == StringName(); bool start_end_selected = selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node); - tool_erase->set_disabled(nothing_selected || start_end_selected); + tool_erase->set_disabled(nothing_selected || start_end_selected || read_only); - if (selected_nodes.is_empty() || start_end_selected) { + if (selected_nodes.is_empty() || start_end_selected || read_only) { tool_group->set_disabled(true); tool_group->set_visible(true); tool_ungroup->set_visible(false); @@ -1899,7 +1916,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { tool_select->set_toggle_mode(true); tool_select->set_button_group(bg); tool_select->set_pressed(true); - tool_select->set_tooltip(TTR("Select and move nodes.\nRMB: Add node at position clicked.\nShift+LMB+Drag: Connects the selected node with another node or creates a new node if you select an area without nodes.")); + tool_select->set_tooltip_text(TTR("Select and move nodes.\nRMB: Add node at position clicked.\nShift+LMB+Drag: Connects the selected node with another node or creates a new node if you select an area without nodes.")); tool_select->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_update_mode), CONNECT_DEFERRED); tool_create = memnew(Button); @@ -1907,7 +1924,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { top_hb->add_child(tool_create); tool_create->set_toggle_mode(true); tool_create->set_button_group(bg); - tool_create->set_tooltip(TTR("Create new nodes.")); + tool_create->set_tooltip_text(TTR("Create new nodes.")); tool_create->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_update_mode), CONNECT_DEFERRED); tool_connect = memnew(Button); @@ -1915,7 +1932,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { top_hb->add_child(tool_connect); tool_connect->set_toggle_mode(true); tool_connect->set_button_group(bg); - tool_connect->set_tooltip(TTR("Connect nodes.")); + tool_connect->set_tooltip_text(TTR("Connect nodes.")); tool_connect->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_update_mode), CONNECT_DEFERRED); tool_erase_hb = memnew(HBoxContainer); @@ -1924,21 +1941,21 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { tool_group = memnew(Button); tool_group->set_flat(true); - tool_group->set_tooltip(TTR("Group Selected Node(s)") + " (Ctrl+G)"); + tool_group->set_tooltip_text(TTR("Group Selected Node(s)") + " (Ctrl+G)"); tool_group->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_group_selected_nodes)); tool_group->set_disabled(true); tool_erase_hb->add_child(tool_group); tool_ungroup = memnew(Button); tool_ungroup->set_flat(true); - tool_ungroup->set_tooltip(TTR("Ungroup Selected Node") + " (Ctrl+Shift+G)"); + tool_ungroup->set_tooltip_text(TTR("Ungroup Selected Node") + " (Ctrl+Shift+G)"); tool_ungroup->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_ungroup_selected_nodes)); tool_ungroup->set_visible(false); tool_erase_hb->add_child(tool_ungroup); tool_erase = memnew(Button); tool_erase->set_flat(true); - tool_erase->set_tooltip(TTR("Remove selected node or transition.")); + tool_erase->set_tooltip_text(TTR("Remove selected node or transition.")); tool_erase->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_erase_selected).bind(false)); tool_erase->set_disabled(true); tool_erase_hb->add_child(tool_erase); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 165940e639..3a59e94a5f 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -40,12 +40,15 @@ #include "scene/gui/tree.h" class EditorFileDialog; +class EditorUndoRedoManager; class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { GDCLASS(AnimationNodeStateMachineEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeStateMachine> state_machine; + bool read_only = false; + Button *tool_select = nullptr; Button *tool_create = nullptr; Button *tool_connect = nullptr; @@ -76,7 +79,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { bool updating = false; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; static AnimationNodeStateMachineEditor *singleton; diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 8ee162d085..3c12d17c57 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -193,7 +193,6 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const void EditorAssetLibraryItemDescription::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { previews_bg->add_theme_style_override("panel", previews->get_theme_stylebox(SNAME("normal"), SNAME("TextEdit"))); } break; @@ -401,7 +400,6 @@ void EditorAssetLibraryItemDownload::configure(const String &p_title, int p_asse void EditorAssetLibraryItemDownload::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("AssetLib"))); status->add_theme_color_override("font_color", get_theme_color(SNAME("status_color"), SNAME("AssetLib"))); @@ -580,7 +578,6 @@ void EditorAssetLibrary::_notification(int p_what) { error_label->raise(); } break; - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { error_tr->set_texture(get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); filter->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); diff --git a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp index 9e551ae0ed..d670197c53 100644 --- a/editor/plugins/audio_stream_randomizer_editor_plugin.cpp +++ b/editor/plugins/audio_stream_randomizer_editor_plugin.cpp @@ -31,6 +31,7 @@ #include "audio_stream_randomizer_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" void AudioStreamRandomizerEditorPlugin::edit(Object *p_object) { } @@ -43,8 +44,8 @@ void AudioStreamRandomizerEditorPlugin::make_visible(bool p_visible) { } void AudioStreamRandomizerEditorPlugin::_move_stream_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) { - UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo); - ERR_FAIL_COND(!undo_redo); + Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo); + ERR_FAIL_COND(undo_redo.is_null()); AudioStreamRandomizer *randomizer = Object::cast_to<AudioStreamRandomizer>(p_edited); if (!randomizer) { diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index 70775c1ee2..c16dca00a3 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -47,6 +47,9 @@ void BoneMapperButton::fetch_textures() { set_offset(SIDE_TOP, 0); set_offset(SIDE_BOTTOM, 0); + // Hack to avoid handle color darkening... + set_modulate(EditorSettings::get_singleton()->is_dark_theme() ? Color(1, 1, 1) : Color(4.25, 4.25, 4.25)); + circle = memnew(TextureRect); circle->set_texture(get_theme_icon(SNAME("BoneMapperHandleCircle"), SNAME("EditorIcons"))); add_child(circle); @@ -98,14 +101,24 @@ BoneMapperButton::~BoneMapperButton() { } void BoneMapperItem::create_editor() { - skeleton_bone_selector = memnew(EditorPropertyTextEnum); - skeleton_bone_selector->setup(skeleton_bone_names, false, true); + HBoxContainer *hbox = memnew(HBoxContainer); + add_child(hbox); + + skeleton_bone_selector = memnew(EditorPropertyText); skeleton_bone_selector->set_label(profile_bone_name); skeleton_bone_selector->set_selectable(false); + skeleton_bone_selector->set_h_size_flags(SIZE_EXPAND_FILL); skeleton_bone_selector->set_object_and_property(bone_map.ptr(), "bone_map/" + String(profile_bone_name)); skeleton_bone_selector->update_property(); skeleton_bone_selector->connect("property_changed", callable_mp(this, &BoneMapperItem::_value_changed)); - add_child(skeleton_bone_selector); + hbox->add_child(skeleton_bone_selector); + + picker_button = memnew(Button); + picker_button->set_icon(get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons"))); + picker_button->connect("pressed", callable_mp(this, &BoneMapperItem::_open_picker)); + hbox->add_child(picker_button); + + add_child(memnew(HSeparator)); } void BoneMapperItem::_update_property() { @@ -114,6 +127,10 @@ void BoneMapperItem::_update_property() { } } +void BoneMapperItem::_open_picker() { + emit_signal(SNAME("pick"), profile_bone_name); +} + void BoneMapperItem::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { bone_map->set(p_property, p_value); } @@ -133,25 +150,153 @@ void BoneMapperItem::_notification(int p_what) { } void BoneMapperItem::_bind_methods() { + ADD_SIGNAL(MethodInfo("pick", PropertyInfo(Variant::STRING_NAME, "profile_bone_name"))); } -BoneMapperItem::BoneMapperItem(Ref<BoneMap> &p_bone_map, PackedStringArray p_skeleton_bone_names, const StringName &p_profile_bone_name) { +BoneMapperItem::BoneMapperItem(Ref<BoneMap> &p_bone_map, const StringName &p_profile_bone_name) { bone_map = p_bone_map; - skeleton_bone_names = p_skeleton_bone_names; profile_bone_name = p_profile_bone_name; } BoneMapperItem::~BoneMapperItem() { } +void BonePicker::create_editors() { + set_title(TTR("Bone Picker:")); + + VBoxContainer *vbox = memnew(VBoxContainer); + add_child(vbox); + + bones = memnew(Tree); + bones->set_select_mode(Tree::SELECT_SINGLE); + bones->set_v_size_flags(Control::SIZE_EXPAND_FILL); + bones->set_hide_root(true); + bones->connect("item_activated", callable_mp(this, &BonePicker::_confirm)); + vbox->add_child(bones); + + create_bones_tree(skeleton); +} + +void BonePicker::create_bones_tree(Skeleton3D *p_skeleton) { + bones->clear(); + + if (!p_skeleton) { + return; + } + + TreeItem *root = bones->create_item(); + + HashMap<int, TreeItem *> items; + + items.insert(-1, root); + + Ref<Texture> bone_icon = get_theme_icon(SNAME("BoneAttachment3D"), SNAME("EditorIcons")); + + Vector<int> bones_to_process = p_skeleton->get_parentless_bones(); + bool is_first = true; + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + bones_to_process.erase(current_bone_idx); + + Vector<int> current_bone_child_bones = p_skeleton->get_bone_children(current_bone_idx); + int child_bone_size = current_bone_child_bones.size(); + for (int i = 0; i < child_bone_size; i++) { + bones_to_process.push_back(current_bone_child_bones[i]); + } + + const int parent_idx = p_skeleton->get_bone_parent(current_bone_idx); + TreeItem *parent_item = items.find(parent_idx)->value; + + TreeItem *joint_item = bones->create_item(parent_item); + items.insert(current_bone_idx, joint_item); + + joint_item->set_text(0, p_skeleton->get_bone_name(current_bone_idx)); + joint_item->set_icon(0, bone_icon); + joint_item->set_selectable(0, true); + joint_item->set_metadata(0, "bones/" + itos(current_bone_idx)); + if (is_first) { + is_first = false; + } else { + joint_item->set_collapsed(true); + } + } +} + +void BonePicker::_confirm() { + _ok_pressed(); +} + +void BonePicker::popup_bones_tree(const Size2i &p_minsize) { + popup_centered(p_minsize); +} + +bool BonePicker::has_selected_bone() { + TreeItem *selected = bones->get_selected(); + if (!selected) { + return false; + } + return true; +} + +StringName BonePicker::get_selected_bone() { + TreeItem *selected = bones->get_selected(); + if (!selected) { + return StringName(); + } + return selected->get_text(0); +} + +void BonePicker::_bind_methods() { +} + +void BonePicker::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + create_editors(); + } break; + } +} + +BonePicker::BonePicker(Skeleton3D *p_skeleton) { + skeleton = p_skeleton; +} + +BonePicker::~BonePicker() { +} + void BoneMapper::create_editor() { + // Create Bone picker. + picker = memnew(BonePicker(skeleton)); + picker->connect("confirmed", callable_mp(this, &BoneMapper::_apply_picker_selection)); + add_child(picker, false, INTERNAL_MODE_FRONT); + + profile_selector = memnew(EditorPropertyResource); + profile_selector->setup(bone_map.ptr(), "profile", "SkeletonProfile"); + profile_selector->set_label("Profile"); + profile_selector->set_selectable(false); + profile_selector->set_object_and_property(bone_map.ptr(), "profile"); + profile_selector->update_property(); + profile_selector->connect("property_changed", callable_mp(this, &BoneMapper::_profile_changed)); + add_child(profile_selector); + add_child(memnew(HSeparator)); + + HBoxContainer *group_hbox = memnew(HBoxContainer); + add_child(group_hbox); + profile_group_selector = memnew(EditorPropertyEnum); profile_group_selector->set_label("Group"); profile_group_selector->set_selectable(false); + profile_group_selector->set_h_size_flags(SIZE_EXPAND_FILL); profile_group_selector->set_object_and_property(this, "current_group_idx"); profile_group_selector->update_property(); profile_group_selector->connect("property_changed", callable_mp(this, &BoneMapper::_value_changed)); - add_child(profile_group_selector); + group_hbox->add_child(profile_group_selector); + + clear_mapping_button = memnew(Button); + clear_mapping_button->set_icon(get_theme_icon(SNAME("Clear"), SNAME("EditorIcons"))); + clear_mapping_button->set_tooltip_text(TTR("Clear mappings in current group.")); + clear_mapping_button->connect("pressed", callable_mp(this, &BoneMapper::_clear_mapping_current_group)); + group_hbox->add_child(clear_mapping_button); bone_mapper_field = memnew(AspectRatioContainer); bone_mapper_field->set_stretch_mode(AspectRatioContainer::STRETCH_FIT); @@ -175,9 +320,6 @@ void BoneMapper::create_editor() { mapper_item_vbox = memnew(VBoxContainer); add_child(mapper_item_vbox); - separator = memnew(HSeparator); - add_child(separator); - recreate_items(); } @@ -201,6 +343,18 @@ void BoneMapper::update_group_idx() { } } +void BoneMapper::_pick_bone(const StringName &p_bone_name) { + picker_key_name = p_bone_name; + picker->popup_bones_tree(Size2(500, 500) * EDSCALE); +} + +void BoneMapper::_apply_picker_selection() { + if (!picker->has_selected_bone()) { + return; + } + bone_map->set_skeleton_bone_name(picker_key_name, picker->get_selected_bone()); +} + void BoneMapper::set_current_group_idx(int p_group_idx) { current_group_idx = p_group_idx; recreate_editor(); @@ -282,6 +436,7 @@ void BoneMapper::clear_items() { // Clear items. int len = bone_mapper_items.size(); for (int i = 0; i < len; i++) { + bone_mapper_items[i]->disconnect("pick", callable_mp(this, &BoneMapper::_pick_bone)); mapper_item_vbox->remove_child(bone_mapper_items[i]); memdelete(bone_mapper_items[i]); } @@ -293,16 +448,11 @@ void BoneMapper::recreate_items() { // Create items by profile. Ref<SkeletonProfile> profile = bone_map->get_profile(); if (profile.is_valid()) { - PackedStringArray skeleton_bone_names; - int len = skeleton->get_bone_count(); - for (int i = 0; i < len; i++) { - skeleton_bone_names.push_back(skeleton->get_bone_name(i)); - } - - len = profile->get_bone_size(); + int len = profile->get_bone_size(); for (int i = 0; i < len; i++) { StringName bn = profile->get_bone_name(i); - bone_mapper_items.append(memnew(BoneMapperItem(bone_map, skeleton_bone_names, bn))); + bone_mapper_items.append(memnew(BoneMapperItem(bone_map, bn))); + bone_mapper_items[i]->connect("pick", callable_mp(this, &BoneMapper::_pick_bone), CONNECT_DEFERRED); mapper_item_vbox->add_child(bone_mapper_items[i]); } } @@ -363,11 +513,754 @@ void BoneMapper::_update_state() { } } +void BoneMapper::_clear_mapping_current_group() { + if (bone_map.is_valid()) { + Ref<SkeletonProfile> profile = bone_map->get_profile(); + if (profile.is_valid() && profile->get_group_size() > 0) { + int len = profile->get_bone_size(); + for (int i = 0; i < len; i++) { + if (profile->get_group(i) == profile->get_group_name(current_group_idx)) { + bone_map->_set_skeleton_bone_name(profile->get_bone_name(i), StringName()); + } + } + recreate_items(); + } + } +} + +#ifdef MODULE_REGEX_ENABLED +int BoneMapper::search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_picklist, BoneSegregation p_segregation, int p_parent, int p_child, int p_children_count) { + // There may be multiple candidates hit by existing the subsidiary bone. + // The one with the shortest name is probably the original. + LocalVector<String> hit_list; + String shortest = ""; + + for (int word_idx = 0; word_idx < p_picklist.size(); word_idx++) { + RegEx re = RegEx(p_picklist[word_idx]); + if (p_child == -1) { + Vector<int> bones_to_process = p_parent == -1 ? p_skeleton->get_parentless_bones() : p_skeleton->get_bone_children(p_parent); + while (bones_to_process.size() > 0) { + int idx = bones_to_process[0]; + bones_to_process.erase(idx); + Vector<int> children = p_skeleton->get_bone_children(idx); + for (int i = 0; i < children.size(); i++) { + bones_to_process.push_back(children[i]); + } + + if (p_children_count == 0 && children.size() > 0) { + continue; + } + if (p_children_count > 0 && children.size() < p_children_count) { + continue; + } + + String bn = skeleton->get_bone_name(idx); + if (!re.search(bn.to_lower()).is_null() && guess_bone_segregation(bn) == p_segregation) { + hit_list.push_back(bn); + } + } + + if (hit_list.size() > 0) { + shortest = hit_list[0]; + for (uint32_t i = 0; i < hit_list.size(); i++) { + if (hit_list[i].length() < shortest.length()) { + shortest = hit_list[i]; // Prioritize parent. + } + } + } + } else { + int idx = skeleton->get_bone_parent(p_child); + while (idx != p_parent && idx >= 0) { + Vector<int> children = p_skeleton->get_bone_children(idx); + if (p_children_count == 0 && children.size() > 0) { + continue; + } + if (p_children_count > 0 && children.size() < p_children_count) { + continue; + } + + String bn = skeleton->get_bone_name(idx); + if (!re.search(bn.to_lower()).is_null() && guess_bone_segregation(bn) == p_segregation) { + hit_list.push_back(bn); + } + idx = skeleton->get_bone_parent(idx); + } + + if (hit_list.size() > 0) { + shortest = hit_list[0]; + for (uint32_t i = 0; i < hit_list.size(); i++) { + if (hit_list[i].length() <= shortest.length()) { + shortest = hit_list[i]; // Prioritize parent. + } + } + } + } + + if (shortest != "") { + break; + } + } + + if (shortest == "") { + return -1; + } + + return skeleton->find_bone(shortest); +} + +BoneMapper::BoneSegregation BoneMapper::guess_bone_segregation(String p_bone_name) { + String fixed_bn = p_bone_name.camelcase_to_underscore().to_lower(); + + LocalVector<String> left_words; + left_words.push_back("(?<![a-zA-Z])left"); + left_words.push_back("(?<![a-zA-Z0-9])l(?![a-zA-Z0-9])"); + + LocalVector<String> right_words; + right_words.push_back("(?<![a-zA-Z])right"); + right_words.push_back("(?<![a-zA-Z0-9])r(?![a-zA-Z0-9])"); + + for (uint32_t i = 0; i < left_words.size(); i++) { + RegEx re_l = RegEx(left_words[i]); + if (!re_l.search(fixed_bn).is_null()) { + return BONE_SEGREGATION_LEFT; + } + RegEx re_r = RegEx(right_words[i]); + if (!re_r.search(fixed_bn).is_null()) { + return BONE_SEGREGATION_RIGHT; + } + } + + return BONE_SEGREGATION_NONE; +} + +void BoneMapper::_run_auto_mapping() { + auto_mapping_process(bone_map); + recreate_items(); +} + +void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) { + WARN_PRINT("Run auto mapping."); + + int bone_idx = -1; + Vector<String> picklist; // Use Vector<String> because match words have priority. + Vector<int> search_path; + + // 1. Guess Hips + picklist.push_back("hip"); + picklist.push_back("pelvis"); + picklist.push_back("waist"); + picklist.push_back("torso"); + int hips = search_bone_by_name(skeleton, picklist); + if (hips == -1) { + WARN_PRINT("Auto Mapping couldn't guess Hips. Abort auto mapping."); + return; // If there is no Hips, we cannot guess bone after then. + } else { + p_bone_map->_set_skeleton_bone_name("Hips", skeleton->get_bone_name(hips)); + } + picklist.clear(); + + // 2. Guess Root + bone_idx = skeleton->get_bone_parent(hips); + while (bone_idx >= 0) { + search_path.push_back(bone_idx); + bone_idx = skeleton->get_bone_parent(bone_idx); + } + if (search_path.size() == 0) { + bone_idx = -1; + } else if (search_path.size() == 1) { + bone_idx = search_path[0]; // It is only one bone which can be root. + } else { + bool found = false; + for (int i = 0; i < search_path.size(); i++) { + RegEx re = RegEx("root"); + if (!re.search(skeleton->get_bone_name(search_path[i]).to_lower()).is_null()) { + bone_idx = search_path[i]; // Name match is preferred. + found = true; + break; + } + } + if (!found) { + for (int i = 0; i < search_path.size(); i++) { + if (Vector3(0, 0, 0).is_equal_approx(skeleton->get_bone_global_rest(search_path[i]).origin)) { + bone_idx = search_path[i]; // The bone existing at the origin is appropriate as a root. + found = true; + break; + } + } + } + if (!found) { + bone_idx = search_path[search_path.size() - 1]; // Ambiguous, but most parental bone selected. + } + } + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess Root."); // Root is not required, so continue. + } else { + p_bone_map->_set_skeleton_bone_name("Root", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + search_path.clear(); + + // 3. Guess Neck + picklist.push_back("neck"); + picklist.push_back("head"); // For no neck model. + picklist.push_back("face"); // Same above. + int neck = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, hips); + picklist.clear(); + + // 4. Guess Head + picklist.push_back("head"); + picklist.push_back("face"); + int head = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck); + if (head == -1) { + search_path = skeleton->get_bone_children(neck); + if (search_path.size() == 1) { + head = search_path[0]; // Maybe only one child of the Neck is Head. + } + } + if (head == -1) { + if (neck != -1) { + head = neck; // The head animation should have more movement. + neck = -1; + p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head)); + } else { + WARN_PRINT("Auto Mapping couldn't guess Neck or Head."); // Continued for guessing on the other bones. But abort when guessing spines step. + } + } else { + p_bone_map->_set_skeleton_bone_name("Neck", skeleton->get_bone_name(neck)); + p_bone_map->_set_skeleton_bone_name("Head", skeleton->get_bone_name(head)); + } + picklist.clear(); + search_path.clear(); + + int neck_or_head = neck != -1 ? neck : (head != -1 ? head : -1); + if (neck_or_head != -1) { + // 4-1. Guess Eyes + picklist.push_back("eye(?!.*(brow|lash|lid))"); + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, neck_or_head); + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftEye."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftEye", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, neck_or_head); + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightEye."); + } else { + p_bone_map->_set_skeleton_bone_name("RightEye", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + picklist.clear(); + + // 4-2. Guess Jaw + picklist.push_back("jaw"); + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_NONE, neck_or_head); + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess Jaw."); + } else { + p_bone_map->_set_skeleton_bone_name("Jaw", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + picklist.clear(); + } + + // 5. Guess Foots + picklist.push_back("foot"); + picklist.push_back("ankle"); + int left_foot = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips); + if (left_foot == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftFoot."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftFoot", skeleton->get_bone_name(left_foot)); + } + int right_foot = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips); + if (right_foot == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightFoot."); + } else { + p_bone_map->_set_skeleton_bone_name("RightFoot", skeleton->get_bone_name(right_foot)); + } + picklist.clear(); + + // 5-1. Guess LowerLegs + picklist.push_back("(low|under).*leg"); + picklist.push_back("knee"); + picklist.push_back("shin"); + picklist.push_back("calf"); + picklist.push_back("leg"); + int left_lower_leg = -1; + if (left_foot != -1) { + left_lower_leg = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips, left_foot); + } + if (left_lower_leg == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftLowerLeg."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftLowerLeg", skeleton->get_bone_name(left_lower_leg)); + } + int right_lower_leg = -1; + if (right_foot != -1) { + right_lower_leg = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips, right_foot); + } + if (right_lower_leg == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightLowerLeg."); + } else { + p_bone_map->_set_skeleton_bone_name("RightLowerLeg", skeleton->get_bone_name(right_lower_leg)); + } + picklist.clear(); + + // 5-2. Guess UpperLegs + picklist.push_back("up.*leg"); + picklist.push_back("thigh"); + picklist.push_back("leg"); + if (left_lower_leg != -1) { + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips, left_lower_leg); + } + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftUpperLeg."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftUpperLeg", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + if (right_lower_leg != -1) { + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips, right_lower_leg); + } + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightUpperLeg."); + } else { + p_bone_map->_set_skeleton_bone_name("RightUpperLeg", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + picklist.clear(); + + // 5-3. Guess Toes + picklist.push_back("toe"); + picklist.push_back("ball"); + if (left_foot != -1) { + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, left_foot); + if (bone_idx == -1) { + search_path = skeleton->get_bone_children(left_foot); + if (search_path.size() == 1) { + bone_idx = search_path[0]; // Maybe only one child of the Foot is Toes. + } + search_path.clear(); + } + } + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftToes."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftToes", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + if (right_foot != -1) { + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, right_foot); + if (bone_idx == -1) { + search_path = skeleton->get_bone_children(right_foot); + if (search_path.size() == 1) { + bone_idx = search_path[0]; // Maybe only one child of the Foot is Toes. + } + search_path.clear(); + } + } + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightToes."); + } else { + p_bone_map->_set_skeleton_bone_name("RightToes", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + picklist.clear(); + + // 6. Guess Hands + picklist.push_back("hand"); + picklist.push_back("wrist"); + picklist.push_back("palm"); + picklist.push_back("fingers"); + int left_hand_or_palm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips, -1, 5); + if (left_hand_or_palm == -1) { + // Ambiguous, but try again for fewer finger models. + left_hand_or_palm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips); + } + int left_hand = left_hand_or_palm; // Check for the presence of a wrist, since bones with five children may be palmar. + while (left_hand != -1) { + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips, left_hand); + if (bone_idx == -1) { + break; + } + left_hand = bone_idx; + } + if (left_hand == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftHand."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftHand", skeleton->get_bone_name(left_hand)); + } + bone_idx = -1; + int right_hand_or_palm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips, -1, 5); + if (right_hand_or_palm == -1) { + // Ambiguous, but try again for fewer finger models. + right_hand_or_palm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips); + } + int right_hand = right_hand_or_palm; + while (right_hand != -1) { + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips, right_hand); + if (bone_idx == -1) { + break; + } + right_hand = bone_idx; + } + if (right_hand == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightHand."); + } else { + p_bone_map->_set_skeleton_bone_name("RightHand", skeleton->get_bone_name(right_hand)); + } + bone_idx = -1; + picklist.clear(); + + // 6-1. Guess Finger + bool named_finger_is_found = false; + LocalVector<String> fingers; + fingers.push_back("thumb|pollex"); + fingers.push_back("index|fore"); + fingers.push_back("middle"); + fingers.push_back("ring"); + fingers.push_back("little|pinkie|pinky"); + if (left_hand_or_palm != -1) { + LocalVector<LocalVector<String>> left_fingers_map; + left_fingers_map.resize(5); + left_fingers_map[0].push_back("LeftThumbMetacarpal"); + left_fingers_map[0].push_back("LeftThumbProximal"); + left_fingers_map[0].push_back("LeftThumbDistal"); + left_fingers_map[1].push_back("LeftIndexProximal"); + left_fingers_map[1].push_back("LeftIndexIntermediate"); + left_fingers_map[1].push_back("LeftIndexDistal"); + left_fingers_map[2].push_back("LeftMiddleProximal"); + left_fingers_map[2].push_back("LeftMiddleIntermediate"); + left_fingers_map[2].push_back("LeftMiddleDistal"); + left_fingers_map[3].push_back("LeftRingProximal"); + left_fingers_map[3].push_back("LeftRingIntermediate"); + left_fingers_map[3].push_back("LeftRingDistal"); + left_fingers_map[4].push_back("LeftLittleProximal"); + left_fingers_map[4].push_back("LeftLittleIntermediate"); + left_fingers_map[4].push_back("LeftLittleDistal"); + for (int i = 0; i < 5; i++) { + picklist.push_back(fingers[i]); + int finger = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, left_hand_or_palm, -1, 0); + if (finger != -1) { + while (finger != left_hand_or_palm && finger >= 0) { + search_path.push_back(finger); + finger = skeleton->get_bone_parent(finger); + } + search_path.reverse(); + if (search_path.size() == 1) { + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + named_finger_is_found = true; + } else if (search_path.size() == 2) { + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][1], skeleton->get_bone_name(search_path[1])); + named_finger_is_found = true; + } else if (search_path.size() >= 3) { + search_path = search_path.slice(-3); // Eliminate the possibility of carpal bone. + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][1], skeleton->get_bone_name(search_path[1])); + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][2], skeleton->get_bone_name(search_path[2])); + named_finger_is_found = true; + } + } + picklist.clear(); + search_path.clear(); + } + + // It is a bit corner case, but possibly the finger names are sequentially numbered... + if (!named_finger_is_found) { + picklist.push_back("finger"); + RegEx finger_re = RegEx("finger"); + search_path = skeleton->get_bone_children(left_hand_or_palm); + Vector<String> finger_names; + for (int i = 0; i < search_path.size(); i++) { + String bn = skeleton->get_bone_name(search_path[i]); + if (!finger_re.search(bn.to_lower()).is_null()) { + finger_names.push_back(bn); + } + } + finger_names.sort(); // Order by lexicographic, normal use cases never have more than 10 fingers in one hand. + search_path.clear(); + for (int i = 0; i < finger_names.size(); i++) { + if (i >= 5) { + break; + } + int finger_root = skeleton->find_bone(finger_names[i]); + int finger = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, finger_root, -1, 0); + if (finger != -1) { + while (finger != finger_root && finger >= 0) { + search_path.push_back(finger); + finger = skeleton->get_bone_parent(finger); + } + } + search_path.push_back(finger_root); + search_path.reverse(); + if (search_path.size() == 1) { + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + } else if (search_path.size() == 2) { + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][1], skeleton->get_bone_name(search_path[1])); + } else if (search_path.size() >= 3) { + search_path = search_path.slice(-3); // Eliminate the possibility of carpal bone. + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][1], skeleton->get_bone_name(search_path[1])); + p_bone_map->_set_skeleton_bone_name(left_fingers_map[i][2], skeleton->get_bone_name(search_path[2])); + } + search_path.clear(); + } + picklist.clear(); + } + } + named_finger_is_found = false; + if (right_hand_or_palm != -1) { + LocalVector<LocalVector<String>> right_fingers_map; + right_fingers_map.resize(5); + right_fingers_map[0].push_back("RightThumbMetacarpal"); + right_fingers_map[0].push_back("RightThumbProximal"); + right_fingers_map[0].push_back("RightThumbDistal"); + right_fingers_map[1].push_back("RightIndexProximal"); + right_fingers_map[1].push_back("RightIndexIntermediate"); + right_fingers_map[1].push_back("RightIndexDistal"); + right_fingers_map[2].push_back("RightMiddleProximal"); + right_fingers_map[2].push_back("RightMiddleIntermediate"); + right_fingers_map[2].push_back("RightMiddleDistal"); + right_fingers_map[3].push_back("RightRingProximal"); + right_fingers_map[3].push_back("RightRingIntermediate"); + right_fingers_map[3].push_back("RightRingDistal"); + right_fingers_map[4].push_back("RightLittleProximal"); + right_fingers_map[4].push_back("RightLittleIntermediate"); + right_fingers_map[4].push_back("RightLittleDistal"); + for (int i = 0; i < 5; i++) { + picklist.push_back(fingers[i]); + int finger = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, right_hand_or_palm, -1, 0); + if (finger != -1) { + while (finger != right_hand_or_palm && finger >= 0) { + search_path.push_back(finger); + finger = skeleton->get_bone_parent(finger); + } + search_path.reverse(); + if (search_path.size() == 1) { + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + named_finger_is_found = true; + } else if (search_path.size() == 2) { + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][1], skeleton->get_bone_name(search_path[1])); + named_finger_is_found = true; + } else if (search_path.size() >= 3) { + search_path = search_path.slice(-3); // Eliminate the possibility of carpal bone. + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][1], skeleton->get_bone_name(search_path[1])); + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][2], skeleton->get_bone_name(search_path[2])); + named_finger_is_found = true; + } + } + picklist.clear(); + search_path.clear(); + } + + // It is a bit corner case, but possibly the finger names are sequentially numbered... + if (!named_finger_is_found) { + picklist.push_back("finger"); + RegEx finger_re = RegEx("finger"); + search_path = skeleton->get_bone_children(right_hand_or_palm); + Vector<String> finger_names; + for (int i = 0; i < search_path.size(); i++) { + String bn = skeleton->get_bone_name(search_path[i]); + if (!finger_re.search(bn.to_lower()).is_null()) { + finger_names.push_back(bn); + } + } + finger_names.sort(); // Order by lexicographic, normal use cases never have more than 10 fingers in one hand. + search_path.clear(); + for (int i = 0; i < finger_names.size(); i++) { + if (i >= 5) { + break; + } + int finger_root = skeleton->find_bone(finger_names[i]); + int finger = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, finger_root, -1, 0); + if (finger != -1) { + while (finger != finger_root && finger >= 0) { + search_path.push_back(finger); + finger = skeleton->get_bone_parent(finger); + } + } + search_path.push_back(finger_root); + search_path.reverse(); + if (search_path.size() == 1) { + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + } else if (search_path.size() == 2) { + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][1], skeleton->get_bone_name(search_path[1])); + } else if (search_path.size() >= 3) { + search_path = search_path.slice(-3); // Eliminate the possibility of carpal bone. + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][0], skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][1], skeleton->get_bone_name(search_path[1])); + p_bone_map->_set_skeleton_bone_name(right_fingers_map[i][2], skeleton->get_bone_name(search_path[2])); + } + search_path.clear(); + } + picklist.clear(); + } + } + + // 7. Guess Arms + picklist.push_back("shoulder"); + picklist.push_back("clavicle"); + picklist.push_back("collar"); + int left_shoulder = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, hips); + if (left_shoulder == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftShoulder."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftShoulder", skeleton->get_bone_name(left_shoulder)); + } + int right_shoulder = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, hips); + if (right_shoulder == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightShoulder."); + } else { + p_bone_map->_set_skeleton_bone_name("RightShoulder", skeleton->get_bone_name(right_shoulder)); + } + picklist.clear(); + + // 7-1. Guess LowerArms + picklist.push_back("(low|fore).*arm"); + picklist.push_back("elbow"); + picklist.push_back("arm"); + int left_lower_arm = -1; + if (left_shoulder != -1 && left_hand_or_palm != -1) { + left_lower_arm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, left_shoulder, left_hand_or_palm); + } + if (left_lower_arm == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftLowerArm."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftLowerArm", skeleton->get_bone_name(left_lower_arm)); + } + int right_lower_arm = -1; + if (right_shoulder != -1 && right_hand_or_palm != -1) { + right_lower_arm = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, right_shoulder, right_hand_or_palm); + } + if (right_lower_arm == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightLowerArm."); + } else { + p_bone_map->_set_skeleton_bone_name("RightLowerArm", skeleton->get_bone_name(right_lower_arm)); + } + picklist.clear(); + + // 7-2. Guess UpperArms + picklist.push_back("up.*arm"); + picklist.push_back("arm"); + if (left_shoulder != -1 && left_lower_arm != -1) { + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_LEFT, left_shoulder, left_lower_arm); + } + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess LeftUpperArm."); + } else { + p_bone_map->_set_skeleton_bone_name("LeftUpperArm", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + if (right_shoulder != -1 && right_lower_arm != -1) { + bone_idx = search_bone_by_name(skeleton, picklist, BONE_SEGREGATION_RIGHT, right_shoulder, right_lower_arm); + } + if (bone_idx == -1) { + WARN_PRINT("Auto Mapping couldn't guess RightUpperArm."); + } else { + p_bone_map->_set_skeleton_bone_name("RightUpperArm", skeleton->get_bone_name(bone_idx)); + } + bone_idx = -1; + picklist.clear(); + + // 8. Guess UpperChest or Chest + if (neck_or_head == -1) { + return; // Abort. + } + int chest_or_upper_chest = skeleton->get_bone_parent(neck_or_head); + bool is_appropriate = true; + if (left_shoulder != -1) { + bone_idx = skeleton->get_bone_parent(left_shoulder); + bool detect = false; + while (bone_idx != hips && bone_idx >= 0) { + if (bone_idx == chest_or_upper_chest) { + detect = true; + break; + } + bone_idx = skeleton->get_bone_parent(bone_idx); + } + if (!detect) { + is_appropriate = false; + } + bone_idx = -1; + } + if (right_shoulder != -1) { + bone_idx = skeleton->get_bone_parent(right_shoulder); + bool detect = false; + while (bone_idx != hips && bone_idx >= 0) { + if (bone_idx == chest_or_upper_chest) { + detect = true; + break; + } + bone_idx = skeleton->get_bone_parent(bone_idx); + } + if (!detect) { + is_appropriate = false; + } + bone_idx = -1; + } + if (!is_appropriate) { + if (skeleton->get_bone_parent(left_shoulder) == skeleton->get_bone_parent(right_shoulder)) { + chest_or_upper_chest = skeleton->get_bone_parent(left_shoulder); + } else { + chest_or_upper_chest = -1; + } + } + if (chest_or_upper_chest == -1) { + WARN_PRINT("Auto Mapping couldn't guess Chest or UpperChest. Abort auto mapping."); + return; // Will be not able to guess Spines. + } + + // 9. Guess Spines + bone_idx = skeleton->get_bone_parent(chest_or_upper_chest); + while (bone_idx != hips && bone_idx >= 0) { + search_path.push_back(bone_idx); + bone_idx = skeleton->get_bone_parent(bone_idx); + } + search_path.reverse(); + if (search_path.size() == 0) { + p_bone_map->_set_skeleton_bone_name("Spine", skeleton->get_bone_name(chest_or_upper_chest)); // Maybe chibi model...? + } else if (search_path.size() == 1) { + p_bone_map->_set_skeleton_bone_name("Spine", skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name("Chest", skeleton->get_bone_name(chest_or_upper_chest)); + } else if (search_path.size() >= 2) { + p_bone_map->_set_skeleton_bone_name("Spine", skeleton->get_bone_name(search_path[0])); + p_bone_map->_set_skeleton_bone_name("Chest", skeleton->get_bone_name(search_path[search_path.size() - 1])); // Probably UppeChest's parent is appropriate. + p_bone_map->_set_skeleton_bone_name("UpperChest", skeleton->get_bone_name(chest_or_upper_chest)); + } + bone_idx = -1; + search_path.clear(); + + WARN_PRINT("Finish auto mapping."); +} +#endif // MODULE_REGEX_ENABLED + void BoneMapper::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { set(p_property, p_value); recreate_editor(); } +void BoneMapper::_profile_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { + bone_map->set(p_property, p_value); + + // Run auto mapping when setting SkeletonProfileHumanoid by GUI Editor. + Ref<SkeletonProfile> profile = bone_map->get_profile(); + if (profile.is_valid()) { + SkeletonProfileHumanoid *hmn = Object::cast_to<SkeletonProfileHumanoid>(profile.ptr()); + if (hmn) { +#ifdef MODULE_REGEX_ENABLED + _run_auto_mapping(); +#endif // MODULE_REGEX_ENABLED + } + } +} + void BoneMapper::_bind_methods() { ClassDB::bind_method(D_METHOD("set_current_group_idx", "current_group_idx"), &BoneMapper::set_current_group_idx); ClassDB::bind_method(D_METHOD("get_current_group_idx"), &BoneMapper::get_current_group_idx); @@ -444,10 +1337,6 @@ void BoneMapEditor::_notification(int p_what) { create_editors(); } break; case NOTIFICATION_EXIT_TREE: { - if (bone_mapper) { - remove_child(bone_mapper); - bone_mapper->queue_delete(); - } skeleton = nullptr; } break; } diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h index 339547ea10..0541ce6eac 100644 --- a/editor/plugins/bone_map_editor_plugin.h +++ b/editor/plugins/bone_map_editor_plugin.h @@ -34,6 +34,12 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "editor/editor_properties.h" + +#include "modules/modules_enabled.gen.h" // For regex. +#ifdef MODULE_REGEX_ENABLED +#include "modules/regex/regex.h" +#endif + #include "scene/3d/skeleton_3d.h" #include "scene/gui/color_rect.h" #include "scene/gui/dialogs.h" @@ -79,12 +85,13 @@ class BoneMapperItem : public VBoxContainer { int button_id = -1; StringName profile_bone_name; - PackedStringArray skeleton_bone_names; Ref<BoneMap> bone_map; - EditorPropertyTextEnum *skeleton_bone_selector; + EditorPropertyText *skeleton_bone_selector; + Button *picker_button; void _update_property(); + void _open_picker(); protected: void _notification(int p_what); @@ -95,20 +102,49 @@ protected: public: void assign_button_id(int p_button_id); - BoneMapperItem(Ref<BoneMap> &p_bone_map, PackedStringArray p_skeleton_bone_names, const StringName &p_profile_bone_name = StringName()); + BoneMapperItem(Ref<BoneMap> &p_bone_map, const StringName &p_profile_bone_name = StringName()); ~BoneMapperItem(); }; +class BonePicker : public AcceptDialog { + GDCLASS(BonePicker, AcceptDialog); + + Skeleton3D *skeleton = nullptr; + Tree *bones = nullptr; + +public: + void popup_bones_tree(const Size2i &p_minsize = Size2i()); + bool has_selected_bone(); + StringName get_selected_bone(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + + void _confirm(); + +private: + void create_editors(); + void create_bones_tree(Skeleton3D *p_skeleton); + +public: + BonePicker(Skeleton3D *p_skeleton); + ~BonePicker(); +}; + class BoneMapper : public VBoxContainer { GDCLASS(BoneMapper, VBoxContainer); Skeleton3D *skeleton; Ref<BoneMap> bone_map; + EditorPropertyResource *profile_selector; + Vector<BoneMapperItem *> bone_mapper_items; + Button *clear_mapping_button; + VBoxContainer *mapper_item_vbox; - HSeparator *separator; int current_group_idx = 0; int current_bone_idx = -1; @@ -126,10 +162,31 @@ class BoneMapper : public VBoxContainer { void update_group_idx(); void _update_state(); + /* Bone picker */ + BonePicker *picker = nullptr; + StringName picker_key_name; + void _pick_bone(const StringName &p_bone_name); + void _apply_picker_selection(); + void _clear_mapping_current_group(); + +#ifdef MODULE_REGEX_ENABLED + /* For auto mapping */ + enum BoneSegregation { + BONE_SEGREGATION_NONE, + BONE_SEGREGATION_LEFT, + BONE_SEGREGATION_RIGHT + }; + int search_bone_by_name(Skeleton3D *p_skeleton, Vector<String> p_picklist, BoneSegregation p_segregation = BONE_SEGREGATION_NONE, int p_parent = -1, int p_child = -1, int p_children_count = -1); + BoneSegregation guess_bone_segregation(String p_bone_name); + void auto_mapping_process(Ref<BoneMap> &p_bone_map); + void _run_auto_mapping(); +#endif // MODULE_REGEX_ENABLED + protected: void _notification(int p_what); static void _bind_methods(); virtual void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing); + virtual void _profile_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing); public: void set_current_group_idx(int p_group_idx); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index fc70ace331..1ed37876f0 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -40,6 +40,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_toaster.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/script_editor_plugin.h" #include "editor/scene_tree_dock.h" @@ -222,8 +223,8 @@ public: grid_step_x->set_value(p_grid_step.x); grid_step_y->set_value(p_grid_step.y); primary_grid_steps->set_value(p_primary_grid_steps); - rotation_offset->set_value(Math::rad2deg(p_rotation_offset)); - rotation_step->set_value(Math::rad2deg(p_rotation_step)); + rotation_offset->set_value(Math::rad_to_deg(p_rotation_offset)); + rotation_step->set_value(Math::rad_to_deg(p_rotation_step)); scale_step->set_value(p_scale_step); } @@ -231,8 +232,8 @@ public: p_grid_offset = Point2(grid_offset_x->get_value(), grid_offset_y->get_value()); p_grid_step = Point2(grid_step_x->get_value(), grid_step_y->get_value()); p_primary_grid_steps = int(primary_grid_steps->get_value()); - p_rotation_offset = Math::deg2rad(rotation_offset->get_value()); - p_rotation_step = Math::deg2rad(rotation_step->get_value()); + p_rotation_offset = Math::deg_to_rad(rotation_offset->get_value()); + p_rotation_step = Math::deg_to_rad(rotation_step->get_value()); p_scale_step = scale_step->get_value(); } }; @@ -1446,7 +1447,7 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { drag_selection, vformat(TTR("Rotate CanvasItem \"%s\" to %d degrees"), drag_selection[0]->get_name(), - Math::rad2deg(drag_selection[0]->_edit_get_rotation())), + Math::rad_to_deg(drag_selection[0]->_edit_get_rotation())), true); } @@ -2957,6 +2958,9 @@ void CanvasItemEditor::_draw_ruler_tool() { Point2 corner = Point2(begin.x, end.y); Vector2 length_vector = (begin - end).abs() / zoom; + const real_t horizontal_angle_rad = length_vector.angle(); + const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad; + Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts")); int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts")); Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); @@ -2973,6 +2977,42 @@ void CanvasItemEditor::_draw_ruler_tool() { text_pos.x = CLAMP(text_pos.x, text_width / 2, viewport->get_rect().size.x - text_width * 1.5); text_pos.y = CLAMP(text_pos.y, text_height * 1.5, viewport->get_rect().size.y - text_height * 1.5); + // Draw lines. + viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3)); + + bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x)); + if (draw_secondary_lines) { + viewport->draw_line(begin, corner, ruler_secondary_color, Math::round(EDSCALE)); + viewport->draw_line(corner, end, ruler_secondary_color, Math::round(EDSCALE)); + + // Angle arcs. + int arc_point_count = 8; + real_t arc_radius_max_length_percent = 0.1; + real_t ruler_length = length_vector.length() * zoom; + real_t arc_max_radius = 50.0; + real_t arc_line_width = 2.0; + + const Vector2 end_to_begin = (end - begin); + + real_t arc_1_start_angle = end_to_begin.x < 0 + ? (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0) + : (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad); + real_t arc_1_end_angle = arc_1_start_angle + vertical_angle_rad; + // Constrain arc to triangle height & max size. + real_t arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius); + + real_t arc_2_start_angle = end_to_begin.x < 0 + ? (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad) + : (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI); + real_t arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad; + // Constrain arc to triangle width & max size. + real_t arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius); + + viewport->draw_arc(begin, arc_1_radius, arc_1_start_angle, arc_1_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); + viewport->draw_arc(end, arc_2_radius, arc_2_start_angle, arc_2_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); + } + + // Draw text. if (begin.is_equal_approx(end)) { viewport->draw_string_outline(font, text_pos, (String)ruler_tool_origin, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); viewport->draw_string(font, text_pos, (String)ruler_tool_origin, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color); @@ -2984,17 +3024,7 @@ void CanvasItemEditor::_draw_ruler_tool() { viewport->draw_string_outline(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); viewport->draw_string(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color); - bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x)); - - viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3)); - if (draw_secondary_lines) { - viewport->draw_line(begin, corner, ruler_secondary_color, Math::round(EDSCALE)); - viewport->draw_line(corner, end, ruler_secondary_color, Math::round(EDSCALE)); - } - if (draw_secondary_lines) { - const real_t horizontal_angle_rad = length_vector.angle(); - const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad; const int horizontal_angle = round(180 * horizontal_angle_rad / Math_PI); const int vertical_angle = round(180 * vertical_angle_rad / Math_PI); @@ -3031,32 +3061,6 @@ void CanvasItemEditor::_draw_ruler_tool() { } viewport->draw_string_outline(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, outline_size, outline_color); viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color); - - // Angle arcs - int arc_point_count = 8; - real_t arc_radius_max_length_percent = 0.1; - real_t ruler_length = length_vector.length() * zoom; - real_t arc_max_radius = 50.0; - real_t arc_line_width = 2.0; - - const Vector2 end_to_begin = (end - begin); - - real_t arc_1_start_angle = end_to_begin.x < 0 - ? (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0) - : (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad); - real_t arc_1_end_angle = arc_1_start_angle + vertical_angle_rad; - // Constrain arc to triangle height & max size - real_t arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius); - - real_t arc_2_start_angle = end_to_begin.x < 0 - ? (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad) - : (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI); - real_t arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad; - // Constrain arc to triangle width & max size - real_t arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius); - - viewport->draw_arc(begin, arc_1_radius, arc_1_start_angle, arc_1_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); - viewport->draw_arc(end, arc_2_radius, arc_2_start_angle, arc_2_end_angle, arc_point_count, ruler_primary_color, Math::round(EDSCALE * arc_line_width)); } if (grid_snap_active) { @@ -3672,7 +3676,7 @@ void CanvasItemEditor::_draw_transform_message() { } break; case DRAG_ROTATE: { - real_t delta = Math::rad2deg(current_transform.get_rotation() - original_transform.get_rotation()); + real_t delta = Math::rad_to_deg(current_transform.get_rotation() - original_transform.get_rotation()); transform_message = TTR("Rotating:") + " " + FORMAT(delta) + String::utf8(" °"); } break; @@ -3988,6 +3992,10 @@ void CanvasItemEditor::_selection_changed() { selected_from_canvas = false; } +void CanvasItemEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { Array selection = editor_selection->get_selected_nodes(); if (selection.size() != 1 || Object::cast_to<Node>(selection[0]) != p_canvas_item) { @@ -4253,11 +4261,11 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, 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(TTR("Project Camera Override\nOverrides the running project's camera with the editor viewport camera.")); + 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(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature.")); + override_camera_button->set_tooltip_text(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature.")); } } @@ -5096,7 +5104,7 @@ CanvasItemEditor::CanvasItemEditor() { select_button->set_pressed(true); select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), Key::Q)); select_button->set_shortcut_context(this); - select_button->set_tooltip(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Alt+Drag: Scale selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("RMB: Add node at position clicked.")); + select_button->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Alt+Drag: Scale selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("RMB: Add node at position clicked.")); main_menu_hbox->add_child(memnew(VSeparator)); @@ -5107,7 +5115,7 @@ CanvasItemEditor::CanvasItemEditor() { move_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_MOVE)); move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), Key::W)); move_button->set_shortcut_context(this); - move_button->set_tooltip(TTR("Move Mode")); + move_button->set_tooltip_text(TTR("Move Mode")); rotate_button = memnew(Button); rotate_button->set_flat(true); @@ -5116,7 +5124,7 @@ CanvasItemEditor::CanvasItemEditor() { rotate_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_ROTATE)); rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), Key::E)); rotate_button->set_shortcut_context(this); - rotate_button->set_tooltip(TTR("Rotate Mode")); + rotate_button->set_tooltip_text(TTR("Rotate Mode")); scale_button = memnew(Button); scale_button->set_flat(true); @@ -5125,7 +5133,7 @@ CanvasItemEditor::CanvasItemEditor() { scale_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_SCALE)); scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), Key::S)); scale_button->set_shortcut_context(this); - scale_button->set_tooltip(TTR("Shift: Scale proportionally.")); + scale_button->set_tooltip_text(TTR("Shift: Scale proportionally.")); main_menu_hbox->add_child(memnew(VSeparator)); @@ -5134,14 +5142,14 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(list_select_button); list_select_button->set_toggle_mode(true); list_select_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_LIST_SELECT)); - list_select_button->set_tooltip(TTR("Show list of selectable nodes at position clicked.")); + list_select_button->set_tooltip_text(TTR("Show list of selectable nodes at position clicked.")); pivot_button = memnew(Button); pivot_button->set_flat(true); main_menu_hbox->add_child(pivot_button); pivot_button->set_toggle_mode(true); pivot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_EDIT_PIVOT)); - pivot_button->set_tooltip(TTR("Click to change object's rotation pivot.")); + pivot_button->set_tooltip_text(TTR("Click to change object's rotation pivot.")); pan_button = memnew(Button); pan_button->set_flat(true); @@ -5150,7 +5158,7 @@ CanvasItemEditor::CanvasItemEditor() { pan_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_PAN)); pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), Key::G)); pan_button->set_shortcut_context(this); - pan_button->set_tooltip(TTR("You can also use Pan View shortcut (Space by default) to pan in any mode.")); + pan_button->set_tooltip_text(TTR("You can also use Pan View shortcut (Space by default) to pan in any mode.")); ruler_button = memnew(Button); ruler_button->set_flat(true); @@ -5159,7 +5167,7 @@ CanvasItemEditor::CanvasItemEditor() { ruler_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select).bind(TOOL_RULER)); ruler_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/ruler_mode", TTR("Ruler Mode"), Key::R)); ruler_button->set_shortcut_context(this); - ruler_button->set_tooltip(TTR("Ruler Mode")); + ruler_button->set_tooltip_text(TTR("Ruler Mode")); main_menu_hbox->add_child(memnew(VSeparator)); @@ -5168,7 +5176,7 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(smart_snap_button); smart_snap_button->set_toggle_mode(true); smart_snap_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_toggle_smart_snap)); - smart_snap_button->set_tooltip(TTR("Toggle smart snapping.")); + smart_snap_button->set_tooltip_text(TTR("Toggle smart snapping.")); smart_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_smart_snap", TTR("Use Smart Snap"), KeyModifierMask::SHIFT | Key::S)); smart_snap_button->set_shortcut_context(this); @@ -5177,7 +5185,7 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(grid_snap_button); grid_snap_button->set_toggle_mode(true); grid_snap_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_toggle_grid_snap)); - grid_snap_button->set_tooltip(TTR("Toggle grid snapping.")); + grid_snap_button->set_tooltip_text(TTR("Toggle grid snapping.")); grid_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_grid_snap", TTR("Use Grid Snap"), KeyModifierMask::SHIFT | Key::G)); grid_snap_button->set_shortcut_context(this); @@ -5185,7 +5193,7 @@ CanvasItemEditor::CanvasItemEditor() { snap_config_menu->set_shortcut_context(this); main_menu_hbox->add_child(snap_config_menu); snap_config_menu->set_h_size_flags(SIZE_SHRINK_END); - snap_config_menu->set_tooltip(TTR("Snapping Options")); + snap_config_menu->set_tooltip_text(TTR("Snapping Options")); snap_config_menu->set_switch_on_hover(true); PopupMenu *p = snap_config_menu->get_popup(); @@ -5219,7 +5227,7 @@ CanvasItemEditor::CanvasItemEditor() { main_menu_hbox->add_child(lock_button); lock_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(LOCK_SELECTED)); - lock_button->set_tooltip(TTR("Lock selected node, preventing selection and movement.")); + lock_button->set_tooltip_text(TTR("Lock selected node, preventing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. lock_button->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD | Key::L)); @@ -5227,7 +5235,7 @@ CanvasItemEditor::CanvasItemEditor() { unlock_button->set_flat(true); main_menu_hbox->add_child(unlock_button); unlock_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(UNLOCK_SELECTED)); - unlock_button->set_tooltip(TTR("Unlock selected node, allowing selection and movement.")); + unlock_button->set_tooltip_text(TTR("Unlock selected node, allowing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. unlock_button->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::L)); @@ -5235,7 +5243,7 @@ CanvasItemEditor::CanvasItemEditor() { group_button->set_flat(true); main_menu_hbox->add_child(group_button); group_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(GROUP_SELECTED)); - group_button->set_tooltip(TTR("Make selected node's children not selectable.")); + group_button->set_tooltip_text(TTR("Make selected node's children not selectable.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. group_button->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD | Key::G)); @@ -5243,7 +5251,7 @@ CanvasItemEditor::CanvasItemEditor() { ungroup_button->set_flat(true); main_menu_hbox->add_child(ungroup_button); ungroup_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(UNGROUP_SELECTED)); - ungroup_button->set_tooltip(TTR("Make selected node's children selectable.")); + ungroup_button->set_tooltip_text(TTR("Make selected node's children selectable.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. ungroup_button->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::G)); @@ -5252,7 +5260,7 @@ CanvasItemEditor::CanvasItemEditor() { skeleton_menu = memnew(MenuButton); skeleton_menu->set_shortcut_context(this); main_menu_hbox->add_child(skeleton_menu); - skeleton_menu->set_tooltip(TTR("Skeleton Options")); + skeleton_menu->set_tooltip_text(TTR("Skeleton Options")); skeleton_menu->set_switch_on_hover(true); p = skeleton_menu->get_popup(); @@ -5332,7 +5340,7 @@ CanvasItemEditor::CanvasItemEditor() { key_loc_button->set_pressed(true); key_loc_button->set_focus_mode(FOCUS_NONE); key_loc_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(ANIM_INSERT_POS)); - key_loc_button->set_tooltip(TTR("Translation mask for inserting keys.")); + key_loc_button->set_tooltip_text(TTR("Translation mask for inserting keys.")); animation_hb->add_child(key_loc_button); key_rot_button = memnew(Button); @@ -5341,7 +5349,7 @@ CanvasItemEditor::CanvasItemEditor() { key_rot_button->set_pressed(true); key_rot_button->set_focus_mode(FOCUS_NONE); key_rot_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(ANIM_INSERT_ROT)); - key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys.")); + key_rot_button->set_tooltip_text(TTR("Rotation mask for inserting keys.")); animation_hb->add_child(key_rot_button); key_scale_button = memnew(Button); @@ -5349,14 +5357,14 @@ CanvasItemEditor::CanvasItemEditor() { key_scale_button->set_toggle_mode(true); key_scale_button->set_focus_mode(FOCUS_NONE); key_scale_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(ANIM_INSERT_SCALE)); - key_scale_button->set_tooltip(TTR("Scale mask for inserting keys.")); + key_scale_button->set_tooltip_text(TTR("Scale mask for inserting keys.")); animation_hb->add_child(key_scale_button); key_insert_button = memnew(Button); key_insert_button->set_flat(true); key_insert_button->set_focus_mode(FOCUS_NONE); key_insert_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback).bind(ANIM_INSERT_KEY)); - key_insert_button->set_tooltip(TTR("Insert keys (based on mask).")); + key_insert_button->set_tooltip_text(TTR("Insert keys (based on mask).")); key_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key", TTR("Insert Key"), Key::INSERT)); key_insert_button->set_shortcut_context(this); animation_hb->add_child(key_insert_button); @@ -5365,14 +5373,14 @@ CanvasItemEditor::CanvasItemEditor() { key_auto_insert_button->set_flat(true); key_auto_insert_button->set_toggle_mode(true); key_auto_insert_button->set_focus_mode(FOCUS_NONE); - key_auto_insert_button->set_tooltip(TTR("Auto insert keys when objects are translated, rotated or scaled (based on mask).\nKeys are only added to existing tracks, no new tracks will be created.\nKeys must be inserted manually for the first time.")); + key_auto_insert_button->set_tooltip_text(TTR("Auto insert keys when objects are translated, rotated or scaled (based on mask).\nKeys are only added to existing tracks, no new tracks will be created.\nKeys must be inserted manually for the first time.")); key_auto_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_auto_insert_key", TTR("Auto Insert Key"))); key_auto_insert_button->set_shortcut_context(this); animation_hb->add_child(key_auto_insert_button); animation_menu = memnew(MenuButton); animation_menu->set_shortcut_context(this); - animation_menu->set_tooltip(TTR("Animation Key and Pose Options")); + animation_menu->set_tooltip_text(TTR("Animation Key and Pose Options")); animation_hb->add_child(animation_menu); animation_menu->get_popup()->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback)); animation_menu->set_switch_on_hover(true); @@ -5419,7 +5427,7 @@ CanvasItemEditor::CanvasItemEditor() { CanvasItemEditor *CanvasItemEditor::singleton = nullptr; void CanvasItemEditorPlugin::edit(Object *p_object) { - canvas_item_editor->set_undo_redo(&get_undo_redo()); + canvas_item_editor->set_undo_redo(EditorNode::get_undo_redo()); canvas_item_editor->edit(Object::cast_to<CanvasItem>(p_object)); } @@ -5555,51 +5563,39 @@ bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_targe void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &path, const Point2 &p_point) { // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others. String name = path.get_file().get_basename(); - switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) { - case NAME_CASING_PASCAL_CASE: - name = name.capitalize().replace(" ", ""); - break; - case NAME_CASING_CAMEL_CASE: - name = name.capitalize().replace(" ", ""); - name[0] = name.to_lower()[0]; - break; - case NAME_CASING_SNAKE_CASE: - name = name.capitalize().replace(" ", "_").to_lower(); - break; - } - child->set_name(name); + child->set_name(Node::adjust_name_casing(name)); Ref<Texture2D> texture = ResourceCache::get_ref(path); if (parent) { - editor_data->get_undo_redo().add_do_method(parent, "add_child", child, true); - editor_data->get_undo_redo().add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - editor_data->get_undo_redo().add_do_reference(child); - editor_data->get_undo_redo().add_undo_method(parent, "remove_child", child); + editor_data->get_undo_redo()->add_do_method(parent, "add_child", child, true); + editor_data->get_undo_redo()->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + editor_data->get_undo_redo()->add_do_reference(child); + editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", child); } else { // If no parent is selected, set as root node of the scene. - editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", child); - editor_data->get_undo_redo().add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - editor_data->get_undo_redo().add_do_reference(child); - editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); + editor_data->get_undo_redo()->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child); + editor_data->get_undo_redo()->add_do_method(child, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + editor_data->get_undo_redo()->add_do_reference(child); + editor_data->get_undo_redo()->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); } if (parent) { String new_name = parent->validate_child_name(child); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + editor_data->get_undo_redo()->add_do_method(ed, "live_debug_create_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), child->get_class(), new_name); + editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); } if (Object::cast_to<TouchScreenButton>(child) || Object::cast_to<TextureButton>(child)) { - editor_data->get_undo_redo().add_do_property(child, "texture_normal", texture); + editor_data->get_undo_redo()->add_do_property(child, "texture_normal", texture); } else { - editor_data->get_undo_redo().add_do_property(child, "texture", texture); + editor_data->get_undo_redo()->add_do_property(child, "texture", texture); } // make visible for certain node type if (Object::cast_to<Control>(child)) { Size2 texture_size = texture->get_size(); - editor_data->get_undo_redo().add_do_property(child, "rect_size", texture_size); + editor_data->get_undo_redo()->add_do_property(child, "rect_size", texture_size); } else if (Object::cast_to<Polygon2D>(child)) { Size2 texture_size = texture->get_size(); Vector<Vector2> list = { @@ -5608,7 +5604,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & Vector2(texture_size.width, texture_size.height), Vector2(0, texture_size.height) }; - editor_data->get_undo_redo().add_do_property(child, "polygon", list); + editor_data->get_undo_redo()->add_do_property(child, "polygon", list); } // Compute the global position @@ -5617,7 +5613,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String & // there's nothing to be used as source position so snapping will work as absolute if enabled target_position = canvas_item_editor->snap_point(target_position); - editor_data->get_undo_redo().add_do_method(child, "set_global_position", target_position); + editor_data->get_undo_redo()->add_do_method(child, "set_global_position", target_position); } bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, const Point2 &p_point) { @@ -5642,15 +5638,15 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path)); - editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene, true); - editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_owner", edited_scene); - editor_data->get_undo_redo().add_do_reference(instantiated_scene); - editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instantiated_scene); + editor_data->get_undo_redo()->add_do_method(parent, "add_child", instantiated_scene, true); + editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_owner", edited_scene); + editor_data->get_undo_redo()->add_do_reference(instantiated_scene); + editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", instantiated_scene); String new_name = parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), path, new_name); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name)); + editor_data->get_undo_redo()->add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), path, new_name); + editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)) + "/" + new_name)); CanvasItem *instance_ci = Object::cast_to<CanvasItem>(instantiated_scene); if (instance_ci) { @@ -5664,7 +5660,7 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons // Preserve instance position of the original scene. target_pos += instance_ci->_edit_get_position(); - editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_position", target_pos); + editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_position", target_pos); } return true; @@ -5682,7 +5678,7 @@ void CanvasItemEditorViewport::_perform_drop_data() { Vector<String> error_files; - editor_data->get_undo_redo().create_action(TTR("Create Node")); + editor_data->get_undo_redo()->create_action(TTR("Create Node")); for (int i = 0; i < selected_files.size(); i++) { String path = selected_files[i]; @@ -5713,7 +5709,7 @@ void CanvasItemEditorViewport::_perform_drop_data() { } } - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); if (error_files.size() > 0) { String files_str; @@ -5860,26 +5856,21 @@ Node *CanvasItemEditorViewport::_make_texture_node_type(String texture_node_type return node; } -void CanvasItemEditorViewport::_update_theme() { - List<BaseButton *> btn_list; - button_group->get_buttons(&btn_list); - - for (int i = 0; i < btn_list.size(); i++) { - CheckBox *check = Object::cast_to<CheckBox>(btn_list[i]); - check->set_icon(get_theme_icon(check->get_text(), SNAME("EditorIcons"))); - } - - label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); -} - void CanvasItemEditorViewport::_notification(int p_what) { switch (p_what) { case NOTIFICATION_THEME_CHANGED: { - _update_theme(); + List<BaseButton *> btn_list; + button_group->get_buttons(&btn_list); + + for (int i = 0; i < btn_list.size(); i++) { + CheckBox *check = Object::cast_to<CheckBox>(btn_list[i]); + check->set_icon(get_theme_icon(check->get_text(), SNAME("EditorIcons"))); + } + + label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); } break; case NOTIFICATION_ENTER_TREE: { - _update_theme(); connect("mouse_exited", callable_mp(this, &CanvasItemEditorViewport::_on_mouse_exit)); } break; diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 04fd819dec..a4107c009e 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -45,6 +45,7 @@ class EditorData; class CanvasItemEditorViewport; class ViewPanner; +class EditorUndoRedoManager; class CanvasItemEditorSelectedItem : public Object { GDCLASS(CanvasItemEditorSelectedItem, Object); @@ -214,7 +215,7 @@ private: int primary_grid_steps = 8; int grid_step_multiplier = 0; - real_t snap_rotation_step = Math::deg2rad(15.0); + real_t snap_rotation_step = Math::deg_to_rad(15.0); real_t snap_rotation_offset = 0.0; real_t snap_scale_step = 0.1f; bool smart_snap_active = false; @@ -400,7 +401,7 @@ private: void _prepare_grid_menu(); void _on_grid_menu_id_pressed(int p_id); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true); Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list); @@ -547,7 +548,7 @@ public: Tool get_current_tool() { return tool; } void set_current_tool(Tool p_tool); - void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void edit(CanvasItem *p_canvas_item); void focus_selection(); @@ -615,7 +616,6 @@ class CanvasItemEditorViewport : public Control { bool _create_instance(Node *parent, String &path, const Point2 &p_point); void _perform_drop_data(); void _show_resource_type_selector(); - void _update_theme(); static void _bind_methods(); diff --git a/editor/plugins/cast_2d_editor_plugin.cpp b/editor/plugins/cast_2d_editor_plugin.cpp index 18c38e7ab8..a8d255f997 100644 --- a/editor/plugins/cast_2d_editor_plugin.cpp +++ b/editor/plugins/cast_2d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/2d/ray_cast_2d.h" #include "scene/2d/shape_cast_2d.h" diff --git a/editor/plugins/cast_2d_editor_plugin.h b/editor/plugins/cast_2d_editor_plugin.h index d9c0cc4a06..85ff497bc7 100644 --- a/editor/plugins/cast_2d_editor_plugin.h +++ b/editor/plugins/cast_2d_editor_plugin.h @@ -35,11 +35,12 @@ #include "scene/2d/node_2d.h" class CanvasItemEditor; +class EditorUndoRedoManager; class Cast2DEditor : public Control { GDCLASS(Cast2DEditor, Control); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; CanvasItemEditor *canvas_item_editor = nullptr; Node2D *node; diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index af20064a8d..11992ad10e 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -33,6 +33,7 @@ #include "canvas_item_editor_plugin.h" #include "core/os/keyboard.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/resources/capsule_shape_2d.h" #include "scene/resources/circle_shape_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h index f7de05ddd1..49e0820ae9 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.h +++ b/editor/plugins/collision_shape_2d_editor_plugin.h @@ -35,6 +35,7 @@ #include "scene/2d/collision_shape_2d.h" class CanvasItemEditor; +class EditorUndoRedoManager; class CollisionShape2DEditor : public Control { GDCLASS(CollisionShape2DEditor, Control); @@ -61,7 +62,7 @@ class CollisionShape2DEditor : public Control { Point2(1, -1), }; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; CanvasItemEditor *canvas_item_editor = nullptr; CollisionShape2D *node = nullptr; diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp index 2756e45cf4..cee09edcc6 100644 --- a/editor/plugins/control_editor_plugin.cpp +++ b/editor/plugins/control_editor_plugin.cpp @@ -31,10 +31,14 @@ #include "control_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "scene/gui/separator.h" +// Inspector controls. + void ControlPositioningWarning::_update_warning() { if (!control_node) { title_icon->set_texture(nullptr); @@ -49,7 +53,7 @@ void ControlPositioningWarning::_update_warning() { title_label->set_text(TTR("This node doesn't have a control parent.")); hint_label->set_text(TTR("Use the appropriate layout properties depending on where you are going to put it.")); } else if (Object::cast_to<Container>(parent_node)) { - title_icon->set_texture(get_theme_icon(SNAME("Container"), SNAME("EditorIcons"))); + title_icon->set_texture(get_theme_icon(SNAME("ContainerLayout"), SNAME("EditorIcons"))); title_label->set_text(TTR("This node is a child of a container.")); hint_label->set_text(TTR("Use container properties for positioning.")); } else { @@ -65,14 +69,14 @@ void ControlPositioningWarning::_update_toggler() { Ref<Texture2D> arrow; if (hint_label->is_visible()) { arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree")); - set_tooltip(TTR("Collapse positioning hint.")); + set_tooltip_text(TTR("Collapse positioning hint.")); } else { if (is_layout_rtl()) { arrow = get_theme_icon(SNAME("arrow_collapsed"), SNAME("Tree")); } else { arrow = get_theme_icon(SNAME("arrow_collapsed_mirrored"), SNAME("Tree")); } - set_tooltip(TTR("Expand positioning hint.")); + set_tooltip_text(TTR("Expand positioning hint.")); } hint_icon->set_texture(arrow); @@ -98,7 +102,6 @@ void ControlPositioningWarning::gui_input(const Ref<InputEvent> &p_event) { void ControlPositioningWarning::_notification(int p_notification) { switch (p_notification) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: _update_warning(); _update_toggler(); @@ -448,37 +451,277 @@ bool EditorInspectorPluginControl::parse_property(Object *p_object, const Varian return false; } -void ControlEditorToolbar::_set_anchors_and_offsets_preset(Control::LayoutPreset p_preset) { +// Toolbars controls. + +Size2 ControlEditorPopupButton::get_minimum_size() const { + Vector2 base_size = Vector2(26, 26) * EDSCALE; + + if (arrow_icon.is_null()) { + return base_size; + } + + Vector2 final_size; + final_size.x = base_size.x + arrow_icon->get_width(); + final_size.y = MAX(base_size.y, arrow_icon->get_height()); + + return final_size; +} + +void ControlEditorPopupButton::toggled(bool p_pressed) { + if (!p_pressed) { + return; + } + + Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale(); + + popup_panel->set_size(Size2(size.width, 0)); + Point2 gp = get_screen_position(); + gp.y += size.y; + if (is_layout_rtl()) { + gp.x += size.width - popup_panel->get_size().width; + } + popup_panel->set_position(gp); + + popup_panel->popup(); +} + +void ControlEditorPopupButton::_popup_visibility_changed(bool p_visible) { + set_pressed(p_visible); +} + +void ControlEditorPopupButton::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + arrow_icon = get_theme_icon("select_arrow", "Tree"); + } break; + + case NOTIFICATION_DRAW: { + if (arrow_icon.is_valid()) { + Vector2 arrow_pos = Point2(26, 0) * EDSCALE; + arrow_pos.y = get_size().y / 2 - arrow_icon->get_height() / 2; + draw_texture(arrow_icon, arrow_pos); + } + } break; + + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + popup_panel->set_layout_direction((Window::LayoutDirection)get_layout_direction()); + } break; + + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible_in_tree()) { + popup_panel->hide(); + } + } break; + } +} + +ControlEditorPopupButton::ControlEditorPopupButton() { + set_flat(true); + set_toggle_mode(true); + set_focus_mode(FOCUS_NONE); + + popup_panel = memnew(PopupPanel); + popup_panel->set_theme_type_variation("ControlEditorPopupButton"); + 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)); + + popup_vbox = memnew(VBoxContainer); + popup_panel->add_child(popup_vbox); +} + +void ControlEditorPresetPicker::_add_row_button(HBoxContainer *p_row, const int p_preset, const String &p_name) { + ERR_FAIL_COND(preset_buttons.has(p_preset)); + + Button *b = memnew(Button); + b->set_custom_minimum_size(Size2i(36, 36) * EDSCALE); + b->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER); + b->set_tooltip_text(p_name); + b->set_flat(true); + p_row->add_child(b); + b->connect("pressed", callable_mp(this, &ControlEditorPresetPicker::_preset_button_pressed).bind(p_preset)); + + preset_buttons[p_preset] = b; +} + +void ControlEditorPresetPicker::_add_separator(BoxContainer *p_box, Separator *p_separator) { + p_separator->add_theme_constant_override("separation", grid_separation); + p_separator->set_custom_minimum_size(Size2i(1, 1)); + p_box->add_child(p_separator); +} + +void AnchorPresetPicker::_preset_button_pressed(const int p_preset) { + emit_signal("anchors_preset_selected", p_preset); +} + +void AnchorPresetPicker::_notification(int p_notification) { + switch (p_notification) { + case NOTIFICATION_THEME_CHANGED: { + preset_buttons[PRESET_TOP_LEFT]->set_icon(get_theme_icon(SNAME("ControlAlignTopLeft"), SNAME("EditorIcons"))); + preset_buttons[PRESET_CENTER_TOP]->set_icon(get_theme_icon(SNAME("ControlAlignCenterTop"), SNAME("EditorIcons"))); + preset_buttons[PRESET_TOP_RIGHT]->set_icon(get_theme_icon(SNAME("ControlAlignTopRight"), SNAME("EditorIcons"))); + + preset_buttons[PRESET_CENTER_LEFT]->set_icon(get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons"))); + preset_buttons[PRESET_CENTER]->set_icon(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons"))); + preset_buttons[PRESET_CENTER_RIGHT]->set_icon(get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons"))); + + preset_buttons[PRESET_BOTTOM_LEFT]->set_icon(get_theme_icon(SNAME("ControlAlignBottomLeft"), SNAME("EditorIcons"))); + preset_buttons[PRESET_CENTER_BOTTOM]->set_icon(get_theme_icon(SNAME("ControlAlignCenterBottom"), SNAME("EditorIcons"))); + preset_buttons[PRESET_BOTTOM_RIGHT]->set_icon(get_theme_icon(SNAME("ControlAlignBottomRight"), SNAME("EditorIcons"))); + + preset_buttons[PRESET_TOP_WIDE]->set_icon(get_theme_icon(SNAME("ControlAlignTopWide"), SNAME("EditorIcons"))); + preset_buttons[PRESET_HCENTER_WIDE]->set_icon(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons"))); + preset_buttons[PRESET_BOTTOM_WIDE]->set_icon(get_theme_icon(SNAME("ControlAlignBottomWide"), SNAME("EditorIcons"))); + + preset_buttons[PRESET_LEFT_WIDE]->set_icon(get_theme_icon(SNAME("ControlAlignLeftWide"), SNAME("EditorIcons"))); + preset_buttons[PRESET_VCENTER_WIDE]->set_icon(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons"))); + preset_buttons[PRESET_RIGHT_WIDE]->set_icon(get_theme_icon(SNAME("ControlAlignRightWide"), SNAME("EditorIcons"))); + + preset_buttons[PRESET_FULL_RECT]->set_icon(get_theme_icon(SNAME("ControlAlignFullRect"), SNAME("EditorIcons"))); + } break; + } +} + +void AnchorPresetPicker::_bind_methods() { + ADD_SIGNAL(MethodInfo("anchors_preset_selected", PropertyInfo(Variant::INT, "preset"))); +} + +AnchorPresetPicker::AnchorPresetPicker() { + VBoxContainer *main_vb = memnew(VBoxContainer); + main_vb->add_theme_constant_override("separation", grid_separation); + add_child(main_vb); + + HBoxContainer *top_row = memnew(HBoxContainer); + top_row->set_alignment(BoxContainer::ALIGNMENT_CENTER); + top_row->add_theme_constant_override("separation", grid_separation); + main_vb->add_child(top_row); + + _add_row_button(top_row, PRESET_TOP_LEFT, TTR("Top Left")); + _add_row_button(top_row, PRESET_CENTER_TOP, TTR("Center Top")); + _add_row_button(top_row, PRESET_TOP_RIGHT, TTR("Top Right")); + _add_separator(top_row, memnew(VSeparator)); + _add_row_button(top_row, PRESET_TOP_WIDE, TTR("Top Wide")); + + HBoxContainer *mid_row = memnew(HBoxContainer); + mid_row->set_alignment(BoxContainer::ALIGNMENT_CENTER); + mid_row->add_theme_constant_override("separation", grid_separation); + main_vb->add_child(mid_row); + + _add_row_button(mid_row, PRESET_CENTER_LEFT, TTR("Center Left")); + _add_row_button(mid_row, PRESET_CENTER, TTR("Center")); + _add_row_button(mid_row, PRESET_CENTER_RIGHT, TTR("Center Right")); + _add_separator(mid_row, memnew(VSeparator)); + _add_row_button(mid_row, PRESET_HCENTER_WIDE, TTR("HCenter Wide")); + + HBoxContainer *bot_row = memnew(HBoxContainer); + bot_row->set_alignment(BoxContainer::ALIGNMENT_CENTER); + bot_row->add_theme_constant_override("separation", grid_separation); + main_vb->add_child(bot_row); + + _add_row_button(bot_row, PRESET_BOTTOM_LEFT, TTR("Bottom Left")); + _add_row_button(bot_row, PRESET_CENTER_BOTTOM, TTR("Center Bottom")); + _add_row_button(bot_row, PRESET_BOTTOM_RIGHT, TTR("Bottom Right")); + _add_separator(bot_row, memnew(VSeparator)); + _add_row_button(bot_row, PRESET_BOTTOM_WIDE, TTR("Bottom Wide")); + + _add_separator(main_vb, memnew(HSeparator)); + + HBoxContainer *extra_row = memnew(HBoxContainer); + extra_row->set_alignment(BoxContainer::ALIGNMENT_CENTER); + extra_row->add_theme_constant_override("separation", grid_separation); + main_vb->add_child(extra_row); + + _add_row_button(extra_row, PRESET_LEFT_WIDE, TTR("Left Wide")); + _add_row_button(extra_row, PRESET_VCENTER_WIDE, TTR("VCenter Wide")); + _add_row_button(extra_row, PRESET_RIGHT_WIDE, TTR("Right Wide")); + _add_separator(extra_row, memnew(VSeparator)); + _add_row_button(extra_row, PRESET_FULL_RECT, TTR("Full Rect")); +} + +void SizeFlagPresetPicker::_preset_button_pressed(const int p_preset) { + int flags = (SizeFlags)p_preset; + if (expand_button->is_pressed()) { + flags |= SIZE_EXPAND; + } + + emit_signal("size_flags_selected", flags); +} + +void SizeFlagPresetPicker::set_allowed_flags(Vector<SizeFlags> &p_flags) { + preset_buttons[SIZE_SHRINK_BEGIN]->set_disabled(!p_flags.has(SIZE_SHRINK_BEGIN)); + preset_buttons[SIZE_SHRINK_CENTER]->set_disabled(!p_flags.has(SIZE_SHRINK_CENTER)); + preset_buttons[SIZE_SHRINK_END]->set_disabled(!p_flags.has(SIZE_SHRINK_END)); + preset_buttons[SIZE_FILL]->set_disabled(!p_flags.has(SIZE_FILL)); + + expand_button->set_disabled(!p_flags.has(SIZE_EXPAND)); + if (p_flags.has(SIZE_EXPAND)) { + expand_button->set_tooltip_text(TTR("Enable to also set the Expand flag.\nDisable to only set Shrink/Fill flags.")); + } else { + expand_button->set_pressed(false); + expand_button->set_tooltip_text(TTR("Some parents of the selected nodes do not support the Expand flag.")); + } +} + +void SizeFlagPresetPicker::_notification(int p_notification) { + switch (p_notification) { + case NOTIFICATION_THEME_CHANGED: { + if (vertical) { + preset_buttons[SIZE_SHRINK_BEGIN]->set_icon(get_theme_icon(SNAME("ControlAlignCenterTop"), SNAME("EditorIcons"))); + preset_buttons[SIZE_SHRINK_CENTER]->set_icon(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons"))); + preset_buttons[SIZE_SHRINK_END]->set_icon(get_theme_icon(SNAME("ControlAlignCenterBottom"), SNAME("EditorIcons"))); + + preset_buttons[SIZE_FILL]->set_icon(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons"))); + } else { + preset_buttons[SIZE_SHRINK_BEGIN]->set_icon(get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons"))); + preset_buttons[SIZE_SHRINK_CENTER]->set_icon(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons"))); + preset_buttons[SIZE_SHRINK_END]->set_icon(get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons"))); + + preset_buttons[SIZE_FILL]->set_icon(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons"))); + } + } break; + } +} + +void SizeFlagPresetPicker::_bind_methods() { + ADD_SIGNAL(MethodInfo("size_flags_selected", PropertyInfo(Variant::INT, "size_flags"))); +} + +SizeFlagPresetPicker::SizeFlagPresetPicker(bool p_vertical) { + vertical = p_vertical; + + VBoxContainer *main_vb = memnew(VBoxContainer); + add_child(main_vb); + + HBoxContainer *main_row = memnew(HBoxContainer); + main_row->set_alignment(BoxContainer::ALIGNMENT_CENTER); + main_row->add_theme_constant_override("separation", grid_separation); + main_vb->add_child(main_row); + + _add_row_button(main_row, SIZE_SHRINK_BEGIN, TTR("Shrink Begin")); + _add_row_button(main_row, SIZE_SHRINK_CENTER, TTR("Shrink Center")); + _add_row_button(main_row, SIZE_SHRINK_END, TTR("Shrink End")); + _add_separator(main_row, memnew(VSeparator)); + _add_row_button(main_row, SIZE_FILL, TTR("Fill")); + + expand_button = memnew(CheckBox); + expand_button->set_flat(true); + expand_button->set_text(TTR("Align with Expand")); + expand_button->set_tooltip_text(TTR("Enable to also set the Expand flag.\nDisable to only set Shrink/Fill flags.")); + main_vb->add_child(expand_button); +} + +// Toolbar. + +void ControlEditorToolbar::_anchors_preset_selected(int p_preset) { + LayoutPreset preset = (LayoutPreset)p_preset; List<Node *> selection = editor_selection->get_selected_node_list(); - undo_redo->create_action(TTR("Change Anchors and Offsets")); + undo_redo->create_action(TTR("Change Anchors, Offsets, Grow Direction")); for (Node *E : selection) { Control *control = Object::cast_to<Control>(E); if (control) { - undo_redo->add_do_method(control, "set_anchors_preset", p_preset); - switch (p_preset) { - case PRESET_TOP_LEFT: - case PRESET_TOP_RIGHT: - case PRESET_BOTTOM_LEFT: - case PRESET_BOTTOM_RIGHT: - case PRESET_CENTER_LEFT: - case PRESET_CENTER_TOP: - case PRESET_CENTER_RIGHT: - case PRESET_CENTER_BOTTOM: - case PRESET_CENTER: - undo_redo->add_do_method(control, "set_offsets_preset", p_preset, Control::PRESET_MODE_KEEP_SIZE); - break; - case PRESET_LEFT_WIDE: - case PRESET_TOP_WIDE: - case PRESET_RIGHT_WIDE: - case PRESET_BOTTOM_WIDE: - case PRESET_VCENTER_WIDE: - case PRESET_HCENTER_WIDE: - case PRESET_FULL_RECT: - undo_redo->add_do_method(control, "set_offsets_preset", p_preset, Control::PRESET_MODE_MINSIZE); - break; - } + undo_redo->add_do_property(control, "anchors_preset", preset); undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); } } @@ -489,10 +732,10 @@ void ControlEditorToolbar::_set_anchors_and_offsets_preset(Control::LayoutPreset anchor_mode_button->set_pressed(anchors_mode); } -void ControlEditorToolbar::_set_anchors_and_offsets_to_keep_ratio() { +void ControlEditorToolbar::_anchors_to_current_ratio() { List<Node *> selection = editor_selection->get_selected_node_list(); - undo_redo->create_action(TTR("Change Anchors and Offsets")); + undo_redo->create_action(TTR("Change Anchors, Offsets (Keep Ratio)")); for (Node *E : selection) { Control *control = Object::cast_to<Control>(E); @@ -521,44 +764,41 @@ void ControlEditorToolbar::_set_anchors_and_offsets_to_keep_ratio() { undo_redo->commit_action(); } -void ControlEditorToolbar::_set_anchors_preset(Control::LayoutPreset p_preset) { - List<Node *> selection = editor_selection->get_selected_node_list(); +void ControlEditorToolbar::_anchor_mode_toggled(bool p_status) { + List<Control *> selection = _get_edited_controls(); + for (Control *E : selection) { + if (Object::cast_to<Container>(E->get_parent())) { + continue; + } - undo_redo->create_action(TTR("Change Anchors")); - for (Node *E : selection) { - Control *control = Object::cast_to<Control>(E); - if (control) { - undo_redo->add_do_method(control, "set_anchors_preset", p_preset); - undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); + if (p_status) { + E->set_meta("_edit_use_anchors_", true); + } else { + E->remove_meta("_edit_use_anchors_"); } } - undo_redo->commit_action(); + anchors_mode = p_status; + CanvasItemEditor::get_singleton()->update_viewport(); } -void ControlEditorToolbar::_set_container_h_preset(Control::SizeFlags p_preset) { +void ControlEditorToolbar::_container_flags_selected(int p_flags, bool p_vertical) { List<Node *> selection = editor_selection->get_selected_node_list(); - undo_redo->create_action(TTR("Change Horizontal Size Flags")); - for (Node *E : selection) { - Control *control = Object::cast_to<Control>(E); - if (control) { - undo_redo->add_do_method(control, "set_h_size_flags", p_preset); - undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); - } + if (p_vertical) { + undo_redo->create_action(TTR("Change Vertical Size Flags")); + } else { + undo_redo->create_action(TTR("Change Horizontal Size Flags")); } - undo_redo->commit_action(); -} - -void ControlEditorToolbar::_set_container_v_preset(Control::SizeFlags p_preset) { - List<Node *> selection = editor_selection->get_selected_node_list(); - - undo_redo->create_action(TTR("Change Horizontal Size Flags")); for (Node *E : selection) { Control *control = Object::cast_to<Control>(E); if (control) { - undo_redo->add_do_method(control, "set_v_size_flags", p_preset); + if (p_vertical) { + undo_redo->add_do_method(control, "set_v_size_flags", p_flags); + } else { + undo_redo->add_do_method(control, "set_h_size_flags", p_flags); + } undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); } } @@ -594,400 +834,204 @@ Vector2 ControlEditorToolbar::_position_to_anchor(const Control *p_control, Vect return output; } -void ControlEditorToolbar::_button_toggle_anchor_mode(bool p_status) { - List<Control *> selection = _get_edited_controls(false, false); - for (Control *E : selection) { - if (Object::cast_to<Container>(E->get_parent())) { - continue; - } - - if (p_status) { - E->set_meta("_edit_use_anchors_", true); - } else { - E->remove_meta("_edit_use_anchors_"); - } - } - - anchors_mode = p_status; - CanvasItemEditor::get_singleton()->update_viewport(); -} - bool ControlEditorToolbar::_is_node_locked(const Node *p_node) { return p_node->get_meta("_edit_lock_", false); } -List<Control *> ControlEditorToolbar::_get_edited_controls(bool retrieve_locked, bool remove_controls_if_parent_in_selection) { +List<Control *> ControlEditorToolbar::_get_edited_controls() { List<Control *> selection; for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) { Control *control = Object::cast_to<Control>(E.key); - if (control && control->is_visible_in_tree() && control->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retrieve_locked || !_is_node_locked(control))) { + if (control && control->is_visible_in_tree() && control->get_viewport() == EditorNode::get_singleton()->get_scene_root() && !_is_node_locked(control)) { selection.push_back(control); } } - if (remove_controls_if_parent_in_selection) { - List<Control *> filtered_selection; - for (Control *E : selection) { - if (!selection.find(E->get_parent())) { - filtered_selection.push_back(E); - } - } - return filtered_selection; - } - return selection; } -void ControlEditorToolbar::_popup_callback(int p_op) { - switch (p_op) { - case ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT: { - _set_anchors_and_offsets_preset(PRESET_TOP_LEFT); - } break; - case ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT: { - _set_anchors_and_offsets_preset(PRESET_TOP_RIGHT); - } break; - case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT: { - _set_anchors_and_offsets_preset(PRESET_BOTTOM_LEFT); - } break; - case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT: { - _set_anchors_and_offsets_preset(PRESET_BOTTOM_RIGHT); - } break; - case ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT: { - _set_anchors_and_offsets_preset(PRESET_CENTER_LEFT); - } break; - case ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT: { - _set_anchors_and_offsets_preset(PRESET_CENTER_RIGHT); - } break; - case ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP: { - _set_anchors_and_offsets_preset(PRESET_CENTER_TOP); - } break; - case ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM: { - _set_anchors_and_offsets_preset(PRESET_CENTER_BOTTOM); - } break; - case ANCHORS_AND_OFFSETS_PRESET_CENTER: { - _set_anchors_and_offsets_preset(PRESET_CENTER); - } break; - case ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE: { - _set_anchors_and_offsets_preset(PRESET_TOP_WIDE); - } break; - case ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE: { - _set_anchors_and_offsets_preset(PRESET_LEFT_WIDE); - } break; - case ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE: { - _set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE); - } break; - case ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE: { - _set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE); - } break; - case ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE: { - _set_anchors_and_offsets_preset(PRESET_VCENTER_WIDE); - } break; - case ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE: { - _set_anchors_and_offsets_preset(PRESET_HCENTER_WIDE); - } break; - case ANCHORS_AND_OFFSETS_PRESET_FULL_RECT: { - _set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); - } break; - case ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO: { - _set_anchors_and_offsets_to_keep_ratio(); - } break; - - case ANCHORS_PRESET_TOP_LEFT: { - _set_anchors_preset(PRESET_TOP_LEFT); - } break; - case ANCHORS_PRESET_TOP_RIGHT: { - _set_anchors_preset(PRESET_TOP_RIGHT); - } break; - case ANCHORS_PRESET_BOTTOM_LEFT: { - _set_anchors_preset(PRESET_BOTTOM_LEFT); - } break; - case ANCHORS_PRESET_BOTTOM_RIGHT: { - _set_anchors_preset(PRESET_BOTTOM_RIGHT); - } break; - case ANCHORS_PRESET_CENTER_LEFT: { - _set_anchors_preset(PRESET_CENTER_LEFT); - } break; - case ANCHORS_PRESET_CENTER_RIGHT: { - _set_anchors_preset(PRESET_CENTER_RIGHT); - } break; - case ANCHORS_PRESET_CENTER_TOP: { - _set_anchors_preset(PRESET_CENTER_TOP); - } break; - case ANCHORS_PRESET_CENTER_BOTTOM: { - _set_anchors_preset(PRESET_CENTER_BOTTOM); - } break; - case ANCHORS_PRESET_CENTER: { - _set_anchors_preset(PRESET_CENTER); - } break; - case ANCHORS_PRESET_TOP_WIDE: { - _set_anchors_preset(PRESET_TOP_WIDE); - } break; - case ANCHORS_PRESET_LEFT_WIDE: { - _set_anchors_preset(PRESET_LEFT_WIDE); - } break; - case ANCHORS_PRESET_RIGHT_WIDE: { - _set_anchors_preset(PRESET_RIGHT_WIDE); - } break; - case ANCHORS_PRESET_BOTTOM_WIDE: { - _set_anchors_preset(PRESET_BOTTOM_WIDE); - } break; - case ANCHORS_PRESET_VCENTER_WIDE: { - _set_anchors_preset(PRESET_VCENTER_WIDE); - } break; - case ANCHORS_PRESET_HCENTER_WIDE: { - _set_anchors_preset(PRESET_HCENTER_WIDE); - } break; - case ANCHORS_PRESET_FULL_RECT: { - _set_anchors_preset(Control::PRESET_FULL_RECT); - } break; - - case CONTAINERS_H_PRESET_FILL: { - _set_container_h_preset(Control::SIZE_FILL); - } break; - case CONTAINERS_H_PRESET_FILL_EXPAND: { - _set_container_h_preset(Control::SIZE_EXPAND_FILL); - } break; - case CONTAINERS_H_PRESET_SHRINK_BEGIN: { - _set_container_h_preset(Control::SIZE_SHRINK_BEGIN); - } break; - case CONTAINERS_H_PRESET_SHRINK_CENTER: { - _set_container_h_preset(Control::SIZE_SHRINK_CENTER); - } break; - case CONTAINERS_H_PRESET_SHRINK_END: { - _set_container_h_preset(Control::SIZE_SHRINK_END); - } break; - - case CONTAINERS_V_PRESET_FILL: { - _set_container_v_preset(Control::SIZE_FILL); - } break; - case CONTAINERS_V_PRESET_FILL_EXPAND: { - _set_container_v_preset(Control::SIZE_EXPAND_FILL); - } break; - case CONTAINERS_V_PRESET_SHRINK_BEGIN: { - _set_container_v_preset(Control::SIZE_SHRINK_BEGIN); - } break; - case CONTAINERS_V_PRESET_SHRINK_CENTER: { - _set_container_v_preset(Control::SIZE_SHRINK_CENTER); - } break; - case CONTAINERS_V_PRESET_SHRINK_END: { - _set_container_v_preset(Control::SIZE_SHRINK_END); - } break; - } -} - void ControlEditorToolbar::_selection_changed() { - // Update the anchors_mode. - int nb_controls = 0; - int nb_valid_controls = 0; - int nb_anchors_mode = 0; + // Update toolbar visibility. + bool has_controls = false; + bool has_control_parents = false; + bool has_container_parents = false; + + // Also update which size flags can be configured for the selected nodes. + Vector<SizeFlags> allowed_h_flags = { + SIZE_SHRINK_BEGIN, + SIZE_SHRINK_CENTER, + SIZE_SHRINK_END, + SIZE_FILL, + SIZE_EXPAND, + }; + Vector<SizeFlags> allowed_v_flags = { + SIZE_SHRINK_BEGIN, + SIZE_SHRINK_CENTER, + SIZE_SHRINK_END, + SIZE_FILL, + SIZE_EXPAND, + }; - List<Node *> selection = editor_selection->get_selected_node_list(); - for (Node *E : selection) { - Control *control = Object::cast_to<Control>(E); + for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) { + Control *control = Object::cast_to<Control>(E.key); if (!control) { continue; } + has_controls = true; - nb_controls++; - if (Object::cast_to<Container>(control->get_parent())) { - continue; + if (Object::cast_to<Control>(control->get_parent())) { + has_control_parents = true; } + if (Object::cast_to<Container>(control->get_parent())) { + has_container_parents = true; - nb_valid_controls++; - if (control->get_meta("_edit_use_anchors_", false)) { - nb_anchors_mode++; - } - } + Container *parent_container = Object::cast_to<Container>(control->get_parent()); - anchors_mode = (nb_valid_controls == nb_anchors_mode); - anchor_mode_button->set_pressed(anchors_mode); + Vector<int> container_h_flags = parent_container->get_allowed_size_flags_horizontal(); + Vector<SizeFlags> tmp_flags = allowed_h_flags.duplicate(); + for (int i = 0; i < allowed_h_flags.size(); i++) { + if (!container_h_flags.has((int)allowed_h_flags[i])) { + tmp_flags.erase(allowed_h_flags[i]); + } + } + allowed_h_flags = tmp_flags; - if (nb_controls > 0) { - set_physics_process(true); - } else { - set_physics_process(false); - set_visible(false); + Vector<int> container_v_flags = parent_container->get_allowed_size_flags_vertical(); + tmp_flags = allowed_v_flags.duplicate(); + for (int i = 0; i < allowed_v_flags.size(); i++) { + if (!container_v_flags.has((int)allowed_v_flags[i])) { + tmp_flags.erase(allowed_v_flags[i]); + } + } + allowed_v_flags = tmp_flags; + } } -} -void ControlEditorToolbar::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: - case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - anchor_presets_menu->set_icon(get_theme_icon(SNAME("ControlLayout"), SNAME("EditorIcons"))); - - PopupMenu *p = anchor_presets_menu->get_popup(); - p->clear(); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopLeft"), SNAME("EditorIcons")), TTR("Top Left"), ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopRight"), SNAME("EditorIcons")), TTR("Top Right"), ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomRight"), SNAME("EditorIcons")), TTR("Bottom Right"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomLeft"), SNAME("EditorIcons")), TTR("Bottom Left"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT); - p->add_separator(); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons")), TTR("Center Left"), ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterTop"), SNAME("EditorIcons")), TTR("Center Top"), ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons")), TTR("Center Right"), ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterBottom"), SNAME("EditorIcons")), TTR("Center Bottom"), ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Center"), ANCHORS_AND_OFFSETS_PRESET_CENTER); - p->add_separator(); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignLeftWide"), SNAME("EditorIcons")), TTR("Left Wide"), ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignTopWide"), SNAME("EditorIcons")), TTR("Top Wide"), ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignRightWide"), SNAME("EditorIcons")), TTR("Right Wide"), ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomWide"), SNAME("EditorIcons")), TTR("Bottom Wide"), ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("VCenter Wide"), ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("HCenter Wide"), ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE); - p->add_separator(); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignFullRect"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_AND_OFFSETS_PRESET_FULL_RECT); - p->add_icon_item(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")), TTR("Keep Current Ratio"), ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO); - p->set_item_tooltip(19, TTR("Adjust anchors and offsets to match the current rect size.")); - - p->add_separator(); - p->add_submenu_item(TTR("Anchors only"), "Anchors"); - p->set_item_icon(21, get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons"))); - - anchors_popup->clear(); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopLeft"), SNAME("EditorIcons")), TTR("Top Left"), ANCHORS_PRESET_TOP_LEFT); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopRight"), SNAME("EditorIcons")), TTR("Top Right"), ANCHORS_PRESET_TOP_RIGHT); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomRight"), SNAME("EditorIcons")), TTR("Bottom Right"), ANCHORS_PRESET_BOTTOM_RIGHT); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomLeft"), SNAME("EditorIcons")), TTR("Bottom Left"), ANCHORS_PRESET_BOTTOM_LEFT); - anchors_popup->add_separator(); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons")), TTR("Center Left"), ANCHORS_PRESET_CENTER_LEFT); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterTop"), SNAME("EditorIcons")), TTR("Center Top"), ANCHORS_PRESET_CENTER_TOP); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons")), TTR("Center Right"), ANCHORS_PRESET_CENTER_RIGHT); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterBottom"), SNAME("EditorIcons")), TTR("Center Bottom"), ANCHORS_PRESET_CENTER_BOTTOM); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Center"), ANCHORS_PRESET_CENTER); - anchors_popup->add_separator(); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignLeftWide"), SNAME("EditorIcons")), TTR("Left Wide"), ANCHORS_PRESET_LEFT_WIDE); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignTopWide"), SNAME("EditorIcons")), TTR("Top Wide"), ANCHORS_PRESET_TOP_WIDE); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignRightWide"), SNAME("EditorIcons")), TTR("Right Wide"), ANCHORS_PRESET_RIGHT_WIDE); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignBottomWide"), SNAME("EditorIcons")), TTR("Bottom Wide"), ANCHORS_PRESET_BOTTOM_WIDE); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("VCenter Wide"), ANCHORS_PRESET_VCENTER_WIDE); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("HCenter Wide"), ANCHORS_PRESET_HCENTER_WIDE); - anchors_popup->add_separator(); - anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignFullRect"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_PRESET_FULL_RECT); + // Set general toolbar visibility. + set_visible(has_controls); - anchor_mode_button->set_icon(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons"))); + // Set anchor tools visibility. + if (has_controls && (!has_control_parents || !has_container_parents)) { + anchors_button->set_visible(true); + anchor_mode_button->set_visible(true); - container_h_presets_menu->set_icon(get_theme_icon(SNAME("Container"), SNAME("EditorIcons"))); - container_v_presets_menu->set_icon(get_theme_icon(SNAME("Container"), SNAME("EditorIcons"))); - - p = container_h_presets_menu->get_popup(); - p->clear(); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("Fill"), CONTAINERS_H_PRESET_FILL); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignHCenterWide"), SNAME("EditorIcons")), TTR("Fill & Expand"), CONTAINERS_H_PRESET_FILL_EXPAND); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterLeft"), SNAME("EditorIcons")), TTR("Shrink Begin"), CONTAINERS_H_PRESET_SHRINK_BEGIN); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Shrink Center"), CONTAINERS_H_PRESET_SHRINK_CENTER); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterRight"), SNAME("EditorIcons")), TTR("Shrink End"), CONTAINERS_H_PRESET_SHRINK_END); - - p = container_v_presets_menu->get_popup(); - p->clear(); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("Fill"), CONTAINERS_V_PRESET_FILL); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignVCenterWide"), SNAME("EditorIcons")), TTR("Fill & Expand"), CONTAINERS_V_PRESET_FILL_EXPAND); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterTop"), SNAME("EditorIcons")), TTR("Shrink Begin"), CONTAINERS_V_PRESET_SHRINK_BEGIN); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenter"), SNAME("EditorIcons")), TTR("Shrink Center"), CONTAINERS_V_PRESET_SHRINK_CENTER); - p->add_icon_item(get_theme_icon(SNAME("ControlAlignCenterBottom"), SNAME("EditorIcons")), TTR("Shrink End"), CONTAINERS_V_PRESET_SHRINK_END); - } break; + // Update anchor mode. + int nb_valid_controls = 0; + int nb_anchors_mode = 0; - case NOTIFICATION_PHYSICS_PROCESS: { - bool has_control_parents = false; - bool has_container_parents = false; + List<Node *> selection = editor_selection->get_selected_node_list(); + for (Node *E : selection) { + Control *control = Object::cast_to<Control>(E); + if (!control) { + continue; + } + if (Object::cast_to<Container>(control->get_parent())) { + continue; + } - // Update the viewport if the canvas_item changes - List<Control *> selection = _get_edited_controls(true); - for (Control *control : selection) { - if (Object::cast_to<Control>(control->get_parent())) { - has_control_parents = true; - } - if (Object::cast_to<Container>(control->get_parent())) { - has_container_parents = true; - } + nb_valid_controls++; + if (control->get_meta("_edit_use_anchors_", false)) { + nb_anchors_mode++; } + } - // Show / Hide the control layout buttons. - if (selection.size() > 0) { - set_visible(true); - - // Toggle anchor and container layout buttons depending on parents of the selected nodes. - // - If there are no control parents, enable everything. - // - If there are container parents, then enable only container buttons. - // - If there are NO container parents, then enable only anchor buttons. - bool enable_anchors = false; - bool enable_containers = false; - if (!has_control_parents) { - enable_anchors = true; - enable_containers = true; - } else if (has_container_parents) { - enable_containers = true; - } else { - enable_anchors = true; - } + anchors_mode = (nb_valid_controls == nb_anchors_mode); + anchor_mode_button->set_pressed(anchors_mode); + } else { + anchors_button->set_visible(false); + anchor_mode_button->set_visible(false); + anchor_mode_button->set_pressed(false); + } - if (enable_anchors) { - anchor_presets_menu->set_visible(true); - anchor_mode_button->set_visible(true); - } else { - anchor_presets_menu->set_visible(false); - anchor_mode_button->set_visible(false); - } + // Set container tools visibility. + if (has_controls && (!has_control_parents || has_container_parents)) { + containers_button->set_visible(true); - if (enable_containers) { - container_h_presets_menu->set_visible(true); - container_v_presets_menu->set_visible(true); - } else { - container_h_presets_menu->set_visible(false); - container_v_presets_menu->set_visible(false); - } - } else { - set_visible(false); - } + // Update allowed size flags. + if (has_container_parents) { + container_h_picker->set_allowed_flags(allowed_h_flags); + container_v_picker->set_allowed_flags(allowed_v_flags); + } else { + Vector<SizeFlags> allowed_all_flags = { + SIZE_SHRINK_BEGIN, + SIZE_SHRINK_CENTER, + SIZE_SHRINK_END, + SIZE_FILL, + SIZE_EXPAND, + }; + + container_h_picker->set_allowed_flags(allowed_all_flags); + container_v_picker->set_allowed_flags(allowed_all_flags); + } + } else { + containers_button->set_visible(false); + } +} + +void ControlEditorToolbar::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + anchors_button->set_icon(get_theme_icon(SNAME("ControlLayout"), SNAME("EditorIcons"))); + anchor_mode_button->set_icon(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons"))); + containers_button->set_icon(get_theme_icon(SNAME("ContainerLayout"), SNAME("EditorIcons"))); } break; } } ControlEditorToolbar::ControlEditorToolbar() { - anchor_presets_menu = memnew(MenuButton); - anchor_presets_menu->set_shortcut_context(this); - anchor_presets_menu->set_text(TTR("Anchors")); - anchor_presets_menu->set_tooltip(TTR("Presets for the anchor and offset values of a Control node.")); - add_child(anchor_presets_menu); - anchor_presets_menu->set_switch_on_hover(true); + add_child(memnew(VSeparator)); - PopupMenu *p = anchor_presets_menu->get_popup(); - p->connect("id_pressed", callable_mp(this, &ControlEditorToolbar::_popup_callback)); + // Anchor and offset tools. + anchors_button = memnew(ControlEditorPopupButton); + anchors_button->set_tooltip_text(TTR("Presets for the anchor and offset values of a Control node.")); + add_child(anchors_button); - anchors_popup = memnew(PopupMenu); - p->add_child(anchors_popup); - anchors_popup->set_name("Anchors"); - anchors_popup->connect("id_pressed", callable_mp(this, &ControlEditorToolbar::_popup_callback)); + Label *anchors_label = memnew(Label); + anchors_label->set_text(TTR("Anchor preset")); + anchors_button->get_popup_hbox()->add_child(anchors_label); + AnchorPresetPicker *anchors_picker = memnew(AnchorPresetPicker); + anchors_picker->set_h_size_flags(SIZE_SHRINK_CENTER); + anchors_button->get_popup_hbox()->add_child(anchors_picker); + anchors_picker->connect("anchors_preset_selected", callable_mp(this, &ControlEditorToolbar::_anchors_preset_selected)); + + anchors_button->get_popup_hbox()->add_child(memnew(HSeparator)); + + Button *keep_ratio_button = memnew(Button); + keep_ratio_button->set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT); + keep_ratio_button->set_text(TTR("Set to Current Ratio")); + keep_ratio_button->set_tooltip_text(TTR("Adjust anchors and offsets to match the current rect size.")); + anchors_button->get_popup_hbox()->add_child(keep_ratio_button); + keep_ratio_button->connect("pressed", callable_mp(this, &ControlEditorToolbar::_anchors_to_current_ratio)); anchor_mode_button = memnew(Button); anchor_mode_button->set_flat(true); anchor_mode_button->set_toggle_mode(true); - anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their offsets.")); + anchor_mode_button->set_tooltip_text(TTR("When active, moving Control nodes changes their anchors instead of their offsets.")); add_child(anchor_mode_button); - anchor_mode_button->connect("toggled", callable_mp(this, &ControlEditorToolbar::_button_toggle_anchor_mode)); - - add_child(memnew(VSeparator)); - - container_h_presets_menu = memnew(MenuButton); - container_h_presets_menu->set_shortcut_context(this); - container_h_presets_menu->set_text(TTR("Horizontal")); - container_h_presets_menu->set_tooltip(TTR("Horizontal sizing setting for children of a Container node.")); - add_child(container_h_presets_menu); - container_h_presets_menu->set_switch_on_hover(true); - - p = container_h_presets_menu->get_popup(); - p->connect("id_pressed", callable_mp(this, &ControlEditorToolbar::_popup_callback)); - - container_v_presets_menu = memnew(MenuButton); - container_v_presets_menu->set_shortcut_context(this); - container_v_presets_menu->set_text(TTR("Vertical")); - container_v_presets_menu->set_tooltip(TTR("Vertical sizing setting for children of a Container node.")); - add_child(container_v_presets_menu); - container_v_presets_menu->set_switch_on_hover(true); - - p = container_v_presets_menu->get_popup(); - p->connect("id_pressed", callable_mp(this, &ControlEditorToolbar::_popup_callback)); - + anchor_mode_button->connect("toggled", callable_mp(this, &ControlEditorToolbar::_anchor_mode_toggled)); + + // Container tools. + containers_button = memnew(ControlEditorPopupButton); + containers_button->set_tooltip_text(TTR("Sizing settings for children of a Container node.")); + add_child(containers_button); + + Label *container_h_label = memnew(Label); + container_h_label->set_text(TTR("Horizontal alignment")); + containers_button->get_popup_hbox()->add_child(container_h_label); + container_h_picker = memnew(SizeFlagPresetPicker(false)); + containers_button->get_popup_hbox()->add_child(container_h_picker); + container_h_picker->connect("size_flags_selected", callable_mp(this, &ControlEditorToolbar::_container_flags_selected).bind(false)); + + containers_button->get_popup_hbox()->add_child(memnew(HSeparator)); + + Label *container_v_label = memnew(Label); + container_v_label->set_text(TTR("Vertical alignment")); + containers_button->get_popup_hbox()->add_child(container_v_label); + container_v_picker = memnew(SizeFlagPresetPicker(true)); + containers_button->get_popup_hbox()->add_child(container_v_picker); + container_v_picker->connect("size_flags_selected", callable_mp(this, &ControlEditorToolbar::_container_flags_selected).bind(true)); + + // Editor connections. undo_redo = EditorNode::get_singleton()->get_undo_redo(); editor_selection = EditorNode::get_singleton()->get_editor_selection(); editor_selection->add_editor_plugin(this); @@ -998,6 +1042,8 @@ ControlEditorToolbar::ControlEditorToolbar() { ControlEditorToolbar *ControlEditorToolbar::singleton = nullptr; +// Editor plugin. + ControlEditorPlugin::ControlEditorPlugin() { toolbar = memnew(ControlEditorToolbar); toolbar->hide(); diff --git a/editor/plugins/control_editor_plugin.h b/editor/plugins/control_editor_plugin.h index 11389bc095..584d05aab0 100644 --- a/editor/plugins/control_editor_plugin.h +++ b/editor/plugins/control_editor_plugin.h @@ -33,14 +33,20 @@ #include "editor/editor_plugin.h" #include "scene/gui/box_container.h" +#include "scene/gui/button.h" #include "scene/gui/check_box.h" #include "scene/gui/control.h" #include "scene/gui/label.h" #include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/panel_container.h" +#include "scene/gui/popup.h" +#include "scene/gui/separator.h" #include "scene/gui/texture_rect.h" +class EditorUndoRedoManager; + +// Inspector controls. class ControlPositioningWarning : public MarginContainer { GDCLASS(ControlPositioningWarning, MarginContainer); @@ -125,102 +131,101 @@ public: virtual bool parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide = false) override; }; +// Toolbar controls. +class ControlEditorPopupButton : public Button { + GDCLASS(ControlEditorPopupButton, Button); + + Ref<Texture2D> arrow_icon; + + PopupPanel *popup_panel = nullptr; + VBoxContainer *popup_vbox = nullptr; + + void _popup_visibility_changed(bool p_visible); + +protected: + void _notification(int p_what); + +public: + virtual Size2 get_minimum_size() const override; + virtual void toggled(bool p_pressed) override; + + VBoxContainer *get_popup_hbox() const { return popup_vbox; } + + ControlEditorPopupButton(); +}; + +class ControlEditorPresetPicker : public MarginContainer { + GDCLASS(ControlEditorPresetPicker, MarginContainer); + + virtual void _preset_button_pressed(const int p_preset) {} + +protected: + static constexpr int grid_separation = 0; + HashMap<int, Button *> preset_buttons; + + void _add_row_button(HBoxContainer *p_row, const int p_preset, const String &p_name); + void _add_separator(BoxContainer *p_box, Separator *p_separator); + +public: + ControlEditorPresetPicker() {} +}; + +class AnchorPresetPicker : public ControlEditorPresetPicker { + GDCLASS(AnchorPresetPicker, ControlEditorPresetPicker); + + virtual void _preset_button_pressed(const int p_preset) override; + +protected: + void _notification(int p_notification); + static void _bind_methods(); + +public: + AnchorPresetPicker(); +}; + +class SizeFlagPresetPicker : public ControlEditorPresetPicker { + GDCLASS(SizeFlagPresetPicker, ControlEditorPresetPicker); + + CheckBox *expand_button; + + bool vertical = false; + + virtual void _preset_button_pressed(const int p_preset) override; + +protected: + void _notification(int p_notification); + static void _bind_methods(); + +public: + void set_allowed_flags(Vector<SizeFlags> &p_flags); + + SizeFlagPresetPicker(bool p_vertical); +}; + class ControlEditorToolbar : public HBoxContainer { GDCLASS(ControlEditorToolbar, HBoxContainer); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; EditorSelection *editor_selection = nullptr; - enum MenuOption { - ANCHORS_AND_OFFSETS_PRESET_TOP_LEFT, - ANCHORS_AND_OFFSETS_PRESET_TOP_RIGHT, - ANCHORS_AND_OFFSETS_PRESET_BOTTOM_LEFT, - ANCHORS_AND_OFFSETS_PRESET_BOTTOM_RIGHT, - ANCHORS_AND_OFFSETS_PRESET_CENTER_LEFT, - ANCHORS_AND_OFFSETS_PRESET_CENTER_RIGHT, - ANCHORS_AND_OFFSETS_PRESET_CENTER_TOP, - ANCHORS_AND_OFFSETS_PRESET_CENTER_BOTTOM, - ANCHORS_AND_OFFSETS_PRESET_CENTER, - ANCHORS_AND_OFFSETS_PRESET_TOP_WIDE, - ANCHORS_AND_OFFSETS_PRESET_LEFT_WIDE, - ANCHORS_AND_OFFSETS_PRESET_RIGHT_WIDE, - ANCHORS_AND_OFFSETS_PRESET_BOTTOM_WIDE, - ANCHORS_AND_OFFSETS_PRESET_VCENTER_WIDE, - ANCHORS_AND_OFFSETS_PRESET_HCENTER_WIDE, - ANCHORS_AND_OFFSETS_PRESET_FULL_RECT, - - ANCHORS_AND_OFFSETS_PRESET_KEEP_RATIO, - - ANCHORS_PRESET_TOP_LEFT, - ANCHORS_PRESET_TOP_RIGHT, - ANCHORS_PRESET_BOTTOM_LEFT, - ANCHORS_PRESET_BOTTOM_RIGHT, - ANCHORS_PRESET_CENTER_LEFT, - ANCHORS_PRESET_CENTER_RIGHT, - ANCHORS_PRESET_CENTER_TOP, - ANCHORS_PRESET_CENTER_BOTTOM, - ANCHORS_PRESET_CENTER, - ANCHORS_PRESET_TOP_WIDE, - ANCHORS_PRESET_LEFT_WIDE, - ANCHORS_PRESET_RIGHT_WIDE, - ANCHORS_PRESET_BOTTOM_WIDE, - ANCHORS_PRESET_VCENTER_WIDE, - ANCHORS_PRESET_HCENTER_WIDE, - ANCHORS_PRESET_FULL_RECT, - - // Offsets Presets are not currently in use. - OFFSETS_PRESET_TOP_LEFT, - OFFSETS_PRESET_TOP_RIGHT, - OFFSETS_PRESET_BOTTOM_LEFT, - OFFSETS_PRESET_BOTTOM_RIGHT, - OFFSETS_PRESET_CENTER_LEFT, - OFFSETS_PRESET_CENTER_RIGHT, - OFFSETS_PRESET_CENTER_TOP, - OFFSETS_PRESET_CENTER_BOTTOM, - OFFSETS_PRESET_CENTER, - OFFSETS_PRESET_TOP_WIDE, - OFFSETS_PRESET_LEFT_WIDE, - OFFSETS_PRESET_RIGHT_WIDE, - OFFSETS_PRESET_BOTTOM_WIDE, - OFFSETS_PRESET_VCENTER_WIDE, - OFFSETS_PRESET_HCENTER_WIDE, - OFFSETS_PRESET_FULL_RECT, - - CONTAINERS_H_PRESET_FILL, - CONTAINERS_H_PRESET_FILL_EXPAND, - CONTAINERS_H_PRESET_SHRINK_BEGIN, - CONTAINERS_H_PRESET_SHRINK_CENTER, - CONTAINERS_H_PRESET_SHRINK_END, - CONTAINERS_V_PRESET_FILL, - CONTAINERS_V_PRESET_FILL_EXPAND, - CONTAINERS_V_PRESET_SHRINK_BEGIN, - CONTAINERS_V_PRESET_SHRINK_CENTER, - CONTAINERS_V_PRESET_SHRINK_END, - }; - - MenuButton *anchor_presets_menu = nullptr; - PopupMenu *anchors_popup = nullptr; - MenuButton *container_h_presets_menu = nullptr; - MenuButton *container_v_presets_menu = nullptr; - + ControlEditorPopupButton *anchors_button = nullptr; + ControlEditorPopupButton *containers_button = nullptr; Button *anchor_mode_button = nullptr; + SizeFlagPresetPicker *container_h_picker = nullptr; + SizeFlagPresetPicker *container_v_picker = nullptr; + bool anchors_mode = false; - void _set_anchors_preset(Control::LayoutPreset p_preset); - void _set_anchors_and_offsets_preset(Control::LayoutPreset p_preset); - void _set_anchors_and_offsets_to_keep_ratio(); - void _set_container_h_preset(Control::SizeFlags p_preset); - void _set_container_v_preset(Control::SizeFlags p_preset); + void _anchors_preset_selected(int p_preset); + void _anchors_to_current_ratio(); + void _anchor_mode_toggled(bool p_status); + void _container_flags_selected(int p_flags, bool p_vertical); Vector2 _anchor_to_position(const Control *p_control, Vector2 anchor); Vector2 _position_to_anchor(const Control *p_control, Vector2 position); - - void _button_toggle_anchor_mode(bool p_status); - bool _is_node_locked(const Node *p_node); - List<Control *> _get_edited_controls(bool retrieve_locked = false, bool remove_controls_if_parent_in_selection = true); - void _popup_callback(int p_op); + List<Control *> _get_edited_controls(); void _selection_changed(); protected: @@ -236,6 +241,7 @@ public: ControlEditorToolbar(); }; +// Editor plugin. class ControlEditorPlugin : public EditorPlugin { GDCLASS(ControlEditorPlugin, EditorPlugin); diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp index a7c3c32120..e56fd5dfe3 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -34,9 +34,10 @@ #include "core/io/image_loader.h" #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/2d/cpu_particles_2d.h" #include "scene/gui/separator.h" -#include "scene/resources/particles_material.h" +#include "scene/resources/particle_process_material.h" void CPUParticles2DEditorPlugin::edit(Object *p_object) { particles = Object::cast_to<CPUParticles2D>(p_object); diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h index cc59bc924f..06ca208463 100644 --- a/editor/plugins/cpu_particles_2d_editor_plugin.h +++ b/editor/plugins/cpu_particles_2d_editor_plugin.h @@ -39,6 +39,7 @@ class EditorPlugin; class SpinBox; class EditorFileDialog; +class EditorUndoRedoManager; class CPUParticles2DEditorPlugin : public EditorPlugin { GDCLASS(CPUParticles2DEditorPlugin, EditorPlugin); @@ -70,7 +71,7 @@ class CPUParticles2DEditorPlugin : public EditorPlugin { String source_emission_file; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; void _file_selected(const String &p_file); void _menu_callback(int p_idx); void _generate_emission_mask(); diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 8aeab684e3..013a9f10a4 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -139,14 +139,14 @@ void CurveEditor::gui_input(const Ref<InputEvent> &p_event) { if (!mb.is_pressed() && _dragging && mb.get_button_index() == MouseButton::LEFT) { _dragging = false; if (_has_undo_data) { - UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); - ur.create_action(_selected_tangent == TANGENT_NONE ? TTR("Modify Curve Point") : TTR("Modify Curve Tangent")); - ur.add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data()); - ur.add_undo_method(*_curve_ref, "_set_data", _undo_data); + ur->create_action(_selected_tangent == TANGENT_NONE ? TTR("Modify Curve Point") : TTR("Modify Curve Tangent")); + ur->add_do_method(*_curve_ref, "_set_data", _curve_ref->get_data()); + ur->add_undo_method(*_curve_ref, "_set_data", _undo_data); // Note: this will trigger one more "changed" signal even if nothing changes, // but it's ok since it would have fired every frame during the drag anyways - ur.commit_action(); + ur->commit_action(); _has_undo_data = false; } @@ -301,13 +301,13 @@ void CurveEditor::on_preset_item_selected(int preset_id) { break; } - UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo(); - ur.create_action(TTR("Load Curve Preset")); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + ur->create_action(TTR("Load Curve Preset")); - ur.add_do_method(&curve, "_set_data", curve.get_data()); - ur.add_undo_method(&curve, "_set_data", previous_data); + ur->add_do_method(&curve, "_set_data", curve.get_data()); + ur->add_undo_method(&curve, "_set_data", previous_data); - ur.commit_action(); + ur->commit_action(); } void CurveEditor::_curve_changed() { @@ -435,8 +435,8 @@ CurveEditor::TangentIndex CurveEditor::get_tangent_at(Vector2 pos) const { void CurveEditor::add_point(Vector2 pos) { ERR_FAIL_COND(_curve_ref.is_null()); - UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo(); - ur.create_action(TTR("Remove Curve Point")); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + ur->create_action(TTR("Remove Curve Point")); Vector2 point_pos = get_world_pos(pos); if (point_pos.y < 0.0) { @@ -449,22 +449,22 @@ void CurveEditor::add_point(Vector2 pos) { int i = _curve_ref->add_point(point_pos); _curve_ref->remove_point(i); - ur.add_do_method(*_curve_ref, "add_point", point_pos); - ur.add_undo_method(*_curve_ref, "remove_point", i); + ur->add_do_method(*_curve_ref, "add_point", point_pos); + ur->add_undo_method(*_curve_ref, "remove_point", i); - ur.commit_action(); + ur->commit_action(); } void CurveEditor::remove_point(int index) { ERR_FAIL_COND(_curve_ref.is_null()); - UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo(); - ur.create_action(TTR("Remove Curve Point")); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + ur->create_action(TTR("Remove Curve Point")); Curve::Point p = _curve_ref->get_point(index); - ur.add_do_method(*_curve_ref, "remove_point", index); - ur.add_undo_method(*_curve_ref, "add_point", p.position, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode); + ur->add_do_method(*_curve_ref, "remove_point", index); + ur->add_undo_method(*_curve_ref, "add_point", p.position, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode); if (index == _selected_point) { set_selected_point(-1); @@ -474,14 +474,14 @@ void CurveEditor::remove_point(int index) { set_hover_point_index(-1); } - ur.commit_action(); + ur->commit_action(); } void CurveEditor::toggle_linear(TangentIndex tangent) { ERR_FAIL_COND(_curve_ref.is_null()); - UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo(); - ur.create_action(TTR("Toggle Curve Linear Tangent")); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + ur->create_action(TTR("Toggle Curve Linear Tangent")); if (tangent == TANGENT_NONE) { tangent = _selected_tangent; @@ -493,8 +493,8 @@ void CurveEditor::toggle_linear(TangentIndex tangent) { Curve::TangentMode prev_mode = _curve_ref->get_point_left_mode(_selected_point); Curve::TangentMode mode = is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR; - ur.add_do_method(*_curve_ref, "set_point_left_mode", _selected_point, mode); - ur.add_undo_method(*_curve_ref, "set_point_left_mode", _selected_point, prev_mode); + ur->add_do_method(*_curve_ref, "set_point_left_mode", _selected_point, mode); + ur->add_undo_method(*_curve_ref, "set_point_left_mode", _selected_point, prev_mode); } else { bool is_linear = _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR; @@ -502,11 +502,11 @@ void CurveEditor::toggle_linear(TangentIndex tangent) { Curve::TangentMode prev_mode = _curve_ref->get_point_right_mode(_selected_point); Curve::TangentMode mode = is_linear ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR; - ur.add_do_method(*_curve_ref, "set_point_right_mode", _selected_point, mode); - ur.add_undo_method(*_curve_ref, "set_point_right_mode", _selected_point, prev_mode); + ur->add_do_method(*_curve_ref, "set_point_right_mode", _selected_point, mode); + ur->add_undo_method(*_curve_ref, "set_point_right_mode", _selected_point, prev_mode); } - ur.commit_action(); + ur->commit_action(); } void CurveEditor::set_selected_point(int index) { diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp index c572b5b7fe..40e7dfead2 100644 --- a/editor/plugins/debugger_editor_plugin.cpp +++ b/editor/plugins/debugger_editor_plugin.cpp @@ -40,7 +40,7 @@ #include "editor/plugins/script_editor_plugin.h" #include "scene/gui/menu_button.h" -DebuggerEditorPlugin::DebuggerEditorPlugin(MenuButton *p_debug_menu) { +DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) { EditorDebuggerServer::initialize(); ED_SHORTCUT("debugger/step_into", TTR("Step Into"), Key::F11); @@ -61,30 +61,29 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(MenuButton *p_debug_menu) { // Main editor debug menu. debug_menu = p_debug_menu; - PopupMenu *p = debug_menu->get_popup(); - p->set_hide_on_checkable_item_selection(false); - p->add_check_shortcut(ED_SHORTCUT("editor/deploy_with_remote_debug", TTR("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG); - p->set_item_tooltip(-1, + debug_menu->set_hide_on_checkable_item_selection(false); + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/deploy_with_remote_debug", TTR("Deploy with Remote Debug")), RUN_DEPLOY_REMOTE_DEBUG); + debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, using one-click deploy will make the executable attempt to connect to this computer's IP so the running project can be debugged.\nThis option is intended to be used for remote debugging (typically with a mobile device).\nYou don't need to enable it to use the GDScript debugger locally.")); - p->add_check_shortcut(ED_SHORTCUT("editor/small_deploy_with_network_fs", TTR("Small Deploy with Network Filesystem")), RUN_FILE_SERVER); - p->set_item_tooltip(-1, + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/small_deploy_with_network_fs", TTR("Small Deploy with Network Filesystem")), RUN_FILE_SERVER); + debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, using one-click deploy for Android will only export an executable without the project data.\nThe filesystem will be provided from the project by the editor over the network.\nOn Android, deploying will use the USB cable for faster performance. This option speeds up testing for projects with large assets.")); - p->add_separator(); - p->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISONS); - p->set_item_tooltip(-1, + debug_menu->add_separator(); + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_collision_shapes", TTR("Visible Collision Shapes")), RUN_DEBUG_COLLISONS); + debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, collision shapes and raycast nodes (for 2D and 3D) will be visible in the running project.")); - p->add_check_shortcut(ED_SHORTCUT("editor/visible_paths", TTR("Visible Paths")), RUN_DEBUG_PATHS); - p->set_item_tooltip(-1, + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_paths", TTR("Visible Paths")), RUN_DEBUG_PATHS); + debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, curve resources used by path nodes will be visible in the running project.")); - p->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION); - p->set_item_tooltip(-1, + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/visible_navigation", TTR("Visible Navigation")), RUN_DEBUG_NAVIGATION); + debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, navigation meshes and polygons will be visible in the running project.")); - p->add_separator(); - p->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Synchronize Scene Changes")), RUN_LIVE_DEBUG); - p->set_item_tooltip(-1, + debug_menu->add_separator(); + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_scene_changes", TTR("Synchronize Scene Changes")), RUN_LIVE_DEBUG); + debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, any changes made to the scene in the editor will be replicated in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled.")); - p->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Synchronize Script Changes")), RUN_RELOAD_SCRIPTS); - p->set_item_tooltip(-1, + debug_menu->add_check_shortcut(ED_SHORTCUT("editor/sync_script_changes", TTR("Synchronize Script Changes")), RUN_RELOAD_SCRIPTS); + debug_menu->set_item_tooltip(-1, TTR("When this option is enabled, any script that is saved will be reloaded in the running project.\nWhen used remotely on a device, this is more efficient when the network filesystem option is enabled.")); // Multi-instance, start/stop @@ -92,9 +91,9 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(MenuButton *p_debug_menu) { instances_menu->set_name("run_instances"); instances_menu->set_hide_on_checkable_item_selection(false); - p->add_child(instances_menu); - p->add_separator(); - p->add_submenu_item(TTR("Run Multiple Instances"), "run_instances"); + debug_menu->add_child(instances_menu); + debug_menu->add_separator(); + debug_menu->add_submenu_item(TTR("Run Multiple Instances"), "run_instances"); instances_menu->add_radio_check_item(TTR("Run 1 Instance")); instances_menu->set_item_metadata(0, 1); @@ -106,7 +105,7 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(MenuButton *p_debug_menu) { instances_menu->set_item_metadata(3, 4); instances_menu->set_item_checked(0, true); instances_menu->connect("index_pressed", callable_mp(this, &DebuggerEditorPlugin::_select_run_count)); - p->connect("id_pressed", callable_mp(this, &DebuggerEditorPlugin::_menu_option)); + debug_menu->connect("id_pressed", callable_mp(this, &DebuggerEditorPlugin::_menu_option)); } DebuggerEditorPlugin::~DebuggerEditorPlugin() { @@ -125,7 +124,7 @@ void DebuggerEditorPlugin::_select_run_count(int p_index) { void DebuggerEditorPlugin::_menu_option(int p_option) { switch (p_option) { case RUN_FILE_SERVER: { - bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_FILE_SERVER)); + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_FILE_SERVER)); if (ischecked) { file_server->stop(); @@ -133,45 +132,45 @@ void DebuggerEditorPlugin::_menu_option(int p_option) { file_server->start(); } - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_FILE_SERVER), !ischecked); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_FILE_SERVER), !ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_file_server", !ischecked); } break; case RUN_LIVE_DEBUG: { - bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG)); + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_LIVE_DEBUG)); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_LIVE_DEBUG), !ischecked); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_LIVE_DEBUG), !ischecked); EditorDebuggerNode::get_singleton()->set_live_debugging(!ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_live_debug", !ischecked); } break; case RUN_DEPLOY_REMOTE_DEBUG: { - bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEPLOY_REMOTE_DEBUG)); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEPLOY_REMOTE_DEBUG), !ischecked); + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEPLOY_REMOTE_DEBUG)); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEPLOY_REMOTE_DEBUG), !ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_deploy_remote_debug", !ischecked); } break; case RUN_DEBUG_COLLISONS: { - bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_COLLISONS)); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_COLLISONS), !ischecked); + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_COLLISONS)); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_COLLISONS), !ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_collisons", !ischecked); } break; case RUN_DEBUG_PATHS: { - bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_PATHS)); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_PATHS), !ischecked); + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_PATHS)); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_PATHS), !ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_paths", !ischecked); } break; case RUN_DEBUG_NAVIGATION: { - bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION)); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_DEBUG_NAVIGATION), !ischecked); + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_DEBUG_NAVIGATION)); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_DEBUG_NAVIGATION), !ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_debug_navigation", !ischecked); } break; case RUN_RELOAD_SCRIPTS: { - bool ischecked = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(RUN_RELOAD_SCRIPTS)); - debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(RUN_RELOAD_SCRIPTS), !ischecked); + bool ischecked = debug_menu->is_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS)); + debug_menu->set_item_checked(debug_menu->get_item_index(RUN_RELOAD_SCRIPTS), !ischecked); ScriptEditor::get_singleton()->set_live_auto_reload_running_scripts(!ischecked); EditorSettings::get_singleton()->set_project_metadata("debug_options", "run_reload_scripts", !ischecked); diff --git a/editor/plugins/debugger_editor_plugin.h b/editor/plugins/debugger_editor_plugin.h index fb963385cd..d8871128c3 100644 --- a/editor/plugins/debugger_editor_plugin.h +++ b/editor/plugins/debugger_editor_plugin.h @@ -41,7 +41,7 @@ class DebuggerEditorPlugin : public EditorPlugin { GDCLASS(DebuggerEditorPlugin, EditorPlugin); private: - MenuButton *debug_menu = nullptr; + PopupMenu *debug_menu = nullptr; EditorFileServer *file_server = nullptr; PopupMenu *instances_menu = nullptr; @@ -64,7 +64,7 @@ public: virtual String get_name() const override { return "Debugger"; } bool has_main_screen() const override { return false; } - DebuggerEditorPlugin(MenuButton *p_menu); + DebuggerEditorPlugin(PopupMenu *p_menu); ~DebuggerEditorPlugin(); }; diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp index cadb974345..9b03b3ae09 100644 --- a/editor/plugins/font_config_plugin.cpp +++ b/editor/plugins/font_config_plugin.cpp @@ -100,11 +100,6 @@ bool EditorPropertyFontOTObject::_get(const StringName &p_name, Variant &r_ret) return false; } -void EditorPropertyFontOTObject::_bind_methods() { - ClassDB::bind_method(D_METHOD("property_can_revert", "name"), &EditorPropertyFontOTObject::property_can_revert); - ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &EditorPropertyFontOTObject::property_get_revert); -} - void EditorPropertyFontOTObject::set_dict(const Dictionary &p_dict) { dict = p_dict; } @@ -121,7 +116,7 @@ Dictionary EditorPropertyFontOTObject::get_defaults() { return defaults_dict; } -bool EditorPropertyFontOTObject::property_can_revert(const String &p_name) { +bool EditorPropertyFontOTObject::_property_can_revert(const StringName &p_name) const { String name = p_name; if (name.begins_with("keys")) { @@ -136,18 +131,19 @@ bool EditorPropertyFontOTObject::property_can_revert(const String &p_name) { return false; } -Variant EditorPropertyFontOTObject::property_get_revert(const String &p_name) { +bool EditorPropertyFontOTObject::_property_get_revert(const StringName &p_name, Variant &r_property) const { String name = p_name; if (name.begins_with("keys")) { int key = name.get_slicec('/', 1).to_int(); if (defaults_dict.has(key)) { Vector3i range = defaults_dict[key]; - return range.z; + r_property = range.z; + return true; } } - return Variant(); + return false; } /*************************************************************************/ @@ -156,7 +152,6 @@ Variant EditorPropertyFontOTObject::property_get_revert(const String &p_name) { void EditorPropertyFontMetaOverride::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (Object::cast_to<Button>(button_add)) { button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); @@ -294,7 +289,7 @@ void EditorPropertyFontMetaOverride::update_property() { } else { prop->set_label(TranslationServer::get_singleton()->get_locale_name(name)); } - prop->set_tooltip(name); + prop->set_tooltip_text(name); prop->set_selectable(false); prop->connect("property_changed", callable_mp(this, &EditorPropertyFontMetaOverride::_property_changed)); @@ -384,14 +379,6 @@ EditorPropertyFontMetaOverride::EditorPropertyFontMetaOverride(bool p_script) { /* EditorPropertyOTVariation */ /*************************************************************************/ -void EditorPropertyOTVariation::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: - case NOTIFICATION_THEME_CHANGED: { - } break; - } -} - void EditorPropertyOTVariation::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) { if (p_property.begins_with("keys")) { Dictionary dict = object->get_dict(); @@ -491,7 +478,7 @@ void EditorPropertyOTVariation::update_property() { String name = TS->tag_to_name(name_tag); prop->set_label(name.capitalize()); - prop->set_tooltip(name); + prop->set_tooltip_text(name); prop->set_selectable(false); prop->connect("property_changed", callable_mp(this, &EditorPropertyOTVariation::_property_changed)); @@ -551,7 +538,6 @@ EditorPropertyOTVariation::EditorPropertyOTVariation() { void EditorPropertyOTFeatures::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { if (Object::cast_to<Button>(button_add)) { button_add->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); @@ -770,7 +756,7 @@ void EditorPropertyOTFeatures::update_property() { disp_name = vformat("%s (%s)", disp_name, info["label"].operator String()); } prop->set_label(disp_name); - prop->set_tooltip(name); + prop->set_tooltip_text(name); prop->set_selectable(false); prop->connect("property_changed", callable_mp(this, &EditorPropertyOTFeatures::_property_changed)); diff --git a/editor/plugins/font_config_plugin.h b/editor/plugins/font_config_plugin.h index 3eaa2fdc17..ae138bab8f 100644 --- a/editor/plugins/font_config_plugin.h +++ b/editor/plugins/font_config_plugin.h @@ -66,7 +66,8 @@ class EditorPropertyFontOTObject : public RefCounted { protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; - static void _bind_methods(); + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; public: void set_dict(const Dictionary &p_dict); @@ -75,9 +76,6 @@ public: void set_defaults(const Dictionary &p_dict); Dictionary get_defaults(); - bool property_can_revert(const String &p_name); - Variant property_get_revert(const String &p_name); - EditorPropertyFontOTObject(){}; }; @@ -141,7 +139,6 @@ class EditorPropertyOTVariation : public EditorProperty { EditorPaginator *paginator = nullptr; protected: - void _notification(int p_what); static void _bind_methods(){}; void _edit_pressed(); diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp index 8e6687c836..e2d19c34e6 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp @@ -34,10 +34,11 @@ #include "core/io/image_loader.h" #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/scene_tree_dock.h" #include "scene/2d/cpu_particles_2d.h" #include "scene/gui/separator.h" -#include "scene/resources/particles_material.h" +#include "scene/resources/particle_process_material.h" void GPUParticles2DEditorPlugin::edit(Object *p_object) { particles = Object::cast_to<GPUParticles2D>(p_object); @@ -111,7 +112,7 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) { cpu_particles->set_process_mode(particles->get_process_mode()); cpu_particles->set_z_index(particles->get_z_index()); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Convert to CPUParticles2D")); ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false); ur->add_do_reference(cpu_particles); @@ -166,9 +167,9 @@ void GPUParticles2DEditorPlugin::_generate_visibility_rect() { } void GPUParticles2DEditorPlugin::_generate_emission_mask() { - Ref<ParticlesMaterial> pm = particles->get_process_material(); + Ref<ParticleProcessMaterial> pm = particles->get_process_material(); if (!pm.is_valid()) { - EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticlesMaterial process material")); + EditorNode::get_singleton()->show_warning(TTR("Can only set point into a ParticleProcessMaterial process material")); return; } @@ -319,7 +320,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { } if (valid_normals.size()) { - pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS); + pm->set_emission_shape(ParticleProcessMaterial::EMISSION_SHAPE_DIRECTED_POINTS); Vector<uint8_t> normdata; normdata.resize(w * h * 2 * sizeof(float)); //use RG texture @@ -338,7 +339,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() { pm->set_emission_normal_texture(ImageTexture::create_from_image(img)); } else { - pm->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS); + pm->set_emission_shape(ParticleProcessMaterial::EMISSION_SHAPE_POINTS); } } diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h index bf49a82166..0229b57c10 100644 --- a/editor/plugins/gpu_particles_2d_editor_plugin.h +++ b/editor/plugins/gpu_particles_2d_editor_plugin.h @@ -38,6 +38,7 @@ #include "scene/gui/spin_box.h" class EditorFileDialog; +class EditorUndoRedoManager; class GPUParticles2DEditorPlugin : public EditorPlugin { GDCLASS(GPUParticles2DEditorPlugin, EditorPlugin); @@ -75,7 +76,7 @@ class GPUParticles2DEditorPlugin : public EditorPlugin { String source_emission_file; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; void _file_selected(const String &p_file); void _menu_callback(int p_idx); void _generate_visibility_rect(); diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp index 6750f1aa9c..ebc92bf531 100644 --- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp @@ -32,10 +32,11 @@ #include "core/io/resource_loader.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "editor/scene_tree_dock.h" #include "scene/3d/cpu_particles_3d.h" -#include "scene/resources/particles_material.h" +#include "scene/resources/particle_process_material.h" bool GPUParticles3DEditorBase::_generate(Vector<Vector3> &points, Vector<Vector3> &normals) { bool use_normals = emission_fill->get_selected() == 1; @@ -254,9 +255,9 @@ void GPUParticles3DEditor::_menu_option(int p_option) { } } break; case MENU_OPTION_CREATE_EMISSION_VOLUME_FROM_NODE: { - Ref<ParticlesMaterial> material = node->get_process_material(); + Ref<ParticleProcessMaterial> material = node->get_process_material(); if (material.is_null()) { - EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticlesMaterial' is required.")); + EditorNode::get_singleton()->show_warning(TTR("A processor material of type 'ParticleProcessMaterial' is required.")); return; } @@ -271,7 +272,7 @@ void GPUParticles3DEditor::_menu_option(int p_option) { cpu_particles->set_visible(node->is_visible()); cpu_particles->set_process_mode(node->get_process_mode()); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Convert to CPUParticles3D")); ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false); ur->add_do_reference(cpu_particles); @@ -321,7 +322,7 @@ void GPUParticles3DEditor::_generate_aabb() { node->set_emitting(false); } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Generate Visibility AABB")); ur->add_do_method(node, "set_visibility_aabb", rect); ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb()); @@ -365,11 +366,11 @@ void GPUParticles3DEditor::_generate_emission_points() { Ref<Image> image = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img)); Ref<ImageTexture> tex = ImageTexture::create_from_image(image); - Ref<ParticlesMaterial> material = node->get_process_material(); + Ref<ParticleProcessMaterial> material = node->get_process_material(); ERR_FAIL_COND(material.is_null()); if (normals.size() > 0) { - material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_DIRECTED_POINTS); + material->set_emission_shape(ParticleProcessMaterial::EMISSION_SHAPE_DIRECTED_POINTS); material->set_emission_point_count(point_count); material->set_emission_point_texture(tex); @@ -391,7 +392,7 @@ void GPUParticles3DEditor::_generate_emission_points() { Ref<Image> image2 = memnew(Image(w, h, false, Image::FORMAT_RGBF, point_img2)); material->set_emission_normal_texture(ImageTexture::create_from_image(image2)); } else { - material->set_emission_shape(ParticlesMaterial::EMISSION_SHAPE_POINTS); + material->set_emission_shape(ParticleProcessMaterial::EMISSION_SHAPE_POINTS); material->set_emission_point_count(point_count); material->set_emission_point_texture(tex); } diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp index b54cb515e4..59d665342f 100644 --- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp +++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp @@ -101,7 +101,7 @@ void GPUParticlesCollisionSDF3DEditorPlugin::_notification(int p_what) { return; } - bake->set_tooltip(text); + bake->set_tooltip_text(text); } break; } } diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index 542aee879b..f368d5bea1 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -34,6 +34,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "node_3d_editor_plugin.h" Size2 GradientEditor::get_minimum_size() const { @@ -55,7 +56,7 @@ void GradientEditor::_gradient_changed() { void GradientEditor::_ramp_changed() { editing = true; - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Gradient Edited"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets()); undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors()); @@ -134,7 +135,7 @@ void EditorInspectorPluginGradient::parse_begin(Object *p_object) { add_custom_control(gradient_tools_hbox); reverse_btn->connect("pressed", callable_mp(this, &EditorInspectorPluginGradient::_reverse_button_pressed)); - reverse_btn->set_tooltip(TTR("Reverse/mirror gradient.")); + reverse_btn->set_tooltip_text(TTR("Reverse/mirror gradient.")); } void EditorInspectorPluginGradient::_reverse_button_pressed() { diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.cpp b/editor/plugins/gradient_texture_2d_editor_plugin.cpp index df45d6c290..9ebca9fb31 100644 --- a/editor/plugins/gradient_texture_2d_editor_plugin.cpp +++ b/editor/plugins/gradient_texture_2d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/box_container.h" #include "scene/gui/flow_container.h" #include "scene/gui/separator.h" @@ -103,7 +104,6 @@ void GradientTexture2DEditorRect::set_snap_size(float p_snap_size) { void GradientTexture2DEditorRect::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { checkerboard->set_texture(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons"))); } break; @@ -175,7 +175,7 @@ void GradientTexture2DEditorRect::_notification(int p_what) { } GradientTexture2DEditorRect::GradientTexture2DEditorRect() { - undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo = EditorNode::get_undo_redo(); checkerboard = memnew(TextureRect); checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE); @@ -213,7 +213,6 @@ void GradientTexture2DEditor::set_texture(Ref<GradientTexture2D> &p_texture) { void GradientTexture2DEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { reverse_button->set_icon(get_theme_icon(SNAME("ReverseGradient"), SNAME("EditorIcons"))); snap_button->set_icon(get_theme_icon(SNAME("SnapGrid"), SNAME("EditorIcons"))); @@ -222,20 +221,20 @@ void GradientTexture2DEditor::_notification(int p_what) { } GradientTexture2DEditor::GradientTexture2DEditor() { - undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo = EditorNode::get_undo_redo(); HFlowContainer *toolbar = memnew(HFlowContainer); add_child(toolbar); reverse_button = memnew(Button); - reverse_button->set_tooltip(TTR("Swap Gradient Fill Points")); + reverse_button->set_tooltip_text(TTR("Swap Gradient Fill Points")); toolbar->add_child(reverse_button); reverse_button->connect("pressed", callable_mp(this, &GradientTexture2DEditor::_reverse_button_pressed)); toolbar->add_child(memnew(VSeparator)); snap_button = memnew(Button); - snap_button->set_tooltip(TTR("Toggle Grid Snap")); + snap_button->set_tooltip_text(TTR("Toggle Grid Snap")); snap_button->set_toggle_mode(true); toolbar->add_child(snap_button); snap_button->connect("toggled", callable_mp(this, &GradientTexture2DEditor::_set_snap_enabled)); diff --git a/editor/plugins/gradient_texture_2d_editor_plugin.h b/editor/plugins/gradient_texture_2d_editor_plugin.h index 93c49b1e6f..9faf33152a 100644 --- a/editor/plugins/gradient_texture_2d_editor_plugin.h +++ b/editor/plugins/gradient_texture_2d_editor_plugin.h @@ -34,6 +34,8 @@ #include "editor/editor_plugin.h" #include "editor/editor_spin_slider.h" +class EditorUndoRedoManager; + class GradientTexture2DEditorRect : public Control { GDCLASS(GradientTexture2DEditorRect, Control); @@ -44,7 +46,7 @@ class GradientTexture2DEditorRect : public Control { }; Ref<GradientTexture2D> texture; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; bool snap_enabled = false; float snap_size = 0; @@ -74,7 +76,7 @@ class GradientTexture2DEditor : public VBoxContainer { GDCLASS(GradientTexture2DEditor, VBoxContainer); Ref<GradientTexture2D> texture; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; Button *reverse_button = nullptr; Button *snap_button = nullptr; diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp index 153eab32d2..6b323e0e57 100644 --- a/editor/plugins/input_event_editor_plugin.cpp +++ b/editor/plugins/input_event_editor_plugin.cpp @@ -35,7 +35,6 @@ void InputEventConfigContainer::_bind_methods() { void InputEventConfigContainer::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { open_config_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); } break; diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index 1b4d98fc3f..74a6e90a6d 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -33,9 +33,10 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/subviewport_container.h" #include "scene/resources/fog_material.h" -#include "scene/resources/particles_material.h" +#include "scene/resources/particle_process_material.h" #include "scene/resources/sky_material.h" void MaterialEditor::_notification(int p_what) { @@ -178,8 +179,8 @@ MaterialEditor::MaterialEditor() { viewport->add_child(box_instance); Transform3D box_xform; - box_xform.basis.rotate(Vector3(1, 0, 0), Math::deg2rad(25.0)); - box_xform.basis = box_xform.basis * Basis().rotated(Vector3(0, 1, 0), Math::deg2rad(-25.0)); + box_xform.basis.rotate(Vector3(1, 0, 0), Math::deg_to_rad(25.0)); + box_xform.basis = box_xform.basis * Basis().rotated(Vector3(0, 1, 0), Math::deg_to_rad(-25.0)); box_xform.basis.scale(Vector3(0.7, 0.7, 0.7)); box_xform.origin.y = 0.05; box_instance->set_transform(box_xform); @@ -261,10 +262,8 @@ void EditorInspectorPluginMaterial::parse_begin(Object *p_object) { } void EditorInspectorPluginMaterial::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) { - UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo); - if (!undo_redo) { - return; - } + Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo); + ERR_FAIL_COND(!undo_redo.is_valid()); // For BaseMaterial3D, if a roughness or metallic textures is being assigned to an empty slot, // set the respective metallic or roughness factor to 1.0 as a convenience feature @@ -406,17 +405,17 @@ Ref<Resource> ORMMaterial3DConversionPlugin::convert(const Ref<Resource> &p_reso return smat; } -String ParticlesMaterialConversionPlugin::converts_to() const { +String ParticleProcessMaterialConversionPlugin::converts_to() const { return "ShaderMaterial"; } -bool ParticlesMaterialConversionPlugin::handles(const Ref<Resource> &p_resource) const { - Ref<ParticlesMaterial> mat = p_resource; +bool ParticleProcessMaterialConversionPlugin::handles(const Ref<Resource> &p_resource) const { + Ref<ParticleProcessMaterial> mat = p_resource; return mat.is_valid(); } -Ref<Resource> ParticlesMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) const { - Ref<ParticlesMaterial> mat = p_resource; +Ref<Resource> ParticleProcessMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) const { + Ref<ParticleProcessMaterial> mat = p_resource; ERR_FAIL_COND_V(!mat.is_valid(), Ref<Resource>()); Ref<ShaderMaterial> smat; diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index fc3da5fd9f..06ae43e6d7 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -122,8 +122,8 @@ public: virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const override; }; -class ParticlesMaterialConversionPlugin : public EditorResourceConversionPlugin { - GDCLASS(ParticlesMaterialConversionPlugin, EditorResourceConversionPlugin); +class ParticleProcessMaterialConversionPlugin : public EditorResourceConversionPlugin { + GDCLASS(ParticleProcessMaterialConversionPlugin, EditorResourceConversionPlugin); public: virtual String converts_to() const override; diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index 31c9f1e387..980d2974a0 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -77,8 +77,8 @@ void MeshEditor::edit(Ref<Mesh> p_mesh) { mesh = p_mesh; mesh_instance->set_mesh(mesh); - rot_x = Math::deg2rad(-15.0); - rot_y = Math::deg2rad(30.0); + rot_x = Math::deg_to_rad(-15.0); + rot_y = Math::deg_to_rad(30.0); _update_rotation(); AABB aabb = mesh->get_aabb(); diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp index 5fb885ad1f..7bd406b869 100644 --- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "node_3d_editor_plugin.h" #include "scene/3d/collision_shape_3d.h" #include "scene/3d/navigation_region_3d.h" @@ -60,7 +61,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) { switch (p_option) { case MENU_OPTION_CREATE_STATIC_TRIMESH_BODY: { EditorSelection *editor_selection = EditorNode::get_singleton()->get_editor_selection(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); List<Node *> selection = editor_selection->get_selected_node_list(); @@ -147,7 +148,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) { Node *owner = get_tree()->get_edited_scene_root(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Create Trimesh Static Shape")); @@ -177,7 +178,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) { err_dialog->popup_centered(); return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); if (simplify) { ur->create_action(TTR("Create Simplified Convex Shape")); @@ -217,7 +218,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) { err_dialog->popup_centered(); return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Create Multiple Convex Shapes")); @@ -254,7 +255,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) { Node *owner = get_tree()->get_edited_scene_root(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Create Navigation Mesh")); ur->add_do_method(node, "add_child", nmi, true); @@ -311,7 +312,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) { return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Unwrap UV2")); ur->add_do_method(node, "set_mesh", unwrapped_mesh); @@ -470,7 +471,7 @@ void MeshInstance3DEditor::_create_outline_mesh() { mi->set_mesh(mesho); Node *owner = get_tree()->get_edited_scene_root(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_singleton()->get_undo_redo(); ur->create_action(TTR("Create Outline")); diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp index e8f143a637..878f8c9a95 100644 --- a/editor/plugins/node_3d_editor_gizmos.cpp +++ b/editor/plugins/node_3d_editor_gizmos.cpp @@ -35,6 +35,7 @@ #include "core/math/geometry_3d.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/node_3d_editor_plugin.h" #include "scene/3d/audio_listener_3d.h" #include "scene/3d/audio_stream_player_3d.h" @@ -51,14 +52,14 @@ #include "scene/3d/light_3d.h" #include "scene/3d/lightmap_gi.h" #include "scene/3d/lightmap_probe.h" +#include "scene/3d/marker_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/navigation_region_3d.h" #include "scene/3d/occluder_instance_3d.h" -#include "scene/3d/position_3d.h" #include "scene/3d/ray_cast_3d.h" #include "scene/3d/reflection_probe.h" #include "scene/3d/shape_cast_3d.h" -#include "scene/3d/soft_dynamic_body_3d.h" +#include "scene/3d/soft_body_3d.h" #include "scene/3d/spring_arm_3d.h" #include "scene/3d/sprite_3d.h" #include "scene/3d/vehicle_body_3d.h" @@ -1294,7 +1295,7 @@ static float _find_closest_angle_to_half_pi_arc(const Vector3 &p_from, const Vec //min_p = p_arc_xform.affine_inverse().xform(min_p); float a = (Math_PI * 0.5) - Vector2(min_p.x, -min_p.z).angle(); - return Math::rad2deg(a); + return Math::rad_to_deg(a); } void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, Camera3D *p_camera, const Point2 &p_point) { @@ -1347,13 +1348,13 @@ void Light3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_i light->set_param(p_id == 0 ? Light3D::PARAM_RANGE : Light3D::PARAM_SPOT_ANGLE, p_restore); } else if (p_id == 0) { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Light Radius")); ur->add_do_method(light, "set_param", Light3D::PARAM_RANGE, light->get_param(Light3D::PARAM_RANGE)); ur->add_undo_method(light, "set_param", Light3D::PARAM_RANGE, p_restore); ur->commit_action(); } else if (p_id == 1) { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Light Radius")); ur->add_do_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, light->get_param(Light3D::PARAM_SPOT_ANGLE)); ur->add_undo_method(light, "set_param", Light3D::PARAM_SPOT_ANGLE, p_restore); @@ -1420,8 +1421,8 @@ void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { for (int i = 0; i < 120; i++) { // Create a circle - const float ra = Math::deg2rad((float)(i * 3)); - const float rb = Math::deg2rad((float)((i + 1) * 3)); + const float ra = Math::deg_to_rad((float)(i * 3)); + const float rb = Math::deg_to_rad((float)((i + 1) * 3)); const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; @@ -1457,13 +1458,13 @@ void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { SpotLight3D *sl = Object::cast_to<SpotLight3D>(light); float r = sl->get_param(Light3D::PARAM_RANGE); - float w = r * Math::sin(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); - float d = r * Math::cos(Math::deg2rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); + float w = r * Math::sin(Math::deg_to_rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); + float d = r * Math::cos(Math::deg_to_rad(sl->get_param(Light3D::PARAM_SPOT_ANGLE))); for (int i = 0; i < 120; i++) { // Draw a circle - const float ra = Math::deg2rad((float)(i * 3)); - const float rb = Math::deg2rad((float)((i + 1) * 3)); + const float ra = Math::deg_to_rad((float)(i * 3)); + const float rb = Math::deg_to_rad((float)((i + 1) * 3)); const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; @@ -1544,8 +1545,8 @@ void AudioStreamPlayer3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo float closest_angle = 1e20; for (int i = 0; i < 180; i++) { - float a = Math::deg2rad((float)i); - float an = Math::deg2rad((float)(i + 1)); + float a = Math::deg_to_rad((float)i); + float an = Math::deg_to_rad((float)(i + 1)); Vector3 from(Math::sin(a), 0, -Math::cos(a)); Vector3 to(Math::sin(an), 0, -Math::cos(an)); @@ -1571,7 +1572,7 @@ void AudioStreamPlayer3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gi player->set_emission_angle(p_restore); } else { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change AudioStreamPlayer3D Emission Angle")); ur->add_do_method(player, "set_emission_angle", player->get_emission_angle()); ur->add_undo_method(player, "set_emission_angle", p_restore); @@ -1627,8 +1628,8 @@ void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { for (int i = 0; i < 120; i++) { // Create a circle. - const float ra = Math::deg2rad((float)(i * 3)); - const float rb = Math::deg2rad((float)((i + 1) * 3)); + const float ra = Math::deg_to_rad((float)(i * 3)); + const float rb = Math::deg_to_rad((float)((i + 1) * 3)); const Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; const Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; @@ -1670,8 +1671,8 @@ void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { if (player->is_emission_angle_enabled()) { const float pc = player->get_emission_angle(); - const float ofs = -Math::cos(Math::deg2rad(pc)); - const float radius = Math::sin(Math::deg2rad(pc)); + const float ofs = -Math::cos(Math::deg_to_rad(pc)); + const float radius = Math::sin(Math::deg_to_rad(pc)); Vector<Vector3> points_primary; points_primary.resize(200); @@ -1706,7 +1707,7 @@ void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->add_lines(points_secondary, material_secondary); Vector<Vector3> handles; - const float ha = Math::deg2rad(player->get_emission_angle()); + const float ha = Math::deg_to_rad(player->get_emission_angle()); handles.push_back(Vector3(Math::sin(ha), 0, -Math::cos(ha))); p_gizmo->add_handles(handles, get_material("handles")); } @@ -1814,7 +1815,7 @@ void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_ if (p_cancel) { camera->set("fov", p_restore); } else { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Camera FOV")); ur->add_do_property(camera, "fov", camera->get_fov()); ur->add_undo_property(camera, "fov", p_restore); @@ -1825,7 +1826,7 @@ void Camera3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_ if (p_cancel) { camera->set("size", p_restore); } else { - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Camera Size")); ur->add_do_property(camera, "size", camera->get_size()); ur->add_undo_property(camera, "size", p_restore); @@ -1871,7 +1872,7 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { // The real FOV is halved for accurate representation float fov = camera->get_fov() / 2.0; - Vector3 side = Vector3(Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov))); + Vector3 side = Vector3(Math::sin(Math::deg_to_rad(fov)), 0, -Math::cos(Math::deg_to_rad(fov))); Vector3 nside = side; nside.x = -nside.x; Vector3 up = Vector3(0, side.x, 0); @@ -1943,7 +1944,7 @@ MeshInstance3DGizmoPlugin::MeshInstance3DGizmoPlugin() { } bool MeshInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to<MeshInstance3D>(p_spatial) != nullptr && Object::cast_to<SoftDynamicBody3D>(p_spatial) == nullptr; + return Object::cast_to<MeshInstance3D>(p_spatial) != nullptr && Object::cast_to<SoftBody3D>(p_spatial) == nullptr; } String MeshInstance3DGizmoPlugin::get_gizmo_name() const { @@ -2141,7 +2142,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Sphere Shape Radius")); ur->add_do_method(so.ptr(), "set_radius", so->get_radius()); ur->add_undo_method(so.ptr(), "set_radius", p_restore); @@ -2155,7 +2156,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Box Shape Size")); ur->add_do_method(bo.ptr(), "set_size", bo->get_size()); ur->add_undo_method(bo.ptr(), "set_size", p_restore); @@ -2169,7 +2170,7 @@ void OccluderInstance3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_giz return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Box Shape Size")); ur->add_do_method(qo.ptr(), "set_size", qo->get_size()); ur->add_undo_method(qo.ptr(), "set_size", p_restore); @@ -2291,7 +2292,7 @@ void Label3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { /// -Position3DGizmoPlugin::Position3DGizmoPlugin() { +Marker3DGizmoPlugin::Marker3DGizmoPlugin() { pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh)); cursor_points = Vector<Vector3>(); @@ -2315,7 +2316,7 @@ Position3DGizmoPlugin::Position3DGizmoPlugin() { // Use the axis color which is brighter for the positive axis. // Use a darkened axis color for the negative axis. - // This makes it possible to see in which direction the Position3D node is rotated + // This makes it possible to see in which direction the Marker3D node is rotated // (which can be important depending on how it's used). const Color color_x = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("axis_x_color"), SNAME("Editor")); cursor_colors.push_back(color_x); @@ -2351,19 +2352,19 @@ Position3DGizmoPlugin::Position3DGizmoPlugin() { pos3d_mesh->surface_set_material(0, mat); } -bool Position3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to<Position3D>(p_spatial) != nullptr; +bool Marker3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to<Marker3D>(p_spatial) != nullptr; } -String Position3DGizmoPlugin::get_gizmo_name() const { - return "Position3D"; +String Marker3DGizmoPlugin::get_gizmo_name() const { + return "Marker3D"; } -int Position3DGizmoPlugin::get_priority() const { +int Marker3DGizmoPlugin::get_priority() const { return -1; } -void Position3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { +void Marker3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->clear(); p_gizmo->add_mesh(pos3d_mesh); p_gizmo->add_collision_segments(cursor_points); @@ -2642,8 +2643,8 @@ void VehicleWheel3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { float r = car_wheel->get_radius(); const int skip = 10; for (int i = 0; i <= 360; i += skip) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + skip); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + skip); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; @@ -2686,30 +2687,30 @@ void VehicleWheel3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { /////////// -SoftDynamicBody3DGizmoPlugin::SoftDynamicBody3DGizmoPlugin() { +SoftBody3DGizmoPlugin::SoftBody3DGizmoPlugin() { Color gizmo_color = EDITOR_GET("editors/3d_gizmos/gizmo_colors/shape"); create_material("shape_material", gizmo_color); create_handle_material("handles"); } -bool SoftDynamicBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { - return Object::cast_to<SoftDynamicBody3D>(p_spatial) != nullptr; +bool SoftBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { + return Object::cast_to<SoftBody3D>(p_spatial) != nullptr; } -String SoftDynamicBody3DGizmoPlugin::get_gizmo_name() const { - return "SoftDynamicBody3D"; +String SoftBody3DGizmoPlugin::get_gizmo_name() const { + return "SoftBody3D"; } -int SoftDynamicBody3DGizmoPlugin::get_priority() const { +int SoftBody3DGizmoPlugin::get_priority() const { return -1; } -bool SoftDynamicBody3DGizmoPlugin::is_selectable_when_hidden() const { +bool SoftBody3DGizmoPlugin::is_selectable_when_hidden() const { return true; } -void SoftDynamicBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { - SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node()); +void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { + SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node()); p_gizmo->clear(); @@ -2745,22 +2746,22 @@ void SoftDynamicBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->add_collision_triangles(tm); } -String SoftDynamicBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { - return "SoftDynamicBody3D pin point"; +String SoftBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { + return "SoftBody3D pin point"; } -Variant SoftDynamicBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { - SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node()); +Variant SoftBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { + SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node()); return Variant(soft_body->is_point_pinned(p_id)); } -void SoftDynamicBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) { - SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node()); +void SoftBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel) { + SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node()); soft_body->pin_point_toggle(p_id); } -bool SoftDynamicBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { - SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node()); +bool SoftBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const { + SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node()); return soft_body->is_point_pinned(p_id); } @@ -2870,7 +2871,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::commit_handle(const EditorNode3DGizmo return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Notifier AABB")); ur->add_do_method(notifier, "set_aabb", notifier->get_aabb()); ur->add_undo_method(notifier, "set_aabb", p_restore); @@ -3061,7 +3062,7 @@ void GPUParticles3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Particles AABB")); ur->add_do_method(particles, "set_visibility_aabb", particles->get_visibility_aabb()); ur->add_undo_method(particles, "set_visibility_aabb", p_restore); @@ -3227,7 +3228,7 @@ void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo * return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Radius")); ur->add_do_method(sn, "set_radius", sn->call("get_radius")); ur->add_undo_method(sn, "set_radius", p_restore); @@ -3240,7 +3241,7 @@ void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo * return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Box Shape Extents")); ur->add_do_method(sn, "set_extents", sn->call("get_extents")); ur->add_undo_method(sn, "set_extents", p_restore); @@ -3271,8 +3272,8 @@ void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector<Vector3> points; for (int i = 0; i <= 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 1); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; @@ -3499,7 +3500,7 @@ void ReflectionProbeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Probe Extents")); ur->add_do_method(probe, "set_extents", probe->get_extents()); ur->add_do_method(probe, "set_origin_offset", probe->get_origin_offset()); @@ -3651,7 +3652,7 @@ void DecalGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Decal Extents")); ur->add_do_method(decal, "set_extents", decal->get_extents()); ur->add_undo_method(decal, "set_extents", restore); @@ -3791,7 +3792,7 @@ void VoxelGIGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_i return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Probe Extents")); ur->add_do_method(probe, "set_extents", probe->get_extents()); ur->add_undo_method(probe, "set_extents", restore); @@ -4406,7 +4407,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Sphere Shape Radius")); ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); ur->add_undo_method(ss.ptr(), "set_radius", p_restore); @@ -4420,7 +4421,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Box Shape Size")); ur->add_do_method(ss.ptr(), "set_size", ss->get_size()); ur->add_undo_method(ss.ptr(), "set_size", p_restore); @@ -4437,7 +4438,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); if (p_id == 0) { ur->create_action(TTR("Change Capsule Shape Radius")); ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); @@ -4462,7 +4463,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); if (p_id == 0) { ur->create_action(TTR("Change Cylinder Shape Radius")); ur->add_do_method(ss.ptr(), "set_radius", ss->get_radius()); @@ -4487,7 +4488,7 @@ void CollisionShape3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Separation Ray Shape Length")); ur->add_do_method(ss.ptr(), "set_length", ss->get_length()); ur->add_undo_method(ss.ptr(), "set_length", p_restore); @@ -4516,8 +4517,8 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector<Vector3> points; for (int i = 0; i <= 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 1); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * r; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * r; @@ -4588,8 +4589,8 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3 d(0, height * 0.5 - radius, 0); for (int i = 0; i < 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 1); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; @@ -4659,8 +4660,8 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { Vector3 d(0, height * 0.5, 0); for (int i = 0; i < 360; i++) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 1); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 1); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * radius; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * radius; @@ -5202,8 +5203,8 @@ void JointGizmosDrawer::draw_cone(const Transform3D &p_offset, const Basis &p_ba //swing for (int i = 0; i < 360; i += 10) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 10); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 10); Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w; Point2 b = Vector2(Math::sin(rb), Math::cos(rb)) * w; @@ -5220,12 +5221,12 @@ void JointGizmosDrawer::draw_cone(const Transform3D &p_offset, const Basis &p_ba r_points.push_back(p_offset.translated_local(p_base.xform(Vector3(1, 0, 0))).origin); /// Twist - float ts = Math::rad2deg(p_twist); + float ts = Math::rad_to_deg(p_twist); ts = MIN(ts, 720); for (int i = 0; i < int(ts); i += 5) { - float ra = Math::deg2rad((float)i); - float rb = Math::deg2rad((float)i + 5); + float ra = Math::deg_to_rad((float)i); + float rb = Math::deg_to_rad((float)i + 5); float c = i / 720.0; float cn = (i + 5) / 720.0; Point2 a = Vector2(Math::sin(ra), Math::cos(ra)) * w * c; @@ -5745,7 +5746,7 @@ void FogVolumeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Change Fog Volume Extents")); ur->add_do_method(sn, "set_extents", sn->call("get_extents")); ur->add_undo_method(sn, "set_extents", p_restore); diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h index 739bf1b929..1b6485ac4e 100644 --- a/editor/plugins/node_3d_editor_gizmos.h +++ b/editor/plugins/node_3d_editor_gizmos.h @@ -334,8 +334,8 @@ public: Label3DGizmoPlugin(); }; -class Position3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(Position3DGizmoPlugin, EditorNode3DGizmoPlugin); +class Marker3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(Marker3DGizmoPlugin, EditorNode3DGizmoPlugin); Ref<ArrayMesh> pos3d_mesh; Vector<Vector3> cursor_points; @@ -346,7 +346,7 @@ public: int get_priority() const override; void redraw(EditorNode3DGizmo *p_gizmo) override; - Position3DGizmoPlugin(); + Marker3DGizmoPlugin(); }; class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin { @@ -409,8 +409,8 @@ public: VehicleWheel3DGizmoPlugin(); }; -class SoftDynamicBody3DGizmoPlugin : public EditorNode3DGizmoPlugin { - GDCLASS(SoftDynamicBody3DGizmoPlugin, EditorNode3DGizmoPlugin); +class SoftBody3DGizmoPlugin : public EditorNode3DGizmoPlugin { + GDCLASS(SoftBody3DGizmoPlugin, EditorNode3DGizmoPlugin); public: bool has_gizmo(Node3D *p_spatial) override; @@ -424,7 +424,7 @@ public: void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary, const Variant &p_restore, bool p_cancel = false) override; bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id, bool p_secondary) const override; - SoftDynamicBody3DGizmoPlugin(); + SoftBody3DGizmoPlugin(); }; class VisibleOnScreenNotifier3DGizmoPlugin : public EditorNode3DGizmoPlugin { diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 6afc6798d0..cc592e532c 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -46,6 +46,7 @@ #include "editor/scene_tree_dock.h" #include "scene/3d/camera_3d.h" #include "scene/3d/collision_shape_3d.h" +#include "scene/3d/decal.h" #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" #include "scene/3d/physics_body_3d.h" @@ -341,7 +342,7 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) { camera->set_global_transform(to_camera_transform(camera_cursor)); if (orthogonal) { - float half_fov = Math::deg2rad(get_fov()) / 2.0; + float half_fov = Math::deg_to_rad(get_fov()) / 2.0; float height = 2.0 * cursor.distance * Math::tan(half_fov); camera->set_orthogonal(height, get_znear(), get_zfar()); } else { @@ -2143,7 +2144,7 @@ void Node3DEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, cons } const real_t degrees_per_pixel = EditorSettings::get_singleton()->get("editors/3d/navigation_feel/orbit_sensitivity"); - const real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + const real_t radians_per_pixel = Math::deg_to_rad(degrees_per_pixel); const bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); const bool invert_x_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_x_axis"); @@ -2176,7 +2177,7 @@ void Node3DEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const // Scale mouse sensitivity with camera FOV scale when zoomed in to make it easier to point at things. const real_t degrees_per_pixel = real_t(EditorSettings::get_singleton()->get("editors/3d/freelook/freelook_sensitivity")) * MIN(1.0, cursor.fov_scale); - const real_t radians_per_pixel = Math::deg2rad(degrees_per_pixel); + const real_t radians_per_pixel = Math::deg_to_rad(degrees_per_pixel); const bool invert_y_axis = EditorSettings::get_singleton()->get("editors/3d/navigation/invert_y_axis"); // Note: do NOT assume the camera has the "current" transform, because it is interpolated and may have "lag". @@ -2972,6 +2973,13 @@ void Node3DEditorViewport::_menu_option(int p_option) { xform.scale_basis(sp->get_scale()); } + if (Object::cast_to<Decal>(E)) { + // Adjust rotation to match Decal's default orientation. + // This makes the decal "look" in the same direction as the camera, + // rather than pointing down relative to the camera orientation. + xform.basis.rotate_local(Vector3(1, 0, 0), Math_TAU * 0.25); + } + undo_redo->add_do_method(sp, "set_global_transform", xform); undo_redo->add_undo_method(sp, "set_global_transform", sp->get_global_gizmo_transform()); } @@ -2999,7 +3007,16 @@ void Node3DEditorViewport::_menu_option(int p_option) { continue; } - undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_euler_normalized()); + Basis basis = camera_transform.basis; + + if (Object::cast_to<Decal>(E)) { + // Adjust rotation to match Decal's default orientation. + // This makes the decal "look" in the same direction as the camera, + // rather than pointing down relative to the camera orientation. + basis.rotate_local(Vector3(1, 0, 0), Math_TAU * 0.25); + } + + undo_redo->add_do_method(sp, "set_rotation", basis.get_euler_normalized()); undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation()); } undo_redo->commit_action(); @@ -3964,19 +3981,7 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others. String name = path.get_file().get_basename(); - switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) { - case NAME_CASING_PASCAL_CASE: - name = name.capitalize().replace(" ", ""); - break; - case NAME_CASING_CAMEL_CASE: - name = name.capitalize().replace(" ", ""); - name[0] = name.to_lower()[0]; - break; - case NAME_CASING_SNAKE_CASE: - name = name.capitalize().replace(" ", "_").to_lower(); - break; - } - mesh_instance->set_name(name); + mesh_instance->set_name(Node::adjust_name_casing(name)); instantiated_scene = mesh_instance; } else { @@ -4003,15 +4008,15 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path)); } - editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene, true); - editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene()); - editor_data->get_undo_redo().add_do_reference(instantiated_scene); - editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instantiated_scene); + editor_data->get_undo_redo()->add_do_method(parent, "add_child", instantiated_scene, true); + editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_owner", EditorNode::get_singleton()->get_edited_scene()); + editor_data->get_undo_redo()->add_do_reference(instantiated_scene); + editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", instantiated_scene); String new_name = parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); + editor_data->get_undo_redo()->add_do_method(ed, "live_debug_instance_node", EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent), path, new_name); + editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(EditorNode::get_singleton()->get_edited_scene()->get_path_to(parent)) + "/" + new_name)); Node3D *node3d = Object::cast_to<Node3D>(instantiated_scene); if (node3d) { @@ -4024,7 +4029,7 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po global_transform.origin = spatial_editor->snap_point(_get_instance_position(p_point)); global_transform.basis *= node3d->get_transform().basis; - editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_global_transform", global_transform); + editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_global_transform", global_transform); } return true; @@ -4035,15 +4040,15 @@ void Node3DEditorViewport::_perform_drop_data() { GeometryInstance3D *geometry_instance = Object::cast_to<GeometryInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target())); MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(spatial_editor->get_preview_material_target())); if (mesh_instance && spatial_editor->get_preview_material_surface() != -1) { - editor_data->get_undo_redo().create_action(vformat(TTR("Set Surface %d Override Material"), spatial_editor->get_preview_material_surface())); - editor_data->get_undo_redo().add_do_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_material()); - editor_data->get_undo_redo().add_undo_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_reset_material()); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->create_action(vformat(TTR("Set Surface %d Override Material"), spatial_editor->get_preview_material_surface())); + editor_data->get_undo_redo()->add_do_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_material()); + editor_data->get_undo_redo()->add_undo_method(geometry_instance, "set_surface_override_material", spatial_editor->get_preview_material_surface(), spatial_editor->get_preview_reset_material()); + editor_data->get_undo_redo()->commit_action(); } else if (geometry_instance) { - editor_data->get_undo_redo().create_action(TTR("Set Material Override")); - editor_data->get_undo_redo().add_do_method(geometry_instance, "set_material_override", spatial_editor->get_preview_material()); - editor_data->get_undo_redo().add_undo_method(geometry_instance, "set_material_override", spatial_editor->get_preview_reset_material()); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->create_action(TTR("Set Material Override")); + editor_data->get_undo_redo()->add_do_method(geometry_instance, "set_material_override", spatial_editor->get_preview_material()); + editor_data->get_undo_redo()->add_undo_method(geometry_instance, "set_material_override", spatial_editor->get_preview_reset_material()); + editor_data->get_undo_redo()->commit_action(); } _remove_preview_material(); @@ -4054,7 +4059,7 @@ void Node3DEditorViewport::_perform_drop_data() { Vector<String> error_files; - editor_data->get_undo_redo().create_action(TTR("Create Node")); + editor_data->get_undo_redo()->create_action(TTR("Create Node")); for (int i = 0; i < selected_files.size(); i++) { String path = selected_files[i]; @@ -4072,7 +4077,7 @@ void Node3DEditorViewport::_perform_drop_data() { } } - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); if (error_files.size() > 0) { String files_str; @@ -4113,6 +4118,7 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant continue; } Ref<PackedScene> scn = res; + Ref<Mesh> mesh = res; Ref<Material> mat = res; Ref<Texture2D> tex = res; if (scn.is_valid()) { @@ -4131,6 +4137,8 @@ bool Node3DEditorViewport::can_drop_data_fw(const Point2 &p_point, const Variant spatial_editor->set_preview_material(mat); break; + } else if (mesh.is_valid()) { + // Let the mesh pass. } else if (tex.is_valid()) { Ref<StandardMaterial3D> new_mat = memnew(StandardMaterial3D); new_mat->set_texture(BaseMaterial3D::TEXTURE_ALBEDO, tex); @@ -4526,7 +4534,7 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { break; } - static const float orthogonal_threshold = Math::cos(Math::deg2rad(87.0f)); + static const float orthogonal_threshold = Math::cos(Math::deg_to_rad(87.0f)); bool axis_is_orthogonal = ABS(plane.normal.dot(global_axis)) < orthogonal_threshold; double angle = 0.0f; @@ -4546,10 +4554,10 @@ void Node3DEditorViewport::update_transform(Point2 p_mousepos, bool p_shift) { if (_edit.snap || spatial_editor->is_snap_enabled()) { snap = spatial_editor->get_rotate_snap(); } - angle = Math::rad2deg(angle) + snap * 0.5; //else it won't reach +180 + angle = Math::rad_to_deg(angle) + snap * 0.5; //else it won't reach +180 angle -= Math::fmod(angle, snap); set_message(vformat(TTR("Rotating %s degrees."), String::num(angle, snap_step_decimals))); - angle = Math::deg2rad(angle); + angle = Math::deg_to_rad(angle); bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW); // Disable local transformation for TRANSFORM_VIEW @@ -5717,7 +5725,7 @@ void Node3DEditor::_xform_dialog_action() { for (int i = 0; i < 3; i++) { translate[i] = xform_translate[i]->get_text().to_float(); - rotate[i] = Math::deg2rad(xform_rotate[i]->get_text().to_float()); + rotate[i] = Math::deg_to_rad(xform_rotate[i]->get_text().to_float()); scale[i] = xform_scale[i]->get_text().to_float(); } @@ -5810,11 +5818,11 @@ void Node3DEditor::_update_camera_override_button(bool p_game_running) { if (p_game_running) { button->set_disabled(false); - button->set_tooltip(TTR("Project Camera Override\nOverrides the running project's camera with the editor viewport camera.")); + 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(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature.")); + button->set_tooltip_text(TTR("Project Camera Override\nNo project instance running. Run the project from the editor to use this feature.")); } } @@ -7126,42 +7134,6 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) { undo_redo->commit_action(); } -void Node3DEditor::_update_theme() { - tool_button[TOOL_MODE_SELECT]->set_icon(get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons"))); - tool_button[TOOL_MODE_MOVE]->set_icon(get_theme_icon(SNAME("ToolMove"), SNAME("EditorIcons"))); - tool_button[TOOL_MODE_ROTATE]->set_icon(get_theme_icon(SNAME("ToolRotate"), SNAME("EditorIcons"))); - tool_button[TOOL_MODE_SCALE]->set_icon(get_theme_icon(SNAME("ToolScale"), SNAME("EditorIcons"))); - tool_button[TOOL_MODE_LIST_SELECT]->set_icon(get_theme_icon(SNAME("ListSelect"), SNAME("EditorIcons"))); - tool_button[TOOL_LOCK_SELECTED]->set_icon(get_theme_icon(SNAME("Lock"), SNAME("EditorIcons"))); - tool_button[TOOL_UNLOCK_SELECTED]->set_icon(get_theme_icon(SNAME("Unlock"), SNAME("EditorIcons"))); - tool_button[TOOL_GROUP_SELECTED]->set_icon(get_theme_icon(SNAME("Group"), SNAME("EditorIcons"))); - tool_button[TOOL_UNGROUP_SELECTED]->set_icon(get_theme_icon(SNAME("Ungroup"), SNAME("EditorIcons"))); - - tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_icon(get_theme_icon(SNAME("Object"), SNAME("EditorIcons"))); - tool_option_button[TOOL_OPT_USE_SNAP]->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons"))); - tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_icon(get_theme_icon(SNAME("Camera3D"), SNAME("EditorIcons"))); - - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_theme_icon(SNAME("Panels1"), SNAME("EditorIcons"))); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_theme_icon(SNAME("Panels2"), SNAME("EditorIcons"))); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_theme_icon(SNAME("Panels2Alt"), SNAME("EditorIcons"))); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_theme_icon(SNAME("Panels3"), SNAME("EditorIcons"))); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_theme_icon(SNAME("Panels3Alt"), SNAME("EditorIcons"))); - view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_theme_icon(SNAME("Panels4"), SNAME("EditorIcons"))); - - sun_button->set_icon(get_theme_icon(SNAME("DirectionalLight3D"), SNAME("EditorIcons"))); - environ_button->set_icon(get_theme_icon(SNAME("WorldEnvironment"), SNAME("EditorIcons"))); - sun_environ_settings->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); - - sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); - environ_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); - - sun_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); - environ_sky_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); - environ_ground_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); - - context_menu_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles"))); -} - void Node3DEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { @@ -7184,7 +7156,6 @@ void Node3DEditor::_notification(int p_what) { } break; case NOTIFICATION_ENTER_TREE: { - _update_theme(); _register_all_gizmos(); _update_gizmos_menu(); _init_indicators(); @@ -7196,7 +7167,40 @@ void Node3DEditor::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - _update_theme(); + tool_button[TOOL_MODE_SELECT]->set_icon(get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons"))); + tool_button[TOOL_MODE_MOVE]->set_icon(get_theme_icon(SNAME("ToolMove"), SNAME("EditorIcons"))); + tool_button[TOOL_MODE_ROTATE]->set_icon(get_theme_icon(SNAME("ToolRotate"), SNAME("EditorIcons"))); + tool_button[TOOL_MODE_SCALE]->set_icon(get_theme_icon(SNAME("ToolScale"), SNAME("EditorIcons"))); + tool_button[TOOL_MODE_LIST_SELECT]->set_icon(get_theme_icon(SNAME("ListSelect"), SNAME("EditorIcons"))); + tool_button[TOOL_LOCK_SELECTED]->set_icon(get_theme_icon(SNAME("Lock"), SNAME("EditorIcons"))); + tool_button[TOOL_UNLOCK_SELECTED]->set_icon(get_theme_icon(SNAME("Unlock"), SNAME("EditorIcons"))); + tool_button[TOOL_GROUP_SELECTED]->set_icon(get_theme_icon(SNAME("Group"), SNAME("EditorIcons"))); + tool_button[TOOL_UNGROUP_SELECTED]->set_icon(get_theme_icon(SNAME("Ungroup"), SNAME("EditorIcons"))); + + tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_icon(get_theme_icon(SNAME("Object"), SNAME("EditorIcons"))); + tool_option_button[TOOL_OPT_USE_SNAP]->set_icon(get_theme_icon(SNAME("Snap"), SNAME("EditorIcons"))); + tool_option_button[TOOL_OPT_OVERRIDE_CAMERA]->set_icon(get_theme_icon(SNAME("Camera3D"), SNAME("EditorIcons"))); + + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_1_VIEWPORT), get_theme_icon(SNAME("Panels1"), SNAME("EditorIcons"))); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS), get_theme_icon(SNAME("Panels2"), SNAME("EditorIcons"))); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_2_VIEWPORTS_ALT), get_theme_icon(SNAME("Panels2Alt"), SNAME("EditorIcons"))); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS), get_theme_icon(SNAME("Panels3"), SNAME("EditorIcons"))); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_3_VIEWPORTS_ALT), get_theme_icon(SNAME("Panels3Alt"), SNAME("EditorIcons"))); + view_menu->get_popup()->set_item_icon(view_menu->get_popup()->get_item_index(MENU_VIEW_USE_4_VIEWPORTS), get_theme_icon(SNAME("Panels4"), SNAME("EditorIcons"))); + + sun_button->set_icon(get_theme_icon(SNAME("DirectionalLight3D"), SNAME("EditorIcons"))); + environ_button->set_icon(get_theme_icon(SNAME("WorldEnvironment"), SNAME("EditorIcons"))); + sun_environ_settings->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + + sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); + environ_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); + + sun_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); + environ_sky_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); + environ_ground_color->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); + + context_menu_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("ContextualToolbar"), SNAME("EditorStyles"))); + _update_gizmos_menu_theme(); sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); environ_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); @@ -7247,6 +7251,14 @@ Vector<int> Node3DEditor::get_subgizmo_selection() { return ret; } +void Node3DEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + +Ref<EditorUndoRedoManager> Node3DEditor::get_undo_redo() { + return undo_redo; +} + void Node3DEditor::add_control_to_menu_panel(Control *p_control) { context_menu_hbox->add_child(p_control); } @@ -7484,10 +7496,10 @@ void Node3DEditor::_register_all_gizmos() { add_gizmo_plugin(Ref<AudioListener3DGizmoPlugin>(memnew(AudioListener3DGizmoPlugin))); add_gizmo_plugin(Ref<MeshInstance3DGizmoPlugin>(memnew(MeshInstance3DGizmoPlugin))); add_gizmo_plugin(Ref<OccluderInstance3DGizmoPlugin>(memnew(OccluderInstance3DGizmoPlugin))); - add_gizmo_plugin(Ref<SoftDynamicBody3DGizmoPlugin>(memnew(SoftDynamicBody3DGizmoPlugin))); + add_gizmo_plugin(Ref<SoftBody3DGizmoPlugin>(memnew(SoftBody3DGizmoPlugin))); add_gizmo_plugin(Ref<Sprite3DGizmoPlugin>(memnew(Sprite3DGizmoPlugin))); add_gizmo_plugin(Ref<Label3DGizmoPlugin>(memnew(Label3DGizmoPlugin))); - add_gizmo_plugin(Ref<Position3DGizmoPlugin>(memnew(Position3DGizmoPlugin))); + add_gizmo_plugin(Ref<Marker3DGizmoPlugin>(memnew(Marker3DGizmoPlugin))); add_gizmo_plugin(Ref<RayCast3DGizmoPlugin>(memnew(RayCast3DGizmoPlugin))); add_gizmo_plugin(Ref<ShapeCast3DGizmoPlugin>(memnew(ShapeCast3DGizmoPlugin))); add_gizmo_plugin(Ref<SpringArm3DGizmoPlugin>(memnew(SpringArm3DGizmoPlugin))); @@ -7595,10 +7607,10 @@ void Node3DEditor::_load_default_preview_settings() { // On any not-tidally-locked planet, a sun would have an angular altitude // of 60 degrees as the average of all points on the sphere at noon. // The azimuth choice is arbitrary, but ideally shouldn't be on an axis. - sun_rotation = Vector2(-Math::deg2rad(60.0), Math::deg2rad(150.0)); + sun_rotation = Vector2(-Math::deg_to_rad(60.0), Math::deg_to_rad(150.0)); - sun_angle_altitude->set_value(-Math::rad2deg(sun_rotation.x)); - sun_angle_azimuth->set_value(180.0 - Math::rad2deg(sun_rotation.y)); + sun_angle_altitude->set_value(-Math::rad_to_deg(sun_rotation.x)); + sun_angle_azimuth->set_value(180.0 - Math::rad_to_deg(sun_rotation.y)); sun_direction->update(); environ_sky_color->set_pick_color(Color(0.385, 0.454, 0.55)); environ_ground_color->set_pick_color(Color(0.2, 0.169, 0.133)); @@ -7607,7 +7619,7 @@ void Node3DEditor::_load_default_preview_settings() { environ_tonemap_button->set_pressed(true); environ_ao_button->set_pressed(false); environ_gi_button->set_pressed(false); - sun_max_distance->set_value(250); + sun_max_distance->set_value(100); sun_color->set_pick_color(Color(1, 1, 1)); sun_energy->set_value(1.0); @@ -7641,8 +7653,8 @@ void Node3DEditor::_update_preview_environment() { } } - sun_angle_altitude->set_value(-Math::rad2deg(sun_rotation.x)); - sun_angle_azimuth->set_value(180.0 - Math::rad2deg(sun_rotation.y)); + sun_angle_altitude->set_value(-Math::rad_to_deg(sun_rotation.x)); + sun_angle_azimuth->set_value(180.0 - Math::rad_to_deg(sun_rotation.y)); bool disable_env = world_env_count > 0 || environ_button->is_pressed(); @@ -7675,15 +7687,15 @@ void Node3DEditor::_sun_direction_input(const Ref<InputEvent> &p_event) { sun_rotation.x += mm->get_relative().y * (0.02 * EDSCALE); sun_rotation.y -= mm->get_relative().x * (0.02 * EDSCALE); sun_rotation.x = CLAMP(sun_rotation.x, -Math_TAU / 4, Math_TAU / 4); - sun_angle_altitude->set_value(-Math::rad2deg(sun_rotation.x)); - sun_angle_azimuth->set_value(180.0 - Math::rad2deg(sun_rotation.y)); + sun_angle_altitude->set_value(-Math::rad_to_deg(sun_rotation.x)); + sun_angle_azimuth->set_value(180.0 - Math::rad_to_deg(sun_rotation.y)); _preview_settings_changed(); } } void Node3DEditor::_sun_direction_angle_set() { - sun_rotation.x = Math::deg2rad(-sun_angle_altitude->get_value()); - sun_rotation.y = Math::deg2rad(180.0 - sun_angle_azimuth->get_value()); + sun_rotation.x = Math::deg_to_rad(-sun_angle_altitude->get_value()); + sun_rotation.y = Math::deg_to_rad(180.0 - sun_angle_azimuth->get_value()); _preview_settings_changed(); } @@ -7731,7 +7743,7 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_MODE_SELECT]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_TOOL_SELECT)); tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), Key::Q)); tool_button[TOOL_MODE_SELECT]->set_shortcut_context(this); - tool_button[TOOL_MODE_SELECT]->set_tooltip(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.")); + tool_button[TOOL_MODE_SELECT]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.")); main_menu_hbox->add_child(memnew(VSeparator)); tool_button[TOOL_MODE_MOVE] = memnew(Button); @@ -7766,13 +7778,13 @@ Node3DEditor::Node3DEditor() { tool_button[TOOL_MODE_LIST_SELECT]->set_toggle_mode(true); tool_button[TOOL_MODE_LIST_SELECT]->set_flat(true); tool_button[TOOL_MODE_LIST_SELECT]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_TOOL_LIST_SELECT)); - tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip(TTR("Show list of selectable nodes at position clicked.")); + tool_button[TOOL_MODE_LIST_SELECT]->set_tooltip_text(TTR("Show list of selectable nodes at position clicked.")); tool_button[TOOL_LOCK_SELECTED] = memnew(Button); main_menu_hbox->add_child(tool_button[TOOL_LOCK_SELECTED]); tool_button[TOOL_LOCK_SELECTED]->set_flat(true); tool_button[TOOL_LOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_LOCK_SELECTED)); - tool_button[TOOL_LOCK_SELECTED]->set_tooltip(TTR("Lock selected node, preventing selection and movement.")); + tool_button[TOOL_LOCK_SELECTED]->set_tooltip_text(TTR("Lock selected node, preventing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD | Key::L)); @@ -7780,7 +7792,7 @@ Node3DEditor::Node3DEditor() { main_menu_hbox->add_child(tool_button[TOOL_UNLOCK_SELECTED]); tool_button[TOOL_UNLOCK_SELECTED]->set_flat(true); tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_UNLOCK_SELECTED)); - tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock selected node, allowing selection and movement.")); + tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip_text(TTR("Unlock selected node, allowing selection and movement.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::L)); @@ -7788,7 +7800,7 @@ Node3DEditor::Node3DEditor() { main_menu_hbox->add_child(tool_button[TOOL_GROUP_SELECTED]); tool_button[TOOL_GROUP_SELECTED]->set_flat(true); tool_button[TOOL_GROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_GROUP_SELECTED)); - tool_button[TOOL_GROUP_SELECTED]->set_tooltip(TTR("Make selected node's children not selectable.")); + tool_button[TOOL_GROUP_SELECTED]->set_tooltip_text(TTR("Make selected node's children not selectable.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD | Key::G)); @@ -7796,7 +7808,7 @@ Node3DEditor::Node3DEditor() { main_menu_hbox->add_child(tool_button[TOOL_UNGROUP_SELECTED]); tool_button[TOOL_UNGROUP_SELECTED]->set_flat(true); tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed).bind(MENU_UNGROUP_SELECTED)); - tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip(TTR("Make selected node's children selectable.")); + tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip_text(TTR("Make selected node's children selectable.")); // Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused. tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::G)); @@ -7830,7 +7842,7 @@ Node3DEditor::Node3DEditor() { main_menu_hbox->add_child(memnew(VSeparator)); sun_button = memnew(Button); - sun_button->set_tooltip(TTR("Toggle preview sunlight.\nIf a DirectionalLight3D node is added to the scene, preview sunlight is disabled.")); + 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); sun_button->set_flat(true); sun_button->connect("pressed", callable_mp(this, &Node3DEditor::_update_preview_environment), CONNECT_DEFERRED); @@ -7839,7 +7851,7 @@ Node3DEditor::Node3DEditor() { main_menu_hbox->add_child(sun_button); environ_button = memnew(Button); - environ_button->set_tooltip(TTR("Toggle preview environment.\nIf a WorldEnvironment node is added to the scene, preview environment is disabled.")); + environ_button->set_tooltip_text(TTR("Toggle preview environment.\nIf a WorldEnvironment node is added to the scene, preview environment is disabled.")); environ_button->set_toggle_mode(true); environ_button->set_flat(true); environ_button->connect("pressed", callable_mp(this, &Node3DEditor::_update_preview_environment), CONNECT_DEFERRED); @@ -7848,7 +7860,7 @@ Node3DEditor::Node3DEditor() { main_menu_hbox->add_child(environ_button); sun_environ_settings = memnew(Button); - sun_environ_settings->set_tooltip(TTR("Edit Sun and Environment settings.")); + sun_environ_settings->set_tooltip_text(TTR("Edit Sun and Environment settings.")); sun_environ_settings->set_flat(true); sun_environ_settings->connect("pressed", callable_mp(this, &Node3DEditor::_sun_environ_settings_pressed)); @@ -8206,7 +8218,7 @@ void fragment() { sun_add_to_scene = memnew(Button); sun_add_to_scene->set_text(TTR("Add Sun to Scene")); - sun_add_to_scene->set_tooltip(TTR("Adds a DirectionalLight3D node matching the preview sun settings to the current scene.\nHold Shift while clicking to also add the preview environment to the current scene.")); + sun_add_to_scene->set_tooltip_text(TTR("Adds a DirectionalLight3D node matching the preview sun settings to the current scene.\nHold Shift while clicking to also add the preview environment to the current scene.")); sun_add_to_scene->connect("pressed", callable_mp(this, &Node3DEditor::_add_sun_to_scene).bind(false)); sun_vb->add_spacer(); sun_vb->add_child(sun_add_to_scene); @@ -8275,7 +8287,7 @@ void fragment() { environ_add_to_scene = memnew(Button); environ_add_to_scene->set_text(TTR("Add Environment to Scene")); - environ_add_to_scene->set_tooltip(TTR("Adds a WorldEnvironment node matching the preview environment settings to the current scene.\nHold Shift while clicking to also add the preview sun to the current scene.")); + environ_add_to_scene->set_tooltip_text(TTR("Adds a WorldEnvironment node matching the preview environment settings to the current scene.\nHold Shift while clicking to also add the preview sun to the current scene.")); environ_add_to_scene->connect("pressed", callable_mp(this, &Node3DEditor::_add_environment_to_scene).bind(false)); environ_vb->add_spacer(); environ_vb->add_child(environ_add_to_scene); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 4469271a38..e07374dd49 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -53,6 +53,7 @@ class Node3DEditorViewport; class SubViewportContainer; class DirectionalLight3D; class WorldEnvironment; +class EditorUndoRedoManager; class ViewportRotationControl : public Control { GDCLASS(ViewportRotationControl, Control); @@ -201,7 +202,7 @@ private: EditorData *editor_data = nullptr; EditorSelection *editor_selection = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; CheckBox *preview_camera = nullptr; SubViewportContainer *subviewport_container = nullptr; @@ -682,7 +683,7 @@ private: HBoxContainer *context_menu_hbox = nullptr; void _generate_selection_boxes(); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; int camera_override_viewport_id; @@ -777,8 +778,6 @@ private: void _add_sun_to_scene(bool p_already_added_environment = false); void _add_environment_to_scene(bool p_already_added_sun = false); - void _update_theme(); - protected: void _notification(int p_what); //void _gui_input(InputEvent p_event); @@ -820,13 +819,13 @@ public: void select_gizmo_highlight_axis(int p_axis); void set_custom_camera(Node *p_camera) { custom_camera = p_camera; } - void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } Dictionary get_state() const; void set_state(const Dictionary &p_state); Ref<Environment> get_viewport_environment() { return viewport_environment; } - UndoRedo *get_undo_redo() { return undo_redo; } + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); + Ref<EditorUndoRedoManager> get_undo_redo(); void add_control_to_menu_panel(Control *p_control); void remove_control_from_menu_panel(Control *p_control); diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp index 8d083d28b2..2f4ae734d1 100644 --- a/editor/plugins/packed_scene_translation_parser_plugin.cpp +++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp @@ -123,7 +123,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path, PackedSceneEditorTranslationParserPlugin::PackedSceneEditorTranslationParserPlugin() { // Scene Node's properties containing strings that will be fetched for translation. lookup_properties.insert("text"); - lookup_properties.insert("hint_tooltip"); + lookup_properties.insert("tooltip_text"); lookup_properties.insert("placeholder_text"); lookup_properties.insert("items"); lookup_properties.insert("title"); diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index fd331c4127..00b0a0b100 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -36,10 +36,10 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" void Path2DEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { curve_edit->set_icon(get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons"))); curve_edit_curve->set_icon(get_theme_icon(SNAME("CurveCurve"), SNAME("EditorIcons"))); @@ -536,7 +536,7 @@ Path2DEditor::Path2DEditor() { curve_edit->set_flat(true); curve_edit->set_toggle_mode(true); curve_edit->set_focus_mode(Control::FOCUS_NONE); - curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Click: Add Point") + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point")); + curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Click: Add Point") + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point")); curve_edit->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_EDIT)); base_hb->add_child(curve_edit); @@ -544,7 +544,7 @@ Path2DEditor::Path2DEditor() { curve_edit_curve->set_flat(true); curve_edit_curve->set_toggle_mode(true); curve_edit_curve->set_focus_mode(Control::FOCUS_NONE); - curve_edit_curve->set_tooltip(TTR("Select Control Points (Shift+Drag)")); + curve_edit_curve->set_tooltip_text(TTR("Select Control Points (Shift+Drag)")); curve_edit_curve->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_EDIT_CURVE)); base_hb->add_child(curve_edit_curve); @@ -552,7 +552,7 @@ Path2DEditor::Path2DEditor() { curve_create->set_flat(true); curve_create->set_toggle_mode(true); curve_create->set_focus_mode(Control::FOCUS_NONE); - curve_create->set_tooltip(TTR("Add Point (in empty space)")); + curve_create->set_tooltip_text(TTR("Add Point (in empty space)")); curve_create->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_CREATE)); base_hb->add_child(curve_create); @@ -560,14 +560,14 @@ Path2DEditor::Path2DEditor() { curve_del->set_flat(true); curve_del->set_toggle_mode(true); curve_del->set_focus_mode(Control::FOCUS_NONE); - curve_del->set_tooltip(TTR("Delete Point")); + curve_del->set_tooltip_text(TTR("Delete Point")); curve_del->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_DELETE)); base_hb->add_child(curve_del); curve_close = memnew(Button); curve_close->set_flat(true); curve_close->set_focus_mode(Control::FOCUS_NONE); - curve_close->set_tooltip(TTR("Close Curve")); + curve_close->set_tooltip_text(TTR("Close Curve")); curve_close->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected).bind(ACTION_CLOSE)); base_hb->add_child(curve_close); diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h index 720f5c090f..13eca79010 100644 --- a/editor/plugins/path_2d_editor_plugin.h +++ b/editor/plugins/path_2d_editor_plugin.h @@ -36,11 +36,12 @@ #include "scene/gui/separator.h" class CanvasItemEditor; +class EditorUndoRedoManager; class Path2DEditor : public HBoxContainer { GDCLASS(Path2DEditor, HBoxContainer); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; CanvasItemEditor *canvas_item_editor = nullptr; Panel *panel = nullptr; diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp index 65b15a6001..d5dbd24eac 100644 --- a/editor/plugins/path_3d_editor_plugin.cpp +++ b/editor/plugins/path_3d_editor_plugin.cpp @@ -35,6 +35,7 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "node_3d_editor_plugin.h" #include "scene/resources/curve.h" @@ -172,7 +173,7 @@ void Path3DGizmo::commit_handle(int p_id, bool p_secondary, const Variant &p_res return; } - UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); if (!p_secondary) { if (p_cancel) { @@ -385,7 +386,7 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera } } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); if (closest_seg != -1) { //subdivide @@ -427,21 +428,21 @@ EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera // Find the offset and point index of the place to break up. // Also check for the control points. if (dist_to_p < click_dist) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove Path Point")); ur->add_do_method(c.ptr(), "remove_point", i); ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i); ur->commit_action(); return EditorPlugin::AFTER_GUI_INPUT_STOP; } else if (dist_to_p_out < click_dist) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove Out-Control Point")); ur->add_do_method(c.ptr(), "set_point_out", i, Vector3()); ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i)); ur->commit_action(); return EditorPlugin::AFTER_GUI_INPUT_STOP; } else if (dist_to_p_in < click_dist) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove In-Control Point")); ur->add_do_method(c.ptr(), "set_point_in", i, Vector3()); ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i)); @@ -520,7 +521,7 @@ void Path3DEditorPlugin::_close_curve() { if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) { return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); 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()); @@ -562,8 +563,6 @@ void Path3DEditorPlugin::_notification(int p_what) { curve_edit->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(1)); curve_del->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_mode_changed).bind(2)); curve_close->connect("pressed", callable_mp(this, &Path3DEditorPlugin::_close_curve)); - - _update_theme(); } break; case NOTIFICATION_READY: { @@ -596,7 +595,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() { curve_edit->set_toggle_mode(true); curve_edit->hide(); curve_edit->set_focus_mode(Control::FOCUS_NONE); - curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point")); + curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point")); Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_edit); curve_create = memnew(Button); @@ -604,7 +603,7 @@ Path3DEditorPlugin::Path3DEditorPlugin() { curve_create->set_toggle_mode(true); curve_create->hide(); curve_create->set_focus_mode(Control::FOCUS_NONE); - curve_create->set_tooltip(TTR("Add Point (in empty space)") + "\n" + TTR("Split Segment (in curve)")); + curve_create->set_tooltip_text(TTR("Add Point (in empty space)") + "\n" + TTR("Split Segment (in curve)")); Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_create); curve_del = memnew(Button); @@ -612,14 +611,14 @@ Path3DEditorPlugin::Path3DEditorPlugin() { curve_del->set_toggle_mode(true); curve_del->hide(); curve_del->set_focus_mode(Control::FOCUS_NONE); - curve_del->set_tooltip(TTR("Delete Point")); + curve_del->set_tooltip_text(TTR("Delete Point")); Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_del); curve_close = memnew(Button); curve_close->set_flat(true); curve_close->hide(); curve_close->set_focus_mode(Control::FOCUS_NONE); - curve_close->set_tooltip(TTR("Close Curve")); + curve_close->set_tooltip_text(TTR("Close Curve")); Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_close); PopupMenu *menu; diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 4f46c99a04..d414ff5143 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -91,8 +91,8 @@ void Polygon2DEditor::_notification(int p_what) { uv_vscroll->set_anchors_and_offsets_preset(PRESET_RIGHT_WIDE); uv_hscroll->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { uv_edit_draw->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); bone_scroll->add_theme_style_override("bg", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); @@ -1237,7 +1237,7 @@ Polygon2DEditor::Polygon2DEditor() { button_uv = memnew(Button); button_uv->set_flat(true); add_child(button_uv); - button_uv->set_tooltip(TTR("Open Polygon 2D UV editor.")); + button_uv->set_tooltip_text(TTR("Open Polygon 2D UV editor.")); button_uv->connect("pressed", callable_mp(this, &Polygon2DEditor::_menu_option).bind(MODE_EDIT_UV)); uv_mode = UV_MODE_EDIT_POINT; @@ -1293,17 +1293,17 @@ Polygon2DEditor::Polygon2DEditor() { uv_button[i]->set_focus_mode(FOCUS_NONE); } - uv_button[UV_MODE_CREATE]->set_tooltip(TTR("Create Polygon")); - uv_button[UV_MODE_CREATE_INTERNAL]->set_tooltip(TTR("Create Internal Vertex")); - uv_button[UV_MODE_REMOVE_INTERNAL]->set_tooltip(TTR("Remove Internal Vertex")); - uv_button[UV_MODE_EDIT_POINT]->set_tooltip(TTR("Move Points") + "\n" + TTR("Ctrl: Rotate") + "\n" + TTR("Shift: Move All") + "\n" + TTR("Shift+Ctrl: Scale")); - uv_button[UV_MODE_MOVE]->set_tooltip(TTR("Move Polygon")); - uv_button[UV_MODE_ROTATE]->set_tooltip(TTR("Rotate Polygon")); - uv_button[UV_MODE_SCALE]->set_tooltip(TTR("Scale Polygon")); - uv_button[UV_MODE_ADD_POLYGON]->set_tooltip(TTR("Create a custom polygon. Enables custom polygon rendering.")); - uv_button[UV_MODE_REMOVE_POLYGON]->set_tooltip(TTR("Remove a custom polygon. If none remain, custom polygon rendering is disabled.")); - uv_button[UV_MODE_PAINT_WEIGHT]->set_tooltip(TTR("Paint weights with specified intensity.")); - uv_button[UV_MODE_CLEAR_WEIGHT]->set_tooltip(TTR("Unpaint weights with specified intensity.")); + uv_button[UV_MODE_CREATE]->set_tooltip_text(TTR("Create Polygon")); + uv_button[UV_MODE_CREATE_INTERNAL]->set_tooltip_text(TTR("Create Internal Vertex")); + uv_button[UV_MODE_REMOVE_INTERNAL]->set_tooltip_text(TTR("Remove Internal Vertex")); + uv_button[UV_MODE_EDIT_POINT]->set_tooltip_text(TTR("Move Points") + "\n" + TTR("Ctrl: Rotate") + "\n" + TTR("Shift: Move All") + "\n" + TTR("Shift+Ctrl: Scale")); + uv_button[UV_MODE_MOVE]->set_tooltip_text(TTR("Move Polygon")); + uv_button[UV_MODE_ROTATE]->set_tooltip_text(TTR("Rotate Polygon")); + uv_button[UV_MODE_SCALE]->set_tooltip_text(TTR("Scale Polygon")); + uv_button[UV_MODE_ADD_POLYGON]->set_tooltip_text(TTR("Create a custom polygon. Enables custom polygon rendering.")); + uv_button[UV_MODE_REMOVE_POLYGON]->set_tooltip_text(TTR("Remove a custom polygon. If none remain, custom polygon rendering is disabled.")); + uv_button[UV_MODE_PAINT_WEIGHT]->set_tooltip_text(TTR("Paint weights with specified intensity.")); + uv_button[UV_MODE_CLEAR_WEIGHT]->set_tooltip_text(TTR("Unpaint weights with specified intensity.")); uv_button[UV_MODE_CREATE]->hide(); uv_button[UV_MODE_CREATE_INTERNAL]->hide(); @@ -1368,7 +1368,7 @@ Polygon2DEditor::Polygon2DEditor() { b_snap_enable->set_focus_mode(FOCUS_NONE); b_snap_enable->set_toggle_mode(true); b_snap_enable->set_pressed(use_snap); - b_snap_enable->set_tooltip(TTR("Enable Snap")); + b_snap_enable->set_tooltip_text(TTR("Enable Snap")); b_snap_enable->connect("toggled", callable_mp(this, &Polygon2DEditor::_set_use_snap)); b_snap_grid = memnew(Button); @@ -1378,7 +1378,7 @@ Polygon2DEditor::Polygon2DEditor() { b_snap_grid->set_focus_mode(FOCUS_NONE); b_snap_grid->set_toggle_mode(true); b_snap_grid->set_pressed(snap_show_grid); - b_snap_grid->set_tooltip(TTR("Show Grid")); + b_snap_grid->set_tooltip_text(TTR("Show Grid")); b_snap_grid->connect("toggled", callable_mp(this, &Polygon2DEditor::_set_show_grid)); grid_settings = memnew(AcceptDialog); diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp index 83092f990f..2b3a5c3e23 100644 --- a/editor/plugins/polygon_3d_editor_plugin.cpp +++ b/editor/plugins/polygon_3d_editor_plugin.cpp @@ -38,6 +38,7 @@ #include "core/os/keyboard.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "node_3d_editor_plugin.h" #include "scene/3d/camera_3d.h" diff --git a/editor/plugins/polygon_3d_editor_plugin.h b/editor/plugins/polygon_3d_editor_plugin.h index e1e1261250..0eb02a39e2 100644 --- a/editor/plugins/polygon_3d_editor_plugin.h +++ b/editor/plugins/polygon_3d_editor_plugin.h @@ -37,11 +37,12 @@ #include "scene/resources/immediate_mesh.h" class CanvasItemEditor; +class EditorUndoRedoManager; class Polygon3DEditor : public HBoxContainer { GDCLASS(Polygon3DEditor, HBoxContainer); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; enum Mode { MODE_CREATE, MODE_EDIT, diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp index 4e528ef066..1d569b7c52 100644 --- a/editor/plugins/resource_preloader_editor_plugin.cpp +++ b/editor/plugins/resource_preloader_editor_plugin.cpp @@ -39,7 +39,6 @@ void ResourcePreloaderEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { load->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); } break; @@ -234,6 +233,10 @@ void ResourcePreloaderEditor::_cell_button_pressed(Object *p_item, int p_column, } } +void ResourcePreloaderEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + void ResourcePreloaderEditor::edit(ResourcePreloader *p_preloader) { preloader = p_preloader; @@ -352,7 +355,7 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { vbc->add_child(hbc); load = memnew(Button); - load->set_tooltip(TTR("Load Resource")); + load->set_tooltip_text(TTR("Load Resource")); hbc->add_child(load); paste = memnew(Button); @@ -387,7 +390,7 @@ ResourcePreloaderEditor::ResourcePreloaderEditor() { } void ResourcePreloaderEditorPlugin::edit(Object *p_object) { - preloader_editor->set_undo_redo(&get_undo_redo()); + preloader_editor->set_undo_redo(EditorNode::get_undo_redo()); ResourcePreloader *s = Object::cast_to<ResourcePreloader>(p_object); if (!s) { return; diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h index 96cef3de21..ef80283dae 100644 --- a/editor/plugins/resource_preloader_editor_plugin.h +++ b/editor/plugins/resource_preloader_editor_plugin.h @@ -37,6 +37,7 @@ #include "scene/main/resource_preloader.h" class EditorFileDialog; +class EditorUndoRedoManager; class ResourcePreloaderEditor : public PanelContainer { GDCLASS(ResourcePreloaderEditor, PanelContainer); @@ -66,7 +67,7 @@ class ResourcePreloaderEditor : public PanelContainer { void _cell_button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button); void _item_edited(); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; 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; @@ -78,7 +79,7 @@ protected: static void _bind_methods(); public: - void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void edit(ResourcePreloader *p_preloader); ResourcePreloaderEditor(); diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index 681dd476e3..49196d6c79 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -198,7 +198,7 @@ void EditorPropertyRootMotion::_node_clear() { void EditorPropertyRootMotion::update_property() { NodePath p = get_edited_object()->get(get_edited_property()); - assign->set_tooltip(p); + assign->set_tooltip_text(p); if (p == NodePath()) { assign->set_icon(Ref<Texture2D>()); assign->set_text(TTR("Assign...")); @@ -234,7 +234,6 @@ void EditorPropertyRootMotion::setup(const NodePath &p_base_hint) { void EditorPropertyRootMotion::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { Ref<Texture2D> t = get_theme_icon(SNAME("Clear"), SNAME("EditorIcons")); clear->set_icon(t); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index c53ac59c11..1a692cffcc 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -49,7 +49,6 @@ #include "editor/find_in_files.h" #include "editor/node_dock.h" #include "editor/plugins/shader_editor_plugin.h" -#include "modules/visual_script/editor/visual_script_editor.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" #include "script_text_editor.h" @@ -66,12 +65,12 @@ String EditorSyntaxHighlighter::_get_name() const { return "Unnamed"; } -Array EditorSyntaxHighlighter::_get_supported_languages() const { - Array ret; +PackedStringArray EditorSyntaxHighlighter::_get_supported_languages() const { + PackedStringArray ret; if (GDVIRTUAL_CALL(_get_supported_languages, ret)) { return ret; } - return Array(); + return PackedStringArray(); } Ref<EditorSyntaxHighlighter> EditorSyntaxHighlighter::_create() const { @@ -353,9 +352,9 @@ void ScriptEditorQuickOpen::_notification(int p_what) { connect("confirmed", callable_mp(this, &ScriptEditorQuickOpen::_confirmed)); search_box->set_clear_button_enabled(true); - [[fallthrough]]; - } - case NOTIFICATION_VISIBILITY_CHANGED: { + } break; + + case NOTIFICATION_THEME_CHANGED: { search_box->set_right_icon(search_options->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); } break; @@ -1156,8 +1155,8 @@ Ref<Script> ScriptEditor::_get_current_script() { } } -Array ScriptEditor::_get_open_scripts() const { - Array ret; +TypedArray<Script> ScriptEditor::_get_open_scripts() const { + TypedArray<Script> ret; Vector<Ref<Script>> scripts = get_open_scripts(); int scrits_amount = scripts.size(); for (int idx_script = 0; idx_script < scrits_amount; idx_script++) { @@ -1408,8 +1407,6 @@ void ScriptEditor::_menu_option(int p_option) { es->set_editor(EditorNode::get_singleton()); es->_run(); - - EditorNode::get_undo_redo()->clear_history(); } break; case FILE_CLOSE: { if (current->is_unsaved()) { @@ -1616,8 +1613,8 @@ void ScriptEditor::_notification(int p_what) { EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ScriptEditor::_editor_settings_changed)); EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &ScriptEditor::_filesystem_changed)); _editor_settings_changed(); - [[fallthrough]]; - } + } break; + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { @@ -1734,7 +1731,7 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) { continue; } - Array bpoints = se->get_breakpoints(); + PackedInt32Array bpoints = se->get_breakpoints(); for (int j = 0; j < bpoints.size(); j++) { p_breakpoints->push_back(base + ":" + itos((int)bpoints[j] + 1)); } @@ -2382,8 +2379,8 @@ bool ScriptEditor::edit(const Ref<Resource> &p_resource, int p_line, int p_col, se->add_syntax_highlighter(highlighter); if (script != nullptr && !highlighter_set) { - Array languages = highlighter->_get_supported_languages(); - if (languages.find(script->get_language()->get_name()) > -1) { + PackedStringArray languages = highlighter->_get_supported_languages(); + if (languages.has(script->get_language()->get_name())) { se->set_syntax_highlighter(highlighter); highlighter_set = true; } @@ -3448,8 +3445,8 @@ Vector<Ref<Script>> ScriptEditor::get_open_scripts() const { return out_scripts; } -Array ScriptEditor::_get_open_script_editors() const { - Array script_editors; +TypedArray<ScriptEditorBase> ScriptEditor::_get_open_script_editors() const { + TypedArray<ScriptEditorBase> script_editors; for (int i = 0; i < tab_container->get_tab_count(); i++) { ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i)); if (!se) { @@ -3715,7 +3712,7 @@ ScriptEditor::ScriptEditor() { members_overview_alphabeta_sort_button = memnew(Button); members_overview_alphabeta_sort_button->set_flat(true); - members_overview_alphabeta_sort_button->set_tooltip(TTR("Toggle alphabetical sorting of the method list.")); + members_overview_alphabeta_sort_button->set_tooltip_text(TTR("Toggle alphabetical sorting of the method list.")); members_overview_alphabeta_sort_button->set_toggle_mode(true); members_overview_alphabeta_sort_button->set_pressed(EditorSettings::get_singleton()->get("text_editor/script_list/sort_members_outline_alphabetically")); members_overview_alphabeta_sort_button->connect("toggled", callable_mp(this, &ScriptEditor::_toggle_members_overview_alpha_sort)); @@ -3861,14 +3858,14 @@ ScriptEditor::ScriptEditor() { site_search->set_text(TTR("Online Docs")); site_search->connect("pressed", callable_mp(this, &ScriptEditor::_menu_option).bind(SEARCH_WEBSITE)); menu_hb->add_child(site_search); - site_search->set_tooltip(TTR("Open Godot online documentation.")); + site_search->set_tooltip_text(TTR("Open Godot online documentation.")); help_search = memnew(Button); help_search->set_flat(true); help_search->set_text(TTR("Search Help")); help_search->connect("pressed", callable_mp(this, &ScriptEditor::_menu_option).bind(SEARCH_HELP)); menu_hb->add_child(help_search); - help_search->set_tooltip(TTR("Search the reference documentation.")); + help_search->set_tooltip_text(TTR("Search the reference documentation.")); menu_hb->add_child(memnew(VSeparator)); @@ -3877,14 +3874,14 @@ ScriptEditor::ScriptEditor() { script_back->connect("pressed", callable_mp(this, &ScriptEditor::_history_back)); menu_hb->add_child(script_back); script_back->set_disabled(true); - script_back->set_tooltip(TTR("Go to previous edited document.")); + script_back->set_tooltip_text(TTR("Go to previous edited document.")); script_forward = memnew(Button); script_forward->set_flat(true); script_forward->connect("pressed", callable_mp(this, &ScriptEditor::_history_forward)); menu_hb->add_child(script_forward); script_forward->set_disabled(true); - script_forward->set_tooltip(TTR("Go to next edited document.")); + script_forward->set_tooltip_text(TTR("Go to next edited document.")); tab_container->connect("tab_changed", callable_mp(this, &ScriptEditor::_tab_changed)); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 9f088aac49..d1898efb69 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -59,11 +59,11 @@ protected: static void _bind_methods(); GDVIRTUAL0RC(String, _get_name) - GDVIRTUAL0RC(Array, _get_supported_languages) + GDVIRTUAL0RC(PackedStringArray, _get_supported_languages) public: virtual String _get_name() const; - virtual Array _get_supported_languages() const; + virtual PackedStringArray _get_supported_languages() const; void _set_edited_resource(const Ref<Resource> &p_res) { edited_resourse = p_res; } Ref<RefCounted> _get_edited_resource() { return edited_resourse; } @@ -156,7 +156,7 @@ public: virtual void ensure_focus() = 0; virtual void tag_saved_version() = 0; virtual void reload(bool p_soft) {} - virtual Array get_breakpoints() = 0; + virtual PackedInt32Array get_breakpoints() = 0; virtual void set_breakpoint(int p_line, bool p_enabled) = 0; virtual void clear_breakpoints() = 0; virtual void add_callback(const String &p_function, PackedStringArray p_args) = 0; @@ -386,7 +386,7 @@ class ScriptEditor : public PanelContainer { Array _get_cached_breakpoints_for_script(const String &p_path) const; ScriptEditorBase *_get_current_editor() const; - Array _get_open_script_editors() const; + TypedArray<ScriptEditorBase> _get_open_script_editors() const; Ref<ConfigFile> script_editor_cache; void _save_editor_state(ScriptEditorBase *p_editor); @@ -452,7 +452,7 @@ class ScriptEditor : public PanelContainer { void _file_dialog_action(String p_file); Ref<Script> _get_current_script(); - Array _get_open_scripts() const; + TypedArray<Script> _get_open_scripts() const; HashSet<String> textfile_extensions; Ref<TextFile> _load_text_file(const String &p_path, Error *r_error) const; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 5d5f452390..421b258f49 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -596,7 +596,7 @@ void ScriptTextEditor::_update_bookmark_list() { bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); - Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines(); + PackedInt32Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines(); if (bookmark_list.size() == 0) { return; } @@ -751,7 +751,7 @@ void ScriptTextEditor::_update_breakpoint_list() { breakpoints_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_breakpoint"), DEBUG_GOTO_NEXT_BREAKPOINT); breakpoints_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_breakpoint"), DEBUG_GOTO_PREV_BREAKPOINT); - Array breakpoint_list = code_editor->get_text_editor()->get_breakpointed_lines(); + PackedInt32Array breakpoint_list = code_editor->get_text_editor()->get_breakpointed_lines(); if (breakpoint_list.size() == 0) { return; } @@ -1264,7 +1264,7 @@ void ScriptTextEditor::_edit_option(int p_op) { EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), line + 1, dobreak); } break; case DEBUG_REMOVE_ALL_BREAKPOINTS: { - Array bpoints = tx->get_breakpointed_lines(); + PackedInt32Array bpoints = tx->get_breakpointed_lines(); for (int i = 0; i < bpoints.size(); i++) { int line = bpoints[i]; @@ -1274,7 +1274,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } } break; case DEBUG_GOTO_NEXT_BREAKPOINT: { - Array bpoints = tx->get_breakpointed_lines(); + PackedInt32Array bpoints = tx->get_breakpointed_lines(); if (bpoints.size() <= 0) { return; } @@ -1300,7 +1300,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case DEBUG_GOTO_PREV_BREAKPOINT: { - Array bpoints = tx->get_breakpointed_lines(); + PackedInt32Array bpoints = tx->get_breakpointed_lines(); if (bpoints.size() <= 0) { return; } @@ -1392,7 +1392,11 @@ void ScriptTextEditor::_change_syntax_highlighter(int p_idx) { void ScriptTextEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_ENTER_TREE: { + code_editor->get_text_editor()->set_gutter_width(connection_gutter, code_editor->get_text_editor()->get_line_height()); + } break; + + case NOTIFICATION_THEME_CHANGED: { if (!editor_enabled) { break; } @@ -1400,9 +1404,6 @@ void ScriptTextEditor::_notification(int p_what) { _update_warnings(); _update_errors(); } - [[fallthrough]]; - case NOTIFICATION_ENTER_TREE: { - code_editor->get_text_editor()->set_gutter_width(connection_gutter, code_editor->get_text_editor()->get_line_height()); } break; } } @@ -1441,7 +1442,7 @@ void ScriptTextEditor::reload(bool p_soft) { scr->get_language()->reload_tool_script(scr, soft); } -Array ScriptTextEditor::get_breakpoints() { +PackedInt32Array ScriptTextEditor::get_breakpoints() { return code_editor->get_text_editor()->get_breakpointed_lines(); } diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index fc87c84a2c..8d2fb98721 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -229,7 +229,7 @@ public: virtual void clear_executing_line() override; virtual void reload(bool p_soft) override; - virtual Array get_breakpoints() override; + virtual PackedInt32Array get_breakpoints() override; virtual void set_breakpoint(int p_line, bool p_enabled) override; virtual void clear_breakpoints() override; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index d70c50f72a..b4a0216e9f 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -712,7 +712,6 @@ void ShaderEditor::_menu_option(int p_option) { void ShaderEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { PopupMenu *popup = help_menu->get_popup(); popup->set_item_icon(popup->get_item_index(HELP_DOCS), get_theme_icon(SNAME("ExternalLink"), SNAME("EditorIcons"))); @@ -994,7 +993,7 @@ void ShaderEditor::_update_bookmark_list() { bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); - Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines(); + PackedInt32Array bookmark_list = shader_editor->get_text_editor()->get_bookmarked_lines(); if (bookmark_list.size() == 0) { return; } @@ -1257,6 +1256,17 @@ void ShaderEditorPlugin::_update_shader_list_status() { } } +void ShaderEditorPlugin::_move_shader_tab(int p_from, int p_to) { + if (p_from == p_to) { + return; + } + EditedShader es = edited_shaders[p_from]; + edited_shaders.remove_at(p_from); + edited_shaders.insert(p_to, es); + shader_tabs->move_child(shader_tabs->get_tab_control(p_from), p_to); + _update_shader_list(); +} + void ShaderEditorPlugin::edit(Object *p_object) { EditedShader es; @@ -1451,6 +1461,109 @@ void ShaderEditorPlugin::_shader_include_created(Ref<ShaderInclude> p_shader_inc EditorNode::get_singleton()->push_item(p_shader_inc.ptr()); } +Variant ShaderEditorPlugin::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + if (shader_list->get_item_count() == 0) { + return Variant(); + } + + int idx = shader_list->get_item_at_position(p_point); + if (idx < 0) { + return Variant(); + } + + HBoxContainer *drag_preview = memnew(HBoxContainer); + String preview_name = shader_list->get_item_text(idx); + Ref<Texture2D> preview_icon = shader_list->get_item_icon(idx); + + if (!preview_icon.is_null()) { + TextureRect *tf = memnew(TextureRect); + tf->set_texture(preview_icon); + tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED); + drag_preview->add_child(tf); + } + Label *label = memnew(Label(preview_name)); + drag_preview->add_child(label); + main_split->set_drag_preview(drag_preview); + + Dictionary drag_data; + drag_data["type"] = "shader_list_element"; + drag_data["shader_list_element"] = idx; + + return drag_data; +} + +bool ShaderEditorPlugin::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + Dictionary d = p_data; + if (!d.has("type")) { + return false; + } + + if (String(d["type"]) == "shader_list_element") { + return true; + } + + if (String(d["type"]) == "files") { + Vector<String> files = d["files"]; + + if (files.size() == 0) { + return false; + } + + for (int i = 0; i < files.size(); i++) { + String file = files[i]; + if (ResourceLoader::exists(file, "Shader")) { + Ref<Shader> shader = ResourceLoader::load(file); + if (shader.is_valid()) { + return true; + } + } + } + return false; + } + + return false; +} + +void ShaderEditorPlugin::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + if (!can_drop_data_fw(p_point, p_data, p_from)) { + return; + } + + Dictionary d = p_data; + if (!d.has("type")) { + return; + } + + if (String(d["type"]) == "shader_list_element") { + int idx = d["shader_list_element"]; + int new_idx = shader_list->get_item_at_position(p_point); + _move_shader_tab(idx, new_idx); + return; + } + + if (String(d["type"]) == "files") { + Vector<String> files = d["files"]; + + for (int i = 0; i < files.size(); i++) { + String file = files[i]; + if (!ResourceLoader::exists(file, "Shader")) { + continue; + } + + Ref<Resource> res = ResourceLoader::load(file); + if (res.is_valid()) { + edit(res.ptr()); + } + } + } +} + +void ShaderEditorPlugin::_bind_methods() { + ClassDB::bind_method(D_METHOD("_get_drag_data_fw", "point", "from"), &ShaderEditorPlugin::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("_can_drop_data_fw", "point", "data", "from"), &ShaderEditorPlugin::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("_drop_data_fw", "point", "data", "from"), &ShaderEditorPlugin::drop_data_fw); +} + ShaderEditorPlugin::ShaderEditorPlugin() { main_split = memnew(HSplitContainer); @@ -1483,6 +1596,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() { vb->add_child(shader_list); shader_list->connect("item_selected", callable_mp(this, &ShaderEditorPlugin::_shader_selected)); shader_list->connect("item_clicked", callable_mp(this, &ShaderEditorPlugin::_shader_list_clicked)); + shader_list->set_drag_forwarding(this); main_split->add_child(vb); vb->set_custom_minimum_size(Size2(200, 300) * EDSCALE); diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 0980cc4db2..afd38ef71a 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -250,6 +250,14 @@ class ShaderEditorPlugin : public EditorPlugin { void _shader_created(Ref<Shader> p_shader); void _shader_include_created(Ref<ShaderInclude> p_shader_inc); void _update_shader_list_status(); + void _move_shader_tab(int p_from, int p_to); + + 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; + void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); + +protected: + static void _bind_methods(); public: virtual void edit(Object *p_object) override; diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp index 5a1505c232..3dc068a72a 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.cpp +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/2d/mesh_instance_2d.h" #include "scene/gui/box_container.h" #include "thirdparty/misc/clipper.hpp" @@ -59,7 +60,7 @@ void Skeleton2DEditor::_menu_option(int p_option) { err_dialog->popup_centered(); return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Rest Pose to Bones")); for (int i = 0; i < node->get_bone_count(); i++) { Bone2D *bone = node->get_bone(i); @@ -75,7 +76,7 @@ void Skeleton2DEditor::_menu_option(int p_option) { err_dialog->popup_centered(); return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Create Rest Pose from Bones")); for (int i = 0; i < node->get_bone_count(); i++) { Bone2D *bone = node->get_bone(i); diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp index c453ed26aa..f21adabffb 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.cpp +++ b/editor/plugins/skeleton_3d_editor_plugin.cpp @@ -31,10 +31,10 @@ #include "skeleton_3d_editor_plugin.h" #include "core/io/resource_saver.h" -#include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_properties.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "node_3d_editor_plugin.h" #include "scene/3d/collision_shape_3d.h" @@ -221,7 +221,7 @@ void Skeleton3DEditor::set_keyable(const bool p_keyable) { }; void Skeleton3DEditor::set_bone_options_enabled(const bool p_bone_options_enabled) { - skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INIT_SELECTED_POSES, !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); }; @@ -231,12 +231,12 @@ void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) { } switch (p_skeleton_option) { - case SKELETON_OPTION_INIT_ALL_POSES: { - init_pose(true); + case SKELETON_OPTION_RESET_ALL_POSES: { + reset_pose(true); break; } - case SKELETON_OPTION_INIT_SELECTED_POSES: { - init_pose(false); + case SKELETON_OPTION_RESET_SELECTED_POSES: { + reset_pose(false); break; } case SKELETON_OPTION_ALL_POSES_TO_RESTS: { @@ -258,7 +258,7 @@ void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) { } } -void Skeleton3DEditor::init_pose(const bool p_all_bones) { +void Skeleton3DEditor::reset_pose(const bool p_all_bones) { if (!skeleton) { return; } @@ -267,31 +267,25 @@ void Skeleton3DEditor::init_pose(const bool p_all_bones) { return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS); if (p_all_bones) { for (int i = 0; i < bone_len; i++) { - Transform3D rest = skeleton->get_bone_rest(i); - ur->add_do_method(skeleton, "set_bone_pose_position", i, rest.origin); - ur->add_do_method(skeleton, "set_bone_pose_rotation", i, rest.basis.get_rotation_quaternion()); - ur->add_do_method(skeleton, "set_bone_pose_scale", i, rest.basis.get_scale()); ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i)); ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i)); ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i)); } + ur->add_do_method(skeleton, "reset_bone_poses"); } else { // Todo: Do method with multiple bone selection. if (selected_bone == -1) { ur->commit_action(); return; } - Transform3D rest = skeleton->get_bone_rest(selected_bone); - ur->add_do_method(skeleton, "set_bone_pose_position", selected_bone, rest.origin); - ur->add_do_method(skeleton, "set_bone_pose_rotation", selected_bone, rest.basis.get_rotation_quaternion()); - ur->add_do_method(skeleton, "set_bone_pose_scale", selected_bone, rest.basis.get_scale()); ur->add_undo_method(skeleton, "set_bone_pose_position", selected_bone, skeleton->get_bone_pose_position(selected_bone)); ur->add_undo_method(skeleton, "set_bone_pose_rotation", selected_bone, skeleton->get_bone_pose_rotation(selected_bone)); ur->add_undo_method(skeleton, "set_bone_pose_scale", selected_bone, skeleton->get_bone_pose_scale(selected_bone)); + ur->add_do_method(skeleton, "reset_bone_pose", selected_bone); } ur->commit_action(); } @@ -340,7 +334,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) { return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS); if (p_all_bones) { for (int i = 0; i < bone_len; i++) { @@ -360,7 +354,7 @@ void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) { } void Skeleton3DEditor::create_physical_skeleton() { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ERR_FAIL_COND(!get_tree()); Node *owner = get_tree()->get_edited_scene_root(); @@ -593,7 +587,7 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se Node *node = get_node_or_null(p_skeleton_path); Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(node); ERR_FAIL_NULL(skeleton); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Bone Parentage")); // If the target is a child of ourselves, we move only *us* and not our children. if (skeleton->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) { @@ -721,8 +715,8 @@ void Skeleton3DEditor::create_editors() { // Skeleton options. PopupMenu *p = skeleton_options->get_popup(); - p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_all_poses", TTR("Init all Poses")), SKELETON_OPTION_INIT_ALL_POSES); - p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_selected_poses", TTR("Init selected Poses")), SKELETON_OPTION_INIT_SELECTED_POSES); + p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/reset_all_poses", TTR("Reset all bone Poses")), SKELETON_OPTION_RESET_ALL_POSES); + p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/reset_selected_poses", TTR("Reset selected Poses")), SKELETON_OPTION_RESET_SELECTED_POSES); p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/all_poses_to_rests", TTR("Apply all poses to rests")), SKELETON_OPTION_ALL_POSES_TO_RESTS); p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/selected_poses_to_rests", TTR("Apply selected poses to rests")), SKELETON_OPTION_SELECTED_POSES_TO_RESTS); p->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON); @@ -739,7 +733,7 @@ void Skeleton3DEditor::create_editors() { edit_mode_button->set_flat(true); edit_mode_button->set_toggle_mode(true); edit_mode_button->set_focus_mode(FOCUS_NONE); - edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints.")); + edit_mode_button->set_tooltip_text(TTR("Edit Mode\nShow buttons on joints.")); edit_mode_button->connect("toggled", callable_mp(this, &Skeleton3DEditor::edit_mode_toggled)); edit_mode = false; @@ -760,7 +754,7 @@ void Skeleton3DEditor::create_editors() { key_loc_button->set_toggle_mode(true); key_loc_button->set_pressed(false); key_loc_button->set_focus_mode(FOCUS_NONE); - key_loc_button->set_tooltip(TTR("Translation mask for inserting keys.")); + key_loc_button->set_tooltip_text(TTR("Translation mask for inserting keys.")); animation_hb->add_child(key_loc_button); key_rot_button = memnew(Button); @@ -768,7 +762,7 @@ void Skeleton3DEditor::create_editors() { key_rot_button->set_toggle_mode(true); key_rot_button->set_pressed(true); key_rot_button->set_focus_mode(FOCUS_NONE); - key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys.")); + key_rot_button->set_tooltip_text(TTR("Rotation mask for inserting keys.")); animation_hb->add_child(key_rot_button); key_scale_button = memnew(Button); @@ -776,14 +770,14 @@ void Skeleton3DEditor::create_editors() { key_scale_button->set_toggle_mode(true); key_scale_button->set_pressed(false); key_scale_button->set_focus_mode(FOCUS_NONE); - key_scale_button->set_tooltip(TTR("Scale mask for inserting keys.")); + key_scale_button->set_tooltip_text(TTR("Scale mask for inserting keys.")); animation_hb->add_child(key_scale_button); key_insert_button = memnew(Button); key_insert_button->set_flat(true); key_insert_button->set_focus_mode(FOCUS_NONE); key_insert_button->connect("pressed", callable_mp(this, &Skeleton3DEditor::insert_keys).bind(false)); - key_insert_button->set_tooltip(TTR("Insert key of bone poses already exist track.")); + key_insert_button->set_tooltip_text(TTR("Insert key of bone poses already exist track.")); key_insert_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_to_existing_tracks", TTR("Insert Key (Existing Tracks)"), Key::INSERT)); animation_hb->add_child(key_insert_button); @@ -791,7 +785,7 @@ void Skeleton3DEditor::create_editors() { key_insert_all_button->set_flat(true); key_insert_all_button->set_focus_mode(FOCUS_NONE); key_insert_all_button->connect("pressed", callable_mp(this, &Skeleton3DEditor::insert_keys).bind(true)); - key_insert_all_button->set_tooltip(TTR("Insert key of all bone poses.")); + key_insert_all_button->set_tooltip_text(TTR("Insert key of all bone poses.")); key_insert_all_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_of_all_bones", TTR("Insert Key (All Bones)"), KeyModifierMask::CMD + Key::INSERT)); animation_hb->add_child(key_insert_all_button); @@ -1315,7 +1309,7 @@ void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, c Skeleton3DEditor *se = Skeleton3DEditor::get_singleton(); Node3DEditor *ne = Node3DEditor::get_singleton(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Bone Transform")); if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) { for (int i = 0; i < p_ids.size(); i++) { diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h index 975b54fa77..9747ed8374 100644 --- a/editor/plugins/skeleton_3d_editor_plugin.h +++ b/editor/plugins/skeleton_3d_editor_plugin.h @@ -31,6 +31,7 @@ #ifndef SKELETON_3D_EDITOR_PLUGIN_H #define SKELETON_3D_EDITOR_PLUGIN_H +#include "editor/editor_file_dialog.h" #include "editor/editor_plugin.h" #include "editor/editor_properties.h" #include "node_3d_editor_plugin.h" @@ -40,6 +41,7 @@ #include "scene/resources/immediate_mesh.h" class EditorInspectorPluginSkeleton; +class EditorUndoRedoManager; class Joint; class PhysicalBone3D; class Skeleton3DEditorPlugin; @@ -63,7 +65,7 @@ class BoneTransformEditor : public VBoxContainer { Skeleton3D *skeleton = nullptr; // String property; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; bool toggle_enabled = false; bool updating = false; @@ -96,8 +98,8 @@ class Skeleton3DEditor : public VBoxContainer { friend class Skeleton3DEditorPlugin; enum SkeletonOption { - SKELETON_OPTION_INIT_ALL_POSES, - SKELETON_OPTION_INIT_SELECTED_POSES, + SKELETON_OPTION_RESET_ALL_POSES, + SKELETON_OPTION_RESET_SELECTED_POSES, SKELETON_OPTION_ALL_POSES_TO_RESTS, SKELETON_OPTION_SELECTED_POSES_TO_RESTS, SKELETON_OPTION_CREATE_PHYSICAL_SKELETON, @@ -148,7 +150,7 @@ class Skeleton3DEditor : public VBoxContainer { void create_editors(); - void init_pose(const bool p_all_bones); + void reset_pose(const bool p_all_bones); void pose_to_rest(const bool p_all_bones); void insert_keys(const bool p_all_bones); diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp index 7d350fd46f..a493827d60 100644 --- a/editor/plugins/sprite_2d_editor_plugin.cpp +++ b/editor/plugins/sprite_2d_editor_plugin.cpp @@ -34,6 +34,7 @@ #include "core/math/geometry_2d.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/scene_tree_dock.h" #include "scene/2d/collision_polygon_2d.h" #include "scene/2d/light_occluder_2d.h" @@ -342,7 +343,7 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() { MeshInstance2D *mesh_instance = memnew(MeshInstance2D); mesh_instance->set_mesh(mesh); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Convert to MeshInstance2D")); ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false); ur->add_do_reference(mesh_instance); @@ -400,7 +401,7 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() { polygon_2d_instance->set_polygon(polygon); polygon_2d_instance->set_polygons(polys); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Convert to Polygon2D")); ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false); ur->add_do_reference(polygon_2d_instance); @@ -422,7 +423,7 @@ void Sprite2DEditor::_create_collision_polygon_2d_node() { CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D); collision_polygon_2d_instance->set_polygon(outline); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Create CollisionPolygon2D Sibling")); ur->add_do_method(this, "_add_as_sibling_or_child", node, collision_polygon_2d_instance); ur->add_do_reference(collision_polygon_2d_instance); @@ -455,7 +456,7 @@ void Sprite2DEditor::_create_light_occluder_2d_node() { LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D); light_occluder_2d_instance->set_occluder_polygon(polygon); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Create LightOccluder2D Sibling")); ur->add_do_method(this, "_add_as_sibling_or_child", node, light_occluder_2d_instance); ur->add_do_reference(light_occluder_2d_instance); @@ -505,7 +506,6 @@ void Sprite2DEditor::_debug_uv_draw() { void Sprite2DEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { options->set_icon(get_theme_icon(SNAME("Sprite2D"), SNAME("EditorIcons"))); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index a39d24a167..56da3c986e 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/scene_tree_dock.h" #include "scene/3d/sprite_3d.h" #include "scene/gui/center_container.h" @@ -407,7 +408,6 @@ void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { void SpriteFramesEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { load->set_icon(get_theme_icon(SNAME("Load"), SNAME("EditorIcons"))); load_sheet->set_icon(get_theme_icon(SNAME("SpriteSheet"), SNAME("EditorIcons"))); @@ -1010,6 +1010,10 @@ void SpriteFramesEditor::edit(SpriteFrames *p_frames) { } } +void SpriteFramesEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + Variant SpriteFramesEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { if (!frames->has_animation(edited_anim)) { return false; @@ -1156,13 +1160,13 @@ SpriteFramesEditor::SpriteFramesEditor() { new_anim = memnew(Button); new_anim->set_flat(true); - new_anim->set_tooltip(TTR("New Animation")); + new_anim->set_tooltip_text(TTR("New Animation")); hbc_animlist->add_child(new_anim); new_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_add)); remove_anim = memnew(Button); remove_anim->set_flat(true); - remove_anim->set_tooltip(TTR("Remove Animation")); + remove_anim->set_tooltip_text(TTR("Remove Animation")); hbc_animlist->add_child(remove_anim); remove_anim->connect("pressed", callable_mp(this, &SpriteFramesEditor::_animation_remove)); @@ -1210,53 +1214,53 @@ SpriteFramesEditor::SpriteFramesEditor() { load = memnew(Button); load->set_flat(true); - load->set_tooltip(TTR("Add a Texture from File")); + load->set_tooltip_text(TTR("Add a Texture from File")); hbc->add_child(load); load_sheet = memnew(Button); load_sheet->set_flat(true); - load_sheet->set_tooltip(TTR("Add Frames from a Sprite Sheet")); + load_sheet->set_tooltip_text(TTR("Add Frames from a Sprite Sheet")); hbc->add_child(load_sheet); hbc->add_child(memnew(VSeparator)); copy = memnew(Button); copy->set_flat(true); - copy->set_tooltip(TTR("Copy")); + copy->set_tooltip_text(TTR("Copy")); hbc->add_child(copy); paste = memnew(Button); paste->set_flat(true); - paste->set_tooltip(TTR("Paste")); + paste->set_tooltip_text(TTR("Paste")); hbc->add_child(paste); hbc->add_child(memnew(VSeparator)); empty = memnew(Button); empty->set_flat(true); - empty->set_tooltip(TTR("Insert Empty (Before)")); + empty->set_tooltip_text(TTR("Insert Empty (Before)")); hbc->add_child(empty); empty2 = memnew(Button); empty2->set_flat(true); - empty2->set_tooltip(TTR("Insert Empty (After)")); + empty2->set_tooltip_text(TTR("Insert Empty (After)")); hbc->add_child(empty2); hbc->add_child(memnew(VSeparator)); move_up = memnew(Button); move_up->set_flat(true); - move_up->set_tooltip(TTR("Move (Before)")); + move_up->set_tooltip_text(TTR("Move (Before)")); hbc->add_child(move_up); move_down = memnew(Button); move_down->set_flat(true); - move_down->set_tooltip(TTR("Move (After)")); + move_down->set_tooltip_text(TTR("Move (After)")); hbc->add_child(move_down); _delete = memnew(Button); _delete->set_flat(true); - _delete->set_tooltip(TTR("Delete")); + _delete->set_tooltip_text(TTR("Delete")); hbc->add_child(_delete); hbc->add_spacer(); @@ -1264,19 +1268,19 @@ SpriteFramesEditor::SpriteFramesEditor() { zoom_out = memnew(Button); zoom_out->connect("pressed", callable_mp(this, &SpriteFramesEditor::_zoom_out)); zoom_out->set_flat(true); - zoom_out->set_tooltip(TTR("Zoom Out")); + zoom_out->set_tooltip_text(TTR("Zoom Out")); hbc->add_child(zoom_out); zoom_reset = memnew(Button); zoom_reset->connect("pressed", callable_mp(this, &SpriteFramesEditor::_zoom_reset)); zoom_reset->set_flat(true); - zoom_reset->set_tooltip(TTR("Zoom Reset")); + zoom_reset->set_tooltip_text(TTR("Zoom Reset")); hbc->add_child(zoom_reset); zoom_in = memnew(Button); zoom_in->connect("pressed", callable_mp(this, &SpriteFramesEditor::_zoom_in)); zoom_in->set_flat(true); - zoom_in->set_tooltip(TTR("Zoom In")); + zoom_in->set_tooltip_text(TTR("Zoom In")); hbc->add_child(zoom_in); file = memnew(EditorFileDialog); @@ -1429,21 +1433,21 @@ SpriteFramesEditor::SpriteFramesEditor() { split_sheet_zoom_out = memnew(Button); split_sheet_zoom_out->set_flat(true); split_sheet_zoom_out->set_focus_mode(FOCUS_NONE); - split_sheet_zoom_out->set_tooltip(TTR("Zoom Out")); + split_sheet_zoom_out->set_tooltip_text(TTR("Zoom Out")); split_sheet_zoom_out->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_zoom_out)); split_sheet_zoom_hb->add_child(split_sheet_zoom_out); split_sheet_zoom_reset = memnew(Button); split_sheet_zoom_reset->set_flat(true); split_sheet_zoom_reset->set_focus_mode(FOCUS_NONE); - split_sheet_zoom_reset->set_tooltip(TTR("Zoom Reset")); + split_sheet_zoom_reset->set_tooltip_text(TTR("Zoom Reset")); split_sheet_zoom_reset->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_zoom_reset)); split_sheet_zoom_hb->add_child(split_sheet_zoom_reset); split_sheet_zoom_in = memnew(Button); split_sheet_zoom_in->set_flat(true); split_sheet_zoom_in->set_focus_mode(FOCUS_NONE); - split_sheet_zoom_in->set_tooltip(TTR("Zoom In")); + split_sheet_zoom_in->set_tooltip_text(TTR("Zoom In")); split_sheet_zoom_in->connect("pressed", callable_mp(this, &SpriteFramesEditor::_sheet_zoom_in)); split_sheet_zoom_hb->add_child(split_sheet_zoom_in); @@ -1471,7 +1475,7 @@ SpriteFramesEditor::SpriteFramesEditor() { } void SpriteFramesEditorPlugin::edit(Object *p_object) { - frames_editor->set_undo_redo(&get_undo_redo()); + frames_editor->set_undo_redo(get_undo_redo()); SpriteFrames *s; AnimatedSprite2D *animated_sprite = Object::cast_to<AnimatedSprite2D>(p_object); diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 6352259b73..f2530b732f 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -45,6 +45,7 @@ #include "scene/gui/tree.h" class EditorFileDialog; +class EditorUndoRedoManager; class SpriteFramesEditor : public HSplitContainer { GDCLASS(SpriteFramesEditor, HSplitContainer); @@ -151,7 +152,7 @@ class SpriteFramesEditor : public HSplitContainer { bool updating; bool updating_split_settings = false; // Skip SpinBox/Range callback when setting value by code. - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; 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; @@ -176,7 +177,7 @@ protected: static void _bind_methods(); public: - void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; } + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void edit(SpriteFrames *p_frames); SpriteFramesEditor(); diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp index d4baff34e2..6dbba3ced2 100644 --- a/editor/plugins/style_box_editor_plugin.cpp +++ b/editor/plugins/style_box_editor_plugin.cpp @@ -71,15 +71,7 @@ void StyleBoxPreview::_sb_changed() { void StyleBoxPreview::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - if (!is_inside_tree()) { - // TODO: This is a workaround because `NOTIFICATION_THEME_CHANGED` - // is getting called for some reason when the `TexturePreview` is - // getting destroyed, which causes `get_theme_font()` to return `nullptr`. - // See https://github.com/godotengine/godot/issues/50743. - break; - } grid_preview->set_normal_texture(get_theme_icon(SNAME("StyleBoxGridInvisible"), SNAME("EditorIcons"))); grid_preview->set_pressed_texture(get_theme_icon(SNAME("StyleBoxGridVisible"), SNAME("EditorIcons"))); grid_preview->set_hover_texture(get_theme_icon(SNAME("StyleBoxGridVisible"), SNAME("EditorIcons"))); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index 196d87da36..0900415b04 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -128,8 +128,8 @@ Control *TextEditor::get_base_editor() const { return code_editor->get_text_editor(); } -Array TextEditor::get_breakpoints() { - return Array(); +PackedInt32Array TextEditor::get_breakpoints() { + return PackedInt32Array(); } void TextEditor::reload_text() { @@ -165,7 +165,7 @@ void TextEditor::_update_bookmark_list() { bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); - Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines(); + PackedInt32Array bookmark_list = code_editor->get_text_editor()->get_bookmarked_lines(); if (bookmark_list.size() == 0) { return; } diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 4f0121da52..15f7c45653 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -118,7 +118,7 @@ public: virtual Variant get_edit_state() override; virtual void set_edit_state(const Variant &p_state) override; virtual Vector<String> get_functions() override; - virtual Array get_breakpoints() override; + virtual PackedInt32Array get_breakpoints() override; virtual void set_breakpoint(int p_line, bool p_enabled) override{}; virtual void clear_breakpoints() override{}; virtual void goto_line(int p_line, bool p_with_error = false) override; diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index be382759f5..1b79527352 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -38,16 +38,7 @@ TextureRect *TexturePreview::get_texture_display() { void TexturePreview::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { - if (!is_inside_tree()) { - // TODO: This is a workaround because `NOTIFICATION_THEME_CHANGED` - // is getting called for some reason when the `TexturePreview` is - // getting destroyed, which causes `get_theme_font()` to return `nullptr`. - // See https://github.com/godotengine/godot/issues/50743. - break; - } - if (metadata_label) { Ref<Font> metadata_label_font = get_theme_font(SNAME("expression"), SNAME("EditorFonts")); metadata_label->add_theme_font_override("font", metadata_label_font); diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 0bd8a8a484..1d0ae4d36f 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -36,6 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/check_box.h" #include "scene/gui/separator.h" #include "scene/gui/view_panner.h" @@ -820,7 +821,6 @@ void TextureRegionEditor::_update_autoslice() { void TextureRegionEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { edit_draw->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); } break; @@ -1109,19 +1109,19 @@ TextureRegionEditor::TextureRegionEditor() { zoom_out = memnew(Button); zoom_out->set_flat(true); - zoom_out->set_tooltip(TTR("Zoom Out")); + zoom_out->set_tooltip_text(TTR("Zoom Out")); zoom_out->connect("pressed", callable_mp(this, &TextureRegionEditor::_zoom_out)); zoom_hb->add_child(zoom_out); zoom_reset = memnew(Button); zoom_reset->set_flat(true); - zoom_reset->set_tooltip(TTR("Zoom Reset")); + zoom_reset->set_tooltip_text(TTR("Zoom Reset")); zoom_reset->connect("pressed", callable_mp(this, &TextureRegionEditor::_zoom_reset)); zoom_hb->add_child(zoom_reset); zoom_in = memnew(Button); zoom_in->set_flat(true); - zoom_in->set_tooltip(TTR("Zoom In")); + zoom_in->set_tooltip_text(TTR("Zoom In")); zoom_in->connect("pressed", callable_mp(this, &TextureRegionEditor::_zoom_in)); zoom_hb->add_child(zoom_in); diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index a18c87f153..e3bbaf49fc 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -40,6 +40,7 @@ #include "scene/resources/texture.h" class ViewPanner; +class EditorUndoRedoManager; class TextureRegionEditor : public AcceptDialog { GDCLASS(TextureRegionEditor, AcceptDialog); @@ -68,7 +69,7 @@ class TextureRegionEditor : public AcceptDialog { VScrollBar *vscroll = nullptr; HScrollBar *hscroll = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; Vector2 draw_ofs; float draw_zoom = 0.0; diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index bbc22c8622..ac67965a6c 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -35,6 +35,7 @@ #include "editor/editor_node.h" #include "editor/editor_resource_picker.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/progress_dialog.h" #include "scene/gui/color_picker.h" @@ -796,7 +797,7 @@ void ThemeItemImportTree::_import_selected() { ProgressDialog::get_singleton()->end_task("import_theme_items"); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Import Theme Items")); ur->add_do_method(*edited_theme, "clear"); @@ -838,7 +839,6 @@ bool ThemeItemImportTree::has_selected_items() const { void ThemeItemImportTree::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { select_icons_warning_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); select_icons_warning->add_theme_color_override("font_color", get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"))); @@ -1102,15 +1102,15 @@ ThemeItemImportTree::ThemeItemImportTree() { button_set->set_alignment(BoxContainer::ALIGNMENT_END); all_set->add_child(button_set); select_all_items_button->set_flat(true); - select_all_items_button->set_tooltip(select_all_items_tooltip); + select_all_items_button->set_tooltip_text(select_all_items_tooltip); button_set->add_child(select_all_items_button); select_all_items_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_select_all_data_type_pressed).bind(i)); select_full_items_button->set_flat(true); - select_full_items_button->set_tooltip(select_full_items_tooltip); + select_full_items_button->set_tooltip_text(select_full_items_tooltip); button_set->add_child(select_full_items_button); select_full_items_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_select_full_data_type_pressed).bind(i)); deselect_all_items_button->set_flat(true); - deselect_all_items_button->set_tooltip(deselect_all_items_tooltip); + deselect_all_items_button->set_tooltip_text(deselect_all_items_tooltip); button_set->add_child(deselect_all_items_button); deselect_all_items_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_deselect_all_data_type_pressed).bind(i)); @@ -1141,12 +1141,12 @@ ThemeItemImportTree::ThemeItemImportTree() { import_collapse_types_button = memnew(Button); import_collapse_types_button->set_flat(true); - import_collapse_types_button->set_tooltip(TTR("Collapse types.")); + import_collapse_types_button->set_tooltip_text(TTR("Collapse types.")); import_buttons->add_child(import_collapse_types_button); import_collapse_types_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_toggle_type_items).bind(true)); import_expand_types_button = memnew(Button); import_expand_types_button->set_flat(true); - import_expand_types_button->set_tooltip(TTR("Expand types.")); + import_expand_types_button->set_tooltip_text(TTR("Expand types.")); import_buttons->add_child(import_expand_types_button); import_expand_types_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_toggle_type_items).bind(false)); @@ -1155,19 +1155,19 @@ ThemeItemImportTree::ThemeItemImportTree() { import_select_all_button = memnew(Button); import_select_all_button->set_flat(true); import_select_all_button->set_text(TTR("Select All")); - import_select_all_button->set_tooltip(TTR("Select all Theme items.")); + import_select_all_button->set_tooltip_text(TTR("Select all Theme items.")); import_buttons->add_child(import_select_all_button); import_select_all_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_select_all_items_pressed)); import_select_full_button = memnew(Button); import_select_full_button->set_flat(true); import_select_full_button->set_text(TTR("Select With Data")); - import_select_full_button->set_tooltip(TTR("Select all Theme items with item data.")); + import_select_full_button->set_tooltip_text(TTR("Select all Theme items with item data.")); import_buttons->add_child(import_select_full_button); import_select_full_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_select_full_items_pressed)); import_deselect_all_button = memnew(Button); import_deselect_all_button->set_flat(true); import_deselect_all_button->set_text(TTR("Deselect All")); - import_deselect_all_button->set_tooltip(TTR("Deselect all Theme items.")); + import_deselect_all_button->set_tooltip_text(TTR("Deselect all Theme items.")); import_buttons->add_child(import_deselect_all_button); import_deselect_all_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_deselect_all_items_pressed)); @@ -1494,7 +1494,7 @@ void ThemeItemEditorDialog::_item_tree_button_pressed(Object *p_item, int p_colu String item_name = item->get_text(0); int data_type = item->get_parent()->get_metadata(0); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove Theme Item")); ur->add_do_method(*edited_theme, "clear_theme_item", (Theme::DataType)data_type, item_name, edited_item_type); ur->add_undo_method(*edited_theme, "set_theme_item", (Theme::DataType)data_type, item_name, edited_item_type, edited_theme->get_theme_item((Theme::DataType)data_type, item_name, edited_item_type)); @@ -1513,7 +1513,7 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) { const String new_type = edit_add_type_value->get_text().strip_edges(); edit_add_type_value->clear(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Add Theme Type")); ur->add_do_method(*edited_theme, "add_type", new_type); @@ -1525,7 +1525,7 @@ void ThemeItemEditorDialog::_add_theme_type(const String &p_new_text) { } void ThemeItemEditorDialog::_add_theme_item(Theme::DataType p_data_type, String p_item_name, String p_item_type) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Create Theme Item")); switch (p_data_type) { @@ -1570,7 +1570,7 @@ void ThemeItemEditorDialog::_remove_theme_type(const String &p_theme_type) { Ref<Theme> old_snapshot = edited_theme->duplicate(); Ref<Theme> new_snapshot = edited_theme->duplicate(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove Theme Type")); new_snapshot->remove_type(p_theme_type); @@ -1593,7 +1593,7 @@ void ThemeItemEditorDialog::_remove_data_type_items(Theme::DataType p_data_type, Ref<Theme> old_snapshot = edited_theme->duplicate(); Ref<Theme> new_snapshot = edited_theme->duplicate(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove Data Type Items From Theme")); new_snapshot->get_theme_item_list(p_data_type, p_item_type, &names); @@ -1622,7 +1622,7 @@ void ThemeItemEditorDialog::_remove_class_items() { Ref<Theme> old_snapshot = edited_theme->duplicate(); Ref<Theme> new_snapshot = edited_theme->duplicate(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove Class Items From Theme")); for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { @@ -1658,7 +1658,7 @@ void ThemeItemEditorDialog::_remove_custom_items() { Ref<Theme> old_snapshot = edited_theme->duplicate(); Ref<Theme> new_snapshot = edited_theme->duplicate(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove Custom Items From Theme")); for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { @@ -1694,7 +1694,7 @@ void ThemeItemEditorDialog::_remove_all_items() { Ref<Theme> old_snapshot = edited_theme->duplicate(); Ref<Theme> new_snapshot = edited_theme->duplicate(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove All Items From Theme")); for (int dt = 0; dt < Theme::DATA_TYPE_MAX; dt++) { @@ -1798,7 +1798,7 @@ void ThemeItemEditorDialog::_confirm_edit_theme_item() { if (item_popup_mode == CREATE_THEME_ITEM) { _add_theme_item(edit_item_data_type, theme_item_name->get_text(), edited_item_type); } else if (item_popup_mode == RENAME_THEME_ITEM) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Rename Theme Item")); ur->add_do_method(*edited_theme, "rename_theme_item", edit_item_data_type, edit_item_old_name, theme_item_name->get_text(), edited_item_type); @@ -1864,8 +1864,8 @@ void ThemeItemEditorDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { connect("about_to_popup", callable_mp(this, &ThemeItemEditorDialog::_dialog_about_to_show)); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { edit_items_add_color->set_icon(get_theme_icon(SNAME("Color"), SNAME("EditorIcons"))); edit_items_add_constant->set_icon(get_theme_icon(SNAME("MemberConstant"), SNAME("EditorIcons"))); @@ -1953,42 +1953,42 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito edit_items_toolbar->add_child(edit_items_toolbar_add_label); edit_items_add_color = memnew(Button); - edit_items_add_color->set_tooltip(TTR("Add Color Item")); + edit_items_add_color->set_tooltip_text(TTR("Add Color Item")); edit_items_add_color->set_flat(true); edit_items_add_color->set_disabled(true); edit_items_toolbar->add_child(edit_items_add_color); edit_items_add_color->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_COLOR)); edit_items_add_constant = memnew(Button); - edit_items_add_constant->set_tooltip(TTR("Add Constant Item")); + edit_items_add_constant->set_tooltip_text(TTR("Add Constant Item")); edit_items_add_constant->set_flat(true); edit_items_add_constant->set_disabled(true); edit_items_toolbar->add_child(edit_items_add_constant); edit_items_add_constant->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_CONSTANT)); edit_items_add_font = memnew(Button); - edit_items_add_font->set_tooltip(TTR("Add Font Item")); + edit_items_add_font->set_tooltip_text(TTR("Add Font Item")); edit_items_add_font->set_flat(true); edit_items_add_font->set_disabled(true); edit_items_toolbar->add_child(edit_items_add_font); edit_items_add_font->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_FONT)); edit_items_add_font_size = memnew(Button); - edit_items_add_font_size->set_tooltip(TTR("Add Font Size Item")); + edit_items_add_font_size->set_tooltip_text(TTR("Add Font Size Item")); edit_items_add_font_size->set_flat(true); edit_items_add_font_size->set_disabled(true); edit_items_toolbar->add_child(edit_items_add_font_size); edit_items_add_font_size->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_FONT_SIZE)); edit_items_add_icon = memnew(Button); - edit_items_add_icon->set_tooltip(TTR("Add Icon Item")); + edit_items_add_icon->set_tooltip_text(TTR("Add Icon Item")); edit_items_add_icon->set_flat(true); edit_items_add_icon->set_disabled(true); edit_items_toolbar->add_child(edit_items_add_icon); edit_items_add_icon->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_open_add_theme_item_dialog).bind(Theme::DATA_TYPE_ICON)); edit_items_add_stylebox = memnew(Button); - edit_items_add_stylebox->set_tooltip(TTR("Add StyleBox Item")); + edit_items_add_stylebox->set_tooltip_text(TTR("Add StyleBox Item")); edit_items_add_stylebox->set_flat(true); edit_items_add_stylebox->set_disabled(true); edit_items_toolbar->add_child(edit_items_add_stylebox); @@ -2001,21 +2001,21 @@ ThemeItemEditorDialog::ThemeItemEditorDialog(ThemeTypeEditor *p_theme_type_edito edit_items_toolbar->add_child(edit_items_toolbar_remove_label); edit_items_remove_class = memnew(Button); - edit_items_remove_class->set_tooltip(TTR("Remove Class Items")); + edit_items_remove_class->set_tooltip_text(TTR("Remove Class Items")); edit_items_remove_class->set_flat(true); edit_items_remove_class->set_disabled(true); edit_items_toolbar->add_child(edit_items_remove_class); edit_items_remove_class->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_remove_class_items)); edit_items_remove_custom = memnew(Button); - edit_items_remove_custom->set_tooltip(TTR("Remove Custom Items")); + edit_items_remove_custom->set_tooltip_text(TTR("Remove Custom Items")); edit_items_remove_custom->set_flat(true); edit_items_remove_custom->set_disabled(true); edit_items_toolbar->add_child(edit_items_remove_custom); edit_items_remove_custom->connect("pressed", callable_mp(this, &ThemeItemEditorDialog::_remove_custom_items)); edit_items_remove_all = memnew(Button); - edit_items_remove_all->set_tooltip(TTR("Remove All Items")); + edit_items_remove_all->set_tooltip_text(TTR("Remove All Items")); edit_items_remove_all->set_flat(true); edit_items_remove_all->set_disabled(true); edit_items_toolbar->add_child(edit_items_remove_all); @@ -2193,8 +2193,8 @@ void ThemeTypeDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { connect("about_to_popup", callable_mp(this, &ThemeTypeDialog::_dialog_about_to_show)); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { _update_add_type_options(); } break; @@ -2411,7 +2411,7 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_ item_name->set_h_size_flags(SIZE_EXPAND_FILL); item_name->set_clip_text(true); item_name->set_text(p_item_name); - item_name->set_tooltip(p_item_name); + item_name->set_tooltip_text(p_item_name); item_name_container->add_child(item_name); if (p_editable) { @@ -2424,21 +2424,21 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_ Button *item_rename_button = memnew(Button); item_rename_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons"))); - item_rename_button->set_tooltip(TTR("Rename Item")); + 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("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_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); - item_remove_button->set_tooltip(TTR("Remove Item")); + 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("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_theme_icon(SNAME("ImportCheck"), SNAME("EditorIcons"))); - item_rename_confirm_button->set_tooltip(TTR("Confirm Item Rename")); + 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); item_rename_confirm_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_confirmed).bind(p_data_type, p_item_name, item_name_container)); @@ -2446,7 +2446,7 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_ Button *item_rename_cancel_button = memnew(Button); item_rename_cancel_button->set_icon(get_theme_icon(SNAME("ImportFail"), SNAME("EditorIcons"))); - item_rename_cancel_button->set_tooltip(TTR("Cancel Item Rename")); + 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); item_rename_cancel_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_rename_canceled).bind(p_data_type, p_item_name, item_name_container)); @@ -2456,7 +2456,7 @@ HBoxContainer *ThemeTypeEditor::_create_property_control(Theme::DataType p_data_ Button *item_override_button = memnew(Button); item_override_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); - item_override_button->set_tooltip(TTR("Override Item")); + item_override_button->set_tooltip_text(TTR("Override Item")); item_override_button->set_flat(true); item_name_container->add_child(item_override_button); item_override_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_item_override_cbk).bind(p_data_type, p_item_name)); @@ -2666,7 +2666,7 @@ void ThemeTypeEditor::_update_type_items() { pin_leader_button->set_toggle_mode(true); pin_leader_button->set_pressed(true); pin_leader_button->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons"))); - pin_leader_button->set_tooltip(TTR("Unpin this StyleBox as a main style.")); + 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("pressed", callable_mp(this, &ThemeTypeEditor::_on_unpin_leader_button_pressed)); @@ -2709,7 +2709,7 @@ void ThemeTypeEditor::_update_type_items() { pin_leader_button->set_flat(true); pin_leader_button->set_toggle_mode(true); pin_leader_button->set_icon(get_theme_icon(SNAME("Pin"), SNAME("EditorIcons"))); - pin_leader_button->set_tooltip(TTR("Pin this StyleBox as a main style. Editing its properties will update the same properties in all other StyleBoxes of this type.")); + 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("pressed", callable_mp(this, &ThemeTypeEditor::_on_pin_leader_button_pressed).bind(item_editor, E.key)); } else { @@ -2824,7 +2824,7 @@ void ThemeTypeEditor::_add_default_type_items() { updating = false; - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Override All Default Theme Items")); ur->add_do_method(*edited_theme, "merge_with", new_snapshot); @@ -2844,7 +2844,7 @@ void ThemeTypeEditor::_item_add_cbk(int p_data_type, Control *p_control) { } String item_name = le->get_text().strip_edges(); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Add Theme Item")); switch (p_data_type) { @@ -2889,7 +2889,7 @@ void ThemeTypeEditor::_item_add_lineedit_cbk(String p_value, int p_data_type, Co } void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Override Theme Item")); switch (p_data_type) { @@ -2928,7 +2928,7 @@ void ThemeTypeEditor::_item_override_cbk(int p_data_type, String p_item_name) { } void ThemeTypeEditor::_item_remove_cbk(int p_data_type, String p_item_name) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Remove Theme Item")); switch (p_data_type) { @@ -3002,7 +3002,7 @@ void ThemeTypeEditor::_item_rename_confirmed(int p_data_type, String p_item_name return; } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Rename Theme Item")); switch (p_data_type) { @@ -3058,7 +3058,7 @@ void ThemeTypeEditor::_item_rename_canceled(int p_data_type, String p_item_name, } void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Color Item in Theme"), UndoRedo::MERGE_ENDS); ur->add_do_method(*edited_theme, "set_color", p_item_name, edited_type, p_value); ur->add_undo_method(*edited_theme, "set_color", p_item_name, edited_type, edited_theme->get_color(p_item_name, edited_type)); @@ -3066,7 +3066,7 @@ void ThemeTypeEditor::_color_item_changed(Color p_value, String p_item_name) { } void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Constant Item in Theme")); ur->add_do_method(*edited_theme, "set_constant", p_item_name, edited_type, p_value); ur->add_undo_method(*edited_theme, "set_constant", p_item_name, edited_type, edited_theme->get_constant(p_item_name, edited_type)); @@ -3074,7 +3074,7 @@ void ThemeTypeEditor::_constant_item_changed(float p_value, String p_item_name) } void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Font Size Item in Theme")); ur->add_do_method(*edited_theme, "set_font_size", p_item_name, edited_type, p_value); ur->add_undo_method(*edited_theme, "set_font_size", p_item_name, edited_type, edited_theme->get_font_size(p_item_name, edited_type)); @@ -3086,7 +3086,7 @@ void ThemeTypeEditor::_edit_resource_item(Ref<Resource> p_resource, bool p_edit) } void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Font Item in Theme")); ur->add_do_method(*edited_theme, "set_font", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Font>()); @@ -3103,7 +3103,7 @@ void ThemeTypeEditor::_font_item_changed(Ref<Font> p_value, String p_item_name) } void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_name) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Icon Item in Theme")); ur->add_do_method(*edited_theme, "set_icon", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<Texture2D>()); @@ -3120,7 +3120,7 @@ void ThemeTypeEditor::_icon_item_changed(Ref<Texture2D> p_value, String p_item_n } void ThemeTypeEditor::_stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Stylebox Item in Theme")); ur->add_do_method(*edited_theme, "set_stylebox", p_item_name, edited_type, p_value.is_valid() ? p_value : Ref<StyleBox>()); @@ -3163,7 +3163,7 @@ void ThemeTypeEditor::_on_pin_leader_button_pressed(Control *p_editor, String p_ stylebox = Object::cast_to<EditorResourcePicker>(p_editor)->get_edited_resource(); } - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Pin Stylebox")); ur->add_do_method(this, "_pin_leading_stylebox", p_item_name, stylebox); @@ -3196,7 +3196,7 @@ void ThemeTypeEditor::_pin_leading_stylebox(String p_item_name, Ref<StyleBox> p_ } void ThemeTypeEditor::_on_unpin_leader_button_pressed() { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Unpin Stylebox")); ur->add_do_method(this, "_unpin_leading_stylebox"); ur->add_undo_method(this, "_pin_leading_stylebox", leading_stylebox.item_name, leading_stylebox.stylebox); @@ -3265,7 +3265,7 @@ void ThemeTypeEditor::_update_stylebox_from_leading() { } void ThemeTypeEditor::_type_variation_changed(const String p_value) { - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); ur->create_action(TTR("Set Theme Type Variation")); if (p_value.is_empty()) { @@ -3301,7 +3301,6 @@ void ThemeTypeEditor::_add_type_dialog_selected(const String p_type_name) { void ThemeTypeEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { add_type_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); @@ -3386,7 +3385,7 @@ ThemeTypeEditor::ThemeTypeEditor() { theme_type_list->connect("item_selected", callable_mp(this, &ThemeTypeEditor::_list_type_selected)); add_type_button = memnew(Button); - add_type_button->set_tooltip(TTR("Add a type from a list of available types or create a new one.")); + add_type_button->set_tooltip_text(TTR("Add a type from a list of available types or create a new one.")); type_list_hb->add_child(add_type_button); add_type_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_type_button_cbk)); @@ -3396,7 +3395,7 @@ ThemeTypeEditor::ThemeTypeEditor() { show_default_items_button = memnew(CheckButton); show_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL); show_default_items_button->set_text(TTR("Show Default")); - show_default_items_button->set_tooltip(TTR("Show default type items alongside items that have been overridden.")); + show_default_items_button->set_tooltip_text(TTR("Show default type items alongside items that have been overridden.")); show_default_items_button->set_pressed(true); type_controls->add_child(show_default_items_button); show_default_items_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_update_type_items)); @@ -3404,7 +3403,7 @@ ThemeTypeEditor::ThemeTypeEditor() { Button *add_default_items_button = memnew(Button); add_default_items_button->set_h_size_flags(SIZE_EXPAND_FILL); add_default_items_button->set_text(TTR("Override All")); - add_default_items_button->set_tooltip(TTR("Override all default type items.")); + add_default_items_button->set_tooltip_text(TTR("Override all default type items.")); type_controls->add_child(add_default_items_button); add_default_items_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_default_type_items)); @@ -3451,7 +3450,7 @@ ThemeTypeEditor::ThemeTypeEditor() { type_variation_edit->connect("focus_exited", callable_mp(this, &ThemeTypeEditor::_update_type_items)); type_variation_button = memnew(Button); type_variation_hb->add_child(type_variation_button); - type_variation_button->set_tooltip(TTR("Select the variation base type from a list of available types.")); + type_variation_button->set_tooltip_text(TTR("Select the variation base type from a list of available types.")); type_variation_button->connect("pressed", callable_mp(this, &ThemeTypeEditor::_add_type_variation_cbk)); type_variation_locked = memnew(Label); @@ -3594,7 +3593,6 @@ void ThemeEditor::_preview_control_picked(String p_class_name) { void ThemeEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { preview_tabs->add_theme_style_override("tab_selected", get_theme_stylebox(SNAME("ThemeEditorPreviewFG"), SNAME("EditorStyles"))); preview_tabs->add_theme_style_override("tab_unselected", get_theme_stylebox(SNAME("ThemeEditorPreviewBG"), SNAME("EditorStyles"))); @@ -3632,7 +3630,7 @@ ThemeEditor::ThemeEditor() { Button *theme_edit_button = memnew(Button); theme_edit_button->set_text(TTR("Manage Items...")); - theme_edit_button->set_tooltip(TTR("Add, remove, organize and import Theme items.")); + theme_edit_button->set_tooltip_text(TTR("Add, remove, organize and import Theme items.")); theme_edit_button->set_flat(true); theme_edit_button->connect("pressed", callable_mp(this, &ThemeEditor::_theme_edit_button_cbk)); top_menu->add_child(theme_edit_button); diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp index b5c6c6d651..41b4a4c407 100644 --- a/editor/plugins/theme_editor_preview.cpp +++ b/editor/plugins/theme_editor_preview.cpp @@ -193,8 +193,8 @@ void ThemeEditorPreview::_notification(int p_what) { } connect("visibility_changed", callable_mp(this, &ThemeEditorPreview::_preview_visibility_changed)); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { picker_button->set_icon(get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons"))); @@ -227,7 +227,7 @@ ThemeEditorPreview::ThemeEditorPreview() { preview_toolbar->add_child(picker_button); picker_button->set_flat(true); picker_button->set_toggle_mode(true); - picker_button->set_tooltip(TTR("Toggle the control picker, allowing to visually select control types for edit.")); + picker_button->set_tooltip_text(TTR("Toggle the control picker, allowing to visually select control types for edit.")); picker_button->connect("pressed", callable_mp(this, &ThemeEditorPreview::_picker_button_cbk)); MarginContainer *preview_body = memnew(MarginContainer); @@ -272,7 +272,6 @@ ThemeEditorPreview::ThemeEditorPreview() { void DefaultThemeEditorPreview::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { test_color_picker_button->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), SNAME("Editor")))); } break; @@ -475,7 +474,6 @@ void SceneThemeEditorPreview::_reload_scene() { void SceneThemeEditorPreview::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { reload_scene_button->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); } break; @@ -517,7 +515,7 @@ SceneThemeEditorPreview::SceneThemeEditorPreview() { reload_scene_button = memnew(Button); reload_scene_button->set_flat(true); - reload_scene_button->set_tooltip(TTR("Reload the scene to reflect its most actual state.")); + reload_scene_button->set_tooltip_text(TTR("Reload the scene to reflect its most actual state.")); preview_toolbar->add_child(reload_scene_button); reload_scene_button->connect("pressed", callable_mp(this, &SceneThemeEditorPreview::_reload_scene)); } diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp index 3fe6778f48..de4f3f7989 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.cpp +++ b/editor/plugins/tiles/atlas_merging_dialog.cpp @@ -33,6 +33,7 @@ #include "editor/editor_file_dialog.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/control.h" #include "scene/gui/split_container.h" diff --git a/editor/plugins/tiles/atlas_merging_dialog.h b/editor/plugins/tiles/atlas_merging_dialog.h index c54e259594..c7e4635d16 100644 --- a/editor/plugins/tiles/atlas_merging_dialog.h +++ b/editor/plugins/tiles/atlas_merging_dialog.h @@ -38,6 +38,7 @@ #include "scene/resources/tile_set.h" class EditorFileDialog; +class EditorUndoRedoManager; class AtlasMergingDialog : public ConfirmationDialog { GDCLASS(AtlasMergingDialog, ConfirmationDialog); @@ -49,7 +50,7 @@ private: LocalVector<HashMap<Vector2i, Vector2i>> merged_mapping; Ref<TileSet> tile_set; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; // Settings. int next_line_after_column = 30; diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp index f119ada810..8d2b150d6d 100644 --- a/editor/plugins/tiles/tile_atlas_view.cpp +++ b/editor/plugins/tiles/tile_atlas_view.cpp @@ -127,8 +127,8 @@ void TileAtlasView::_update_zoom_and_panning(bool p_zoom_on_mouse_pos) { } // Update the backgrounds. - background_left->update(); - background_right->update(); + background_left->set_size(base_tiles_root_control->get_custom_minimum_size()); + background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size()); // Zoom on the position. if (p_zoom_on_mouse_pos) { @@ -160,7 +160,7 @@ void TileAtlasView::_center_view() { } void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) { - base_tiles_root_control->set_tooltip(""); + base_tiles_root_control->set_tooltip_text(""); Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { @@ -169,7 +169,7 @@ void TileAtlasView::_base_tiles_root_control_gui_input(const Ref<InputEvent> &p_ if (coords != TileSetSource::INVALID_ATLAS_COORDS) { coords = tile_set_atlas_source->get_tile_at_coords(coords); if (coords != TileSetSource::INVALID_ATLAS_COORDS) { - base_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords)); + base_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: 0"), source_id, coords)); } } } @@ -319,7 +319,7 @@ void TileAtlasView::_draw_base_tiles_shape_grid() { } void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEvent> &p_event) { - alternative_tiles_root_control->set_tooltip(""); + alternative_tiles_root_control->set_tooltip_text(""); Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { @@ -328,7 +328,7 @@ void TileAtlasView::_alternative_tiles_root_control_gui_input(const Ref<InputEve Vector2i coords = Vector2i(coords3.x, coords3.y); int alternative_id = coords3.z; if (coords != TileSetSource::INVALID_ATLAS_COORDS && alternative_id != TileSetSource::INVALID_TILE_ALTERNATIVE) { - alternative_tiles_root_control->set_tooltip(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id)); + alternative_tiles_root_control->set_tooltip_text(vformat(TTR("Source: %d\nAtlas coordinates: %s\nAlternative: %d"), source_id, coords, alternative_id)); } } } @@ -374,13 +374,11 @@ void TileAtlasView::_draw_alternatives() { void TileAtlasView::_draw_background_left() { Ref<Texture2D> texture = get_theme_icon(SNAME("Checkerboard"), SNAME("EditorIcons")); - background_left->set_size(base_tiles_root_control->get_custom_minimum_size()); background_left->draw_texture_rect(texture, Rect2(Vector2(), background_left->get_size()), true); } void TileAtlasView::_draw_background_right() { Ref<Texture2D> texture = get_theme_icon(SNAME("Checkerboard"), SNAME("EditorIcons")); - background_right->set_size(alternative_tiles_root_control->get_custom_minimum_size()); background_right->draw_texture_rect(texture, Rect2(Vector2(), background_right->get_size()), true); } @@ -405,23 +403,6 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_ // Update everything. _update_zoom_and_panning(); - // Change children control size. - Size2i base_tiles_control_size = _compute_base_tiles_control_size(); - for (int i = 0; i < base_tiles_drawing_root->get_child_count(); i++) { - Control *control = Object::cast_to<Control>(base_tiles_drawing_root->get_child(i)); - if (control) { - control->set_size(base_tiles_control_size); - } - } - - Size2i alternative_control_size = _compute_alternative_tiles_control_size(); - for (int i = 0; i < alternative_tiles_drawing_root->get_child_count(); i++) { - Control *control = Object::cast_to<Control>(alternative_tiles_drawing_root->get_child(i)); - if (control) { - control->set_size(alternative_control_size); - } - } - // Update. base_tiles_draw->update(); base_tiles_texture_grid->update(); @@ -561,7 +542,7 @@ TileAtlasView::TileAtlasView() { button_center_view->connect("pressed", callable_mp(this, &TileAtlasView::_center_view)); button_center_view->set_flat(true); button_center_view->set_disabled(true); - button_center_view->set_tooltip(TTR("Center View")); + button_center_view->set_tooltip_text(TTR("Center View")); add_child(button_center_view); panner.instantiate(); @@ -613,7 +594,7 @@ TileAtlasView::TileAtlasView() { background_left = memnew(Control); background_left->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); - background_left->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); + background_left->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT); background_left->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); background_left->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_left)); base_tiles_root_control->add_child(background_left); @@ -651,23 +632,26 @@ TileAtlasView::TileAtlasView() { alternative_tiles_root_control = memnew(Control); alternative_tiles_root_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); + alternative_tiles_root_control->set_v_size_flags(Control::SIZE_EXPAND_FILL); alternative_tiles_root_control->connect("gui_input", callable_mp(this, &TileAtlasView::_alternative_tiles_root_control_gui_input)); right_vbox->add_child(alternative_tiles_root_control); background_right = memnew(Control); background_right->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + background_right->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT); background_right->set_texture_repeat(TextureRepeat::TEXTURE_REPEAT_ENABLED); background_right->connect("draw", callable_mp(this, &TileAtlasView::_draw_background_right)); - alternative_tiles_root_control->add_child(background_right); alternative_tiles_drawing_root = memnew(Control); alternative_tiles_drawing_root->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + alternative_tiles_drawing_root->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); alternative_tiles_drawing_root->set_texture_filter(TEXTURE_FILTER_NEAREST); alternative_tiles_root_control->add_child(alternative_tiles_drawing_root); alternatives_draw = memnew(Control); alternatives_draw->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); + alternatives_draw->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); alternatives_draw->connect("draw", callable_mp(this, &TileAtlasView::_draw_alternatives)); alternative_tiles_drawing_root->add_child(alternatives_draw); } diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h index 196a642283..1c0b622bb1 100644 --- a/editor/plugins/tiles/tile_atlas_view.h +++ b/editor/plugins/tiles/tile_atlas_view.h @@ -136,6 +136,7 @@ public: } else { base_tiles_root_control->add_child(p_control); } + p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); }; @@ -149,6 +150,7 @@ public: } else { alternative_tiles_root_control->add_child(p_control); } + p_control->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); p_control->set_mouse_filter(Control::MOUSE_FILTER_PASS); }; diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index a00e1ed9e8..f6001e8972 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -38,6 +38,7 @@ #include "editor/editor_node.h" #include "editor/editor_properties.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" void TileDataEditor::_tile_set_changed_plan_update() { _tile_set_changed_update_needed = true; @@ -248,7 +249,14 @@ void GenericTilePolygonEditor::_zoom_changed() { } void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { - UndoRedo *undo_redo = use_undo_redo ? editor_undo_redo : memnew(UndoRedo); + Ref<EditorUndoRedoManager> undo_redo; + if (use_undo_redo) { + undo_redo = editor_undo_redo; + } else { + // This nice hack allows for discarding undo actions without making code too complex. + undo_redo.instantiate(); + } + switch (p_item_pressed) { case RESET_TO_DEFAULT_TILE: { undo_redo->create_action(TTR("Reset Polygons")); @@ -322,9 +330,6 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) { default: break; } - if (!use_undo_redo) { - memdelete(undo_redo); - } } void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) { @@ -409,7 +414,14 @@ void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) { } void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) { - UndoRedo *undo_redo = use_undo_redo ? editor_undo_redo : memnew(UndoRedo); + Ref<EditorUndoRedoManager> undo_redo; + if (use_undo_redo) { + undo_redo = editor_undo_redo; + } else { + // This nice hack allows for discarding undo actions without making code too complex. + undo_redo.instantiate(); + } + real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius"); hovered_polygon_index = -1; @@ -600,10 +612,6 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) } base_control->update(); - - if (!use_undo_redo) { - memdelete(undo_redo); - } } void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) { @@ -714,7 +722,6 @@ void GenericTilePolygonEditor::set_multiple_polygon_mode(bool p_multiple_polygon void GenericTilePolygonEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { button_create->set_icon(get_theme_icon(SNAME("CurveCreate"), SNAME("EditorIcons"))); button_edit->set_icon(get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons"))); @@ -756,21 +763,21 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { button_create->set_toggle_mode(true); button_create->set_button_group(tools_button_group); button_create->set_pressed(true); - button_create->set_tooltip(TTR("Add polygon tool")); + button_create->set_tooltip_text(TTR("Add polygon tool")); toolbar->add_child(button_create); button_edit = memnew(Button); button_edit->set_flat(true); button_edit->set_toggle_mode(true); button_edit->set_button_group(tools_button_group); - button_edit->set_tooltip(TTR("Edit points tool")); + button_edit->set_tooltip_text(TTR("Edit points tool")); toolbar->add_child(button_edit); button_delete = memnew(Button); button_delete->set_flat(true); button_delete->set_toggle_mode(true); button_delete->set_button_group(tools_button_group); - button_delete->set_tooltip(TTR("Delete points tool")); + button_delete->set_tooltip_text(TTR("Delete points tool")); toolbar->add_child(button_delete); button_advanced_menu = memnew(MenuButton); @@ -793,7 +800,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { button_pixel_snap->set_flat(true); button_pixel_snap->set_toggle_mode(true); button_pixel_snap->set_pressed(true); - button_pixel_snap->set_tooltip(TTR("Snap to half-pixel")); + button_pixel_snap->set_tooltip_text(TTR("Snap to half-pixel")); toolbar->add_child(button_pixel_snap); Control *root = memnew(Control); @@ -1153,14 +1160,13 @@ void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p property_editor->set_label(p_label); } property_editor->connect("property_changed", callable_mp(this, &TileDataDefaultEditor::_property_value_changed).unbind(1)); - property_editor->set_tooltip(p_property); + property_editor->set_tooltip_text(p_property); property_editor->update_property(); add_child(property_editor); } void TileDataDefaultEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { picker_button->set_icon(get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons"))); tile_bool_checked = get_theme_icon(SNAME("TileChecked"), SNAME("EditorIcons")); @@ -1363,7 +1369,7 @@ void TileDataCollisionEditor::_polygons_changed() { one_way_property_editor->set_label(one_way_property); one_way_property_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); one_way_property_editor->connect("selected", callable_mp(this, &TileDataCollisionEditor::_property_selected)); - one_way_property_editor->set_tooltip(one_way_property_editor->get_edited_property()); + one_way_property_editor->set_tooltip_text(one_way_property_editor->get_edited_property()); one_way_property_editor->update_property(); add_child(one_way_property_editor); property_editors[one_way_property] = one_way_property_editor; @@ -1375,7 +1381,7 @@ void TileDataCollisionEditor::_polygons_changed() { one_way_margin_property_editor->set_label(one_way_margin_property); one_way_margin_property_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); one_way_margin_property_editor->connect("selected", callable_mp(this, &TileDataCollisionEditor::_property_selected)); - one_way_margin_property_editor->set_tooltip(one_way_margin_property_editor->get_edited_property()); + one_way_margin_property_editor->set_tooltip_text(one_way_margin_property_editor->get_edited_property()); one_way_margin_property_editor->update_property(); add_child(one_way_margin_property_editor); property_editors[one_way_margin_property] = one_way_margin_property_editor; @@ -1536,7 +1542,7 @@ TileDataCollisionEditor::TileDataCollisionEditor() { linear_velocity_editor->set_label("linear_velocity"); linear_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); linear_velocity_editor->connect("selected", callable_mp(this, &TileDataCollisionEditor::_property_selected)); - linear_velocity_editor->set_tooltip(linear_velocity_editor->get_edited_property()); + linear_velocity_editor->set_tooltip_text(linear_velocity_editor->get_edited_property()); linear_velocity_editor->update_property(); add_child(linear_velocity_editor); property_editors["linear_velocity"] = linear_velocity_editor; @@ -1546,7 +1552,7 @@ TileDataCollisionEditor::TileDataCollisionEditor() { angular_velocity_editor->set_label("angular_velocity"); angular_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1)); angular_velocity_editor->connect("selected", callable_mp(this, &TileDataCollisionEditor::_property_selected)); - angular_velocity_editor->set_tooltip(angular_velocity_editor->get_edited_property()); + angular_velocity_editor->set_tooltip_text(angular_velocity_editor->get_edited_property()); angular_velocity_editor->update_property(); add_child(angular_velocity_editor); property_editors["angular_velocity"] = angular_velocity_editor; @@ -2559,7 +2565,6 @@ void TileDataTerrainsEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform void TileDataTerrainsEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { picker_button->set_icon(get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons"))); } break; @@ -2594,7 +2599,7 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() { terrain_set_property_editor->set_object_and_property(dummy_object, "terrain_set"); terrain_set_property_editor->set_label("Terrain Set"); terrain_set_property_editor->connect("property_changed", callable_mp(this, &TileDataTerrainsEditor::_property_value_changed).unbind(1)); - terrain_set_property_editor->set_tooltip(terrain_set_property_editor->get_edited_property()); + terrain_set_property_editor->set_tooltip_text(terrain_set_property_editor->get_edited_property()); add_child(terrain_set_property_editor); terrain_property_editor = memnew(EditorPropertyEnum); diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index f9b8948d0a..c1560138b2 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -39,6 +39,8 @@ #include "scene/gui/control.h" #include "scene/gui/label.h" +class EditorUndoRedoManager; + class TileDataEditor : public VBoxContainer { GDCLASS(TileDataEditor, VBoxContainer); @@ -93,7 +95,7 @@ private: bool multiple_polygon_mode = false; bool use_undo_redo = true; - UndoRedo *editor_undo_redo = nullptr; + Ref<EditorUndoRedoManager> editor_undo_redo; // UI int hovered_polygon_index = -1; @@ -214,7 +216,7 @@ private: protected: DummyObject *dummy_object = memnew(DummyObject); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; StringName type; String property; @@ -279,7 +281,7 @@ private: virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; protected: - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; virtual void _tile_set_changed() override; @@ -314,7 +316,7 @@ class TileDataCollisionEditor : public TileDataDefaultEditor { virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; protected: - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; virtual void _tile_set_changed() override; @@ -366,7 +368,7 @@ protected: void _notification(int p_what); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; public: virtual Control *get_toolbar() override { return toolbar; }; @@ -399,7 +401,7 @@ private: virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; protected: - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; virtual void _tile_set_changed() override; diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp index 1bf24a7393..9e08d4cea7 100644 --- a/editor/plugins/tiles/tile_map_editor.cpp +++ b/editor/plugins/tiles/tile_map_editor.cpp @@ -35,6 +35,7 @@ #include "editor/editor_node.h" #include "editor/editor_resource_preview.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "scene/2d/camera_2d.h" @@ -2049,7 +2050,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { paint_tool_button->set_toggle_mode(true); paint_tool_button->set_button_group(tool_buttons_group); paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", Key::D)); - paint_tool_button->set_tooltip(TTR("Shift: Draw line.") + "\n" + TTR("Shift+Ctrl: Draw rectangle.")); + paint_tool_button->set_tooltip_text(TTR("Shift: Draw line.") + "\n" + TTR("Shift+Ctrl: Draw rectangle.")); paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar)); tilemap_tiles_tools_buttons->add_child(paint_tool_button); @@ -2090,7 +2091,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { picker_button->set_flat(true); picker_button->set_toggle_mode(true); picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", Key::P)); - picker_button->set_tooltip(TTR("Alternatively hold Ctrl with other tools to pick tile.")); + picker_button->set_tooltip_text(TTR("Alternatively hold Ctrl with other tools to pick tile.")); picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); tools_settings->add_child(picker_button); @@ -2099,7 +2100,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { erase_button->set_flat(true); erase_button->set_toggle_mode(true); erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", Key::E)); - erase_button->set_tooltip(TTR("Alternatively use RMB to erase tiles.")); + erase_button->set_tooltip_text(TTR("Alternatively use RMB to erase tiles.")); erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport)); tools_settings->add_child(erase_button); @@ -2118,13 +2119,13 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { random_tile_toggle = memnew(Button); random_tile_toggle->set_flat(true); random_tile_toggle->set_toggle_mode(true); - random_tile_toggle->set_tooltip(TTR("Place Random Tile")); + random_tile_toggle->set_tooltip_text(TTR("Place Random Tile")); random_tile_toggle->connect("toggled", callable_mp(this, &TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled)); tools_settings->add_child(random_tile_toggle); // Random tile scattering. scatter_label = memnew(Label); - scatter_label->set_tooltip(TTR("Defines the probability of painting nothing instead of a randomly selected tile.")); + scatter_label->set_tooltip_text(TTR("Defines the probability of painting nothing instead of a randomly selected tile.")); scatter_label->set_text(TTR("Scattering:")); tools_settings->add_child(scatter_label); @@ -2132,7 +2133,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { scatter_spinbox->set_min(0.0); scatter_spinbox->set_max(1000); scatter_spinbox->set_step(0.001); - scatter_spinbox->set_tooltip(TTR("Defines the probability of painting nothing instead of a randomly selected tile.")); + scatter_spinbox->set_tooltip_text(TTR("Defines the probability of painting nothing instead of a randomly selected tile.")); scatter_spinbox->get_line_edit()->add_theme_constant_override("minimum_character_width", 4); scatter_spinbox->connect("value_changed", callable_mp(this, &TileMapEditorTilesPlugin::_on_scattering_spinbox_changed)); tools_settings->add_child(scatter_spinbox); @@ -2145,7 +2146,6 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { // --- Bottom panel tiles --- tiles_bottom_panel = memnew(VBoxContainer); - tiles_bottom_panel->connect("tree_entered", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme)); tiles_bottom_panel->connect("theme_changed", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme)); tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_stop_dragging)); tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_tab_changed)); @@ -2177,7 +2177,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() { source_sort_button = memnew(MenuButton); source_sort_button->set_flat(true); - source_sort_button->set_tooltip(TTR("Sort sources")); + source_sort_button->set_tooltip_text(TTR("Sort sources")); PopupMenu *p = source_sort_button->get_popup(); p->connect("id_pressed", callable_mp(this, &TileMapEditorTilesPlugin::_set_source_sort)); @@ -3310,7 +3310,6 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() { undo_redo = EditorNode::get_undo_redo(); main_vbox_container = memnew(VBoxContainer); - main_vbox_container->connect("tree_entered", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme)); main_vbox_container->connect("theme_changed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme)); main_vbox_container->set_name("Terrains"); @@ -3418,7 +3417,6 @@ TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() { void TileMapEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { missing_tile_texture = get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons")); warning_pattern_texture = get_theme_icon(SNAME("WarningPattern"), SNAME("EditorIcons")); @@ -3697,8 +3695,8 @@ void TileMapEditor::_update_layers_selection() { } void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) { - UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo); - ERR_FAIL_COND(!undo_redo); + Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo); + ERR_FAIL_COND(undo_redo.is_null()); TileMap *tile_map = Object::cast_to<TileMap>(p_edited); if (!tile_map) { @@ -4002,7 +4000,7 @@ TileMapEditor::TileMapEditor() { layers_selection_button = memnew(OptionButton); layers_selection_button->set_custom_minimum_size(Size2(200, 0)); layers_selection_button->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS); - layers_selection_button->set_tooltip(TTR("TileMap Layers")); + layers_selection_button->set_tooltip_text(TTR("TileMap Layers")); layers_selection_button->connect("item_selected", callable_mp(this, &TileMapEditor::_layers_selection_item_selected)); tile_map_toolbar->add_child(layers_selection_button); @@ -4011,7 +4009,7 @@ TileMapEditor::TileMapEditor() { toggle_highlight_selected_layer_button->set_toggle_mode(true); toggle_highlight_selected_layer_button->set_pressed(true); toggle_highlight_selected_layer_button->connect("pressed", callable_mp(this, &TileMapEditor::_update_layers_selection)); - toggle_highlight_selected_layer_button->set_tooltip(TTR("Highlight Selected TileMap Layer")); + toggle_highlight_selected_layer_button->set_tooltip_text(TTR("Highlight Selected TileMap Layer")); tile_map_toolbar->add_child(toggle_highlight_selected_layer_button); tile_map_toolbar->add_child(memnew(VSeparator)); @@ -4020,7 +4018,7 @@ TileMapEditor::TileMapEditor() { toggle_grid_button = memnew(Button); toggle_grid_button->set_flat(true); toggle_grid_button->set_toggle_mode(true); - toggle_grid_button->set_tooltip(TTR("Toggle grid visibility.")); + toggle_grid_button->set_tooltip_text(TTR("Toggle grid visibility.")); toggle_grid_button->connect("toggled", callable_mp(this, &TileMapEditor::_on_grid_toggled)); tile_map_toolbar->add_child(toggle_grid_button); diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h index 605fbe4823..9a47d8bbc4 100644 --- a/editor/plugins/tiles/tile_map_editor.h +++ b/editor/plugins/tiles/tile_map_editor.h @@ -47,7 +47,7 @@ #include "scene/gui/tab_bar.h" #include "scene/gui/tree.h" -class UndoRedo; +class EditorUndoRedoManager; class TileMapEditorPlugin : public Object { public: @@ -70,7 +70,7 @@ class TileMapEditorTilesPlugin : public TileMapEditorPlugin { GDCLASS(TileMapEditorTilesPlugin, TileMapEditorPlugin); private: - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; ObjectID tile_map_id; int tile_map_layer = -1; virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override; @@ -223,7 +223,7 @@ class TileMapEditorTerrainsPlugin : public TileMapEditorPlugin { GDCLASS(TileMapEditorTerrainsPlugin, TileMapEditorPlugin); private: - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; ObjectID tile_map_id; int tile_map_layer = -1; virtual void edit(ObjectID p_tile_map_id, int p_tile_map_layer) override; @@ -317,7 +317,7 @@ class TileMapEditor : public VBoxContainer { GDCLASS(TileMapEditor, VBoxContainer); private: - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; bool tileset_changed_needs_update = false; ObjectID tile_map_id; int tile_map_layer = -1; diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp index 12e1615484..9e4c29fa79 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list, MouseButton p_mouse_button_index) { if (p_mouse_button_index != MouseButton::RIGHT) { diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.h b/editor/plugins/tiles/tile_proxies_manager_dialog.h index 44de708898..511e442a10 100644 --- a/editor/plugins/tiles/tile_proxies_manager_dialog.h +++ b/editor/plugins/tiles/tile_proxies_manager_dialog.h @@ -43,7 +43,7 @@ private: int commited_actions_count = 0; Ref<TileSet> tile_set; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; TileMapCell from; TileMapCell to; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index 6950f57a00..098cf93721 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -527,7 +527,7 @@ void TileSetAtlasSourceEditor::_update_tile_id_label() { if (selection.size() == 1) { TileSelection selected = selection.front()->get(); tool_tile_id_label->set_text(vformat("%d, %s, %d", tile_set_atlas_source_id, selected.tile, selected.alternative)); - tool_tile_id_label->set_tooltip(vformat(TTR("Selected tile:\nSource: %d\nAtlas coordinates: %s\nAlternative: %d"), tile_set_atlas_source_id, selected.tile, selected.alternative)); + tool_tile_id_label->set_tooltip_text(vformat(TTR("Selected tile:\nSource: %d\nAtlas coordinates: %s\nAlternative: %d"), tile_set_atlas_source_id, selected.tile, selected.alternative)); tool_tile_id_label->show(); } else { tool_tile_id_label->hide(); @@ -1888,7 +1888,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In alternative_tiles_control_unscaled->update(); if (drag_type == DRAG_TYPE_MAY_POPUP_MENU) { - if (Vector2(drag_start_mouse_pos).distance_to(tile_atlas_control->get_local_mouse_position()) > 5.0 * EDSCALE) { + if (Vector2(drag_start_mouse_pos).distance_to(alternative_tiles_control->get_local_mouse_position()) > 5.0 * EDSCALE) { drag_type = DRAG_TYPE_NONE; } } @@ -1896,8 +1896,6 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - drag_type = DRAG_TYPE_NONE; - Vector2 mouse_local_pos = alternative_tiles_control->get_local_mouse_position(); if (mb->get_button_index() == MouseButton::LEFT) { if (mb->is_pressed()) { @@ -1919,25 +1917,29 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In if (mb->is_pressed()) { drag_type = DRAG_TYPE_MAY_POPUP_MENU; drag_start_mouse_pos = alternative_tiles_control->get_local_mouse_position(); - } else if (drag_type == DRAG_TYPE_MAY_POPUP_MENU) { - // Right click released and wasn't dragged too far - Vector3 tile = tile_atlas_view->get_alternative_tile_at_pos(mouse_local_pos); + } else { + if (drag_type == DRAG_TYPE_MAY_POPUP_MENU) { + // Right click released and wasn't dragged too far + Vector3 tile = tile_atlas_view->get_alternative_tile_at_pos(mouse_local_pos); - selection.clear(); - TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) }; - if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { - selection.insert(selected); - } + selection.clear(); + TileSelection selected = { Vector2i(tile.x, tile.y), int(tile.z) }; + if (selected.tile != TileSetSource::INVALID_ATLAS_COORDS) { + selection.insert(selected); + } - _update_tile_inspector(); - _update_tile_id_label(); + _update_tile_inspector(); + _update_tile_id_label(); - if (selection.size() == 1) { - selected = selection.front()->get(); - menu_option_coords = selected.tile; - menu_option_alternative = selected.alternative; - alternative_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); + if (selection.size() == 1) { + selected = selection.front()->get(); + menu_option_coords = selected.tile; + menu_option_alternative = selected.alternative; + alternative_tile_popup_menu->popup(Rect2i(get_global_mouse_position(), Size2i())); + } } + + drag_type = DRAG_TYPE_NONE; } } tile_atlas_control->update(); @@ -2058,14 +2060,16 @@ void TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed(String p_what) } void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) { - UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo); - ERR_FAIL_COND(!undo_redo); + Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo); + ERR_FAIL_COND(!undo_redo.is_valid()); #define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property)); - undo_redo->start_force_keep_in_merge_ends(); AtlasTileProxyObject *tile_data_proxy = Object::cast_to<AtlasTileProxyObject>(p_edited); if (tile_data_proxy) { + UndoRedo *internal_undo_redo = undo_redo->get_history_for_object(tile_data_proxy).undo_redo; + internal_undo_redo->start_force_keep_in_merge_ends(); + Vector<String> components = String(p_property).split("/", true, 2); if (components.size() == 2 && components[1] == "polygons_count") { int layer_index = components[0].trim_prefix("physics_layer_").to_int(); @@ -2088,6 +2092,7 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo } } } + internal_undo_redo->end_force_keep_in_merge_ends(); } TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited); @@ -2095,6 +2100,9 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo TileSetAtlasSource *atlas_source = atlas_source_proxy->get_edited(); ERR_FAIL_COND(!atlas_source); + UndoRedo *internal_undo_redo = undo_redo->get_history_for_object(atlas_source).undo_redo; + internal_undo_redo->start_force_keep_in_merge_ends(); + PackedVector2Array arr; if (p_property == "texture") { arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size()); @@ -2121,8 +2129,8 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo } } } + internal_undo_redo->end_force_keep_in_merge_ends(); } - undo_redo->end_force_keep_in_merge_ends(); #undef ADD_UNDO } @@ -2279,7 +2287,6 @@ void TileSetAtlasSourceEditor::_auto_remove_tiles() { void TileSetAtlasSourceEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { tool_setup_atlas_source_button->set_icon(get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); tool_select_button->set_icon(get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons"))); @@ -2438,7 +2445,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tool_setup_atlas_source_button->set_toggle_mode(true); tool_setup_atlas_source_button->set_pressed(true); tool_setup_atlas_source_button->set_button_group(tools_button_group); - tool_setup_atlas_source_button->set_tooltip(TTR("Atlas setup. Add/Remove tiles tool (use the shift key to create big tiles, control for rectangle editing).")); + tool_setup_atlas_source_button->set_tooltip_text(TTR("Atlas setup. Add/Remove tiles tool (use the shift key to create big tiles, control for rectangle editing).")); toolbox->add_child(tool_setup_atlas_source_button); tool_select_button = memnew(Button); @@ -2446,14 +2453,14 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tool_select_button->set_toggle_mode(true); tool_select_button->set_pressed(false); tool_select_button->set_button_group(tools_button_group); - tool_select_button->set_tooltip(TTR("Select tiles.")); + tool_select_button->set_tooltip_text(TTR("Select tiles.")); toolbox->add_child(tool_select_button); tool_paint_button = memnew(Button); tool_paint_button->set_flat(true); tool_paint_button->set_toggle_mode(true); tool_paint_button->set_button_group(tools_button_group); - tool_paint_button->set_tooltip(TTR("Paint properties.")); + tool_paint_button->set_tooltip_text(TTR("Paint properties.")); toolbox->add_child(tool_paint_button); // Tool settings. @@ -2518,7 +2525,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control); tile_atlas_control_unscaled = memnew(Control); - tile_atlas_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); tile_atlas_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw)); tile_atlas_view->add_control_over_atlas_tiles(tile_atlas_control_unscaled, false); tile_atlas_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); @@ -2535,7 +2541,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() { tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control); alternative_tiles_control_unscaled = memnew(Control); - alternative_tiles_control_unscaled->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); alternative_tiles_control_unscaled->connect("draw", callable_mp(this, &TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw)); tile_atlas_view->add_control_over_alternative_tiles(alternative_tiles_control_unscaled, false); alternative_tiles_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE); diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index 738fe1044d..badb702e29 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -114,7 +114,7 @@ private: TileSetAtlasSource *tile_set_atlas_source = nullptr; int tile_set_atlas_source_id = TileSet::INVALID_SOURCE; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; bool tile_set_changed_needs_update = false; diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp index 81804710b4..302c34d8a5 100644 --- a/editor/plugins/tiles/tile_set_editor.cpp +++ b/editor/plugins/tiles/tile_set_editor.cpp @@ -36,6 +36,7 @@ #include "editor/editor_file_system.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "scene/gui/box_container.h" #include "scene/gui/control.h" @@ -332,7 +333,6 @@ void TileSetEditor::_set_source_sort(int p_sort) { void TileSetEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { sources_delete_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); sources_add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); @@ -405,8 +405,8 @@ void TileSetEditor::_tab_changed(int p_tab_changed) { } void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) { - UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo); - ERR_FAIL_COND(!undo_redo); + Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo); + ERR_FAIL_COND(undo_redo.is_null()); TileSet *tile_set = Object::cast_to<TileSet>(p_edited); if (!tile_set) { @@ -586,8 +586,8 @@ void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_ } void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) { - UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo); - ERR_FAIL_COND(!undo_redo); + Ref<EditorUndoRedoManager> undo_redo = Object::cast_to<EditorUndoRedoManager>(p_undo_redo); + ERR_FAIL_COND(undo_redo.is_null()); #define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property)); TileSet *tile_set = Object::cast_to<TileSet>(p_edited); @@ -694,7 +694,7 @@ TileSetEditor::TileSetEditor() { source_sort_button = memnew(MenuButton); source_sort_button->set_flat(true); - source_sort_button->set_tooltip(TTR("Sort sources")); + source_sort_button->set_tooltip_text(TTR("Sort sources")); PopupMenu *p = source_sort_button->get_popup(); p->connect("id_pressed", callable_mp(this, &TileSetEditor::_set_source_sort)); diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h index c45240043e..3b9b80dac4 100644 --- a/editor/plugins/tiles/tile_set_editor.h +++ b/editor/plugins/tiles/tile_set_editor.h @@ -39,6 +39,8 @@ #include "tile_set_atlas_source_editor.h" #include "tile_set_scenes_collection_source_editor.h" +class EditorUndoRedoManager; + class TileSetEditor : public VBoxContainer { GDCLASS(TileSetEditor, VBoxContainer); @@ -58,7 +60,7 @@ private: TileSetAtlasSourceEditor *tile_set_atlas_source_editor = nullptr; TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; 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 9a4b14616f..40cdb243fe 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp @@ -329,7 +329,6 @@ void TileSetScenesCollectionSourceEditor::_update_scenes_list() { void TileSetScenesCollectionSourceEditor::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { scene_tile_add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons"))); scene_tile_delete_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h index 77a583e522..0284b45c0f 100644 --- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h +++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h @@ -97,7 +97,7 @@ private: TileSetScenesCollectionSource *tile_set_scenes_collection_source = nullptr; int tile_set_source_id = -1; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; bool tile_set_scenes_collection_source_changed_needs_update = false; diff --git a/editor/plugins/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp index cf55465417..2b55ba64c3 100644 --- a/editor/plugins/version_control_editor_plugin.cpp +++ b/editor/plugins/version_control_editor_plugin.cpp @@ -242,7 +242,7 @@ void VersionControlEditorPlugin::_view_file_diff() { } void VersionControlEditorPlugin::_display_file_diff(String p_file_path) { - Array diff_content = EditorVCSInterface::get_singleton()->get_file_diff(p_file_path); + TypedArray<Dictionary> diff_content = EditorVCSInterface::get_singleton()->get_file_diff(p_file_path); diff_file_name->set_text(p_file_path); @@ -464,7 +464,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { stage_tools->add_child(staging_area_label); refresh_button = memnew(Button); - refresh_button->set_tooltip(TTR("Detect new changes")); + refresh_button->set_tooltip_text(TTR("Detect new changes")); refresh_button->set_text(TTR("Refresh")); refresh_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); refresh_button->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_refresh_stage_area)); @@ -551,7 +551,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { diff_heading = memnew(Label); diff_heading->set_text(TTR("Status")); - diff_heading->set_tooltip(TTR("View file diffs before committing them to the latest version")); + diff_heading->set_tooltip_text(TTR("View file diffs before committing them to the latest version")); diff_hbc->add_child(diff_heading); diff_file_name = memnew(Label); @@ -561,7 +561,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() { diff_hbc->add_child(diff_file_name); diff_refresh_button = memnew(Button); - diff_refresh_button->set_tooltip(TTR("Detect changes in file diff")); + diff_refresh_button->set_tooltip_text(TTR("Detect changes in file diff")); diff_refresh_button->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons"))); diff_refresh_button->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_refresh_file_diff)); diff_hbc->add_child(diff_refresh_button); diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 4a144bc391..579c816dd8 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -40,6 +40,7 @@ #include "editor/editor_node.h" #include "editor/editor_properties.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/plugins/curve_editor_plugin.h" #include "editor/plugins/shader_editor_plugin.h" #include "scene/animation/animation_player.h" @@ -753,7 +754,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { Button *remove_btn = memnew(Button); remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); - remove_btn->set_tooltip(TTR("Remove") + " " + name_left); + remove_btn->set_tooltip_text(TTR("Remove") + " " + name_left); remove_btn->connect("pressed", callable_mp(editor, &VisualShaderEditor::_remove_input_port).bind(p_id, i), CONNECT_DEFERRED); hb->add_child(remove_btn); } else { @@ -780,7 +781,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) { if (is_group) { Button *remove_btn = memnew(Button); remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"))); - remove_btn->set_tooltip(TTR("Remove") + " " + name_left); + remove_btn->set_tooltip_text(TTR("Remove") + " " + name_left); remove_btn->connect("pressed", callable_mp(editor, &VisualShaderEditor::_remove_output_port).bind(p_id, i), CONNECT_DEFERRED); hb->add_child(remove_btn); @@ -1715,7 +1716,6 @@ void VisualShaderEditor::_update_graph() { graph_plugin->clear_links(); graph_plugin->make_dirty(true); - graph_plugin->update_theme(); for (int n_i = 0; n_i < nodes.size(); n_i++) { graph_plugin->add_node(type, nodes[n_i]); @@ -3703,9 +3703,11 @@ void VisualShaderEditor::_notification(int p_what) { graph->get_panner()->setup((ViewPanner::ControlScheme)EDITOR_GET("editors/panning/sub_editors_panning_scheme").operator int(), ED_GET_SHORTCUT("canvas_item_editor/pan_view"), bool(EditorSettings::get_singleton()->get("editors/panning/simple_panning"))); graph->set_warped_panning(bool(EditorSettings::get_singleton()->get("editors/panning/warped_mouse_panning"))); - [[fallthrough]]; - } + } break; + case NOTIFICATION_THEME_CHANGED: { + graph_plugin->update_theme(); + highend_label->set_modulate(get_theme_color(SNAME("vulkan_color"), SNAME("Editor"))); node_filter->set_right_icon(Control::get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); @@ -3758,7 +3760,7 @@ void VisualShaderEditor::_notification(int p_what) { tools->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Tools"), SNAME("EditorIcons"))); - if (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree()) { + if (is_visible_in_tree()) { _update_graph(); } } break; @@ -4063,7 +4065,7 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> p_input, bool type_changed = next_input_type != prev_input_type; - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Visual Shader Input Type Changed")); undo_redo->add_do_method(p_input.ptr(), "set_input_name", p_name); @@ -4132,7 +4134,7 @@ void VisualShaderEditor::_uniform_select_item(Ref<VisualShaderNodeUniformRef> p_ bool type_changed = p_uniform_ref->get_uniform_type_by_name(p_name) != p_uniform_ref->get_uniform_type_by_name(prev_name); - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("UniformRef Name Changed")); undo_redo->add_do_method(p_uniform_ref.ptr(), "set_uniform_name", p_name); @@ -4176,7 +4178,7 @@ void VisualShaderEditor::_varying_select_item(Ref<VisualShaderNodeVarying> p_var bool is_getter = Ref<VisualShaderNodeVaryingGetter>(p_varying.ptr()).is_valid(); - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Varying Name Changed")); undo_redo->add_do_method(p_varying.ptr(), "set_varying_name", p_name); @@ -4311,6 +4313,9 @@ void VisualShaderEditor::_update_varying_tree() { case VisualShader::VARYING_TYPE_FLOAT: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons"))); break; + case VisualShader::VARYING_TYPE_INT: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons"))); + break; case VisualShader::VARYING_TYPE_VECTOR_2D: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons"))); break; @@ -4320,8 +4325,8 @@ void VisualShaderEditor::_update_varying_tree() { case VisualShader::VARYING_TYPE_VECTOR_4D: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons"))); break; - case VisualShader::VARYING_TYPE_COLOR: - item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons"))); + case VisualShader::VARYING_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons"))); break; case VisualShader::VARYING_TYPE_TRANSFORM: item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons"))); @@ -4822,7 +4827,7 @@ VisualShaderEditor::VisualShaderEditor() { preview_shader = memnew(Button); preview_shader->set_flat(true); preview_shader->set_toggle_mode(true); - preview_shader->set_tooltip(TTR("Show generated shader code.")); + preview_shader->set_tooltip_text(TTR("Show generated shader code.")); graph->get_zoom_hbox()->add_child(preview_shader); preview_shader->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_preview_text)); @@ -4892,7 +4897,7 @@ VisualShaderEditor::VisualShaderEditor() { tools = memnew(MenuButton); filter_hb->add_child(tools); - tools->set_tooltip(TTR("Options")); + tools->set_tooltip_text(TTR("Options")); tools->get_popup()->connect("id_pressed", callable_mp(this, &VisualShaderEditor::_tools_menu_option)); tools->get_popup()->add_item(TTR("Expand All"), EXPAND_ALL); tools->get_popup()->add_item(TTR("Collapse All"), COLLAPSE_ALL); @@ -4924,7 +4929,7 @@ VisualShaderEditor::VisualShaderEditor() { highend_label->set_visible(false); highend_label->set_text("Vulkan"); highend_label->set_mouse_filter(Control::MOUSE_FILTER_STOP); - highend_label->set_tooltip(TTR("High-end node")); + highend_label->set_tooltip_text(TTR("High-end node")); node_desc = memnew(RichTextLabel); members_vb->add_child(node_desc); @@ -4962,10 +4967,11 @@ VisualShaderEditor::VisualShaderEditor() { varying_type = memnew(OptionButton); hb->add_child(varying_type); varying_type->add_item("Float"); + varying_type->add_item("Int"); varying_type->add_item("Vector2"); varying_type->add_item("Vector3"); varying_type->add_item("Vector4"); - varying_type->add_item("Color"); + varying_type->add_item("Boolean"); varying_type->add_item("Transform"); varying_name = memnew(LineEdit); @@ -5406,6 +5412,7 @@ VisualShaderEditor::VisualShaderEditor() { // TEXTURES add_options.push_back(AddOption("UVFunc", "Textures", "Common", "VisualShaderNodeUVFunc", TTR("Function to be applied on texture coordinates."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D)); + add_options.push_back(AddOption("UVPolarCoord", "Textures", "Common", "VisualShaderNodeUVPolarCoord", TTR("Polar coordinates conversion applied on texture coordinates."), {}, VisualShaderNode::PORT_TYPE_VECTOR_2D)); cubemap_node_option_idx = add_options.size(); add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubemap", TTR("Perform the cubic texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); @@ -5413,6 +5420,7 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("CurveTexture", "Textures", "Functions", "VisualShaderNodeCurveTexture", TTR("Perform the curve texture lookup."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); curve_xyz_node_option_idx = add_options.size(); add_options.push_back(AddOption("CurveXYZTexture", "Textures", "Functions", "VisualShaderNodeCurveXYZTexture", TTR("Perform the three components curve texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); + add_options.push_back(AddOption("LinearSceneDepth", "Textures", "Functions", "VisualShaderNodeLinearSceneDepth", TTR("Returns the depth value of the DEPTH_TEXTURE node in a linear space."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); texture2d_node_option_idx = add_options.size(); add_options.push_back(AddOption("Texture2D", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the 2D texture lookup."), {}, VisualShaderNode::PORT_TYPE_VECTOR_4D)); texture2d_array_node_option_idx = add_options.size(); @@ -5452,6 +5460,13 @@ VisualShaderEditor::VisualShaderEditor() { add_options.push_back(AddOption("TransformConstant", "Transform", "Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM)); add_options.push_back(AddOption("TransformUniform", "Transform", "Variables", "VisualShaderNodeTransformUniform", TTR("Transform uniform."), {}, VisualShaderNode::PORT_TYPE_TRANSFORM)); + // UTILITY + + add_options.push_back(AddOption("DistanceFade", "Utility", "", "VisualShaderNodeDistanceFade", TTR("The distance fade effect fades out each pixel based on its distance to another object."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ProximityFade", "Utility", "", "VisualShaderNodeProximityFade", TTR("The proximity fade effect fades out each pixel based on its distance to another object."), {}, VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("RandomRange", "Utility", "", "VisualShaderNodeRandomRange", TTR("Returns a random value between the minimum and maximum input values."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Remap", "Utility", "", "VisualShaderNodeRemap", TTR("Remaps a given input from the input range to the output range."), {}, VisualShaderNode::PORT_TYPE_SCALAR)); + // VECTOR add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), {}, VisualShaderNode::PORT_TYPE_VECTOR_3D)); @@ -5668,7 +5683,7 @@ VisualShaderEditor::VisualShaderEditor() { _update_options_menu(); - undo_redo = EditorNode::get_singleton()->get_undo_redo(); + undo_redo = EditorNode::get_undo_redo(); Ref<VisualShaderNodePluginDefault> default_plugin; default_plugin.instantiate(); @@ -5758,10 +5773,11 @@ public: Ref<Texture2D> type_icon[] = { EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("float"), SNAME("EditorIcons")), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("int"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector2"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector3"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Vector4"), SNAME("EditorIcons")), - EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Color"), SNAME("EditorIcons")), + EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("bool"), SNAME("EditorIcons")), EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Transform3D"), SNAME("EditorIcons")), }; @@ -5876,7 +5892,7 @@ public: return; } - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); updating = true; undo_redo->create_action(TTR("Edit Visual Property:") + " " + p_property, UndoRedo::MERGE_ENDS); @@ -6063,7 +6079,7 @@ Control *VisualShaderNodePluginDefault::create_editor(const Ref<Resource> &p_par return editor; } -void EditorPropertyShaderMode::_option_selected(int p_which) { +void EditorPropertyVisualShaderMode::_option_selected(int p_which) { Ref<VisualShader> visual_shader(Object::cast_to<VisualShader>(get_edited_object())); if (visual_shader->get_mode() == p_which) { return; @@ -6078,7 +6094,7 @@ void EditorPropertyShaderMode::_option_selected(int p_which) { return; } - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Visual Shader Mode Changed")); //do is easy undo_redo->add_do_method(visual_shader.ptr(), "set_mode", p_which); @@ -6149,39 +6165,39 @@ void EditorPropertyShaderMode::_option_selected(int p_which) { undo_redo->commit_action(); } -void EditorPropertyShaderMode::update_property() { +void EditorPropertyVisualShaderMode::update_property() { int which = get_edited_object()->get(get_edited_property()); options->select(which); } -void EditorPropertyShaderMode::setup(const Vector<String> &p_options) { +void EditorPropertyVisualShaderMode::setup(const Vector<String> &p_options) { for (int i = 0; i < p_options.size(); i++) { options->add_item(p_options[i], i); } } -void EditorPropertyShaderMode::set_option_button_clip(bool p_enable) { +void EditorPropertyVisualShaderMode::set_option_button_clip(bool p_enable) { options->set_clip_text(p_enable); } -void EditorPropertyShaderMode::_bind_methods() { +void EditorPropertyVisualShaderMode::_bind_methods() { } -EditorPropertyShaderMode::EditorPropertyShaderMode() { +EditorPropertyVisualShaderMode::EditorPropertyVisualShaderMode() { options = memnew(OptionButton); options->set_clip_text(true); add_child(options); add_focusable(options); - options->connect("item_selected", callable_mp(this, &EditorPropertyShaderMode::_option_selected)); + options->connect("item_selected", callable_mp(this, &EditorPropertyVisualShaderMode::_option_selected)); } -bool EditorInspectorShaderModePlugin::can_handle(Object *p_object) { +bool EditorInspectorVisualShaderModePlugin::can_handle(Object *p_object) { return true; // Can handle everything. } -bool EditorInspectorShaderModePlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) { +bool EditorInspectorVisualShaderModePlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, const bool p_wide) { if (p_path == "mode" && p_object->is_class("VisualShader") && p_type == Variant::INT) { - EditorPropertyShaderMode *mode_editor = memnew(EditorPropertyShaderMode); + EditorPropertyVisualShaderMode *mode_editor = memnew(EditorPropertyVisualShaderMode); Vector<String> options = p_hint_text.split(","); mode_editor->setup(options); add_property_editor(p_path, mode_editor); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index b846c34f9e..ede6513b83 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -48,6 +48,7 @@ class TextEdit; class Tree; class VisualShaderEditor; +class EditorUndoRedoManager; class VisualShaderNodePlugin : public RefCounted { GDCLASS(VisualShaderNodePlugin, RefCounted); @@ -192,7 +193,7 @@ class VisualShaderEditor : public VBoxContainer { PanelContainer *error_panel = nullptr; Label *error_label = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; Point2 saved_node_pos; bool saved_node_pos_dirty = false; @@ -529,8 +530,8 @@ public: virtual Control *create_editor(const Ref<Resource> &p_parent_resource, const Ref<VisualShaderNode> &p_node) override; }; -class EditorPropertyShaderMode : public EditorProperty { - GDCLASS(EditorPropertyShaderMode, EditorProperty); +class EditorPropertyVisualShaderMode : public EditorProperty { + GDCLASS(EditorPropertyVisualShaderMode, EditorProperty); OptionButton *options = nullptr; void _option_selected(int p_which); @@ -542,11 +543,11 @@ public: void setup(const Vector<String> &p_options); virtual void update_property() override; void set_option_button_clip(bool p_enable); - EditorPropertyShaderMode(); + EditorPropertyVisualShaderMode(); }; -class EditorInspectorShaderModePlugin : public EditorInspectorPlugin { - GDCLASS(EditorInspectorShaderModePlugin, EditorInspectorPlugin); +class EditorInspectorVisualShaderModePlugin : public EditorInspectorPlugin { + GDCLASS(EditorInspectorVisualShaderModePlugin, EditorInspectorPlugin); public: virtual bool can_handle(Object *p_object) override; diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp index e3b2be33df..713c90c075 100644 --- a/editor/plugins/voxel_gi_editor_plugin.cpp +++ b/editor/plugins/voxel_gi_editor_plugin.cpp @@ -100,7 +100,7 @@ void VoxelGIEditorPlugin::_notification(int p_what) { return; } - bake->set_tooltip(text); + bake->set_tooltip_text(text); } break; } } diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 5ce837f862..cdd088f0b1 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -76,8 +76,7 @@ static const char *enum_renames[][2] = { { "ARVR_STEREO", "XR_STEREO" }, // XRInterface { "ARVR_UNKNOWN_TRACKING", "XR_UNKNOWN_TRACKING" }, // XRInterface { "BAKE_ERROR_INVALID_MESH", "BAKE_ERROR_MESHES_INVALID" }, // LightmapGI - { "BODY_MODE_CHARACTER", "BODY_MODE_DYNAMIC" }, // PhysicsServer2D - { "BODY_MODE_DYNAMIC_LOCKED", "BODY_MODE_DYNAMIC_LINEAR" }, // PhysicsServer3D + { "BODY_MODE_CHARACTER", "BODY_MODE_RIGID_LINEAR" }, // PhysicsServer { "BUTTON_LEFT", "MOUSE_BUTTON_LEFT" }, // Globals { "BUTTON_MASK_LEFT", "MOUSE_BUTTON_MASK_LEFT" }, // Globals { "BUTTON_MASK_MIDDLE", "MOUSE_BUTTON_MASK_MIDDLE" }, // Globals @@ -125,15 +124,13 @@ static const char *enum_renames[][2] = { { "MATH_RAND", "MATH_RANDF_RANGE" }, // VisualScriptBuiltinFunc { "MATH_RANDOM", "MATH_RANDI_RANGE" }, // VisualScriptBuiltinFunc { "MATH_STEPIFY", "MATH_STEP_DECIMALS" }, // VisualScriptBuiltinFunc - { "MODE_CHARACTER", "MODE_DYNAMIC_LOCKED" }, // RigidBody2D, RigidBody3D - { "MODE_KINEMATIC", "FREEZE_MODE_KINEMATIC" }, // RigidDynamicBody + { "MODE_KINEMATIC", "FREEZE_MODE_KINEMATIC" }, // RigidBody { "MODE_OPEN_ANY", "FILE_MODE_OPEN_ANY" }, // FileDialog { "MODE_OPEN_DIR", "FILE_MODE_OPEN_DIR" }, // FileDialog { "MODE_OPEN_FILE", "FILE_MODE_OPEN_FILE" }, // FileDialog { "MODE_OPEN_FILES", "FILE_MODE_OPEN_FILES" }, // FileDialog - { "MODE_RIGID", "MODE_DYNAMIC" }, // RigidBody2D, RigidBody3D { "MODE_SAVE_FILE", "FILE_MODE_SAVE_FILE" }, // FileDialog - { "MODE_STATIC", "FREEZE_MODE_STATIC" }, // RigidDynamicBody + { "MODE_STATIC", "FREEZE_MODE_STATIC" }, // RigidBody { "NOTIFICATION_APP_PAUSED", "NOTIFICATION_APPLICATION_PAUSED" }, // MainLoop { "NOTIFICATION_APP_RESUMED", "NOTIFICATION_APPLICATION_RESUMED" }, // MainLoop { "NOTIFICATION_PATH_CHANGED", "NOTIFICATION_PATH_RENAMED" }, //Node @@ -201,13 +198,15 @@ static const char *gdscript_function_renames[][2] = { // { "set_color", "surface_set_color"}, // ImmediateMesh broke Light2D, Theme, SurfaceTool // { "set_event", "set_shortcut" }, // BaseButton - Cyclic Rename // { "set_extents", "set_size"}, // BoxShape, RectangleShape broke ReflectionProbe - // { "set_flag", "set_particle_flag"}, // ParticlesMaterial broke Window, HingeJoint3D + // { "set_flag", "set_particle_flag"}, // ParticleProcessMaterial broke Window, HingeJoint3D // { "set_h_offset", "set_drag_horizontal_offset" }, // Camera2D broke Camera3D, PathFollow3D, PathFollow2D // { "set_margin", "set_offset" }, // Control broke Shape3D, AtlasTexture // { "set_mode", "set_mode_file_mode" }, // FileDialog broke Panel, Shader, CSGPolygon, Tilemap // { "set_normal", "surface_set_normal"}, // ImmediateGeometry broke SurfaceTool, WorldMarginShape2D + // { "set_offset", "set_progress" }, // PathFollow2D, PathFollow3D - Too common // { "set_process_mode", "set_process_callback" }, // AnimationTree broke Node, Tween, Sky // { "set_refuse_new_network_connections", "set_refuse_new_connections"}, // MultiplayerAPI broke SceneTree + // { "set_tooltip", "set_tooltip_text" }, // Control, breaks TreeItem, at least for now. // { "set_uv", "surface_set_uv" }, // ImmediateMesh broke Polygon2D // { "set_v_offset", "set_drag_vertical_offset" }, // Camera2D broke Camera3D, PathFollow3D, PathFollow2D // {"get_points","get_points_id"},// Astar, broke Line2D, Convexpolygonshape @@ -220,16 +219,16 @@ static const char *gdscript_function_renames[][2] = { { "_update_wrap_at", "_update_wrap_at_column" }, // TextEdit { "add_animation", "add_animation_library" }, // AnimationPlayer { "add_cancel", "add_cancel_button" }, // AcceptDialog - { "add_central_force", "apply_central_force" }, //RigidDynamicBody2D + { "add_central_force", "apply_central_force" }, //RigidBody2D { "add_child_below_node", "add_sibling" }, // Node { "add_color_override", "add_theme_color_override" }, // Control { "add_constant_override", "add_theme_constant_override" }, // Control { "add_font_override", "add_theme_font_override" }, // Control - { "add_force", "apply_force" }, //RigidDynamicBody2D + { "add_force", "apply_force" }, //RigidBody2D { "add_icon_override", "add_theme_icon_override" }, // Control { "add_scene_import_plugin", "add_scene_format_importer_plugin" }, //EditorPlugin { "add_stylebox_override", "add_theme_stylebox_override" }, // Control - { "add_torque", "apply_torque" }, //RigidDynamicBody2D + { "add_torque", "apply_torque" }, //RigidBody2D { "apply_changes", "_apply_changes" }, // EditorPlugin { "bind_child_node_to_bone", "set_bone_children" }, // Skeleton3D { "body_add_force", "body_apply_force" }, // PhysicsServer2D @@ -275,8 +274,8 @@ static const char *gdscript_function_renames[][2] = { { "get_action_list", "action_get_events" }, // InputMap { "get_alt", "is_alt_pressed" }, // InputEventWithModifiers { "get_animation_process_mode", "get_process_callback" }, // AnimationPlayer - { "get_applied_force", "get_constant_force" }, //RigidDynamicBody2D - { "get_applied_torque", "get_constant_torque" }, //RigidDynamicBody2D + { "get_applied_force", "get_constant_force" }, //RigidBody2D + { "get_applied_torque", "get_constant_torque" }, //RigidBody2D { "get_audio_bus", "get_audio_bus_name" }, // Area3D { "get_bound_child_nodes_to_bone", "get_bone_children" }, // Skeleton3D { "get_camera", "get_camera_3d" }, // Viewport -> this is also convertable to get_camera_2d, broke GLTFNode @@ -371,6 +370,7 @@ static const char *gdscript_function_renames[][2] = { { "get_theme_item_types", "get_theme_item_type_list" }, // Theme { "get_timer_process_mode", "get_timer_process_callback" }, // Timer { "get_translation", "get_position" }, // Node3D broke GLTFNode which is used rarely + { "get_unit_offset", "get_progress_ratio" }, // PathFollow2D, PathFollow3D { "get_use_in_baked_light", "is_baking_navigation" }, // GridMap { "get_used_cells_by_id", "get_used_cells" }, // TileMap { "get_v_scrollbar", "get_v_scroll_bar" }, //ScrollContainer @@ -532,6 +532,7 @@ static const char *gdscript_function_renames[][2] = { { "set_timer_process_mode", "set_timer_process_callback" }, // Timer { "set_tonemap_auto_exposure", "set_tonemap_auto_exposure_enabled" }, // Environment { "set_translation", "set_position" }, // Node3D - this broke GLTFNode which is used rarely + { "set_unit_offset", "set_progress_ratio" }, // PathFollow2D, PathFollow3D { "set_uv2", "surface_set_uv2" }, // ImmediateMesh broke Surffacetool { "set_v_drag_enabled", "set_drag_vertical_enabled" }, // Camera2D { "set_valign", "set_vertical_alignment" }, // Label @@ -566,9 +567,23 @@ static const char *gdscript_function_renames[][2] = { { "to_utf8", "to_utf8_buffer" }, // String { "to_wchar", "to_utf32_buffer" }, // String // TODO - utf32 or utf16? - // Globals + // @GlobalScope + { "bytes2var", "bytes_to_var" }, + { "bytes2var_with_objects", "bytes_to_var_with_objects" }, + { "db2linear", "db_to_linear" }, + { "deg2rad", "deg_to_rad" }, + { "linear2db", "linear_to_db" }, + { "rad2deg", "rad_to_deg" }, { "rand_range", "randf_range" }, { "stepify", "snapped" }, + { "str2var", "str_to_var" }, + { "var2str", "var_to_str" }, + { "var2bytes", "var_to_bytes" }, + { "var2bytes_with_objects", "var_to_bytes_with_objects" }, + + // @GDScript + { "dict2inst", "dict_to_inst" }, + { "inst2dict", "inst_to_dict" }, { nullptr, nullptr }, }; @@ -607,13 +622,15 @@ static const char *csharp_function_renames[][2] = { // { "SetColor", "SurfaceSetColor"}, // ImmediateMesh broke Light2D, Theme, SurfaceTool // { "SetEvent", "SetShortcut" }, // BaseButton - Cyclic Rename // { "SetExtents", "SetSize"}, // BoxShape, RectangleShape broke ReflectionProbe - // { "SetFlag", "SetParticleFlag"}, // ParticlesMaterial broke Window, HingeJoint3D + // { "SetFlag", "SetParticleFlag"}, // ParticleProcessMaterial broke Window, HingeJoint3D // { "SetHOffset", "SetDragHorizontalOffset" }, // Camera2D broke Camera3D, PathFollow3D, PathFollow2D // { "SetMargin", "SetOffset" }, // Control broke Shape3D, AtlasTexture // { "SetMode", "SetModeFileMode" }, // FileDialog broke Panel, Shader, CSGPolygon, Tilemap // { "SetNormal", "SurfaceSetNormal"}, // ImmediateGeometry broke SurfaceTool, WorldMarginShape2D + // { "SetOffset", "SetProgress" }, // PathFollow2D, PathFollow3D - Too common // { "SetProcessMode", "SetProcessCallback" }, // AnimationTree broke Node, Tween, Sky // { "SetRefuseNewNetworkConnections", "SetRefuseNewConnections"}, // MultiplayerAPI broke SceneTree + // { "SetTooltip", "SetTooltipText" }, // Control, breaks TreeItem, at least for now. // { "SetUv", "SurfaceSetUv" }, // ImmediateMesh broke Polygon2D // { "SetVOffset", "SetDragVerticalOffset" }, // Camera2D broke Camera3D, PathFollow3D, PathFollow2D // {"GetPoints","GetPointsId"},// Astar, broke Line2D, Convexpolygonshape @@ -627,16 +644,16 @@ static const char *csharp_function_renames[][2] = { { "_UpdateWrapAt", "_UpdateWrapAtColumn" }, // TextEdit { "AddAnimation", "AddAnimationLibrary" }, // AnimationPlayer { "AddCancel", "AddCancelButton" }, // AcceptDialog - { "AddCentralForce", "AddConstantCentralForce" }, //RigidDynamicBody2D + { "AddCentralForce", "AddConstantCentralForce" }, //RigidBody2D { "AddChildBelowNode", "AddSibling" }, // Node { "AddColorOverride", "AddThemeColorOverride" }, // Control { "AddConstantOverride", "AddThemeConstantOverride" }, // Control { "AddFontOverride", "AddThemeFontOverride" }, // Control - { "AddForce", "AddConstantForce" }, //RigidDynamicBody2D + { "AddForce", "AddConstantForce" }, //RigidBody2D { "AddIconOverride", "AddThemeIconOverride" }, // Control { "AddSceneImportPlugin", "AddSceneFormatImporterPlugin" }, //EditorPlugin { "AddStyleboxOverride", "AddThemeStyleboxOverride" }, // Control - { "AddTorque", "AddConstantTorque" }, //RigidDynamicBody2D + { "AddTorque", "AddConstantTorque" }, //RigidBody2D { "BindChildNodeToBone", "SetBoneChildren" }, // Skeleton3D { "BumpmapToNormalmap", "BumpMapToNormalMap" }, // Image { "CanBeHidden", "_CanBeHidden" }, // EditorNode3DGizmoPlugin @@ -675,8 +692,8 @@ static const char *csharp_function_renames[][2] = { { "GetActionList", "ActionGetEvents" }, // InputMap { "GetAlt", "IsAltPressed" }, // InputEventWithModifiers { "GetAnimationProcessMode", "GetProcessCallback" }, // AnimationPlayer - { "GetAppliedForce", "GetConstantForce" }, //RigidDynamicBody2D - { "GetAppliedTorque", "GetConstantTorque" }, //RigidDynamicBody2D + { "GetAppliedForce", "GetConstantForce" }, //RigidBody2D + { "GetAppliedTorque", "GetConstantTorque" }, //RigidBody2D { "GetAudioBus", "GetAudioBusName" }, // Area3D { "GetBoundChildNodesToBone", "GetBoneChildren" }, // Skeleton3D { "GetCamera", "GetCamera3d" }, // Viewport -> this is also convertable to getCamera2d, broke GLTFNode @@ -767,6 +784,7 @@ static const char *csharp_function_renames[][2] = { { "GetThemeItemTypes", "GetThemeItemTypeList" }, // Theme { "GetTimerProcessMode", "GetTimerProcessCallback" }, // Timer { "GetTranslation", "GetPosition" }, // Node3D broke GLTFNode which is used rarely + { "GetUnitOffset", "GetProgressRatio" }, // PathFollow2D, PathFollow3D { "GetUseInBakedLight", "IsBakingNavigation" }, // GridMap { "GetUsedCellsById", "GetUsedCells" }, // TileMap { "GetVScrollbar", "GetVScrollBar" }, //ScrollContainer @@ -918,6 +936,7 @@ static const char *csharp_function_renames[][2] = { { "SetTimerProcessMode", "SetTimerProcessCallback" }, // Timer { "SetTonemapAutoExposure", "SetTonemapAutoExposureEnabled" }, // Environment { "SetTranslation", "SetPosition" }, // Node3D - this broke GLTFNode which is used rarely + { "SetUnitOffset", "SetProgressRatio" }, // PathFollow2D, PathFollow3D { "SetUv2", "SurfaceSetUv2" }, // ImmediateMesh broke Surffacetool { "SetVDragEnabled", "SetDragVerticalEnabled" }, // Camera2D { "SetValign", "SetVerticalAlignment" }, // Label @@ -952,9 +971,23 @@ static const char *csharp_function_renames[][2] = { { "ToUtf8", "ToUtf8Buffer" }, // String { "ToWchar", "ToUtf32Buffer" }, // String // TODO - utf32 or utf16? - // Globals + // @GlobalScope + { "Bytes2Var", "BytesToVar" }, + { "Bytes2VarWithObjects", "BytesToVarWithObjects" }, + { "Db2Linear", "DbToLinear" }, + { "Deg2Rad", "DegToRad" }, + { "Linear2Db", "LinearToDb" }, + { "Rad2Deg", "RadToDeg" }, { "RandRange", "RandfRange" }, { "Stepify", "Snapped" }, + { "Str2Var", "StrToVar" }, + { "Var2Str", "VarToStr" }, + { "Var2Bytes", "VarToBytes" }, + { "Var2BytesWithObjects", "VarToBytesWithObjects" }, + + // @GDScript + { "Dict2Inst", "DictToInst" }, + { "Inst2Dict", "InstToDict" }, { nullptr, nullptr }, }; @@ -969,6 +1002,7 @@ static const char *gdscript_properties_renames[][2] = { // // {"meta","meta_pressed"},// This may broke a lot of comments and user variables // // {"pause_mode","process_mode"}, // Node - Cyclic rename, look for others // // {"rotate","rotates"}, // PathFollow2D - probably function exists with same name + // // {"offset","progress"}, // PathFollow2D, PathFollow3D - Name is way too vague // // {"shift","shift_pressed"},// This may broke a lot of comments and user variables // { "autowrap", "autowrap_mode" }, // Label // { "cast_to", "target_position" }, // RayCast2D, RayCast3D @@ -990,6 +1024,7 @@ static const char *gdscript_properties_renames[][2] = { { "close_h_ofs", "close_h_offset" }, // Theme { "close_v_ofs", "close_v_offset" }, // Theme { "commentfocus", "comment_focus" }, // Theme + { "contacts_reported", "max_contacts_reported" }, // RigidBody { "drag_margin_bottom", "drag_bottom_margin" }, // Camera2D { "drag_margin_h_enabled", "drag_horizontal_enabled" }, // Camera2D { "drag_margin_left", "drag_left_margin" }, // Camera2D @@ -1006,8 +1041,10 @@ static const char *gdscript_properties_renames[][2] = { { "global_rate_scale", "playback_speed_scale" }, // AudioServer { "gravity_distance_scale", "gravity_point_distance_scale" }, // Area2D { "gravity_vec", "gravity_direction" }, // Area2D + { "hint_tooltip", "tooltip_text" }, // Control { "hseparation", "h_separation" }, // Theme { "iterations_per_second", "physics_ticks_per_second" }, // Engine + { "invert_enable", "invert_enabled" }, // Polygon2D { "margin_bottom", "offset_bottom" }, // Control broke NinePatchRect, StyleBox { "margin_left", "offset_left" }, // Control broke NinePatchRect, StyleBox { "margin_right", "offset_right" }, // Control broke NinePatchRect, StyleBox @@ -1036,6 +1073,7 @@ static const char *gdscript_properties_renames[][2] = { { "table_hseparation", "table_h_separation" }, // Theme { "table_vseparation", "table_v_separation" }, // Theme { "translation", "position" }, // Node3D - broke GLTFNode + { "unit_offset", "progress_ratio" }, // PathFollow2D, PathFollow3D { "vseparation", "v_separation" }, // Theme { nullptr, nullptr }, @@ -1051,6 +1089,7 @@ static const char *csharp_properties_renames[][2] = { // // {"Meta","MetaPressed"},// This may broke a lot of comments and user variables // // {"PauseMode","ProcessMode"}, // Node - Cyclic rename, look for others // // {"Rotate","Rotates"}, // PathFollow2D - probably function exists with same name + // // {"Offset","Progress"}, // PathFollow2D, PathFollow3D - Name is way too vague // // {"Shift","ShiftPressed"},// This may broke a lot of comments and user variables // { "Autowrap", "AutowrapMode" }, // Label // { "CastTo", "TargetPosition" }, // RayCast2D, RayCast3D @@ -1087,8 +1126,10 @@ static const char *csharp_properties_renames[][2] = { { "GlobalRateScale", "PlaybackSpeedScale" }, // AudioServer { "GravityDistanceScale", "GravityPointDistanceScale" }, // Area2D { "GravityVec", "GravityDirection" }, // Area2D + { "HintTooltip", "TooltipText" }, // Control { "Hseparation", "HSeparation" }, // Theme { "IterationsPerSecond", "PhysicsTicksPerSecond" }, // Engine + { "InvertEnable", "InvertEnabled" }, // Polygon2D { "MarginBottom", "OffsetBottom" }, // Control broke NinePatchRect, StyleBox { "MarginLeft", "OffsetLeft" }, // Control broke NinePatchRect, StyleBox { "MarginRight", "OffsetRight" }, // Control broke NinePatchRect, StyleBox @@ -1117,6 +1158,7 @@ static const char *csharp_properties_renames[][2] = { { "TableHseparation", "TableHSeparation" }, // Theme { "TableVseparation", "TableVSeparation" }, // Theme { "Translation", "Position" }, // Node3D - broke GLTFNode + { "UnitOffset", "ProgressRatio" }, // PathFollow2D, PathFollow3D { "Vseparation", "VSeparation" }, // Theme { nullptr, nullptr }, @@ -1344,6 +1386,7 @@ static const char *class_renames[][2] = { { "PanoramaSky", "Sky" }, { "Particles", "GPUParticles3D" }, // Be careful, this will be used everywhere { "Particles2D", "GPUParticles2D" }, + { "ParticlesMaterial", "ParticleProcessMaterial" }, { "Path", "Path3D" }, // Be careful, this will be used everywhere { "PathFollow", "PathFollow3D" }, { "PhysicalBone", "PhysicalBone3D" }, @@ -1362,6 +1405,8 @@ static const char *class_renames[][2] = { { "PinJoint", "PinJoint3D" }, { "PlaneShape", "WorldBoundaryShape3D" }, { "PopupDialog", "Popup" }, + { "Position2D", "Marker2D" }, + { "Position3D", "Marker3D" }, { "ProceduralSky", "Sky" }, { "RayCast", "RayCast3D" }, { "RayShape", "SeparationRayShape3D" }, @@ -1369,19 +1414,17 @@ static const char *class_renames[][2] = { { "Reference", "RefCounted" }, // Be careful, this will be used everywhere { "RemoteTransform", "RemoteTransform3D" }, { "ResourceInteractiveLoader", "ResourceLoader" }, - { "RigidBody", "RigidDynamicBody3D" }, - { "RigidBody2D", "RigidDynamicBody2D" }, + { "RigidBody", "RigidBody3D" }, { "SceneTreeTween", "Tween" }, { "Shape", "Shape3D" }, // Be careful, this will be used everywhere { "ShortCut", "Shortcut" }, { "Skeleton", "Skeleton3D" }, { "SkeletonIK", "SkeletonIK3D" }, { "SliderJoint", "SliderJoint3D" }, - { "SoftBody", "SoftDynamicBody3D" }, + { "SoftBody", "SoftBody3D" }, { "Spatial", "Node3D" }, { "SpatialGizmo", "Node3DGizmo" }, { "SpatialMaterial", "StandardMaterial3D" }, - { "SpatialVelocityTracker", "VelocityTracker3D" }, { "SphereShape", "SphereShape3D" }, { "SpotLight", "SpotLight3D" }, { "SpringArm", "SpringArm3D" }, @@ -1607,8 +1650,8 @@ public: RegEx reg_is_empty = RegEx("\\bempty\\("); RegEx reg_super = RegEx("([\t ])\\.([a-zA-Z_])"); RegEx reg_json_to = RegEx("\\bto_json\\b"); - RegEx reg_json_parse = RegEx("([\t]{0,})([^\n]+)parse_json\\(([^\n]+)"); - RegEx reg_json_non_new = RegEx("([\t]{0,})([^\n]+)JSON\\.parse\\(([^\n]+)"); + RegEx reg_json_parse = RegEx("([\t ]{0,})([^\n]+)parse_json\\(([^\n]+)"); + RegEx reg_json_non_new = RegEx("([\t ]{0,})([^\n]+)JSON\\.parse\\(([^\n]+)"); RegEx reg_export = RegEx("export\\(([a-zA-Z0-9_]+)\\)[ ]+var[ ]+([a-zA-Z0-9_]+)"); RegEx reg_export_advanced = RegEx("export\\(([^)^\n]+)\\)[ ]+var[ ]+([a-zA-Z0-9_]+)([^\n]+)"); RegEx reg_setget_setget = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+)setget[ \t]+([a-zA-Z0-9_]+)[ \t]*,[ \t]*([a-zA-Z0-9_]+)"); @@ -2103,6 +2146,8 @@ bool ProjectConverter3To4::test_conversion(const RegExContainer ®_container) valid = valid & test_conversion_single_additional_builtin("(connect(A,B,C) != OK):", "(connect(A,Callable(B,C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("(connect(A,B,C,D) != OK):", "(connect(A,Callable(B,C).bind(D)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("(connect(A,B,C,[D]) != OK):", "(connect(A,Callable(B,C).bind(D)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("(connect(A,B,C,[D,E]) != OK):", "(connect(A,Callable(B,C).bind(D,E)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); + valid = valid & test_conversion_single_additional_builtin("(connect(A,B,C,[D,E],F) != OK):", "(connect(A,Callable(B,C).bind(D,E),F) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("(connect(A,B,C,D,E) != OK):", "(connect(A,Callable(B,C).bind(D),E) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); valid = valid & test_conversion_single_additional_builtin("(start(A,B) != OK):", "(start(Callable(A,B)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); @@ -2442,6 +2487,21 @@ Vector<String> ProjectConverter3To4::parse_arguments(const String &line) { } break; }; + case '[': { + parts_counter++; + if (parts_counter == 1 && !is_inside_string) { + start_part = current_index; + } + break; + }; + case ']': { + parts_counter--; + if (parts_counter == 0 && !is_inside_string) { + parts.append(line.substr(start_part, current_index - start_part)); + start_part = current_index; + } + break; + }; case ',': { if (parts_counter == 1 && !is_inside_string) { parts.append(line.substr(start_part + 1, current_index - start_part - 1)); diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 21891e381b..a17a759c17 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -47,6 +47,7 @@ #include "editor/editor_settings.h" #include "editor/editor_themes.h" #include "editor/editor_vcs_interface.h" +#include "main/main.h" #include "scene/gui/center_container.h" #include "scene/gui/line_edit.h" #include "scene/gui/margin_container.h" @@ -483,12 +484,20 @@ private: project_features.sort(); initial_settings["application/config/features"] = project_features; initial_settings["application/config/name"] = project_name->get_text().strip_edges(); - initial_settings["application/config/icon"] = "res://icon.png"; + initial_settings["application/config/icon"] = "res://icon.svg"; if (ProjectSettings::get_singleton()->save_custom(dir.plus_file("project.godot"), initial_settings, Vector<String>(), false) != OK) { set_message(TTR("Couldn't create project.godot in project path."), MESSAGE_ERROR); } else { - ResourceSaver::save(create_unscaled_default_project_icon(), dir.plus_file("icon.png")); + // Store default project icon in SVG format. + Error err; + Ref<FileAccess> fa_icon = FileAccess::open(dir.plus_file("icon.svg"), FileAccess::WRITE, &err); + fa_icon->store_string(get_default_project_icon()); + + if (err != OK) { + set_message(TTR("Couldn't create icon.svg in project path."), MESSAGE_ERROR); + } + EditorVCSInterface::create_vcs_metadata_files(EditorVCSInterface::VCSMetadata(vcs_metadata_selection->get_selected()), dir); } } else if (mode == MODE_INSTALL) { @@ -1164,6 +1173,12 @@ void ProjectList::load_project_icon(int p_index) { icon = default_icon; } + // The default project icon is 128×128 to look crisp on hiDPI displays, + // but we want the actual displayed size to be 64×64 on loDPI displays. + item.control->icon->set_ignore_texture_size(true); + item.control->icon->set_custom_minimum_size(Size2(64, 64) * EDSCALE); + item.control->icon->set_stretch_mode(TextureRect::STRETCH_KEEP_ASPECT_CENTERED); + item.control->icon->set_texture(icon); item.control->icon_needs_reload = false; } @@ -1347,7 +1362,7 @@ void ProjectList::create_project_item_control(int p_index) { hb->connect("draw", callable_mp(this, &ProjectList::_panel_draw).bind(hb)); hb->connect("gui_input", callable_mp(this, &ProjectList::_panel_input).bind(hb)); hb->add_theme_constant_override("separation", 10 * EDSCALE); - hb->set_tooltip(item.description); + hb->set_tooltip_text(item.description); VBoxContainer *favorite_box = memnew(VBoxContainer); favorite_box->set_name("FavoriteBox"); @@ -1430,9 +1445,9 @@ void ProjectList::create_project_item_control(int p_index) { if (!item.missing) { show->connect("pressed", callable_mp(this, &ProjectList::_show_project).bind(item.path)); - show->set_tooltip(TTR("Show in File Manager")); + show->set_tooltip_text(TTR("Show in File Manager")); } else { - show->set_tooltip(TTR("Error: Project is missing on the filesystem.")); + show->set_tooltip_text(TTR("Error: Project is missing on the filesystem.")); } Label *fpath = memnew(Label(item.path)); @@ -2109,27 +2124,15 @@ void ProjectManager::_open_selected_projects() { List<String> args; + for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_TOOL)) { + args.push_back(a); + } + args.push_back("--path"); args.push_back(path); args.push_back("--editor"); - if (OS::get_singleton()->is_stdout_debug_enabled()) { - args.push_back("--debug"); - } - - if (OS::get_singleton()->is_stdout_verbose()) { - args.push_back("--verbose"); - } - - if (OS::get_singleton()->is_disable_crash_handler()) { - args.push_back("--disable-crash-handler"); - } - - if (OS::get_singleton()->is_single_window()) { - args.push_back("--single-window"); - } - Error err = OS::get_singleton()->create_instance(args); ERR_FAIL_COND(err); } @@ -2242,13 +2245,13 @@ void ProjectManager::_run_project_confirm() { List<String> args; + for (const String &a : Main::get_forwardable_cli_arguments(Main::CLI_SCOPE_PROJECT)) { + args.push_back(a); + } + args.push_back("--path"); args.push_back(path); - if (OS::get_singleton()->is_disable_crash_handler()) { - args.push_back("--disable-crash-handler"); - } - Error err = OS::get_singleton()->create_instance(args); ERR_FAIL_COND(err); } @@ -2567,7 +2570,7 @@ ProjectManager::ProjectManager() { search_box = memnew(LineEdit); search_box->set_placeholder(TTR("Filter Projects")); - search_box->set_tooltip(TTR("This field filters projects by name and last path component.\nTo filter projects by name and full path, the query must contain at least one `/` character.")); + search_box->set_tooltip_text(TTR("This field filters projects by name and last path component.\nTo filter projects by name and full path, the query must contain at least one `/` character.")); search_box->connect("text_changed", callable_mp(this, &ProjectManager::_on_search_term_changed)); search_box->set_h_size_flags(Control::SIZE_EXPAND_FILL); hb->add_child(search_box); @@ -2707,7 +2710,7 @@ ProjectManager::ProjectManager() { // Fade the version label to be less prominent, but still readable. version_btn->set_self_modulate(Color(1, 1, 1, 0.6)); version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER); - version_btn->set_tooltip(TTR("Click to copy.")); + version_btn->set_tooltip_text(TTR("Click to copy.")); version_btn->connect("pressed", callable_mp(this, &ProjectManager::_version_button_pressed)); spacer_vb->add_child(version_btn); diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index ca5eeaa787..279ed55a38 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -35,6 +35,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" +#include "editor/editor_undo_redo_manager.h" #include "servers/movie_writer/movie_writer.h" ProjectSettingsEditor *ProjectSettingsEditor::singleton = nullptr; @@ -517,24 +518,6 @@ void ProjectSettingsEditor::_update_action_map_editor() { action_map_editor->update_action_list(actions); } -void ProjectSettingsEditor::_update_theme() { - search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); - restart_close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons"))); - restart_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); - restart_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); - restart_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); - - type_box->clear(); - for (int i = 0; i < Variant::VARIANT_MAX; i++) { - if (i == Variant::NIL || i == Variant::OBJECT || i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) { - // These types can't be serialized properly, so skip them. - continue; - } - String type = Variant::get_type_name(Variant::Type(i)); - type_box->add_icon_item(get_theme_icon(type, SNAME("EditorIcons")), type, i); - } -} - void ProjectSettingsEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -546,11 +529,24 @@ void ProjectSettingsEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { general_settings_inspector->edit(ps); _update_action_map_editor(); - _update_theme(); } break; case NOTIFICATION_THEME_CHANGED: { - _update_theme(); + search_box->set_right_icon(get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); + restart_close_button->set_icon(get_theme_icon(SNAME("Close"), SNAME("EditorIcons"))); + restart_container->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + restart_icon->set_texture(get_theme_icon(SNAME("StatusWarning"), SNAME("EditorIcons"))); + restart_label->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor"))); + + type_box->clear(); + for (int i = 0; i < Variant::VARIANT_MAX; i++) { + if (i == Variant::NIL || i == Variant::OBJECT || i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) { + // These types can't be serialized properly, so skip them. + continue; + } + String type = Variant::get_type_name(Variant::Type(i)); + type_box->add_icon_item(get_theme_icon(type, SNAME("EditorIcons")), type, i); + } } break; } } @@ -566,7 +562,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { set_title(TTR("Project Settings (project.godot)")); ps = ProjectSettings::get_singleton(); - undo_redo = &p_data->get_undo_redo(); + undo_redo = p_data->get_undo_redo(); data = p_data; tab_container = memnew(TabContainer); @@ -625,7 +621,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) { custom_properties->add_child(del_button); general_settings_inspector = memnew(SectionedInspector); - general_settings_inspector->get_inspector()->set_undo_redo(EditorNode::get_singleton()->get_undo_redo()); + general_settings_inspector->get_inspector()->set_undo_redo(EditorNode::get_undo_redo()); general_settings_inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL); general_settings_inspector->register_search_box(search_box); general_settings_inspector->get_inspector()->set_use_filter(true); diff --git a/editor/project_settings_editor.h b/editor/project_settings_editor.h index c2d2c2d8f4..f01f5c1836 100644 --- a/editor/project_settings_editor.h +++ b/editor/project_settings_editor.h @@ -32,7 +32,6 @@ #define PROJECT_SETTINGS_EDITOR_H #include "core/config/project_settings.h" -#include "core/object/undo_redo.h" #include "editor/action_map_editor.h" #include "editor/editor_autoload_settings.h" #include "editor/editor_data.h" @@ -43,6 +42,7 @@ #include "editor/shader_globals_editor.h" #include "scene/gui/tab_container.h" +class EditorUndoRedoManager; class FileSystemDock; class ProjectSettingsEditor : public AcceptDialog { @@ -77,7 +77,7 @@ class ProjectSettingsEditor : public AcceptDialog { ImportDefaultsEditor *import_defaults_editor = nullptr; EditorData *data = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; void _advanced_toggled(bool p_button_pressed); void _update_advanced(bool p_is_advanced); @@ -106,7 +106,6 @@ class ProjectSettingsEditor : public AcceptDialog { void _action_renamed(const String &p_old_name, const String &p_new_name); void _action_reordered(const String &p_action_name, const String &p_relative_to, bool p_before); void _update_action_map_editor(); - void _update_theme(); protected: void _notification(int p_what); diff --git a/editor/rename_dialog.cpp b/editor/rename_dialog.cpp index 665aca6a37..7b4df696b7 100644 --- a/editor/rename_dialog.cpp +++ b/editor/rename_dialog.cpp @@ -38,6 +38,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_themes.h" +#include "editor/editor_undo_redo_manager.h" #include "modules/regex/regex.h" #include "plugins/script_editor_plugin.h" #include "scene/gui/control.h" @@ -45,7 +46,7 @@ #include "scene/gui/separator.h" #include "scene/gui/tab_container.h" -RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_undo_redo) { +RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, Ref<EditorUndoRedoManager> p_undo_redo) { scene_tree_editor = p_scene_tree_editor; undo_redo = p_undo_redo; preview_node = nullptr; @@ -138,7 +139,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und but_insert_name = memnew(Button); but_insert_name->set_text("NAME"); - but_insert_name->set_tooltip(String("${NAME}\n") + TTR("Node name.")); + but_insert_name->set_tooltip_text(String("${NAME}\n") + TTR("Node name.")); but_insert_name->set_focus_mode(Control::FOCUS_NONE); but_insert_name->connect("pressed", callable_mp(this, &RenameDialog::_insert_text).bind("${NAME}")); but_insert_name->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -148,7 +149,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und but_insert_parent = memnew(Button); but_insert_parent->set_text("PARENT"); - but_insert_parent->set_tooltip(String("${PARENT}\n") + TTR("Node's parent name, if available.")); + but_insert_parent->set_tooltip_text(String("${PARENT}\n") + TTR("Node's parent name, if available.")); but_insert_parent->set_focus_mode(Control::FOCUS_NONE); but_insert_parent->connect("pressed", callable_mp(this, &RenameDialog::_insert_text).bind("${PARENT}")); but_insert_parent->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -158,7 +159,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und but_insert_type = memnew(Button); but_insert_type->set_text("TYPE"); - but_insert_type->set_tooltip(String("${TYPE}\n") + TTR("Node type.")); + but_insert_type->set_tooltip_text(String("${TYPE}\n") + TTR("Node type.")); but_insert_type->set_focus_mode(Control::FOCUS_NONE); but_insert_type->connect("pressed", callable_mp(this, &RenameDialog::_insert_text).bind("${TYPE}")); but_insert_type->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -168,7 +169,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und but_insert_scene = memnew(Button); but_insert_scene->set_text("SCENE"); - but_insert_scene->set_tooltip(String("${SCENE}\n") + TTR("Current scene name.")); + but_insert_scene->set_tooltip_text(String("${SCENE}\n") + TTR("Current scene name.")); but_insert_scene->set_focus_mode(Control::FOCUS_NONE); but_insert_scene->connect("pressed", callable_mp(this, &RenameDialog::_insert_text).bind("${SCENE}")); but_insert_scene->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -178,7 +179,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und but_insert_root = memnew(Button); but_insert_root->set_text("ROOT"); - but_insert_root->set_tooltip(String("${ROOT}\n") + TTR("Root node name.")); + but_insert_root->set_tooltip_text(String("${ROOT}\n") + TTR("Root node name.")); but_insert_root->set_focus_mode(Control::FOCUS_NONE); but_insert_root->connect("pressed", callable_mp(this, &RenameDialog::_insert_text).bind("${ROOT}")); but_insert_root->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -188,7 +189,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und but_insert_count = memnew(Button); but_insert_count->set_text("COUNTER"); - but_insert_count->set_tooltip(String("${COUNTER}\n") + TTR("Sequential integer counter.\nCompare counter options.")); + but_insert_count->set_tooltip_text(String("${COUNTER}\n") + TTR("Sequential integer counter.\nCompare counter options.")); but_insert_count->set_focus_mode(Control::FOCUS_NONE); but_insert_count->connect("pressed", callable_mp(this, &RenameDialog::_insert_text).bind("${COUNTER}")); but_insert_count->set_h_size_flags(Control::SIZE_EXPAND_FILL); @@ -196,7 +197,7 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und chk_per_level_counter = memnew(CheckBox); chk_per_level_counter->set_text(TTR("Per-level Counter")); - chk_per_level_counter->set_tooltip(TTR("If set, the counter restarts for each group of child nodes.")); + chk_per_level_counter->set_tooltip_text(TTR("If set, the counter restarts for each group of child nodes.")); vbc_substitute->add_child(chk_per_level_counter); HBoxContainer *hbc_count_options = memnew(HBoxContainer); @@ -204,32 +205,32 @@ RenameDialog::RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_und Label *lbl_count_start = memnew(Label); lbl_count_start->set_text(TTR("Start")); - lbl_count_start->set_tooltip(TTR("Initial value for the counter.")); + lbl_count_start->set_tooltip_text(TTR("Initial value for the counter.")); hbc_count_options->add_child(lbl_count_start); spn_count_start = memnew(SpinBox); - spn_count_start->set_tooltip(TTR("Initial value for the counter.")); + spn_count_start->set_tooltip_text(TTR("Initial value for the counter.")); spn_count_start->set_step(1); spn_count_start->set_min(0); hbc_count_options->add_child(spn_count_start); Label *lbl_count_step = memnew(Label); lbl_count_step->set_text(TTR("Step")); - lbl_count_step->set_tooltip(TTR("Amount by which counter is incremented for each node.")); + lbl_count_step->set_tooltip_text(TTR("Amount by which counter is incremented for each node.")); hbc_count_options->add_child(lbl_count_step); spn_count_step = memnew(SpinBox); - spn_count_step->set_tooltip(TTR("Amount by which counter is incremented for each node.")); + spn_count_step->set_tooltip_text(TTR("Amount by which counter is incremented for each node.")); spn_count_step->set_step(1); hbc_count_options->add_child(spn_count_step); Label *lbl_count_padding = memnew(Label); lbl_count_padding->set_text(TTR("Padding")); - lbl_count_padding->set_tooltip(TTR("Minimum number of digits for the counter.\nMissing digits are padded with leading zeros.")); + lbl_count_padding->set_tooltip_text(TTR("Minimum number of digits for the counter.\nMissing digits are padded with leading zeros.")); hbc_count_options->add_child(lbl_count_padding); spn_count_padding = memnew(SpinBox); - spn_count_padding->set_tooltip(TTR("Minimum number of digits for the counter.\nMissing digits are padded with leading zeros.")); + spn_count_padding->set_tooltip_text(TTR("Minimum number of digits for the counter.\nMissing digits are padded with leading zeros.")); spn_count_padding->set_step(1); hbc_count_options->add_child(spn_count_padding); @@ -581,7 +582,7 @@ void RenameDialog::rename() { // Forward recursive as opposed to the actual renaming. _iterate_scene(root_node, selected_node_list, &global_count); - if (undo_redo && !to_rename.is_empty()) { + if (undo_redo.is_valid() && !to_rename.is_empty()) { undo_redo->create_action(TTR("Batch Rename")); // Make sure to iterate reversed so that child nodes will find parents. diff --git a/editor/rename_dialog.h b/editor/rename_dialog.h index f3a850045e..dac73b13b8 100644 --- a/editor/rename_dialog.h +++ b/editor/rename_dialog.h @@ -34,7 +34,6 @@ #include "modules/modules_enabled.gen.h" // For regex. #ifdef MODULE_REGEX_ENABLED -#include "core/object/undo_redo.h" #include "editor/scene_tree_editor.h" #include "scene/gui/check_box.h" #include "scene/gui/check_button.h" @@ -43,6 +42,8 @@ #include "scene/gui/spin_box.h" #include "scene/gui/tab_container.h" +class EditorUndoRedoManager; + class RenameDialog : public ConfirmationDialog { GDCLASS(RenameDialog, ConfirmationDialog); @@ -63,7 +64,7 @@ class RenameDialog : public ConfirmationDialog { static void _error_handler(void *p_self, const char *p_func, const char *p_file, int p_line, const char *p_error, const char *p_errorexp, bool p_editor_notify, ErrorHandlerType p_type); SceneTreeEditor *scene_tree_editor = nullptr; - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; int global_count = 0; LineEdit *lne_search = nullptr; @@ -109,7 +110,7 @@ public: void reset(); void rename(); - RenameDialog(SceneTreeEditor *p_scene_tree_editor, UndoRedo *p_undo_redo = nullptr); + RenameDialog(SceneTreeEditor *p_scene_tree_editor, Ref<EditorUndoRedoManager> p_undo_redo = Ref<EditorUndoRedoManager>()); ~RenameDialog() {} }; diff --git a/editor/scene_create_dialog.cpp b/editor/scene_create_dialog.cpp index 94a5c07709..30a41eea7a 100644 --- a/editor/scene_create_dialog.cpp +++ b/editor/scene_create_dialog.cpp @@ -46,7 +46,6 @@ void SceneCreateDialog::_notification(int p_what) { switch (p_what) { - case NOTIFICATION_ENTER_TREE: case NOTIFICATION_THEME_CHANGED: { select_node_button->set_icon(get_theme_icon(SNAME("ClassList"), SNAME("EditorIcons"))); node_type_2d->set_icon(get_theme_icon(SNAME("Node2D"), SNAME("EditorIcons"))); diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index d19a40599f..c7c713fc67 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -222,28 +222,28 @@ void SceneTreeDock::_perform_instantiate_scenes(const Vector<String> &p_files, N return; } - editor_data->get_undo_redo().create_action(TTR("Instance Scene(s)")); + editor_data->get_undo_redo()->create_action(TTR("Instance Scene(s)")); for (int i = 0; i < instances.size(); i++) { Node *instantiated_scene = instances[i]; - editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene, true); + editor_data->get_undo_redo()->add_do_method(parent, "add_child", instantiated_scene, true); if (p_pos >= 0) { - editor_data->get_undo_redo().add_do_method(parent, "move_child", instantiated_scene, p_pos + i); + editor_data->get_undo_redo()->add_do_method(parent, "move_child", instantiated_scene, p_pos + i); } - editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_owner", edited_scene); - editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); - editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", instantiated_scene); - editor_data->get_undo_redo().add_do_reference(instantiated_scene); - editor_data->get_undo_redo().add_undo_method(parent, "remove_child", instantiated_scene); + editor_data->get_undo_redo()->add_do_method(instantiated_scene, "set_owner", edited_scene); + editor_data->get_undo_redo()->add_do_method(editor_selection, "clear"); + editor_data->get_undo_redo()->add_do_method(editor_selection, "add_node", instantiated_scene); + editor_data->get_undo_redo()->add_do_reference(instantiated_scene); + editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", instantiated_scene); String new_name = parent->validate_child_name(instantiated_scene); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name))); + editor_data->get_undo_redo()->add_do_method(ed, "live_debug_instance_node", edited_scene->get_path_to(parent), p_files[i], new_name); + editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(new_name))); } - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); _push_item(instances[instances.size() - 1]); for (int i = 0; i < instances.size(); i++) { emit_signal(SNAME("node_created"), instances[i]); @@ -265,7 +265,7 @@ void SceneTreeDock::_replace_with_branch_scene(const String &p_file, Node *base) return; } - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Replace with Branch Scene")); Node *parent = base->get_parent(); @@ -510,9 +510,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } if (selected) { - create_dialog->popup_create(false, true, selected->get_class()); + create_dialog->popup_create(false, true, selected->get_class(), selected->get_name()); } - } break; case TOOL_EXTEND_SCRIPT: { attach_script_to_selected(true); @@ -531,23 +530,23 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { return; } - editor_data->get_undo_redo().create_action(TTR("Detach Script")); - editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "push_item", (Script *)nullptr); + editor_data->get_undo_redo()->create_action(TTR("Detach Script"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + editor_data->get_undo_redo()->add_do_method(EditorNode::get_singleton(), "push_item", (Script *)nullptr); for (int i = 0; i < selection.size(); i++) { Node *n = Object::cast_to<Node>(selection[i]); Ref<Script> existing = n->get_script(); Ref<Script> empty = EditorNode::get_singleton()->get_object_custom_type_base(n); if (existing != empty) { - editor_data->get_undo_redo().add_do_method(n, "set_script", empty); - editor_data->get_undo_redo().add_undo_method(n, "set_script", existing); + editor_data->get_undo_redo()->add_do_method(n, "set_script", empty); + editor_data->get_undo_redo()->add_undo_method(n, "set_script", existing); } } - editor_data->get_undo_redo().add_do_method(this, "_update_script_button"); - editor_data->get_undo_redo().add_undo_method(this, "_update_script_button"); + editor_data->get_undo_redo()->add_do_method(this, "_update_script_button"); + editor_data->get_undo_redo()->add_undo_method(this, "_update_script_button"); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); } break; case TOOL_MOVE_UP: case TOOL_MOVE_DOWN: { @@ -602,10 +601,10 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { } if (selection.size() == 1) { - editor_data->get_undo_redo().create_action(TTR("Move Node In Parent")); + editor_data->get_undo_redo()->create_action(TTR("Move Node In Parent")); } if (selection.size() > 1) { - editor_data->get_undo_redo().create_action(TTR("Move Nodes In Parent")); + editor_data->get_undo_redo()->create_action(TTR("Move Nodes In Parent")); } for (int i = 0; i < selection.size(); i++) { @@ -618,11 +617,11 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { int bottom_node_pos = bottom_node->get_index(); int top_node_pos_next = top_node->get_index() + (MOVING_DOWN ? 1 : -1); - editor_data->get_undo_redo().add_do_method(top_node->get_parent(), "move_child", top_node, top_node_pos_next); - editor_data->get_undo_redo().add_undo_method(bottom_node->get_parent(), "move_child", bottom_node, bottom_node_pos); + editor_data->get_undo_redo()->add_do_method(top_node->get_parent(), "move_child", top_node, top_node_pos_next); + editor_data->get_undo_redo()->add_undo_method(bottom_node->get_parent(), "move_child", bottom_node, bottom_node_pos); } - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); } break; case TOOL_DUPLICATE: { @@ -650,8 +649,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { break; } - editor_data->get_undo_redo().create_action(TTR("Duplicate Node(s)")); - editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); + editor_data->get_undo_redo()->create_action(TTR("Duplicate Node(s)"), UndoRedo::MERGE_DISABLE, selection.front()->get()); + editor_data->get_undo_redo()->add_do_method(editor_selection, "clear"); Node *dupsingle = nullptr; @@ -676,28 +675,28 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { dup->set_name(parent->validate_child_name(dup)); - editor_data->get_undo_redo().add_do_method(add_below_node, "add_sibling", dup, true); + editor_data->get_undo_redo()->add_do_method(add_below_node, "add_sibling", dup, true); for (Node *F : owned) { if (!duplimap.has(F)) { continue; } Node *d = duplimap[F]; - editor_data->get_undo_redo().add_do_method(d, "set_owner", node->get_owner()); + editor_data->get_undo_redo()->add_do_method(d, "set_owner", node->get_owner()); } - editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", dup); - editor_data->get_undo_redo().add_undo_method(parent, "remove_child", dup); - editor_data->get_undo_redo().add_do_reference(dup); + editor_data->get_undo_redo()->add_do_method(editor_selection, "add_node", dup); + editor_data->get_undo_redo()->add_undo_method(parent, "remove_child", dup); + editor_data->get_undo_redo()->add_do_reference(dup); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(ed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name()); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name()))); + editor_data->get_undo_redo()->add_do_method(ed, "live_debug_duplicate_node", edited_scene->get_path_to(node), dup->get_name()); + editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(parent)).plus_file(dup->get_name()))); add_below_node = dup; } - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); if (dupsingle) { _push_item(dupsingle); @@ -766,29 +765,29 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { return; } - editor_data->get_undo_redo().create_action(TTR("Make node as Root")); - editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node); - editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", node); - editor_data->get_undo_redo().add_do_method(node, "add_child", root, true); - editor_data->get_undo_redo().add_do_method(node, "set_scene_file_path", root->get_scene_file_path()); - editor_data->get_undo_redo().add_do_method(root, "set_scene_file_path", String()); - editor_data->get_undo_redo().add_do_method(node, "set_owner", (Object *)nullptr); - editor_data->get_undo_redo().add_do_method(root, "set_owner", node); + editor_data->get_undo_redo()->create_action(TTR("Make node as Root")); + editor_data->get_undo_redo()->add_do_method(node->get_parent(), "remove_child", node); + editor_data->get_undo_redo()->add_do_method(EditorNode::get_singleton(), "set_edited_scene", node); + editor_data->get_undo_redo()->add_do_method(node, "add_child", root, true); + editor_data->get_undo_redo()->add_do_method(node, "set_scene_file_path", root->get_scene_file_path()); + editor_data->get_undo_redo()->add_do_method(root, "set_scene_file_path", String()); + editor_data->get_undo_redo()->add_do_method(node, "set_owner", (Object *)nullptr); + editor_data->get_undo_redo()->add_do_method(root, "set_owner", node); _node_replace_owner(root, root, node, MODE_DO); - editor_data->get_undo_redo().add_undo_method(root, "set_scene_file_path", root->get_scene_file_path()); - editor_data->get_undo_redo().add_undo_method(node, "set_scene_file_path", String()); - editor_data->get_undo_redo().add_undo_method(node, "remove_child", root); - editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", root); - editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node, true); - editor_data->get_undo_redo().add_undo_method(node->get_parent(), "move_child", node, node->get_index()); - editor_data->get_undo_redo().add_undo_method(root, "set_owner", (Object *)nullptr); - editor_data->get_undo_redo().add_undo_method(node, "set_owner", root); + editor_data->get_undo_redo()->add_undo_method(root, "set_scene_file_path", root->get_scene_file_path()); + editor_data->get_undo_redo()->add_undo_method(node, "set_scene_file_path", String()); + editor_data->get_undo_redo()->add_undo_method(node, "remove_child", root); + editor_data->get_undo_redo()->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", root); + editor_data->get_undo_redo()->add_undo_method(node->get_parent(), "add_child", node, true); + editor_data->get_undo_redo()->add_undo_method(node->get_parent(), "move_child", node, node->get_index()); + editor_data->get_undo_redo()->add_undo_method(root, "set_owner", (Object *)nullptr); + editor_data->get_undo_redo()->add_undo_method(node, "set_owner", root); _node_replace_owner(root, root, root, MODE_UNDO); - editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); - editor_data->get_undo_redo().add_undo_method(scene_tree, "update_tree"); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->add_do_method(scene_tree, "update_tree"); + editor_data->get_undo_redo()->add_undo_method(scene_tree, "update_tree"); + editor_data->get_undo_redo()->commit_action(); } break; case TOOL_MULTI_EDIT: { if (!profile_allow_editing) { @@ -1009,7 +1008,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { Node *node = e->get(); if (node) { Node *root = EditorNode::get_singleton()->get_edited_scene(); - UndoRedo *undo_redo = &editor_data->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = editor_data->get_undo_redo(); if (!root) { break; } @@ -1072,7 +1071,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { List<Node *> selection = editor_selection->get_selected_node_list(); List<Node *>::Element *e = selection.front(); if (e) { - UndoRedo *undo_redo = &editor_data->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = editor_data->get_undo_redo(); Node *node = e->get(); bool enabled = node->is_unique_name_in_owner(); if (!enabled && get_tree()->get_edited_scene_root()->get_node_or_null(UNIQUE_NODE_PREFIX + String(node->get_name())) != nullptr) { @@ -1161,19 +1160,19 @@ void SceneTreeDock::_property_selected(int p_idx) { } void SceneTreeDock::_perform_property_drop(Node *p_node, String p_property, Ref<Resource> p_res) { - editor_data->get_undo_redo().create_action(vformat(TTR("Set %s"), p_property)); - editor_data->get_undo_redo().add_do_property(p_node, p_property, p_res); - editor_data->get_undo_redo().add_undo_property(p_node, p_property, p_node->get(p_property)); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->create_action(vformat(TTR("Set %s"), p_property)); + editor_data->get_undo_redo()->add_do_property(p_node, p_property, p_res); + editor_data->get_undo_redo()->add_undo_property(p_node, p_property, p_node->get(p_property)); + editor_data->get_undo_redo()->commit_action(); } void SceneTreeDock::add_root_node(Node *p_node) { - editor_data->get_undo_redo().create_action(TTR("New Scene Root")); - editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_node); - editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); - editor_data->get_undo_redo().add_do_reference(p_node); - editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->create_action_for_history(TTR("New Scene Root"), editor_data->get_current_edited_scene_history_id()); + editor_data->get_undo_redo()->add_do_method(EditorNode::get_singleton(), "set_edited_scene", p_node); + editor_data->get_undo_redo()->add_do_method(scene_tree, "update_tree"); + editor_data->get_undo_redo()->add_do_reference(p_node); + editor_data->get_undo_redo()->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); + editor_data->get_undo_redo()->commit_action(); } void SceneTreeDock::_node_collapsed(Object *p_obj) { @@ -1231,7 +1230,7 @@ void SceneTreeDock::_notification(int p_what) { node_shortcuts_toggle->set_name("NodeShortcutsToggle"); node_shortcuts_toggle->set_icon(get_theme_icon(SNAME("Favorites"), SNAME("EditorIcons"))); node_shortcuts_toggle->set_toggle_mode(true); - node_shortcuts_toggle->set_tooltip(TTR("Switch to Favorite Nodes")); + node_shortcuts_toggle->set_tooltip_text(TTR("Switch to Favorite Nodes")); node_shortcuts_toggle->set_pressed(EDITOR_GET("_use_favorites_root_selection")); node_shortcuts_toggle->set_anchors_and_offsets_preset(Control::PRESET_CENTER_RIGHT); node_shortcuts_toggle->connect("pressed", callable_mp(this, &SceneTreeDock::_update_create_root_dialog)); @@ -1335,7 +1334,7 @@ void SceneTreeDock::_notification(int p_what) { void SceneTreeDock::_node_replace_owner(Node *p_base, Node *p_node, Node *p_root, ReplaceOwnerMode p_mode) { if (p_node->get_owner() == p_base && p_node != p_root) { - UndoRedo *undo_redo = &editor_data->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = editor_data->get_undo_redo(); switch (p_mode) { case MODE_BIDI: { bool disable_unique = p_node->is_unique_name_in_owner() && p_root->get_node_or_null(UNIQUE_NODE_PREFIX + String(p_node->get_name())) != nullptr; @@ -1568,8 +1567,8 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath> Variant old_variant = p_base->get(propertyname); Variant updated_variant = old_variant; if (_check_node_path_recursive(p_base, updated_variant, p_renames)) { - editor_data->get_undo_redo().add_do_property(p_base, propertyname, updated_variant); - editor_data->get_undo_redo().add_undo_property(p_base, propertyname, old_variant); + editor_data->get_undo_redo()->add_do_property(p_base, propertyname, updated_variant); + editor_data->get_undo_redo()->add_undo_property(p_base, propertyname, old_variant); p_base->set(propertyname, updated_variant); } } @@ -1627,12 +1626,12 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath> ERR_FAIL_COND(!EI); //another bug } - editor_data->get_undo_redo().add_do_method(anim.ptr(), "remove_track", idx); - editor_data->get_undo_redo().add_undo_method(anim.ptr(), "add_track", anim->track_get_type(i), idx); - editor_data->get_undo_redo().add_undo_method(anim.ptr(), "track_set_path", idx, track_np); - editor_data->get_undo_redo().add_undo_method(anim.ptr(), "track_set_interpolation_type", idx, anim->track_get_interpolation_type(i)); + editor_data->get_undo_redo()->add_do_method(anim.ptr(), "remove_track", idx); + editor_data->get_undo_redo()->add_undo_method(anim.ptr(), "add_track", anim->track_get_type(i), idx); + editor_data->get_undo_redo()->add_undo_method(anim.ptr(), "track_set_path", idx, track_np); + editor_data->get_undo_redo()->add_undo_method(anim.ptr(), "track_set_interpolation_type", idx, anim->track_get_interpolation_type(i)); for (int j = 0; j < anim->track_get_key_count(i); j++) { - editor_data->get_undo_redo().add_undo_method(anim.ptr(), "track_insert_key", idx, anim->track_get_key_time(i, j), anim->track_get_key_value(i, j), anim->track_get_key_transition(i, j)); + editor_data->get_undo_redo()->add_undo_method(anim.ptr(), "track_insert_key", idx, anim->track_get_key_time(i, j), anim->track_get_key_value(i, j), anim->track_get_key_transition(i, j)); } ran.erase(i); //byebye channel @@ -1645,8 +1644,8 @@ void SceneTreeDock::perform_node_renames(Node *p_base, HashMap<Node *, NodePath> if (new_path == track_np) { continue; //bleh } - editor_data->get_undo_redo().add_do_method(anim.ptr(), "track_set_path", i, new_path); - editor_data->get_undo_redo().add_undo_method(anim.ptr(), "track_set_path", i, track_np); + editor_data->get_undo_redo()->add_do_method(anim.ptr(), "track_set_path", i, new_path); + editor_data->get_undo_redo()->add_undo_method(anim.ptr(), "track_set_path", i, track_np); } } } @@ -1780,7 +1779,7 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V // Sort by tree order, so re-adding is easy. p_nodes.sort_custom<Node::Comparator>(); - editor_data->get_undo_redo().create_action(TTR("Reparent Node")); + editor_data->get_undo_redo()->create_action(TTR("Reparent Node")); HashMap<Node *, NodePath> path_renames; Vector<StringName> former_names; @@ -1805,11 +1804,11 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V inc--; // If the child will generate a gap when moved, adjust. } - editor_data->get_undo_redo().add_do_method(node->get_parent(), "remove_child", node); - editor_data->get_undo_redo().add_do_method(new_parent, "add_child", node, true); + editor_data->get_undo_redo()->add_do_method(node->get_parent(), "remove_child", node); + editor_data->get_undo_redo()->add_do_method(new_parent, "add_child", node, true); if (p_position_in_parent >= 0) { - editor_data->get_undo_redo().add_do_method(new_parent, "move_child", node, p_position_in_parent + inc); + editor_data->get_undo_redo()->add_do_method(new_parent, "move_child", node, p_position_in_parent + inc); } EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); @@ -1839,29 +1838,29 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V } } - editor_data->get_undo_redo().add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index()); + editor_data->get_undo_redo()->add_do_method(ed, "live_debug_reparent_node", edited_scene->get_path_to(node), edited_scene->get_path_to(new_parent), new_name, p_position_in_parent + inc); + editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_reparent_node", NodePath(String(edited_scene->get_path_to(new_parent)).plus_file(new_name)), edited_scene->get_path_to(node->get_parent()), node->get_name(), node->get_index()); if (p_keep_global_xform) { if (Object::cast_to<Node2D>(node)) { - editor_data->get_undo_redo().add_do_method(node, "set_global_transform", Object::cast_to<Node2D>(node)->get_global_transform()); + editor_data->get_undo_redo()->add_do_method(node, "set_global_transform", Object::cast_to<Node2D>(node)->get_global_transform()); } if (Object::cast_to<Node3D>(node)) { - editor_data->get_undo_redo().add_do_method(node, "set_global_transform", Object::cast_to<Node3D>(node)->get_global_transform()); + editor_data->get_undo_redo()->add_do_method(node, "set_global_transform", Object::cast_to<Node3D>(node)->get_global_transform()); } if (Object::cast_to<Control>(node)) { - editor_data->get_undo_redo().add_do_method(node, "set_global_position", Object::cast_to<Control>(node)->get_global_position()); + editor_data->get_undo_redo()->add_do_method(node, "set_global_position", Object::cast_to<Control>(node)->get_global_position()); } } - editor_data->get_undo_redo().add_do_method(this, "_set_owners", edited_scene, owners); + editor_data->get_undo_redo()->add_do_method(this, "_set_owners", edited_scene, owners); if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == node) { - editor_data->get_undo_redo().add_do_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", node); + editor_data->get_undo_redo()->add_do_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", node); } - editor_data->get_undo_redo().add_undo_method(new_parent, "remove_child", node); - editor_data->get_undo_redo().add_undo_method(node, "set_name", former_names[ni]); + editor_data->get_undo_redo()->add_undo_method(new_parent, "remove_child", node); + editor_data->get_undo_redo()->add_undo_method(node, "set_name", former_names[ni]); inc++; } @@ -1879,29 +1878,29 @@ void SceneTreeDock::_do_reparent(Node *p_new_parent, int p_position_in_parent, V int child_pos = node->get_index(); - editor_data->get_undo_redo().add_undo_method(node->get_parent(), "add_child", node, true); - editor_data->get_undo_redo().add_undo_method(node->get_parent(), "move_child", node, child_pos); - editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners); + editor_data->get_undo_redo()->add_undo_method(node->get_parent(), "add_child", node, true); + editor_data->get_undo_redo()->add_undo_method(node->get_parent(), "move_child", node, child_pos); + editor_data->get_undo_redo()->add_undo_method(this, "_set_owners", edited_scene, owners); if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == node) { - editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", node); + editor_data->get_undo_redo()->add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", node); } if (p_keep_global_xform) { if (Object::cast_to<Node2D>(node)) { - editor_data->get_undo_redo().add_undo_method(node, "set_transform", Object::cast_to<Node2D>(node)->get_transform()); + editor_data->get_undo_redo()->add_undo_method(node, "set_transform", Object::cast_to<Node2D>(node)->get_transform()); } if (Object::cast_to<Node3D>(node)) { - editor_data->get_undo_redo().add_undo_method(node, "set_transform", Object::cast_to<Node3D>(node)->get_transform()); + editor_data->get_undo_redo()->add_undo_method(node, "set_transform", Object::cast_to<Node3D>(node)->get_transform()); } if (Object::cast_to<Control>(node)) { - editor_data->get_undo_redo().add_undo_method(node, "set_position", Object::cast_to<Control>(node)->get_position()); + editor_data->get_undo_redo()->add_undo_method(node, "set_position", Object::cast_to<Control>(node)->get_position()); } } } perform_node_renames(nullptr, &path_renames); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); } bool SceneTreeDock::_is_collapsed_recursive(TreeItem *p_item) const { @@ -1957,30 +1956,30 @@ void SceneTreeDock::_script_created(Ref<Script> p_script) { Node *node = selected.front()->get(); Ref<Script> existing = node->get_script(); - editor_data->get_undo_redo().create_action(TTR("Attach Script")); - editor_data->get_undo_redo().add_do_method(InspectorDock::get_singleton(), "store_script_properties", node); - editor_data->get_undo_redo().add_undo_method(InspectorDock::get_singleton(), "store_script_properties", node); - editor_data->get_undo_redo().add_do_method(node, "set_script", p_script); - editor_data->get_undo_redo().add_undo_method(node, "set_script", existing); - editor_data->get_undo_redo().add_do_method(InspectorDock::get_singleton(), "apply_script_properties", node); - editor_data->get_undo_redo().add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", node); - editor_data->get_undo_redo().add_do_method(this, "_update_script_button"); - editor_data->get_undo_redo().add_undo_method(this, "_update_script_button"); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->create_action(TTR("Attach Script")); + editor_data->get_undo_redo()->add_do_method(InspectorDock::get_singleton(), "store_script_properties", node); + editor_data->get_undo_redo()->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", node); + editor_data->get_undo_redo()->add_do_method(node, "set_script", p_script); + editor_data->get_undo_redo()->add_undo_method(node, "set_script", existing); + editor_data->get_undo_redo()->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", node); + editor_data->get_undo_redo()->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", node); + editor_data->get_undo_redo()->add_do_method(this, "_update_script_button"); + editor_data->get_undo_redo()->add_undo_method(this, "_update_script_button"); + editor_data->get_undo_redo()->commit_action(); } else { - editor_data->get_undo_redo().create_action(TTR("Attach Script")); - for (List<Node *>::Element *E = selected.front(); E; E = E->next()) { - Ref<Script> existing = E->get()->get_script(); - editor_data->get_undo_redo().add_do_method(InspectorDock::get_singleton(), "store_script_properties", E->get()); - editor_data->get_undo_redo().add_undo_method(InspectorDock::get_singleton(), "store_script_properties", E->get()); - editor_data->get_undo_redo().add_do_method(E->get(), "set_script", p_script); - editor_data->get_undo_redo().add_undo_method(E->get(), "set_script", existing); - editor_data->get_undo_redo().add_do_method(InspectorDock::get_singleton(), "apply_script_properties", E->get()); - editor_data->get_undo_redo().add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", E->get()); - editor_data->get_undo_redo().add_do_method(this, "_update_script_button"); - editor_data->get_undo_redo().add_undo_method(this, "_update_script_button"); + editor_data->get_undo_redo()->create_action(TTR("Attach Script")); + for (Node *E : selected) { + Ref<Script> existing = E->get_script(); + editor_data->get_undo_redo()->add_do_method(InspectorDock::get_singleton(), "store_script_properties", E); + editor_data->get_undo_redo()->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", E); + editor_data->get_undo_redo()->add_do_method(E, "set_script", p_script); + editor_data->get_undo_redo()->add_undo_method(E, "set_script", existing); + editor_data->get_undo_redo()->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", E); + editor_data->get_undo_redo()->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", E); + editor_data->get_undo_redo()->add_do_method(this, "_update_script_button"); + editor_data->get_undo_redo()->add_undo_method(this, "_update_script_button"); } - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); } _push_item(p_script.operator->()); @@ -1994,10 +1993,10 @@ void SceneTreeDock::_shader_created(Ref<Shader> p_shader) { Ref<Shader> existing = selected_shader_material->get_shader(); - editor_data->get_undo_redo().create_action(TTR("Set Shader")); - editor_data->get_undo_redo().add_do_method(selected_shader_material.ptr(), "set_shader", p_shader); - editor_data->get_undo_redo().add_undo_method(selected_shader_material.ptr(), "set_shader", existing); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->create_action(TTR("Set Shader")); + editor_data->get_undo_redo()->add_do_method(selected_shader_material.ptr(), "set_shader", p_shader); + editor_data->get_undo_redo()->add_undo_method(selected_shader_material.ptr(), "set_shader", existing); + editor_data->get_undo_redo()->commit_action(); } void SceneTreeDock::_script_creation_closed() { @@ -2063,9 +2062,9 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { EditorNode::get_singleton()->get_editor_plugins_over()->make_visible(false); if (p_cut) { - editor_data->get_undo_redo().create_action(TTR("Cut Node(s)")); + editor_data->get_undo_redo()->create_action(TTR("Cut Node(s)")); } else { - editor_data->get_undo_redo().create_action(TTR("Remove Node(s)")); + editor_data->get_undo_redo()->create_action(TTR("Remove Node(s)")); } bool entire_scene = false; @@ -2077,11 +2076,11 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { } if (entire_scene) { - editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); - editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", edited_scene); - editor_data->get_undo_redo().add_undo_method(edited_scene, "set_owner", edited_scene->get_owner()); - editor_data->get_undo_redo().add_undo_method(scene_tree, "update_tree"); - editor_data->get_undo_redo().add_undo_reference(edited_scene); + editor_data->get_undo_redo()->add_do_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); + editor_data->get_undo_redo()->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", edited_scene); + editor_data->get_undo_redo()->add_undo_method(edited_scene, "set_owner", edited_scene->get_owner()); + editor_data->get_undo_redo()->add_undo_method(scene_tree, "update_tree"); + editor_data->get_undo_redo()->add_undo_reference(edited_scene); } else { remove_list.sort_custom<Node::Comparator>(); //sort nodes to keep positions @@ -2110,21 +2109,21 @@ void SceneTreeDock::_delete_confirm(bool p_cut) { owners.push_back(F); } - editor_data->get_undo_redo().add_do_method(n->get_parent(), "remove_child", n); - editor_data->get_undo_redo().add_undo_method(n->get_parent(), "add_child", n, true); - editor_data->get_undo_redo().add_undo_method(n->get_parent(), "move_child", n, n->get_index()); + editor_data->get_undo_redo()->add_do_method(n->get_parent(), "remove_child", n); + editor_data->get_undo_redo()->add_undo_method(n->get_parent(), "add_child", n, true); + editor_data->get_undo_redo()->add_undo_method(n->get_parent(), "move_child", n, n->get_index()); if (AnimationPlayerEditor::get_singleton()->get_track_editor()->get_root() == n) { - editor_data->get_undo_redo().add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", n); + editor_data->get_undo_redo()->add_undo_method(AnimationPlayerEditor::get_singleton()->get_track_editor(), "set_root", n); } - editor_data->get_undo_redo().add_undo_method(this, "_set_owners", edited_scene, owners); - editor_data->get_undo_redo().add_undo_reference(n); + editor_data->get_undo_redo()->add_undo_method(this, "_set_owners", edited_scene, owners); + editor_data->get_undo_redo()->add_undo_reference(n); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(ed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id()); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index()); + editor_data->get_undo_redo()->add_do_method(ed, "live_debug_remove_and_keep_node", edited_scene->get_path_to(n), n->get_instance_id()); + editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_restore_node", n->get_instance_id(), edited_scene->get_path_to(n->get_parent()), n->get_index()); } } - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); // hack, force 2d editor viewport to refresh after deletion if (CanvasItemEditor *editor = CanvasItemEditor::get_singleton()) { @@ -2190,29 +2189,29 @@ void SceneTreeDock::_do_create(Node *p_parent) { Node *child = Object::cast_to<Node>(c); ERR_FAIL_COND(!child); - editor_data->get_undo_redo().create_action(TTR("Create Node")); + editor_data->get_undo_redo()->create_action(TTR("Create Node")); if (edited_scene) { - editor_data->get_undo_redo().add_do_method(p_parent, "add_child", child, true); - editor_data->get_undo_redo().add_do_method(child, "set_owner", edited_scene); - editor_data->get_undo_redo().add_do_method(editor_selection, "clear"); - editor_data->get_undo_redo().add_do_method(editor_selection, "add_node", child); - editor_data->get_undo_redo().add_do_reference(child); - editor_data->get_undo_redo().add_undo_method(p_parent, "remove_child", child); + editor_data->get_undo_redo()->add_do_method(p_parent, "add_child", child, true); + editor_data->get_undo_redo()->add_do_method(child, "set_owner", edited_scene); + editor_data->get_undo_redo()->add_do_method(editor_selection, "clear"); + editor_data->get_undo_redo()->add_do_method(editor_selection, "add_node", child); + editor_data->get_undo_redo()->add_do_reference(child); + editor_data->get_undo_redo()->add_undo_method(p_parent, "remove_child", child); String new_name = p_parent->validate_child_name(child); EditorDebuggerNode *ed = EditorDebuggerNode::get_singleton(); - editor_data->get_undo_redo().add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name); - editor_data->get_undo_redo().add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name))); + editor_data->get_undo_redo()->add_do_method(ed, "live_debug_create_node", edited_scene->get_path_to(p_parent), child->get_class(), new_name); + editor_data->get_undo_redo()->add_undo_method(ed, "live_debug_remove_node", NodePath(String(edited_scene->get_path_to(p_parent)).plus_file(new_name))); } else { - editor_data->get_undo_redo().add_do_method(EditorNode::get_singleton(), "set_edited_scene", child); - editor_data->get_undo_redo().add_do_method(scene_tree, "update_tree"); - editor_data->get_undo_redo().add_do_reference(child); - editor_data->get_undo_redo().add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); + editor_data->get_undo_redo()->add_do_method(EditorNode::get_singleton(), "set_edited_scene", child); + editor_data->get_undo_redo()->add_do_method(scene_tree, "update_tree"); + editor_data->get_undo_redo()->add_do_reference(child); + editor_data->get_undo_redo()->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); } - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->commit_action(); _push_item(c); editor_selection->clear(); editor_selection->add_node(child); @@ -2259,8 +2258,8 @@ void SceneTreeDock::_create() { List<Node *> selection = editor_selection->get_selected_node_list(); ERR_FAIL_COND(selection.size() <= 0); - UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action(TTR("Change type of node(s)")); + Ref<EditorUndoRedoManager> &ur = EditorNode::get_undo_redo(); + ur->create_action(TTR("Change type of node(s)"), UndoRedo::MERGE_DISABLE, selection.front()->get()); for (Node *n : selection) { ERR_FAIL_COND(!n); @@ -2393,7 +2392,7 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop } //p_remove_old was added to support undo if (p_remove_old) { - editor_data->get_undo_redo().clear_history(); + editor_data->get_undo_redo()->clear_history(); } newnode->set_name(newname); @@ -2607,16 +2606,16 @@ void SceneTreeDock::_script_dropped(String p_file, NodePath p_to) { Ref<Script> scr = ResourceLoader::load(p_file); ERR_FAIL_COND(!scr.is_valid()); if (Node *n = get_node(p_to)) { - editor_data->get_undo_redo().create_action(TTR("Attach Script")); - editor_data->get_undo_redo().add_do_method(InspectorDock::get_singleton(), "store_script_properties", n); - editor_data->get_undo_redo().add_undo_method(InspectorDock::get_singleton(), "store_script_properties", n); - editor_data->get_undo_redo().add_do_method(n, "set_script", scr); - editor_data->get_undo_redo().add_undo_method(n, "set_script", n->get_script()); - editor_data->get_undo_redo().add_do_method(InspectorDock::get_singleton(), "apply_script_properties", n); - editor_data->get_undo_redo().add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", n); - editor_data->get_undo_redo().add_do_method(this, "_update_script_button"); - editor_data->get_undo_redo().add_undo_method(this, "_update_script_button"); - editor_data->get_undo_redo().commit_action(); + editor_data->get_undo_redo()->create_action(TTR("Attach Script")); + editor_data->get_undo_redo()->add_do_method(InspectorDock::get_singleton(), "store_script_properties", n); + editor_data->get_undo_redo()->add_undo_method(InspectorDock::get_singleton(), "store_script_properties", n); + editor_data->get_undo_redo()->add_do_method(n, "set_script", scr); + editor_data->get_undo_redo()->add_undo_method(n, "set_script", n->get_script()); + editor_data->get_undo_redo()->add_do_method(InspectorDock::get_singleton(), "apply_script_properties", n); + editor_data->get_undo_redo()->add_undo_method(InspectorDock::get_singleton(), "apply_script_properties", n); + editor_data->get_undo_redo()->add_do_method(this, "_update_script_button"); + editor_data->get_undo_redo()->add_undo_method(this, "_update_script_button"); + editor_data->get_undo_redo()->commit_action(); } } @@ -2678,7 +2677,7 @@ void SceneTreeDock::_add_children_to_popup(Object *p_obj, int p_depth) { } int index = menu_subresources->get_item_count(); menu_subresources->add_icon_item(icon, E.name.capitalize(), EDIT_SUBRESOURCE_BASE + subresources.size()); - menu_subresources->set_item_horizontal_offset(index, p_depth * 10 * EDSCALE); + menu_subresources->set_item_indent(index, p_depth); subresources.push_back(obj->get_instance_id()); _add_children_to_popup(obj, p_depth + 1); @@ -3064,9 +3063,9 @@ List<Node *> SceneTreeDock::paste_nodes() { owner = paste_parent; } - UndoRedo &ur = editor_data->get_undo_redo(); - ur.create_action(TTR("Paste Node(s)")); - ur.add_do_method(editor_selection, "clear"); + Ref<EditorUndoRedoManager> &ur = editor_data->get_undo_redo(); + ur->create_action(TTR("Paste Node(s)"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); + ur->add_do_method(editor_selection, "clear"); HashMap<Ref<Resource>, Ref<Resource>> resource_remap; String target_scene; @@ -3095,36 +3094,36 @@ List<Node *> SceneTreeDock::paste_nodes() { if (!paste_parent) { paste_parent = dup; owner = dup; - ur.add_do_method(EditorNode::get_singleton(), "set_edited_scene", dup); + ur->add_do_method(EditorNode::get_singleton(), "set_edited_scene", dup); } else { - ur.add_do_method(paste_parent, "add_child", dup, true); + ur->add_do_method(paste_parent, "add_child", dup, true); } for (KeyValue<const Node *, Node *> &E2 : duplimap) { Node *d = E2.value; if (d != dup) { - ur.add_do_method(d, "set_owner", owner); + ur->add_do_method(d, "set_owner", owner); } } if (dup != owner) { - ur.add_do_method(dup, "set_owner", owner); + ur->add_do_method(dup, "set_owner", owner); } - ur.add_do_method(editor_selection, "add_node", dup); + ur->add_do_method(editor_selection, "add_node", dup); if (dup == paste_parent) { - ur.add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); + ur->add_undo_method(EditorNode::get_singleton(), "set_edited_scene", (Object *)nullptr); } else { - ur.add_undo_method(paste_parent, "remove_child", dup); + ur->add_undo_method(paste_parent, "remove_child", dup); } - ur.add_do_reference(dup); + ur->add_do_reference(dup); if (node_clipboard.size() == 1) { - ur.add_do_method(EditorNode::get_singleton(), "push_item", dup); + ur->add_do_method(EditorNode::get_singleton(), "push_item", dup); } } - ur.commit_action(); + ur->commit_action(); return pasted_nodes; } @@ -3398,14 +3397,14 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec button_add = memnew(Button); button_add->set_flat(true); button_add->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_NEW, false)); - button_add->set_tooltip(TTR("Add/Create a New Node.")); + button_add->set_tooltip_text(TTR("Add/Create a New Node.")); button_add->set_shortcut(ED_GET_SHORTCUT("scene_tree/add_child_node")); filter_hbc->add_child(button_add); button_instance = memnew(Button); button_instance->set_flat(true); button_instance->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_INSTANTIATE, false)); - button_instance->set_tooltip(TTR("Instantiate a scene file as a Node. Creates an inherited scene if no root node exists.")); + button_instance->set_tooltip_text(TTR("Instantiate a scene file as a Node. Creates an inherited scene if no root node exists.")); button_instance->set_shortcut(ED_GET_SHORTCUT("scene_tree/instance_scene")); filter_hbc->add_child(button_instance); @@ -3420,7 +3419,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec button_create_script = memnew(Button); button_create_script->set_flat(true); button_create_script->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_ATTACH_SCRIPT, false)); - button_create_script->set_tooltip(TTR("Attach a new or existing script to the selected node.")); + button_create_script->set_tooltip_text(TTR("Attach a new or existing script to the selected node.")); button_create_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/attach_script")); filter_hbc->add_child(button_create_script); button_create_script->hide(); @@ -3428,7 +3427,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec button_detach_script = memnew(Button); button_detach_script->set_flat(true); button_detach_script->connect("pressed", callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_DETACH_SCRIPT, false)); - button_detach_script->set_tooltip(TTR("Detach the script from the selected node.")); + button_detach_script->set_tooltip_text(TTR("Detach the script from the selected node.")); button_detach_script->set_shortcut(ED_GET_SHORTCUT("scene_tree/detach_script")); filter_hbc->add_child(button_detach_script); button_detach_script->hide(); @@ -3451,7 +3450,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec edit_remote->set_h_size_flags(SIZE_EXPAND_FILL); edit_remote->set_text(TTR("Remote")); edit_remote->set_toggle_mode(true); - edit_remote->set_tooltip(TTR("If selected, the Remote scene tree dock will cause the project to stutter every time it updates.\nSwitch back to the Local scene tree dock to improve performance.")); + edit_remote->set_tooltip_text(TTR("If selected, the Remote scene tree dock will cause the project to stutter every time it updates.\nSwitch back to the Local scene tree dock to improve performance.")); edit_remote->connect("pressed", callable_mp(this, &SceneTreeDock::_remote_tree_selected)); edit_local = memnew(Button); @@ -3492,7 +3491,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec editor_selection->connect("selection_changed", callable_mp(this, &SceneTreeDock::_selection_changed)); - scene_tree->set_undo_redo(&editor_data->get_undo_redo()); + scene_tree->set_undo_redo(editor_data->get_undo_redo()); scene_tree->set_editor_selection(editor_selection); create_dialog = memnew(CreateDialog); @@ -3502,7 +3501,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec create_dialog->connect("favorites_updated", callable_mp(this, &SceneTreeDock::_update_create_root_dialog)); #ifdef MODULE_REGEX_ENABLED - rename_dialog = memnew(RenameDialog(scene_tree, &editor_data->get_undo_redo())); + rename_dialog = memnew(RenameDialog(scene_tree, editor_data->get_undo_redo())); add_child(rename_dialog); #endif // MODULE_REGEX_ENABLED diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp index ad83db9b60..7b0c68fb6b 100644 --- a/editor/scene_tree_editor.cpp +++ b/editor/scene_tree_editor.cpp @@ -35,6 +35,7 @@ #include "editor/editor_file_system.h" #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "editor/editor_undo_redo_manager.h" #include "editor/node_dock.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" @@ -278,7 +279,19 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { warning_icon = SNAME("NodeWarnings4Plus"); } - item->add_button(0, get_theme_icon(warning_icon, SNAME("EditorIcons")), BUTTON_WARNING, false, TTR("Node configuration warning:") + "\n" + warning); + // Improve looks on tooltip, extra spacing on non-bullet point newlines. + const String bullet_point = String::utf8("• "); + int next_newline = 0; + while (next_newline != -1) { + next_newline = warning.find("\n", next_newline + 2); + if (warning.substr(next_newline + 1, bullet_point.length()) != bullet_point) { + warning = warning.insert(next_newline + 1, " "); + } + } + + String newline = (num_warnings == 1 ? "\n" : "\n\n"); + + item->add_button(0, get_theme_icon(warning_icon, SNAME("EditorIcons")), BUTTON_WARNING, false, TTR("Node configuration warning:") + newline + warning); } if (p_node->is_unique_name_in_owner()) { @@ -365,7 +378,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) { item->set_tooltip(0, tooltip); } - if (can_open_instance && undo_redo) { //Show buttons only when necessary(SceneTreeDock) to avoid crashes + if (can_open_instance && undo_redo.is_valid()) { //Show buttons only when necessary(SceneTreeDock) to avoid crashes if (!p_node->is_connected("script_changed", callable_mp(this, &SceneTreeEditor::_node_script_changed))) { p_node->connect("script_changed", callable_mp(this, &SceneTreeEditor::_node_script_changed).bind(p_node)); @@ -602,7 +615,9 @@ bool SceneTreeEditor::_update_filter(TreeItem *p_parent, bool p_scroll_to_select } if (!keep) { - keep = filter.is_subsequence_ofn(p_parent->get_text(0)); + StringName node_type = get_node(p_parent->get_metadata(0))->get_class(); + bool is_kept_by_type = (filter.begins_with("type:") && filter.trim_prefix("type:").is_subsequence_ofn(node_type)) || (filter.begins_with("t:") && filter.trim_prefix("t:").is_subsequence_ofn(node_type)); + keep = (filter.is_subsequence_ofn(p_parent->get_text(0)) || is_kept_by_type); } p_parent->set_visible(keep); @@ -883,7 +898,7 @@ void SceneTreeEditor::_renamed() { return; } - if (!undo_redo) { + if (!undo_redo.is_valid()) { n->set_name(new_name); which->set_metadata(0, n->get_path()); emit_signal(SNAME("node_renamed")); @@ -927,6 +942,10 @@ String SceneTreeEditor::get_filter() const { return filter; } +void SceneTreeEditor::set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo) { + undo_redo = p_undo_redo; +} + void SceneTreeEditor::set_display_foreign_nodes(bool p_display) { display_foreign = p_display; _update_tree(); @@ -1258,7 +1277,6 @@ void SceneTreeEditor::_bind_methods() { } SceneTreeEditor::SceneTreeEditor(bool p_label, bool p_can_rename, bool p_can_open_instance) { - undo_redo = nullptr; selected = nullptr; can_rename = p_can_rename; @@ -1327,10 +1345,6 @@ void SceneTreeDialog::popup_scenetree_dialog() { popup_centered_clamped(Size2(350, 700) * EDSCALE); } -void SceneTreeDialog::_update_theme() { - filter->set_right_icon(tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); -} - void SceneTreeDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_VISIBILITY_CHANGED: { @@ -1341,11 +1355,10 @@ void SceneTreeDialog::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { connect("confirmed", callable_mp(this, &SceneTreeDialog::_select)); - _update_theme(); } break; case NOTIFICATION_THEME_CHANGED: { - _update_theme(); + filter->set_right_icon(tree->get_theme_icon(SNAME("Search"), SNAME("EditorIcons"))); } break; case NOTIFICATION_EXIT_TREE: { diff --git a/editor/scene_tree_editor.h b/editor/scene_tree_editor.h index 31772e55b5..f3ca0347ea 100644 --- a/editor/scene_tree_editor.h +++ b/editor/scene_tree_editor.h @@ -31,13 +31,14 @@ #ifndef SCENE_TREE_EDITOR_H #define SCENE_TREE_EDITOR_H -#include "core/object/undo_redo.h" #include "editor/editor_data.h" #include "editor/editor_settings.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" #include "scene/gui/tree.h" +class EditorUndoRedoManager; + class SceneTreeEditor : public Control { GDCLASS(SceneTreeEditor, Control); @@ -98,7 +99,7 @@ class SceneTreeEditor : public Control { bool show_enabled_subscene = false; void _renamed(); - UndoRedo *undo_redo = nullptr; + Ref<EditorUndoRedoManager> undo_redo; HashSet<Node *> marked; bool marked_selectable = false; @@ -139,7 +140,7 @@ public: void set_filter(const String &p_filter); String get_filter() const; - void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }; + void set_undo_redo(Ref<EditorUndoRedoManager> p_undo_redo); void set_display_foreign_nodes(bool p_display); void set_marked(const HashSet<Node *> &p_marked, bool p_selectable = false, bool p_children_selectable = true); @@ -178,7 +179,6 @@ class SceneTreeDialog : public ConfirmationDialog { void _cancel(); void _selected_changed(); void _filter_changed(const String &p_filter); - void _update_theme(); protected: void _notification(int p_what); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 77e0321f83..28790f2711 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -120,9 +120,8 @@ void ScriptCreateDialog::_notification(int p_what) { } else { language_menu->select(default_language); } + } break; - [[fallthrough]]; - } case NOTIFICATION_THEME_CHANGED: { for (int i = 0; i < ScriptServer::get_language_count(); i++) { Ref<Texture2D> language_icon = get_theme_icon(ScriptServer::get_language(i)->get_type(), SNAME("EditorIcons")); @@ -166,6 +165,7 @@ void ScriptCreateDialog::config(const String &p_base_name, const String &p_base_ class_name->deselect(); parent_name->set_text(p_base_name); parent_name->deselect(); + internal_name->set_text(""); if (!p_base_path.is_empty()) { initial_bp = p_base_path.get_basename(); diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp index bd1f2529ca..fc7fff325c 100644 --- a/editor/shader_create_dialog.cpp +++ b/editor/shader_create_dialog.cpp @@ -37,11 +37,16 @@ #include "scene/resources/visual_shader.h" #include "servers/rendering/shader_types.h" +enum ShaderType { + SHADER_TYPE_TEXT, + SHADER_TYPE_VISUAL, + SHADER_TYPE_INC, + SHADER_TYPE_MAX, +}; + void ShaderCreateDialog::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - _update_theme(); - String last_lang = EditorSettings::get_singleton()->get_project_metadata("shader_setup", "last_selected_language", ""); if (!last_lang.is_empty()) { for (int i = 0; i < type_menu->get_item_count(); i++) { @@ -60,29 +65,25 @@ void ShaderCreateDialog::_notification(int p_what) { } break; case NOTIFICATION_THEME_CHANGED: { - _update_theme(); - } break; - } -} + Ref<Texture2D> shader_icon = gc->get_theme_icon(SNAME("Shader"), SNAME("EditorIcons")); + if (shader_icon.is_valid()) { + type_menu->set_item_icon(0, shader_icon); + } -void ShaderCreateDialog::_update_theme() { - Ref<Texture2D> shader_icon = gc->get_theme_icon(SNAME("Shader"), SNAME("EditorIcons")); - if (shader_icon.is_valid()) { - type_menu->set_item_icon(0, shader_icon); - } + Ref<Texture2D> visual_shader_icon = gc->get_theme_icon(SNAME("VisualShader"), SNAME("EditorIcons")); + if (visual_shader_icon.is_valid()) { + type_menu->set_item_icon(1, visual_shader_icon); + } - Ref<Texture2D> visual_shader_icon = gc->get_theme_icon(SNAME("VisualShader"), SNAME("EditorIcons")); - if (visual_shader_icon.is_valid()) { - type_menu->set_item_icon(1, visual_shader_icon); - } + Ref<Texture2D> include_icon = gc->get_theme_icon(SNAME("TextFile"), SNAME("EditorIcons")); + if (include_icon.is_valid()) { + type_menu->set_item_icon(2, include_icon); + } - Ref<Texture2D> include_icon = gc->get_theme_icon(SNAME("TextFile"), SNAME("EditorIcons")); - if (include_icon.is_valid()) { - type_menu->set_item_icon(2, include_icon); + path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); + status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); + } break; } - - path_button->set_icon(get_theme_icon(SNAME("Folder"), SNAME("EditorIcons"))); - status_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("bg"), SNAME("Tree"))); } void ShaderCreateDialog::_update_language_info() { diff --git a/editor/shader_create_dialog.h b/editor/shader_create_dialog.h index bf031c3601..9496d0ce9e 100644 --- a/editor/shader_create_dialog.h +++ b/editor/shader_create_dialog.h @@ -44,13 +44,6 @@ class EditorFileDialog; class ShaderCreateDialog : public ConfirmationDialog { GDCLASS(ShaderCreateDialog, ConfirmationDialog); - enum ShaderType { - SHADER_TYPE_TEXT, - SHADER_TYPE_VISUAL, - SHADER_TYPE_INC, - SHADER_TYPE_MAX, - }; - struct ShaderTypeData { List<String> extensions; String default_extension; @@ -105,7 +98,6 @@ class ShaderCreateDialog : public ConfirmationDialog { void _update_dialog(); protected: - void _update_theme(); void _notification(int p_what); static void _bind_methods(); diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index 1cd1b4ea00..9058596830 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "editor/editor_node.h" +#include "editor/editor_undo_redo_manager.h" #include "servers/rendering/shader_language.h" static const char *global_var_type_names[RS::GLOBAL_VAR_TYPE_MAX] = { @@ -85,7 +86,7 @@ protected: return false; } - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_undo_redo(); undo_redo->create_action(TTR("Set Shader Global Variable")); undo_redo->add_do_method(RS::get_singleton(), "global_shader_uniform_set", p_name, p_value); @@ -393,7 +394,7 @@ void ShaderGlobalsEditor::_variable_added() { return; } - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); Variant value = create_var(RS::GlobalShaderUniformType(variable_type->get_selected())); @@ -412,7 +413,7 @@ void ShaderGlobalsEditor::_variable_added() { } void ShaderGlobalsEditor::_variable_deleted(const String &p_variable) { - UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); + Ref<EditorUndoRedoManager> undo_redo = EditorNode::get_singleton()->get_undo_redo(); undo_redo->create_action(TTR("Add Shader Global Uniform")); undo_redo->add_do_method(RS::get_singleton(), "global_shader_uniform_remove", p_variable); diff --git a/editor/shader_globals_editor.h b/editor/shader_globals_editor.h index d29052eaee..1e2f1dd828 100644 --- a/editor/shader_globals_editor.h +++ b/editor/shader_globals_editor.h @@ -31,7 +31,6 @@ #ifndef SHADER_GLOBALS_EDITOR_H #define SHADER_GLOBALS_EDITOR_H -#include "core/object/undo_redo.h" #include "editor/editor_autoload_settings.h" #include "editor/editor_data.h" #include "editor/editor_plugin_settings.h" |