summaryrefslogtreecommitdiff
path: root/editor
diff options
context:
space:
mode:
Diffstat (limited to 'editor')
-rw-r--r--editor/animation_bezier_editor.cpp337
-rw-r--r--editor/animation_bezier_editor.h3
-rw-r--r--editor/animation_track_editor.cpp327
-rw-r--r--editor/animation_track_editor.h10
-rw-r--r--editor/code_editor.cpp3
-rw-r--r--editor/doc_tools.cpp515
-rw-r--r--editor/editor_help.cpp17
-rw-r--r--editor/editor_inspector.cpp22
-rw-r--r--editor/editor_log.cpp16
-rw-r--r--editor/editor_log.h10
-rw-r--r--editor/editor_node.cpp300
-rw-r--r--editor/editor_node.h13
-rw-r--r--editor/editor_path.cpp2
-rw-r--r--editor/editor_plugin.cpp6
-rw-r--r--editor/editor_plugin.h2
-rw-r--r--editor/editor_properties.cpp15
-rw-r--r--editor/editor_properties.h3
-rw-r--r--editor/editor_sectioned_inspector.cpp4
-rw-r--r--editor/editor_settings.cpp20
-rw-r--r--editor/editor_settings.h4
-rw-r--r--editor/editor_themes.cpp36
-rw-r--r--editor/export/editor_export_platform.cpp54
-rw-r--r--editor/export/editor_export_platform.h11
-rw-r--r--editor/export/editor_export_platform_pc.cpp6
-rw-r--r--editor/export/editor_export_platform_pc.h3
-rw-r--r--editor/export/project_export.cpp3
-rw-r--r--editor/icons/ContainerLayout.svg1
-rw-r--r--editor/icons/ControlLayout.svg2
-rw-r--r--editor/icons/DefaultProjectIcon.svg2
-rw-r--r--editor/icons/MenuBar.svg1
-rw-r--r--editor/import/resource_importer_layered_texture.cpp7
-rw-r--r--editor/plugins/animation_library_editor.cpp137
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp109
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp41
-rw-r--r--editor/plugins/control_editor_plugin.cpp848
-rw-r--r--editor/plugins/control_editor_plugin.h168
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp73
-rw-r--r--editor/plugins/debugger_editor_plugin.h4
-rw-r--r--editor/plugins/font_config_plugin.cpp14
-rw-r--r--editor/plugins/font_config_plugin.h6
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp35
-rw-r--r--editor/plugins/shader_editor_plugin.cpp133
-rw-r--r--editor/plugins/shader_editor_plugin.h21
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp5
-rw-r--r--editor/plugins/texture_editor_plugin.cpp2
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp20
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h10
-rw-r--r--editor/project_converter_3_to_4.cpp115
-rw-r--r--editor/project_manager.cpp6
-rw-r--r--editor/scene_tree_dock.cpp2
-rw-r--r--editor/scene_tree_editor.cpp9
-rw-r--r--editor/shader_create_dialog.cpp7
-rw-r--r--editor/shader_create_dialog.h7
53 files changed, 2087 insertions, 1440 deletions
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index ab9afda803..e10ed7e976 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -328,6 +328,8 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
}
}
+ Color dc = get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"));
+
Ref<Texture2D> remove = get_theme_icon(SNAME("Remove"), SNAME("EditorIcons"));
float remove_hpos = limit - hsep - remove->get_width();
@@ -402,7 +404,11 @@ void AnimationBezierTrackEdit::_notification(int p_what) {
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());
- draw_texture(remove, remove_rect.position);
+ 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());
if (locked_tracks.has(current_track)) {
@@ -632,8 +638,9 @@ Ref<Animation> AnimationBezierTrackEdit::get_animation() const {
return animation;
}
-void AnimationBezierTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track) {
+void AnimationBezierTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only) {
animation = p_animation;
+ read_only = p_read_only;
selected_track = p_track;
update();
}
@@ -715,7 +722,7 @@ void AnimationBezierTrackEdit::set_filtered(bool p_filtered) {
continue; // Skip track due to not selected.
}
- set_animation_and_track(animation, i);
+ set_animation_and_track(animation, i, read_only);
break;
}
}
@@ -819,12 +826,16 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (p_event->is_pressed()) {
if (ED_GET_SHORTCUT("animation_editor/duplicate_selection")->matches_event(p_event)) {
- duplicate_selection();
+ if (!read_only) {
+ duplicate_selection();
+ }
accept_event();
}
if (ED_GET_SHORTCUT("animation_editor/delete_selection")->matches_event(p_event)) {
- delete_selection();
+ if (!read_only) {
+ delete_selection();
+ }
accept_event();
}
}
@@ -917,26 +928,28 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
menu_insert_key = mb->get_position();
if (menu_insert_key.x >= limit && menu_insert_key.x <= get_size().width) {
- Vector2 popup_pos = get_screen_position() + mb->get_position();
+ if (!read_only) {
+ Vector2 popup_pos = get_screen_position() + mb->get_position();
- menu->clear();
- if (!locked_tracks.has(selected_track) || locked_tracks.has(selected_track)) {
- menu->add_icon_item(bezier_icon, TTR("Insert Key Here"), MENU_KEY_INSERT);
- }
- if (selection.size()) {
- menu->add_separator();
- menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Duplicate Selected Key(s)"), MENU_KEY_DUPLICATE);
- menu->add_separator();
- 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("BezierHandlesBalanced"), SNAME("EditorIcons")), TTR("Make Handles Balanced"), MENU_KEY_SET_HANDLE_BALANCED);
- }
+ menu->clear();
+ if (!locked_tracks.has(selected_track) || locked_tracks.has(selected_track)) {
+ menu->add_icon_item(bezier_icon, TTR("Insert Key Here"), MENU_KEY_INSERT);
+ }
+ if (selection.size()) {
+ menu->add_separator();
+ menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Duplicate Selected Key(s)"), MENU_KEY_DUPLICATE);
+ menu->add_separator();
+ 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("BezierHandlesBalanced"), SNAME("EditorIcons")), TTR("Make Handles Balanced"), MENU_KEY_SET_HANDLE_BALANCED);
+ }
- if (menu->get_item_count()) {
- menu->reset_size();
- menu->set_position(popup_pos);
- menu->popup();
+ if (menu->get_item_count()) {
+ menu->reset_size();
+ menu->set_position(popup_pos);
+ menu->popup();
+ }
}
}
}
@@ -945,7 +958,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
for (const KeyValue<int, Rect2> &E : subtracks) {
if (E.value.has_point(mb->get_position())) {
if (!locked_tracks.has(E.key) && !hidden_tracks.has(E.key)) {
- set_animation_and_track(animation, E.key);
+ set_animation_and_track(animation, E.key, read_only);
_clear_selection();
}
return;
@@ -958,30 +971,32 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
for (const KeyValue<int, Rect2> &I : track_icons) {
if (I.value.has_point(mb->get_position())) {
if (I.key == REMOVE_ICON) {
- undo_redo->create_action("Remove Bezier Track");
-
- undo_redo->add_do_method(this, "_update_locked_tracks_after", track);
- undo_redo->add_do_method(this, "_update_hidden_tracks_after", track);
-
- undo_redo->add_do_method(animation.ptr(), "remove_track", track);
+ if (!read_only) {
+ undo_redo->create_action("Remove Bezier Track");
+
+ undo_redo->add_do_method(this, "_update_locked_tracks_after", track);
+ undo_redo->add_do_method(this, "_update_hidden_tracks_after", track);
+
+ undo_redo->add_do_method(animation.ptr(), "remove_track", track);
+
+ undo_redo->add_undo_method(animation.ptr(), "add_track", Animation::TrackType::TYPE_BEZIER, track);
+ undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track));
+
+ 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),
+ 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),
+ animation->bezier_track_get_key_handle_mode(track, i));
+ }
- undo_redo->add_undo_method(animation.ptr(), "add_track", Animation::TrackType::TYPE_BEZIER, track);
- undo_redo->add_undo_method(animation.ptr(), "track_set_path", track, animation->track_get_path(track));
+ undo_redo->commit_action();
- 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),
- 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),
- animation->bezier_track_get_key_handle_mode(track, i));
+ selected_track = CLAMP(selected_track, 0, animation->get_track_count() - 1);
}
-
- undo_redo->commit_action();
-
- selected_track = CLAMP(selected_track, 0, animation->get_track_count() - 1);
return;
} else if (I.key == LOCK_ICON) {
if (locked_tracks.has(track)) {
@@ -991,7 +1006,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (selected_track == track) {
for (int i = 0; i < animation->get_track_count(); ++i) {
if (!locked_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
- set_animation_and_track(animation, i);
+ set_animation_and_track(animation, i, read_only);
break;
}
}
@@ -1007,7 +1022,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (selected_track == track) {
for (int i = 0; i < animation->get_track_count(); ++i) {
if (!hidden_tracks.has(i) && animation->track_get_type(i) == Animation::TrackType::TYPE_BEZIER) {
- set_animation_and_track(animation, i);
+ set_animation_and_track(animation, i, read_only);
break;
}
}
@@ -1046,7 +1061,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- set_animation_and_track(animation, track);
+ set_animation_and_track(animation, track, read_only);
solo_track = track;
}
update();
@@ -1087,7 +1102,7 @@ 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);
+ set_animation_and_track(animation, pair.first, read_only);
selection.clear();
selection.insert(pair);
update();
@@ -1096,24 +1111,26 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (edit_points[i].in_rect.has_point(mb->get_position())) {
- moving_handle = -1;
- moving_handle_key = edit_points[i].key;
- moving_handle_track = edit_points[i].track;
- moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key);
- moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key);
- update();
- return;
- }
+ if (!read_only) {
+ if (edit_points[i].in_rect.has_point(mb->get_position())) {
+ moving_handle = -1;
+ moving_handle_key = edit_points[i].key;
+ moving_handle_track = edit_points[i].track;
+ moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key);
+ moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key);
+ update();
+ return;
+ }
- if (edit_points[i].out_rect.has_point(mb->get_position())) {
- moving_handle = 1;
- moving_handle_key = edit_points[i].key;
- moving_handle_track = edit_points[i].track;
- moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key);
- moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key);
- update();
- return;
+ if (edit_points[i].out_rect.has_point(mb->get_position())) {
+ moving_handle = 1;
+ moving_handle_key = edit_points[i].key;
+ moving_handle_track = edit_points[i].track;
+ moving_handle_left = animation->bezier_track_get_key_in_handle(edit_points[i].track, edit_points[i].key);
+ moving_handle_right = animation->bezier_track_get_key_out_handle(edit_points[i].track, edit_points[i].key);
+ update();
+ return;
+ }
}
}
@@ -1191,7 +1208,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
selection.insert(IntPair(edit_points[i].track, edit_points[i].key));
if (!track_set) {
track_set = true;
- set_animation_and_track(animation, edit_points[i].track);
+ set_animation_and_track(animation, edit_points[i].track, read_only);
}
}
}
@@ -1215,7 +1232,7 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
float track_height = _bezier_h_to_pixel(track_h);
if (abs(mb->get_position().y - track_height) < 10) {
- set_animation_and_track(animation, i);
+ set_animation_and_track(animation, i, read_only);
break;
}
}
@@ -1229,102 +1246,106 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
}
if (moving_handle != 0 && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
- 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();
+ 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();
+ moving_handle = 0;
+ update();
+ }
}
if (moving_selection_attempt && mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
- if (moving_selection) {
- //combit it
+ if (!read_only) {
+ if (moving_selection) {
+ //combit it
- undo_redo->create_action(TTR("Move Bezier Points"));
-
- List<AnimMoveRestore> to_restore;
- // 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);
+ undo_redo->create_action(TTR("Move Bezier Points"));
- int idx = animation->track_find_key(E->get().first, newtime, true);
- if (idx == -1) {
- continue;
+ List<AnimMoveRestore> to_restore;
+ // 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);
- if (selection.has(IntPair(E->get().first, idx))) {
- continue; //already in selection, don't save
- }
+ int idx = animation->track_find_key(E->get().first, newtime, true);
+ if (idx == -1) {
+ continue;
+ }
- undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newtime);
- AnimMoveRestore amr;
+ if (selection.has(IntPair(E->get().first, idx))) {
+ continue; //already in selection, don't save
+ }
- amr.key = animation->track_get_key_value(E->get().first, idx);
- amr.track = E->get().first;
- amr.time = newtime;
+ undo_redo->add_do_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newtime);
+ AnimMoveRestore amr;
- to_restore.push_back(amr);
- }
+ amr.key = animation->track_get_key_value(E->get().first, idx);
+ amr.track = E->get().first;
+ amr.time = newtime;
- // 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);
- Array key = animation->track_get_key_value(E->get().first, E->get().second);
- float 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);
- }
+ to_restore.push_back(amr);
+ }
- // 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);
- undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos);
- }
+ // 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);
+ Array key = animation->track_get_key_value(E->get().first, E->get().second);
+ float 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);
+ }
- // 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);
- }
+ // 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);
+ undo_redo->add_undo_method(animation.ptr(), "track_remove_key_at_time", E->get().first, newpos);
+ }
- // 6-(undo) reinsert overlapped keys
- for (const AnimMoveRestore &amr : to_restore) {
- undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1);
- }
+ // 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);
+ }
- undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
- undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
+ // 6-(undo) reinsert overlapped keys
+ for (const AnimMoveRestore &amr : to_restore) {
+ undo_redo->add_undo_method(animation.ptr(), "track_insert_key", amr.track, amr.time, amr.key, 1);
+ }
- // 7-reselect
+ undo_redo->add_do_method(this, "_clear_selection_for_anim", animation);
+ undo_redo->add_undo_method(this, "_clear_selection_for_anim", animation);
- 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);
+ // 7-reselect
- 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);
- }
+ 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);
- undo_redo->commit_action();
+ 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);
+ }
- moving_selection = false;
- } else if (select_single_attempt != IntPair(-1, -1)) {
- selection.clear();
- selection.insert(select_single_attempt);
- set_animation_and_track(animation, select_single_attempt.first);
- }
+ undo_redo->commit_action();
- moving_selection_attempt = false;
- update();
+ moving_selection = false;
+ } else if (select_single_attempt != IntPair(-1, -1)) {
+ selection.clear();
+ selection.insert(select_single_attempt);
+ set_animation_and_track(animation, select_single_attempt.first, read_only);
+ }
+
+ moving_selection_attempt = false;
+ update();
+ }
}
Ref<InputEventMouseMotion> mm = p_event;
@@ -1337,7 +1358,9 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
float y = (get_size().height / 2 - 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());
- 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));
+ 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));
+ }
update();
}
@@ -1399,20 +1422,22 @@ void AnimationBezierTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
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) {
- undo_redo->create_action(TTR("Move Bezier Points"));
- if (moving_handle == -1) {
- double 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;
- 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();
+ if (!read_only) {
+ undo_redo->create_action(TTR("Move Bezier Points"));
+ if (moving_handle == -1) {
+ double 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;
+ 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();
+ moving_handle = 0;
+ update();
+ }
}
}
diff --git a/editor/animation_bezier_editor.h b/editor/animation_bezier_editor.h
index 22b58a6703..070a6589ad 100644
--- a/editor/animation_bezier_editor.h
+++ b/editor/animation_bezier_editor.h
@@ -54,6 +54,7 @@ class AnimationBezierTrackEdit : public Control {
float play_position_pos = 0;
Ref<Animation> animation;
+ bool read_only = false;
int selected_track = 0;
Vector<Rect2> view_rects;
@@ -176,7 +177,7 @@ public:
Ref<Animation> get_animation() const;
- void set_animation_and_track(const Ref<Animation> &p_animation, int p_track);
+ 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);
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index 83047caf98..0db82551cb 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -48,6 +48,7 @@ class AnimationTrackKeyEdit : public Object {
public:
bool setting = false;
+ bool animation_read_only = false;
bool _hide_script_from_inspector() {
return true;
@@ -57,12 +58,17 @@ public:
return true;
}
+ bool _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);
}
void _fix_node_path(Variant &value) {
@@ -703,6 +709,7 @@ class AnimationMultiTrackKeyEdit : public Object {
public:
bool setting = false;
+ bool animation_read_only = false;
bool _hide_script_from_inspector() {
return true;
@@ -712,12 +719,17 @@ public:
return true;
}
+ bool _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);
}
void _fix_node_path(Variant &value, NodePath &base) {
@@ -1416,22 +1428,32 @@ void AnimationTimelineEdit::_anim_length_changed(double p_new_len) {
}
void AnimationTimelineEdit::_anim_loop_pressed() {
- undo_redo->create_action(TTR("Change Animation Loop"));
- switch (animation->get_loop_mode()) {
- case Animation::LOOP_NONE: {
- undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_LINEAR);
- } break;
- case Animation::LOOP_LINEAR: {
- undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_PINGPONG);
- } break;
- case Animation::LOOP_PINGPONG: {
- undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_NONE);
- } break;
- default:
- break;
+ if (!read_only) {
+ undo_redo->create_action(TTR("Change Animation Loop"));
+ switch (animation->get_loop_mode()) {
+ case Animation::LOOP_NONE: {
+ undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_LINEAR);
+ } break;
+ case Animation::LOOP_LINEAR: {
+ undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_PINGPONG);
+ } break;
+ case Animation::LOOP_PINGPONG: {
+ undo_redo->add_do_method(animation.ptr(), "set_loop_mode", Animation::LOOP_NONE);
+ } break;
+ default:
+ break;
+ }
+ undo_redo->add_undo_method(animation.ptr(), "set_loop_mode", animation->get_loop_mode());
+ undo_redo->commit_action();
+ } else {
+ String base_path = animation->get_path();
+ if (FileAccess::exists(base_path + ".import")) {
+ EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation instanced from imported scene."));
+ } else {
+ EditorNode::get_singleton()->show_warning(TTR("Can't change loop mode on animation embedded in another scene."));
+ }
+ update_values();
}
- undo_redo->add_undo_method(animation.ptr(), "set_loop_mode", animation->get_loop_mode());
- undo_redo->commit_action();
}
int AnimationTimelineEdit::get_buttons_width() const {
@@ -1656,11 +1678,17 @@ void AnimationTimelineEdit::_notification(int p_what) {
}
}
-void AnimationTimelineEdit::set_animation(const Ref<Animation> &p_animation) {
+void AnimationTimelineEdit::set_animation(const Ref<Animation> &p_animation, bool p_read_only) {
animation = p_animation;
+ read_only = p_read_only;
+
if (animation.is_valid()) {
len_hb->show();
- add_track->show();
+ if (read_only) {
+ add_track->hide();
+ } else {
+ add_track->show();
+ }
play_position->show();
} else {
len_hb->hide();
@@ -1982,6 +2010,8 @@ void AnimationTrackEdit::_notification(int p_what) {
Color linecolor = color;
linecolor.a = 0.2;
+ Color dc = get_theme_color(SNAME("disabled_font_color"), SNAME("Editor"));
+
// NAMES AND ICONS //
{
@@ -2131,14 +2161,18 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += update_icon->get_width() + hsep / 2;
update_mode_rect.size.x += hsep / 2;
- if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
- draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
- update_mode_rect.size.x += down_icon->get_width();
- } else if (animation->track_get_type(track) == Animation::TYPE_BEZIER) {
- Ref<Texture2D> bezier_icon = get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons"));
- update_mode_rect.size.x += down_icon->get_width();
+ if (!read_only) {
+ if (animation->track_get_type(track) == Animation::TYPE_VALUE) {
+ draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
+ update_mode_rect.size.x += down_icon->get_width();
+ } else if (animation->track_get_type(track) == Animation::TYPE_BEZIER) {
+ Ref<Texture2D> bezier_icon = get_theme_icon(SNAME("EditBezier"), SNAME("EditorIcons"));
+ update_mode_rect.size.x += down_icon->get_width();
- update_mode_rect = Rect2();
+ update_mode_rect = Rect2();
+ } else {
+ update_mode_rect = Rect2();
+ }
} else {
update_mode_rect = Rect2();
}
@@ -2169,7 +2203,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep / 2;
interp_mode_rect.size.x += hsep / 2;
- if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
+ if (!read_only && !animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
interp_mode_rect.size.x += down_icon->get_width();
} else {
@@ -2202,7 +2236,7 @@ void AnimationTrackEdit::_notification(int p_what) {
ofs += icon->get_width() + hsep / 2;
loop_wrap_rect.size.x += hsep / 2;
- if (!animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
+ if (!read_only && !animation->track_is_compressed(track) && (animation->track_get_type(track) == Animation::TYPE_VALUE || animation->track_get_type(track) == Animation::TYPE_BLEND_SHAPE || animation->track_get_type(track) == Animation::TYPE_POSITION_3D || animation->track_get_type(track) == Animation::TYPE_SCALE_3D || animation->track_get_type(track) == Animation::TYPE_ROTATION_3D)) {
draw_texture(down_icon, Vector2(ofs, int(get_size().height - down_icon->get_height()) / 2));
loop_wrap_rect.size.x += down_icon->get_width();
} else {
@@ -2223,7 +2257,11 @@ void AnimationTrackEdit::_notification(int p_what) {
remove_rect.position.y = int(get_size().height - icon->get_height()) / 2;
remove_rect.size = icon->get_size();
- draw_texture(icon, remove_rect.position);
+ if (read_only) {
+ draw_texture(icon, remove_rect.position, dc);
+ } else {
+ draw_texture(icon, remove_rect.position);
+ }
}
}
@@ -2439,8 +2477,10 @@ Ref<Animation> AnimationTrackEdit::get_animation() const {
return animation;
}
-void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track) {
+void AnimationTrackEdit::set_animation_and_track(const Ref<Animation> &p_animation, int p_track, bool p_read_only) {
animation = p_animation;
+ read_only = p_read_only;
+
track = p_track;
update();
@@ -2721,17 +2761,23 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (p_event->is_pressed()) {
if (ED_GET_SHORTCUT("animation_editor/duplicate_selection")->matches_event(p_event)) {
- emit_signal(SNAME("duplicate_request"));
+ if (!read_only) {
+ emit_signal(SNAME("duplicate_request"));
+ }
accept_event();
}
if (ED_GET_SHORTCUT("animation_editor/duplicate_selection_transposed")->matches_event(p_event)) {
- emit_signal(SNAME("duplicate_transpose_request"));
+ if (!read_only) {
+ emit_signal(SNAME("duplicate_transpose_request"));
+ }
accept_event();
}
if (ED_GET_SHORTCUT("animation_editor/delete_selection")->matches_event(p_event)) {
- emit_signal(SNAME("delete_request"));
+ if (!read_only) {
+ emit_signal(SNAME("delete_request"));
+ }
accept_event();
}
}
@@ -2740,79 +2786,81 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
Point2 pos = mb->get_position();
- if (check_rect.has_point(pos)) {
- undo_redo->create_action(TTR("Toggle Track Enabled"));
- undo_redo->add_do_method(animation.ptr(), "track_set_enabled", track, !animation->track_is_enabled(track));
- undo_redo->add_undo_method(animation.ptr(), "track_set_enabled", track, animation->track_is_enabled(track));
- undo_redo->commit_action();
- update();
- accept_event();
- }
+ if (!read_only) {
+ if (check_rect.has_point(pos)) {
+ undo_redo->create_action(TTR("Toggle Track Enabled"));
+ undo_redo->add_do_method(animation.ptr(), "track_set_enabled", track, !animation->track_is_enabled(track));
+ undo_redo->add_undo_method(animation.ptr(), "track_set_enabled", track, animation->track_is_enabled(track));
+ undo_redo->commit_action();
+ update();
+ accept_event();
+ }
- // Don't overlap track keys if they start at 0.
- if (path_rect.has_point(pos + Size2(type_icon->get_width(), 0))) {
- clicking_on_name = true;
- accept_event();
- }
+ // Don't overlap track keys if they start at 0.
+ if (path_rect.has_point(pos + Size2(type_icon->get_width(), 0))) {
+ clicking_on_name = true;
+ accept_event();
+ }
- if (update_mode_rect.has_point(pos)) {
- if (!menu) {
- menu = memnew(PopupMenu);
- add_child(menu);
- menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
+ if (update_mode_rect.has_point(pos)) {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
+ }
+ menu->clear();
+ menu->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), TTR("Continuous"), MENU_CALL_MODE_CONTINUOUS);
+ menu->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), TTR("Discrete"), MENU_CALL_MODE_DISCRETE);
+ menu->add_icon_item(get_theme_icon(SNAME("TrackTrigger"), SNAME("EditorIcons")), TTR("Trigger"), MENU_CALL_MODE_TRIGGER);
+ menu->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), TTR("Capture"), MENU_CALL_MODE_CAPTURE);
+ menu->reset_size();
+
+ Vector2 popup_pos = get_screen_position() + update_mode_rect.position + Vector2(0, update_mode_rect.size.height);
+ menu->set_position(popup_pos);
+ menu->popup();
+ accept_event();
}
- menu->clear();
- menu->add_icon_item(get_theme_icon(SNAME("TrackContinuous"), SNAME("EditorIcons")), TTR("Continuous"), MENU_CALL_MODE_CONTINUOUS);
- menu->add_icon_item(get_theme_icon(SNAME("TrackDiscrete"), SNAME("EditorIcons")), TTR("Discrete"), MENU_CALL_MODE_DISCRETE);
- menu->add_icon_item(get_theme_icon(SNAME("TrackTrigger"), SNAME("EditorIcons")), TTR("Trigger"), MENU_CALL_MODE_TRIGGER);
- menu->add_icon_item(get_theme_icon(SNAME("TrackCapture"), SNAME("EditorIcons")), TTR("Capture"), MENU_CALL_MODE_CAPTURE);
- menu->reset_size();
-
- Vector2 popup_pos = get_screen_position() + update_mode_rect.position + Vector2(0, update_mode_rect.size.height);
- menu->set_position(popup_pos);
- menu->popup();
- accept_event();
- }
- if (interp_mode_rect.has_point(pos)) {
- if (!menu) {
- menu = memnew(PopupMenu);
- add_child(menu);
- menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
+ if (interp_mode_rect.has_point(pos)) {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
+ }
+ menu->clear();
+ 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);
+ menu->reset_size();
+
+ Vector2 popup_pos = get_screen_position() + interp_mode_rect.position + Vector2(0, interp_mode_rect.size.height);
+ menu->set_position(popup_pos);
+ menu->popup();
+ accept_event();
}
- menu->clear();
- 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);
- menu->reset_size();
-
- Vector2 popup_pos = get_screen_position() + interp_mode_rect.position + Vector2(0, interp_mode_rect.size.height);
- menu->set_position(popup_pos);
- menu->popup();
- accept_event();
- }
- if (loop_wrap_rect.has_point(pos)) {
- if (!menu) {
- menu = memnew(PopupMenu);
- add_child(menu);
- menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
+ if (loop_wrap_rect.has_point(pos)) {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
+ }
+ menu->clear();
+ menu->add_icon_item(get_theme_icon(SNAME("InterpWrapClamp"), SNAME("EditorIcons")), TTR("Clamp Loop Interp"), MENU_LOOP_CLAMP);
+ menu->add_icon_item(get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")), TTR("Wrap Loop Interp"), MENU_LOOP_WRAP);
+ menu->reset_size();
+
+ Vector2 popup_pos = get_screen_position() + loop_wrap_rect.position + Vector2(0, loop_wrap_rect.size.height);
+ menu->set_position(popup_pos);
+ menu->popup();
+ accept_event();
}
- menu->clear();
- menu->add_icon_item(get_theme_icon(SNAME("InterpWrapClamp"), SNAME("EditorIcons")), TTR("Clamp Loop Interp"), MENU_LOOP_CLAMP);
- menu->add_icon_item(get_theme_icon(SNAME("InterpWrapLoop"), SNAME("EditorIcons")), TTR("Wrap Loop Interp"), MENU_LOOP_WRAP);
- menu->reset_size();
-
- Vector2 popup_pos = get_screen_position() + loop_wrap_rect.position + Vector2(0, loop_wrap_rect.size.height);
- menu->set_position(popup_pos);
- menu->popup();
- accept_event();
- }
- if (remove_rect.has_point(pos)) {
- emit_signal(SNAME("remove_request"), track);
- accept_event();
- return;
+ if (remove_rect.has_point(pos)) {
+ emit_signal(SNAME("remove_request"), track);
+ accept_event();
+ return;
+ }
}
// Check keyframes.
@@ -2872,6 +2920,11 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
moving_selection_attempt = true;
moving_selection_from_ofs = (mb->get_position().x - limit) / timeline->get_zoom_scale();
}
+
+ if (read_only) {
+ moving_selection_attempt = false;
+ moving_selection_from_ofs = 0.0f;
+ }
accept_event();
}
}
@@ -2883,33 +2936,35 @@ void AnimationTrackEdit::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x >= timeline->get_name_limit() && pos.x <= get_size().width - timeline->get_buttons_width()) {
// Can do something with menu too! show insert key.
float offset = (pos.x - timeline->get_name_limit()) / timeline->get_zoom_scale();
- if (!menu) {
- menu = memnew(PopupMenu);
- add_child(menu);
- menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
- }
+ if (!read_only) {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ menu->connect("id_pressed", callable_mp(this, &AnimationTrackEdit::_menu_selected));
+ }
- menu->clear();
- menu->add_icon_item(get_theme_icon(SNAME("Key"), SNAME("EditorIcons")), TTR("Insert Key"), MENU_KEY_INSERT);
- if (editor->is_selection_active()) {
- menu->add_separator();
- menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Duplicate Key(s)"), MENU_KEY_DUPLICATE);
+ menu->clear();
+ menu->add_icon_item(get_theme_icon(SNAME("Key"), SNAME("EditorIcons")), TTR("Insert Key"), MENU_KEY_INSERT);
+ if (editor->is_selection_active()) {
+ menu->add_separator();
+ menu->add_icon_item(get_theme_icon(SNAME("Duplicate"), SNAME("EditorIcons")), TTR("Duplicate Key(s)"), MENU_KEY_DUPLICATE);
- AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
- if (!player->has_animation(SceneStringNames::get_singleton()->RESET) || animation != player->get_animation(SceneStringNames::get_singleton()->RESET)) {
- menu->add_icon_item(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")), TTR("Add RESET Value(s)"), MENU_KEY_ADD_RESET);
- }
+ AnimationPlayer *player = AnimationPlayerEditor::get_singleton()->get_player();
+ if (!player->has_animation(SceneStringNames::get_singleton()->RESET) || animation != player->get_animation(SceneStringNames::get_singleton()->RESET)) {
+ menu->add_icon_item(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")), TTR("Add RESET Value(s)"), MENU_KEY_ADD_RESET);
+ }
- menu->add_separator();
- menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete Key(s)"), MENU_KEY_DELETE);
- }
- menu->reset_size();
+ menu->add_separator();
+ menu->add_icon_item(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")), TTR("Delete Key(s)"), MENU_KEY_DELETE);
+ }
+ menu->reset_size();
- menu->set_position(get_screen_position() + get_local_mouse_position());
- menu->popup();
+ menu->set_position(get_screen_position() + get_local_mouse_position());
+ menu->popup();
- insert_at_pos = offset + timeline->get_value();
- accept_event();
+ insert_at_pos = offset + timeline->get_value();
+ accept_event();
+ }
}
}
@@ -3354,7 +3409,7 @@ void AnimationTrackEditor::remove_track_edit_plugin(const Ref<AnimationTrackEdit
track_edit_plugins.erase(p_plugin);
}
-void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
+void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_read_only) {
if (animation != p_anim && _get_track_selected() >= 0) {
track_edits[_get_track_selected()]->release_focus();
}
@@ -3363,7 +3418,8 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
_clear_selection();
}
animation = p_anim;
- timeline->set_animation(p_anim);
+ read_only = p_read_only;
+ timeline->set_animation(p_anim, read_only);
_cancel_bezier_edit();
_update_tracks();
@@ -3372,7 +3428,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim) {
animation->connect("changed", callable_mp(this, &AnimationTrackEditor::_animation_changed));
hscroll->show();
- edit->set_disabled(false);
+ edit->set_disabled(read_only);
step->set_block_signals(true);
_update_step_spinbox();
@@ -3501,7 +3557,7 @@ void AnimationTrackEditor::set_state(const Dictionary &p_state) {
}
void AnimationTrackEditor::cleanup() {
- set_animation(Ref<Animation>());
+ set_animation(Ref<Animation>(), read_only);
}
void AnimationTrackEditor::_name_limit_changed() {
@@ -4378,6 +4434,27 @@ void AnimationTrackEditor::_update_tracks() {
return;
}
+ bool read_only = false;
+ if (!animation->get_path().is_resource_file()) {
+ int srpos = animation->get_path().find("::");
+ if (srpos != -1) {
+ String base = animation->get_path().substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ read_only = true;
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ read_only = true;
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(animation->get_path() + ".import")) {
+ read_only = true;
+ }
+ }
+
RBMap<String, VBoxContainer *> group_sort;
bool use_grouping = !view_group->is_pressed();
@@ -4506,7 +4583,7 @@ void AnimationTrackEditor::_update_tracks() {
track_edit->set_undo_redo(undo_redo);
track_edit->set_timeline(timeline);
track_edit->set_root(root);
- track_edit->set_animation_and_track(animation, i);
+ track_edit->set_animation_and_track(animation, i, read_only);
track_edit->set_play_position(timeline->get_play_position());
track_edit->set_editor(this);
@@ -5178,6 +5255,7 @@ void AnimationTrackEditor::_update_key_edit() {
if (selection.size() == 1) {
key_edit = memnew(AnimationTrackKeyEdit);
key_edit->animation = animation;
+ key_edit->animation_read_only = read_only;
key_edit->track = selection.front()->key().track;
key_edit->use_fps = timeline->is_using_fps();
@@ -5194,6 +5272,7 @@ void AnimationTrackEditor::_update_key_edit() {
} else if (selection.size() > 1) {
multi_key_edit = memnew(AnimationMultiTrackKeyEdit);
multi_key_edit->animation = animation;
+ multi_key_edit->animation_read_only = read_only;
RBMap<int, List<float>> key_ofs_map;
RBMap<int, NodePath> base_map;
@@ -5473,7 +5552,7 @@ void AnimationTrackEditor::_cancel_bezier_edit() {
void AnimationTrackEditor::_bezier_edit(int p_for_track) {
_clear_selection(); // Bezier probably wants to use a separate selection mode.
bezier_edit->set_root(root);
- bezier_edit->set_animation_and_track(animation, p_for_track);
+ bezier_edit->set_animation_and_track(animation, p_for_track, read_only);
scroll->hide();
bezier_edit->show();
// Search everything within the track and curve - edit it.
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index cb7d3c7d96..b0553c54a5 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -56,6 +56,8 @@ class AnimationTimelineEdit : public Range {
GDCLASS(AnimationTimelineEdit, Range);
Ref<Animation> animation;
+ bool read_only = false;
+
AnimationTrackEdit *track_edit = nullptr;
int name_limit = 0;
Range *zoom = nullptr;
@@ -106,7 +108,7 @@ public:
float get_zoom_scale() const;
virtual Size2 get_minimum_size() const override;
- void set_animation(const Ref<Animation> &p_animation);
+ void set_animation(const Ref<Animation> &p_animation, bool p_read_only);
void set_track_edit(AnimationTrackEdit *p_track_edit);
void set_zoom(Range *p_zoom);
Range *get_zoom() const { return zoom; }
@@ -159,6 +161,7 @@ class AnimationTrackEdit : public Control {
NodePath node_path;
Ref<Animation> animation;
+ bool read_only = false;
int track = 0;
Rect2 check_rect;
@@ -232,7 +235,7 @@ public:
AnimationTrackEditor *get_editor() const { return editor; }
UndoRedo *get_undo_redo() const { return undo_redo; }
NodePath get_path() const;
- void set_animation_and_track(const Ref<Animation> &p_animation, int p_track);
+ 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);
@@ -290,6 +293,7 @@ class AnimationTrackEditor : public VBoxContainer {
GDCLASS(AnimationTrackEditor, VBoxContainer);
Ref<Animation> animation;
+ bool read_only = false;
Node *root = nullptr;
MenuButton *edit = nullptr;
@@ -533,7 +537,7 @@ public:
void add_track_edit_plugin(const Ref<AnimationTrackEditPlugin> &p_plugin);
void remove_track_edit_plugin(const Ref<AnimationTrackEditPlugin> &p_plugin);
- void set_animation(const Ref<Animation> &p_anim);
+ void set_animation(const Ref<Animation> &p_anim, bool p_read_only);
Ref<Animation> get_current_animation() const;
void set_root(Node *p_root);
Node *get_root() const;
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index b0eb384efc..9e72c8ec10 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -1869,7 +1869,8 @@ CodeTextEditor::CodeTextEditor() {
code_complete_func = nullptr;
ED_SHORTCUT("script_editor/zoom_in", TTR("Zoom In"), KeyModifierMask::CMD | Key::EQUAL);
ED_SHORTCUT("script_editor/zoom_out", TTR("Zoom Out"), KeyModifierMask::CMD | Key::MINUS);
- ED_SHORTCUT("script_editor/reset_zoom", TTR("Reset Zoom"), KeyModifierMask::CMD | Key::KEY_0);
+ ED_SHORTCUT_ARRAY("script_editor/reset_zoom", TTR("Reset Zoom"),
+ { int32_t(KeyModifierMask::CMD | Key::KEY_0), int32_t(KeyModifierMask::CMD | Key::KP_0) });
text_editor = memnew(CodeEdit);
add_child(text_editor);
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index a819458417..864871bb7e 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -337,311 +337,318 @@ static Variant get_documentation_default_value(const StringName &p_class_name, c
}
void DocTools::generate(bool p_basic_types) {
- List<StringName> classes;
- ClassDB::get_class_list(&classes);
- classes.sort_custom<StringName::AlphCompare>();
- // Move ProjectSettings, so that other classes can register properties there.
- classes.move_to_back(classes.find("ProjectSettings"));
-
- bool skip_setter_getter_methods = true;
-
- while (classes.size()) {
- HashSet<StringName> setters_getters;
-
- String name = classes.front()->get();
- if (!ClassDB::is_class_exposed(name)) {
- print_verbose(vformat("Class '%s' is not exposed, skipping.", name));
- classes.pop_front();
- continue;
- }
-
- String cname = name;
-
- class_list[cname] = DocData::ClassDoc();
- DocData::ClassDoc &c = class_list[cname];
- c.name = cname;
- c.inherits = ClassDB::get_parent_class(name);
+ // Add ClassDB-exposed classes.
+ {
+ List<StringName> classes;
+ ClassDB::get_class_list(&classes);
+ classes.sort_custom<StringName::AlphCompare>();
+ // Move ProjectSettings, so that other classes can register properties there.
+ classes.move_to_back(classes.find("ProjectSettings"));
+
+ bool skip_setter_getter_methods = true;
+
+ // Populate documentation data for each exposed class.
+ while (classes.size()) {
+ String name = classes.front()->get();
+ if (!ClassDB::is_class_exposed(name)) {
+ print_verbose(vformat("Class '%s' is not exposed, skipping.", name));
+ classes.pop_front();
+ continue;
+ }
- List<PropertyInfo> properties;
- List<PropertyInfo> own_properties;
-
- // Special case for editor and project settings, so they can be documented.
- if (name == "EditorSettings") {
- // We don't create the full blown EditorSettings (+ config file) with `create()`,
- // instead we just make a local instance to get default values.
- Ref<EditorSettings> edset = memnew(EditorSettings);
- edset->get_property_list(&properties);
- own_properties = properties;
- } else if (name == "ProjectSettings") {
- ProjectSettings::get_singleton()->get_property_list(&properties);
- own_properties = properties;
- } else {
- ClassDB::get_property_list(name, &properties);
- ClassDB::get_property_list(name, &own_properties, true);
- }
+ String cname = name;
+ // Property setters and getters do not get exposed as individual methods.
+ HashSet<StringName> setters_getters;
- properties.sort();
- own_properties.sort();
+ class_list[cname] = DocData::ClassDoc();
+ DocData::ClassDoc &c = class_list[cname];
+ c.name = cname;
+ c.inherits = ClassDB::get_parent_class(name);
- List<PropertyInfo>::Element *EO = own_properties.front();
- for (const PropertyInfo &E : properties) {
- bool inherited = true;
- if (EO && EO->get() == E) {
- inherited = false;
- EO = EO->next();
- }
+ List<PropertyInfo> properties;
+ List<PropertyInfo> own_properties;
- if (E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP || E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_INTERNAL || (E.type == Variant::NIL && E.usage & PROPERTY_USAGE_ARRAY)) {
- continue;
+ // Special case for editor and project settings, so they can be documented.
+ if (name == "EditorSettings") {
+ // We don't create the full blown EditorSettings (+ config file) with `create()`,
+ // instead we just make a local instance to get default values.
+ Ref<EditorSettings> edset = memnew(EditorSettings);
+ edset->get_property_list(&properties);
+ own_properties = properties;
+ } else if (name == "ProjectSettings") {
+ ProjectSettings::get_singleton()->get_property_list(&properties);
+ own_properties = properties;
+ } else {
+ ClassDB::get_property_list(name, &properties);
+ ClassDB::get_property_list(name, &own_properties, true);
}
- DocData::PropertyDoc prop;
- prop.name = E.name;
- prop.overridden = inherited;
+ properties.sort();
+ own_properties.sort();
- if (inherited) {
- String parent = ClassDB::get_parent_class(c.name);
- while (!ClassDB::has_property(parent, prop.name, true)) {
- parent = ClassDB::get_parent_class(parent);
+ List<PropertyInfo>::Element *EO = own_properties.front();
+ for (const PropertyInfo &E : properties) {
+ bool inherited = true;
+ if (EO && EO->get() == E) {
+ inherited = false;
+ EO = EO->next();
}
- prop.overrides = parent;
- }
-
- bool default_value_valid = false;
- Variant default_value;
- if (name == "EditorSettings") {
- if (E.name == "resource_local_to_scene" || E.name == "resource_name" || E.name == "resource_path" || E.name == "script") {
- // Don't include spurious properties in the generated EditorSettings class reference.
+ if (E.usage & PROPERTY_USAGE_GROUP || E.usage & PROPERTY_USAGE_SUBGROUP || E.usage & PROPERTY_USAGE_CATEGORY || E.usage & PROPERTY_USAGE_INTERNAL || (E.type == Variant::NIL && E.usage & PROPERTY_USAGE_ARRAY)) {
continue;
}
- }
- if (name == "ProjectSettings") {
- // Special case for project settings, so that settings are not taken from the current project's settings
- if (E.name == "script" || !ProjectSettings::get_singleton()->is_builtin_setting(E.name)) {
- continue;
- }
- if (E.usage & PROPERTY_USAGE_EDITOR) {
- if (!ProjectSettings::get_singleton()->get_ignore_value_in_docs(E.name)) {
- default_value = ProjectSettings::get_singleton()->property_get_revert(E.name);
- default_value_valid = true;
+ DocData::PropertyDoc prop;
+ prop.name = E.name;
+ prop.overridden = inherited;
+
+ if (inherited) {
+ String parent = ClassDB::get_parent_class(c.name);
+ while (!ClassDB::has_property(parent, prop.name, true)) {
+ parent = ClassDB::get_parent_class(parent);
}
+ prop.overrides = parent;
}
- } else {
- default_value = get_documentation_default_value(name, E.name, default_value_valid);
- if (inherited) {
- bool base_default_value_valid = false;
- Variant base_default_value = get_documentation_default_value(ClassDB::get_parent_class(name), E.name, base_default_value_valid);
- if (!default_value_valid || !base_default_value_valid || default_value == base_default_value) {
+
+ bool default_value_valid = false;
+ Variant default_value;
+
+ if (name == "EditorSettings") {
+ if (E.name == "resource_local_to_scene" || E.name == "resource_name" || E.name == "resource_path" || E.name == "script") {
+ // Don't include spurious properties in the generated EditorSettings class reference.
continue;
}
}
- }
- if (default_value_valid && default_value.get_type() != Variant::OBJECT) {
- prop.default_value = default_value.get_construct_string().replace("\n", " ");
- }
-
- StringName setter = ClassDB::get_property_setter(name, E.name);
- StringName getter = ClassDB::get_property_getter(name, E.name);
-
- prop.setter = setter;
- prop.getter = getter;
-
- bool found_type = false;
- if (getter != StringName()) {
- MethodBind *mb = ClassDB::get_method(name, getter);
- if (mb) {
- PropertyInfo retinfo = mb->get_return_info();
-
- found_type = true;
- if (retinfo.type == Variant::INT && retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
- prop.enumeration = retinfo.class_name;
- prop.type = "int";
- } else if (retinfo.class_name != StringName()) {
- prop.type = retinfo.class_name;
- } else if (retinfo.type == Variant::ARRAY && retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
- prop.type = retinfo.hint_string + "[]";
- } else if (retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- prop.type = retinfo.hint_string;
- } else if (retinfo.type == Variant::NIL && retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
- prop.type = "Variant";
- } else if (retinfo.type == Variant::NIL) {
- prop.type = "void";
- } else {
- prop.type = Variant::get_type_name(retinfo.type);
+ if (name == "ProjectSettings") {
+ // Special case for project settings, so that settings are not taken from the current project's settings
+ if (E.name == "script" || !ProjectSettings::get_singleton()->is_builtin_setting(E.name)) {
+ continue;
+ }
+ if (E.usage & PROPERTY_USAGE_EDITOR) {
+ if (!ProjectSettings::get_singleton()->get_ignore_value_in_docs(E.name)) {
+ default_value = ProjectSettings::get_singleton()->property_get_revert(E.name);
+ default_value_valid = true;
+ }
+ }
+ } else {
+ default_value = get_documentation_default_value(name, E.name, default_value_valid);
+ if (inherited) {
+ bool base_default_value_valid = false;
+ Variant base_default_value = get_documentation_default_value(ClassDB::get_parent_class(name), E.name, base_default_value_valid);
+ if (!default_value_valid || !base_default_value_valid || default_value == base_default_value) {
+ continue;
+ }
}
}
- setters_getters.insert(getter);
- }
+ if (default_value_valid && default_value.get_type() != Variant::OBJECT) {
+ prop.default_value = default_value.get_construct_string().replace("\n", " ");
+ }
- if (setter != StringName()) {
- setters_getters.insert(setter);
- }
+ StringName setter = ClassDB::get_property_setter(name, E.name);
+ StringName getter = ClassDB::get_property_getter(name, E.name);
+
+ prop.setter = setter;
+ prop.getter = getter;
+
+ bool found_type = false;
+ if (getter != StringName()) {
+ MethodBind *mb = ClassDB::get_method(name, getter);
+ if (mb) {
+ PropertyInfo retinfo = mb->get_return_info();
+
+ found_type = true;
+ if (retinfo.type == Variant::INT && retinfo.usage & (PROPERTY_USAGE_CLASS_IS_ENUM | PROPERTY_USAGE_CLASS_IS_BITFIELD)) {
+ prop.enumeration = retinfo.class_name;
+ prop.type = "int";
+ } else if (retinfo.class_name != StringName()) {
+ prop.type = retinfo.class_name;
+ } else if (retinfo.type == Variant::ARRAY && retinfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
+ prop.type = retinfo.hint_string + "[]";
+ } else if (retinfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ prop.type = retinfo.hint_string;
+ } else if (retinfo.type == Variant::NIL && retinfo.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
+ prop.type = "Variant";
+ } else if (retinfo.type == Variant::NIL) {
+ prop.type = "void";
+ } else {
+ prop.type = Variant::get_type_name(retinfo.type);
+ }
+ }
- if (!found_type) {
- if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) {
- prop.type = E.hint_string;
- } else {
- prop.type = Variant::get_type_name(E.type);
+ setters_getters.insert(getter);
}
- }
- c.properties.push_back(prop);
- }
+ if (setter != StringName()) {
+ setters_getters.insert(setter);
+ }
- List<MethodInfo> method_list;
- ClassDB::get_method_list(name, &method_list, true);
- method_list.sort();
+ if (!found_type) {
+ if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) {
+ prop.type = E.hint_string;
+ } else {
+ prop.type = Variant::get_type_name(E.type);
+ }
+ }
- for (const MethodInfo &E : method_list) {
- if (E.name.is_empty() || (E.name[0] == '_' && !(E.flags & METHOD_FLAG_VIRTUAL))) {
- continue; //hidden, don't count
+ c.properties.push_back(prop);
}
- if (skip_setter_getter_methods && setters_getters.has(E.name)) {
- // Don't skip parametric setters and getters, i.e. method which require
- // one or more parameters to define what property should be set or retrieved.
- // E.g. CPUParticles3D::set_param(Parameter param, float value).
- if (E.arguments.size() == 0 /* getter */ || (E.arguments.size() == 1 && E.return_val.type == Variant::NIL /* setter */)) {
- continue;
- }
- }
+ List<MethodInfo> method_list;
+ ClassDB::get_method_list(name, &method_list, true);
+ method_list.sort();
- DocData::MethodDoc method;
- DocData::method_doc_from_methodinfo(method, E, "");
+ for (const MethodInfo &E : method_list) {
+ if (E.name.is_empty() || (E.name[0] == '_' && !(E.flags & METHOD_FLAG_VIRTUAL))) {
+ continue; //hidden, don't count
+ }
- Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name);
- if (errs.size()) {
- if (!errs.has(OK)) {
- errs.insert(0, OK);
+ if (skip_setter_getter_methods && setters_getters.has(E.name)) {
+ // Don't skip parametric setters and getters, i.e. method which require
+ // one or more parameters to define what property should be set or retrieved.
+ // E.g. CPUParticles3D::set_param(Parameter param, float value).
+ if (E.arguments.size() == 0 /* getter */ || (E.arguments.size() == 1 && E.return_val.type == Variant::NIL /* setter */)) {
+ continue;
+ }
}
- for (int i = 0; i < errs.size(); i++) {
- if (!method.errors_returned.has(errs[i])) {
- method.errors_returned.push_back(errs[i]);
+
+ DocData::MethodDoc method;
+ DocData::method_doc_from_methodinfo(method, E, "");
+
+ Vector<Error> errs = ClassDB::get_method_error_return_values(name, E.name);
+ if (errs.size()) {
+ if (!errs.has(OK)) {
+ errs.insert(0, OK);
+ }
+ for (int i = 0; i < errs.size(); i++) {
+ if (!method.errors_returned.has(errs[i])) {
+ method.errors_returned.push_back(errs[i]);
+ }
}
}
+
+ c.methods.push_back(method);
}
- c.methods.push_back(method);
- }
+ List<MethodInfo> signal_list;
+ ClassDB::get_signal_list(name, &signal_list, true);
- List<MethodInfo> signal_list;
- ClassDB::get_signal_list(name, &signal_list, true);
+ if (signal_list.size()) {
+ for (List<MethodInfo>::Element *EV = signal_list.front(); EV; EV = EV->next()) {
+ DocData::MethodDoc signal;
+ signal.name = EV->get().name;
+ for (int i = 0; i < EV->get().arguments.size(); i++) {
+ const PropertyInfo &arginfo = EV->get().arguments[i];
+ DocData::ArgumentDoc argument;
+ DocData::argument_doc_from_arginfo(argument, arginfo);
- if (signal_list.size()) {
- for (List<MethodInfo>::Element *EV = signal_list.front(); EV; EV = EV->next()) {
- DocData::MethodDoc signal;
- signal.name = EV->get().name;
- for (int i = 0; i < EV->get().arguments.size(); i++) {
- const PropertyInfo &arginfo = EV->get().arguments[i];
- DocData::ArgumentDoc argument;
- DocData::argument_doc_from_arginfo(argument, arginfo);
+ signal.arguments.push_back(argument);
+ }
- signal.arguments.push_back(argument);
+ c.signals.push_back(signal);
}
-
- c.signals.push_back(signal);
}
- }
- List<String> constant_list;
- ClassDB::get_integer_constant_list(name, &constant_list, true);
+ List<String> constant_list;
+ ClassDB::get_integer_constant_list(name, &constant_list, true);
+
+ for (const String &E : constant_list) {
+ DocData::ConstantDoc constant;
+ constant.name = E;
+ constant.value = itos(ClassDB::get_integer_constant(name, E));
+ constant.is_value_valid = true;
+ constant.enumeration = ClassDB::get_integer_constant_enum(name, E);
+ constant.is_bitfield = ClassDB::is_enum_bitfield(name, constant.enumeration);
+ c.constants.push_back(constant);
+ }
- for (const String &E : constant_list) {
- DocData::ConstantDoc constant;
- constant.name = E;
- constant.value = itos(ClassDB::get_integer_constant(name, E));
- constant.is_value_valid = true;
- constant.enumeration = ClassDB::get_integer_constant_enum(name, E);
- constant.is_bitfield = ClassDB::is_enum_bitfield(name, constant.enumeration);
- c.constants.push_back(constant);
- }
+ // Theme items.
+ {
+ List<StringName> l;
+
+ Theme::get_default()->get_color_list(cname, &l);
+ for (const StringName &E : l) {
+ DocData::ThemeItemDoc tid;
+ tid.name = E;
+ tid.type = "Color";
+ tid.data_type = "color";
+ tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string().replace("\n", " ");
+ c.theme_properties.push_back(tid);
+ }
- // Theme items.
- {
- List<StringName> l;
-
- Theme::get_default()->get_color_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "Color";
- tid.data_type = "color";
- tid.default_value = Variant(Theme::get_default()->get_color(E, cname)).get_construct_string().replace("\n", " ");
- c.theme_properties.push_back(tid);
- }
+ l.clear();
+ Theme::get_default()->get_constant_list(cname, &l);
+ for (const StringName &E : l) {
+ DocData::ThemeItemDoc tid;
+ tid.name = E;
+ tid.type = "int";
+ tid.data_type = "constant";
+ tid.default_value = itos(Theme::get_default()->get_constant(E, cname));
+ c.theme_properties.push_back(tid);
+ }
- l.clear();
- Theme::get_default()->get_constant_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "int";
- tid.data_type = "constant";
- tid.default_value = itos(Theme::get_default()->get_constant(E, cname));
- c.theme_properties.push_back(tid);
- }
+ l.clear();
+ Theme::get_default()->get_font_list(cname, &l);
+ for (const StringName &E : l) {
+ DocData::ThemeItemDoc tid;
+ tid.name = E;
+ tid.type = "Font";
+ tid.data_type = "font";
+ c.theme_properties.push_back(tid);
+ }
- l.clear();
- Theme::get_default()->get_font_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "Font";
- tid.data_type = "font";
- c.theme_properties.push_back(tid);
- }
+ l.clear();
+ Theme::get_default()->get_font_size_list(cname, &l);
+ for (const StringName &E : l) {
+ DocData::ThemeItemDoc tid;
+ tid.name = E;
+ tid.type = "int";
+ tid.data_type = "font_size";
+ c.theme_properties.push_back(tid);
+ }
- l.clear();
- Theme::get_default()->get_font_size_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "int";
- tid.data_type = "font_size";
- c.theme_properties.push_back(tid);
- }
+ l.clear();
+ Theme::get_default()->get_icon_list(cname, &l);
+ for (const StringName &E : l) {
+ DocData::ThemeItemDoc tid;
+ tid.name = E;
+ tid.type = "Texture2D";
+ tid.data_type = "icon";
+ c.theme_properties.push_back(tid);
+ }
- l.clear();
- Theme::get_default()->get_icon_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "Texture2D";
- tid.data_type = "icon";
- c.theme_properties.push_back(tid);
- }
+ l.clear();
+ Theme::get_default()->get_stylebox_list(cname, &l);
+ for (const StringName &E : l) {
+ DocData::ThemeItemDoc tid;
+ tid.name = E;
+ tid.type = "StyleBox";
+ tid.data_type = "style";
+ c.theme_properties.push_back(tid);
+ }
- l.clear();
- Theme::get_default()->get_stylebox_list(cname, &l);
- for (const StringName &E : l) {
- DocData::ThemeItemDoc tid;
- tid.name = E;
- tid.type = "StyleBox";
- tid.data_type = "style";
- c.theme_properties.push_back(tid);
+ c.theme_properties.sort();
}
- c.theme_properties.sort();
+ classes.pop_front();
}
-
- classes.pop_front();
}
+ // Add a dummy Variant entry.
{
- // So we can document the concept of Variant even if it's not a usable class per se.
+ // This allows us to document the concept of Variant even though
+ // it's not a ClassDB-exposed class.
class_list["Variant"] = DocData::ClassDoc();
class_list["Variant"].name = "Variant";
}
+ // If we don't want to populate basic types, break here.
if (!p_basic_types) {
return;
}
- // Add Variant types.
+ // Add Variant data types.
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
if (i == Variant::NIL) {
continue; // Not exposed outside of 'null', should not be in class list.
@@ -809,14 +816,14 @@ void DocTools::generate(bool p_basic_types) {
}
}
- //built in constants and functions
-
+ // Add global API (servers, engine singletons, global constants) and Variant utility functions.
{
String cname = "@GlobalScope";
class_list[cname] = DocData::ClassDoc();
DocData::ClassDoc &c = class_list[cname];
c.name = cname;
+ // Global constants.
for (int i = 0; i < CoreConstants::get_global_constant_count(); i++) {
DocData::ConstantDoc cd;
cd.name = CoreConstants::get_global_constant_name(i);
@@ -830,10 +837,11 @@ void DocTools::generate(bool p_basic_types) {
c.constants.push_back(cd);
}
+ // Servers/engine singletons.
List<Engine::Singleton> singletons;
Engine::get_singleton()->get_singletons(&singletons);
- //servers (this is kind of hackish)
+ // FIXME: this is kind of hackish...
for (const Engine::Singleton &s : singletons) {
DocData::PropertyDoc pd;
if (!s.ptr) {
@@ -847,13 +855,14 @@ void DocTools::generate(bool p_basic_types) {
c.properties.push_back(pd);
}
+ // Variant utility functions.
List<StringName> utility_functions;
Variant::get_utility_function_list(&utility_functions);
utility_functions.sort_custom<StringName::AlphCompare>();
for (const StringName &E : utility_functions) {
DocData::MethodDoc md;
md.name = E;
- //return
+ // Utility function's return type.
if (Variant::has_utility_function_return_value(E)) {
PropertyInfo pi;
pi.type = Variant::get_utility_function_return_type(E);
@@ -865,6 +874,7 @@ void DocTools::generate(bool p_basic_types) {
md.return_type = ad.type;
}
+ // Utility function's arguments.
if (Variant::is_utility_function_vararg(E)) {
md.qualifiers = "vararg";
} else {
@@ -885,11 +895,10 @@ void DocTools::generate(bool p_basic_types) {
}
}
- // Built-in script reference.
- // We only add a doc entry for languages which actually define any built-in
- // methods or constants.
-
+ // Add scripting language built-ins.
{
+ // We only add a doc entry for languages which actually define any built-in
+ // methods, constants, or annotations.
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
ScriptLanguage *lang = ScriptServer::get_language(i);
String cname = "@" + lang->get_name();
@@ -1010,7 +1019,7 @@ static Error _parse_methods(Ref<XMLParser> &parser, Vector<DocData::MethodDoc> &
} else if (name == "returns_error") {
ERR_FAIL_COND_V(!parser->has_attribute("number"), ERR_FILE_CORRUPT);
method.errors_returned.push_back(parser->get_attribute_value("number").to_int());
- } else if (name == "argument") {
+ } else if (name == "param") {
DocData::ArgumentDoc argument;
ERR_FAIL_COND_V(!parser->has_attribute("name"), ERR_FILE_CORRUPT);
argument.name = parser->get_attribute_value("name");
@@ -1341,9 +1350,9 @@ static void _write_method_doc(Ref<FileAccess> f, const String &p_name, Vector<Do
}
if (!a.default_value.is_empty()) {
- _write_string(f, 3, "<argument index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\"" + enum_text + " default=\"" + a.default_value.xml_escape(true) + "\" />");
+ _write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\"" + enum_text + " default=\"" + a.default_value.xml_escape(true) + "\" />");
} else {
- _write_string(f, 3, "<argument index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\"" + enum_text + " />");
+ _write_string(f, 3, "<param index=\"" + itos(j) + "\" name=\"" + a.name.xml_escape() + "\" type=\"" + a.type.xml_escape() + "\"" + enum_text + " />");
}
}
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 5c3038c468..7fa4303145 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -1785,7 +1785,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(" ");
@@ -1803,6 +1803,21 @@ static void _add_text_to_rt(const String &p_bbcode, RichTextLabel *p_rt) {
p_rt->pop();
pos = brk_end + 1;
+ } else if (tag.begins_with("param ")) {
+ const int tag_end = tag.find(" ");
+ const String param_name = tag.substr(tag_end + 1, tag.length()).lstrip(" ");
+
+ // Use monospace font with translucent background color to make code easier to distinguish from other text.
+ p_rt->push_font(doc_code_font);
+ p_rt->push_bgcolor(Color(0.5, 0.5, 0.5, 0.15));
+ p_rt->push_color(code_color);
+ p_rt->add_text(param_name);
+ p_rt->pop();
+ p_rt->pop();
+ p_rt->pop();
+
+ pos = brk_end + 1;
+
} else if (doc->class_list.has(tag)) {
// Class reference tag such as [Node2D] or [SceneTree].
// Use monospace font with translucent colored background color to make clickable references
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 2064b6f3a1..e954f06f08 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -432,11 +432,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);
@@ -2662,7 +2662,7 @@ void EditorInspector::update_tree() {
_parse_added_editors(main_vbox, nullptr, ped);
}
- StringName type_name;
+ StringName doc_name;
// Get the lists of editors for properties.
for (List<PropertyInfo>::Element *E_property = plist.front(); E_property; E_property = E_property->next()) {
@@ -2735,7 +2735,7 @@ void EditorInspector::update_tree() {
String type = p.name;
String label = p.name;
- type_name = p.name;
+ doc_name = p.name;
// Set the category icon.
if (!ClassDB::class_exists(type) && !ScriptServer::is_global_class(type) && p.hint_string.length() && FileAccess::exists(p.hint_string)) {
@@ -2746,6 +2746,10 @@ void EditorInspector::update_tree() {
if (script.is_valid()) {
base_type = script->get_instance_base_type();
name = EditorNode::get_editor_data().script_class_get_name(script->get_path());
+ Vector<DocData::ClassDoc> docs = script->get_documentation();
+ if (!docs.is_empty()) {
+ doc_name = docs[0].name;
+ }
if (name != StringName() && label != name) {
label = name;
}
@@ -2774,17 +2778,17 @@ void EditorInspector::update_tree() {
if (use_doc_hints) {
// Sets the category tooltip to show documentation.
- if (!class_descr_cache.has(type_name)) {
+ if (!class_descr_cache.has(doc_name)) {
String descr;
DocTools *dd = EditorHelp::get_doc_data();
- HashMap<String, DocData::ClassDoc>::Iterator E = dd->class_list.find(type_name);
+ HashMap<String, DocData::ClassDoc>::Iterator E = dd->class_list.find(doc_name);
if (E) {
descr = DTR(E->value.brief_description);
}
- class_descr_cache[type_name] = descr;
+ class_descr_cache[doc_name] = descr;
}
- category->set_tooltip(p.name + "::" + (class_descr_cache[type_name].is_empty() ? "" : class_descr_cache[type_name]));
+ category->set_tooltip(p.name + "::" + (class_descr_cache[doc_name].is_empty() ? "" : class_descr_cache[doc_name]));
}
// Add editors at the start of a category.
@@ -3082,7 +3086,7 @@ void EditorInspector::update_tree() {
// Build the doc hint, to use as tooltip.
// Get the class name.
- StringName classname = type_name == "" ? object->get_class_name() : type_name;
+ StringName classname = doc_name == "" ? object->get_class_name() : doc_name;
if (!object_class.is_empty()) {
classname = object_class;
}
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 38cc85bb4e..8aa099ddff 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -93,6 +93,12 @@ void EditorLog::_update_theme() {
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) {
@@ -264,22 +270,22 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
case MSG_TYPE_STD_RICH: {
} break;
case MSG_TYPE_ERROR: {
- log->push_color(get_theme_color(SNAME("error_color"), SNAME("Editor")));
- Ref<Texture2D> icon = get_theme_icon(SNAME("Error"), SNAME("EditorIcons"));
+ log->push_color(theme_cache.error_color);
+ Ref<Texture2D> icon = theme_cache.error_icon;
log->add_image(icon);
log->add_text(" ");
tool_button->set_icon(icon);
} break;
case MSG_TYPE_WARNING: {
- log->push_color(get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- Ref<Texture2D> icon = get_theme_icon(SNAME("Warning"), SNAME("EditorIcons"));
+ log->push_color(theme_cache.warning_color);
+ Ref<Texture2D> icon = theme_cache.warning_icon;
log->add_image(icon);
log->add_text(" ");
tool_button->set_icon(icon);
} break;
case MSG_TYPE_EDITOR: {
// Distinguish editor messages from messages printed by the project
- log->push_color(get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.6));
+ log->push_color(theme_cache.message_color);
} break;
}
diff --git a/editor/editor_log.h b/editor/editor_log.h
index 003a148b9b..c225e6d8c5 100644
--- a/editor/editor_log.h
+++ b/editor/editor_log.h
@@ -67,6 +67,16 @@ private:
}
};
+ struct {
+ Color error_color;
+ Ref<Texture2D> error_icon;
+
+ Color warning_color;
+ Ref<Texture2D> warning_icon;
+
+ Color message_color;
+ } theme_cache;
+
// Encapsulates all data and functionality regarding filters.
struct LogFilter {
private:
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index e7946f56da..2941ae6695 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"
@@ -751,11 +752,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 +800,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")));
@@ -1942,6 +1938,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)) {
@@ -2396,10 +2407,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 +2423,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"))) {
@@ -3181,17 +3191,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());
+ 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() {
@@ -4102,8 +4110,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());
+ }
}
}
@@ -6130,7 +6145,7 @@ EditorNode::EditorNode() {
rmp.instantiate();
EditorInspector::add_inspector_plugin(rmp);
- Ref<EditorInspectorShaderModePlugin> smp;
+ Ref<EditorInspectorVisualShaderModePlugin> smp;
smp.instantiate();
EditorInspector::add_inspector_plugin(smp);
}
@@ -6450,15 +6465,20 @@ 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);
- 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);
+ 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);
@@ -6528,84 +6548,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."));
-
- p = file_menu->get_popup();
+ 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->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);
-
- 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);
+ 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_separator();
+ file_menu->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 +6625,15 @@ 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);
menu_hb->add_spacer();
@@ -6630,85 +6641,79 @@ 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);
+
+ Control *right_spacer = memnew(Control);
+ menu_hb->add_child(right_spacer);
HBoxContainer *play_hb = memnew(HBoxContainer);
menu_hb->add_child(play_hb);
@@ -6857,7 +6862,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();
@@ -7077,11 +7082,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));
@@ -7364,11 +7369,14 @@ 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());
+ main_menu->set_custom_minimum_size(Size2(MAX(main_menu->get_minimum_size().x, play_hb->get_minimum_size().x + right_menu_hb->get_minimum_size().x), 0));
+ right_spacer->set_custom_minimum_size(Size2(MAX(0, main_menu->get_minimum_size().x - play_hb->get_minimum_size().x - right_menu_hb->get_minimum_size().x), 0));
+
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..f7a102b4c7 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -78,6 +78,7 @@ class FileSystemDock;
class HSplitContainer;
class ImportDock;
class LinkButton;
+class MenuBar;
class MenuButton;
class NodeDock;
class OrphanResourcesDialog;
@@ -141,6 +142,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,
@@ -321,11 +323,12 @@ private:
HBoxContainer *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;
diff --git a/editor/editor_path.cpp b/editor/editor_path.cpp
index dc77b5fea9..87ebd3e1c1 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);
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 566c22f5a9..400ad1ebac 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -445,7 +445,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 +498,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;
@@ -950,7 +950,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);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 84c63d1021..3f9d276b6a 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -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 5e66d436fc..f434df3a1e 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -1147,6 +1147,17 @@ void EditorPropertyLayersGrid::_bind_methods() {
ADD_SIGNAL(MethodInfo("rename_confirmed", PropertyInfo(Variant::INT, "layer_id"), PropertyInfo(Variant::STRING, "new_name")));
}
+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")));
+ button->set_disabled_texture(get_theme_icon(SNAME("GuiTabMenu"), SNAME("EditorIcons")));
+ } break;
+ }
+}
+
void EditorPropertyLayers::_set_read_only(bool p_read_only) {
button->set_disabled(p_read_only);
grid->set_read_only(p_read_only);
@@ -1283,9 +1294,9 @@ EditorPropertyLayers::EditorPropertyLayers() {
grid->set_h_size_flags(SIZE_EXPAND_FILL);
hb->add_child(grid);
- button = memnew(Button);
+ button = memnew(TextureButton);
+ button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
button->set_toggle_mode(true);
- button->set_text("...");
button->connect("pressed", callable_mp(this, &EditorPropertyLayers::_button_pressed));
hb->add_child(button);
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 5c73a53184..c1dfb5cb1e 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -342,13 +342,14 @@ private:
String basename;
LayerType layer_type;
PopupMenu *layers = nullptr;
- Button *button = nullptr;
+ TextureButton *button = nullptr;
void _button_pressed();
void _menu_pressed(int p_menu);
void _refresh_names();
protected:
+ void _notification(int p_what);
virtual void _set_read_only(bool p_read_only) override;
static void _bind_methods();
diff --git a/editor/editor_sectioned_inspector.cpp b/editor/editor_sectioned_inspector.cpp
index 801a1a4641..cbca3e9dcd 100644
--- a/editor/editor_sectioned_inspector.cpp
+++ b/editor/editor_sectioned_inspector.cpp
@@ -114,11 +114,11 @@ class SectionedInspectorFilter : public Object {
}
bool property_can_revert(const String &p_name) {
- return edited->call("property_can_revert", section + "/" + 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);
+ return edited->property_get_revert(section + "/" + p_name);
}
protected:
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 80e77a1125..36e64cbd7a 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -406,6 +406,7 @@ 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::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")
@@ -1073,24 +1074,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) {
@@ -1621,8 +1623,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..f921171c57 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,8 +140,6 @@ 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;
bool check_changed_settings_in_group(const String &p_setting_prefix) const;
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index a3086a2ccf..3da9899052 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -788,6 +788,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 +931,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();
@@ -1262,7 +1284,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_stylebox("focus", "LineEdit", style_widget_focus);
theme->set_stylebox("read_only", "LineEdit", style_line_edit_disabled);
theme->set_icon("clear", "LineEdit", theme->get_icon(SNAME("GuiClose"), SNAME("EditorIcons")));
- theme->set_color("read_only", "LineEdit", font_disabled_color);
theme->set_color("font_color", "LineEdit", font_color);
theme->set_color("font_selected_color", "LineEdit", mono_color);
theme->set_color("font_uneditable_color", "LineEdit", font_readonly_color);
@@ -1455,12 +1476,23 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_tooltip->set_bg_color(dark_color_3 * Color(0.8, 0.8, 0.8, 0.9));
style_tooltip->set_border_width_all(0);
theme->set_color("font_color", "TooltipLabel", font_hover_color);
- theme->set_color("font_color_shadow", "TooltipLabel", Color(0, 0, 0, 0));
+ theme->set_color("font_shadow_color", "TooltipLabel", Color(0, 0, 0, 0));
theme->set_stylebox("panel", "TooltipPanel", style_tooltip);
// 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")));
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 34b407779e..ab1586cb77 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -429,24 +429,21 @@ void EditorExportPlatform::_edit_filter_list(HashSet<String> &r_list, const Stri
_edit_files_with_filter(da, filters, r_list, exclude);
}
-EditorExportPlatform::FeatureContainers EditorExportPlatform::get_feature_containers(const Ref<EditorExportPreset> &p_preset, bool p_debug) const {
+HashSet<String> EditorExportPlatform::get_features(const Ref<EditorExportPreset> &p_preset, bool p_debug) const {
Ref<EditorExportPlatform> platform = p_preset->get_platform();
List<String> feature_list;
platform->get_platform_features(&feature_list);
platform->get_preset_features(p_preset, &feature_list);
- FeatureContainers result;
+ HashSet<String> result;
for (const String &E : feature_list) {
- result.features.insert(E);
- result.features_pv.push_back(E);
+ result.insert(E);
}
if (p_debug) {
- result.features.insert("debug");
- result.features_pv.push_back("debug");
+ result.insert("debug");
} else {
- result.features.insert("release");
- result.features_pv.push_back("release");
+ result.insert("release");
}
if (!p_preset->get_custom_features().is_empty()) {
@@ -455,8 +452,7 @@ EditorExportPlatform::FeatureContainers EditorExportPlatform::get_feature_contai
for (int i = 0; i < tmp_custom_list.size(); i++) {
String f = tmp_custom_list[i].strip_edges();
if (!f.is_empty()) {
- result.features.insert(f);
- result.features_pv.push_back(f);
+ result.insert(f);
}
}
}
@@ -465,14 +461,18 @@ EditorExportPlatform::FeatureContainers EditorExportPlatform::get_feature_contai
}
EditorExportPlatform::ExportNotifier::ExportNotifier(EditorExportPlatform &p_platform, const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
- FeatureContainers features = p_platform.get_feature_containers(p_preset, p_debug);
+ HashSet<String> features = p_platform.get_features(p_preset, p_debug);
Vector<Ref<EditorExportPlugin>> export_plugins = EditorExport::get_singleton()->get_export_plugins();
//initial export plugin callback
for (int i = 0; i < export_plugins.size(); i++) {
if (export_plugins[i]->get_script_instance()) { //script based
- export_plugins.write[i]->_export_begin_script(features.features_pv, p_debug, p_path, p_flags);
+ PackedStringArray features_psa;
+ for (const String &feature : features) {
+ features_psa.push_back(feature);
+ }
+ export_plugins.write[i]->_export_begin_script(features_psa, p_debug, p_path, p_flags);
} else {
- export_plugins.write[i]->_export_begin(features.features, p_debug, p_path, p_flags);
+ export_plugins.write[i]->_export_begin(features, p_debug, p_path, p_flags);
}
}
}
@@ -621,9 +621,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
export_plugins.write[i]->_clear();
}
- FeatureContainers feature_containers = get_feature_containers(p_preset, p_debug);
- HashSet<String> &features = feature_containers.features;
- Vector<String> &features_pv = feature_containers.features_pv;
+ HashSet<String> features = get_features(p_preset, p_debug);
//store everything in the export medium
int idx = 0;
@@ -709,7 +707,11 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
bool do_export = true;
for (int i = 0; i < export_plugins.size(); i++) {
if (export_plugins[i]->get_script_instance()) { //script based
- export_plugins.write[i]->_export_file_script(path, type, features_pv);
+ PackedStringArray features_psa;
+ for (const String &feature : features) {
+ features_psa.push_back(feature);
+ }
+ export_plugins.write[i]->_export_file_script(path, type, features_psa);
} else {
export_plugins.write[i]->_export_file(path, type, features);
}
@@ -1174,5 +1176,23 @@ void EditorExportPlatform::gen_export_flags(Vector<String> &r_flags, int p_flags
}
}
+bool EditorExportPlatform::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+ String templates_error;
+ bool valid_export_configuration = has_valid_export_configuration(p_preset, templates_error, r_missing_templates);
+
+ String project_configuration_error;
+ bool valid_project_configuration = has_valid_project_configuration(p_preset, project_configuration_error);
+
+ if (!templates_error.is_empty()) {
+ r_error += templates_error;
+ }
+
+ if (!project_configuration_error.is_empty()) {
+ r_error += project_configuration_error;
+ }
+
+ return valid_export_configuration && valid_project_configuration;
+}
+
EditorExportPlatform::EditorExportPlatform() {
}
diff --git a/editor/export/editor_export_platform.h b/editor/export/editor_export_platform.h
index 832a0cf846..c870ee66aa 100644
--- a/editor/export/editor_export_platform.h
+++ b/editor/export/editor_export_platform.h
@@ -85,11 +85,6 @@ private:
EditorProgress *ep = nullptr;
};
- struct FeatureContainers {
- HashSet<String> features;
- Vector<String> features_pv;
- };
-
Vector<ExportMessage> messages;
void _export_find_resources(EditorFileSystemDirectory *p_dir, HashSet<String> &p_paths);
@@ -110,7 +105,7 @@ protected:
~ExportNotifier();
};
- FeatureContainers get_feature_containers(const Ref<EditorExportPreset> &p_preset, bool p_debug) const;
+ HashSet<String> get_features(const Ref<EditorExportPreset> &p_preset, bool p_debug) const;
bool exists_export_template(String template_file_name, String *err) const;
String find_export_template(String template_file_name, String *err = nullptr) const;
@@ -205,7 +200,9 @@ public:
virtual Ref<Texture2D> get_run_icon() const { return get_logo(); }
String test_etc2() const;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0;
+ bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const;
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const = 0;
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const = 0;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const = 0;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) = 0;
diff --git a/editor/export/editor_export_platform_pc.cpp b/editor/export/editor_export_platform_pc.cpp
index 5e0044f2ae..9fca4c908a 100644
--- a/editor/export/editor_export_platform_pc.cpp
+++ b/editor/export/editor_export_platform_pc.cpp
@@ -75,7 +75,7 @@ Ref<Texture2D> EditorExportPlatformPC::get_logo() const {
return logo;
}
-bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+bool EditorExportPlatformPC::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
String err;
bool valid = false;
@@ -106,6 +106,10 @@ bool EditorExportPlatformPC::can_export(const Ref<EditorExportPreset> &p_preset,
return valid;
}
+bool EditorExportPlatformPC::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
+ return true;
+}
+
Error EditorExportPlatformPC::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
ExportNotifier notifier(*this, p_preset, p_debug, p_path, p_flags);
diff --git a/editor/export/editor_export_platform_pc.h b/editor/export/editor_export_platform_pc.h
index bdb86e924a..cf96db6c2d 100644
--- a/editor/export/editor_export_platform_pc.h
+++ b/editor/export/editor_export_platform_pc.h
@@ -52,7 +52,8 @@ public:
virtual String get_os_name() const override;
virtual Ref<Texture2D> get_logo() const override;
- virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const override;
+ virtual bool has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const override;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
virtual Error sign_shared_object(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path);
virtual String get_template_file_name(const String &p_target, const String &p_arch) const = 0;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index cb82cefbbb..76493d330f 100644
--- a/editor/export/project_export.cpp
+++ b/editor/export/project_export.cpp
@@ -219,6 +219,7 @@ void ProjectExportDialog::_edit_preset(int p_index) {
export_path->show();
duplicate_preset->set_disabled(false);
delete_preset->set_disabled(false);
+ get_ok_button()->set_disabled(false);
name->set_text(current->get_name());
List<String> extension_list = current->get_platform()->get_binary_extensions(current);
@@ -265,7 +266,6 @@ void ProjectExportDialog::_edit_preset(int p_index) {
export_warning->hide();
export_button->set_disabled(true);
- get_ok_button()->set_disabled(true);
} else {
if (error != String()) {
Vector<String> items = error.split("\n", false);
@@ -285,7 +285,6 @@ void ProjectExportDialog::_edit_preset(int p_index) {
export_error->hide();
export_templates_error->hide();
export_button->set_disabled(false);
- get_ok_button()->set_disabled(false);
}
custom_features->set_text(current->get_custom_features());
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/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/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/import/resource_importer_layered_texture.cpp b/editor/import/resource_importer_layered_texture.cpp
index 2e6de95a46..b301bbf0f9 100644
--- a/editor/import/resource_importer_layered_texture.cpp
+++ b/editor/import/resource_importer_layered_texture.cpp
@@ -139,7 +139,7 @@ void ResourceImporterLayeredTexture::get_import_options(const String &p_path, Li
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "compress/lossy_quality", PROPERTY_HINT_RANGE, "0,1,0.01"), 0.7));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/hdr_compression", PROPERTY_HINT_ENUM, "Disabled,Opaque Only,Always"), 1));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/bptc_ldr", PROPERTY_HINT_ENUM, "Disabled,Enabled,RGBA Only"), 0));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized"), 0));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/channel_pack", PROPERTY_HINT_ENUM, "sRGB Friendly,Optimized,Normal Map (RG Channels)"), 0));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "mipmaps/generate"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "mipmaps/limit", PROPERTY_HINT_RANGE, "-1,256"), -1));
@@ -250,7 +250,7 @@ void ResourceImporterLayeredTexture::_save_tex(Vector<Ref<Image>> p_images, cons
}
if (p_mipmaps) {
- p_images.write[i]->generate_mipmaps();
+ p_images.write[i]->generate_mipmaps(p_csource == Image::COMPRESS_SOURCE_NORMAL);
} else {
p_images.write[i]->clear_mipmaps();
}
@@ -354,6 +354,9 @@ Error ResourceImporterLayeredTexture::import(const String &p_source_file, const
Image::CompressSource csource = Image::COMPRESS_SOURCE_GENERIC;
if (channel_pack == 0) {
csource = Image::COMPRESS_SOURCE_SRGB;
+ } else if (channel_pack == 2) {
+ // force normal
+ csource = Image::COMPRESS_SOURCE_NORMAL;
}
Image::UsedChannels used_channels = image->detect_used_channels(csource);
diff --git a/editor/plugins/animation_library_editor.cpp b/editor/plugins/animation_library_editor.cpp
index cae33edecb..c36ae1c521 100644
--- a/editor/plugins/animation_library_editor.cpp
+++ b/editor/plugins/animation_library_editor.cpp
@@ -149,13 +149,35 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) {
}
switch (p_id) {
case FILE_MENU_SAVE_LIBRARY: {
- if (al->get_path().is_resource_file()) {
+ if (al->get_path().is_resource_file() && !FileAccess::exists(al->get_path() + ".import")) {
EditorNode::get_singleton()->save_resource(al);
break;
}
[[fallthrough]];
}
case FILE_MENU_SAVE_AS_LIBRARY: {
+ // Check if we're allowed to save this
+ {
+ String al_path = al->get_path();
+ if (!al_path.is_resource_file()) {
+ int srpos = al_path.find("::");
+ if (srpos != -1) {
+ String base = al_path.substr(0, srpos);
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ error_dialog->set_text(TTR("This animation library can't be saved because it does not belong to the edited scene. Make it unique first."));
+ error_dialog->popup_centered();
+ return;
+ }
+ }
+ } else {
+ if (FileAccess::exists(al_path + ".import")) {
+ error_dialog->set_text(TTR("This animation library can't be saved because it was imported from another file. Make it unique first."));
+ error_dialog->popup_centered();
+ return;
+ }
+ }
+ }
+
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_dialog->set_title(TTR("Save Library"));
if (al->get_path().is_resource_file()) {
@@ -178,6 +200,9 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) {
Ref<AnimationLibrary> ald = al->duplicate();
+ // TODO: should probably make all foreign animations assigned to this library
+ // unique too.
+
UndoRedo *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);
@@ -188,19 +213,43 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) {
undo_redo->add_undo_method(this, "_update_editor", player);
undo_redo->commit_action();
+ update_tree();
+
} break;
case FILE_MENU_EDIT_LIBRARY: {
EditorNode::get_singleton()->push_item(al.ptr());
} break;
case FILE_MENU_SAVE_ANIMATION: {
- if (anim->get_path().is_resource_file()) {
+ if (anim->get_path().is_resource_file() && !FileAccess::exists(anim->get_path() + ".import")) {
EditorNode::get_singleton()->save_resource(anim);
break;
}
[[fallthrough]];
}
case FILE_MENU_SAVE_AS_ANIMATION: {
+ // Check if we're allowed to save this
+ {
+ String anim_path = al->get_path();
+ if (!anim_path.is_resource_file()) {
+ int srpos = anim_path.find("::");
+ if (srpos != -1) {
+ String base = anim_path.substr(0, srpos);
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ error_dialog->set_text(TTR("This animation can't be saved because it does not belong to the edited scene. Make it unique first."));
+ error_dialog->popup_centered();
+ return;
+ }
+ }
+ } else {
+ if (FileAccess::exists(anim_path + ".import")) {
+ error_dialog->set_text(TTR("This animation can't be saved because it was imported from another file. Make it unique first."));
+ error_dialog->popup_centered();
+ return;
+ }
+ }
+ }
+
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
file_dialog->set_title(TTR("Save Animation"));
if (anim->get_path().is_resource_file()) {
@@ -232,6 +281,8 @@ void AnimationLibraryEditor::_file_popup_selected(int p_id) {
undo_redo->add_do_method(this, "_update_editor", player);
undo_redo->add_undo_method(this, "_update_editor", player);
undo_redo->commit_action();
+
+ update_tree();
} break;
case FILE_MENU_EDIT_ANIMATION: {
EditorNode::get_singleton()->push_item(anim.ptr());
@@ -577,19 +628,45 @@ void AnimationLibraryEditor::update_tree() {
} else {
libitem->set_suffix(0, "");
}
- libitem->set_editable(0, true);
- libitem->set_metadata(0, K);
- libitem->set_icon(0, get_theme_icon("AnimationLibrary", "EditorIcons"));
- libitem->add_button(0, get_theme_icon("Add", "EditorIcons"), LIB_BUTTON_ADD, false, TTR("Add Animation to Library"));
- libitem->add_button(0, get_theme_icon("Load", "EditorIcons"), LIB_BUTTON_LOAD, false, TTR("Load animation from file and add to library"));
- libitem->add_button(0, get_theme_icon("ActionPaste", "EditorIcons"), LIB_BUTTON_PASTE, false, TTR("Paste Animation to Library from clipboard"));
+
Ref<AnimationLibrary> al = player->call("get_animation_library", K);
- if (al->get_path().is_resource_file()) {
- libitem->set_text(1, al->get_path().get_file());
- libitem->set_tooltip(1, al->get_path());
- } else {
+ bool animation_library_is_foreign = false;
+ String al_path = al->get_path();
+ if (!al_path.is_resource_file()) {
libitem->set_text(1, TTR("[built-in]"));
+ libitem->set_tooltip(1, al_path);
+ int srpos = al_path.find("::");
+ if (srpos != -1) {
+ String base = al_path.substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ animation_library_is_foreign = true;
+ libitem->set_text(1, TTR("[foreign]"));
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ animation_library_is_foreign = true;
+ libitem->set_text(1, TTR("[imported]"));
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(al_path + ".import")) {
+ animation_library_is_foreign = true;
+ libitem->set_text(1, TTR("[imported]"));
+ } else {
+ libitem->set_text(1, al_path.get_file());
+ }
}
+
+ libitem->set_editable(0, !animation_library_is_foreign);
+ libitem->set_metadata(0, K);
+ libitem->set_icon(0, get_theme_icon("AnimationLibrary", "EditorIcons"));
+
+ libitem->add_button(0, get_theme_icon("Add", "EditorIcons"), LIB_BUTTON_ADD, animation_library_is_foreign, TTR("Add Animation to Library"));
+ libitem->add_button(0, get_theme_icon("Load", "EditorIcons"), LIB_BUTTON_LOAD, animation_library_is_foreign, TTR("Load animation from file and add to library"));
+ libitem->add_button(0, get_theme_icon("ActionPaste", "EditorIcons"), LIB_BUTTON_PASTE, animation_library_is_foreign, TTR("Paste Animation to Library from clipboard"));
+
libitem->add_button(1, get_theme_icon("Save", "EditorIcons"), LIB_BUTTON_FILE, false, TTR("Save animation library to resource on disk"));
libitem->add_button(1, get_theme_icon("Remove", "EditorIcons"), LIB_BUTTON_DELETE, false, TTR("Remove animation library"));
@@ -600,20 +677,38 @@ void AnimationLibraryEditor::update_tree() {
for (const StringName &L : animations) {
TreeItem *anitem = tree->create_item(libitem);
anitem->set_text(0, L);
- anitem->set_editable(0, true);
+ anitem->set_editable(0, !animation_library_is_foreign);
anitem->set_metadata(0, L);
anitem->set_icon(0, get_theme_icon("Animation", "EditorIcons"));
- anitem->add_button(0, get_theme_icon("ActionCopy", "EditorIcons"), ANIM_BUTTON_COPY, false, TTR("Copy animation to clipboard"));
- Ref<Animation> anim = al->get_animation(L);
+ anitem->add_button(0, get_theme_icon("ActionCopy", "EditorIcons"), ANIM_BUTTON_COPY, animation_library_is_foreign, TTR("Copy animation to clipboard"));
- if (anim->get_path().is_resource_file()) {
- anitem->set_text(1, anim->get_path().get_file());
- anitem->set_tooltip(1, anim->get_path());
- } else {
+ Ref<Animation> anim = al->get_animation(L);
+ String anim_path = anim->get_path();
+ if (!anim_path.is_resource_file()) {
anitem->set_text(1, TTR("[built-in]"));
+ anitem->set_tooltip(1, anim_path);
+ int srpos = anim_path.find("::");
+ if (srpos != -1) {
+ String base = anim_path.substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ anitem->set_text(1, TTR("[foreign]"));
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ anitem->set_text(1, TTR("[imported]"));
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(anim_path + ".import")) {
+ anitem->set_text(1, TTR("[imported]"));
+ } else {
+ anitem->set_text(1, anim_path.get_file());
+ }
}
- anitem->add_button(1, get_theme_icon("Save", "EditorIcons"), ANIM_BUTTON_FILE, false, TTR("Save animation to resource on disk"));
- anitem->add_button(1, get_theme_icon("Remove", "EditorIcons"), ANIM_BUTTON_DELETE, false, TTR("Remove animation from Library"));
+ anitem->add_button(1, get_theme_icon("Save", "EditorIcons"), ANIM_BUTTON_FILE, animation_library_is_foreign, TTR("Save animation to resource on disk"));
+ anitem->add_button(1, get_theme_icon("Remove", "EditorIcons"), ANIM_BUTTON_DELETE, animation_library_is_foreign, TTR("Remove animation from Library"));
}
}
}
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index ebd7525bb8..516079673d 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -55,7 +55,7 @@ void AnimationPlayerEditor::_node_removed(Node *p_node) {
set_process(false);
- track_editor->set_animation(Ref<Animation>());
+ track_editor->set_animation(Ref<Animation>(), true);
track_editor->set_root(nullptr);
track_editor->show_select_node_warning(true);
_update_player();
@@ -283,7 +283,28 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
Ref<Animation> anim = player->get_animation(current);
{
- track_editor->set_animation(anim);
+ bool animation_library_is_foreign = false;
+ if (!anim->get_path().is_resource_file()) {
+ int srpos = anim->get_path().find("::");
+ if (srpos != -1) {
+ String base = anim->get_path().substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ animation_library_is_foreign = true;
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ animation_library_is_foreign = true;
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(anim->get_path() + ".import")) {
+ animation_library_is_foreign = true;
+ }
+ }
+
+ track_editor->set_animation(anim, animation_library_is_foreign);
Node *root = player->get_node(player->get_root());
if (root) {
track_editor->set_root(root);
@@ -292,7 +313,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
frame->set_max((double)anim->get_length());
} else {
- track_editor->set_animation(Ref<Animation>());
+ track_editor->set_animation(Ref<Animation>(), true);
track_editor->set_root(nullptr);
}
@@ -751,14 +772,36 @@ void AnimationPlayerEditor::_animation_edit() {
String current = _get_current();
if (current != String()) {
Ref<Animation> anim = player->get_animation(current);
- track_editor->set_animation(anim);
+
+ bool animation_library_is_foreign = false;
+ if (!anim->get_path().is_resource_file()) {
+ int srpos = anim->get_path().find("::");
+ if (srpos != -1) {
+ String base = anim->get_path().substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ animation_library_is_foreign = true;
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ animation_library_is_foreign = true;
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(anim->get_path() + ".import")) {
+ animation_library_is_foreign = true;
+ }
+ }
+
+ track_editor->set_animation(anim, animation_library_is_foreign);
Node *root = player->get_node(player->get_root());
if (root) {
track_editor->set_root(root);
}
} else {
- track_editor->set_animation(Ref<Animation>());
+ track_editor->set_animation(Ref<Animation>(), true);
track_editor->set_root(nullptr);
}
}
@@ -812,13 +855,37 @@ void AnimationPlayerEditor::_update_player() {
int active_idx = -1;
bool no_anims_found = true;
+ bool foreign_global_anim_lib = false;
for (const StringName &K : libraries) {
if (K != StringName()) {
animation->add_separator(K);
}
+ // Check if the global library is foreign since we want to disable options for adding/remove/renaming animations if it is.
Ref<AnimationLibrary> library = player->get_animation_library(K);
+ if (K == "") {
+ if (!library->get_path().is_resource_file()) {
+ int srpos = library->get_path().find("::");
+ if (srpos != -1) {
+ String base = library->get_path().substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ foreign_global_anim_lib = true;
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ foreign_global_anim_lib = true;
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(library->get_path() + ".import")) {
+ foreign_global_anim_lib = true;
+ }
+ }
+ }
+
List<StringName> animlist;
library->get_animation_list(&animlist);
@@ -835,7 +902,13 @@ void AnimationPlayerEditor::_update_player() {
no_anims_found = false;
}
}
-#define ITEM_CHECK_DISABLED(m_item) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), no_anims_found)
+#define ITEM_CHECK_DISABLED(m_item) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), foreign_global_anim_lib)
+
+ ITEM_CHECK_DISABLED(TOOL_NEW_ANIM);
+
+#undef ITEM_CHECK_DISABLED
+
+#define ITEM_CHECK_DISABLED(m_item) tool_anim->get_popup()->set_item_disabled(tool_anim->get_popup()->get_item_index(m_item), no_anims_found || foreign_global_anim_lib)
ITEM_CHECK_DISABLED(TOOL_DUPLICATE_ANIM);
ITEM_CHECK_DISABLED(TOOL_RENAME_ANIM);
@@ -877,7 +950,29 @@ void AnimationPlayerEditor::_update_player() {
if (!no_anims_found) {
String current = animation->get_item_text(animation->get_selected());
Ref<Animation> anim = player->get_animation(current);
- track_editor->set_animation(anim);
+
+ bool animation_library_is_foreign = false;
+ if (!anim->get_path().is_resource_file()) {
+ int srpos = anim->get_path().find("::");
+ if (srpos != -1) {
+ String base = anim->get_path().substr(0, srpos);
+ if (ResourceLoader::get_resource_type(base) == "PackedScene") {
+ if (!get_tree()->get_edited_scene_root() || get_tree()->get_edited_scene_root()->get_scene_file_path() != base) {
+ animation_library_is_foreign = true;
+ }
+ } else {
+ if (FileAccess::exists(base + ".import")) {
+ animation_library_is_foreign = true;
+ }
+ }
+ }
+ } else {
+ if (FileAccess::exists(anim->get_path() + ".import")) {
+ animation_library_is_foreign = true;
+ }
+ }
+
+ track_editor->set_animation(anim, animation_library_is_foreign);
Node *root = player->get_node(player->get_root());
if (root) {
track_editor->set_root(root);
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index ac85eb5e1b..fc70ace331 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5020,17 +5020,36 @@ CanvasItemEditor::CanvasItemEditor() {
controls_vb->set_begin(Point2(5, 5));
// To ensure that scripts can parse the list of shortcuts correctly, we have to define
- // those shortcuts one by one. Define shortcut before using it (by EditorZoomWidget)
- ED_SHORTCUT("canvas_item_editor/zoom_3.125_percent", TTR("Zoom to 3.125%"), KeyModifierMask::SHIFT | Key::KEY_5);
- ED_SHORTCUT("canvas_item_editor/zoom_6.25_percent", TTR("Zoom to 6.25%"), KeyModifierMask::SHIFT | Key::KEY_4);
- ED_SHORTCUT("canvas_item_editor/zoom_12.5_percent", TTR("Zoom to 12.5%"), KeyModifierMask::SHIFT | Key::KEY_3);
- ED_SHORTCUT("canvas_item_editor/zoom_25_percent", TTR("Zoom to 25%"), KeyModifierMask::SHIFT | Key::KEY_2);
- ED_SHORTCUT("canvas_item_editor/zoom_50_percent", TTR("Zoom to 50%"), KeyModifierMask::SHIFT | Key::KEY_1);
- ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), { (int32_t)Key::KEY_1, (int32_t)(KeyModifierMask::CMD | Key::KEY_0) });
- ED_SHORTCUT("canvas_item_editor/zoom_200_percent", TTR("Zoom to 200%"), Key::KEY_2);
- ED_SHORTCUT("canvas_item_editor/zoom_400_percent", TTR("Zoom to 400%"), Key::KEY_3);
- ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), Key::KEY_4);
- ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"), Key::KEY_5);
+ // those shortcuts one by one. Define shortcut before using it (by EditorZoomWidget).
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_3.125_percent", TTR("Zoom to 3.125%"),
+ { int32_t(KeyModifierMask::SHIFT | Key::KEY_5), int32_t(KeyModifierMask::SHIFT | Key::KP_5) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_6.25_percent", TTR("Zoom to 6.25%"),
+ { int32_t(KeyModifierMask::SHIFT | Key::KEY_4), int32_t(KeyModifierMask::SHIFT | Key::KP_4) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_12.5_percent", TTR("Zoom to 12.5%"),
+ { int32_t(KeyModifierMask::SHIFT | Key::KEY_3), int32_t(KeyModifierMask::SHIFT | Key::KP_3) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_25_percent", TTR("Zoom to 25%"),
+ { int32_t(KeyModifierMask::SHIFT | Key::KEY_2), int32_t(KeyModifierMask::SHIFT | Key::KP_2) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_50_percent", TTR("Zoom to 50%"),
+ { int32_t(KeyModifierMask::SHIFT | Key::KEY_1), int32_t(KeyModifierMask::SHIFT | Key::KP_1) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"),
+ { int32_t(Key::KEY_1), int32_t(KeyModifierMask::CMD | Key::KEY_0), int32_t(Key::KP_1), int32_t(KeyModifierMask::CMD | Key::KP_0) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_200_percent", TTR("Zoom to 200%"),
+ { int32_t(Key::KEY_2), int32_t(Key::KP_2) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_400_percent", TTR("Zoom to 400%"),
+ { int32_t(Key::KEY_3), int32_t(Key::KP_3) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"),
+ { int32_t(Key::KEY_4), int32_t(Key::KP_4) });
+
+ ED_SHORTCUT_ARRAY("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"),
+ { int32_t(Key::KEY_5), int32_t(Key::KP_5) });
zoom_widget = memnew(EditorZoomWidget);
controls_vb->add_child(zoom_widget);
diff --git a/editor/plugins/control_editor_plugin.cpp b/editor/plugins/control_editor_plugin.cpp
index 2756e45cf4..ff5d112956 100644
--- a/editor/plugins/control_editor_plugin.cpp
+++ b/editor/plugins/control_editor_plugin.cpp
@@ -31,10 +31,13 @@
#include "control_editor_plugin.h"
#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
#include "editor/editor_settings.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 +52,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 {
@@ -448,37 +451,280 @@ 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_ENTER_TREE:
+ 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(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_ENTER_TREE:
+ 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(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(TTR("Some parents of the selected nodes do not support the Expand flag."));
+ }
+}
+
+void SizeFlagPresetPicker::_notification(int p_notification) {
+ switch (p_notification) {
+ case NOTIFICATION_ENTER_TREE:
+ 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(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 +735,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 +767,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 +837,205 @@ 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<Control>(control->get_parent())) {
+ has_control_parents = true;
+ }
if (Object::cast_to<Container>(control->get_parent())) {
- continue;
+ has_container_parents = true;
+
+ Container *parent_container = Object::cast_to<Container>(control->get_parent());
+
+ 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;
+
+ 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;
}
+ }
+
+ // Set general toolbar visibility.
+ set_visible(has_controls);
+
+ // Set anchor tools visibility.
+ if (has_controls && (!has_control_parents || !has_container_parents)) {
+ anchors_button->set_visible(true);
+ anchor_mode_button->set_visible(true);
+
+ // Update anchor mode.
+ int nb_valid_controls = 0;
+ int nb_anchors_mode = 0;
- nb_valid_controls++;
- if (control->get_meta("_edit_use_anchors_", false)) {
- nb_anchors_mode++;
+ 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;
+ }
+
+ nb_valid_controls++;
+ if (control->get_meta("_edit_use_anchors_", false)) {
+ nb_anchors_mode++;
+ }
}
+
+ 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);
}
- anchors_mode = (nb_valid_controls == nb_anchors_mode);
- anchor_mode_button->set_pressed(anchors_mode);
+ // Set container tools visibility.
+ if (has_controls && (!has_control_parents || has_container_parents)) {
+ containers_button->set_visible(true);
- if (nb_controls > 0) {
- set_physics_process(true);
+ // 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 {
- set_physics_process(false);
- set_visible(false);
+ containers_button->set_visible(false);
}
}
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);
-
+ 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")));
-
- 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;
-
- case NOTIFICATION_PHYSICS_PROCESS: {
- bool has_control_parents = false;
- bool has_container_parents = false;
-
- // 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;
- }
- }
-
- // 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;
- }
-
- 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);
- }
-
- 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);
- }
+ 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(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(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."));
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(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 +1046,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..f1b9190a0b 100644
--- a/editor/plugins/control_editor_plugin.h
+++ b/editor/plugins/control_editor_plugin.h
@@ -33,14 +33,18 @@
#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"
+// Inspector controls.
class ControlPositioningWarning : public MarginContainer {
GDCLASS(ControlPositioningWarning, MarginContainer);
@@ -125,102 +129,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;
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 +239,7 @@ public:
ControlEditorToolbar();
};
+// Editor plugin.
class ControlEditorPlugin : public EditorPlugin {
GDCLASS(ControlEditorPlugin, EditorPlugin);
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..c7d3e92802 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;
}
/*************************************************************************/
diff --git a/editor/plugins/font_config_plugin.h b/editor/plugins/font_config_plugin.h
index 3eaa2fdc17..41dde3cc59 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(){};
};
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index 0070226d40..e8f143a637 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -4839,6 +4839,10 @@ void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
////
NavigationRegion3DGizmoPlugin::NavigationRegion3DGizmoPlugin() {
+ create_material("face_material", NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color(), false, false, true);
+ create_material("face_material_disabled", NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_disabled_color(), false, false, true);
+ create_material("edge_material", NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_color());
+ create_material("edge_material_disabled", NavigationServer3D::get_singleton()->get_debug_navigation_geometry_edge_disabled_color());
}
bool NavigationRegion3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
@@ -4924,6 +4928,7 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
tmesh->create(tmeshfaces);
p_gizmo->add_collision_triangles(tmesh);
+ p_gizmo->add_collision_segments(lines);
Ref<ArrayMesh> debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
int polygon_count = navigationmesh->get_polygon_count();
@@ -4945,6 +4950,7 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
face_mesh_array[Mesh::ARRAY_VERTEX] = face_vertex_array;
// if enabled add vertex colors to colorize each face individually
+ RandomPCG rand;
bool enabled_geometry_face_random_color = NavigationServer3D::get_singleton()->get_debug_navigation_enable_geometry_face_random_color();
if (enabled_geometry_face_random_color) {
Color debug_navigation_geometry_face_color = NavigationServer3D::get_singleton()->get_debug_navigation_geometry_face_color();
@@ -4954,7 +4960,9 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
face_color_array.resize(polygon_count * 3);
for (int i = 0; i < polygon_count; i++) {
- polygon_color = debug_navigation_geometry_face_color * (Color(Math::randf(), Math::randf(), Math::randf()));
+ // Generate the polygon color, slightly randomly modified from the settings one.
+ polygon_color.set_hsv(debug_navigation_geometry_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, debug_navigation_geometry_face_color.get_s(), debug_navigation_geometry_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2);
+ polygon_color.a = debug_navigation_geometry_face_color.a;
Vector<int> polygon = navigationmesh->get_polygon(i);
@@ -4966,12 +4974,10 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, face_mesh_array);
- Ref<StandardMaterial3D> debug_geometry_face_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_material();
- debug_mesh->surface_set_material(0, debug_geometry_face_material);
+ p_gizmo->add_mesh(debug_mesh, navigationregion->is_enabled() ? get_material("face_material", p_gizmo) : get_material("face_material_disabled", p_gizmo));
// if enabled build geometry edge line surface
bool enabled_edge_lines = NavigationServer3D::get_singleton()->get_debug_navigation_enable_edge_lines();
-
if (enabled_edge_lines) {
Vector<Vector3> line_vertex_array;
line_vertex_array.resize(polygon_count * 6);
@@ -4987,27 +4993,8 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
line_vertex_array.push_back(vertices[polygon[0]]);
}
- Array line_mesh_array;
- line_mesh_array.resize(Mesh::ARRAY_MAX);
- line_mesh_array[Mesh::ARRAY_VERTEX] = line_vertex_array;
- debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, line_mesh_array);
- Ref<StandardMaterial3D> debug_geometry_edge_material = NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_material();
- debug_mesh->surface_set_material(1, debug_geometry_edge_material);
+ p_gizmo->add_lines(line_vertex_array, navigationregion->is_enabled() ? get_material("edge_material", p_gizmo) : get_material("edge_material_disabled", p_gizmo));
}
-
- if (!navigationregion->is_enabled()) {
- if (debug_mesh.is_valid()) {
- if (debug_mesh->get_surface_count() > 0) {
- debug_mesh->surface_set_material(0, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_face_disabled_material());
- }
- if (debug_mesh->get_surface_count() > 1) {
- debug_mesh->surface_set_material(1, NavigationServer3D::get_singleton_mut()->get_debug_navigation_geometry_edge_disabled_material());
- }
- }
- }
-
- p_gizmo->add_mesh(debug_mesh);
- p_gizmo->add_collision_segments(lines);
}
//////
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index e700412188..d70c50f72a 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -47,6 +47,50 @@
#include "servers/rendering/shader_preprocessor.h"
#include "servers/rendering/shader_types.h"
+/*** SHADER SYNTAX HIGHLIGHTER ****/
+
+Dictionary GDShaderSyntaxHighlighter::_get_line_syntax_highlighting_impl(int p_line) {
+ Dictionary color_map;
+
+ for (const Point2i &region : disabled_branch_regions) {
+ if (p_line >= region.x && p_line <= region.y) {
+ Dictionary highlighter_info;
+ highlighter_info["color"] = disabled_branch_color;
+
+ color_map[0] = highlighter_info;
+ return color_map;
+ }
+ }
+
+ return CodeHighlighter::_get_line_syntax_highlighting_impl(p_line);
+}
+
+void GDShaderSyntaxHighlighter::add_disabled_branch_region(const Point2i &p_region) {
+ ERR_FAIL_COND(p_region.x < 0);
+ ERR_FAIL_COND(p_region.y < 0);
+
+ for (int i = 0; i < disabled_branch_regions.size(); i++) {
+ ERR_FAIL_COND_MSG(disabled_branch_regions[i].x == p_region.x, "Branch region with a start line '" + itos(p_region.x) + "' already exists.");
+ }
+
+ Point2i disabled_branch_region;
+ disabled_branch_region.x = p_region.x;
+ disabled_branch_region.y = p_region.y;
+ disabled_branch_regions.push_back(disabled_branch_region);
+
+ clear_highlighting_cache();
+}
+
+void GDShaderSyntaxHighlighter::clear_disabled_branch_regions() {
+ disabled_branch_regions.clear();
+ clear_highlighting_cache();
+}
+
+void GDShaderSyntaxHighlighter::set_disabled_branch_color(const Color &p_color) {
+ disabled_branch_color = p_color;
+ clear_highlighting_cache();
+}
+
/*** SHADER SCRIPT EDITOR ****/
static bool saved_warnings_enabled = false;
@@ -134,6 +178,7 @@ void ShaderTextEditor::set_edited_code(const String &p_code) {
get_text_editor()->clear_undo_history();
get_text_editor()->call_deferred(SNAME("set_h_scroll"), 0);
get_text_editor()->call_deferred(SNAME("set_v_scroll"), 0);
+ get_text_editor()->tag_saved_version();
_validate_script();
_line_col_changed();
@@ -263,6 +308,7 @@ void ShaderTextEditor::_load_theme_settings() {
syntax_highlighter->clear_color_regions();
syntax_highlighter->add_color_region("/*", "*/", comment_color, false);
syntax_highlighter->add_color_region("//", "", comment_color, true);
+ syntax_highlighter->set_disabled_branch_color(comment_color);
text_editor->clear_comment_delimiters();
text_editor->add_comment_delimiter("/*", "*/", false);
@@ -345,7 +391,7 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLa
if (!complete_from_path.ends_with("/")) {
complete_from_path += "/";
}
- preprocessor.preprocess(p_code, code, nullptr, nullptr, nullptr, &pp_options, _complete_include_paths);
+ preprocessor.preprocess(p_code, "", code, nullptr, nullptr, nullptr, nullptr, &pp_options, _complete_include_paths);
complete_from_path = String();
if (pp_options.size()) {
for (const ScriptLanguage::CodeCompletionOption &E : pp_options) {
@@ -391,11 +437,29 @@ void ShaderTextEditor::_validate_script() {
String code_pp;
String error_pp;
List<ShaderPreprocessor::FilePosition> err_positions;
- last_compile_result = preprocessor.preprocess(code, code_pp, &error_pp, &err_positions);
+ List<ShaderPreprocessor::Region> regions;
+ String filename;
+ if (shader.is_valid()) {
+ filename = shader->get_path();
+ } else if (shader_inc.is_valid()) {
+ filename = shader_inc->get_path();
+ }
+ last_compile_result = preprocessor.preprocess(code, filename, code_pp, &error_pp, &err_positions, &regions);
for (int i = 0; i < get_text_editor()->get_line_count(); i++) {
get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0));
}
+
+ syntax_highlighter->clear_disabled_branch_regions();
+ for (const ShaderPreprocessor::Region &region : regions) {
+ if (!region.enabled) {
+ if (filename != region.file) {
+ continue;
+ }
+ syntax_highlighter->add_disabled_branch_region(Point2i(region.from_line, region.to_line));
+ }
+ }
+
set_error("");
set_error_count(0);
@@ -843,6 +907,7 @@ void ShaderEditor::save_external_data(const String &p_str) {
if (shader_inc.is_valid() && shader_inc != edited_shader_inc) {
ResourceSaver::save(shader_inc);
}
+ shader_editor->get_text_editor()->tag_saved_version();
disk_changed->hide();
}
@@ -851,6 +916,10 @@ void ShaderEditor::validate_script() {
shader_editor->_validate_script();
}
+bool ShaderEditor::is_unsaved() const {
+ return shader_editor->get_text_editor()->get_saved_version() != shader_editor->get_text_editor()->get_version();
+}
+
void ShaderEditor::apply_shaders() {
String editor_code = shader_editor->get_text_editor()->get_text();
if (shader.is_valid()) {
@@ -1127,36 +1196,34 @@ ShaderEditor::ShaderEditor() {
void ShaderEditorPlugin::_update_shader_list() {
shader_list->clear();
for (uint32_t i = 0; i < edited_shaders.size(); i++) {
- String text;
- String path;
- String _class;
- String shader_name;
- if (edited_shaders[i].shader.is_valid()) {
- Ref<Shader> shader = edited_shaders[i].shader;
-
- path = shader->get_path();
- _class = shader->get_class();
- shader_name = shader->get_name();
- } else {
- Ref<ShaderInclude> shader_inc = edited_shaders[i].shader_inc;
-
- path = shader_inc->get_path();
- _class = shader_inc->get_class();
- shader_name = shader_inc->get_name();
+ Ref<Resource> shader = edited_shaders[i].shader;
+ if (shader.is_null()) {
+ shader = edited_shaders[i].shader_inc;
}
- if (path.is_resource_file()) {
- text = path.get_file();
- } else if (shader_name != "") {
- text = shader_name;
- } else {
- if (edited_shaders[i].shader.is_valid()) {
- text = _class + ":" + itos(edited_shaders[i].shader->get_instance_id());
- } else {
- text = _class + ":" + itos(edited_shaders[i].shader_inc->get_instance_id());
+ String path = shader->get_path();
+ String text = path.get_file();
+ if (text.is_empty()) {
+ // This appears for newly created built-in shaders before saving the scene.
+ text = TTR("[unsaved]");
+ } else if (shader->is_built_in()) {
+ const String &shader_name = shader->get_name();
+ if (!shader_name.is_empty()) {
+ text = vformat("%s (%s)", shader_name, text.get_slice("::", 0));
}
}
+ bool unsaved = false;
+ if (edited_shaders[i].shader_editor) {
+ unsaved = edited_shaders[i].shader_editor->is_unsaved();
+ }
+ // TODO: Handle visual shaders too.
+
+ if (unsaved) {
+ text += "(*)";
+ }
+
+ String _class = shader->get_class();
if (!shader_list->has_theme_icon(_class, SNAME("EditorIcons"))) {
_class = "TextFile";
}
@@ -1206,7 +1273,7 @@ void ShaderEditorPlugin::edit(Object *p_object) {
es.shader_editor = memnew(ShaderEditor);
es.shader_editor->edit(si);
shader_tabs->add_child(es.shader_editor);
- es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list_status));
+ es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
} else {
Shader *s = Object::cast_to<Shader>(p_object);
for (uint32_t i = 0; i < edited_shaders.size(); i++) {
@@ -1226,7 +1293,7 @@ void ShaderEditorPlugin::edit(Object *p_object) {
es.shader_editor = memnew(ShaderEditor);
shader_tabs->add_child(es.shader_editor);
es.shader_editor->edit(s);
- es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list_status));
+ es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
}
}
@@ -1272,6 +1339,7 @@ void ShaderEditorPlugin::save_external_data() {
edited_shaders[i].shader_editor->save_external_data();
}
}
+ _update_shader_list();
}
void ShaderEditorPlugin::apply_changes() {
@@ -1289,6 +1357,12 @@ void ShaderEditorPlugin::_shader_selected(int p_index) {
shader_tabs->set_current_tab(p_index);
}
+void ShaderEditorPlugin::_shader_list_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index) {
+ if (p_mouse_button_index == MouseButton::MIDDLE) {
+ _close_shader(p_item);
+ }
+}
+
void ShaderEditorPlugin::_close_shader(int p_index) {
int index = shader_tabs->get_current_tab();
ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
@@ -1408,6 +1482,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
shader_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
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));
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 907de6ea87..0980cc4db2 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -48,6 +48,21 @@ class VisualShaderEditor;
class HSplitContainer;
class ShaderCreateDialog;
+class GDShaderSyntaxHighlighter : public CodeHighlighter {
+ GDCLASS(GDShaderSyntaxHighlighter, CodeHighlighter)
+
+private:
+ Vector<Point2i> disabled_branch_regions;
+ Color disabled_branch_color;
+
+public:
+ virtual Dictionary _get_line_syntax_highlighting_impl(int p_line) override;
+
+ void add_disabled_branch_region(const Point2i &p_region);
+ void clear_disabled_branch_regions();
+ void set_disabled_branch_color(const Color &p_color);
+};
+
class ShaderTextEditor : public CodeTextEditor {
GDCLASS(ShaderTextEditor, CodeTextEditor);
@@ -57,7 +72,7 @@ class ShaderTextEditor : public CodeTextEditor {
_ALWAYS_INLINE_ bool operator()(const ShaderWarning &p_a, const ShaderWarning &p_b) const { return (p_a.get_line() < p_b.get_line()); }
};
- Ref<CodeHighlighter> syntax_highlighter;
+ Ref<GDShaderSyntaxHighlighter> syntax_highlighter;
RichTextLabel *warnings_panel = nullptr;
Ref<Shader> shader;
Ref<ShaderInclude> shader_inc;
@@ -185,6 +200,7 @@ public:
void goto_line_selection(int p_line, int p_begin, int p_end);
void save_external_data(const String &p_str = "");
void validate_script();
+ bool is_unsaved() const;
virtual Size2 get_minimum_size() const override { return Size2(0, 200); }
@@ -226,6 +242,7 @@ class ShaderEditorPlugin : public EditorPlugin {
void _update_shader_list();
void _shader_selected(int p_index);
+ void _shader_list_clicked(int p_item, Vector2 p_local_mouse_pos, MouseButton p_mouse_button_index);
void _menu_item_pressed(int p_index);
void _resource_saved(Object *obj);
void _close_shader(int p_index);
@@ -235,8 +252,6 @@ class ShaderEditorPlugin : public EditorPlugin {
void _update_shader_list_status();
public:
- virtual String get_name() const override { return "Shader"; }
- bool has_main_screen() const override { return false; }
virtual void edit(Object *p_object) override;
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp
index 3323d865c2..7d350fd46f 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -160,6 +160,11 @@ void Sprite2DEditor::_menu_option(int p_option) {
}
void Sprite2DEditor::_update_mesh_data() {
+ if (node->get_owner() != get_tree()->get_edited_scene_root()) {
+ err_dialog->set_text(TTR("Can't convert a Sprite2D from a foreign scene."));
+ err_dialog->popup_centered();
+ }
+
Ref<Texture2D> texture = node->get_texture();
if (texture.is_null()) {
err_dialog->set_text(TTR("Sprite2D is empty!"));
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index f6b02d5f80..be382759f5 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -137,7 +137,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
// It's okay that these colors are static since the grid color is static too.
metadata_label->add_theme_color_override("font_color", Color::named("white"));
- metadata_label->add_theme_color_override("font_color_shadow", Color::named("black"));
+ metadata_label->add_theme_color_override("font_shadow_color", Color::named("black"));
metadata_label->add_theme_font_size_override("font_size", 14 * EDSCALE);
metadata_label->add_theme_color_override("font_outline_color", Color::named("black"));
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 4a144bc391..cf24095582 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -6063,7 +6063,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;
@@ -6149,39 +6149,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..b6a3b43754 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -529,8 +529,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 +542,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/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
index 421e56f9a1..924b735012 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -358,6 +358,7 @@ static const char *gdscript_function_renames[][2] = {
{ "get_scancode_with_modifiers", "get_keycode_with_modifiers" }, // InputEventKey
{ "get_shift", "is_shift_pressed" }, // InputEventWithModifiers
{ "get_size_override", "get_size_2d_override" }, // SubViewport
+ { "get_slide_count", "get_slide_collision_count" }, // CharacterBody2D, CharacterBody3D
{ "get_slips_on_slope", "get_slide_on_slope" }, // SeparationRayShape2D, SeparationRayShape3D
{ "get_space_override_mode", "get_gravity_space_override_mode" }, // Area2D
{ "get_speed", "get_velocity" }, // InputEventMouseMotion
@@ -515,6 +516,7 @@ static const char *gdscript_function_renames[][2] = {
{ "set_region_filter_clip", "set_region_filter_clip_enabled" }, // Sprite2D
{ "set_rotate", "set_rotates" }, // PathFollow2D
{ "set_scancode", "set_keycode" }, // InputEventKey
+ { "set_shader_param", "set_shader_uniform" }, // ShaderMaterial
{ "set_shift", "set_shift_pressed" }, // InputEventWithModifiers
{ "set_size_override", "set_size_2d_override" }, // SubViewport broke ImageTexture
{ "set_size_override_stretch", "set_size_2d_override_stretch" }, // SubViewport
@@ -1379,7 +1381,6 @@ static const char *class_renames[][2] = {
{ "Spatial", "Node3D" },
{ "SpatialGizmo", "Node3DGizmo" },
{ "SpatialMaterial", "StandardMaterial3D" },
- { "SpatialVelocityTracker", "VelocityTracker3D" },
{ "SphereShape", "SphereShape3D" },
{ "SpotLight", "SpotLight3D" },
{ "SpringArm", "SpringArm3D" },
@@ -1617,6 +1618,7 @@ public:
RegEx reg_image_lock = RegEx("([a-zA-Z0-9_\\.]+)\\.lock\\(\\)");
RegEx reg_image_unlock = RegEx("([a-zA-Z0-9_\\.]+)\\.unlock\\(\\)");
RegEx reg_os_fullscreen = RegEx("OS.window_fullscreen[= ]+([^#^\n]+)");
+ RegEx reg_instantiate = RegEx("\\.instance\\(([^\\)]*)\\)");
};
// Function responsible for converting project
@@ -1689,7 +1691,6 @@ int ProjectConverter3To4::convert() {
rename_common(builtin_types_renames, file_content);
custom_rename(file_content, "\\.shader", ".gdshader");
- custom_rename(file_content, "instance", "instantiate");
} else if (file_name.ends_with(".tscn")) {
rename_classes(file_content); // Using only specialized function
@@ -1834,7 +1835,6 @@ int ProjectConverter3To4::validate_conversion() {
changed_elements.append_array(check_for_rename_common(shaders_renames, file_content));
changed_elements.append_array(check_for_rename_common(builtin_types_renames, file_content));
- changed_elements.append_array(check_for_custom_rename(file_content, "instance", "instantiate"));
changed_elements.append_array(check_for_custom_rename(file_content, "\\.shader", ".gdshader"));
} else if (file_name.ends_with(".tscn")) {
changed_elements.append_array(check_for_rename_classes(file_content));
@@ -2024,8 +2024,8 @@ bool ProjectConverter3To4::test_conversion(const RegExContainer &reg_container)
valid = valid & test_conversion_single_additional_builtin("OS.window_fullscreen = Settings.fullscreen", "ProjectSettings.set(\\\"display/window/size/fullscreen\\\", Settings.fullscreen)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, true);
valid = valid & test_conversion_single_additional_builtin("OS.get_window_safe_area()", "DisplayServer.get_display_safe_area()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid & test_conversion_single_additional_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_motion_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tvar aa = roman(r.move_and_slide()) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid & test_conversion_single_additional_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_motion_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tvar aa = roman(r.move_and_slide()) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid & test_conversion_single_additional_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid & test_conversion_single_additional_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\t# TODOConverter40 looks that snap in Godot 4.0 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin("list_dir_begin( a , b )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin("list_dir_begin( a )", "list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@@ -2071,7 +2071,7 @@ bool ProjectConverter3To4::test_conversion(const RegExContainer &reg_container)
valid = valid & test_conversion_single_additional_builtin("get_node(@", "get_node(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin("yield(this, \"timeout\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
- valid = valid & test_conversion_single_additional_builtin("yield(this, \"timeout\")", "await this.\"timeout\"", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, true);
+ valid = valid & test_conversion_single_additional_builtin("yield(this, \\\"timeout\\\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, true);
valid = valid & test_conversion_single_additional_builtin(" Transform.xform(Vector3(a,b,c)) ", " Transform * Vector3(a,b,c) ", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin(" Transform.xform_inv(Vector3(a,b,c)) ", " Vector3(a,b,c) * Transform ", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@@ -2105,11 +2105,17 @@ bool ProjectConverter3To4::test_conversion(const RegExContainer &reg_container)
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);
+ valid = valid & test_conversion_single_additional_builtin("func start(A,B):", "func start(A,B):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin("(start(A,B,C,D,E,F,G) != OK):", "(start(Callable(A,B).bind(C),D,E,F,G) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin("disconnect(A,B,C) != OK):", "disconnect(A,Callable(B,C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin("is_connected(A,B,C) != OK):", "is_connected(A,Callable(B,C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin("is_connected(A,B,C))", "is_connected(A,Callable(B,C)))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid & test_conversion_single_additional_builtin("(tween_method(A,B,C,D,E).foo())", "(tween_method(Callable(A,B),C,D,E).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid & test_conversion_single_additional_builtin("(tween_method(A,B,C,D,E,[F,G]).foo())", "(tween_method(Callable(A,B).bind(F,G),C,D,E).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid & test_conversion_single_additional_builtin("(tween_callback(A,B).foo())", "(tween_callback(Callable(A,B)).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+ valid = valid & test_conversion_single_additional_builtin("(tween_callback(A,B,[C,D]).foo())", "(tween_callback(Callable(A,B).bind(C,D)).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
+
valid = valid & test_conversion_single_additional_builtin("func _init(p_x:int)->void:", "func _init(p_x:int):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
valid = valid & test_conversion_single_additional_builtin("q_PackedDataContainer._iter_init(variable1)", "q_PackedDataContainer._iter_init(variable1)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
@@ -2181,6 +2187,15 @@ bool ProjectConverter3To4::test_conversion(const RegExContainer &reg_container)
}
valid = valid & (got == expected);
}
+{
+ String base = "var node = $world/ukraine/lviv.";
+ String expected = "$world/ukraine/lviv.";
+ String got = get_object_of_execution(base);
+ if (got != expected) {
+ ERR_PRINT("Failed to get proper data from get_object_of_execution `" + base + "` should return `" + expected + "`(" + itos(expected.size()) + "), got instead `" + got + "`(" + itos(got.size()) + ")");
+ }
+ valid = valid & (got == expected);
+}
}
// get_starting_space
{
@@ -2532,22 +2547,43 @@ String ProjectConverter3To4::get_starting_space(const String &line) const {
// so it is `var roman = kieliszek.` and this function return `kieliszek.`
String ProjectConverter3To4::get_object_of_execution(const String &line) const {
int end = line.size() - 1; // Last one is \0
+ int variable_start = end - 1;
int start = end - 1;
+ bool is_possibly_nodepath = false;
+ bool is_valid_nodepath = false;
+
while (start >= 0) {
char32_t character = line[start];
- if ((character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z') || character == '.' || character == '_') {
+ bool is_variable_char = (character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z') || character == '.' || character == '_';
+ bool is_nodepath_start = character == '$';
+ bool is_nodepath_sep = character == '/';
+ if (is_variable_char || is_nodepath_start || is_nodepath_sep) {
if (start == 0) {
break;
+ } else if (is_nodepath_sep) {
+ // Freeze variable_start, try to fetch more chars since this might be node path literal
+ is_possibly_nodepath = true;
+ } else if (is_nodepath_start) {
+ // Found $, this is a node path literal
+ is_valid_nodepath = true;
+ break;
+ }
+ if (!is_possibly_nodepath) {
+ variable_start--;
}
start--;
continue;
} else {
- start++; // Found invalid character, needs to be ignored
+ // Abandon all hope, this is neither a variable nor a node path literal
+ variable_start++; // Found invalid character, needs to be ignored
break;
}
}
- return line.substr(start, (end - start));
+ if (is_valid_nodepath) {
+ variable_start = start;
+ }
+ return line.substr(variable_start, (end - variable_start));
}
void ProjectConverter3To4::rename_enums(String &file_content) {
@@ -2768,7 +2804,10 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
line = reg_container.reg_os_fullscreen.sub(line, "ProjectSettings.set(\"display/window/size/fullscreen\", $1)", true);
}
- // -- r.move_and_slide( a, b, c, d, e ) -> r.set_motion_velocity(a) ... r.move_and_slide() KinematicBody
+ // Instantiate
+ line = reg_container.reg_instantiate.sub(line, ".instantiate($1)", true);
+
+ // -- r.move_and_slide( a, b, c, d, e ) -> r.set_velocity(a) ... r.move_and_slide() KinematicBody
if (line.find("move_and_slide(") != -1) {
int start = line.find("move_and_slide(");
int end = get_end_parenthess(line.substr(start)) + 1;
@@ -2781,7 +2820,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
String line_new;
// motion_velocity
- line_new += starting_space + base_obj + "set_motion_velocity(" + parts[0] + ")\n";
+ line_new += starting_space + base_obj + "set_velocity(" + parts[0] + ")\n";
// up_direction
if (parts.size() >= 2) {
@@ -2808,12 +2847,13 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[5] + "`\n";
}
- line = line_new + line.substr(0, start) + "move_and_slide()" + line.substr(end + start);
+ line_new += starting_space + base_obj + "move_and_slide()\n";
+ line = line_new + line.substr(0, start) + "velocity" + line.substr(end + start);
}
}
}
- // -- r.move_and_slide_with_snap( a, b, c, d, e ) -> r.set_motion_velocity(a) ... r.move_and_slide() KinematicBody
+ // -- r.move_and_slide_with_snap( a, b, c, d, e ) -> r.set_velocity(a) ... r.move_and_slide() KinematicBody
if (line.find("move_and_slide_with_snap(") != -1) {
int start = line.find("move_and_slide_with_snap(");
int end = get_end_parenthess(line.substr(start)) + 1;
@@ -2826,7 +2866,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
String line_new;
// motion_velocity
- line_new += starting_space + base_obj + "set_motion_velocity(" + parts[0] + ")\n";
+ line_new += starting_space + base_obj + "set_velocity(" + parts[0] + ")\n";
// snap
if (parts.size() >= 2) {
@@ -2858,7 +2898,8 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
line_new += starting_space + "# TODOConverter40 infinite_inertia were removed in Godot 4.0 - previous value `" + parts[6] + "`\n";
}
- line = line_new + line.substr(0, start) + "move_and_slide()" + line.substr(end + start);
+ line_new += starting_space + base_obj + "move_and_slide()\n";
+ line = line_new + line.substr(0, start) + "velocity" + line.substr(end + start); // move_and_slide used to return velocity
}
}
}
@@ -2923,7 +2964,7 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
Vector<String> parts = parse_arguments(line.substr(start, end));
if (parts.size() == 2) {
if (builtin) {
- line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace(" ", "") + line.substr(end + start);
+ line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\\\"", "").replace("\\'", "").replace(" ", "") + line.substr(end + start);
} else {
line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\"", "").replace("\'", "").replace(" ", "") + line.substr(end + start);
}
@@ -3007,17 +3048,47 @@ void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContai
}
}
}
- // -- start(a,b) -> start(Callable(a,b)) Thread
- // -- start(a,b,c,d) -> start(Callable(a,b).bind(c),d) Thread
- if (line.find("start(") != -1) {
- int start = line.find("start(");
+ // -- "(tween_method(A,B,C,D,E) != OK):", "(tween_method(Callable(A,B),C,D,E) Object
+ // -- "(tween_method(A,B,C,D,E,[F,G]) != OK):", "(tween_method(Callable(A,B).bind(F,G),C,D,E) Object
+ if (line.find("tween_method(") != -1) {
+ int start = line.find("tween_method(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 5) {
+ line = line.substr(0, start) + "tween_method(Callable(" + parts[0] + "," + parts[1] + ")," + parts[2] + "," + parts[3] + "," + parts[4] + ")" + line.substr(end + start);
+ } else if (parts.size() >= 6) {
+ line = line.substr(0, start) + "tween_method(Callable(" + parts[0] + "," + parts[1] + ").bind(" + connect_arguments(parts, 5).substr(1).lstrip("[").rstrip("]") + ")," + parts[2] + "," + parts[3] + "," + parts[4] + ")" + line.substr(end + start);
+ }
+ }
+ }
+ // -- "(tween_callback(A,B,[C,D]) != OK):", "(connect(Callable(A,B).bind(C,D)) Object
+ if (line.find("tween_callback(") != -1) {
+ int start = line.find("tween_callback(");
int end = get_end_parenthess(line.substr(start)) + 1;
if (end > -1) {
Vector<String> parts = parse_arguments(line.substr(start, end));
if (parts.size() == 2) {
- line = line.substr(0, start) + "start(Callable(" + parts[0] + "," + parts[1] + "))" + line.substr(end + start);
+ line = line.substr(0, start) + "tween_callback(Callable(" + parts[0] + "," + parts[1] + "))" + line.substr(end + start);
} else if (parts.size() >= 3) {
- line = line.substr(0, start) + "start(Callable(" + parts[0] + "," + parts[1] + ").bind(" + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ line = line.substr(0, start) + "tween_callback(Callable(" + parts[0] + "," + parts[1] + ").bind(" + connect_arguments(parts, 2).substr(1).lstrip("[").rstrip("]") + "))" + line.substr(end + start);
+ }
+ }
+ }
+ // -- start(a,b) -> start(Callable(a,b)) Thread
+ // -- start(a,b,c,d) -> start(Callable(a,b).bind(c),d) Thread
+ if (line.find("start(") != -1) {
+ int start = line.find("start(");
+ int end = get_end_parenthess(line.substr(start)) + 1;
+ // Protection from 'func start'
+ if (!line.begins_with("func ")) {
+ if (end > -1) {
+ Vector<String> parts = parse_arguments(line.substr(start, end));
+ if (parts.size() == 2) {
+ line = line.substr(0, start) + "start(Callable(" + parts[0] + "," + parts[1] + "))" + line.substr(end + start);
+ } else if (parts.size() >= 3) {
+ line = line.substr(0, start) + "start(Callable(" + parts[0] + "," + parts[1] + ").bind(" + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
+ }
}
}
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 21891e381b..9d2dcd129c 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1164,6 +1164,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;
}
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index d19a40599f..0f4f3dcfcf 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -2678,7 +2678,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);
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index 282cfa80e8..ad83db9b60 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -400,7 +400,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
}
_update_visibility_color(p_node, item);
- } else if (p_node->is_class("CanvasLayer")) {
+ } else if (p_node->is_class("CanvasLayer") || p_node->is_class("Window")) {
bool v = p_node->call("is_visible");
if (v) {
item->add_button(0, get_theme_icon(SNAME("GuiVisibilityVisible"), SNAME("EditorIcons")), BUTTON_VISIBILITY, false, TTR("Toggle Visibility"));
@@ -490,10 +490,7 @@ void SceneTreeEditor::_node_visibility_changed(Node *p_node) {
bool visible = false;
- if (p_node->is_class("CanvasItem")) {
- visible = p_node->call("is_visible");
- CanvasItemEditor::get_singleton()->get_viewport_control()->update();
- } else if (p_node->is_class("CanvasLayer")) {
+ if (p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer") || p_node->is_class("Window")) {
visible = p_node->call("is_visible");
CanvasItemEditor::get_singleton()->get_viewport_control()->update();
} else if (p_node->is_class("Node3D")) {
@@ -539,7 +536,7 @@ void SceneTreeEditor::_node_removed(Node *p_node) {
p_node->disconnect("script_changed", callable_mp(this, &SceneTreeEditor::_node_script_changed));
}
- if (p_node->is_class("Node3D") || p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer")) {
+ if (p_node->is_class("Node3D") || p_node->is_class("CanvasItem") || p_node->is_class("CanvasLayer") || p_node->is_class("Window")) {
if (p_node->is_connected("visibility_changed", callable_mp(this, &SceneTreeEditor::_node_visibility_changed))) {
p_node->disconnect("visibility_changed", callable_mp(this, &SceneTreeEditor::_node_visibility_changed));
}
diff --git a/editor/shader_create_dialog.cpp b/editor/shader_create_dialog.cpp
index bd1f2529ca..8c4a231e8a 100644
--- a/editor/shader_create_dialog.cpp
+++ b/editor/shader_create_dialog.cpp
@@ -37,6 +37,13 @@
#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: {
diff --git a/editor/shader_create_dialog.h b/editor/shader_create_dialog.h
index bf031c3601..9ba655369b 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;