summaryrefslogtreecommitdiff
path: root/editor/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'editor/plugins')
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp28
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h1
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp19
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.h2
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp38
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp108
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.h22
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp64
-rw-r--r--editor/plugins/animation_player_editor_plugin.h11
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp31
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp5
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp46
-rw-r--r--editor/plugins/asset_library_editor_plugin.h1
-rw-r--r--editor/plugins/audio_stream_editor_plugin.cpp6
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp475
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h20
-rw-r--r--editor/plugins/collision_polygon_3d_editor_plugin.cpp44
-rw-r--r--editor/plugins/collision_polygon_3d_editor_plugin.h4
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.cpp44
-rw-r--r--editor/plugins/collision_shape_2d_editor_plugin.h2
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.cpp2
-rw-r--r--editor/plugins/curve_editor_plugin.cpp44
-rw-r--r--editor/plugins/debugger_editor_plugin.cpp9
-rw-r--r--editor/plugins/editor_preview_plugins.cpp62
-rw-r--r--editor/plugins/editor_preview_plugins.h40
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp24
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp90
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.h13
-rw-r--r--editor/plugins/gradient_editor_plugin.cpp45
-rw-r--r--editor/plugins/gradient_editor_plugin.h16
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp398
-rw-r--r--editor/plugins/item_list_editor_plugin.h248
-rw-r--r--editor/plugins/lightmap_gi_editor_plugin.cpp8
-rw-r--r--editor/plugins/material_editor_plugin.cpp147
-rw-r--r--editor/plugins/material_editor_plugin.h46
-rw-r--r--editor/plugins/mesh_editor_plugin.cpp4
-rw-r--r--editor/plugins/mesh_instance_3d_editor_plugin.cpp17
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp59
-rw-r--r--editor/plugins/mesh_library_editor_plugin.h14
-rw-r--r--editor/plugins/multimesh_editor_plugin.cpp4
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp477
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h52
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp709
-rw-r--r--editor/plugins/node_3d_editor_plugin.h14
-rw-r--r--editor/plugins/occluder_instance_3d_editor_plugin.cpp8
-rw-r--r--editor/plugins/ot_features_plugin.cpp7
-rw-r--r--editor/plugins/ot_features_plugin.h2
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.cpp49
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.h4
-rw-r--r--editor/plugins/path_2d_editor_plugin.cpp12
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp58
-rw-r--r--editor/plugins/path_3d_editor_plugin.h2
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp48
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.cpp8
-rw-r--r--editor/plugins/resource_preloader_editor_plugin.h1
-rw-r--r--editor/plugins/root_motion_editor_plugin.cpp16
-rw-r--r--editor/plugins/root_motion_editor_plugin.h2
-rw-r--r--editor/plugins/script_editor_plugin.cpp579
-rw-r--r--editor/plugins/script_editor_plugin.h23
-rw-r--r--editor/plugins/script_text_editor.cpp224
-rw-r--r--editor/plugins/script_text_editor.h4
-rw-r--r--editor/plugins/shader_editor_plugin.cpp44
-rw-r--r--editor/plugins/shader_file_editor_plugin.cpp10
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp21
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.h2
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp1261
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.h169
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp6
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.cpp146
-rw-r--r--editor/plugins/sprite_frames_editor_plugin.h3
-rw-r--r--editor/plugins/style_box_editor_plugin.cpp7
-rw-r--r--editor/plugins/style_box_editor_plugin.h2
-rw-r--r--editor/plugins/text_control_editor_plugin.cpp375
-rw-r--r--editor/plugins/text_control_editor_plugin.h119
-rw-r--r--editor/plugins/text_editor.cpp40
-rw-r--r--editor/plugins/text_editor.h5
-rw-r--r--editor/plugins/texture_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/texture_editor_plugin.cpp29
-rw-r--r--editor/plugins/texture_editor_plugin.h2
-rw-r--r--editor/plugins/texture_layered_editor_plugin.cpp4
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp137
-rw-r--r--editor/plugins/texture_region_editor_plugin.h4
-rw-r--r--editor/plugins/theme_editor_plugin.cpp85
-rw-r--r--editor/plugins/theme_editor_plugin.h14
-rw-r--r--editor/plugins/theme_editor_preview.cpp26
-rw-r--r--editor/plugins/theme_editor_preview.h2
-rw-r--r--editor/plugins/tiles/atlas_merging_dialog.cpp17
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp143
-rw-r--r--editor/plugins/tiles/tile_atlas_view.h4
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp413
-rw-r--r--editor/plugins/tiles/tile_data_editors.h15
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp2331
-rw-r--r--editor/plugins/tiles/tile_map_editor.h156
-rw-r--r--editor/plugins/tiles/tile_proxies_manager_dialog.cpp2
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp672
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h43
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp349
-rw-r--r--editor/plugins/tiles/tile_set_editor.h21
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp44
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.h6
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp312
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.h63
-rw-r--r--editor/plugins/version_control_editor_plugin.cpp49
-rw-r--r--editor/plugins/version_control_editor_plugin.h4
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp455
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h39
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp49
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.h1
111 files changed, 7430 insertions, 4840 deletions
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index 36a814c30a..72d94e61ca 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -245,11 +245,11 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
Ref<InputEventMouseButton> mb = p_event;
if (!_has_resource()) {
- if (mb.is_valid() && mb->get_button_index() == 1 && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
create_resource->set_text(String("No polygon resource on this node.\nCreate and assign one?"));
create_resource->popup_centered();
}
- return (mb.is_valid() && mb->get_button_index() == 1);
+ return (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT);
}
CanvasItemEditor::Tool tool = CanvasItemEditor::get_singleton()->get_current_tool();
@@ -264,7 +264,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
Vector2 cpoint = _get_node()->to_local(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mb->get_position())));
if (mode == MODE_EDIT || (_is_line() && mode == MODE_CREATE)) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (mb->is_ctrl_pressed() || mb->is_shift_pressed() || mb->is_alt_pressed()) {
return false;
@@ -326,7 +326,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
return true;
}
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed() && !edited_point.valid()) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && !edited_point.valid()) {
const PosVertex closest = closest_point(gpoint);
if (closest.valid()) {
@@ -335,7 +335,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
}
}
} else if (mode == MODE_DELETE) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
const PosVertex closest = closest_point(gpoint);
if (closest.valid()) {
@@ -346,7 +346,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
}
if (mode == MODE_CREATE) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
if (_is_line()) {
// for lines, we don't have a wip mode, and we can undo each single add point.
Vector<Vector2> vertices = _get_polygon(0);
@@ -384,7 +384,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
return true;
}
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed() && wip_active) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) {
_wip_cancel();
}
}
@@ -395,7 +395,7 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
if (mm.is_valid()) {
Vector2 gpoint = mm->get_position();
- if (edited_point.valid() && (wip_active || (mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT))) {
+ if (edited_point.valid() && (wip_active || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE)) {
Vector2 cpoint = _get_node()->to_local(canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)));
//Move the point in a single axis. Should only work when editing a polygon and while holding shift.
@@ -443,10 +443,10 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
Ref<InputEventKey> k = p_event;
if (k.is_valid() && k->is_pressed()) {
- if (k->get_keycode() == KEY_DELETE || k->get_keycode() == KEY_BACKSPACE) {
+ if (k->get_keycode() == Key::KEY_DELETE || k->get_keycode() == Key::BACKSPACE) {
if (wip_active && selected_point.polygon == -1) {
if (wip.size() > selected_point.vertex) {
- wip.remove(selected_point.vertex);
+ wip.remove_at(selected_point.vertex);
_wip_changed();
selected_point = wip.size() - 1;
canvas_item_editor->update_viewport();
@@ -460,9 +460,9 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event)
return true;
}
}
- } else if (wip_active && k->get_keycode() == KEY_ENTER) {
+ } else if (wip_active && k->get_keycode() == Key::ENTER) {
_wip_close();
- } else if (wip_active && k->get_keycode() == KEY_ESCAPE) {
+ } else if (wip_active && k->get_keycode() == Key::ESCAPE) {
_wip_cancel();
}
}
@@ -554,7 +554,7 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
String num = String::num(vertex.vertex);
Size2 num_size = font->get_string_size(num, font_size);
- p_overlay->draw_string(font, point - num_size * 0.5, num, HALIGN_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5));
+ p_overlay->draw_string(font, point - num_size * 0.5, num, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5));
}
}
}
@@ -599,7 +599,7 @@ void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) {
Vector<Vector2> vertices = _get_polygon(p_vertex.polygon);
if (vertices.size() > (_is_line() ? 2 : 3)) {
- vertices.remove(p_vertex.vertex);
+ vertices.remove_at(p_vertex.vertex);
undo_redo->create_action(TTR("Edit Polygon (Remove Point)"));
_action_set_polygon(p_vertex.polygon, vertices);
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 4f9adfff25..5fea8b75d6 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -106,7 +106,6 @@ protected:
void _wip_changed();
void _wip_close();
void _wip_cancel();
- bool _delete_point(const Vector2 &p_gpoint);
void _notification(int p_what);
void _node_removed(Node *p_node);
diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp
index ad2d9866fa..f16ea36a23 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -42,7 +42,7 @@ StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const {
void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
- if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_DELETE && !k->is_echo()) {
+ if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) {
if (selected_point != -1) {
_erase_selected();
accept_event();
@@ -51,7 +51,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) || (mb->get_button_index() == MOUSE_BUTTON_LEFT && tool_create->is_pressed()))) {
+ if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (mb->get_button_index() == MouseButton::LEFT && tool_create->is_pressed()))) {
menu->clear();
animations_menu->clear();
animations_to_add.clear();
@@ -98,7 +98,8 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
menu->add_separator();
menu->add_item(TTR("Load..."), MENU_LOAD_FILE);
- menu->set_position(blend_space_draw->get_screen_transform().xform(mb->get_position()));
+ menu->set_position(blend_space_draw->get_screen_position() + mb->get_position());
+ menu->reset_size();
menu->popup();
add_point_pos = (mb->get_position() / blend_space_draw->get_size()).x;
@@ -110,7 +111,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
}
}
- if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
blend_space_draw->update(); // why not
// try to see if a point can be selected
@@ -132,7 +133,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
}
}
- if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) {
if (dragging_selected) {
// move
float point = blend_space->get_blend_point_position(selected_point);
@@ -161,7 +162,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
}
// *set* the blend
- if (mb.is_valid() && !mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && !mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
float blend_pos = mb->get_position().x / blend_space_draw->get_size().x;
blend_pos *= blend_space->get_max_space() - blend_space->get_min_space();
blend_pos += blend_space->get_min_space();
@@ -184,7 +185,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven
_update_edited_point_pos();
}
- if (mm.is_valid() && tool_blend->is_pressed() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
float blend_pos = mm->get_position().x / blend_space_draw->get_size().x;
blend_pos *= blend_space->get_max_space() - blend_space->get_min_space();
blend_pos += blend_space->get_min_space();
@@ -222,7 +223,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_draw() {
float x = point;
blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor);
- blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height(font_size) + font->get_ascent(font_size)), "0", HALIGN_LEFT, -1, font_size, linecolor);
+ blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height(font_size) + font->get_ascent(font_size)), "0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, linecolor);
blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft);
}
@@ -551,7 +552,7 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) {
if (error != error_label->get_text()) {
error_label->set_text(error);
- if (error != String()) {
+ if (!error.is_empty()) {
error_panel->show();
} else {
error_panel->hide();
diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h
index fe98a91ab3..503e066894 100644
--- a/editor/plugins/animation_blend_space_1d_editor.h
+++ b/editor/plugins/animation_blend_space_1d_editor.h
@@ -109,8 +109,6 @@ class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin {
void _edit_point_pos(double);
void _open_editor();
- void _goto_parent();
-
EditorFileDialog *open_file;
Ref<AnimationNode> file_loaded;
void _file_opened(const String &p_file);
diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp
index 49fcac512b..b107478724 100644
--- a/editor/plugins/animation_blend_space_2d_editor.cpp
+++ b/editor/plugins/animation_blend_space_2d_editor.cpp
@@ -70,7 +70,7 @@ StringName AnimationNodeBlendSpace2DEditor::get_blend_position_path() const {
void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> k = p_event;
- if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_DELETE && !k->is_echo()) {
+ if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) {
if (selected_point != -1 || selected_triangle != -1) {
_erase_selected();
accept_event();
@@ -79,7 +79,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) || (mb->get_button_index() == MOUSE_BUTTON_LEFT && tool_create->is_pressed()))) {
+ if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (mb->get_button_index() == MouseButton::LEFT && tool_create->is_pressed()))) {
menu->clear();
animations_menu->clear();
animations_to_add.clear();
@@ -121,7 +121,8 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
menu->add_separator();
menu->add_item(TTR("Load..."), MENU_LOAD_FILE);
- menu->set_position(blend_space_draw->get_screen_transform().xform(mb->get_position()));
+ menu->set_position(blend_space_draw->get_screen_position() + mb->get_position());
+ menu->reset_size();
menu->popup();
add_point_pos = (mb->get_position() / blend_space_draw->get_size());
add_point_pos.y = 1.0 - add_point_pos.y;
@@ -129,12 +130,11 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
add_point_pos += blend_space->get_min_space();
if (snap->is_pressed()) {
- add_point_pos.x = Math::snapped(add_point_pos.x, blend_space->get_snap().x);
- add_point_pos.y = Math::snapped(add_point_pos.y, blend_space->get_snap().y);
+ add_point_pos = add_point_pos.snapped(blend_space->get_snap());
}
}
- if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
blend_space_draw->update(); //update anyway
//try to see if a point can be selected
selected_point = -1;
@@ -174,7 +174,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
}
}
- if (mb.is_valid() && mb->is_pressed() && tool_triangle->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && tool_triangle->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
blend_space_draw->update(); //update anyway
//try to see if a point can be selected
selected_point = -1;
@@ -209,14 +209,13 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
}
}
- if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && !mb->is_pressed() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT) {
if (dragging_selected) {
//move
Vector2 point = blend_space->get_blend_point_position(selected_point);
point += drag_ofs;
if (snap->is_pressed()) {
- point.x = Math::snapped(point.x, blend_space->get_snap().x);
- point.y = Math::snapped(point.y, blend_space->get_snap().y);
+ point = point.snapped(blend_space->get_snap());
}
updating = true;
@@ -236,7 +235,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
blend_space_draw->update();
}
- if (mb.is_valid() && mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && tool_blend->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
Vector2 blend_pos = (mb->get_position() / blend_space_draw->get_size());
blend_pos.y = 1.0 - blend_pos.y;
blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
@@ -270,7 +269,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven
blend_space_draw->update();
}
- if (mm.is_valid() && tool_blend->is_pressed() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if (mm.is_valid() && tool_blend->is_pressed() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
Vector2 blend_pos = (mm->get_position() / blend_space_draw->get_size());
blend_pos.y = 1.0 - blend_pos.y;
blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space());
@@ -413,14 +412,14 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
if (blend_space->get_min_space().y < 0) {
int y = (blend_space->get_max_space().y / (blend_space->get_max_space().y - blend_space->get_min_space().y)) * s.height;
blend_space_draw->draw_line(Point2(0, y), Point2(5 * EDSCALE, y), linecolor);
- blend_space_draw->draw_string(font, Point2(2 * EDSCALE, y - font->get_height(font_size) + font->get_ascent(font_size)), "0", HALIGN_LEFT, -1, font_size, linecolor);
+ blend_space_draw->draw_string(font, Point2(2 * EDSCALE, y - font->get_height(font_size) + font->get_ascent(font_size)), "0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, linecolor);
blend_space_draw->draw_line(Point2(5 * EDSCALE, y), Point2(s.width, y), linecolor_soft);
}
if (blend_space->get_min_space().x < 0) {
int x = (-blend_space->get_min_space().x / (blend_space->get_max_space().x - blend_space->get_min_space().x)) * s.width;
blend_space_draw->draw_line(Point2(x, s.height - 1), Point2(x, s.height - 5 * EDSCALE), linecolor);
- blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height(font_size) + font->get_ascent(font_size)), "0", HALIGN_LEFT, -1, font_size, linecolor);
+ blend_space_draw->draw_string(font, Point2(x + 2 * EDSCALE, s.height - 2 * EDSCALE - font->get_height(font_size) + font->get_ascent(font_size)), "0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, linecolor);
blend_space_draw->draw_line(Point2(x, s.height - 5 * EDSCALE), Point2(x, 0), linecolor_soft);
}
@@ -467,8 +466,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
if (dragging_selected && selected_point == point_idx) {
point += drag_ofs;
if (snap->is_pressed()) {
- point.x = Math::snapped(point.x, blend_space->get_snap().x);
- point.y = Math::snapped(point.y, blend_space->get_snap().y);
+ point = point.snapped(blend_space->get_snap());
}
}
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
@@ -503,8 +501,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() {
if (dragging_selected && selected_point == i) {
point += drag_ofs;
if (snap->is_pressed()) {
- point.x = Math::snapped(point.x, blend_space->get_snap().x);
- point.y = Math::snapped(point.y, blend_space->get_snap().y);
+ point = point.snapped(blend_space->get_snap());
}
}
point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space());
@@ -702,8 +699,7 @@ void AnimationNodeBlendSpace2DEditor::_update_edited_point_pos() {
if (dragging_selected) {
pos += drag_ofs;
if (snap->is_pressed()) {
- pos.x = Math::snapped(pos.x, blend_space->get_snap().x);
- pos.y = Math::snapped(pos.y, blend_space->get_snap().y);
+ pos = pos.snapped(blend_space->get_snap());
}
}
updating = true;
@@ -765,7 +761,7 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) {
if (error != error_label->get_text()) {
error_label->set_text(error);
- if (error != String()) {
+ if (!error.is_empty()) {
error_panel->show();
} else {
error_panel->hide();
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 030d90eeca..d9a6f19384 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -58,7 +58,7 @@ void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const R
void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_script) {
for (int i = 0; i < add_options.size(); i++) {
if (add_options[i].script == p_script) {
- add_options.remove(i);
+ add_options.remove_at(i);
return;
}
}
@@ -66,9 +66,13 @@ void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_scrip
_update_options_menu();
}
-void AnimationNodeBlendTreeEditor::_update_options_menu() {
+void AnimationNodeBlendTreeEditor::_update_options_menu(bool p_has_input_ports) {
add_node->get_popup()->clear();
+ add_node->get_popup()->reset_size();
for (int i = 0; i < add_options.size(); i++) {
+ if (p_has_input_ports && add_options[i].input_port_count == 0) {
+ continue;
+ }
add_node->get_popup()->add_item(add_options[i].name, i);
}
@@ -79,7 +83,7 @@ void AnimationNodeBlendTreeEditor::_update_options_menu() {
}
add_node->get_popup()->add_separator();
add_node->get_popup()->add_item(TTR("Load..."), MENU_LOAD_FILE);
- use_popup_menu_position = false;
+ use_position_from_popup_menu = false;
}
Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const {
@@ -288,7 +292,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
anode = EditorSettings::get_singleton()->get_resource_clipboard();
ERR_FAIL_COND(!anode.is_valid());
base_name = anode->get_class();
- } else if (add_options[p_idx].type != String()) {
+ } else if (!add_options[p_idx].type.is_empty()) {
AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instantiate(add_options[p_idx].type));
ERR_FAIL_COND(!an);
anode = Ref<AnimationNode>(an);
@@ -309,9 +313,14 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
return;
}
+ if (!from_node.is_empty() && anode->get_input_count() == 0) {
+ from_node = "";
+ return;
+ }
+
Point2 instance_pos = graph->get_scroll_ofs();
- if (use_popup_menu_position) {
- instance_pos += popup_menu_position;
+ if (use_position_from_popup_menu) {
+ instance_pos += position_from_popup_menu;
} else {
instance_pos += graph->get_size() * 0.5;
}
@@ -328,11 +337,52 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
undo_redo->create_action(TTR("Add Node to BlendTree"));
undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE);
undo_redo->add_undo_method(blend_tree.ptr(), "remove_node", name);
+
+ if (!from_node.is_empty()) {
+ undo_redo->add_do_method(blend_tree.ptr(), "connect_node", name, 0, from_node);
+ from_node = "";
+ }
+ if (!to_node.is_empty() && to_slot != -1) {
+ undo_redo->add_do_method(blend_tree.ptr(), "connect_node", to_node, to_slot, name);
+ to_node = "";
+ to_slot = -1;
+ }
+
undo_redo->add_do_method(this, "_update_graph");
undo_redo->add_undo_method(this, "_update_graph");
undo_redo->commit_action();
}
+void AnimationNodeBlendTreeEditor::_popup(bool p_has_input_ports, const Vector2 &p_popup_position, const Vector2 &p_node_position) {
+ _update_options_menu(p_has_input_ports);
+ use_position_from_popup_menu = true;
+ position_from_popup_menu = p_node_position;
+ add_node->get_popup()->set_position(p_popup_position);
+ add_node->get_popup()->reset_size();
+ add_node->get_popup()->popup();
+}
+
+void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) {
+ _popup(false, graph->get_screen_position() + graph->get_local_mouse_position(), p_position);
+}
+
+void AnimationNodeBlendTreeEditor::_connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position) {
+ Ref<AnimationNode> node = blend_tree->get_node(p_from);
+ if (node.is_valid()) {
+ from_node = p_from;
+ _popup(true, p_release_position, graph->get_global_mouse_position());
+ }
+}
+
+void AnimationNodeBlendTreeEditor::_connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position) {
+ Ref<AnimationNode> node = blend_tree->get_node(p_to);
+ if (node.is_valid()) {
+ to_node = p_to;
+ to_slot = p_to_slot;
+ _popup(false, p_release_position, graph->get_global_mouse_position());
+ }
+}
+
void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) {
updating = true;
undo_redo->create_action(TTR("Node Moved"));
@@ -431,14 +481,6 @@ void AnimationNodeBlendTreeEditor::_delete_nodes_request() {
undo_redo->commit_action();
}
-void AnimationNodeBlendTreeEditor::_popup_request(const Vector2 &p_position) {
- _update_options_menu();
- use_popup_menu_position = true;
- popup_menu_position = graph->get_local_mouse_position();
- add_node->get_popup()->set_position(p_position);
- add_node->get_popup()->popup();
-}
-
void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) {
GraphNode *gn = Object::cast_to<GraphNode>(p_node);
ERR_FAIL_COND(!gn);
@@ -558,7 +600,7 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano
String accum;
for (int i = 0; i < path.get_name_count(); i++) {
String name = path.get_name(i);
- if (accum != String()) {
+ if (!accum.is_empty()) {
accum += "/";
}
accum += name;
@@ -710,7 +752,7 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
if (error != error_label->get_text()) {
error_label->set_text(error);
- if (error != String()) {
+ if (!error.is_empty()) {
error_panel->show();
} else {
error_panel->hide();
@@ -735,16 +777,16 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) {
}
if (player) {
- for (Map<StringName, ProgressBar *>::Element *E = animations.front(); E; E = E->next()) {
- Ref<AnimationNodeAnimation> an = blend_tree->get_node(E->key());
+ for (const KeyValue<StringName, ProgressBar *> &E : animations) {
+ Ref<AnimationNodeAnimation> an = blend_tree->get_node(E.key);
if (an.is_valid()) {
if (player->has_animation(an->get_animation())) {
Ref<Animation> anim = player->get_animation(an->get_animation());
if (anim.is_valid()) {
- E->get()->set_max(anim->get_length());
+ E.value->set_max(anim->get_length());
//StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + E.input_node;
- StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E->key()) + "/time";
- E->get()->set_value(AnimationTreeEditor::get_singleton()->get_tree()->get(time_path));
+ StringName time_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E.key) + "/time";
+ E.value->set_value(AnimationTreeEditor::get_singleton()->get_tree()->get(time_path));
}
}
}
@@ -779,13 +821,13 @@ AnimationNodeBlendTreeEditor *AnimationNodeBlendTreeEditor::singleton = nullptr;
void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<AnimationNode> p_node) {
String prev_name = blend_tree->get_node_name(p_node);
- ERR_FAIL_COND(prev_name == String());
+ ERR_FAIL_COND(prev_name.is_empty());
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(prev_name));
ERR_FAIL_COND(!gn);
const String &new_name = p_text;
- ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1);
+ ERR_FAIL_COND(new_name.is_empty() || new_name.find(".") != -1 || new_name.find("/") != -1);
if (new_name == prev_name) {
return; //nothing to do
@@ -877,7 +919,7 @@ void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) {
AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
singleton = this;
updating = false;
- use_popup_menu_position = false;
+ use_position_from_popup_menu = false;
graph = memnew(GraphEdit);
add_child(graph);
@@ -890,6 +932,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
graph->connect("scroll_offset_changed", callable_mp(this, &AnimationNodeBlendTreeEditor::_scroll_changed));
graph->connect("delete_nodes_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_delete_nodes_request));
graph->connect("popup_request", callable_mp(this, &AnimationNodeBlendTreeEditor::_popup_request));
+ graph->connect("connection_to_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_to_empty));
+ graph->connect("connection_from_empty", callable_mp(this, &AnimationNodeBlendTreeEditor::_connection_from_empty));
float graph_minimap_opacity = EditorSettings::get_singleton()->get("editors/visual_editors/minimap_opacity");
graph->set_minimap_opacity(graph_minimap_opacity);
@@ -902,16 +946,16 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
add_node->set_text(TTR("Add Node..."));
graph->get_zoom_hbox()->move_child(add_node, 0);
add_node->get_popup()->connect("id_pressed", callable_mp(this, &AnimationNodeBlendTreeEditor::_add_node));
- add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu));
+ add_node->connect("about_to_popup", callable_mp(this, &AnimationNodeBlendTreeEditor::_update_options_menu), varray(false));
add_options.push_back(AddOption("Animation", "AnimationNodeAnimation"));
- add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot"));
- add_options.push_back(AddOption("Add2", "AnimationNodeAdd2"));
- add_options.push_back(AddOption("Add3", "AnimationNodeAdd3"));
- add_options.push_back(AddOption("Blend2", "AnimationNodeBlend2"));
- add_options.push_back(AddOption("Blend3", "AnimationNodeBlend3"));
- add_options.push_back(AddOption("Seek", "AnimationNodeTimeSeek"));
- add_options.push_back(AddOption("TimeScale", "AnimationNodeTimeScale"));
+ add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot", 2));
+ add_options.push_back(AddOption("Add2", "AnimationNodeAdd2", 2));
+ add_options.push_back(AddOption("Add3", "AnimationNodeAdd3", 3));
+ add_options.push_back(AddOption("Blend2", "AnimationNodeBlend2", 2));
+ add_options.push_back(AddOption("Blend3", "AnimationNodeBlend3", 3));
+ add_options.push_back(AddOption("Seek", "AnimationNodeTimeSeek", 1));
+ add_options.push_back(AddOption("TimeScale", "AnimationNodeTimeScale", 1));
add_options.push_back(AddOption("Transition", "AnimationNodeTransition"));
add_options.push_back(AddOption("BlendTree", "AnimationNodeBlendTree"));
add_options.push_back(AddOption("BlendSpace1D", "AnimationNodeBlendSpace1D"));
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h
index 9f09069719..68da5c6f79 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.h
+++ b/editor/plugins/animation_blend_tree_editor_plugin.h
@@ -49,8 +49,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
Ref<AnimationNodeBlendTree> blend_tree;
GraphEdit *graph;
MenuButton *add_node;
- Vector2 popup_menu_position;
- bool use_popup_menu_position;
+ Vector2 position_from_popup_menu;
+ bool use_position_from_popup_menu;
PanelContainer *error_panel;
Label *error_label;
@@ -64,22 +64,28 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
Map<StringName, ProgressBar *> animations;
Vector<EditorProperty *> visible_properties;
+ String to_node = "";
+ int to_slot = -1;
+ String from_node = "";
+
void _update_graph();
struct AddOption {
String name;
String type;
Ref<Script> script;
- AddOption(const String &p_name = String(), const String &p_type = String()) :
+ int input_port_count;
+ AddOption(const String &p_name = String(), const String &p_type = String(), bool p_input_port_count = 0) :
name(p_name),
- type(p_type) {
+ type(p_type),
+ input_port_count(p_input_port_count) {
}
};
Vector<AddOption> add_options;
void _add_node(int p_idx);
- void _update_options_menu();
+ void _update_options_menu(bool p_has_input_ports = false);
static AnimationNodeBlendTreeEditor *singleton;
@@ -98,7 +104,6 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
void _anim_selected(int p_index, Array p_options, const String &p_node);
void _delete_request(const String &p_which);
void _delete_nodes_request();
- void _popup_request(const Vector2 &p_position);
bool _update_filters(const Ref<AnimationNode> &anode);
void _edit_filters(const String &p_which);
@@ -106,6 +111,11 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin {
void _filter_toggled();
Ref<AnimationNode> _filter_edit;
+ void _popup(bool p_has_input_ports, const Vector2 &p_popup_position, const Vector2 &p_node_position);
+ void _popup_request(const Vector2 &p_position);
+ void _connection_to_empty(const String &p_from, int p_from_slot, const Vector2 &p_release_position);
+ void _connection_from_empty(const String &p_to, int p_to_slot, const Vector2 &p_release_position);
+
void _property_changed(const StringName &p_property, const Variant &p_value, const String &p_field, bool p_changing);
void _removed_from_graph();
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 830b010d01..14390511de 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -41,6 +41,8 @@
#include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning.
#include "editor/plugins/node_3d_editor_plugin.h" // For onion skinning.
#include "scene/main/window.h"
+#include "scene/resources/animation.h"
+#include "scene/scene_string_names.h"
#include "servers/rendering_server.h"
void AnimationPlayerEditor::_node_removed(Node *p_node) {
@@ -72,7 +74,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
if (player->has_animation(animname)) {
Ref<Animation> anim = player->get_animation(animname);
if (!anim.is_null()) {
- frame->set_max(anim->get_length());
+ frame->set_max((double)anim->get_length());
}
}
}
@@ -120,7 +122,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
Ref<Image> autoplay_img = autoplay_icon->get_image();
Ref<Image> reset_img = reset_icon->get_image();
Ref<Image> autoplay_reset_img;
- Size2 icon_size = Size2(autoplay_img->get_width(), autoplay_img->get_height());
+ Size2 icon_size = autoplay_img->get_size();
autoplay_reset_img.instantiate();
autoplay_reset_img->create(icon_size.x * 2, icon_size.y, false, autoplay_img->get_format());
autoplay_reset_img->blit_rect(autoplay_img, Rect2(Point2(), icon_size), Point2());
@@ -188,7 +190,7 @@ void AnimationPlayerEditor::_play_pressed() {
current = animation->get_item_text(animation->get_selected());
}
- if (current != "") {
+ if (!current.is_empty()) {
if (current == player->get_assigned_animation()) {
player->stop(); //so it won't blend with itself
}
@@ -205,7 +207,7 @@ void AnimationPlayerEditor::_play_from_pressed() {
current = animation->get_item_text(animation->get_selected());
}
- if (current != "") {
+ if (!current.is_empty()) {
float time = player->get_current_animation_position();
if (current == player->get_assigned_animation() && player->is_playing()) {
@@ -226,7 +228,7 @@ void AnimationPlayerEditor::_play_bw_pressed() {
current = animation->get_item_text(animation->get_selected());
}
- if (current != "") {
+ if (!current.is_empty()) {
if (current == player->get_assigned_animation()) {
player->stop(); //so it won't blend with itself
}
@@ -243,7 +245,7 @@ void AnimationPlayerEditor::_play_bw_from_pressed() {
current = animation->get_item_text(animation->get_selected());
}
- if (current != "") {
+ if (!current.is_empty()) {
float time = player->get_current_animation_position();
if (current == player->get_assigned_animation()) {
player->stop(); //so it won't blend with itself
@@ -278,7 +280,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
current = animation->get_item_text(animation->get_selected());
}
- if (current != "") {
+ if (!current.is_empty()) {
player->set_assigned_animation(current);
Ref<Animation> anim = player->get_animation(current);
@@ -289,7 +291,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
track_editor->set_root(root);
}
}
- frame->set_max(anim->get_length());
+ frame->set_max((double)anim->get_length());
} else {
track_editor->set_animation(Ref<Animation>());
@@ -298,7 +300,7 @@ void AnimationPlayerEditor::_animation_selected(int p_which) {
autoplay->set_pressed(current == player->get_autoplay());
- AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying();
EditorNode::get_singleton()->update_keying();
_animation_key_editor_seek(timeline_position, false);
}
@@ -395,7 +397,7 @@ void AnimationPlayerEditor::_animation_save_as(const Ref<Resource> &p_resource)
String path;
//file->set_current_path(current_path);
- if (p_resource->get_path() != "") {
+ if (!p_resource->get_path().is_empty()) {
path = p_resource->get_path();
if (extensions.size()) {
if (extensions.find(p_resource->get_path().get_extension().to_lower()) == nullptr) {
@@ -404,7 +406,7 @@ void AnimationPlayerEditor::_animation_save_as(const Ref<Resource> &p_resource)
}
} else {
if (extensions.size()) {
- if (p_resource->get_name() != "") {
+ if (!p_resource->get_name().is_empty()) {
path = p_resource->get_name() + "." + extensions.front()->get().to_lower();
} else {
String resource_name_snake_case = p_resource->get_class().camelcase_to_underscore();
@@ -474,7 +476,7 @@ double AnimationPlayerEditor::_get_editor_step() const {
ERR_FAIL_COND_V(!anim.is_valid(), 0.0);
// Use more precise snapping when holding Shift
- return Input::get_singleton()->is_key_pressed(KEY_SHIFT) ? anim->get_step() * 0.25 : anim->get_step();
+ return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? anim->get_step() * 0.25 : anim->get_step();
}
return 0.0;
@@ -484,7 +486,7 @@ void AnimationPlayerEditor::_animation_name_edited() {
player->stop();
String new_name = name->get_text();
- if (new_name == "" || new_name.find(":") != -1 || new_name.find("/") != -1) {
+ if (new_name.is_empty() || new_name.find(":") != -1 || new_name.find("/") != -1) {
error_dialog->set_text(TTR("Invalid animation name!"));
error_dialog->popup_centered();
return;
@@ -718,7 +720,7 @@ void AnimationPlayerEditor::_animation_edit() {
void AnimationPlayerEditor::_save_animation(String p_file) {
String current = animation->get_item_text(animation->get_selected());
- if (current != "") {
+ if (!current.is_empty()) {
Ref<Animation> anim = player->get_animation(current);
ERR_FAIL_COND(!Object::cast_to<Resource>(*anim));
@@ -826,7 +828,7 @@ void AnimationPlayerEditor::_update_player() {
pin->set_disabled(player == nullptr);
if (!player) {
- AnimationPlayerEditor::singleton->get_track_editor()->update_keying();
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->update_keying();
EditorNode::get_singleton()->update_keying();
return;
}
@@ -835,12 +837,12 @@ void AnimationPlayerEditor::_update_player() {
for (const StringName &E : animlist) {
Ref<Texture2D> icon;
if (E == player->get_autoplay()) {
- if (E == "RESET") {
+ if (E == SceneStringNames::get_singleton()->RESET) {
icon = autoplay_reset_icon;
} else {
icon = autoplay_icon;
}
- } else if (E == "RESET") {
+ } else if (E == SceneStringNames::get_singleton()->RESET) {
icon = reset_icon;
}
animation->add_icon_item(icon, E);
@@ -904,7 +906,7 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) {
}
}
-void AnimationPlayerEditor::forward_canvas_force_draw_over_viewport(Control *p_overlay) {
+void AnimationPlayerEditor::forward_force_draw_over_viewport(Control *p_overlay) {
if (!onion.can_overlay) {
return;
}
@@ -1005,7 +1007,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool
updating = true;
String current = player->get_assigned_animation();
- if (current == "" || !player->has_animation(current)) {
+ if (current.is_empty() || !player->has_animation(current)) {
updating = false;
current = "";
return;
@@ -1014,7 +1016,7 @@ void AnimationPlayerEditor::_seek_value_changed(float p_value, bool p_set, bool
Ref<Animation> anim;
anim = player->get_animation(current);
- float pos = CLAMP(anim->get_length() * (p_value / frame->get_max()), 0, anim->get_length());
+ float pos = CLAMP((double)anim->get_length() * (p_value / frame->get_max()), 0, (double)anim->get_length());
if (track_editor->is_snap_enabled()) {
pos = Math::snapped(pos, _get_editor_step());
}
@@ -1084,7 +1086,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
}
Ref<Animation> anim;
- if (current != String()) {
+ if (!current.is_empty()) {
anim = player->get_animation(current);
}
@@ -1139,7 +1141,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) {
}
String name = anim2->get_name();
- if (name == "") {
+ if (name.is_empty()) {
name = TTR("Pasted Animation");
}
@@ -1228,7 +1230,7 @@ void AnimationPlayerEditor::unhandled_key_input(const Ref<InputEvent> &p_ev) {
Ref<InputEventKey> k = p_ev;
if (is_visible_in_tree() && k.is_valid() && k->is_pressed() && !k->is_echo() && !k->is_alt_pressed() && !k->is_ctrl_pressed() && !k->is_meta_pressed()) {
switch (k->get_keycode()) {
- case KEY_A: {
+ case Key::A: {
if (!k->is_shift_pressed()) {
_play_bw_from_pressed();
} else {
@@ -1236,11 +1238,11 @@ void AnimationPlayerEditor::unhandled_key_input(const Ref<InputEvent> &p_ev) {
}
accept_event();
} break;
- case KEY_S: {
+ case Key::S: {
_stop_pressed();
accept_event();
} break;
- case KEY_D: {
+ case Key::D: {
if (!k->is_shift_pressed()) {
_play_from_pressed();
} else {
@@ -1424,7 +1426,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
float pos = cpos + step_off * anim->get_step();
- bool valid = anim->has_loop() || (pos >= 0 && pos <= anim->get_length());
+ bool valid = anim->get_loop_mode() != Animation::LoopMode::LOOP_NONE || (pos >= 0 && pos <= anim->get_length());
onion.captures_valid.write[cidx] = valid;
if (valid) {
player->seek(pos, true);
@@ -1466,15 +1468,15 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() {
}
void AnimationPlayerEditor::_start_onion_skinning() {
- // FIXME: Using "idle_frame" makes onion layers update one frame behind the current.
- if (!get_tree()->is_connected("idle_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) {
- get_tree()->connect("idle_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred));
+ // FIXME: Using "process_frame" makes onion layers update one frame behind the current.
+ if (!get_tree()->is_connected("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) {
+ get_tree()->connect("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred));
}
}
void AnimationPlayerEditor::_stop_onion_skinning() {
- if (get_tree()->is_connected("idle_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) {
- get_tree()->disconnect("idle_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred));
+ if (get_tree()->is_connected("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred))) {
+ get_tree()->disconnect("process_frame", callable_mp(this, &AnimationPlayerEditor::_prepare_onion_layers_1_deferred));
_free_onion_layers();
diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h
index be80b7f4e3..26bcff891d 100644
--- a/editor/plugins/animation_player_editor_plugin.h
+++ b/editor/plugins/animation_player_editor_plugin.h
@@ -128,6 +128,7 @@ class AnimationPlayerEditor : public VBoxContainer {
bool updating_blends;
AnimationTrackEditor *track_editor;
+ static AnimationPlayerEditor *singleton;
// Onion skinning.
struct {
@@ -186,7 +187,6 @@ class AnimationPlayerEditor : public VBoxContainer {
void _scale_changed(const String &p_scale);
void _save_animation(String p_file);
void _load_animations(Vector<String> p_files);
- void _seek_frame_changed(const String &p_frame);
void _seek_value_changed(float p_value, bool p_set = false, bool p_timeline_only = false);
void _blend_editor_next_changed(const int p_idx);
@@ -216,7 +216,6 @@ class AnimationPlayerEditor : public VBoxContainer {
void _pin_pressed();
- AnimationPlayerEditor();
~AnimationPlayerEditor();
protected:
@@ -226,7 +225,8 @@ protected:
public:
AnimationPlayer *get_player() const;
- static AnimationPlayerEditor *singleton;
+
+ static AnimationPlayerEditor *get_singleton() { return singleton; }
bool is_pinned() const { return pin->is_pressed(); }
void unpin() { pin->set_pressed(false); }
@@ -238,7 +238,7 @@ public:
void set_undo_redo(UndoRedo *p_undo_redo) { undo_redo = p_undo_redo; }
void edit(AnimationPlayer *p_player);
- void forward_canvas_force_draw_over_viewport(Control *p_overlay);
+ void forward_force_draw_over_viewport(Control *p_overlay);
AnimationPlayerEditor(EditorNode *p_editor, AnimationPlayerEditorPlugin *p_plugin);
};
@@ -262,7 +262,8 @@ public:
virtual bool handles(Object *p_object) const override;
virtual void make_visible(bool p_visible) override;
- virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay) override { anim_editor->forward_canvas_force_draw_over_viewport(p_overlay); }
+ virtual void forward_canvas_force_draw_over_viewport(Control *p_overlay) override { anim_editor->forward_force_draw_over_viewport(p_overlay); }
+ virtual void forward_spatial_force_draw_over_viewport(Control *p_overlay) override { anim_editor->forward_force_draw_over_viewport(p_overlay); }
AnimationPlayerEditorPlugin(EditorNode *p_node);
~AnimationPlayerEditorPlugin();
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index a1f96f21bf..cf3bc58af9 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -66,7 +66,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
}
Ref<InputEventKey> k = p_event;
- if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_DELETE && !k->is_echo()) {
+ if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_keycode() == Key::KEY_DELETE && !k->is_echo()) {
if (selected_node != StringName() || selected_transition_to != StringName() || selected_transition_from != StringName()) {
_erase_selected();
accept_event();
@@ -76,7 +76,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
Ref<InputEventMouseButton> mb = p_event;
//Add new node
- if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) || (tool_create->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT))) {
+ if (mb.is_valid() && mb->is_pressed() && ((tool_select->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) || (tool_create->is_pressed() && mb->get_button_index() == MouseButton::LEFT))) {
menu->clear();
animations_menu->clear();
animations_to_add.clear();
@@ -118,13 +118,14 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
menu->add_separator();
menu->add_item(TTR("Load..."), MENU_LOAD_FILE);
- menu->set_position(state_machine_draw->get_screen_transform().xform(mb->get_position()));
+ menu->set_position(state_machine_draw->get_screen_position() + mb->get_position());
+ menu->reset_size();
menu->popup();
add_node_pos = mb->get_position() / EDSCALE + state_machine->get_graph_offset();
}
// select node or push a field inside
- if (mb.is_valid() && !mb->is_shift_pressed() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && !mb->is_shift_pressed() && mb->is_pressed() && tool_select->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
selected_transition_from = StringName();
selected_transition_to = StringName();
selected_node = StringName();
@@ -151,7 +152,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
edit_rect.position -= line_sb->get_offset();
edit_rect.size += line_sb->get_minimum_size();
- name_edit_popup->set_position(state_machine_draw->get_screen_transform().xform(edit_rect.position));
+ name_edit_popup->set_position(state_machine_draw->get_screen_position() + edit_rect.position);
name_edit_popup->set_size(edit_rect.size);
name_edit->set_text(node_rects[i].node_name);
name_edit_popup->popup();
@@ -216,7 +217,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
}
//end moving node
- if (mb.is_valid() && dragging_selected_attempt && mb->get_button_index() == MOUSE_BUTTON_LEFT && !mb->is_pressed()) {
+ if (mb.is_valid() && dragging_selected_attempt && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
if (dragging_selected) {
Ref<AnimationNode> an = state_machine->get_node(selected_node);
updating = true;
@@ -237,7 +238,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
}
//connect nodes
- if (mb.is_valid() && ((tool_select->is_pressed() && mb->is_shift_pressed()) || tool_connect->is_pressed()) && mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed()) {
+ if (mb.is_valid() && ((tool_select->is_pressed() && mb->is_shift_pressed()) || tool_connect->is_pressed()) && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order
if (node_rects[i].node.has_point(mb->get_position())) { //select node since nothing else was selected
connecting = true;
@@ -250,7 +251,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
}
//end connecting nodes
- if (mb.is_valid() && connecting && mb->get_button_index() == MOUSE_BUTTON_LEFT && !mb->is_pressed()) {
+ if (mb.is_valid() && connecting && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {
if (connecting_to_node != StringName()) {
if (state_machine->has_transition(connecting_from, connecting_to_node)) {
EditorNode::get_singleton()->show_warning(TTR("Transition exists!"));
@@ -284,7 +285,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
Ref<InputEventMouseMotion> mm = p_event;
//pan window
- if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE) {
+ if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) {
h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x);
v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y);
}
@@ -437,7 +438,7 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) {
return;
}
- if (base_name == String()) {
+ if (base_name.is_empty()) {
base_name = node->get_class().replace_first("AnimationNode", "");
}
@@ -760,12 +761,12 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
bool onstart = state_machine->get_start_node() == name;
if (onstart) {
- state_machine_draw->draw_string(font, offset + Vector2(0, -font->get_height(font_size) - 3 * EDSCALE + font->get_ascent(font_size)), TTR("Start"), HALIGN_LEFT, -1, font_size, font_color);
+ state_machine_draw->draw_string(font, offset + Vector2(0, -font->get_height(font_size) - 3 * EDSCALE + font->get_ascent(font_size)), TTR("Start"), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
}
if (state_machine->get_end_node() == name) {
int endofs = nr.node.size.x - font->get_string_size(TTR("End"), font_size).x;
- state_machine_draw->draw_string(font, offset + Vector2(endofs, -font->get_height(font_size) - 3 * EDSCALE + font->get_ascent(font_size)), TTR("End"), HALIGN_LEFT, -1, font_size, font_color);
+ state_machine_draw->draw_string(font, offset + Vector2(endofs, -font->get_height(font_size) - 3 * EDSCALE + font->get_ascent(font_size)), TTR("End"), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
}
offset.x += sb->get_offset().x;
@@ -785,7 +786,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
nr.name.position = offset + Vector2(0, (h - font->get_height(font_size)) / 2).floor();
nr.name.size = Vector2(strsize, font->get_height(font_size));
- state_machine_draw->draw_string(font, nr.name.position + Vector2(0, font->get_ascent(font_size)), name, HALIGN_LEFT, -1, font_size, font_color);
+ state_machine_draw->draw_string(font, nr.name.position + Vector2(0, font->get_ascent(font_size)), name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
offset.x += strsize + sep;
if (needs_editor) {
@@ -926,7 +927,7 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
if (error != error_label->get_text()) {
error_label->set_text(error);
- if (error != String()) {
+ if (!error.is_empty()) {
error_panel->show();
} else {
error_panel->hide();
@@ -1058,7 +1059,7 @@ void AnimationNodeStateMachineEditor::_removed_from_graph() {
void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) {
const String &new_name = p_text;
- ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1);
+ ERR_FAIL_COND(new_name.is_empty() || new_name.find(".") != -1 || new_name.find("/") != -1);
if (new_name == prev_name) {
return; // Nothing to do.
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index cd84be0c25..d65ed9cea2 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -55,7 +55,7 @@ void AnimationTreeEditor::edit(AnimationTree *p_tree) {
tree = p_tree;
Vector<String> path;
- if (tree->has_meta("_tree_edit_path")) {
+ if (tree && tree->has_meta("_tree_edit_path")) {
path = tree->get_meta("_tree_edit_path");
edit_path(path);
} else {
@@ -226,8 +226,7 @@ AnimationTreeEditor::AnimationTreeEditor() {
AnimationNodeAnimation::get_editable_animation_list = get_animation_list;
path_edit = memnew(ScrollContainer);
add_child(path_edit);
- path_edit->set_enable_h_scroll(true);
- path_edit->set_enable_v_scroll(false);
+ path_edit->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
path_hb = memnew(HBoxContainer);
path_edit->add_child(path_hb);
path_hb->add_child(memnew(Label(TTR("Path:"))));
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 5405723d10..951af92467 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -230,7 +230,8 @@ void EditorAssetLibraryItemDescription::configure(const String &p_title, int p_a
description->add_text(TTR("View Files"));
description->pop();
description->add_text("\n" + TTR("Description:") + "\n\n");
- description->append_bbcode(p_description);
+ description->append_text(p_description);
+ description->set_selection_enabled(true);
set_title(p_title);
}
@@ -288,8 +289,7 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() {
previews = memnew(ScrollContainer);
previews_bg->add_child(previews);
- previews->set_enable_v_scroll(false);
- previews->set_enable_h_scroll(true);
+ previews->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
preview_hb = memnew(HBoxContainer);
preview_hb->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -344,7 +344,7 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int
if (p_code != 200) {
error_text = TTR("Request failed, return code:") + " " + itos(p_code);
status->set_text(TTR("Failed:") + " " + itos(p_code));
- } else if (sha256 != "") {
+ } else if (!sha256.is_empty()) {
String download_sha256 = FileAccess::get_sha256(download->get_download_file());
if (sha256 != download_sha256) {
error_text = TTR("Bad download hash, assuming file has been tampered with.") + "\n";
@@ -355,7 +355,7 @@ void EditorAssetLibraryItemDownload::_http_download_completed(int p_status, int
} break;
}
- if (error_text != String()) {
+ if (!error_text.is_empty()) {
download_error->set_text(TTR("Asset Download Error:") + "\n" + error_text);
download_error->popup_centered();
// Let the user retry the download.
@@ -620,7 +620,7 @@ void EditorAssetLibrary::unhandled_key_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventKey> key = p_event;
if (key.is_valid() && key->is_pressed()) {
- if (key->get_keycode_with_modifiers() == (KEY_MASK_CMD | KEY_F) && is_visible_in_tree()) {
+ if (key->get_keycode_with_modifiers() == (KeyModifierMask::CMD | Key::F) && is_visible_in_tree()) {
filter->grab_focus();
filter->select_all();
accept_event();
@@ -830,9 +830,9 @@ void EditorAssetLibrary::_update_image_queue() {
int current_images = 0;
List<int> to_delete;
- for (Map<int, ImageQueue>::Element *E = image_queue.front(); E; E = E->next()) {
- if (!E->get().active && current_images < max_images) {
- String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + E->get().image_url.md5_text());
+ for (KeyValue<int, ImageQueue> &E : image_queue) {
+ if (!E.value.active && current_images < max_images) {
+ String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + E.value.image_url.md5_text());
Vector<String> headers;
if (FileAccess::exists(cache_filename_base + ".etag") && FileAccess::exists(cache_filename_base + ".data")) {
@@ -844,14 +844,14 @@ void EditorAssetLibrary::_update_image_queue() {
}
}
- Error err = E->get().request->request(E->get().image_url, headers);
+ Error err = E.value.request->request(E.value.image_url, headers);
if (err != OK) {
- to_delete.push_back(E->key());
+ to_delete.push_back(E.key);
} else {
- E->get().active = true;
+ E.value.active = true;
}
current_images++;
- } else if (E->get().active) {
+ } else if (E.value.active) {
current_images++;
}
}
@@ -922,7 +922,7 @@ void EditorAssetLibrary::_search(int p_page) {
support_list += String(support_key[i]) + "+";
}
}
- if (support_list != String()) {
+ if (!support_list.is_empty()) {
args += "&support=" + support_list.substr(0, support_list.length() - 1);
}
@@ -935,7 +935,7 @@ void EditorAssetLibrary::_search(int p_page) {
args += "&reverse=true";
}
- if (filter->get_text() != String()) {
+ if (!filter->get_text().is_empty()) {
args += "&filter=" + filter->get_text().uri_encode();
}
@@ -1188,7 +1188,7 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
library_vb->add_child(asset_bottom_page);
if (result.is_empty()) {
- if (filter->get_text() != String()) {
+ if (!filter->get_text().is_empty()) {
library_error->set_text(
vformat(TTR("No results for \"%s\"."), filter->get_text()));
} else {
@@ -1219,7 +1219,7 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
item->connect("author_selected", callable_mp(this, &EditorAssetLibrary::_select_author));
item->connect("category_selected", callable_mp(this, &EditorAssetLibrary::_select_category));
- if (r.has("icon_url") && r["icon_url"] != "") {
+ if (r.has("icon_url") && !r["icon_url"].operator String().is_empty()) {
_request_image(item->get_instance_id(), r["icon_url"], IMAGE_QUEUE_ICON, 0);
}
}
@@ -1256,7 +1256,7 @@ void EditorAssetLibrary::_http_request_completed(int p_status, int p_code, const
description->configure(r["title"], r["asset_id"], category_map[r["category_id"]], r["category_id"], r["author"], r["author_id"], r["cost"], r["version"], r["version_string"], r["description"], r["download_url"], r["browse_url"], r["download_hash"]);
- if (r.has("icon_url") && r["icon_url"] != "") {
+ if (r.has("icon_url") && !r["icon_url"].operator String().is_empty()) {
_request_image(description->get_instance_id(), r["icon_url"], IMAGE_QUEUE_ICON, 0);
}
@@ -1431,8 +1431,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
library_scroll_bg->set_v_size_flags(Control::SIZE_EXPAND_FILL);
library_scroll = memnew(ScrollContainer);
- library_scroll->set_enable_v_scroll(true);
- library_scroll->set_enable_h_scroll(false);
+ library_scroll->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
library_scroll_bg->add_child(library_scroll);
@@ -1454,11 +1453,11 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
library_vb_border->add_child(library_vb);
library_loading = memnew(Label(TTR("Loading...")));
- library_loading->set_align(Label::ALIGN_CENTER);
+ library_loading->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
library_vb->add_child(library_loading);
library_error = memnew(Label);
- library_error->set_align(Label::ALIGN_CENTER);
+ library_error->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
library_error->hide();
library_vb->add_child(library_error);
@@ -1499,8 +1498,7 @@ EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) {
set_process_unhandled_key_input(true); // Global shortcuts since there is no main element to be focused.
downloads_scroll = memnew(ScrollContainer);
- downloads_scroll->set_enable_h_scroll(true);
- downloads_scroll->set_enable_v_scroll(false);
+ downloads_scroll->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
library_main->add_child(downloads_scroll);
downloads_hb = memnew(HBoxContainer);
downloads_scroll->add_child(downloads_hb);
diff --git a/editor/plugins/asset_library_editor_plugin.h b/editor/plugins/asset_library_editor_plugin.h
index 286546f962..5fbf2833b2 100644
--- a/editor/plugins/asset_library_editor_plugin.h
+++ b/editor/plugins/asset_library_editor_plugin.h
@@ -285,7 +285,6 @@ class EditorAssetLibrary : public PanelContainer {
void _search_text_submitted(const String &p_text = "");
void _api_request(const String &p_request, RequestType p_request_type, const String &p_arguments = "");
void _http_request_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
- void _http_download_completed(int p_status, int p_code, const PackedStringArray &headers, const PackedByteArray &p_data);
void _filter_debounce_timer_timeout();
void _repository_changed(int p_repository_id);
diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp
index 482c08f50a..c621ade5c8 100644
--- a/editor/plugins/audio_stream_editor_plugin.cpp
+++ b/editor/plugins/audio_stream_editor_plugin.cpp
@@ -157,7 +157,7 @@ void AudioStreamEditor::_draw_indicator() {
void AudioStreamEditor::_on_input_indicator(Ref<InputEvent> p_event) {
const Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
_seek_to(mb->get_position().x);
}
@@ -232,7 +232,7 @@ AudioStreamEditor::AudioStreamEditor() {
hbox->add_child(_play_button);
_play_button->set_focus_mode(Control::FOCUS_NONE);
_play_button->connect("pressed", callable_mp(this, &AudioStreamEditor::_play));
- _play_button->set_shortcut(ED_SHORTCUT("inspector/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), KEY_SPACE));
+ _play_button->set_shortcut(ED_SHORTCUT("inspector/audio_preview_play_pause", TTR("Audio Preview Play/Pause"), Key::SPACE));
_stop_button = memnew(Button);
_stop_button->set_flat(true);
@@ -241,7 +241,7 @@ AudioStreamEditor::AudioStreamEditor() {
_stop_button->connect("pressed", callable_mp(this, &AudioStreamEditor::_stop));
_current_label = memnew(Label);
- _current_label->set_align(Label::ALIGN_RIGHT);
+ _current_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
_current_label->set_h_size_flags(SIZE_EXPAND_FILL);
_current_label->add_theme_font_override("font", EditorNode::get_singleton()->get_gui_base()->get_theme_font(SNAME("status_source"), SNAME("EditorFonts")));
_current_label->add_theme_font_size_override("font_size", EditorNode::get_singleton()->get_gui_base()->get_theme_font_size(SNAME("status_source_size"), SNAME("EditorFonts")));
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index d96cc1cd18..4a19e7752e 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -242,7 +242,7 @@ bool CanvasItemEditor::_is_node_movable(const Node *p_node, bool p_popup_warning
}
if (Object::cast_to<Control>(p_node) && Object::cast_to<Container>(p_node->get_parent())) {
if (p_popup_warning) {
- _popup_warning_temporarily(warning_child_of_container, 3.0);
+ EditorToaster::get_singleton()->popup_str("Children of a container get their position and size determined only by their parent.", EditorToaster::SEVERITY_WARNING);
}
return false;
}
@@ -333,7 +333,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig
snap_target[0] = SNAP_TARGET_NONE;
snap_target[1] = SNAP_TARGET_NONE;
- bool is_snap_active = smart_snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool is_snap_active = smart_snap_active ^ Input::get_singleton()->is_key_pressed(Key::CTRL);
// Smart snap using the canvas position
Vector2 output = p_target;
@@ -387,7 +387,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig
// Self center
if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) {
if (p_self_canvas_item->_edit_use_rect()) {
- Point2 center = p_self_canvas_item->get_global_transform_with_canvas().xform(p_self_canvas_item->_edit_get_rect().get_position() + p_self_canvas_item->_edit_get_rect().get_size() / 2.0);
+ Point2 center = p_self_canvas_item->get_global_transform_with_canvas().xform(p_self_canvas_item->_edit_get_rect().get_center());
_snap_if_closer_point(p_target, output, snap_target, center, SNAP_TARGET_SELF, rotation);
} else {
Point2 position = p_self_canvas_item->get_global_transform_with_canvas().xform(Point2());
@@ -461,7 +461,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsig
}
real_t CanvasItemEditor::snap_angle(real_t p_target, real_t p_start) const {
- if (((smart_snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(KEY_CTRL)) && snap_rotation_step != 0) {
+ if (((smart_snap_active || snap_rotation) ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) && snap_rotation_step != 0) {
if (snap_relative) {
return Math::snapped(p_target - snap_rotation_offset, snap_rotation_step) + snap_rotation_offset + (p_start - (int)(p_start / snap_rotation_step) * snap_rotation_step);
} else {
@@ -482,7 +482,7 @@ void CanvasItemEditor::unhandled_key_input(const Ref<InputEvent> &p_ev) {
}
if (k.is_valid()) {
- if (k->get_keycode() == KEY_CTRL || k->get_keycode() == KEY_ALT || k->get_keycode() == KEY_SHIFT) {
+ if (k->get_keycode() == Key::CTRL || k->get_keycode() == Key::ALT || k->get_keycode() == Key::SHIFT) {
viewport->update();
}
@@ -513,7 +513,7 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) {
}
void CanvasItemEditor::_keying_changed() {
- if (AnimationPlayerEditor::singleton->get_track_editor()->is_visible_in_tree()) {
+ if (AnimationPlayerEditor::get_singleton()->get_track_editor()->is_visible_in_tree()) {
animation_hb->show();
} else {
animation_hb->hide();
@@ -525,7 +525,7 @@ Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_li
// Handles the first element
CanvasItem *canvas_item = p_list.front()->get();
- Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().position + canvas_item->_edit_get_rect().size / 2), Size2());
+ Rect2 rect = Rect2(canvas_item->get_global_transform_with_canvas().xform(canvas_item->_edit_get_rect().get_center()), Size2());
// Expand with the other ones
for (CanvasItem *canvas_item2 : p_list) {
@@ -564,7 +564,7 @@ void CanvasItemEditor::_expand_encompassing_rect_using_children(Rect2 &r_rect, c
Transform2D xform = p_parent_xform * p_canvas_xform * canvas_item->get_transform();
Rect2 rect = canvas_item->_edit_get_rect();
if (r_first) {
- r_rect = Rect2(xform.xform(rect.position + rect.size / 2), Size2());
+ r_rect = Rect2(xform.xform(rect.get_center()), Size2());
r_first = false;
}
r_rect.expand_to(xform.xform(rect.position));
@@ -658,7 +658,7 @@ void CanvasItemEditor::_get_canvas_items_at_pos(const Point2 &p_pos, Vector<_Sel
//Remove the item if invalid
if (!canvas_item || duplicate || (canvas_item != scene && canvas_item->get_owner() != scene && !scene->is_editable_instance(canvas_item->get_owner())) || (!p_allow_locked && _is_node_locked(canvas_item))) {
- r_items.remove(i);
+ r_items.remove_at(i);
i--;
} else {
r_items.write[i].item = canvas_item;
@@ -748,8 +748,8 @@ bool CanvasItemEditor::_select_click_on_item(CanvasItem *item, Point2 p_click_po
List<CanvasItem *> CanvasItemEditor::_get_edited_canvas_items(bool retreive_locked, bool remove_canvas_item_if_parent_in_selection) {
List<CanvasItem *> selection;
- for (Map<Node *, Object *>::Element *E = editor_selection->get_selection().front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : editor_selection->get_selection()) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (canvas_item && canvas_item->is_visible_in_tree() && canvas_item->get_viewport() == EditorNode::get_singleton()->get_scene_root() && (retreive_locked || !_is_node_locked(canvas_item))) {
CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (se) {
@@ -877,7 +877,7 @@ void CanvasItemEditor::_selection_result_pressed(int p_result) {
void CanvasItemEditor::_selection_menu_hide() {
selection_results.clear();
selection_menu->clear();
- selection_menu->set_size(Vector2(0, 0));
+ selection_menu->reset_size();
}
void CanvasItemEditor::_add_node_pressed(int p_result) {
@@ -950,7 +950,7 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve
}
// Start dragging a guide
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) {
// Press button
if (b->get_position().x < RULER_WIDTH && b->get_position().y < RULER_WIDTH) {
// Drag a new double guide
@@ -1009,7 +1009,7 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve
}
// Release confirms the guide move
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && !b->is_pressed()) {
if (show_guides && EditorNode::get_singleton()->get_edited_scene()) {
Transform2D xform = viewport_scrollable->get_transform() * transform;
@@ -1045,7 +1045,7 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve
}
} else {
if (dragged_guide_index >= 0) {
- vguides.remove(dragged_guide_index);
+ vguides.remove_at(dragged_guide_index);
undo_redo->create_action(TTR("Remove Vertical Guide"));
if (vguides.is_empty()) {
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "remove_meta", "_edit_vertical_guides_");
@@ -1078,7 +1078,7 @@ bool CanvasItemEditor::_gui_input_rulers_and_guides(const Ref<InputEvent> &p_eve
}
} else {
if (dragged_guide_index >= 0) {
- hguides.remove(dragged_guide_index);
+ hguides.remove_at(dragged_guide_index);
undo_redo->create_action(TTR("Remove Horizontal Guide"));
if (hguides.is_empty()) {
undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "remove_meta", "_edit_horizontal_guides_");
@@ -1123,7 +1123,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
if (pan_on_scroll) {
// Perform horizontal scrolling first so we can check for Shift being held.
if (b->is_pressed() &&
- (b->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT || (b->is_shift_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_UP))) {
+ (b->get_button_index() == MouseButton::WHEEL_LEFT || (b->is_shift_pressed() && b->get_button_index() == MouseButton::WHEEL_UP))) {
// Pan left
view_offset.x -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
@@ -1131,7 +1131,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
}
if (b->is_pressed() &&
- (b->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT || (b->is_shift_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN))) {
+ (b->get_button_index() == MouseButton::WHEEL_RIGHT || (b->is_shift_pressed() && b->get_button_index() == MouseButton::WHEEL_DOWN))) {
// Pan right
view_offset.x += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
@@ -1139,13 +1139,13 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
}
}
- if (b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN) {
+ if (b->is_pressed() && b->get_button_index() == MouseButton::WHEEL_DOWN) {
// Scroll or pan down
if (pan_on_scroll) {
view_offset.y += int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
} else {
- zoom_widget->set_zoom_by_increments(-1, Input::get_singleton()->is_key_pressed(KEY_ALT));
+ zoom_widget->set_zoom_by_increments(-1, Input::get_singleton()->is_key_pressed(Key::ALT));
if (!Math::is_equal_approx(b->get_factor(), 1.0f)) {
// Handle high-precision (analog) scrolling.
zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
@@ -1155,13 +1155,13 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
return true;
}
- if (b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_WHEEL_UP) {
+ if (b->is_pressed() && b->get_button_index() == MouseButton::WHEEL_UP) {
// Scroll or pan up
if (pan_on_scroll) {
view_offset.y -= int(EditorSettings::get_singleton()->get("editors/2d/pan_speed")) / zoom * b->get_factor();
update_viewport();
} else {
- zoom_widget->set_zoom_by_increments(1, Input::get_singleton()->is_key_pressed(KEY_ALT));
+ zoom_widget->set_zoom_by_increments(1, Input::get_singleton()->is_key_pressed(Key::ALT));
if (!Math::is_equal_approx(b->get_factor(), 1.0f)) {
// Handle high-precision (analog) scrolling.
zoom_widget->set_zoom(zoom * ((zoom_widget->get_zoom() / zoom - 1.f) * b->get_factor() + 1.f));
@@ -1173,17 +1173,16 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
if (!panning) {
if (b->is_pressed() &&
- (b->get_button_index() == MOUSE_BUTTON_MIDDLE ||
- b->get_button_index() == MOUSE_BUTTON_RIGHT ||
- (b->get_button_index() == MOUSE_BUTTON_LEFT && tool == TOOL_PAN) ||
- (b->get_button_index() == MOUSE_BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_panning") && pan_pressed))) {
+ (b->get_button_index() == MouseButton::MIDDLE ||
+ (b->get_button_index() == MouseButton::LEFT && tool == TOOL_PAN) ||
+ (b->get_button_index() == MouseButton::LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_panning") && pan_pressed))) {
// Pan the viewport
panning = true;
}
}
if (panning) {
- if (!b->is_pressed() && (pan_on_scroll || (b->get_button_index() != MOUSE_BUTTON_WHEEL_DOWN && b->get_button_index() != MOUSE_BUTTON_WHEEL_UP))) {
+ if (!b->is_pressed() && (pan_on_scroll || (b->get_button_index() != MouseButton::WHEEL_DOWN && b->get_button_index() != MouseButton::WHEEL_UP))) {
// Stop panning the viewport (for any mouse button press except zooming)
panning = false;
}
@@ -1232,8 +1231,9 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event, bo
}
}
- if (is_pan_key) {
+ if (is_pan_key && pan_pressed != k->is_pressed()) {
pan_pressed = k->is_pressed();
+ _update_cursor();
}
}
@@ -1294,8 +1294,8 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
// Drag the pivot (in pivot mode / with V key)
if (drag_type == DRAG_NONE) {
- if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) ||
- (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == KEY_V)) {
+ if ((b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) ||
+ (k.is_valid() && k->is_pressed() && !k->is_echo() && k->get_keycode() == Key::V && tool == TOOL_SELECT && k->get_modifiers_mask() == Key::NONE)) {
List<CanvasItem *> selection = _get_edited_canvas_items();
// Filters the selection with nodes that allow setting the pivot
@@ -1345,8 +1345,8 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
// Confirm the pivot move
if (drag_selection.size() >= 1 &&
- ((b.is_valid() && !b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_LEFT && tool == TOOL_EDIT_PIVOT) ||
- (k.is_valid() && !k->is_pressed() && k->get_keycode() == KEY_V))) {
+ ((b.is_valid() && !b->is_pressed() && b->get_button_index() == MouseButton::LEFT && tool == TOOL_EDIT_PIVOT) ||
+ (k.is_valid() && !k->is_pressed() && k->get_keycode() == Key::V))) {
_commit_canvas_item_state(
drag_selection,
vformat(
@@ -1359,7 +1359,7 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
}
// Cancel a drag
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::RIGHT && b->is_pressed()) {
_restore_canvas_item_state(drag_selection);
drag_type = DRAG_NONE;
viewport->update();
@@ -1375,7 +1375,7 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
// Start rotation
if (drag_type == DRAG_NONE) {
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) {
if ((b->is_command_pressed() && !b->is_alt_pressed() && tool == TOOL_SELECT) || tool == TOOL_ROTATE) {
List<CanvasItem *> selection = _get_edited_canvas_items();
@@ -1418,7 +1418,7 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
}
// Confirms the node rotation
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && !b->is_pressed()) {
if (drag_selection.size() != 1) {
_commit_canvas_item_state(
drag_selection,
@@ -1442,7 +1442,7 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) {
}
// Cancel a drag
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::RIGHT && b->is_pressed()) {
_restore_canvas_item_state(drag_selection);
drag_type = DRAG_NONE;
viewport->update();
@@ -1456,12 +1456,12 @@ bool CanvasItemEditor::_gui_input_open_scene_on_double_click(const Ref<InputEven
Ref<InputEventMouseButton> b = p_event;
// Open a sub-scene on double-click
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && b->is_double_click() && tool == TOOL_SELECT) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && b->is_double_click() && tool == TOOL_SELECT) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
- if (canvas_item->get_filename() != "" && canvas_item != editor->get_edited_scene()) {
- editor->open_request(canvas_item->get_filename());
+ if (!canvas_item->get_scene_file_path().is_empty() && canvas_item != editor->get_edited_scene()) {
+ editor->open_request(canvas_item->get_scene_file_path());
return true;
}
}
@@ -1475,7 +1475,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) {
// Starts anchor dragging if needed
if (drag_type == DRAG_NONE) {
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && tool == TOOL_SELECT) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
Control *control = Object::cast_to<Control>(selection[0]);
@@ -1595,7 +1595,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) {
}
// Confirms new anchor position
- if (drag_selection.size() >= 1 && b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed()) {
+ if (drag_selection.size() >= 1 && b.is_valid() && b->get_button_index() == MouseButton::LEFT && !b->is_pressed()) {
_commit_canvas_item_state(
drag_selection,
vformat(TTR("Move CanvasItem \"%s\" Anchor"), drag_selection[0]->get_name()));
@@ -1604,7 +1604,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) {
}
// Cancel a drag
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::RIGHT && b->is_pressed()) {
_restore_canvas_item_state(drag_selection);
drag_type = DRAG_NONE;
viewport->update();
@@ -1620,7 +1620,7 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
// Drag resize handles
if (drag_type == DRAG_NONE) {
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && tool == TOOL_SELECT) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
@@ -1774,7 +1774,7 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
}
// Confirm resize
- if (drag_selection.size() >= 1 && b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed()) {
+ if (drag_selection.size() >= 1 && b.is_valid() && b->get_button_index() == MouseButton::LEFT && !b->is_pressed()) {
const Node2D *node2d = Object::cast_to<Node2D>(drag_selection[0]);
if (node2d) {
// Extends from Node2D.
@@ -1811,7 +1811,7 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
}
// Cancel a drag
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::RIGHT && b->is_pressed()) {
_restore_canvas_item_state(drag_selection);
snap_target[0] = SNAP_TARGET_NONE;
snap_target[1] = SNAP_TARGET_NONE;
@@ -1829,7 +1829,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
// Drag resize handles
if (drag_type == DRAG_NONE) {
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_ctrl_pressed()) || tool == TOOL_SCALE)) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && ((b->is_alt_pressed() && b->is_ctrl_pressed()) || tool == TOOL_SCALE)) {
List<CanvasItem *> selection = _get_edited_canvas_items();
if (selection.size() == 1) {
CanvasItem *canvas_item = selection[0];
@@ -1876,7 +1876,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
Transform2D simple_xform = (viewport->get_transform() * unscaled_transform).affine_inverse() * transform;
bool uniform = m->is_shift_pressed();
- bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
Point2 drag_from_local = simple_xform.xform(drag_from);
Point2 drag_to_local = simple_xform.xform(drag_to);
@@ -1925,7 +1925,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
}
// Confirm resize
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && !b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && !b->is_pressed()) {
if (drag_selection.size() != 1) {
_commit_canvas_item_state(
drag_selection,
@@ -1950,7 +1950,7 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) {
}
// Cancel a drag
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::RIGHT && b->is_pressed()) {
_restore_canvas_item_state(drag_selection);
drag_type = DRAG_NONE;
viewport->update();
@@ -1967,7 +1967,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
if (drag_type == DRAG_NONE) {
//Start moving the nodes
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed()) {
if ((b->is_alt_pressed() && !b->is_ctrl_pressed()) || tool == TOOL_MOVE) {
List<CanvasItem *> selection = _get_edited_canvas_items();
@@ -2050,7 +2050,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
}
// Confirm the move (only if it was moved)
- if (b.is_valid() && !b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (b.is_valid() && !b->is_pressed() && b->get_button_index() == MouseButton::LEFT) {
if (transform.affine_inverse().xform(b->get_position()) != drag_from) {
if (drag_selection.size() != 1) {
_commit_canvas_item_state(
@@ -2083,7 +2083,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
}
// Cancel a drag
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_pressed()) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::RIGHT && b->is_pressed()) {
_restore_canvas_item_state(drag_selection, true);
snap_target[0] = SNAP_TARGET_NONE;
snap_target[1] = SNAP_TARGET_NONE;
@@ -2095,7 +2095,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
// Move the canvas items with the arrow keys
if (k.is_valid() && k->is_pressed() && (tool == TOOL_SELECT || tool == TOOL_MOVE) &&
- (k->get_keycode() == KEY_UP || k->get_keycode() == KEY_DOWN || k->get_keycode() == KEY_LEFT || k->get_keycode() == KEY_RIGHT)) {
+ (k->get_keycode() == Key::UP || k->get_keycode() == Key::DOWN || k->get_keycode() == Key::LEFT || k->get_keycode() == Key::RIGHT)) {
if (!k->is_echo()) {
// Start moving the canvas items with the keyboard
drag_selection = _get_edited_canvas_items();
@@ -2112,13 +2112,13 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
bool move_local_base_rotated = k->is_ctrl_pressed() || k->is_meta_pressed();
Vector2 dir;
- if (k->get_keycode() == KEY_UP) {
+ if (k->get_keycode() == Key::UP) {
dir += Vector2(0, -1);
- } else if (k->get_keycode() == KEY_DOWN) {
+ } else if (k->get_keycode() == Key::DOWN) {
dir += Vector2(0, 1);
- } else if (k->get_keycode() == KEY_LEFT) {
+ } else if (k->get_keycode() == Key::LEFT) {
dir += Vector2(-1, 0);
- } else if (k->get_keycode() == KEY_RIGHT) {
+ } else if (k->get_keycode() == Key::RIGHT) {
dir += Vector2(1, 0);
}
if (k->is_shift_pressed()) {
@@ -2166,12 +2166,12 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
}
if (k.is_valid() && !k->is_pressed() && drag_type == DRAG_KEY_MOVE && (tool == TOOL_SELECT || tool == TOOL_MOVE) &&
- (k->get_keycode() == KEY_UP || k->get_keycode() == KEY_DOWN || k->get_keycode() == KEY_LEFT || k->get_keycode() == KEY_RIGHT)) {
+ (k->get_keycode() == Key::UP || k->get_keycode() == Key::DOWN || k->get_keycode() == Key::LEFT || k->get_keycode() == Key::RIGHT)) {
// Confirm canvas items move by arrow keys
- if ((!Input::get_singleton()->is_key_pressed(KEY_UP)) &&
- (!Input::get_singleton()->is_key_pressed(KEY_DOWN)) &&
- (!Input::get_singleton()->is_key_pressed(KEY_LEFT)) &&
- (!Input::get_singleton()->is_key_pressed(KEY_RIGHT))) {
+ if ((!Input::get_singleton()->is_key_pressed(Key::UP)) &&
+ (!Input::get_singleton()->is_key_pressed(Key::DOWN)) &&
+ (!Input::get_singleton()->is_key_pressed(Key::LEFT)) &&
+ (!Input::get_singleton()->is_key_pressed(Key::RIGHT))) {
if (drag_selection.size() > 1) {
_commit_canvas_item_state(
drag_selection,
@@ -2192,7 +2192,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
return true;
}
- return (k.is_valid() && (k->get_keycode() == KEY_UP || k->get_keycode() == KEY_DOWN || k->get_keycode() == KEY_LEFT || k->get_keycode() == KEY_RIGHT)); // Accept the key event in any case
+ return (k.is_valid() && (k->get_keycode() == Key::UP || k->get_keycode() == Key::DOWN || k->get_keycode() == Key::LEFT || k->get_keycode() == Key::RIGHT)); // Accept the key event in any case
}
bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
@@ -2202,8 +2202,8 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
if (drag_type == DRAG_NONE) {
if (b.is_valid() &&
- ((b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_alt_pressed() && tool == TOOL_SELECT) ||
- (b->get_button_index() == MOUSE_BUTTON_LEFT && tool == TOOL_LIST_SELECT))) {
+ ((b->get_button_index() == MouseButton::RIGHT && b->is_alt_pressed() && tool == TOOL_SELECT) ||
+ (b->get_button_index() == MouseButton::LEFT && tool == TOOL_LIST_SELECT))) {
// Popup the selection menu list
Point2 click = transform.affine_inverse().xform(b->get_position());
@@ -2258,21 +2258,22 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
}
selection_menu_additive_selection = b->is_shift_pressed();
- selection_menu->set_position(get_screen_transform().xform(b->get_position()));
+ selection_menu->set_position(get_screen_position() + b->get_position());
+ selection_menu->reset_size();
selection_menu->popup();
return true;
}
}
- if (b.is_valid() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_RIGHT && b->is_ctrl_pressed()) {
- add_node_menu->set_position(get_global_transform().xform(get_local_mouse_position()));
- add_node_menu->set_size(Vector2(1, 1));
+ if (b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) {
+ add_node_menu->reset_size();
+ add_node_menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
add_node_menu->popup();
node_create_position = transform.affine_inverse().xform((get_local_mouse_position()));
return true;
}
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && tool == TOOL_SELECT) {
// Single item selection
Point2 click = transform.affine_inverse().xform(b->get_position());
@@ -2339,7 +2340,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
if (selection2.size() > 0) {
drag_type = DRAG_MOVE;
- drag_from = click;
+ drag_from = drag_start_origin;
_save_canvas_item_state(drag_selection);
}
return true;
@@ -2348,7 +2349,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
}
if (drag_type == DRAG_BOX_SELECTION) {
- if (b.is_valid() && !b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (b.is_valid() && !b->is_pressed() && b->get_button_index() == MouseButton::LEFT) {
// Confirms box selection
Node *scene = editor->get_edited_scene();
if (scene) {
@@ -2377,7 +2378,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
return true;
}
- if (b.is_valid() && b->is_pressed() && b->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (b.is_valid() && b->is_pressed() && b->get_button_index() == MouseButton::RIGHT) {
// Cancel box selection
drag_type = DRAG_NONE;
viewport->update();
@@ -2392,7 +2393,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) {
}
}
- if (k.is_valid() && k->is_pressed() && k->get_keycode() == KEY_ESCAPE && drag_type == DRAG_NONE && tool == TOOL_SELECT) {
+ if (k.is_valid() && k->is_pressed() && k->get_keycode() == Key::ESCAPE && drag_type == DRAG_NONE && tool == TOOL_SELECT) {
// Unselect everything
editor_selection->clear();
viewport->update();
@@ -2414,7 +2415,7 @@ bool CanvasItemEditor::_gui_input_ruler_tool(const Ref<InputEvent> &p_event) {
ruler_tool_origin = snap_point(viewport->get_local_mouse_position() / zoom + view_offset);
}
- if (b.is_valid() && b->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (b.is_valid() && b->get_button_index() == MouseButton::LEFT) {
if (b->is_pressed()) {
ruler_tool_active = true;
} else {
@@ -2615,7 +2616,19 @@ void CanvasItemEditor::_update_cursor() {
c = CURSOR_HSIZE;
}
- viewport->set_default_cursor_shape(c);
+ if (pan_pressed) {
+ c = CURSOR_DRAG;
+ }
+
+ if (c != viewport->get_default_cursor_shape()) {
+ viewport->set_default_cursor_shape(c);
+
+ // Force refresh cursor if it's over the viewport.
+ if (viewport->get_global_rect().has_point(get_global_mouse_position())) {
+ DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)viewport->get_default_cursor_shape();
+ DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
+ }
+ }
}
void CanvasItemEditor::_draw_text_at_position(Point2 p_position, String p_string, Side p_side) {
@@ -2638,7 +2651,7 @@ void CanvasItemEditor::_draw_text_at_position(Point2 p_position, String p_string
p_position += Vector2(-text_size.x / 2, text_size.y + 5);
break;
}
- viewport->draw_string(font, p_position, p_string, HALIGN_LEFT, -1, font_size, color);
+ viewport->draw_string(font, p_position, p_string, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color);
}
void CanvasItemEditor::_draw_margin_at_position(int p_value, Point2 p_position, Side p_side) {
@@ -2698,7 +2711,7 @@ void CanvasItemEditor::_draw_guides() {
Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
Size2 text_size = font->get_string_size(str, font_size);
- viewport->draw_string(font, Point2(dragged_guide_pos.x + 10, RULER_WIDTH + text_size.y / 2 + 10), str, HALIGN_LEFT, -1, font_size, text_color, outline_size, outline_color);
+ viewport->draw_string(font, Point2(dragged_guide_pos.x + 10, RULER_WIDTH + text_size.y / 2 + 10), str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color, outline_size, outline_color);
viewport->draw_line(Point2(dragged_guide_pos.x, 0), Point2(dragged_guide_pos.x, viewport->get_size().y), guide_color, Math::round(EDSCALE));
}
if (drag_type == DRAG_DOUBLE_GUIDE || drag_type == DRAG_H_GUIDE) {
@@ -2706,7 +2719,7 @@ void CanvasItemEditor::_draw_guides() {
Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
Size2 text_size = font->get_string_size(str, font_size);
- viewport->draw_string(font, Point2(RULER_WIDTH + 10, dragged_guide_pos.y + text_size.y / 2 + 10), str, HALIGN_LEFT, -1, font_size, text_color, outline_size, outline_color);
+ viewport->draw_string(font, Point2(RULER_WIDTH + 10, dragged_guide_pos.y + text_size.y / 2 + 10), str, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color, outline_size, outline_color);
viewport->draw_line(Point2(0, dragged_guide_pos.y), Point2(viewport->get_size().x, dragged_guide_pos.y), guide_color, Math::round(EDSCALE));
}
}
@@ -2778,7 +2791,7 @@ void CanvasItemEditor::_draw_rulers() {
if (i % (major_subdivision * minor_subdivision) == 0) {
viewport->draw_line(Point2(position.x, 0), Point2(position.x, RULER_WIDTH), graduation_color, Math::round(EDSCALE));
real_t val = (ruler_transform * major_subdivide * minor_subdivide).xform(Point2(i, 0)).x;
- viewport->draw_string(font, Point2(position.x + 2, font->get_height(font_size)), TS->format_number(vformat(((int)val == val) ? "%d" : "%.1f", val)), HALIGN_LEFT, -1, font_size, font_color);
+ viewport->draw_string(font, Point2(position.x + 2, font->get_height(font_size)), TS->format_number(vformat(((int)val == val) ? "%d" : "%.1f", val)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
} else {
if (i % minor_subdivision == 0) {
viewport->draw_line(Point2(position.x, RULER_WIDTH * 0.33), Point2(position.x, RULER_WIDTH), graduation_color, Math::round(EDSCALE));
@@ -2798,7 +2811,7 @@ void CanvasItemEditor::_draw_rulers() {
Transform2D text_xform = Transform2D(-Math_PI / 2.0, Point2(font->get_height(font_size), position.y - 2));
viewport->draw_set_transform_matrix(viewport->get_transform() * text_xform);
- viewport->draw_string(font, Point2(), TS->format_number(vformat(((int)val == val) ? "%d" : "%.1f", val)), HALIGN_LEFT, -1, font_size, font_color);
+ viewport->draw_string(font, Point2(), TS->format_number(vformat(((int)val == val) ? "%d" : "%.1f", val)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
viewport->draw_set_transform_matrix(viewport->get_transform());
} else {
@@ -2901,14 +2914,6 @@ void CanvasItemEditor::_draw_ruler_tool() {
Point2 corner = Point2(begin.x, end.y);
Vector2 length_vector = (begin - end).abs() / zoom;
- bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x));
-
- viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3));
- if (draw_secondary_lines) {
- viewport->draw_line(begin, corner, ruler_secondary_color, Math::round(EDSCALE));
- viewport->draw_line(corner, end, ruler_secondary_color, Math::round(EDSCALE));
- }
-
Ref<Font> font = get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
int font_size = get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
Color font_color = get_theme_color(SNAME("font_color"), SNAME("Editor"));
@@ -2924,26 +2929,42 @@ void CanvasItemEditor::_draw_ruler_tool() {
Point2 text_pos = (begin + end) / 2 - Vector2(text_width / 2, text_height / 2);
text_pos.x = CLAMP(text_pos.x, text_width / 2, viewport->get_rect().size.x - text_width * 1.5);
text_pos.y = CLAMP(text_pos.y, text_height * 1.5, viewport->get_rect().size.y - text_height * 1.5);
- viewport->draw_string(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HALIGN_LEFT, -1, font_size, font_color, outline_size, outline_color);
+ if (begin.is_equal_approx(end)) {
+ viewport->draw_string(font, text_pos, (String)ruler_tool_origin, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color, outline_size, outline_color);
+ Ref<Texture2D> position_icon = get_theme_icon(SNAME("EditorPosition"), SNAME("EditorIcons"));
+ viewport->draw_texture(get_theme_icon(SNAME("EditorPosition"), SNAME("EditorIcons")), (ruler_tool_origin - view_offset) * zoom - position_icon->get_size() / 2);
+ return;
+ }
+
+ viewport->draw_string(font, text_pos, TS->format_number(vformat("%.1f px", length_vector.length())), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color, outline_size, outline_color);
+
+ bool draw_secondary_lines = !(Math::is_equal_approx(begin.y, corner.y) || Math::is_equal_approx(end.x, corner.x));
+
+ viewport->draw_line(begin, end, ruler_primary_color, Math::round(EDSCALE * 3));
if (draw_secondary_lines) {
- const real_t horizontal_angle_rad = atan2(length_vector.y, length_vector.x);
+ viewport->draw_line(begin, corner, ruler_secondary_color, Math::round(EDSCALE));
+ viewport->draw_line(corner, end, ruler_secondary_color, Math::round(EDSCALE));
+ }
+
+ if (draw_secondary_lines) {
+ const real_t horizontal_angle_rad = length_vector.angle();
const real_t vertical_angle_rad = Math_PI / 2.0 - horizontal_angle_rad;
const int horizontal_angle = round(180 * horizontal_angle_rad / Math_PI);
const int vertical_angle = round(180 * vertical_angle_rad / Math_PI);
Point2 text_pos2 = text_pos;
text_pos2.x = begin.x < text_pos.x ? MIN(text_pos.x - text_width, begin.x - text_width / 2) : MAX(text_pos.x + text_width, begin.x - text_width / 2);
- viewport->draw_string(font, text_pos2, TS->format_number(vformat("%.1f px", length_vector.y)), HALIGN_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
+ viewport->draw_string(font, text_pos2, TS->format_number(vformat("%.1f px", length_vector.y)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
Point2 v_angle_text_pos = Point2();
v_angle_text_pos.x = CLAMP(begin.x - angle_text_width / 2, angle_text_width / 2, viewport->get_rect().size.x - angle_text_width);
v_angle_text_pos.y = begin.y < end.y ? MIN(text_pos2.y - 2 * text_height, begin.y - text_height * 0.5) : MAX(text_pos2.y + text_height * 3, begin.y + text_height * 1.5);
- viewport->draw_string(font, v_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), vertical_angle)), HALIGN_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
+ viewport->draw_string(font, v_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), vertical_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
text_pos2 = text_pos;
text_pos2.y = end.y < text_pos.y ? MIN(text_pos.y - text_height * 2, end.y - text_height / 2) : MAX(text_pos.y + text_height * 2, end.y - text_height / 2);
- viewport->draw_string(font, text_pos2, TS->format_number(vformat("%.1f px", length_vector.x)), HALIGN_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
+ viewport->draw_string(font, text_pos2, TS->format_number(vformat("%.1f px", length_vector.x)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
Point2 h_angle_text_pos = Point2();
h_angle_text_pos.x = CLAMP(end.x - angle_text_width / 2, angle_text_width / 2, viewport->get_rect().size.x - angle_text_width);
@@ -2960,7 +2981,7 @@ void CanvasItemEditor::_draw_ruler_tool() {
h_angle_text_pos.y = MIN(text_pos.y - height_multiplier * text_height, MIN(end.y - text_height * 0.5, text_pos2.y - height_multiplier * text_height));
}
}
- viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HALIGN_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
+ viewport->draw_string(font, h_angle_text_pos, TS->format_number(vformat(String::utf8("%d°"), horizontal_angle)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
// Angle arcs
int arc_point_count = 8;
@@ -2971,18 +2992,16 @@ void CanvasItemEditor::_draw_ruler_tool() {
const Vector2 end_to_begin = (end - begin);
- real_t arc_1_start_angle =
- end_to_begin.x < 0 ?
- (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0) :
- (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad);
+ real_t arc_1_start_angle = end_to_begin.x < 0
+ ? (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 - vertical_angle_rad : Math_PI / 2.0)
+ : (end_to_begin.y < 0 ? 3.0 * Math_PI / 2.0 : Math_PI / 2.0 - vertical_angle_rad);
real_t arc_1_end_angle = arc_1_start_angle + vertical_angle_rad;
// Constrain arc to triangle height & max size
real_t arc_1_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.y)), arc_max_radius);
- real_t arc_2_start_angle =
- end_to_begin.x < 0 ?
- (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad) :
- (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI);
+ real_t arc_2_start_angle = end_to_begin.x < 0
+ ? (end_to_begin.y < 0 ? 0.0 : -horizontal_angle_rad)
+ : (end_to_begin.y < 0 ? Math_PI - horizontal_angle_rad : Math_PI);
real_t arc_2_end_angle = arc_2_start_angle + horizontal_angle_rad;
// Constrain arc to triangle width & max size
real_t arc_2_radius = MIN(MIN(arc_radius_max_length_percent * ruler_length, ABS(end_to_begin.x)), arc_max_radius);
@@ -2997,17 +3016,17 @@ void CanvasItemEditor::_draw_ruler_tool() {
text_pos.y = CLAMP(text_pos.y, text_height * 2.5, viewport->get_rect().size.y - text_height / 2);
if (draw_secondary_lines) {
- viewport->draw_string(font, text_pos, TS->format_number(vformat("%.2f " + TTR("units"), (length_vector / grid_step).length())), HALIGN_LEFT, -1, font_size, font_color, outline_size, outline_color);
+ viewport->draw_string(font, text_pos, TS->format_number(vformat("%.2f " + TTR("units"), (length_vector / grid_step).length())), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color, outline_size, outline_color);
Point2 text_pos2 = text_pos;
text_pos2.x = begin.x < text_pos.x ? MIN(text_pos.x - text_width, begin.x - text_width / 2) : MAX(text_pos.x + text_width, begin.x - text_width / 2);
- viewport->draw_string(font, text_pos2, TS->format_number(vformat("%d " + TTR("units"), roundf(length_vector.y / grid_step.y))), HALIGN_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
+ viewport->draw_string(font, text_pos2, TS->format_number(vformat("%d " + TTR("units"), roundf(length_vector.y / grid_step.y))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
text_pos2 = text_pos;
text_pos2.y = end.y < text_pos.y ? MIN(text_pos.y - text_height * 2, end.y + text_height / 2) : MAX(text_pos.y + text_height * 2, end.y + text_height / 2);
- viewport->draw_string(font, text_pos2, TS->format_number(vformat("%d " + TTR("units"), roundf(length_vector.x / grid_step.x))), HALIGN_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
+ viewport->draw_string(font, text_pos2, TS->format_number(vformat("%d " + TTR("units"), roundf(length_vector.x / grid_step.x))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_secondary_color, outline_size, outline_color);
} else {
- viewport->draw_string(font, text_pos, TS->format_number(vformat("%d " + TTR("units"), roundf((length_vector / grid_step).length()))), HALIGN_LEFT, -1, font_size, font_color, outline_size, outline_color);
+ viewport->draw_string(font, text_pos, TS->format_number(vformat("%d " + TTR("units"), roundf((length_vector / grid_step).length()))), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color, outline_size, outline_color);
}
}
} else {
@@ -3344,8 +3363,8 @@ void CanvasItemEditor::_draw_selection() {
}
// Draw the move handles
- bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL);
- bool is_alt = Input::get_singleton()->is_key_pressed(KEY_ALT);
+ bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
+ bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT);
if (tool == TOOL_MOVE && show_transformation_gizmos) {
if (_is_node_movable(canvas_item)) {
Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * canvas_item->_edit_get_transform()).orthonormalized();
@@ -3381,7 +3400,7 @@ void CanvasItemEditor::_draw_selection() {
Transform2D simple_xform = viewport->get_transform() * unscaled_transform;
Size2 scale_factor = Size2(SCALE_HANDLE_DISTANCE, SCALE_HANDLE_DISTANCE);
- bool uniform = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+ bool uniform = Input::get_singleton()->is_key_pressed(Key::SHIFT);
Point2 offset = (simple_xform.affine_inverse().xform(drag_to) - simple_xform.affine_inverse().xform(drag_from)) * zoom;
if (drag_type == DRAG_SCALE_X) {
@@ -3553,7 +3572,7 @@ void CanvasItemEditor::_draw_hover() {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
- Size2 node_name_size = font->get_string_size(node_name);
+ Size2 node_name_size = font->get_string_size(node_name, font_size);
Size2 item_size = Size2(node_icon->get_size().x + 4 + node_name_size.x, MAX(node_icon->get_size().y, node_name_size.y - 3));
Point2 pos = transform.xform(hovering_results[i].position) - Point2(0, item_size.y) + (Point2(node_icon->get_size().x, -node_icon->get_size().y) / 4);
@@ -3570,7 +3589,7 @@ void CanvasItemEditor::_draw_hover() {
viewport->draw_texture(node_icon, pos, Color(1.0, 1.0, 1.0, 0.5));
// Draw name
- viewport->draw_string(font, pos + Point2(node_icon->get_size().x + 4, item_size.y - 3), node_name, HALIGN_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5));
+ viewport->draw_string(font, pos + Point2(node_icon->get_size().x + 4, item_size.y - 3), node_name, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5));
}
}
@@ -3655,8 +3674,6 @@ void CanvasItemEditor::_draw_viewport() {
group_button->set_disabled(selection.is_empty());
ungroup_button->set_visible(all_group);
- info_overlay->set_offset(SIDE_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
-
_draw_grid();
_draw_ruler_tool();
_draw_axis();
@@ -3782,8 +3799,8 @@ void CanvasItemEditor::_notification(int p_what) {
}
// Update the viewport if bones changes
- for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
- Object *b = ObjectDB::get_instance(E->key().from);
+ for (KeyValue<BoneKey, BoneList> &E : bone_list) {
+ Object *b = ObjectDB::get_instance(E.key.from);
if (!b) {
viewport->update();
break;
@@ -3796,14 +3813,14 @@ void CanvasItemEditor::_notification(int p_what) {
Transform2D global_xform = b2->get_global_transform();
- if (global_xform != E->get().xform) {
- E->get().xform = global_xform;
+ if (global_xform != E.value.xform) {
+ E.value.xform = global_xform;
viewport->update();
}
Bone2D *bone = Object::cast_to<Bone2D>(b);
- if (bone && bone->get_length() != E->get().length) {
- E->get().length = bone->get_length();
+ if (bone && bone->get_length() != E.value.length) {
+ E.value.length = bone->get_length();
viewport->update();
}
}
@@ -3816,7 +3833,7 @@ void CanvasItemEditor::_notification(int p_what) {
select_sb->set_default_margin(Side(i), 4);
}
- AnimationPlayerEditor::singleton->get_track_editor()->connect("visibility_changed", callable_mp(this, &CanvasItemEditor::_keying_changed));
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->connect("visibility_changed", callable_mp(this, &CanvasItemEditor::_keying_changed));
_keying_changed();
} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
@@ -3909,11 +3926,6 @@ void CanvasItemEditor::_notification(int p_what) {
anchors_popup->add_icon_item(get_theme_icon(SNAME("ControlAlignWide"), SNAME("EditorIcons")), TTR("Full Rect"), ANCHORS_PRESET_WIDE);
anchor_mode_button->set_icon(get_theme_icon(SNAME("Anchor"), SNAME("EditorIcons")));
-
- info_overlay->get_theme()->set_stylebox("normal", "Label", get_theme_stylebox(SNAME("CanvasItemInfoOverlay"), SNAME("EditorStyles")));
- warning_child_of_container->add_theme_color_override("font_color", get_theme_color(SNAME("warning_color"), SNAME("Editor")));
- warning_child_of_container->add_theme_font_override("font", get_theme_font(SNAME("main"), SNAME("EditorFonts")));
- warning_child_of_container->add_theme_font_size_override("font_size", get_theme_font_size(SNAME("main_size"), SNAME("EditorFonts")));
}
if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
@@ -3968,7 +3980,7 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
void CanvasItemEditor::_update_context_menu_stylebox() {
// This must be called when the theme changes to follow the new accent color.
Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat);
- const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color("accent_color", "Editor");
+ const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
// Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
context_menu_stylebox->set_border_color(accent_color);
@@ -4069,34 +4081,6 @@ void CanvasItemEditor::_update_scrollbars() {
updating_scroll = false;
}
-void CanvasItemEditor::_popup_warning_depop(Control *p_control) {
- ERR_FAIL_COND(!popup_temporarily_timers.has(p_control));
-
- Timer *timer = popup_temporarily_timers[p_control];
- timer->queue_delete();
- p_control->hide();
- popup_temporarily_timers.erase(p_control);
- info_overlay->set_offset(SIDE_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
-}
-
-void CanvasItemEditor::_popup_warning_temporarily(Control *p_control, const double p_duration) {
- Timer *timer;
- if (!popup_temporarily_timers.has(p_control)) {
- timer = memnew(Timer);
- timer->connect("timeout", callable_mp(this, &CanvasItemEditor::_popup_warning_depop), varray(p_control));
- timer->set_one_shot(true);
- add_child(timer);
-
- popup_temporarily_timers[p_control] = timer;
- } else {
- timer = popup_temporarily_timers[p_control];
- }
-
- timer->start(p_duration);
- p_control->show();
- info_overlay->set_offset(SIDE_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
-}
-
void CanvasItemEditor::_update_scroll(real_t) {
if (updating_scroll) {
return;
@@ -4254,17 +4238,13 @@ void CanvasItemEditor::_button_tool_select(int p_index) {
viewport->update();
_update_cursor();
-
- // Request immediate refresh of cursor when using hot-keys to switch between tools
- DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)viewport->get_default_cursor_shape();
- DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
}
void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing) {
Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item || !canvas_item->is_visible_in_tree()) {
continue;
}
@@ -4277,13 +4257,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Node2D *n2d = Object::cast_to<Node2D>(canvas_item);
if (key_pos && p_location) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), p_on_existing);
}
if (key_rot && p_rotation) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "rotation", n2d->get_rotation(), p_on_existing);
}
if (key_scale && p_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), p_on_existing);
}
if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) {
@@ -4309,13 +4289,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
if (has_chain && ik_chain.size()) {
for (Node2D *&F : ik_chain) {
if (key_pos) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "position", F->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "rotation", F->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(F, "scale", F->get_scale(), p_on_existing);
}
}
}
@@ -4325,13 +4305,13 @@ void CanvasItemEditor::_insert_animation_keys(bool p_location, bool p_rotation,
Control *ctrl = Object::cast_to<Control>(canvas_item);
if (key_pos) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_position", ctrl->get_position(), p_on_existing);
}
if (key_rot) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation(), p_on_existing);
}
if (key_scale) {
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
+ AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing);
}
}
}
@@ -4695,8 +4675,8 @@ void CanvasItemEditor::_popup_callback(int p_op) {
Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item || !canvas_item->is_visible_in_tree()) {
continue;
}
@@ -4741,8 +4721,8 @@ void CanvasItemEditor::_popup_callback(int p_op) {
case ANIM_CLEAR_POSE: {
Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item || !canvas_item->is_visible_in_tree()) {
continue;
}
@@ -4769,10 +4749,6 @@ void CanvasItemEditor::_popup_callback(int p_op) {
if (key_pos) {
ctrl->set_position(Point2());
}
- /*
- if (key_scale)
- AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl,"rect/size",ctrl->get_size());
- */
}
}
@@ -4816,8 +4792,8 @@ void CanvasItemEditor::_popup_callback(int p_op) {
Node *editor_root = EditorNode::get_singleton()->get_edited_scene()->get_tree()->get_edited_scene_root();
undo_redo->create_action(TTR("Create Custom Bone2D(s) from Node(s)"));
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- Node2D *n2d = Object::cast_to<Node2D>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ Node2D *n2d = Object::cast_to<Node2D>(E.key);
Bone2D *new_bone = memnew(Bone2D);
String new_bone_name = n2d->get_name();
@@ -4861,8 +4837,8 @@ void CanvasItemEditor::_focus_selection(int p_op) {
int count = 0;
Map<Node *, Object *> &selection = editor_selection->get_selection();
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E.key);
if (!canvas_item) {
continue;
}
@@ -4896,10 +4872,9 @@ void CanvasItemEditor::_focus_selection(int p_op) {
};
if (p_op == VIEW_CENTER_TO_SELECTION) {
- center = rect.position + rect.size / 2;
+ center = rect.get_center();
Vector2 offset = viewport->get_size() / 2 - editor->get_scene_root()->get_global_canvas_transform().xform(center);
- view_offset.x -= Math::round(offset.x / zoom);
- view_offset.y -= Math::round(offset.y / zoom);
+ view_offset -= (offset / zoom).round();
update_viewport();
} else { // VIEW_FRAME_TO_SELECTION
@@ -5135,19 +5110,6 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) {
viewport->update();
}
-void CanvasItemEditor::add_control_to_info_overlay(Control *p_control) {
- ERR_FAIL_COND(!p_control);
-
- p_control->set_h_size_flags(p_control->get_h_size_flags() & ~Control::SIZE_EXPAND_FILL);
- info_overlay->add_child(p_control);
- info_overlay->set_offset(SIDE_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
-}
-
-void CanvasItemEditor::remove_control_from_info_overlay(Control *p_control) {
- info_overlay->remove_child(p_control);
- info_overlay->set_offset(SIDE_LEFT, (show_rulers ? RULER_WIDTH : 0) + 10);
-}
-
void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) {
ERR_FAIL_COND(!p_control);
@@ -5204,7 +5166,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
snap_rotation = false;
snap_scale = false;
snap_relative = false;
- snap_pixel = false;
+ // Enable pixel snapping even if pixel snap rendering is disabled in the Project Settings.
+ // This results in crisper visuals by preventing 2D nodes from being placed at subpixel coordinates.
+ snap_pixel = true;
snap_target[0] = SNAP_TARGET_NONE;
snap_target[1] = SNAP_TARGET_NONE;
@@ -5278,23 +5242,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
viewport->connect("draw", callable_mp(this, &CanvasItemEditor::_draw_viewport));
viewport->connect("gui_input", callable_mp(this, &CanvasItemEditor::_gui_input_viewport));
- info_overlay = memnew(VBoxContainer);
- info_overlay->set_anchors_and_offsets_preset(Control::PRESET_BOTTOM_LEFT);
- info_overlay->set_offset(SIDE_LEFT, 10);
- info_overlay->set_offset(SIDE_BOTTOM, -15);
- info_overlay->set_v_grow_direction(Control::GROW_DIRECTION_BEGIN);
- info_overlay->add_theme_constant_override("separation", 10);
- viewport_scrollable->add_child(info_overlay);
-
- // Make sure all labels inside of the container are styled the same.
- Theme *info_overlay_theme = memnew(Theme);
- info_overlay->set_theme(info_overlay_theme);
-
- warning_child_of_container = memnew(Label);
- warning_child_of_container->hide();
- warning_child_of_container->set_text(TTR("Warning: Children of a container get their position and size determined only by their parent."));
- add_control_to_info_overlay(warning_child_of_container);
-
h_scroll = memnew(HScrollBar);
viewport->add_child(h_scroll);
h_scroll->connect("value_changed", callable_mp(this, &CanvasItemEditor::_update_scroll));
@@ -5327,9 +5274,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
select_button->set_toggle_mode(true);
select_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_SELECT));
select_button->set_pressed(true);
- select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), KEY_Q));
+ select_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/select_mode", TTR("Select Mode"), Key::Q));
select_button->set_shortcut_context(this);
- select_button->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("RMB: Add node at position clicked."));
+ select_button->set_tooltip(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+Drag: Move selected node.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Alt+Drag: Scale selected node.") + "\n" + TTR("V: Set selected node's pivot position.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked.") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("RMB: Add node at position clicked."));
hb->add_child(memnew(VSeparator));
@@ -5338,7 +5285,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
hb->add_child(move_button);
move_button->set_toggle_mode(true);
move_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_MOVE));
- move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), KEY_W));
+ move_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/move_mode", TTR("Move Mode"), Key::W));
move_button->set_shortcut_context(this);
move_button->set_tooltip(TTR("Move Mode"));
@@ -5347,7 +5294,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
hb->add_child(rotate_button);
rotate_button->set_toggle_mode(true);
rotate_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_ROTATE));
- rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), KEY_E));
+ rotate_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/rotate_mode", TTR("Rotate Mode"), Key::E));
rotate_button->set_shortcut_context(this);
rotate_button->set_tooltip(TTR("Rotate Mode"));
@@ -5356,9 +5303,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
hb->add_child(scale_button);
scale_button->set_toggle_mode(true);
scale_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_SCALE));
- scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), KEY_S));
+ scale_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/scale_mode", TTR("Scale Mode"), Key::S));
scale_button->set_shortcut_context(this);
- scale_button->set_tooltip(TTR("Scale Mode"));
+ scale_button->set_tooltip(TTR("Shift: Scale proportionally."));
hb->add_child(memnew(VSeparator));
@@ -5381,16 +5328,16 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
hb->add_child(pan_button);
pan_button->set_toggle_mode(true);
pan_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_PAN));
- pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), KEY_G));
+ pan_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/pan_mode", TTR("Pan Mode"), Key::G));
pan_button->set_shortcut_context(this);
- pan_button->set_tooltip(TTR("Pan Mode"));
+ pan_button->set_tooltip(TTR("You can also use Pan View shortcut (Space by default) to pan in any mode."));
ruler_button = memnew(Button);
ruler_button->set_flat(true);
hb->add_child(ruler_button);
ruler_button->set_toggle_mode(true);
ruler_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_button_tool_select), make_binds(TOOL_RULER));
- ruler_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/ruler_mode", TTR("Ruler Mode"), KEY_R));
+ ruler_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/ruler_mode", TTR("Ruler Mode"), Key::R));
ruler_button->set_shortcut_context(this);
ruler_button->set_tooltip(TTR("Ruler Mode"));
@@ -5402,7 +5349,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
smart_snap_button->set_toggle_mode(true);
smart_snap_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_toggle_smart_snap));
smart_snap_button->set_tooltip(TTR("Toggle smart snapping."));
- smart_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_smart_snap", TTR("Use Smart Snap"), KEY_MASK_SHIFT | KEY_S));
+ smart_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_smart_snap", TTR("Use Smart Snap"), KeyModifierMask::SHIFT | Key::S));
smart_snap_button->set_shortcut_context(this);
grid_snap_button = memnew(Button);
@@ -5411,7 +5358,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
grid_snap_button->set_toggle_mode(true);
grid_snap_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_toggle_grid_snap));
grid_snap_button->set_tooltip(TTR("Toggle grid snapping."));
- grid_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_grid_snap", TTR("Use Grid Snap"), KEY_MASK_SHIFT | KEY_G));
+ grid_snap_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/use_grid_snap", TTR("Use Grid Snap"), KeyModifierMask::SHIFT | Key::G));
grid_snap_button->set_shortcut_context(this);
snap_config_menu = memnew(MenuButton);
@@ -5454,7 +5401,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
lock_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(LOCK_SELECTED));
lock_button->set_tooltip(TTR("Lock selected node, preventing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- lock_button->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KEY_MASK_CMD | KEY_L));
+ lock_button->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD | Key::L));
unlock_button = memnew(Button);
unlock_button->set_flat(true);
@@ -5462,7 +5409,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
unlock_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(UNLOCK_SELECTED));
unlock_button->set_tooltip(TTR("Unlock selected node, allowing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- unlock_button->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_L));
+ unlock_button->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::L));
group_button = memnew(Button);
group_button->set_flat(true);
@@ -5470,7 +5417,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
group_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(GROUP_SELECTED));
group_button->set_tooltip(TTR("Makes sure the object's children are not selectable."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- group_button->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KEY_MASK_CMD | KEY_G));
+ group_button->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD | Key::G));
ungroup_button = memnew(Button);
ungroup_button->set_flat(true);
@@ -5478,7 +5425,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
ungroup_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(UNGROUP_SELECTED));
ungroup_button->set_tooltip(TTR("Restores the object's children's ability to be selected."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- ungroup_button->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G));
+ ungroup_button->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::G));
hb->add_child(memnew(VSeparator));
@@ -5492,7 +5439,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p->set_hide_on_checkable_item_selection(false);
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES);
p->add_separator();
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bone2D Node(s) from Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bone2D Node(s) from Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::B), SKELETON_MAKE_BONES);
p->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_popup_callback));
hb->add_child(memnew(VSeparator));
@@ -5516,21 +5463,21 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p = view_menu->get_popup();
p->set_hide_on_checkable_item_selection(false);
- p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Always Show Grid"), KEY_NUMBERSIGN), SHOW_GRID);
- p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), KEY_H), SHOW_HELPERS);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_grid", TTR("Always Show Grid"), Key::NUMBERSIGN), SHOW_GRID);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_helpers", TTR("Show Helpers"), Key::H), SHOW_HELPERS);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_rulers", TTR("Show Rulers")), SHOW_RULERS);
- p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), KEY_Y), SHOW_GUIDES);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_guides", TTR("Show Guides"), Key::Y), SHOW_GUIDES);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_origin", TTR("Show Origin")), SHOW_ORIGIN);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_viewport", TTR("Show Viewport")), SHOW_VIEWPORT);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_edit_locks", TTR("Show Group And Lock Icons")), SHOW_EDIT_LOCKS);
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/show_transformation_gizmos", TTR("Show Transformation Gizmos")), SHOW_TRANSFORMATION_GIZMOS);
p->add_separator();
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), KEY_F), VIEW_CENTER_TO_SELECTION);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KEY_MASK_SHIFT | KEY_F), VIEW_FRAME_TO_SELECTION);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/center_selection", TTR("Center Selection"), Key::F), VIEW_CENTER_TO_SELECTION);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/frame_selection", TTR("Frame Selection"), KeyModifierMask::SHIFT | Key::F), VIEW_FRAME_TO_SELECTION);
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/clear_guides", TTR("Clear Guides")), CLEAR_GUIDES);
p->add_separator();
- p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/preview_canvas_scale", TTR("Preview Canvas Scale"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_P), PREVIEW_CANVAS_SCALE);
+ p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/preview_canvas_scale", TTR("Preview Canvas Scale"), KeyModifierMask::SHIFT | KeyModifierMask::CMD | Key::P), PREVIEW_CANVAS_SCALE);
hb->add_child(memnew(VSeparator));
@@ -5601,7 +5548,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_insert_button->set_focus_mode(FOCUS_NONE);
key_insert_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(ANIM_INSERT_KEY));
key_insert_button->set_tooltip(TTR("Insert keys (based on mask)."));
- key_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key", TTR("Insert Key"), KEY_INSERT));
+ key_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key", TTR("Insert Key"), Key::INSERT));
key_insert_button->set_shortcut_context(this);
animation_hb->add_child(key_insert_button);
@@ -5624,11 +5571,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p = animation_menu->get_popup();
p->add_shortcut(ED_GET_SHORTCUT("canvas_item_editor/anim_insert_key"), ANIM_INSERT_KEY);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key_existing_tracks", TTR("Insert Key (Existing Tracks)"), KEY_MASK_CMD + KEY_INSERT), ANIM_INSERT_KEY_EXISTING);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_insert_key_existing_tracks", TTR("Insert Key (Existing Tracks)"), KeyModifierMask::CMD + Key::INSERT), ANIM_INSERT_KEY_EXISTING);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_copy_pose", TTR("Copy Pose")), ANIM_COPY_POSE);
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_paste_pose", TTR("Paste Pose")), ANIM_PASTE_POSE);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_clear_pose", TTR("Clear Pose"), KEY_MASK_SHIFT | KEY_K), ANIM_CLEAR_POSE);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/anim_clear_pose", TTR("Clear Pose"), KeyModifierMask::SHIFT | Key::K), ANIM_CLEAR_POSE);
snap_dialog = memnew(SnapDialog);
snap_dialog->connect("confirmed", callable_mp(this, &CanvasItemEditor::_snap_changed));
@@ -5648,9 +5595,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
add_node_menu->add_icon_item(editor->get_scene_tree_dock()->get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")), TTR("Instance Scene Here"));
add_node_menu->connect("id_pressed", callable_mp(this, &CanvasItemEditor::_add_node_pressed));
- multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), KEY_KP_MULTIPLY);
- divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), KEY_KP_DIVIDE);
- pan_view_shortcut = ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), KEY_SPACE);
+ multiply_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/multiply_grid_step", TTR("Multiply grid step by 2"), Key::KP_MULTIPLY);
+ divide_grid_step_shortcut = ED_SHORTCUT("canvas_item_editor/divide_grid_step", TTR("Divide grid step by 2"), Key::KP_DIVIDE);
+ pan_view_shortcut = ED_SHORTCUT("canvas_item_editor/pan_view", TTR("Pan View"), Key::SPACE);
skeleton_menu->get_popup()->set_item_checked(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES), true);
singleton = this;
@@ -5659,16 +5606,16 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
// those shortcuts one by one.
// Resetting zoom to 100% is a duplicate shortcut of `canvas_item_editor/reset_zoom`,
// but it ensures both 1 and Ctrl + 0 can be used to reset zoom.
- ED_SHORTCUT("canvas_item_editor/zoom_3.125_percent", TTR("Zoom to 3.125%"), KEY_MASK_SHIFT | KEY_5);
- ED_SHORTCUT("canvas_item_editor/zoom_6.25_percent", TTR("Zoom to 6.25%"), KEY_MASK_SHIFT | KEY_4);
- ED_SHORTCUT("canvas_item_editor/zoom_12.5_percent", TTR("Zoom to 12.5%"), KEY_MASK_SHIFT | KEY_3);
- ED_SHORTCUT("canvas_item_editor/zoom_25_percent", TTR("Zoom to 25%"), KEY_MASK_SHIFT | KEY_2);
- ED_SHORTCUT("canvas_item_editor/zoom_50_percent", TTR("Zoom to 50%"), KEY_MASK_SHIFT | KEY_1);
- ED_SHORTCUT("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), KEY_1);
- ED_SHORTCUT("canvas_item_editor/zoom_200_percent", TTR("Zoom to 200%"), KEY_2);
- ED_SHORTCUT("canvas_item_editor/zoom_400_percent", TTR("Zoom to 400%"), KEY_3);
- ED_SHORTCUT("canvas_item_editor/zoom_800_percent", TTR("Zoom to 800%"), KEY_4);
- ED_SHORTCUT("canvas_item_editor/zoom_1600_percent", TTR("Zoom to 1600%"), KEY_5);
+ 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("canvas_item_editor/zoom_100_percent", TTR("Zoom to 100%"), Key::KEY_1);
+ 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);
set_process_unhandled_key_input(true);
@@ -5799,7 +5746,7 @@ void CanvasItemEditorViewport::_remove_preview() {
}
bool CanvasItemEditorViewport::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) {
- if (p_desired_node->get_filename() == p_target_scene_path) {
+ if (p_desired_node->get_scene_file_path() == p_target_scene_path) {
return true;
}
@@ -5833,7 +5780,7 @@ void CanvasItemEditorViewport::_create_nodes(Node *parent, Node *child, String &
Ref<Texture2D> texture = Ref<Texture2D>(Object::cast_to<Texture2D>(ResourceCache::get(path)));
if (parent) {
- editor_data->get_undo_redo().add_do_method(parent, "add_child", child);
+ editor_data->get_undo_redo().add_do_method(parent, "add_child", child, true);
editor_data->get_undo_redo().add_do_method(child, "set_owner", editor->get_edited_scene());
editor_data->get_undo_redo().add_do_reference(child);
editor_data->get_undo_redo().add_undo_method(parent, "remove_child", child);
@@ -5896,14 +5843,14 @@ bool CanvasItemEditorViewport::_create_instance(Node *parent, String &path, cons
return false;
}
- if (editor->get_edited_scene()->get_filename() != "") { // cyclical instancing
- if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instantiated_scene)) {
+ if (!editor->get_edited_scene()->get_scene_file_path().is_empty()) { // cyclical instancing
+ if (_cyclical_dependency_exists(editor->get_edited_scene()->get_scene_file_path(), instantiated_scene)) {
memdelete(instantiated_scene);
return false;
}
}
- instantiated_scene->set_filename(ProjectSettings::get_singleton()->localize_path(path));
+ instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene);
editor_data->get_undo_redo().add_do_method(instantiated_scene, "set_owner", editor->get_edited_scene());
@@ -6056,9 +6003,9 @@ bool CanvasItemEditorViewport::_only_packed_scenes_selected() const {
}
void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) {
- bool is_shift = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
- bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL);
- bool is_alt = Input::get_singleton()->is_key_pressed(KEY_ALT);
+ bool is_shift = Input::get_singleton()->is_key_pressed(Key::SHIFT);
+ bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
+ bool is_alt = Input::get_singleton()->is_key_pressed(Key::ALT);
selected_files.clear();
Dictionary d = p_data;
@@ -6189,14 +6136,14 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte
label = memnew(Label);
label->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 1));
- label->add_theme_constant_override("shadow_as_outline", 1 * EDSCALE);
+ label->add_theme_constant_override("shadow_outline_size", 1 * EDSCALE);
label->hide();
canvas_item_editor->get_controls_container()->add_child(label);
label_desc = memnew(Label);
label_desc->add_theme_color_override("font_color", Color(0.6f, 0.6f, 0.6f, 1));
label_desc->add_theme_color_override("font_shadow_color", Color(0.2f, 0.2f, 0.2f, 1));
- label_desc->add_theme_constant_override("shadow_as_outline", 1 * EDSCALE);
+ label_desc->add_theme_constant_override("shadow_outline_size", 1 * EDSCALE);
label_desc->add_theme_constant_override("line_spacing", 0);
label_desc->hide();
canvas_item_editor->get_controls_container()->add_child(label_desc);
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 1965efbf30..b6576b7144 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -235,11 +235,6 @@ private:
PanelContainer *context_menu_container;
HBoxContainer *hbc_context_menu;
- Map<Control *, Timer *> popup_temporarily_timers;
-
- Label *warning_child_of_container;
- VBoxContainer *info_overlay;
-
Transform2D transform;
bool show_grid;
bool show_rulers;
@@ -421,8 +416,6 @@ private:
CanvasItem *ref_item;
- void _add_canvas_item(CanvasItem *p_canvas_item);
-
void _save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones = false);
void _restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones = false);
void _commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones = false);
@@ -434,7 +427,6 @@ private:
bool updating_scroll;
void _update_scroll(real_t);
void _update_scrollbars();
- void _append_canvas_item(CanvasItem *p_item);
void _snap_changed();
void _selection_result_pressed(int);
void _selection_menu_hide();
@@ -519,7 +511,6 @@ private:
const Node *p_current);
void _set_anchors_preset(Control::LayoutPreset p_preset);
- void _set_offsets_preset(Control::LayoutPreset p_preset);
void _set_anchors_and_offsets_preset(Control::LayoutPreset p_preset);
void _set_anchors_and_offsets_to_keep_ratio();
@@ -540,8 +531,6 @@ private:
VSplitContainer *bottom_split;
void _update_context_menu_stylebox();
- void _popup_warning_temporarily(Control *p_control, const double p_duration);
- void _popup_warning_depop(Control *p_control);
void _set_owner_for_node_and_children(Node *p_node, Node *p_owner);
@@ -551,15 +540,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
- void end_drag();
- void box_selection_start(Point2 &click);
- bool box_selection_end();
HBoxContainer *get_panel_hb() { return hb; }
- template <class P, class C>
- void space_selected_items();
-
static CanvasItemEditor *singleton;
public:
@@ -588,9 +571,6 @@ public:
void add_control_to_menu_panel(Control *p_control);
void remove_control_from_menu_panel(Control *p_control);
- void add_control_to_info_overlay(Control *p_control);
- void remove_control_from_info_overlay(Control *p_control);
-
HSplitContainer *get_palette_split();
VSplitContainer *get_bottom_split();
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.cpp b/editor/plugins/collision_polygon_3d_editor_plugin.cpp
index 8b354c33a1..4c728ff757 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/collision_polygon_3d_editor_plugin.cpp
@@ -103,16 +103,16 @@ void CollisionPolygon3DEditor::_wip_close() {
undo_redo->commit_action();
}
-bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!node) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Transform3D gt = node->get_global_transform();
Transform3D gi = gt.affine_inverse();
float depth = _get_depth() * 0.5;
Vector3 n = gt.basis.get_axis(2).normalized();
- Plane p(gt.origin + n * depth, n);
+ Plane p(n, gt.origin + n * depth);
Ref<InputEventMouseButton> mb = p_event;
@@ -124,7 +124,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
@@ -142,7 +142,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
switch (mode) {
case MODE_CREATE: {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT && mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
if (!wip_active) {
wip.clear();
wip.push_back(cpoint);
@@ -151,29 +151,29 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
snap_ignore = false;
_polygon_draw();
edited_point = 1;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
if (wip.size() > 1 && p_camera->unproject_position(gt.xform(Vector3(wip[0].x, wip[0].y, depth))).distance_to(gpoint) < grab_threshold) {
//wip closed
_wip_close();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
wip.push_back(cpoint);
edited_point = wip.size();
snap_ignore = false;
_polygon_draw();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed() && wip_active) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && wip_active) {
_wip_close();
}
} break;
case MODE_EDIT: {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (mb->is_ctrl_pressed()) {
if (poly.size() < 3) {
@@ -184,7 +184,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
//search edges
@@ -219,7 +219,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
_polygon_draw();
snap_ignore = true;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
} else {
//look for points to move
@@ -244,7 +244,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
edited_point_pos = poly[closest_idx];
_polygon_draw();
snap_ignore = false;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
} else {
@@ -253,7 +253,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
if (edited_point != -1) {
//apply
- ERR_FAIL_INDEX_V(edited_point, poly.size(), false);
+ ERR_FAIL_INDEX_V(edited_point, poly.size(), EditorPlugin::AFTER_GUI_INPUT_PASS);
poly.write[edited_point] = edited_point_pos;
undo_redo->create_action(TTR("Edit Poly"));
undo_redo->add_do_method(node, "set_polygon", poly);
@@ -263,11 +263,11 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
undo_redo->commit_action();
edited_point = -1;
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed() && edited_point == -1) {
+ if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed() && edited_point == -1) {
int closest_idx = -1;
Vector2 closest_pos;
real_t closest_dist = 1e10;
@@ -285,12 +285,12 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
if (closest_idx >= 0) {
undo_redo->create_action(TTR("Edit Poly (Remove Point)"));
undo_redo->add_undo_method(node, "set_polygon", poly);
- poly.remove(closest_idx);
+ poly.remove_at(closest_idx);
undo_redo->add_do_method(node, "set_polygon", poly);
undo_redo->add_do_method(this, "_polygon_draw");
undo_redo->add_undo_method(this, "_polygon_draw");
undo_redo->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
@@ -301,7 +301,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
- if (edited_point != -1 && (wip_active || mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT)) {
+ if (edited_point != -1 && (wip_active || (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE)) {
Vector2 gpoint = mm->get_position();
Vector3 ray_from = p_camera->project_ray_origin(gpoint);
@@ -310,14 +310,14 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
Vector3 spoint;
if (!p.intersects_ray(ray_from, ray_dir, &spoint)) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
spoint = gi.xform(spoint);
Vector2 cpoint(spoint.x, spoint.y);
- if (snap_ignore && !Input::get_singleton()->is_key_pressed(KEY_CTRL)) {
+ if (snap_ignore && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
snap_ignore = false;
}
@@ -332,7 +332,7 @@ bool CollisionPolygon3DEditor::forward_spatial_gui_input(Camera3D *p_camera, con
}
}
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
float CollisionPolygon3DEditor::_get_depth() {
diff --git a/editor/plugins/collision_polygon_3d_editor_plugin.h b/editor/plugins/collision_polygon_3d_editor_plugin.h
index 5db0f7308a..10b0adf76c 100644
--- a/editor/plugins/collision_polygon_3d_editor_plugin.h
+++ b/editor/plugins/collision_polygon_3d_editor_plugin.h
@@ -88,7 +88,7 @@ protected:
static void _bind_methods();
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event);
void edit(Node *p_collision_polygon);
CollisionPolygon3DEditor(EditorNode *p_editor);
~CollisionPolygon3DEditor();
@@ -101,7 +101,7 @@ class Polygon3DEditorPlugin : public EditorPlugin {
EditorNode *editor;
public:
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return collision_polygon_editor->forward_spatial_gui_input(p_camera, p_event); }
virtual String get_name() const override { return "Polygon3DEditor"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp
index bfcc293625..94fd9a5a08 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.cpp
+++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp
@@ -39,7 +39,7 @@
#include "scene/resources/rectangle_shape_2d.h"
#include "scene/resources/segment_shape_2d.h"
#include "scene/resources/separation_ray_shape_2d.h"
-#include "scene/resources/world_margin_shape_2d.h"
+#include "scene/resources/world_boundary_shape_2d.h"
void CollisionShape2DEditor::_node_removed(Node *p_node) {
if (p_node == node) {
@@ -70,13 +70,13 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const {
case CONVEX_POLYGON_SHAPE: {
} break;
- case WORLD_MARGIN_SHAPE: {
- Ref<WorldMarginShape2D> line = node->get_shape();
+ case WORLD_BOUNDARY_SHAPE: {
+ Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
if (idx == 0) {
- return line->get_distance();
+ return world_boundary->get_distance();
} else {
- return line->get_normal();
+ return world_boundary->get_normal();
}
} break;
@@ -147,14 +147,14 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
case CONVEX_POLYGON_SHAPE: {
} break;
- case WORLD_MARGIN_SHAPE: {
+ case WORLD_BOUNDARY_SHAPE: {
if (idx < 2) {
- Ref<WorldMarginShape2D> line = node->get_shape();
+ Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
if (idx == 0) {
- line->set_distance(p_point.length());
+ world_boundary->set_distance(p_point.length());
} else {
- line->set_normal(p_point.normalized());
+ world_boundary->set_normal(p_point.normalized());
}
canvas_item_editor->update_viewport();
@@ -183,7 +183,7 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) {
size.y = p_point.y * RECT_HANDLES[idx].y * 2;
}
- if (Input::get_singleton()->is_key_pressed(KEY_ALT)) {
+ if (Input::get_singleton()->is_key_pressed(Key::ALT)) {
rect->set_size(size.abs());
node->set_global_position(original_transform.get_origin());
} else {
@@ -255,18 +255,18 @@ void CollisionShape2DEditor::commit_handle(int idx, Variant &p_org) {
// Cannot be edited directly, use CollisionPolygon2D instead.
} break;
- case WORLD_MARGIN_SHAPE: {
- Ref<WorldMarginShape2D> line = node->get_shape();
+ case WORLD_BOUNDARY_SHAPE: {
+ Ref<WorldBoundaryShape2D> world_boundary = node->get_shape();
if (idx == 0) {
- undo_redo->add_do_method(line.ptr(), "set_distance", line->get_distance());
+ undo_redo->add_do_method(world_boundary.ptr(), "set_distance", world_boundary->get_distance());
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
- undo_redo->add_undo_method(line.ptr(), "set_distance", p_org);
+ undo_redo->add_undo_method(world_boundary.ptr(), "set_distance", p_org);
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
} else {
- undo_redo->add_do_method(line.ptr(), "set_normal", line->get_normal());
+ undo_redo->add_do_method(world_boundary.ptr(), "set_normal", world_boundary->get_normal());
undo_redo->add_do_method(canvas_item_editor, "update_viewport");
- undo_redo->add_undo_method(line.ptr(), "set_normal", p_org);
+ undo_redo->add_undo_method(world_boundary.ptr(), "set_normal", p_org);
undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
}
@@ -333,7 +333,7 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
if (mb.is_valid()) {
Vector2 gpoint = mb->get_position();
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
for (int i = 0; i < handles.size(); i++) {
if (xform.xform(handles[i]).distance_to(gpoint) < 8) {
@@ -394,7 +394,7 @@ bool CollisionShape2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_e
return false;
}
- if (shape_type == RECTANGLE_SHAPE && k->get_keycode() == KEY_ALT) {
+ if (shape_type == RECTANGLE_SHAPE && k->get_keycode() == Key::ALT) {
set_handle(edit_handle, last_point); // Update handle when Alt key is toggled.
}
}
@@ -421,8 +421,8 @@ void CollisionShape2DEditor::_get_current_shape_type() {
shape_type = CONCAVE_POLYGON_SHAPE;
} else if (Object::cast_to<ConvexPolygonShape2D>(*s)) {
shape_type = CONVEX_POLYGON_SHAPE;
- } else if (Object::cast_to<WorldMarginShape2D>(*s)) {
- shape_type = WORLD_MARGIN_SHAPE;
+ } else if (Object::cast_to<WorldBoundaryShape2D>(*s)) {
+ shape_type = WORLD_BOUNDARY_SHAPE;
} else if (Object::cast_to<SeparationRayShape2D>(*s)) {
shape_type = SEPARATION_RAY_SHAPE;
} else if (Object::cast_to<RectangleShape2D>(*s)) {
@@ -490,8 +490,8 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla
case CONVEX_POLYGON_SHAPE: {
} break;
- case WORLD_MARGIN_SHAPE: {
- Ref<WorldMarginShape2D> shape = node->get_shape();
+ case WORLD_BOUNDARY_SHAPE: {
+ Ref<WorldBoundaryShape2D> shape = node->get_shape();
handles.resize(2);
handles.write[0] = shape->get_normal() * shape->get_distance();
diff --git a/editor/plugins/collision_shape_2d_editor_plugin.h b/editor/plugins/collision_shape_2d_editor_plugin.h
index 421e674df8..ab95600a52 100644
--- a/editor/plugins/collision_shape_2d_editor_plugin.h
+++ b/editor/plugins/collision_shape_2d_editor_plugin.h
@@ -46,7 +46,7 @@ class CollisionShape2DEditor : public Control {
CIRCLE_SHAPE,
CONCAVE_POLYGON_SHAPE,
CONVEX_POLYGON_SHAPE,
- WORLD_MARGIN_SHAPE,
+ WORLD_BOUNDARY_SHAPE,
SEPARATION_RAY_SHAPE,
RECTANGLE_SHAPE,
SEGMENT_SHAPE
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
index 6f246c1661..fb9f8696fe 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
@@ -83,7 +83,7 @@ void CPUParticles2DEditorPlugin::_generate_emission_mask() {
}
img->convert(Image::FORMAT_RGBA8);
ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8);
- Size2i s = Size2(img->get_width(), img->get_height());
+ Size2i s = img->get_size();
ERR_FAIL_COND(s.width == 0 || s.height == 0);
Vector<Point2> valid_positions;
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index 4a22dc5b62..3bb707d415 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -115,16 +115,16 @@ void CurveEditor::gui_input(const Ref<InputEvent> &p_event) {
}
switch (mb.get_button_index()) {
- case MOUSE_BUTTON_RIGHT:
+ case MouseButton::RIGHT:
_context_click_pos = mpos;
- open_context_menu(get_global_transform().xform(mpos));
+ open_context_menu(get_screen_position() + mpos);
break;
- case MOUSE_BUTTON_MIDDLE:
+ case MouseButton::MIDDLE:
remove_point(_hover_point);
break;
- case MOUSE_BUTTON_LEFT:
+ case MouseButton::LEFT:
_dragging = true;
break;
default:
@@ -132,7 +132,7 @@ void CurveEditor::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (!mb.is_pressed() && _dragging && mb.get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (!mb.is_pressed() && _dragging && mb.get_button_index() == MouseButton::LEFT) {
_dragging = false;
if (_has_undo_data) {
UndoRedo &ur = *EditorNode::get_singleton()->get_undo_redo();
@@ -210,7 +210,7 @@ void CurveEditor::gui_input(const Ref<InputEvent> &p_event) {
tangent = 9999 * (dir.y >= 0 ? 1 : -1);
}
- bool link = !Input::get_singleton()->is_key_pressed(KEY_SHIFT);
+ bool link = !Input::get_singleton()->is_key_pressed(Key::SHIFT);
if (_selected_tangent == TANGENT_LEFT) {
curve.set_point_left_tangent(_selected_point, tangent);
@@ -240,7 +240,7 @@ void CurveEditor::gui_input(const Ref<InputEvent> &p_event) {
const InputEventKey &key = **key_ref;
if (key.is_pressed() && _selected_point != -1) {
- if (key.get_keycode() == KEY_DELETE) {
+ if (key.get_keycode() == Key::KEY_DELETE) {
remove_point(_selected_point);
}
}
@@ -354,9 +354,9 @@ void CurveEditor::open_context_menu(Vector2 pos) {
_context_menu->add_check_item(TTR("Linear"), CONTEXT_LINEAR);
- bool is_linear = _selected_tangent == TANGENT_LEFT ?
- _curve_ref->get_point_left_mode(_selected_point) == Curve::TANGENT_LINEAR :
- _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR;
+ bool is_linear = _selected_tangent == TANGENT_LEFT
+ ? _curve_ref->get_point_left_mode(_selected_point) == Curve::TANGENT_LINEAR
+ : _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR;
_context_menu->set_item_checked(_context_menu->get_item_index(CONTEXT_LINEAR), is_linear);
@@ -383,7 +383,7 @@ void CurveEditor::open_context_menu(Vector2 pos) {
_context_menu->add_submenu_item(TTR("Load Preset"), _presets_menu->get_name());
- _context_menu->set_size(Size2(0, 0));
+ _context_menu->reset_size();
_context_menu->popup();
}
@@ -460,7 +460,7 @@ void CurveEditor::remove_point(int index) {
Curve::Point p = _curve_ref->get_point(index);
ur.add_do_method(*_curve_ref, "remove_point", index);
- ur.add_undo_method(*_curve_ref, "add_point", p.pos, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode);
+ ur.add_undo_method(*_curve_ref, "add_point", p.position, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode);
if (index == _selected_point) {
set_selected_point(-1);
@@ -675,11 +675,11 @@ void CurveEditor::_draw() {
// X axis
float y = curve.get_min_value();
Vector2 off(0, font_height - 1);
- draw_string(font, get_view_pos(Vector2(0, y)) + off, "0.0", HALIGN_LEFT, -1, font_size, text_color);
- draw_string(font, get_view_pos(Vector2(0.25, y)) + off, "0.25", HALIGN_LEFT, -1, font_size, text_color);
- draw_string(font, get_view_pos(Vector2(0.5, y)) + off, "0.5", HALIGN_LEFT, -1, font_size, text_color);
- draw_string(font, get_view_pos(Vector2(0.75, y)) + off, "0.75", HALIGN_LEFT, -1, font_size, text_color);
- draw_string(font, get_view_pos(Vector2(1, y)) + off, "1.0", HALIGN_LEFT, -1, font_size, text_color);
+ draw_string(font, get_view_pos(Vector2(0, y)) + off, "0.0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
+ draw_string(font, get_view_pos(Vector2(0.25, y)) + off, "0.25", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
+ draw_string(font, get_view_pos(Vector2(0.5, y)) + off, "0.5", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
+ draw_string(font, get_view_pos(Vector2(0.75, y)) + off, "0.75", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
+ draw_string(font, get_view_pos(Vector2(1, y)) + off, "1.0", HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
}
{
@@ -688,9 +688,9 @@ void CurveEditor::_draw() {
float m1 = 0.5 * (curve.get_min_value() + curve.get_max_value());
float m2 = curve.get_max_value();
Vector2 off(1, -1);
- draw_string(font, get_view_pos(Vector2(0, m0)) + off, String::num(m0, 2), HALIGN_LEFT, -1, font_size, text_color);
- draw_string(font, get_view_pos(Vector2(0, m1)) + off, String::num(m1, 2), HALIGN_LEFT, -1, font_size, text_color);
- draw_string(font, get_view_pos(Vector2(0, m2)) + off, String::num(m2, 3), HALIGN_LEFT, -1, font_size, text_color);
+ draw_string(font, get_view_pos(Vector2(0, m0)) + off, String::num(m0, 2), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
+ draw_string(font, get_view_pos(Vector2(0, m1)) + off, String::num(m1, 2), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
+ draw_string(font, get_view_pos(Vector2(0, m2)) + off, String::num(m2, 3), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
}
// Draw tangents for current point
@@ -750,10 +750,10 @@ void CurveEditor::_draw() {
if (_selected_point > 0 && _selected_point + 1 < curve.get_point_count()) {
text_color.a *= 0.4;
- draw_string(font, Vector2(50 * EDSCALE, font_height), TTR("Hold Shift to edit tangents individually"), HALIGN_LEFT, -1, font_size, text_color);
+ draw_string(font, Vector2(50 * EDSCALE, font_height), TTR("Hold Shift to edit tangents individually"), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
} else if (curve.get_point_count() == 0) {
text_color.a *= 0.4;
- draw_string(font, Vector2(50 * EDSCALE, font_height), TTR("Right click to add point"), HALIGN_LEFT, -1, font_size, text_color);
+ draw_string(font, Vector2(50 * EDSCALE, font_height), TTR("Right click to add point"), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
}
}
diff --git a/editor/plugins/debugger_editor_plugin.cpp b/editor/plugins/debugger_editor_plugin.cpp
index 1512e1817a..51e1b639a4 100644
--- a/editor/plugins/debugger_editor_plugin.cpp
+++ b/editor/plugins/debugger_editor_plugin.cpp
@@ -34,16 +34,17 @@
#include "editor/debugger/editor_debugger_node.h"
#include "editor/debugger/editor_debugger_server.h"
#include "editor/editor_node.h"
+#include "editor/editor_scale.h"
#include "editor/fileserver/editor_file_server.h"
#include "scene/gui/menu_button.h"
DebuggerEditorPlugin::DebuggerEditorPlugin(EditorNode *p_editor, MenuButton *p_debug_menu) {
EditorDebuggerServer::initialize();
- ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11);
- ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10);
+ ED_SHORTCUT("debugger/step_into", TTR("Step Into"), Key::F11);
+ ED_SHORTCUT("debugger/step_over", TTR("Step Over"), Key::F10);
ED_SHORTCUT("debugger/break", TTR("Break"));
- ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12);
+ ED_SHORTCUT("debugger/continue", TTR("Continue"), Key::F12);
ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open"));
ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor"));
@@ -52,6 +53,8 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(EditorNode *p_editor, MenuButton *p_d
EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
+ // Add separation for the warning/error icon that is displayed later.
+ db->add_theme_constant_override("hseparation", 6 * EDSCALE);
debugger->set_tool_button(db);
// Main editor debug menu.
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 415832ab3b..3b85fad345 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -297,12 +297,14 @@ EditorPackedScenePreviewPlugin::EditorPackedScenePreviewPlugin() {
//////////////////////////////////////////////////////////////////
-void EditorMaterialPreviewPlugin::_preview_done(const Variant &p_udata) {
- preview_done.set();
+void EditorMaterialPreviewPlugin::_generate_frame_started() {
+ RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
+
+ RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<EditorMaterialPreviewPlugin *>(this), &EditorMaterialPreviewPlugin::_preview_done));
}
-void EditorMaterialPreviewPlugin::_bind_methods() {
- ClassDB::bind_method("_preview_done", &EditorMaterialPreviewPlugin::_preview_done);
+void EditorMaterialPreviewPlugin::_preview_done() {
+ preview_done.post();
}
bool EditorMaterialPreviewPlugin::handles(const String &p_type) const {
@@ -320,14 +322,9 @@ Ref<Texture2D> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Si
if (material->get_shader_mode() == Shader::MODE_SPATIAL) {
RS::get_singleton()->mesh_surface_set_material(sphere, 0, material->get_rid());
- RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
+ RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<EditorMaterialPreviewPlugin *>(this), &EditorMaterialPreviewPlugin::_generate_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
- preview_done.clear();
- RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMaterialPreviewPlugin *>(this), "_preview_done", Variant());
-
- while (!preview_done.is_set()) {
- OS::get_singleton()->delay_usec(10);
- }
+ preview_done.wait();
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
RS::get_singleton()->mesh_surface_set_material(sphere, 0, RID());
@@ -480,7 +477,7 @@ Ref<Texture2D> EditorScriptPreviewPlugin::generate(const RES &p_from, const Size
}
String code = scr->get_source_code().strip_edges();
- if (code == "") {
+ if (code.is_empty()) {
return Ref<Texture2D>();
}
@@ -699,12 +696,14 @@ EditorAudioStreamPreviewPlugin::EditorAudioStreamPreviewPlugin() {
///////////////////////////////////////////////////////////////////////////
-void EditorMeshPreviewPlugin::_preview_done(const Variant &p_udata) {
- preview_done.set();
+void EditorMeshPreviewPlugin::_generate_frame_started() {
+ RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
+
+ RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<EditorMeshPreviewPlugin *>(this), &EditorMeshPreviewPlugin::_preview_done));
}
-void EditorMeshPreviewPlugin::_bind_methods() {
- ClassDB::bind_method("_preview_done", &EditorMeshPreviewPlugin::_preview_done);
+void EditorMeshPreviewPlugin::_preview_done() {
+ preview_done.post();
}
bool EditorMeshPreviewPlugin::handles(const String &p_type) const {
@@ -718,7 +717,7 @@ Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2
RS::get_singleton()->instance_set_base(mesh_instance, mesh->get_rid());
AABB aabb = mesh->get_aabb();
- Vector3 ofs = aabb.position + aabb.size * 0.5;
+ Vector3 ofs = aabb.get_center();
aabb.position -= ofs;
Transform3D xform;
xform.basis = Basis().rotated(Vector3(0, 1, 0), -Math_PI * 0.125);
@@ -735,14 +734,9 @@ Ref<Texture2D> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2
xform.origin.z -= rot_aabb.size.z * 2;
RS::get_singleton()->instance_set_transform(mesh_instance, xform);
- RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
+ RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<EditorMeshPreviewPlugin *>(this), &EditorMeshPreviewPlugin::_generate_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
- preview_done.clear();
- RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorMeshPreviewPlugin *>(this), "_preview_done", Variant());
-
- while (!preview_done.is_set()) {
- OS::get_singleton()->delay_usec(10);
- }
+ preview_done.wait();
Ref<Image> img = RS::get_singleton()->texture_2d_get(viewport_texture);
ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>());
@@ -814,12 +808,14 @@ EditorMeshPreviewPlugin::~EditorMeshPreviewPlugin() {
///////////////////////////////////////////////////////////////////////////
-void EditorFontPreviewPlugin::_preview_done(const Variant &p_udata) {
- preview_done.set();
+void EditorFontPreviewPlugin::_generate_frame_started() {
+ RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
+
+ RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<EditorFontPreviewPlugin *>(this), &EditorFontPreviewPlugin::_preview_done));
}
-void EditorFontPreviewPlugin::_bind_methods() {
- ClassDB::bind_method("_preview_done", &EditorFontPreviewPlugin::_preview_done);
+void EditorFontPreviewPlugin::_preview_done() {
+ preview_done.post();
}
bool EditorFontPreviewPlugin::handles(const String &p_type) const {
@@ -855,15 +851,11 @@ Ref<Texture2D> EditorFontPreviewPlugin::generate_from_path(const String &p_path,
Ref<Font> font = sampled_font;
- font->draw_string(canvas_item, pos, sample, HALIGN_LEFT, -1.f, 50, Color(1, 1, 1));
+ font->draw_string(canvas_item, pos, sample, HORIZONTAL_ALIGNMENT_LEFT, -1.f, 50, Color(1, 1, 1));
- preview_done.clear();
- RS::get_singleton()->viewport_set_update_mode(viewport, RS::VIEWPORT_UPDATE_ONCE); //once used for capture
- RS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant());
+ RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<EditorFontPreviewPlugin *>(this), &EditorFontPreviewPlugin::_generate_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
- while (!preview_done.is_set()) {
- OS::get_singleton()->delay_usec(10);
- }
+ preview_done.wait();
RS::get_singleton()->canvas_item_clear(canvas_item);
diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h
index 6e8b9a34cf..bf52f5771d 100644
--- a/editor/plugins/editor_preview_plugins.h
+++ b/editor/plugins/editor_preview_plugins.h
@@ -92,12 +92,10 @@ class EditorMaterialPreviewPlugin : public EditorResourcePreviewGenerator {
RID light2;
RID light_instance2;
RID camera;
- mutable SafeFlag preview_done;
+ Semaphore preview_done;
- void _preview_done(const Variant &p_udata);
-
-protected:
- static void _bind_methods();
+ void _generate_frame_started();
+ void _preview_done();
public:
virtual bool handles(const String &p_type) const override;
@@ -136,12 +134,10 @@ class EditorMeshPreviewPlugin : public EditorResourcePreviewGenerator {
RID light2;
RID light_instance2;
RID camera;
- mutable SafeFlag preview_done;
-
- void _preview_done(const Variant &p_udata);
+ Semaphore preview_done;
-protected:
- static void _bind_methods();
+ void _generate_frame_started();
+ void _preview_done();
public:
virtual bool handles(const String &p_type) const override;
@@ -158,12 +154,10 @@ class EditorFontPreviewPlugin : public EditorResourcePreviewGenerator {
RID viewport_texture;
RID canvas;
RID canvas_item;
- mutable SafeFlag preview_done;
-
- void _preview_done(const Variant &p_udata);
+ Semaphore preview_done;
-protected:
- static void _bind_methods();
+ void _generate_frame_started();
+ void _preview_done();
public:
virtual bool handles(const String &p_type) const override;
@@ -173,4 +167,20 @@ public:
EditorFontPreviewPlugin();
~EditorFontPreviewPlugin();
};
+
+class EditorTileMapPatternPreviewPlugin : public EditorResourcePreviewGenerator {
+ GDCLASS(EditorTileMapPatternPreviewPlugin, EditorResourcePreviewGenerator);
+
+ Semaphore preview_done;
+
+ void _generate_frame_started();
+ void _preview_done();
+
+public:
+ virtual bool handles(const String &p_type) const override;
+ virtual Ref<Texture2D> generate(const RES &p_from, const Size2 &p_size) const override;
+
+ EditorTileMapPatternPreviewPlugin();
+ ~EditorTileMapPatternPreviewPlugin();
+};
#endif // EDITORPREVIEWPLUGINS_H
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index dd91df747a..4b50f484a4 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -57,6 +57,27 @@ void GPUParticles2DEditorPlugin::_file_selected(const String &p_file) {
emission_mask->popup_centered();
}
+void GPUParticles2DEditorPlugin::_selection_changed() {
+ List<Node *> selected_nodes = editor->get_editor_selection()->get_selected_node_list();
+
+ if (selected_particles.is_empty() && selected_nodes.is_empty()) {
+ return;
+ }
+
+ for (GPUParticles2D *SP : selected_particles) {
+ SP->set_show_visibility_rect(false);
+ }
+ selected_particles.clear();
+
+ for (Node *P : selected_nodes) {
+ GPUParticles2D *selected_particle = Object::cast_to<GPUParticles2D>(P);
+ if (selected_particle != nullptr) {
+ selected_particle->set_show_visibility_rect(true);
+ selected_particles.push_back(selected_particle);
+ }
+ }
+}
+
void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
switch (p_idx) {
case MENU_GENERATE_VISIBILITY_RECT: {
@@ -158,7 +179,7 @@ void GPUParticles2DEditorPlugin::_generate_emission_mask() {
}
img->convert(Image::FORMAT_RGBA8);
ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8);
- Size2i s = Size2(img->get_width(), img->get_height());
+ Size2i s = img->get_size();
ERR_FAIL_COND(s.width == 0 || s.height == 0);
Vector<Point2> valid_positions;
@@ -334,6 +355,7 @@ void GPUParticles2DEditorPlugin::_notification(int p_what) {
menu->get_popup()->connect("id_pressed", callable_mp(this, &GPUParticles2DEditorPlugin::_menu_callback));
menu->set_icon(menu->get_theme_icon(SNAME("GPUParticles2D"), SNAME("EditorIcons")));
file->connect("file_selected", callable_mp(this, &GPUParticles2DEditorPlugin::_file_selected));
+ EditorNode::get_singleton()->get_editor_selection()->connect("selection_changed", callable_mp(this, &GPUParticles2DEditorPlugin::_selection_changed));
}
}
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h
index 0b2028b745..bdfc021aa7 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.h
@@ -56,6 +56,7 @@ class GPUParticles2DEditorPlugin : public EditorPlugin {
};
GPUParticles2D *particles;
+ List<GPUParticles2D *> selected_particles;
EditorFileDialog *file;
EditorNode *editor;
@@ -79,6 +80,7 @@ class GPUParticles2DEditorPlugin : public EditorPlugin {
void _menu_callback(int p_idx);
void _generate_visibility_rect();
void _generate_emission_mask();
+ void _selection_changed();
protected:
void _notification(int p_what);
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 903a3689b0..5ac58795d1 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -362,6 +362,7 @@ void GPUParticles3DEditor::_generate_emission_points() {
Ref<ImageTexture> tex;
tex.instantiate();
+ tex->create_from_image(image);
Ref<ParticlesMaterial> material = node->get_process_material();
ERR_FAIL_COND(material.is_null());
@@ -390,6 +391,7 @@ void GPUParticles3DEditor::_generate_emission_points() {
Ref<ImageTexture> tex2;
tex2.instantiate();
+ tex2->create_from_image(image2);
material->set_emission_normal_texture(tex2);
} else {
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
index a4436525fb..f6788f80ba 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
@@ -30,11 +30,11 @@
#include "gpu_particles_collision_sdf_editor_plugin.h"
-void GPUParticlesCollisionSDFEditorPlugin::_bake() {
+void GPUParticlesCollisionSDF3DEditorPlugin::_bake() {
if (col_sdf) {
if (col_sdf->get_texture().is_null() || !col_sdf->get_texture()->get_path().is_resource_file()) {
- String path = get_tree()->get_edited_scene_root()->get_filename();
- if (path == String()) {
+ String path = get_tree()->get_edited_scene_root()->get_scene_file_path();
+ if (path.is_empty()) {
path = "res://" + col_sdf->get_name() + "_data.exr";
} else {
String ext = path.get_extension();
@@ -49,8 +49,8 @@ void GPUParticlesCollisionSDFEditorPlugin::_bake() {
}
}
-void GPUParticlesCollisionSDFEditorPlugin::edit(Object *p_object) {
- GPUParticlesCollisionSDF *s = Object::cast_to<GPUParticlesCollisionSDF>(p_object);
+void GPUParticlesCollisionSDF3DEditorPlugin::edit(Object *p_object) {
+ GPUParticlesCollisionSDF3D *s = Object::cast_to<GPUParticlesCollisionSDF3D>(p_object);
if (!s) {
return;
}
@@ -58,46 +58,50 @@ void GPUParticlesCollisionSDFEditorPlugin::edit(Object *p_object) {
col_sdf = s;
}
-bool GPUParticlesCollisionSDFEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("GPUParticlesCollisionSDF");
+bool GPUParticlesCollisionSDF3DEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("GPUParticlesCollisionSDF3D");
}
-void GPUParticlesCollisionSDFEditorPlugin::_notification(int p_what) {
+void GPUParticlesCollisionSDF3DEditorPlugin::_notification(int p_what) {
if (p_what == NOTIFICATION_PROCESS) {
if (!col_sdf) {
return;
}
+ // Set information tooltip on the Bake button. This information is useful
+ // to optimize performance (video RAM size) and reduce collision tunneling (individual cell size).
+
const Vector3i size = col_sdf->get_estimated_cell_size();
- String text = vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z);
- int data_size = 2;
- const double size_mb = size.x * size.y * size.z * data_size / (1024.0 * 1024.0);
- text += " - " + vformat(TTR("VRAM Size: %s MB"), String::num(size_mb, 2));
+ const Vector3 extents = col_sdf->get_extents();
- if (bake_info->get_text() == text) {
- return;
+ int data_size = 2;
+ const double size_mb = size.x * size.y * size.z * data_size / (1024.0 * 1024.0);
+ // Add a qualitative measurement to help the user assess whether a GPUParticlesCollisionSDF3D node is using a lot of VRAM.
+ String size_quality;
+ if (size_mb < 8.0) {
+ size_quality = TTR("Low");
+ } else if (size_mb < 32.0) {
+ size_quality = TTR("Moderate");
+ } else {
+ size_quality = TTR("High");
}
- // Color the label depending on the estimated performance level.
- Color color;
- if (size_mb <= 16.0 + CMP_EPSILON) {
- // Fast.
- color = bake_info->get_theme_color(SNAME("success_color"), SNAME("Editor"));
- } else if (size_mb <= 64.0 + CMP_EPSILON) {
- // Medium.
- color = bake_info->get_theme_color(SNAME("warning_color"), SNAME("Editor"));
- } else {
- // Slow.
- color = bake_info->get_theme_color(SNAME("error_color"), SNAME("Editor"));
+ String text;
+ text += vformat(TTR("Subdivisions: %s"), vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z)) + "\n";
+ text += vformat(TTR("Cell size: %s"), vformat(String::utf8("%.3f × %.3f × %.3f"), extents.x / size.x, extents.y / size.y, extents.z / size.z)) + "\n";
+ text += vformat(TTR("Video RAM size: %s MB (%s)"), String::num(size_mb, 2), size_quality);
+
+ // Only update the tooltip when needed to avoid constant redrawing.
+ if (bake->get_tooltip(Point2()) == text) {
+ return;
}
- bake_info->add_theme_color_override("font_color", color);
- bake_info->set_text(text);
+ bake->set_tooltip(text);
}
}
-void GPUParticlesCollisionSDFEditorPlugin::make_visible(bool p_visible) {
+void GPUParticlesCollisionSDF3DEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
bake_hb->show();
set_process(true);
@@ -107,26 +111,26 @@ void GPUParticlesCollisionSDFEditorPlugin::make_visible(bool p_visible) {
}
}
-EditorProgress *GPUParticlesCollisionSDFEditorPlugin::tmp_progress = nullptr;
+EditorProgress *GPUParticlesCollisionSDF3DEditorPlugin::tmp_progress = nullptr;
-void GPUParticlesCollisionSDFEditorPlugin::bake_func_begin(int p_steps) {
+void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_begin(int p_steps) {
ERR_FAIL_COND(tmp_progress != nullptr);
tmp_progress = memnew(EditorProgress("bake_sdf", TTR("Bake SDF"), p_steps));
}
-void GPUParticlesCollisionSDFEditorPlugin::bake_func_step(int p_step, const String &p_description) {
+void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_step(int p_step, const String &p_description) {
ERR_FAIL_COND(tmp_progress == nullptr);
tmp_progress->step(p_description, p_step, false);
}
-void GPUParticlesCollisionSDFEditorPlugin::bake_func_end() {
+void GPUParticlesCollisionSDF3DEditorPlugin::bake_func_end() {
ERR_FAIL_COND(tmp_progress == nullptr);
memdelete(tmp_progress);
tmp_progress = nullptr;
}
-void GPUParticlesCollisionSDFEditorPlugin::_sdf_save_path_and_bake(const String &p_path) {
+void GPUParticlesCollisionSDF3DEditorPlugin::_sdf_save_path_and_bake(const String &p_path) {
probe_file->hide();
if (col_sdf) {
Ref<Image> bake_img = col_sdf->bake();
@@ -164,10 +168,10 @@ void GPUParticlesCollisionSDFEditorPlugin::_sdf_save_path_and_bake(const String
}
}
-void GPUParticlesCollisionSDFEditorPlugin::_bind_methods() {
+void GPUParticlesCollisionSDF3DEditorPlugin::_bind_methods() {
}
-GPUParticlesCollisionSDFEditorPlugin::GPUParticlesCollisionSDFEditorPlugin(EditorNode *p_node) {
+GPUParticlesCollisionSDF3DEditorPlugin::GPUParticlesCollisionSDF3DEditorPlugin(EditorNode *p_node) {
editor = p_node;
bake_hb = memnew(HBoxContainer);
bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -176,26 +180,22 @@ GPUParticlesCollisionSDFEditorPlugin::GPUParticlesCollisionSDFEditorPlugin(Edito
bake->set_flat(true);
bake->set_icon(editor->get_gui_base()->get_theme_icon(SNAME("Bake"), SNAME("EditorIcons")));
bake->set_text(TTR("Bake SDF"));
- bake->connect("pressed", callable_mp(this, &GPUParticlesCollisionSDFEditorPlugin::_bake));
+ bake->connect("pressed", callable_mp(this, &GPUParticlesCollisionSDF3DEditorPlugin::_bake));
bake_hb->add_child(bake);
- bake_info = memnew(Label);
- bake_info->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- bake_info->set_clip_text(true);
- bake_hb->add_child(bake_info);
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake_hb);
col_sdf = nullptr;
probe_file = memnew(EditorFileDialog);
probe_file->set_file_mode(EditorFileDialog::FILE_MODE_SAVE_FILE);
probe_file->add_filter("*.exr");
- probe_file->connect("file_selected", callable_mp(this, &GPUParticlesCollisionSDFEditorPlugin::_sdf_save_path_and_bake));
+ probe_file->connect("file_selected", callable_mp(this, &GPUParticlesCollisionSDF3DEditorPlugin::_sdf_save_path_and_bake));
get_editor_interface()->get_base_control()->add_child(probe_file);
probe_file->set_title(TTR("Select path for SDF Texture"));
- GPUParticlesCollisionSDF::bake_begin_function = bake_func_begin;
- GPUParticlesCollisionSDF::bake_step_function = bake_func_step;
- GPUParticlesCollisionSDF::bake_end_function = bake_func_end;
+ GPUParticlesCollisionSDF3D::bake_begin_function = bake_func_begin;
+ GPUParticlesCollisionSDF3D::bake_step_function = bake_func_step;
+ GPUParticlesCollisionSDF3D::bake_end_function = bake_func_end;
}
-GPUParticlesCollisionSDFEditorPlugin::~GPUParticlesCollisionSDFEditorPlugin() {
+GPUParticlesCollisionSDF3DEditorPlugin::~GPUParticlesCollisionSDF3DEditorPlugin() {
}
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
index 5a71fc44ef..ce7ca18293 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
@@ -36,13 +36,12 @@
#include "scene/3d/gpu_particles_collision_3d.h"
#include "scene/resources/material.h"
-class GPUParticlesCollisionSDFEditorPlugin : public EditorPlugin {
- GDCLASS(GPUParticlesCollisionSDFEditorPlugin, EditorPlugin);
+class GPUParticlesCollisionSDF3DEditorPlugin : public EditorPlugin {
+ GDCLASS(GPUParticlesCollisionSDF3DEditorPlugin, EditorPlugin);
- GPUParticlesCollisionSDF *col_sdf;
+ GPUParticlesCollisionSDF3D *col_sdf;
HBoxContainer *bake_hb;
- Label *bake_info;
Button *bake;
EditorNode *editor;
@@ -61,14 +60,14 @@ protected:
void _notification(int p_what);
public:
- virtual String get_name() const override { return "GPUParticlesCollisionSDF"; }
+ virtual String get_name() const override { return "GPUParticlesCollisionSDF3D"; }
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;
- GPUParticlesCollisionSDFEditorPlugin(EditorNode *p_node);
- ~GPUParticlesCollisionSDFEditorPlugin();
+ GPUParticlesCollisionSDF3DEditorPlugin(EditorNode *p_node);
+ ~GPUParticlesCollisionSDF3DEditorPlugin();
};
#endif // GPU_PARTICLES_COLLISION_SDF_EDITOR_PLUGIN_H
diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp
index 355bdb69d8..da050abc02 100644
--- a/editor/plugins/gradient_editor_plugin.cpp
+++ b/editor/plugins/gradient_editor_plugin.cpp
@@ -46,6 +46,8 @@ void GradientEditor::_gradient_changed() {
editing = true;
Vector<Gradient::Point> points = gradient->get_points();
set_points(points);
+ set_interpolation_mode(gradient->get_interpolation_mode());
+ update();
editing = false;
}
@@ -55,8 +57,10 @@ void GradientEditor::_ramp_changed() {
undo_redo->create_action(TTR("Gradient Edited"));
undo_redo->add_do_method(gradient.ptr(), "set_offsets", get_offsets());
undo_redo->add_do_method(gradient.ptr(), "set_colors", get_colors());
+ undo_redo->add_do_method(gradient.ptr(), "set_interpolation_mode", get_interpolation_mode());
undo_redo->add_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets());
undo_redo->add_undo_method(gradient.ptr(), "set_colors", gradient->get_colors());
+ undo_redo->add_undo_method(gradient.ptr(), "set_interpolation_mode", gradient->get_interpolation_mode());
undo_redo->commit_action();
editing = false;
}
@@ -69,6 +73,14 @@ void GradientEditor::set_gradient(const Ref<Gradient> &p_gradient) {
connect("ramp_changed", callable_mp(this, &GradientEditor::_ramp_changed));
gradient->connect("changed", callable_mp(this, &GradientEditor::_gradient_changed));
set_points(gradient->get_points());
+ set_interpolation_mode(gradient->get_interpolation_mode());
+}
+
+void GradientEditor::reverse_gradient() {
+ gradient->reverse();
+ set_points(gradient->get_points());
+ emit_signal(SNAME("ramp_changed"));
+ update();
}
GradientEditor::GradientEditor() {
@@ -77,6 +89,23 @@ GradientEditor::GradientEditor() {
///////////////////////
+void GradientReverseButton::_notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW) {
+ Ref<Texture2D> icon = get_theme_icon(SNAME("ReverseGradient"), SNAME("EditorIcons"));
+ if (is_pressed()) {
+ draw_texture_rect(icon, Rect2(margin, margin, icon->get_width(), icon->get_height()), false, get_theme_color(SNAME("icon_pressed_color"), SNAME("Button")));
+ } else {
+ draw_texture_rect(icon, Rect2(margin, margin, icon->get_width(), icon->get_height()));
+ }
+ }
+}
+
+Size2 GradientReverseButton::get_minimum_size() const {
+ return (get_theme_icon(SNAME("ReverseGradient"), SNAME("EditorIcons"))->get_size() + Size2(margin * 2, margin * 2));
+}
+
+///////////////////////
+
bool EditorInspectorPluginGradient::can_handle(Object *p_object) {
return Object::cast_to<Gradient>(p_object) != nullptr;
}
@@ -85,9 +114,23 @@ void EditorInspectorPluginGradient::parse_begin(Object *p_object) {
Gradient *gradient = Object::cast_to<Gradient>(p_object);
Ref<Gradient> g(gradient);
- GradientEditor *editor = memnew(GradientEditor);
+ editor = memnew(GradientEditor);
editor->set_gradient(g);
add_custom_control(editor);
+
+ reverse_btn = memnew(GradientReverseButton);
+
+ gradient_tools_hbox = memnew(HBoxContainer);
+ gradient_tools_hbox->add_child(reverse_btn);
+
+ add_custom_control(gradient_tools_hbox);
+
+ reverse_btn->connect("pressed", callable_mp(this, &EditorInspectorPluginGradient::_reverse_button_pressed));
+ reverse_btn->set_tooltip(TTR("Reverse/mirror gradient."));
+}
+
+void EditorInspectorPluginGradient::_reverse_button_pressed() {
+ editor->reverse_gradient();
}
GradientEditorPlugin::GradientEditorPlugin(EditorNode *p_node) {
diff --git a/editor/plugins/gradient_editor_plugin.h b/editor/plugins/gradient_editor_plugin.h
index bcbb86e422..95b7b466c9 100644
--- a/editor/plugins/gradient_editor_plugin.h
+++ b/editor/plugins/gradient_editor_plugin.h
@@ -50,12 +50,28 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
void set_gradient(const Ref<Gradient> &p_gradient);
+ void reverse_gradient();
GradientEditor();
};
+class GradientReverseButton : public BaseButton {
+ GDCLASS(GradientReverseButton, BaseButton);
+
+ int margin = 2;
+
+ void _notification(int p_what);
+ virtual Size2 get_minimum_size() const override;
+};
+
class EditorInspectorPluginGradient : public EditorInspectorPlugin {
GDCLASS(EditorInspectorPluginGradient, EditorInspectorPlugin);
+ GradientEditor *editor;
+ HBoxContainer *gradient_tools_hbox;
+ GradientReverseButton *reverse_btn;
+
+ void _reverse_button_pressed();
+
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
deleted file mode 100644
index 3207a989bd..0000000000
--- a/editor/plugins/item_list_editor_plugin.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-/*************************************************************************/
-/* item_list_editor_plugin.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#include "item_list_editor_plugin.h"
-
-#include "core/io/resource_loader.h"
-#include "editor/editor_scale.h"
-
-bool ItemListPlugin::_set(const StringName &p_name, const Variant &p_value) {
- String name = p_name;
- int idx = name.get_slice("/", 0).to_int();
- String what = name.get_slice("/", 1);
-
- if (what == "text") {
- set_item_text(idx, p_value);
- } else if (what == "icon") {
- set_item_icon(idx, p_value);
- } else if (what == "checkable") {
- // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
- switch ((int)p_value) {
- case 0:
- case 1:
- set_item_checkable(idx, p_value);
- break;
- case 2:
- set_item_radio_checkable(idx, true);
- break;
- }
- } else if (what == "checked") {
- set_item_checked(idx, p_value);
- } else if (what == "id") {
- set_item_id(idx, p_value);
- } else if (what == "enabled") {
- set_item_enabled(idx, p_value);
- } else if (what == "separator") {
- set_item_separator(idx, p_value);
- } else {
- return false;
- }
-
- return true;
-}
-
-bool ItemListPlugin::_get(const StringName &p_name, Variant &r_ret) const {
- String name = p_name;
- int idx = name.get_slice("/", 0).to_int();
- String what = name.get_slice("/", 1);
-
- if (what == "text") {
- r_ret = get_item_text(idx);
- } else if (what == "icon") {
- r_ret = get_item_icon(idx);
- } else if (what == "checkable") {
- // This keeps compatibility to/from versions where this property was a boolean, before radio buttons
- if (!is_item_checkable(idx)) {
- r_ret = 0;
- } else {
- r_ret = is_item_radio_checkable(idx) ? 2 : 1;
- }
- } else if (what == "checked") {
- r_ret = is_item_checked(idx);
- } else if (what == "id") {
- r_ret = get_item_id(idx);
- } else if (what == "enabled") {
- r_ret = is_item_enabled(idx);
- } else if (what == "separator") {
- r_ret = is_item_separator(idx);
- } else {
- return false;
- }
-
- return true;
-}
-
-void ItemListPlugin::_get_property_list(List<PropertyInfo> *p_list) const {
- for (int i = 0; i < get_item_count(); i++) {
- String base = itos(i) + "/";
-
- p_list->push_back(PropertyInfo(Variant::STRING, base + "text"));
- p_list->push_back(PropertyInfo(Variant::OBJECT, base + "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"));
-
- int flags = get_flags();
-
- if (flags & FLAG_CHECKABLE) {
- p_list->push_back(PropertyInfo(Variant::INT, base + "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"));
- p_list->push_back(PropertyInfo(Variant::BOOL, base + "checked"));
- }
-
- if (flags & FLAG_ID) {
- p_list->push_back(PropertyInfo(Variant::INT, base + "id", PROPERTY_HINT_RANGE, "-1,4096"));
- }
-
- if (flags & FLAG_ENABLE) {
- p_list->push_back(PropertyInfo(Variant::BOOL, base + "enabled"));
- }
-
- if (flags & FLAG_SEPARATOR) {
- p_list->push_back(PropertyInfo(Variant::BOOL, base + "separator"));
- }
- }
-}
-
-///////////////////////////////////////////////////////////////
-///////////////////////// PLUGINS /////////////////////////////
-///////////////////////////////////////////////////////////////
-
-void ItemListOptionButtonPlugin::set_object(Object *p_object) {
- ob = Object::cast_to<OptionButton>(p_object);
-}
-
-bool ItemListOptionButtonPlugin::handles(Object *p_object) const {
- return p_object->is_class("OptionButton");
-}
-
-int ItemListOptionButtonPlugin::get_flags() const {
- return FLAG_ICON | FLAG_ID | FLAG_ENABLE;
-}
-
-void ItemListOptionButtonPlugin::add_item() {
- ob->add_item(vformat(TTR("Item %d"), ob->get_item_count()));
- notify_property_list_changed();
-}
-
-int ItemListOptionButtonPlugin::get_item_count() const {
- return ob->get_item_count();
-}
-
-void ItemListOptionButtonPlugin::erase(int p_idx) {
- ob->remove_item(p_idx);
- notify_property_list_changed();
-}
-
-ItemListOptionButtonPlugin::ItemListOptionButtonPlugin() {
- ob = nullptr;
-}
-
-///////////////////////////////////////////////////////////////
-
-void ItemListPopupMenuPlugin::set_object(Object *p_object) {
- if (p_object->is_class("MenuButton")) {
- pp = Object::cast_to<MenuButton>(p_object)->get_popup();
- } else {
- pp = Object::cast_to<PopupMenu>(p_object);
- }
-}
-
-bool ItemListPopupMenuPlugin::handles(Object *p_object) const {
- return p_object->is_class("PopupMenu") || p_object->is_class("MenuButton");
-}
-
-int ItemListPopupMenuPlugin::get_flags() const {
- return FLAG_ICON | FLAG_CHECKABLE | FLAG_ID | FLAG_ENABLE | FLAG_SEPARATOR;
-}
-
-void ItemListPopupMenuPlugin::add_item() {
- pp->add_item(vformat(TTR("Item %d"), pp->get_item_count()));
- notify_property_list_changed();
-}
-
-int ItemListPopupMenuPlugin::get_item_count() const {
- return pp->get_item_count();
-}
-
-void ItemListPopupMenuPlugin::erase(int p_idx) {
- pp->remove_item(p_idx);
- notify_property_list_changed();
-}
-
-ItemListPopupMenuPlugin::ItemListPopupMenuPlugin() {
- pp = nullptr;
-}
-
-///////////////////////////////////////////////////////////////
-
-void ItemListItemListPlugin::set_object(Object *p_object) {
- pp = Object::cast_to<ItemList>(p_object);
-}
-
-bool ItemListItemListPlugin::handles(Object *p_object) const {
- return p_object->is_class("ItemList");
-}
-
-int ItemListItemListPlugin::get_flags() const {
- return FLAG_ICON | FLAG_ENABLE;
-}
-
-void ItemListItemListPlugin::add_item() {
- pp->add_item(vformat(TTR("Item %d"), pp->get_item_count()));
- notify_property_list_changed();
-}
-
-int ItemListItemListPlugin::get_item_count() const {
- return pp->get_item_count();
-}
-
-void ItemListItemListPlugin::erase(int p_idx) {
- pp->remove_item(p_idx);
- notify_property_list_changed();
-}
-
-ItemListItemListPlugin::ItemListItemListPlugin() {
- pp = nullptr;
-}
-
-///////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////
-
-void ItemListEditor::_node_removed(Node *p_node) {
- if (p_node == item_list) {
- item_list = nullptr;
- hide();
- dialog->hide();
- }
-}
-
-void ItemListEditor::_notification(int p_notification) {
- if (p_notification == NOTIFICATION_ENTER_TREE || p_notification == NOTIFICATION_THEME_CHANGED) {
- add_button->set_icon(get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
- del_button->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
- } else if (p_notification == NOTIFICATION_READY) {
- get_tree()->connect("node_removed", callable_mp(this, &ItemListEditor::_node_removed));
- }
-}
-
-void ItemListEditor::_add_pressed() {
- if (selected_idx == -1) {
- return;
- }
-
- item_plugins[selected_idx]->add_item();
-}
-
-void ItemListEditor::_delete_pressed() {
- if (selected_idx == -1) {
- return;
- }
-
- String current_selected = (String)property_editor->get_selected_path();
-
- if (current_selected == "") {
- return;
- }
-
- // FIXME: Currently relying on selecting a *property* to derive what item to delete
- // e.g. you select "1/enabled" to delete item 1.
- // This should be fixed so that you can delete by selecting the item section header,
- // or a delete button on that header.
-
- int idx = current_selected.get_slice("/", 0).to_int();
-
- item_plugins[selected_idx]->erase(idx);
-}
-
-void ItemListEditor::_edit_items() {
- dialog->popup_centered_clamped(Vector2(425, 1200) * EDSCALE, 0.8);
-}
-
-void ItemListEditor::edit(Node *p_item_list) {
- item_list = p_item_list;
-
- if (!item_list) {
- selected_idx = -1;
- property_editor->edit(nullptr);
- return;
- }
-
- for (int i = 0; i < item_plugins.size(); i++) {
- if (item_plugins[i]->handles(p_item_list)) {
- item_plugins[i]->set_object(p_item_list);
- property_editor->edit(item_plugins[i]);
-
- toolbar_button->set_icon(EditorNode::get_singleton()->get_object_icon(item_list, ""));
-
- selected_idx = i;
- return;
- }
- }
-
- selected_idx = -1;
- property_editor->edit(nullptr);
-}
-
-bool ItemListEditor::handles(Object *p_object) const {
- for (int i = 0; i < item_plugins.size(); i++) {
- if (item_plugins[i]->handles(p_object)) {
- return true;
- }
- }
-
- return false;
-}
-
-void ItemListEditor::_bind_methods() {
-}
-
-ItemListEditor::ItemListEditor() {
- selected_idx = -1;
- item_list = nullptr;
-
- toolbar_button = memnew(Button);
- toolbar_button->set_flat(true);
- toolbar_button->set_text(TTR("Items"));
- add_child(toolbar_button);
- toolbar_button->connect("pressed", callable_mp(this, &ItemListEditor::_edit_items));
-
- dialog = memnew(AcceptDialog);
- dialog->set_title(TTR("Item List Editor"));
- add_child(dialog);
-
- VBoxContainer *vbc = memnew(VBoxContainer);
- dialog->add_child(vbc);
- //dialog->set_child_rect(vbc);
-
- HBoxContainer *hbc = memnew(HBoxContainer);
- hbc->set_h_size_flags(SIZE_EXPAND_FILL);
- vbc->add_child(hbc);
-
- add_button = memnew(Button);
- add_button->set_text(TTR("Add"));
- hbc->add_child(add_button);
- add_button->connect("pressed", callable_mp(this, &ItemListEditor::_add_pressed));
-
- hbc->add_spacer();
-
- del_button = memnew(Button);
- del_button->set_text(TTR("Delete"));
- hbc->add_child(del_button);
- del_button->connect("pressed", callable_mp(this, &ItemListEditor::_delete_pressed));
-
- property_editor = memnew(EditorInspector);
- vbc->add_child(property_editor);
- property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
-}
-
-ItemListEditor::~ItemListEditor() {
- for (int i = 0; i < item_plugins.size(); i++) {
- memdelete(item_plugins[i]);
- }
-}
-
-void ItemListEditorPlugin::edit(Object *p_object) {
- item_list_editor->edit(Object::cast_to<Node>(p_object));
-}
-
-bool ItemListEditorPlugin::handles(Object *p_object) const {
- return item_list_editor->handles(p_object);
-}
-
-void ItemListEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- item_list_editor->show();
- } else {
- item_list_editor->hide();
- item_list_editor->edit(nullptr);
- }
-}
-
-ItemListEditorPlugin::ItemListEditorPlugin(EditorNode *p_node) {
- editor = p_node;
- item_list_editor = memnew(ItemListEditor);
- CanvasItemEditor::get_singleton()->add_control_to_menu_panel(item_list_editor);
-
- item_list_editor->hide();
- item_list_editor->add_plugin(memnew(ItemListOptionButtonPlugin));
- item_list_editor->add_plugin(memnew(ItemListPopupMenuPlugin));
- item_list_editor->add_plugin(memnew(ItemListItemListPlugin));
-}
-
-ItemListEditorPlugin::~ItemListEditorPlugin() {
-}
diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h
deleted file mode 100644
index 8c77f3d952..0000000000
--- a/editor/plugins/item_list_editor_plugin.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*************************************************************************/
-/* item_list_editor_plugin.h */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
-/* */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
-
-#ifndef ITEM_LIST_EDITOR_PLUGIN_H
-#define ITEM_LIST_EDITOR_PLUGIN_H
-
-#include "canvas_item_editor_plugin.h"
-#include "editor/editor_inspector.h"
-#include "editor/editor_node.h"
-#include "editor/editor_plugin.h"
-#include "scene/gui/menu_button.h"
-#include "scene/gui/option_button.h"
-#include "scene/gui/popup_menu.h"
-
-class ItemListPlugin : public Object {
- GDCLASS(ItemListPlugin, Object);
-
-protected:
- bool _set(const StringName &p_name, const Variant &p_value);
- bool _get(const StringName &p_name, Variant &r_ret) const;
- void _get_property_list(List<PropertyInfo> *p_list) const;
-
-public:
- enum Flags {
- FLAG_ICON = 1,
- FLAG_CHECKABLE = 2,
- FLAG_ID = 4,
- FLAG_ENABLE = 8,
- FLAG_SEPARATOR = 16
- };
-
- virtual void set_object(Object *p_object) = 0;
- virtual bool handles(Object *p_object) const = 0;
-
- virtual int get_flags() const = 0;
-
- virtual void set_item_text(int p_idx, const String &p_text) {}
- virtual String get_item_text(int p_idx) const { return ""; };
-
- virtual void set_item_icon(int p_idx, const Ref<Texture2D> &p_tex) {}
- virtual Ref<Texture2D> get_item_icon(int p_idx) const { return Ref<Texture2D>(); };
-
- virtual void set_item_checkable(int p_idx, bool p_check) {}
- virtual void set_item_radio_checkable(int p_idx, bool p_check) {}
- virtual bool is_item_checkable(int p_idx) const { return false; };
- virtual bool is_item_radio_checkable(int p_idx) const { return false; };
-
- virtual void set_item_checked(int p_idx, bool p_checked) {}
- virtual bool is_item_checked(int p_idx) const { return false; };
-
- virtual void set_item_enabled(int p_idx, int p_enabled) {}
- virtual bool is_item_enabled(int p_idx) const { return false; };
-
- virtual void set_item_id(int p_idx, int p_id) {}
- virtual int get_item_id(int p_idx) const { return -1; };
-
- virtual void set_item_separator(int p_idx, bool p_separator) {}
- virtual bool is_item_separator(int p_idx) const { return false; };
-
- virtual void add_item() = 0;
- virtual int get_item_count() const = 0;
- virtual void erase(int p_idx) = 0;
-
- ItemListPlugin() {}
-};
-
-///////////////////////////////////////////////////////////////
-
-class ItemListOptionButtonPlugin : public ItemListPlugin {
- GDCLASS(ItemListOptionButtonPlugin, ItemListPlugin);
-
- OptionButton *ob;
-
-public:
- virtual void set_object(Object *p_object) override;
- virtual bool handles(Object *p_object) const override;
- virtual int get_flags() const override;
-
- virtual void set_item_text(int p_idx, const String &p_text) override { ob->set_item_text(p_idx, p_text); }
- virtual String get_item_text(int p_idx) const override { return ob->get_item_text(p_idx); }
-
- virtual void set_item_icon(int p_idx, const Ref<Texture2D> &p_tex) override { ob->set_item_icon(p_idx, p_tex); }
- virtual Ref<Texture2D> get_item_icon(int p_idx) const override { return ob->get_item_icon(p_idx); }
-
- virtual void set_item_enabled(int p_idx, int p_enabled) override { ob->set_item_disabled(p_idx, !p_enabled); }
- virtual bool is_item_enabled(int p_idx) const override { return !ob->is_item_disabled(p_idx); }
-
- virtual void set_item_id(int p_idx, int p_id) override { ob->set_item_id(p_idx, p_id); }
- virtual int get_item_id(int p_idx) const override { return ob->get_item_id(p_idx); }
-
- virtual void add_item() override;
- virtual int get_item_count() const override;
- virtual void erase(int p_idx) override;
-
- ItemListOptionButtonPlugin();
-};
-
-class ItemListPopupMenuPlugin : public ItemListPlugin {
- GDCLASS(ItemListPopupMenuPlugin, ItemListPlugin);
-
- PopupMenu *pp;
-
-public:
- virtual void set_object(Object *p_object) override;
- virtual bool handles(Object *p_object) const override;
- virtual int get_flags() const override;
-
- virtual void set_item_text(int p_idx, const String &p_text) override { pp->set_item_text(p_idx, p_text); }
- virtual String get_item_text(int p_idx) const override { return pp->get_item_text(p_idx); }
-
- virtual void set_item_icon(int p_idx, const Ref<Texture2D> &p_tex) override { pp->set_item_icon(p_idx, p_tex); }
- virtual Ref<Texture2D> get_item_icon(int p_idx) const override { return pp->get_item_icon(p_idx); }
-
- virtual void set_item_checkable(int p_idx, bool p_check) override { pp->set_item_as_checkable(p_idx, p_check); }
- virtual void set_item_radio_checkable(int p_idx, bool p_check) override { pp->set_item_as_radio_checkable(p_idx, p_check); }
- virtual bool is_item_checkable(int p_idx) const override { return pp->is_item_checkable(p_idx); }
- virtual bool is_item_radio_checkable(int p_idx) const override { return pp->is_item_radio_checkable(p_idx); }
-
- virtual void set_item_checked(int p_idx, bool p_checked) override { pp->set_item_checked(p_idx, p_checked); }
- virtual bool is_item_checked(int p_idx) const override { return pp->is_item_checked(p_idx); }
-
- virtual void set_item_enabled(int p_idx, int p_enabled) override { pp->set_item_disabled(p_idx, !p_enabled); }
- virtual bool is_item_enabled(int p_idx) const override { return !pp->is_item_disabled(p_idx); }
-
- virtual void set_item_id(int p_idx, int p_id) override { pp->set_item_id(p_idx, p_id); }
- virtual int get_item_id(int p_idx) const override { return pp->get_item_id(p_idx); }
-
- virtual void set_item_separator(int p_idx, bool p_separator) override { pp->set_item_as_separator(p_idx, p_separator); }
- virtual bool is_item_separator(int p_idx) const override { return pp->is_item_separator(p_idx); }
-
- virtual void add_item() override;
- virtual int get_item_count() const override;
- virtual void erase(int p_idx) override;
-
- ItemListPopupMenuPlugin();
-};
-
-///////////////////////////////////////////////////////////////
-
-class ItemListItemListPlugin : public ItemListPlugin {
- GDCLASS(ItemListItemListPlugin, ItemListPlugin);
-
- ItemList *pp;
-
-public:
- virtual void set_object(Object *p_object) override;
- virtual bool handles(Object *p_object) const override;
- virtual int get_flags() const override;
-
- virtual void set_item_text(int p_idx, const String &p_text) override { pp->set_item_text(p_idx, p_text); }
- virtual String get_item_text(int p_idx) const override { return pp->get_item_text(p_idx); }
-
- virtual void set_item_icon(int p_idx, const Ref<Texture2D> &p_tex) override { pp->set_item_icon(p_idx, p_tex); }
- virtual Ref<Texture2D> get_item_icon(int p_idx) const override { return pp->get_item_icon(p_idx); }
-
- virtual void set_item_enabled(int p_idx, int p_enabled) override { pp->set_item_disabled(p_idx, !p_enabled); }
- virtual bool is_item_enabled(int p_idx) const override { return !pp->is_item_disabled(p_idx); }
-
- virtual void add_item() override;
- virtual int get_item_count() const override;
- virtual void erase(int p_idx) override;
-
- ItemListItemListPlugin();
-};
-
-///////////////////////////////////////////////////////////////
-
-class ItemListEditor : public HBoxContainer {
- GDCLASS(ItemListEditor, HBoxContainer);
-
- Node *item_list;
-
- Button *toolbar_button;
-
- AcceptDialog *dialog;
- EditorInspector *property_editor;
- Tree *tree;
- Button *add_button;
- Button *del_button;
-
- int selected_idx;
-
- Vector<ItemListPlugin *> item_plugins;
-
- void _edit_items();
-
- void _add_pressed();
- void _delete_pressed();
-
- void _node_removed(Node *p_node);
-
-protected:
- void _notification(int p_notification);
- static void _bind_methods();
-
-public:
- void edit(Node *p_item_list);
- bool handles(Object *p_object) const;
- void add_plugin(ItemListPlugin *p_plugin) { item_plugins.push_back(p_plugin); }
- ItemListEditor();
- ~ItemListEditor();
-};
-
-class ItemListEditorPlugin : public EditorPlugin {
- GDCLASS(ItemListEditorPlugin, EditorPlugin);
-
- ItemListEditor *item_list_editor;
- EditorNode *editor;
-
-public:
- virtual String get_name() const override { return "ItemList"; }
- 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;
-
- ItemListEditorPlugin(EditorNode *p_node);
- ~ItemListEditorPlugin();
-};
-
-#endif // ITEM_LIST_EDITOR_PLUGIN_H
diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp
index b4a70cd31d..d7b469cb74 100644
--- a/editor/plugins/lightmap_gi_editor_plugin.cpp
+++ b/editor/plugins/lightmap_gi_editor_plugin.cpp
@@ -43,11 +43,11 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) {
switch (err) {
case LightmapGI::BAKE_ERROR_NO_SAVE_PATH: {
- String scene_path = lightmap->get_filename();
- if (scene_path == String()) {
- scene_path = lightmap->get_owner()->get_filename();
+ String scene_path = lightmap->get_scene_file_path();
+ if (scene_path.is_empty()) {
+ scene_path = lightmap->get_owner()->get_scene_file_path();
}
- if (scene_path == String()) {
+ if (scene_path.is_empty()) {
EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for lightmap images.\nSave your scene and try again."));
break;
}
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index 94966d4fe6..bfbe6e26a9 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -32,6 +32,7 @@
#include "editor/editor_scale.h"
#include "scene/gui/subviewport_container.h"
+#include "scene/resources/fog_material.h"
#include "scene/resources/particles_material.h"
#include "scene/resources/sky_material.h"
@@ -68,8 +69,24 @@ void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_en
material = p_material;
camera->set_environment(p_env);
if (!material.is_null()) {
- sphere_instance->set_material_override(material);
- box_instance->set_material_override(material);
+ Shader::Mode mode = p_material->get_shader_mode();
+ switch (mode) {
+ case Shader::MODE_CANVAS_ITEM:
+ layout_3d->hide();
+ layout_2d->show();
+ vc->hide();
+ rect_instance->set_material(material);
+ break;
+ case Shader::MODE_SPATIAL:
+ layout_2d->hide();
+ layout_3d->show();
+ vc->show();
+ sphere_instance->set_material_override(material);
+ box_instance->set_material_override(material);
+ break;
+ default:
+ break;
+ }
} else {
hide();
}
@@ -105,6 +122,21 @@ void MaterialEditor::_bind_methods() {
}
MaterialEditor::MaterialEditor() {
+ // canvas item
+
+ layout_2d = memnew(HBoxContainer);
+ layout_2d->set_alignment(BoxContainer::ALIGNMENT_CENTER);
+ add_child(layout_2d);
+ layout_2d->set_anchors_and_offsets_preset(PRESET_WIDE);
+
+ rect_instance = memnew(ColorRect);
+ layout_2d->add_child(rect_instance);
+ rect_instance->set_custom_minimum_size(Size2(150, 150) * EDSCALE);
+
+ layout_2d->set_visible(false);
+
+ // spatial
+
vc = memnew(SubViewportContainer);
vc->set_stretch(true);
add_child(vc);
@@ -153,12 +185,12 @@ MaterialEditor::MaterialEditor() {
set_custom_minimum_size(Size2(1, 150) * EDSCALE);
- HBoxContainer *hb = memnew(HBoxContainer);
- add_child(hb);
- hb->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 2);
+ layout_3d = memnew(HBoxContainer);
+ add_child(layout_3d);
+ layout_3d->set_anchors_and_offsets_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 2);
VBoxContainer *vb_shape = memnew(VBoxContainer);
- hb->add_child(vb_shape);
+ layout_3d->add_child(vb_shape);
sphere_switch = memnew(TextureButton);
sphere_switch->set_toggle_mode(true);
@@ -172,10 +204,10 @@ MaterialEditor::MaterialEditor() {
vb_shape->add_child(box_switch);
box_switch->connect("pressed", callable_mp(this, &MaterialEditor::_button_pressed), varray(box_switch));
- hb->add_spacer();
+ layout_3d->add_spacer();
VBoxContainer *vb_light = memnew(VBoxContainer);
- hb->add_child(vb_light);
+ layout_3d->add_child(vb_light);
light_1_switch = memnew(TextureButton);
light_1_switch->set_toggle_mode(true);
@@ -206,8 +238,8 @@ bool EditorInspectorPluginMaterial::can_handle(Object *p_object) {
if (!material) {
return false;
}
-
- return material->get_shader_mode() == Shader::MODE_SPATIAL;
+ Shader::Mode mode = material->get_shader_mode();
+ return mode == Shader::MODE_SPATIAL || mode == Shader::MODE_CANVAS_ITEM;
}
void EditorInspectorPluginMaterial::parse_begin(Object *p_object) {
@@ -278,6 +310,54 @@ Ref<Resource> StandardMaterial3DConversionPlugin::convert(const Ref<Resource> &p
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
+ return smat;
+}
+
+String ORMMaterial3DConversionPlugin::converts_to() const {
+ return "ShaderMaterial";
+}
+
+bool ORMMaterial3DConversionPlugin::handles(const Ref<Resource> &p_resource) const {
+ Ref<ORMMaterial3D> mat = p_resource;
+ return mat.is_valid();
+}
+
+Ref<Resource> ORMMaterial3DConversionPlugin::convert(const Ref<Resource> &p_resource) const {
+ Ref<ORMMaterial3D> mat = p_resource;
+ ERR_FAIL_COND_V(!mat.is_valid(), Ref<Resource>());
+
+ Ref<ShaderMaterial> smat;
+ smat.instantiate();
+
+ Ref<Shader> shader;
+ shader.instantiate();
+
+ String code = RS::get_singleton()->shader_get_code(mat->get_shader_rid());
+
+ shader->set_code(code);
+
+ smat->set_shader(shader);
+
+ List<PropertyInfo> params;
+ RS::get_singleton()->shader_get_param_list(mat->get_shader_rid(), &params);
+
+ for (const PropertyInfo &E : params) {
+ // Texture parameter has to be treated specially since ORMMaterial3D saved it
+ // as RID but ShaderMaterial needs Texture itself
+ Ref<Texture2D> texture = mat->get_texture_by_name(E.name);
+ if (texture.is_valid()) {
+ smat->set_shader_param(E.name, texture);
+ } else {
+ Variant value = RS::get_singleton()->material_get_param(mat->get_rid(), E.name);
+ smat->set_shader_param(E.name, value);
+ }
+ }
+
+ smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -315,6 +395,8 @@ Ref<Resource> ParticlesMaterialConversionPlugin::convert(const Ref<Resource> &p_
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -352,6 +434,8 @@ Ref<Resource> CanvasItemMaterialConversionPlugin::convert(const Ref<Resource> &p
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -389,6 +473,8 @@ Ref<Resource> ProceduralSkyMaterialConversionPlugin::convert(const Ref<Resource>
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -426,6 +512,8 @@ Ref<Resource> PanoramaSkyMaterialConversionPlugin::convert(const Ref<Resource> &
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
return smat;
}
@@ -463,5 +551,44 @@ Ref<Resource> PhysicalSkyMaterialConversionPlugin::convert(const Ref<Resource> &
}
smat->set_render_priority(mat->get_render_priority());
+ smat->set_local_to_scene(mat->is_local_to_scene());
+ smat->set_name(mat->get_name());
+ return smat;
+}
+
+String FogMaterialConversionPlugin::converts_to() const {
+ return "ShaderMaterial";
+}
+
+bool FogMaterialConversionPlugin::handles(const Ref<Resource> &p_resource) const {
+ Ref<FogMaterial> mat = p_resource;
+ return mat.is_valid();
+}
+
+Ref<Resource> FogMaterialConversionPlugin::convert(const Ref<Resource> &p_resource) const {
+ Ref<FogMaterial> mat = p_resource;
+ ERR_FAIL_COND_V(!mat.is_valid(), Ref<Resource>());
+
+ Ref<ShaderMaterial> smat;
+ smat.instantiate();
+
+ Ref<Shader> shader;
+ shader.instantiate();
+
+ String code = RS::get_singleton()->shader_get_code(mat->get_shader_rid());
+
+ shader->set_code(code);
+
+ smat->set_shader(shader);
+
+ List<PropertyInfo> params;
+ RS::get_singleton()->shader_get_param_list(mat->get_shader_rid(), &params);
+
+ for (const PropertyInfo &E : params) {
+ Variant value = RS::get_singleton()->material_get_param(mat->get_rid(), E.name);
+ smat->set_shader_param(E.name, value);
+ }
+
+ smat->set_render_priority(mat->get_render_priority());
return smat;
}
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
index a4532b58b3..8262b8149d 100644
--- a/editor/plugins/material_editor_plugin.h
+++ b/editor/plugins/material_editor_plugin.h
@@ -39,6 +39,7 @@
#include "scene/3d/camera_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/mesh_instance_3d.h"
+#include "scene/gui/color_rect.h"
#include "scene/resources/material.h"
class SubViewportContainer;
@@ -46,22 +47,27 @@ class SubViewportContainer;
class MaterialEditor : public Control {
GDCLASS(MaterialEditor, Control);
- SubViewportContainer *vc;
- SubViewport *viewport;
- MeshInstance3D *sphere_instance;
- MeshInstance3D *box_instance;
- DirectionalLight3D *light1;
- DirectionalLight3D *light2;
- Camera3D *camera;
+ HBoxContainer *layout_2d = nullptr;
+ ColorRect *rect_instance = nullptr;
+
+ SubViewportContainer *vc = nullptr;
+ SubViewport *viewport = nullptr;
+ MeshInstance3D *sphere_instance = nullptr;
+ MeshInstance3D *box_instance = nullptr;
+ DirectionalLight3D *light1 = nullptr;
+ DirectionalLight3D *light2 = nullptr;
+ Camera3D *camera = nullptr;
Ref<SphereMesh> sphere_mesh;
Ref<BoxMesh> box_mesh;
- TextureButton *sphere_switch;
- TextureButton *box_switch;
+ HBoxContainer *layout_3d = nullptr;
+
+ TextureButton *sphere_switch = nullptr;
+ TextureButton *box_switch = nullptr;
- TextureButton *light_1_switch;
- TextureButton *light_2_switch;
+ TextureButton *light_1_switch = nullptr;
+ TextureButton *light_2_switch = nullptr;
Ref<Material> material;
@@ -107,6 +113,15 @@ public:
virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const override;
};
+class ORMMaterial3DConversionPlugin : public EditorResourceConversionPlugin {
+ GDCLASS(ORMMaterial3DConversionPlugin, EditorResourceConversionPlugin);
+
+public:
+ virtual String converts_to() const override;
+ virtual bool handles(const Ref<Resource> &p_resource) const override;
+ virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const override;
+};
+
class ParticlesMaterialConversionPlugin : public EditorResourceConversionPlugin {
GDCLASS(ParticlesMaterialConversionPlugin, EditorResourceConversionPlugin);
@@ -152,4 +167,13 @@ public:
virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const override;
};
+class FogMaterialConversionPlugin : public EditorResourceConversionPlugin {
+ GDCLASS(FogMaterialConversionPlugin, EditorResourceConversionPlugin);
+
+public:
+ virtual String converts_to() const override;
+ virtual bool handles(const Ref<Resource> &p_resource) const override;
+ virtual Ref<Resource> convert(const Ref<Resource> &p_resource) const override;
+};
+
#endif // MATERIAL_EDITOR_PLUGIN_H
diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp
index 768f29e15a..4b18ac6e9f 100644
--- a/editor/plugins/mesh_editor_plugin.cpp
+++ b/editor/plugins/mesh_editor_plugin.cpp
@@ -36,7 +36,7 @@ void MeshEditor::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
rot_x -= mm->get_relative().y * 0.01;
rot_y -= mm->get_relative().x * 0.01;
if (rot_x < -Math_PI / 2) {
@@ -80,7 +80,7 @@ void MeshEditor::edit(Ref<Mesh> p_mesh) {
_update_rotation();
AABB aabb = mesh->get_aabb();
- Vector3 ofs = aabb.position + aabb.size * 0.5;
+ Vector3 ofs = aabb.get_center();
float m = aabb.get_longest_axis_size();
if (m != 0) {
m = 1.0 / m;
diff --git a/editor/plugins/mesh_instance_3d_editor_plugin.cpp b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
index 9a2b222f21..2ce13b717a 100644
--- a/editor/plugins/mesh_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/mesh_instance_3d_editor_plugin.cpp
@@ -79,7 +79,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
Node *owner = node == get_tree()->get_edited_scene_root() ? node : node->get_owner();
ur->create_action(TTR("Create Static Trimesh Body"));
- ur->add_do_method(node, "add_child", body);
+ ur->add_do_method(node, "add_child", body, true);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_reference(body);
@@ -113,7 +113,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
Node *owner = instance == get_tree()->get_edited_scene_root() ? instance : instance->get_owner();
- ur->add_do_method(instance, "add_child", body);
+ ur->add_do_method(instance, "add_child", body, true);
ur->add_do_method(body, "set_owner", owner);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_reference(body);
@@ -146,7 +146,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
ur->create_action(TTR("Create Trimesh Static Shape"));
- ur->add_do_method(node->get_parent(), "add_child", cshape);
+ ur->add_do_method(node->get_parent(), "add_child", cshape, true);
ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_reference(cshape);
@@ -185,7 +185,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
Node *owner = node->get_owner();
- ur->add_do_method(node->get_parent(), "add_child", cshape);
+ ur->add_do_method(node->get_parent(), "add_child", cshape, true);
ur->add_do_method(node->get_parent(), "move_child", cshape, node->get_index() + 1);
ur->add_do_method(cshape, "set_owner", owner);
ur->add_do_reference(cshape);
@@ -202,7 +202,8 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
return;
}
- Vector<Ref<Shape3D>> shapes = mesh->convex_decompose();
+ Mesh::ConvexDecompositionSettings settings;
+ Vector<Ref<Shape3D>> shapes = mesh->convex_decompose(settings);
if (!shapes.size()) {
err_dialog->set_text(TTR("Couldn't create any collision shapes."));
@@ -246,7 +247,7 @@ void MeshInstance3DEditor::_menu_option(int p_option) {
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Create Navigation Mesh"));
- ur->add_do_method(node, "add_child", nmi);
+ ur->add_do_method(node, "add_child", nmi, true);
ur->add_do_method(nmi, "set_owner", owner);
ur->add_do_reference(nmi);
@@ -397,7 +398,7 @@ void MeshInstance3DEditor::_create_outline_mesh() {
}
if (mesh->get_surface_count() == 0) {
- err_dialog->set_text(TTR("Mesh has not surface to create outlines from."));
+ err_dialog->set_text(TTR("Mesh has no surface to create outlines from."));
err_dialog->popup_centered();
return;
} else if (mesh->get_surface_count() == 1 && mesh->surface_get_primitive_type(0) != Mesh::PRIMITIVE_TRIANGLES) {
@@ -425,7 +426,7 @@ void MeshInstance3DEditor::_create_outline_mesh() {
ur->create_action(TTR("Create Outline"));
- ur->add_do_method(node, "add_child", mi);
+ ur->add_do_method(node, "add_child", mi, true);
ur->add_do_method(mi, "set_owner", owner);
ur->add_do_reference(mi);
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index b3f92c9d95..fd62f632dc 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -47,23 +47,25 @@ void MeshLibraryEditor::edit(const Ref<MeshLibrary> &p_mesh_library) {
}
}
-void MeshLibraryEditor::_menu_confirm() {
+void MeshLibraryEditor::_menu_remove_confirm() {
switch (option) {
case MENU_OPTION_REMOVE_ITEM: {
mesh_library->remove_item(to_erase);
} break;
- case MENU_OPTION_UPDATE_FROM_SCENE: {
- String existing = mesh_library->get_meta("_editor_source_scene");
- ERR_FAIL_COND(existing == "");
- _import_scene_cbk(existing);
-
- } break;
default: {
};
}
}
-void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge) {
+void MeshLibraryEditor::_menu_update_confirm(bool p_apply_xforms) {
+ cd_update->hide();
+ apply_xforms = p_apply_xforms;
+ String existing = mesh_library->get_meta("_editor_source_scene");
+ ERR_FAIL_COND(existing.is_empty());
+ _import_scene_cbk(existing);
+}
+
+void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms) {
if (!p_merge) {
p_library->clear();
}
@@ -108,6 +110,13 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library,
}
p_library->set_item_mesh(id, mesh);
+
+ if (p_apply_xforms) {
+ p_library->set_item_mesh_transform(id, mi->get_transform());
+ } else {
+ p_library->set_item_mesh_transform(id, Transform3D());
+ }
+
mesh_instances[id] = mi;
Vector<MeshLibrary::ShapeData> collisions;
@@ -197,15 +206,16 @@ void MeshLibraryEditor::_import_scene_cbk(const String &p_str) {
ERR_FAIL_COND_MSG(!scene, "Cannot create an instance from PackedScene '" + p_str + "'.");
- _import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE);
+ _import_scene(scene, mesh_library, option == MENU_OPTION_UPDATE_FROM_SCENE, apply_xforms);
memdelete(scene);
mesh_library->set_meta("_editor_source_scene", p_str);
+
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), false);
}
-Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge) {
- _import_scene(p_base_scene, ml, p_merge);
+Error MeshLibraryEditor::update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge, bool p_apply_xforms) {
+ _import_scene(p_base_scene, ml, p_merge, p_apply_xforms);
return OK;
}
@@ -219,16 +229,21 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
String p = editor->get_inspector()->get_selected_path();
if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) {
to_erase = p.get_slice("/", 3).to_int();
- cd->set_text(vformat(TTR("Remove item %d?"), to_erase));
- cd->popup_centered(Size2(300, 60));
+ cd_remove->set_text(vformat(TTR("Remove item %d?"), to_erase));
+ cd_remove->popup_centered(Size2(300, 60));
}
} break;
case MENU_OPTION_IMPORT_FROM_SCENE: {
+ apply_xforms = false;
+ file->popup_file_dialog();
+ } break;
+ case MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS: {
+ apply_xforms = true;
file->popup_file_dialog();
} break;
case MENU_OPTION_UPDATE_FROM_SCENE: {
- cd->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));
- cd->popup_centered(Size2(500, 60));
+ cd_update->set_text(vformat(TTR("Update from existing scene?:\n%s"), String(mesh_library->get_meta("_editor_source_scene"))));
+ cd_update->popup_centered(Size2(500, 60));
} break;
}
}
@@ -258,16 +273,22 @@ MeshLibraryEditor::MeshLibraryEditor(EditorNode *p_editor) {
menu->get_popup()->add_item(TTR("Add Item"), MENU_OPTION_ADD_ITEM);
menu->get_popup()->add_item(TTR("Remove Selected Item"), MENU_OPTION_REMOVE_ITEM);
menu->get_popup()->add_separator();
- menu->get_popup()->add_item(TTR("Import from Scene"), MENU_OPTION_IMPORT_FROM_SCENE);
+ menu->get_popup()->add_item(TTR("Import from Scene (Ignore Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE);
+ menu->get_popup()->add_item(TTR("Import from Scene (Apply Transforms)"), MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS);
menu->get_popup()->add_item(TTR("Update from Scene"), MENU_OPTION_UPDATE_FROM_SCENE);
menu->get_popup()->set_item_disabled(menu->get_popup()->get_item_index(MENU_OPTION_UPDATE_FROM_SCENE), true);
menu->get_popup()->connect("id_pressed", callable_mp(this, &MeshLibraryEditor::_menu_cbk));
menu->hide();
editor = p_editor;
- cd = memnew(ConfirmationDialog);
- add_child(cd);
- cd->get_ok_button()->connect("pressed", callable_mp(this, &MeshLibraryEditor::_menu_confirm));
+ cd_remove = memnew(ConfirmationDialog);
+ add_child(cd_remove);
+ cd_remove->get_ok_button()->connect("pressed", callable_mp(this, &MeshLibraryEditor::_menu_remove_confirm));
+ cd_update = memnew(ConfirmationDialog);
+ add_child(cd_update);
+ cd_update->get_ok_button()->set_text("Apply without Transforms");
+ cd_update->get_ok_button()->connect("pressed", callable_mp(this, &MeshLibraryEditor::_menu_update_confirm), varray(false));
+ cd_update->add_button("Apply with Transforms")->connect("pressed", callable_mp(this, &MeshLibraryEditor::_menu_update_confirm), varray(true));
}
void MeshLibraryEditorPlugin::edit(Object *p_node) {
diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h
index 6c33c8bb9e..9e225ffb9b 100644
--- a/editor/plugins/mesh_library_editor_plugin.h
+++ b/editor/plugins/mesh_library_editor_plugin.h
@@ -41,23 +41,27 @@ class MeshLibraryEditor : public Control {
EditorNode *editor;
MenuButton *menu;
- ConfirmationDialog *cd;
+ ConfirmationDialog *cd_remove;
+ ConfirmationDialog *cd_update;
EditorFileDialog *file;
+ bool apply_xforms;
int to_erase;
enum {
MENU_OPTION_ADD_ITEM,
MENU_OPTION_REMOVE_ITEM,
MENU_OPTION_UPDATE_FROM_SCENE,
- MENU_OPTION_IMPORT_FROM_SCENE
+ MENU_OPTION_IMPORT_FROM_SCENE,
+ MENU_OPTION_IMPORT_FROM_SCENE_APPLY_XFORMS
};
int option;
void _import_scene_cbk(const String &p_str);
void _menu_cbk(int p_option);
- void _menu_confirm();
+ void _menu_remove_confirm();
+ void _menu_update_confirm(bool p_apply_xforms);
- static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge);
+ static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms);
protected:
static void _bind_methods();
@@ -66,7 +70,7 @@ public:
MenuButton *get_menu_button() const { return menu; }
void edit(const Ref<MeshLibrary> &p_mesh_library);
- static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true);
+ static Error update_library_file(Node *p_base_scene, Ref<MeshLibrary> ml, bool p_merge = true, bool p_apply_xforms = false);
MeshLibraryEditor(EditorNode *p_editor);
};
diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp
index 5514bccabb..517aca596a 100644
--- a/editor/plugins/multimesh_editor_plugin.cpp
+++ b/editor/plugins/multimesh_editor_plugin.cpp
@@ -48,7 +48,7 @@ void MultiMeshEditor::_populate() {
Ref<Mesh> mesh;
- if (mesh_source->get_text() == "") {
+ if (mesh_source->get_text().is_empty()) {
Ref<MultiMesh> multimesh;
multimesh = node->get_multimesh();
if (multimesh.is_null()) {
@@ -89,7 +89,7 @@ void MultiMeshEditor::_populate() {
}
}
- if (surface_source->get_text() == "") {
+ if (surface_source->get_text().is_empty()) {
err_dialog->set_text(TTR("No surface source specified."));
err_dialog->popup_centered();
return;
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index d04e88e915..32355735cc 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -34,26 +34,27 @@
#include "core/math/geometry_2d.h"
#include "core/math/geometry_3d.h"
#include "editor/plugins/node_3d_editor_plugin.h"
+#include "scene/3d/audio_listener_3d.h"
#include "scene/3d/audio_stream_player_3d.h"
#include "scene/3d/camera_3d.h"
#include "scene/3d/collision_polygon_3d.h"
#include "scene/3d/collision_shape_3d.h"
#include "scene/3d/cpu_particles_3d.h"
#include "scene/3d/decal.h"
+#include "scene/3d/fog_volume.h"
#include "scene/3d/gpu_particles_3d.h"
#include "scene/3d/gpu_particles_collision_3d.h"
+#include "scene/3d/joint_3d.h"
#include "scene/3d/light_3d.h"
#include "scene/3d/lightmap_gi.h"
#include "scene/3d/lightmap_probe.h"
-#include "scene/3d/listener_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/navigation_region_3d.h"
#include "scene/3d/occluder_instance_3d.h"
-#include "scene/3d/physics_joint_3d.h"
#include "scene/3d/position_3d.h"
#include "scene/3d/ray_cast_3d.h"
#include "scene/3d/reflection_probe.h"
-#include "scene/3d/soft_body_3d.h"
+#include "scene/3d/soft_dynamic_body_3d.h"
#include "scene/3d/spring_arm_3d.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body_3d.h"
@@ -69,7 +70,7 @@
#include "scene/resources/separation_ray_shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
#include "scene/resources/surface_tool.h"
-#include "scene/resources/world_margin_shape_3d.h"
+#include "scene/resources/world_boundary_shape_3d.h"
#define HANDLE_HALF_SIZE 9.5
@@ -243,8 +244,10 @@ void EditorNode3DGizmo::Instance::create_instance(Node3D *p_base, bool p_hidden)
RS::get_singleton()->instance_geometry_set_flag(instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
}
-void EditorNode3DGizmo::add_mesh(const Ref<ArrayMesh> &p_mesh, const Ref<Material> &p_material, const Transform3D &p_xform, const Ref<SkinReference> &p_skin_reference) {
+void EditorNode3DGizmo::add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p_material, const Transform3D &p_xform, const Ref<SkinReference> &p_skin_reference) {
ERR_FAIL_COND(!spatial_node);
+ ERR_FAIL_COND_MSG(!p_mesh.is_valid(), "EditorNode3DGizmo.add_mesh() requires a valid Mesh resource.");
+
Instance ins;
ins.mesh = p_mesh;
@@ -682,7 +685,7 @@ bool EditorNode3DGizmo::intersect_ray(Camera3D *p_camera, const Point2 &p_point,
}
if (collision_segments.size()) {
- Plane camp(p_camera->get_transform().origin, (-p_camera->get_transform().basis.get_axis(2)).normalized());
+ Plane camp(-p_camera->get_transform().basis.get_axis(2).normalized(), p_camera->get_transform().origin);
int vc = collision_segments.size();
const Vector3 *vptr = collision_segments.ptr();
@@ -832,7 +835,7 @@ void EditorNode3DGizmo::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_plugin"), &EditorNode3DGizmo::get_plugin);
ClassDB::bind_method(D_METHOD("clear"), &EditorNode3DGizmo::clear);
ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorNode3DGizmo::set_hidden);
- ClassDB::bind_method(D_METHOD("is_subgizmo_selected"), &EditorNode3DGizmo::is_subgizmo_selected);
+ ClassDB::bind_method(D_METHOD("is_subgizmo_selected", "id"), &EditorNode3DGizmo::is_subgizmo_selected);
ClassDB::bind_method(D_METHOD("get_subgizmo_selection"), &EditorNode3DGizmo::get_subgizmo_selection);
GDVIRTUAL_BIND(_redraw);
@@ -1003,7 +1006,9 @@ String EditorNode3DGizmoPlugin::get_gizmo_name() const {
if (get_script_instance() && get_script_instance()->has_method("_get_gizmo_name")) {
return get_script_instance()->call("_get_gizmo_name");
}
- return TTR("Nameless gizmo");
+
+ WARN_PRINT_ONCE("A 3D editor gizmo has no name defined (it will appear as \"Unnamed Gizmo\" in the \"View > Gizmos\" menu). To resolve this, override the `_get_gizmo_name()` function to return a String in the script that extends EditorNode3DGizmoPlugin.");
+ return TTR("Unnamed Gizmo");
}
int EditorNode3DGizmoPlugin::get_priority() const {
@@ -1311,7 +1316,7 @@ void Light3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id,
light->set_param(Light3D::PARAM_RANGE, d);
} else if (Object::cast_to<OmniLight3D>(light)) {
- Plane cp = Plane(gt.origin, p_camera->get_transform().basis.get_axis(2));
+ Plane cp = Plane(p_camera->get_transform().basis.get_axis(2), gt.origin);
Vector3 inters;
if (cp.intersects_ray(ray_from, ray_dir, &inters)) {
@@ -1481,8 +1486,6 @@ void Light3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
}
-//////
-
//// player gizmo
AudioStreamPlayer3DGizmoPlugin::AudioStreamPlayer3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/stream_player_3d", Color(0.4, 0.8, 1));
@@ -1621,6 +1624,29 @@ void AudioStreamPlayer3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
//////
+AudioListener3DGizmoPlugin::AudioListener3DGizmoPlugin() {
+ create_icon_material("audio_listener_3d_icon", Node3DEditor::get_singleton()->get_theme_icon("GizmoAudioListener3D", "EditorIcons"));
+}
+
+bool AudioListener3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<AudioListener3D>(p_spatial) != nullptr;
+}
+
+String AudioListener3DGizmoPlugin::get_gizmo_name() const {
+ return "AudioListener3D";
+}
+
+int AudioListener3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+void AudioListener3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ const Ref<Material> icon = get_material("audio_listener_3d_icon", p_gizmo);
+ p_gizmo->add_unscaled_billboard(icon, 0.05);
+}
+
+//////
+
Camera3DGizmoPlugin::Camera3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/camera", Color(0.8, 0.4, 0.8));
@@ -1817,47 +1843,6 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_lines(lines, material);
p_gizmo->add_handles(handles, get_material("handles"));
-
- ClippedCamera3D *clipcam = Object::cast_to<ClippedCamera3D>(camera);
- if (clipcam) {
- Node3D *parent = Object::cast_to<Node3D>(camera->get_parent());
- if (!parent) {
- return;
- }
- Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized();
- Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized();
- Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized();
- Vector3 cam_pos = camera->get_global_transform().origin;
- Vector3 parent_pos = parent->get_global_transform().origin;
-
- Plane parent_plane(parent_pos, cam_normal);
- Vector3 ray_from = parent_plane.project(cam_pos);
-
- lines.clear();
- lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5);
- lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5);
-
- lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5);
- lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5);
-
- lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5);
- lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5);
-
- lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5);
- lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5);
-
- if (parent_plane.distance_to(cam_pos) < 0) {
- lines.push_back(ray_from);
- lines.push_back(cam_pos);
- }
-
- Transform3D local = camera->get_global_transform().affine_inverse();
- for (int i = 0; i < lines.size(); i++) {
- lines.write[i] = local.xform(lines[i]);
- }
-
- p_gizmo->add_lines(lines, material);
- }
}
//////
@@ -1866,7 +1851,7 @@ MeshInstance3DGizmoPlugin::MeshInstance3DGizmoPlugin() {
}
bool MeshInstance3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- return Object::cast_to<MeshInstance3D>(p_spatial) != nullptr && Object::cast_to<SoftBody3D>(p_spatial) == nullptr;
+ return Object::cast_to<MeshInstance3D>(p_spatial) != nullptr && Object::cast_to<SoftDynamicBody3D>(p_spatial) == nullptr;
}
String MeshInstance3DGizmoPlugin::get_gizmo_name() const {
@@ -2047,171 +2032,6 @@ void Position3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_segments(cursor_points);
}
-/////
-
-Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {
- Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
- create_material("skeleton_material", gizmo_color);
-}
-
-bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- return Object::cast_to<Skeleton3D>(p_spatial) != nullptr;
-}
-
-String Skeleton3DGizmoPlugin::get_gizmo_name() const {
- return "Skeleton3D";
-}
-
-int Skeleton3DGizmoPlugin::get_priority() const {
- return -1;
-}
-
-void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
- Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
-
- p_gizmo->clear();
-
- Ref<Material> material = get_material("skeleton_material", p_gizmo);
-
- Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));
-
- surface_tool->begin(Mesh::PRIMITIVE_LINES);
- surface_tool->set_material(material);
- LocalVector<Transform3D> grests;
- grests.resize(skel->get_bone_count());
-
- LocalVector<int> bones;
- LocalVector<float> weights;
- bones.resize(4);
- weights.resize(4);
-
- for (int i = 0; i < 4; i++) {
- bones[i] = 0;
- weights[i] = 0;
- }
-
- weights[0] = 1;
-
- AABB aabb;
-
- Color bonecolor = Color(1.0, 0.4, 0.4, 0.3);
- Color rootcolor = Color(0.4, 1.0, 0.4, 0.1);
-
- //LocalVector<int> bones_to_process = skel->get_parentless_bones();
- LocalVector<int> bones_to_process;
- bones_to_process = skel->get_parentless_bones();
-
- while (bones_to_process.size() > 0) {
- int current_bone_idx = bones_to_process[0];
- bones_to_process.erase(current_bone_idx);
-
- LocalVector<int> child_bones_vector;
- child_bones_vector = skel->get_bone_children(current_bone_idx);
- int child_bones_size = child_bones_vector.size();
-
- // You have children but no parent, then you must be a root/parentless bone.
- if (child_bones_size >= 0 && skel->get_bone_parent(current_bone_idx) <= 0) {
- grests[current_bone_idx] = skel->global_pose_to_local_pose(current_bone_idx, skel->get_bone_global_pose(current_bone_idx));
- }
-
- for (int i = 0; i < child_bones_size; i++) {
- int child_bone_idx = child_bones_vector[i];
-
- grests[child_bone_idx] = skel->global_pose_to_local_pose(child_bone_idx, skel->get_bone_global_pose(child_bone_idx));
- Vector3 v0 = grests[current_bone_idx].origin;
- Vector3 v1 = grests[child_bone_idx].origin;
- Vector3 d = skel->get_bone_rest(child_bone_idx).origin.normalized();
- real_t dist = skel->get_bone_rest(child_bone_idx).origin.length();
-
- // Find closest axis.
- int closest = -1;
- real_t closest_d = 0.0;
- for (int j = 0; j < 3; j++) {
- real_t dp = Math::abs(grests[current_bone_idx].basis[j].normalized().dot(d));
- if (j == 0 || dp > closest_d) {
- closest = j;
- }
- }
-
- // Find closest other.
- Vector3 first;
- Vector3 points[4];
- int point_idx = 0;
- for (int j = 0; j < 3; j++) {
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(rootcolor);
- surface_tool->add_vertex(v0 - grests[current_bone_idx].basis[j].normalized() * dist * 0.05);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(rootcolor);
- surface_tool->add_vertex(v0 + grests[current_bone_idx].basis[j].normalized() * dist * 0.05);
-
- if (j == closest) {
- continue;
- }
-
- Vector3 axis;
- if (first == Vector3()) {
- axis = d.cross(d.cross(grests[current_bone_idx].basis[j])).normalized();
- first = axis;
- } else {
- axis = d.cross(first).normalized();
- }
-
- for (int k = 0; k < 2; k++) {
- if (k == 1) {
- axis = -axis;
- }
- Vector3 point = v0 + d * dist * 0.2;
- point += axis * dist * 0.1;
-
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(v0);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(point);
-
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(point);
- bones[0] = child_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(v1);
- points[point_idx++] = point;
- }
- }
- SWAP(points[1], points[2]);
- for (int j = 0; j < 4; j++) {
- bones[0] = current_bone_idx;
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(points[j]);
- surface_tool->set_bones(bones);
- surface_tool->set_weights(weights);
- surface_tool->set_color(bonecolor);
- surface_tool->add_vertex(points[(j + 1) % 4]);
- }
-
- // Add the bone's children to the list of bones to be processed.
- bones_to_process.push_back(child_bones_vector[i]);
- }
- }
-
- Ref<ArrayMesh> m = surface_tool->commit();
- p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skel->register_skin(Ref<Skin>()));
-}
-
////
PhysicalBone3DGizmoPlugin::PhysicalBone3DGizmoPlugin() {
@@ -2491,30 +2311,30 @@ void VehicleWheel3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
///////////
-SoftBody3DGizmoPlugin::SoftBody3DGizmoPlugin() {
+SoftDynamicBody3DGizmoPlugin::SoftDynamicBody3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
create_material("shape_material", gizmo_color);
create_handle_material("handles");
}
-bool SoftBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
- return Object::cast_to<SoftBody3D>(p_spatial) != nullptr;
+bool SoftDynamicBody3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<SoftDynamicBody3D>(p_spatial) != nullptr;
}
-String SoftBody3DGizmoPlugin::get_gizmo_name() const {
- return "SoftBody3D";
+String SoftDynamicBody3DGizmoPlugin::get_gizmo_name() const {
+ return "SoftDynamicBody3D";
}
-int SoftBody3DGizmoPlugin::get_priority() const {
+int SoftDynamicBody3DGizmoPlugin::get_priority() const {
return -1;
}
-bool SoftBody3DGizmoPlugin::is_selectable_when_hidden() const {
+bool SoftDynamicBody3DGizmoPlugin::is_selectable_when_hidden() const {
return true;
}
-void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
- SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node());
+void SoftDynamicBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node());
p_gizmo->clear();
@@ -2550,22 +2370,22 @@ void SoftBody3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_triangles(tm);
}
-String SoftBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- return "SoftBody3D pin point";
+String SoftDynamicBody3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ return "SoftDynamicBody3D pin point";
}
-Variant SoftBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node());
+Variant SoftDynamicBody3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node());
return Variant(soft_body->is_point_pinned(p_id));
}
-void SoftBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) {
- SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node());
+void SoftDynamicBody3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) {
+ SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node());
soft_body->pin_point_toggle(p_id);
}
-bool SoftBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const {
- SoftBody3D *soft_body = Object::cast_to<SoftBody3D>(p_gizmo->get_spatial_node());
+bool SoftDynamicBody3DGizmoPlugin::is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ SoftDynamicBody3D *soft_body = Object::cast_to<SoftDynamicBody3D>(p_gizmo->get_spatial_node());
return soft_body->is_point_pinned(p_id);
}
@@ -2631,7 +2451,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p
Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
- Vector3 ofs = aabb.position + aabb.size * 0.5;
+ Vector3 ofs = aabb.get_center();
Vector3 axis;
axis[p_id] = 1.0;
@@ -2707,7 +2527,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
handles.push_back(ax);
}
- Vector3 center = aabb.position + aabb.size * 0.5;
+ Vector3 center = aabb.get_center();
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = 1.0;
@@ -2723,7 +2543,7 @@ void VisibleOnScreenNotifier3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (p_gizmo->is_selected()) {
Ref<Material> solid_material = get_material("visibility_notifier_solid_material", p_gizmo);
- p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0);
+ p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
}
p_gizmo->add_handles(handles, get_material("handles"));
@@ -2761,7 +2581,7 @@ void CPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
GPUParticles3DGizmoPlugin::GPUParticles3DGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/particles", Color(0.8, 0.7, 0.4));
create_material("particles_material", gizmo_color);
- gizmo_color.a = 0.1;
+ gizmo_color.a = MAX((gizmo_color.a - 0.2) * 0.02, 0.0);
create_material("particles_solid_material", gizmo_color);
create_icon_material("particles_icon", Node3DEditor::get_singleton()->get_theme_icon(SNAME("GizmoGPUParticles3D"), SNAME("EditorIcons")));
create_handle_material("handles");
@@ -2822,7 +2642,7 @@ void GPUParticles3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int
Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
- Vector3 ofs = aabb.position + aabb.size * 0.5;
+ Vector3 ofs = aabb.get_center();
Vector3 axis;
axis[p_id] = 1.0;
@@ -2898,7 +2718,7 @@ void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
handles.push_back(ax);
}
- Vector3 center = aabb.position + aabb.size * 0.5;
+ Vector3 center = aabb.get_center();
for (int i = 0; i < 3; i++) {
Vector3 ax;
ax[i] = 1.0;
@@ -2914,7 +2734,7 @@ void GPUParticles3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
if (p_gizmo->is_selected()) {
Ref<Material> solid_material = get_material("particles_solid_material", p_gizmo);
- p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_position() + aabb.get_size() / 2.0);
+ p_gizmo->add_solid_box(solid_material, aabb.get_size(), aabb.get_center());
}
p_gizmo->add_handles(handles, get_material("handles"));
@@ -2947,11 +2767,11 @@ int GPUParticlesCollision3DGizmoPlugin::get_priority() const {
String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {
const Node3D *cs = p_gizmo->get_spatial_node();
- if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
return "Radius";
}
- if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
return "Extents";
}
@@ -2961,11 +2781,11 @@ String GPUParticlesCollision3DGizmoPlugin::get_handle_name(const EditorNode3DGiz
Variant GPUParticlesCollision3DGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {
const Node3D *cs = p_gizmo->get_spatial_node();
- if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
return p_gizmo->get_spatial_node()->call("get_radius");
}
- if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
return Vector3(p_gizmo->get_spatial_node()->call("get_extents"));
}
@@ -2983,7 +2803,7 @@ void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_g
Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
- if (Object::cast_to<GPUParticlesCollisionSphere>(sn) || Object::cast_to<GPUParticlesAttractorSphere>(sn)) {
+ if (Object::cast_to<GPUParticlesCollisionSphere3D>(sn) || Object::cast_to<GPUParticlesAttractorSphere3D>(sn)) {
Vector3 ra, rb;
Geometry3D::get_closest_points_between_segments(Vector3(), Vector3(4096, 0, 0), sg[0], sg[1], ra, rb);
float d = ra.x;
@@ -2998,7 +2818,7 @@ void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_g
sn->call("set_radius", d);
}
- if (Object::cast_to<GPUParticlesCollisionBox>(sn) || Object::cast_to<GPUParticlesAttractorBox>(sn) || Object::cast_to<GPUParticlesAttractorVectorField>(sn) || Object::cast_to<GPUParticlesCollisionSDF>(sn) || Object::cast_to<GPUParticlesCollisionHeightField>(sn)) {
+ if (Object::cast_to<GPUParticlesCollisionBox3D>(sn) || Object::cast_to<GPUParticlesAttractorBox3D>(sn) || Object::cast_to<GPUParticlesAttractorVectorField3D>(sn) || Object::cast_to<GPUParticlesCollisionSDF3D>(sn) || Object::cast_to<GPUParticlesCollisionHeightField3D>(sn)) {
Vector3 axis;
axis[p_id] = 1.0;
Vector3 ra, rb;
@@ -3021,7 +2841,7 @@ void GPUParticlesCollision3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_g
void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) {
Node3D *sn = p_gizmo->get_spatial_node();
- if (Object::cast_to<GPUParticlesCollisionSphere>(sn) || Object::cast_to<GPUParticlesAttractorSphere>(sn)) {
+ if (Object::cast_to<GPUParticlesCollisionSphere3D>(sn) || Object::cast_to<GPUParticlesAttractorSphere3D>(sn)) {
if (p_cancel) {
sn->call("set_radius", p_restore);
return;
@@ -3034,7 +2854,7 @@ void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *
ur->commit_action();
}
- if (Object::cast_to<GPUParticlesCollisionBox>(sn) || Object::cast_to<GPUParticlesAttractorBox>(sn) || Object::cast_to<GPUParticlesAttractorVectorField>(sn) || Object::cast_to<GPUParticlesCollisionSDF>(sn) || Object::cast_to<GPUParticlesCollisionHeightField>(sn)) {
+ if (Object::cast_to<GPUParticlesCollisionBox3D>(sn) || Object::cast_to<GPUParticlesAttractorBox3D>(sn) || Object::cast_to<GPUParticlesAttractorVectorField3D>(sn) || Object::cast_to<GPUParticlesCollisionSDF3D>(sn) || Object::cast_to<GPUParticlesCollisionHeightField3D>(sn)) {
if (p_cancel) {
sn->call("set_extents", p_restore);
return;
@@ -3051,7 +2871,6 @@ void GPUParticlesCollision3DGizmoPlugin::commit_handle(const EditorNode3DGizmo *
void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Node3D *cs = p_gizmo->get_spatial_node();
- print_line("redraw request " + itos(cs != nullptr));
p_gizmo->clear();
const Ref<Material> material =
@@ -3061,7 +2880,7 @@ void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
Ref<Material> handles_material = get_material("handles");
- if (Object::cast_to<GPUParticlesCollisionSphere>(cs) || Object::cast_to<GPUParticlesAttractorSphere>(cs)) {
+ if (Object::cast_to<GPUParticlesCollisionSphere3D>(cs) || Object::cast_to<GPUParticlesAttractorSphere3D>(cs)) {
float r = cs->call("get_radius");
Vector<Vector3> points;
@@ -3103,7 +2922,7 @@ void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_handles(handles, handles_material);
}
- if (Object::cast_to<GPUParticlesCollisionBox>(cs) || Object::cast_to<GPUParticlesAttractorBox>(cs) || Object::cast_to<GPUParticlesAttractorVectorField>(cs) || Object::cast_to<GPUParticlesCollisionSDF>(cs) || Object::cast_to<GPUParticlesCollisionHeightField>(cs)) {
+ if (Object::cast_to<GPUParticlesCollisionBox3D>(cs) || Object::cast_to<GPUParticlesAttractorBox3D>(cs) || Object::cast_to<GPUParticlesAttractorVectorField3D>(cs) || Object::cast_to<GPUParticlesCollisionSDF3D>(cs) || Object::cast_to<GPUParticlesCollisionHeightField3D>(cs)) {
Vector<Vector3> lines;
AABB aabb;
aabb.position = -cs->call("get_extents").operator Vector3();
@@ -3128,9 +2947,9 @@ void GPUParticlesCollision3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_collision_segments(lines);
p_gizmo->add_handles(handles, handles_material);
- GPUParticlesCollisionSDF *col_sdf = Object::cast_to<GPUParticlesCollisionSDF>(cs);
+ GPUParticlesCollisionSDF3D *col_sdf = Object::cast_to<GPUParticlesCollisionSDF3D>(cs);
if (col_sdf) {
- static const int subdivs[GPUParticlesCollisionSDF::RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
+ static const int subdivs[GPUParticlesCollisionSDF3D::RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 };
int subdiv = subdivs[col_sdf->get_resolution()];
float cell_size = aabb.get_longest_axis_size() / subdiv;
@@ -3824,15 +3643,15 @@ void LightmapGIGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
const float c4 = 0.886227;
const float c5 = 0.247708;
Vector3 light = (c1 * sh_col[8] * (n.x * n.x - n.y * n.y) +
- c3 * sh_col[6] * n.z * n.z +
- c4 * sh_col[0] -
- c5 * sh_col[6] +
- 2.0 * c1 * sh_col[4] * n.x * n.y +
- 2.0 * c1 * sh_col[7] * n.x * n.z +
- 2.0 * c1 * sh_col[5] * n.y * n.z +
- 2.0 * c2 * sh_col[3] * n.x +
- 2.0 * c2 * sh_col[1] * n.y +
- 2.0 * c2 * sh_col[2] * n.z);
+ c3 * sh_col[6] * n.z * n.z +
+ c4 * sh_col[0] -
+ c5 * sh_col[6] +
+ 2.0 * c1 * sh_col[4] * n.x * n.y +
+ 2.0 * c1 * sh_col[7] * n.x * n.z +
+ 2.0 * c1 * sh_col[5] * n.y * n.z +
+ 2.0 * c2 * sh_col[3] * n.x +
+ 2.0 * c2 * sh_col[1] * n.y +
+ 2.0 * c2 * sh_col[2] * n.z);
colors.push_back(Color(light.x, light.y, light.z, 1));
}
@@ -4539,9 +4358,9 @@ void CollisionShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
p_gizmo->add_handles(handles, handles_material);
}
- if (Object::cast_to<WorldMarginShape3D>(*s)) {
- Ref<WorldMarginShape3D> ps = s;
- Plane p = ps->get_plane();
+ if (Object::cast_to<WorldBoundaryShape3D>(*s)) {
+ Ref<WorldBoundaryShape3D> wbs = s;
+ const Plane &p = wbs->get_plane();
Vector<Vector3> points;
Vector3 n1 = p.get_any_perpendicular_normal();
@@ -4752,10 +4571,10 @@ void NavigationRegion3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
Vector<Vector3> lines;
- for (Map<_EdgeKey, bool>::Element *E = edge_map.front(); E; E = E->next()) {
- if (E->get()) {
- lines.push_back(E->key().from);
- lines.push_back(E->key().to);
+ for (const KeyValue<_EdgeKey, bool> &E : edge_map) {
+ if (E.value) {
+ lines.push_back(E.key.from);
+ lines.push_back(E.key.to);
}
}
@@ -5455,3 +5274,119 @@ void Joint3DGizmoPlugin::CreateGeneric6DOFJointGizmo(
#undef ADD_VTX
}
+
+////
+
+FogVolumeGizmoPlugin::FogVolumeGizmoPlugin() {
+ Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/fog_volume", Color(0.5, 0.7, 1));
+ create_material("shape_material", gizmo_color);
+ gizmo_color.a = 0.15;
+ create_material("shape_material_internal", gizmo_color);
+
+ create_handle_material("handles");
+}
+
+bool FogVolumeGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return (Object::cast_to<FogVolume>(p_spatial) != nullptr);
+}
+
+String FogVolumeGizmoPlugin::get_gizmo_name() const {
+ return "FogVolume";
+}
+
+int FogVolumeGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+String FogVolumeGizmoPlugin::get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ return "Extents";
+}
+
+Variant FogVolumeGizmoPlugin::get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ return Vector3(p_gizmo->get_spatial_node()->call("get_extents"));
+}
+
+void FogVolumeGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) {
+ Node3D *sn = p_gizmo->get_spatial_node();
+
+ Transform3D gt = sn->get_global_transform();
+ Transform3D gi = gt.affine_inverse();
+
+ Vector3 ray_from = p_camera->project_ray_origin(p_point);
+ Vector3 ray_dir = p_camera->project_ray_normal(p_point);
+
+ Vector3 sg[2] = { gi.xform(ray_from), gi.xform(ray_from + ray_dir * 4096) };
+
+ Vector3 axis;
+ axis[p_id] = 1.0;
+ Vector3 ra, rb;
+ Geometry3D::get_closest_points_between_segments(Vector3(), axis * 4096, sg[0], sg[1], ra, rb);
+ float d = ra[p_id];
+ if (Node3DEditor::get_singleton()->is_snap_enabled()) {
+ d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
+ }
+
+ if (d < 0.001) {
+ d = 0.001;
+ }
+
+ Vector3 he = sn->call("get_extents");
+ he[p_id] = d;
+ sn->call("set_extents", he);
+}
+
+void FogVolumeGizmoPlugin::commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel) {
+ Node3D *sn = p_gizmo->get_spatial_node();
+
+ if (p_cancel) {
+ sn->call("set_extents", p_restore);
+ return;
+ }
+
+ UndoRedo *ur = Node3DEditor::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Change Fog Volume Extents"));
+ ur->add_do_method(sn, "set_extents", sn->call("get_extents"));
+ ur->add_undo_method(sn, "set_extents", p_restore);
+ ur->commit_action();
+}
+
+void FogVolumeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ Node3D *cs = p_gizmo->get_spatial_node();
+
+ p_gizmo->clear();
+
+ if (RS::FogVolumeShape(int(p_gizmo->get_spatial_node()->call("get_shape"))) != RS::FOG_VOLUME_SHAPE_WORLD) {
+ const Ref<Material> material =
+ get_material("shape_material", p_gizmo);
+ const Ref<Material> material_internal =
+ get_material("shape_material_internal", p_gizmo);
+
+ Ref<Material> handles_material = get_material("handles");
+
+ Vector<Vector3> lines;
+ AABB aabb;
+ aabb.position = -cs->call("get_extents").operator Vector3();
+ aabb.size = aabb.position * -2;
+
+ for (int i = 0; i < 12; i++) {
+ Vector3 a, b;
+ aabb.get_edge(i, a, b);
+ lines.push_back(a);
+ lines.push_back(b);
+ }
+
+ Vector<Vector3> handles;
+
+ for (int i = 0; i < 3; i++) {
+ Vector3 ax;
+ ax[i] = cs->call("get_extents").operator Vector3()[i];
+ handles.push_back(ax);
+ }
+
+ p_gizmo->add_lines(lines, material);
+ p_gizmo->add_collision_segments(lines);
+ p_gizmo->add_handles(handles, handles_material);
+ }
+}
+
+/////
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index 415ed5da5c..cf9a464b69 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -45,7 +45,7 @@ class EditorNode3DGizmo : public Node3DGizmo {
struct Instance {
RID instance;
- Ref<ArrayMesh> mesh;
+ Ref<Mesh> mesh;
Ref<Material> material;
Ref<SkinReference> skin_reference;
bool extra_margin = false;
@@ -95,7 +95,7 @@ protected:
public:
void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1));
void add_vertices(const Vector<Vector3> &p_vertices, const Ref<Material> &p_material, Mesh::PrimitiveType p_primitive_type, bool p_billboard = false, const Color &p_modulate = Color(1, 1, 1));
- void add_mesh(const Ref<ArrayMesh> &p_mesh, const Ref<Material> &p_material = Ref<Material>(), const Transform3D &p_xform = Transform3D(), const Ref<SkinReference> &p_skin_reference = Ref<SkinReference>());
+ void add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p_material = Ref<Material>(), const Transform3D &p_xform = Transform3D(), const Ref<SkinReference> &p_skin_reference = Ref<SkinReference>());
void add_collision_segments(const Vector<Vector3> &p_lines);
void add_collision_triangles(const Ref<TriangleMesh> &p_tmesh);
void add_unscaled_billboard(const Ref<Material> &p_material, real_t p_scale = 1, const Color &p_modulate = Color(1, 1, 1));
@@ -249,6 +249,19 @@ public:
AudioStreamPlayer3DGizmoPlugin();
};
+class AudioListener3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(AudioListener3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_gizmo_name() const override;
+ int get_priority() const override;
+
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ AudioListener3DGizmoPlugin();
+};
+
class Camera3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(Camera3DGizmoPlugin, EditorNode3DGizmoPlugin);
@@ -319,18 +332,6 @@ public:
Position3DGizmoPlugin();
};
-class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin {
- GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin);
-
-public:
- bool has_gizmo(Node3D *p_spatial) override;
- String get_gizmo_name() const override;
- int get_priority() const override;
- void redraw(EditorNode3DGizmo *p_gizmo) override;
-
- Skeleton3DGizmoPlugin();
-};
-
class PhysicalBone3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(PhysicalBone3DGizmoPlugin, EditorNode3DGizmoPlugin);
@@ -379,8 +380,8 @@ public:
VehicleWheel3DGizmoPlugin();
};
-class SoftBody3DGizmoPlugin : public EditorNode3DGizmoPlugin {
- GDCLASS(SoftBody3DGizmoPlugin, EditorNode3DGizmoPlugin);
+class SoftDynamicBody3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(SoftDynamicBody3DGizmoPlugin, EditorNode3DGizmoPlugin);
public:
bool has_gizmo(Node3D *p_spatial) override;
@@ -394,7 +395,7 @@ public:
void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override;
bool is_handle_highlighted(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
- SoftBody3DGizmoPlugin();
+ SoftDynamicBody3DGizmoPlugin();
};
class VisibleOnScreenNotifier3DGizmoPlugin : public EditorNode3DGizmoPlugin {
@@ -668,4 +669,21 @@ public:
Joint3DGizmoPlugin();
};
+class FogVolumeGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(FogVolumeGizmoPlugin, EditorNode3DGizmoPlugin);
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_gizmo_name() const override;
+ int get_priority() const override;
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ String get_handle_name(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
+ Variant get_handle_value(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
+ void set_handle(const EditorNode3DGizmo *p_gizmo, int p_id, Camera3D *p_camera, const Point2 &p_point) override;
+ void commit_handle(const EditorNode3DGizmo *p_gizmo, int p_id, const Variant &p_restore, bool p_cancel = false) override;
+
+ FogVolumeGizmoPlugin();
+};
+
#endif // NODE_3D_EDITOR_GIZMOS_H
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 291cafab2b..f49b749046 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -128,7 +128,7 @@ void ViewportRotationControl::_draw_axis(const Axis2D &p_axis) {
const Color axis_color = axis_colors[direction];
const double alpha = focused ? 1.0 : ((p_axis.z_axis + 1.0) / 2.0) * 0.5 + 0.5;
- const Color c = focused ? Color(0.9, 0.9, 0.9) : Color(axis_color.r, axis_color.g, axis_color.b, alpha);
+ const Color c = focused ? Color(0.9, 0.9, 0.9) : Color(axis_color, alpha);
if (positive) {
// Draw axis lines for the positive axes.
@@ -185,7 +185,7 @@ void ViewportRotationControl::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
const Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
Vector2 pos = mb->get_position();
if (mb->is_pressed()) {
if (pos.distance_to(get_size() / 2.0) < get_size().x / 2.0) {
@@ -252,6 +252,14 @@ void ViewportRotationControl::set_viewport(Node3DEditorViewport *p_viewport) {
viewport = p_viewport;
}
+void Node3DEditorViewport::_view_settings_confirmed(real_t p_interp_delta) {
+ // Set FOV override multiplier back to the default, so that the FOV
+ // setting specified in the View menu is correctly applied.
+ cursor.fov_scale = 1.0;
+
+ _update_camera(p_interp_delta);
+}
+
void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
bool is_orthogonal = camera->get_projection() == Camera3D::PROJECTION_ORTHOGONAL;
@@ -265,15 +273,13 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
if (is_freelook_active()) {
// Higher inertia should increase "lag" (lerp with factor between 0 and 1)
// Inertia of zero should produce instant movement (lerp with factor of 1) in this case it returns a really high value and gets clamped to 1.
- real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia");
- inertia = MAX(0.001, inertia);
+ const real_t inertia = EDITOR_GET("editors/3d/freelook/freelook_inertia");
real_t factor = (1.0 / inertia) * p_interp_delta;
// We interpolate a different point here, because in freelook mode the focus point (cursor.pos) orbits around eye_pos
camera_cursor.eye_pos = old_camera_cursor.eye_pos.lerp(cursor.eye_pos, CLAMP(factor, 0, 1));
- real_t orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
- orbit_inertia = MAX(0.0001, orbit_inertia);
+ const real_t orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
@@ -289,24 +295,9 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
camera_cursor.pos = camera_cursor.eye_pos + forward * camera_cursor.distance;
} else {
- //when not being manipulated, move softly
- real_t free_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
- real_t free_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia");
- //when being manipulated, move more quickly
- real_t manip_orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_orbit_inertia");
- real_t manip_translation_inertia = EDITOR_GET("editors/3d/navigation_feel/manipulation_translation_inertia");
-
- real_t zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia");
-
- //determine if being manipulated
- bool manipulated = Input::get_singleton()->get_mouse_button_mask() & (2 | 4);
- manipulated |= Input::get_singleton()->is_key_pressed(KEY_SHIFT);
- manipulated |= Input::get_singleton()->is_key_pressed(KEY_ALT);
- manipulated |= Input::get_singleton()->is_key_pressed(KEY_CTRL);
-
- real_t orbit_inertia = MAX(0.00001, manipulated ? manip_orbit_inertia : free_orbit_inertia);
- real_t translation_inertia = MAX(0.0001, manipulated ? manip_translation_inertia : free_translation_inertia);
- zoom_inertia = MAX(0.0001, zoom_inertia);
+ const real_t orbit_inertia = EDITOR_GET("editors/3d/navigation_feel/orbit_inertia");
+ const real_t translation_inertia = EDITOR_GET("editors/3d/navigation_feel/translation_inertia");
+ const real_t zoom_inertia = EDITOR_GET("editors/3d/navigation_feel/zoom_inertia");
camera_cursor.x_rot = Math::lerp(old_camera_cursor.x_rot, cursor.x_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
camera_cursor.y_rot = Math::lerp(old_camera_cursor.y_rot, cursor.y_rot, MIN(1.f, p_interp_delta * (1 / orbit_inertia)));
@@ -335,6 +326,8 @@ void Node3DEditorViewport::_update_camera(real_t p_interp_delta) {
equal = false;
} else if (!Math::is_equal_approx(old_camera_cursor.distance, camera_cursor.distance, tolerance)) {
equal = false;
+ } else if (!Math::is_equal_approx(old_camera_cursor.fov_scale, camera_cursor.fov_scale, tolerance)) {
+ equal = false;
}
if (!equal || p_interp_delta == 0 || is_orthogonal != orthogonal) {
@@ -374,8 +367,8 @@ int Node3DEditorViewport::get_selected_count() const {
int count = 0;
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- Node3D *sp = Object::cast_to<Node3D>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ Node3D *sp = Object::cast_to<Node3D>(E.key);
if (!sp) {
continue;
}
@@ -400,7 +393,7 @@ float Node3DEditorViewport::get_zfar() const {
}
float Node3DEditorViewport::get_fov() const {
- return CLAMP(spatial_editor->get_fov(), MIN_FOV, MAX_FOV);
+ return CLAMP(spatial_editor->get_fov() * cursor.fov_scale, MIN_FOV, MAX_FOV);
}
Transform3D Node3DEditorViewport::_get_camera_transform() const {
@@ -496,7 +489,7 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) {
RS::get_singleton()->sdfgi_set_debug_probe_select(pos, ray);
}
- Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world_3d()->get_scenario());
+ Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario());
Set<Ref<EditorNode3DGizmo>> found_gizmos;
Node *edited_scene = get_tree()->get_edited_scene_root();
@@ -559,7 +552,7 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe
Vector3 ray = _get_ray(p_pos);
Vector3 pos = _get_ray_pos(p_pos);
- Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world_3d()->get_scenario());
+ Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario());
Set<Node3D *> found_nodes;
for (int i = 0; i < instances.size(); i++) {
@@ -668,13 +661,13 @@ void Node3DEditorViewport::_select_region() {
Vector3 a = _get_screen_to_space(box[i]);
Vector3 b = _get_screen_to_space(box[(i + 1) % 4]);
if (orthogonal) {
- frustum.push_back(Plane(a, (a - b).normalized()));
+ frustum.push_back(Plane((a - b).normalized(), a));
} else {
frustum.push_back(Plane(a, b, cam_pos));
}
}
- Plane near(cam_pos, -_get_camera_normal());
+ Plane near(-_get_camera_normal(), cam_pos);
near.d -= get_znear();
frustum.push_back(near);
@@ -835,7 +828,7 @@ void Node3DEditorViewport::_update_name() {
if (orthogonal) {
name = TTR("Left Orthogonal");
} else {
- name = TTR("Right Perspective");
+ name = TTR("Left Perspective");
}
} break;
case VIEW_TYPE_RIGHT: {
@@ -867,12 +860,12 @@ void Node3DEditorViewport::_update_name() {
}
view_menu->set_text(name);
- view_menu->set_size(Vector2(0, 0)); // resets the button size
+ view_menu->reset_size();
}
void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {
- _edit.click_ray = _get_ray(Vector2(p_point.x, p_point.y));
- _edit.click_ray_pos = _get_ray_pos(Vector2(p_point.x, p_point.y));
+ _edit.click_ray = _get_ray(p_point);
+ _edit.click_ray_pos = _get_ray_pos(p_point);
_edit.plane = TRANSFORM_VIEW;
spatial_editor->update_transform_gizmo();
_edit.center = spatial_editor->get_gizmo_transform().origin;
@@ -881,8 +874,8 @@ void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {
Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr;
if (se && se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) {
- int subgizmo_id = E->key();
+ for (const KeyValue<int, Transform3D> &E : se->subgizmos) {
+ int subgizmo_id = E.key;
se->subgizmos[subgizmo_id] = se->gizmo->get_subgizmo_transform(subgizmo_id);
}
se->original_local = selected->get_transform();
@@ -908,36 +901,36 @@ void Node3DEditorViewport::_compute_edit(const Point2 &p_point) {
}
}
-static int _get_key_modifier_setting(const String &p_property) {
+static Key _get_key_modifier_setting(const String &p_property) {
switch (EditorSettings::get_singleton()->get(p_property).operator int()) {
case 0:
- return 0;
+ return Key::NONE;
case 1:
- return KEY_SHIFT;
+ return Key::SHIFT;
case 2:
- return KEY_ALT;
+ return Key::ALT;
case 3:
- return KEY_META;
+ return Key::META;
case 4:
- return KEY_CTRL;
+ return Key::CTRL;
}
- return 0;
+ return Key::NONE;
}
-static int _get_key_modifier(Ref<InputEventWithModifiers> e) {
+static Key _get_key_modifier(Ref<InputEventWithModifiers> e) {
if (e->is_shift_pressed()) {
- return KEY_SHIFT;
+ return Key::SHIFT;
}
if (e->is_alt_pressed()) {
- return KEY_ALT;
+ return Key::ALT;
}
if (e->is_ctrl_pressed()) {
- return KEY_CTRL;
+ return Key::CTRL;
}
if (e->is_meta_pressed()) {
- return KEY_META;
+ return Key::META;
}
- return 0;
+ return Key::NONE;
}
bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, bool p_highlight_only) {
@@ -951,8 +944,8 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
return false;
}
- Vector3 ray_pos = _get_ray_pos(Vector2(p_screenpos.x, p_screenpos.y));
- Vector3 ray = _get_ray(Vector2(p_screenpos.x, p_screenpos.y));
+ Vector3 ray_pos = _get_ray_pos(p_screenpos);
+ Vector3 ray = _get_ray(p_screenpos);
Transform3D gt = spatial_editor->get_gizmo_transform();
@@ -989,7 +982,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gizmo_scale * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667);
Vector3 r;
- Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+ Plane plane(gt.basis.get_axis(i).normalized(), gt.origin);
if (plane.intersects_ray(ray_pos, ray, &r)) {
const real_t dist = r.distance_to(grabber_pos);
@@ -1015,7 +1008,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
} else {
//handle plane translate
_edit.mode = TRANSFORM_TRANSLATE;
- _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
+ _compute_edit(p_screenpos);
_edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_translate ? 3 : 0));
}
return true;
@@ -1027,7 +1020,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
float col_d = 1e20;
for (int i = 0; i < 3; i++) {
- Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+ Plane plane(gt.basis.get_axis(i).normalized(), gt.origin);
Vector3 r;
if (!plane.intersects_ray(ray_pos, ray, &r)) {
continue;
@@ -1053,7 +1046,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
} else {
//handle rotate
_edit.mode = TRANSFORM_ROTATE;
- _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
+ _compute_edit(p_screenpos);
_edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis);
}
return true;
@@ -1093,7 +1086,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
const Vector3 grabber_pos = gt.origin + (ivec2 + ivec3) * gizmo_scale * (GIZMO_PLANE_SIZE + GIZMO_PLANE_DST * 0.6667);
Vector3 r;
- Plane plane(gt.origin, gt.basis.get_axis(i).normalized());
+ Plane plane(gt.basis.get_axis(i).normalized(), gt.origin);
if (plane.intersects_ray(ray_pos, ray, &r)) {
const real_t dist = r.distance_to(grabber_pos);
@@ -1119,7 +1112,7 @@ bool Node3DEditorViewport::_transform_gizmo_select(const Vector2 &p_screenpos, b
} else {
//handle scale
_edit.mode = TRANSFORM_SCALE;
- _compute_edit(Point2(p_screenpos.x, p_screenpos.y));
+ _compute_edit(p_screenpos);
_edit.plane = TransformPlane(TRANSFORM_X_AXIS + col_axis + (is_plane_scale ? 3 : 0));
}
return true;
@@ -1245,7 +1238,7 @@ void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
Node3D *item = selection_results[i].item;
if (item != scene && item->get_owner() != scene && item != scene->get_deepest_editable_node(item)) {
//invalid result
- selection_results.remove(i);
+ selection_results.remove_at(i);
i--;
}
}
@@ -1298,7 +1291,8 @@ void Node3DEditorViewport::_list_select(Ref<InputEventMouseButton> b) {
selection_menu->set_item_tooltip(i, String(spat->get_name()) + "\nType: " + spat->get_class() + "\nPath: " + node_path);
}
- selection_menu->set_position(get_screen_transform().xform(b->get_position()));
+ selection_menu->set_position(get_screen_position() + b->get_position());
+ selection_menu->reset_size();
selection_menu->popup();
}
}
@@ -1308,24 +1302,31 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return; //do NONE
}
+ EditorPlugin::AfterGUIInput after = EditorPlugin::AFTER_GUI_INPUT_PASS;
{
EditorNode *en = editor;
EditorPluginList *force_input_forwarding_list = en->get_editor_plugins_force_input_forwarding();
if (!force_input_forwarding_list->is_empty()) {
- bool discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true);
- if (discard) {
+ EditorPlugin::AfterGUIInput discard = force_input_forwarding_list->forward_spatial_gui_input(camera, p_event, true);
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) {
return;
}
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
}
}
{
EditorNode *en = editor;
EditorPluginList *over_plugin_list = en->get_editor_plugins_over();
if (!over_plugin_list->is_empty()) {
- bool discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false);
- if (discard) {
+ EditorPlugin::AfterGUIInput discard = over_plugin_list->forward_spatial_gui_input(camera, p_event, false);
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_STOP) {
return;
}
+ if (discard == EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ after = EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
}
}
@@ -1336,21 +1337,29 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
const real_t zoom_factor = 1 + (ZOOM_FREELOOK_MULTIPLIER - 1) * b->get_factor();
switch (b->get_button_index()) {
- case MOUSE_BUTTON_WHEEL_UP: {
- if (is_freelook_active()) {
- scale_freelook_speed(zoom_factor);
+ case MouseButton::WHEEL_UP: {
+ if (b->is_alt_pressed()) {
+ scale_fov(-0.05);
} else {
- scale_cursor_distance(1.0 / zoom_factor);
+ if (is_freelook_active()) {
+ scale_freelook_speed(zoom_factor);
+ } else {
+ scale_cursor_distance(1.0 / zoom_factor);
+ }
}
} break;
- case MOUSE_BUTTON_WHEEL_DOWN: {
- if (is_freelook_active()) {
- scale_freelook_speed(1.0 / zoom_factor);
+ case MouseButton::WHEEL_DOWN: {
+ if (b->is_alt_pressed()) {
+ scale_fov(0.05);
} else {
- scale_cursor_distance(zoom_factor);
+ if (is_freelook_active()) {
+ scale_freelook_speed(1.0 / zoom_factor);
+ } else {
+ scale_cursor_distance(zoom_factor);
+ }
}
} break;
- case MOUSE_BUTTON_RIGHT: {
+ case MouseButton::RIGHT: {
NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
if (b->is_pressed() && _edit.gizmo.is_valid()) {
@@ -1391,9 +1400,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector<int> ids;
Vector<Transform3D> restore;
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- ids.push_back(GE->key());
- restore.push_back(GE->value());
+ for (const KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ ids.push_back(GE.key);
+ restore.push_back(GE.value);
}
se->gizmo->commit_subgizmos(ids, restore, true);
@@ -1407,7 +1416,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (b->is_pressed()) {
- const int mod = _get_key_modifier(b);
+ const Key mod = _get_key_modifier(b);
if (!orthogonal) {
if (mod == _get_key_modifier_setting("editors/3d/freelook/freelook_activation_modifier")) {
set_freelook_active(true);
@@ -1424,7 +1433,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
} break;
- case MOUSE_BUTTON_MIDDLE: {
+ case MouseButton::MIDDLE: {
if (b->is_pressed() && _edit.mode != TRANSFORM_NONE) {
switch (_edit.plane) {
case TRANSFORM_VIEW: {
@@ -1455,7 +1464,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
}
} break;
- case MOUSE_BUTTON_LEFT: {
+ case MouseButton::LEFT: {
if (b->is_pressed()) {
NavigationScheme nav_scheme = (NavigationScheme)EditorSettings::get_singleton()->get("editors/3d/navigation/navigation_scheme").operator int();
if ((nav_scheme == NAVIGATION_MAYA || nav_scheme == NAVIGATION_MODO) && b->is_alt_pressed()) {
@@ -1590,17 +1599,19 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
}
- clicked = _select_ray(b->get_position());
+ if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ clicked = _select_ray(b->get_position());
- //clicking is always deferred to either move or release
+ //clicking is always deferred to either move or release
- clicked_wants_append = b->is_shift_pressed();
+ clicked_wants_append = b->is_shift_pressed();
- if (clicked.is_null()) {
- //default to regionselect
- cursor.region_select = true;
- cursor.region_begin = b->get_position();
- cursor.region_end = b->get_position();
+ if (clicked.is_null()) {
+ //default to regionselect
+ cursor.region_select = true;
+ cursor.region_begin = b->get_position();
+ cursor.region_end = b->get_position();
+ }
}
surface->update();
@@ -1611,14 +1622,16 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
break;
}
- if (clicked.is_valid()) {
- _select_clicked(false);
- }
+ if (after != EditorPlugin::AFTER_GUI_INPUT_DESELECT) {
+ if (clicked.is_valid()) {
+ _select_clicked(false);
+ }
- if (cursor.region_select) {
- _select_region();
- cursor.region_select = false;
- surface->update();
+ if (cursor.region_select) {
+ _select_region();
+ cursor.region_select = false;
+ surface->update();
+ }
}
if (_edit.mode != TRANSFORM_NONE) {
@@ -1629,9 +1642,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector<int> ids;
Vector<Transform3D> restore;
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- ids.push_back(GE->key());
- restore.push_back(GE->value());
+ for (const KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ ids.push_back(GE.key);
+ restore.push_back(GE.value);
}
se->gizmo->commit_subgizmos(ids, restore, false);
@@ -1712,7 +1725,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
}
- if (spatial_editor->get_current_hover_gizmo().is_null() && !(m->get_button_mask() & 1) && !_edit.gizmo.is_valid()) {
+ if (spatial_editor->get_current_hover_gizmo().is_null() && (m->get_button_mask() & MouseButton::MASK_LEFT) == MouseButton::NONE && !_edit.gizmo.is_valid()) {
_transform_gizmo_select(_edit.mouse_pos, true);
}
@@ -1725,7 +1738,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
String n = _edit.gizmo->get_handle_name(_edit.gizmo_handle);
set_message(n + ": " + String(v));
- } else if (m->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ } else if ((m->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ORBIT;
} else if (nav_scheme == NAVIGATION_MODO && m->is_alt_pressed() && m->is_shift_pressed()) {
@@ -1737,7 +1750,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
} else {
const bool movement_threshold_passed = _edit.original_mouse_pos.distance_to(_edit.mouse_pos) > 8 * EDSCALE;
if (clicked.is_valid() && movement_threshold_passed) {
- _compute_edit(_edit.mouse_pos);
+ _compute_edit(_edit.original_mouse_pos);
clicked = ObjectID();
_edit.mode = TRANSFORM_TRANSLATE;
@@ -1767,33 +1780,33 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
motion_mask = Vector3(0, 0, 0);
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_get_camera_normal(), _edit.center);
break;
case TRANSFORM_X_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_Y_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_Z_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_YZ:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
plane_mv = true;
break;
case TRANSFORM_XZ:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2) + spatial_editor->get_gizmo_transform().basis.get_axis(0);
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
plane_mv = true;
break;
case TRANSFORM_XY:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0) + spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
plane_mv = true;
break;
}
@@ -1815,7 +1828,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
} else {
// Alternative planar scaling mode
- if (_get_key_modifier(m) != KEY_SHIFT) {
+ if (_get_key_modifier(m) != Key::SHIFT) {
motion = motion_mask.dot(motion) * motion_mask;
}
}
@@ -1831,6 +1844,8 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
motion = Vector3(scale, scale, scale);
}
+ motion /= click.distance_to(_edit.center);
+
// Disable local transformation for TRANSFORM_VIEW
bool local_coords = (spatial_editor->are_local_coords_enabled() && _edit.plane != TRANSFORM_VIEW);
@@ -1841,7 +1856,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
motion_snapped.snap(Vector3(snap, snap, snap));
// This might not be necessary anymore after issue #288 is solved (in 4.0?).
set_message(TTR("Scaling: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
- String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
+ String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
List<Node *> &selection = editor_selection->get_selected_node_list();
for (Node *E : selection) {
@@ -1860,13 +1875,13 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- Transform3D xform = GE->get();
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original * xform, xform, motion, snap, local_coords);
if (!local_coords) {
new_xform = se->original.affine_inverse() * new_xform;
}
- se->gizmo->set_subgizmo_transform(GE->key(), new_xform);
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
}
} else {
Transform3D new_xform = _compute_transform(TRANSFORM_SCALE, se->original, se->original_local, motion, snap, local_coords);
@@ -1886,30 +1901,30 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_get_camera_normal(), _edit.center);
break;
case TRANSFORM_X_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(0);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_Y_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(1);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_Z_AXIS:
motion_mask = spatial_editor->get_gizmo_transform().basis.get_axis(2);
- plane = Plane(_edit.center, motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized());
+ plane = Plane(motion_mask.cross(motion_mask.cross(_get_camera_normal())).normalized(), _edit.center);
break;
case TRANSFORM_YZ:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
plane_mv = true;
break;
case TRANSFORM_XZ:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
plane_mv = true;
break;
case TRANSFORM_XY:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
plane_mv = true;
break;
}
@@ -1940,7 +1955,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector3 motion_snapped = motion;
motion_snapped.snap(Vector3(snap, snap, snap));
set_message(TTR("Translating: ") + "(" + String::num(motion_snapped.x, snap_step_decimals) + ", " +
- String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
+ String::num(motion_snapped.y, snap_step_decimals) + ", " + String::num(motion_snapped.z, snap_step_decimals) + ")");
List<Node *> &selection = editor_selection->get_selected_node_list();
for (Node *E : selection) {
@@ -1959,11 +1974,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- Transform3D xform = GE->get();
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original * xform, xform, motion, snap, local_coords);
new_xform = se->original.affine_inverse() * new_xform;
- se->gizmo->set_subgizmo_transform(GE->key(), new_xform);
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
}
} else {
Transform3D new_xform = _compute_transform(TRANSFORM_TRANSLATE, se->original, se->original_local, motion, snap, local_coords);
@@ -1982,18 +1997,18 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
switch (_edit.plane) {
case TRANSFORM_VIEW:
- plane = Plane(_edit.center, _get_camera_normal());
+ plane = Plane(_get_camera_normal(), _edit.center);
break;
case TRANSFORM_X_AXIS:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(0));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(0), _edit.center);
axis = Vector3(1, 0, 0);
break;
case TRANSFORM_Y_AXIS:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(1));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(1), _edit.center);
axis = Vector3(0, 1, 0);
break;
case TRANSFORM_Z_AXIS:
- plane = Plane(_edit.center, spatial_editor->get_gizmo_transform().basis.get_axis(2));
+ plane = Plane(spatial_editor->get_gizmo_transform().basis.get_axis(2), _edit.center);
axis = Vector3(0, 0, 1);
break;
case TRANSFORM_YZ:
@@ -2045,14 +2060,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
Vector3 compute_axis = local_coords ? axis : plane.normal;
if (se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- Transform3D xform = GE->get();
+ for (KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ Transform3D xform = GE.value;
Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original * xform, xform, compute_axis, angle, local_coords);
if (!local_coords) {
new_xform = se->original.affine_inverse() * new_xform;
}
- se->gizmo->set_subgizmo_transform(GE->key(), new_xform);
+ se->gizmo->set_subgizmo_transform(GE.key, new_xform);
}
} else {
Transform3D new_xform = _compute_transform(TRANSFORM_ROTATE, se->original, se->original_local, compute_axis, angle, local_coords);
@@ -2068,7 +2083,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
}
}
- } else if ((m->get_button_mask() & MOUSE_BUTTON_MASK_RIGHT) || freelook_active) {
+ } else if ((m->get_button_mask() & MouseButton::MASK_RIGHT) != MouseButton::NONE || freelook_active) {
if (nav_scheme == NAVIGATION_MAYA && m->is_alt_pressed()) {
nav_mode = NAVIGATION_ZOOM;
} else if (freelook_active) {
@@ -2077,14 +2092,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
nav_mode = NAVIGATION_PAN;
}
- } else if (m->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE) {
- const int mod = _get_key_modifier(m);
+ } else if ((m->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE) {
+ const Key mod = _get_key_modifier(m);
if (nav_scheme == NAVIGATION_GODOT) {
if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
nav_mode = NAVIGATION_PAN;
} else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
nav_mode = NAVIGATION_ZOOM;
- } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
+ } else if (mod == Key::ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
// Always allow Alt as a modifier to better support graphic tablets.
nav_mode = NAVIGATION_ORBIT;
}
@@ -2095,14 +2110,14 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
} else if (EditorSettings::get_singleton()->get("editors/3d/navigation/emulate_3_button_mouse")) {
// Handle trackpad (no external mouse) use case
- const int mod = _get_key_modifier(m);
+ const Key mod = _get_key_modifier(m);
- if (mod) {
+ if (mod != Key::NONE) {
if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
nav_mode = NAVIGATION_PAN;
} else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
nav_mode = NAVIGATION_ZOOM;
- } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
+ } else if (mod == Key::ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
// Always allow Alt as a modifier to better support graphic tablets.
nav_mode = NAVIGATION_ORBIT;
}
@@ -2150,13 +2165,13 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
NavigationMode nav_mode = NAVIGATION_NONE;
if (nav_scheme == NAVIGATION_GODOT) {
- const int mod = _get_key_modifier(pan_gesture);
+ const Key mod = _get_key_modifier(pan_gesture);
if (mod == _get_key_modifier_setting("editors/3d/navigation/pan_modifier")) {
nav_mode = NAVIGATION_PAN;
} else if (mod == _get_key_modifier_setting("editors/3d/navigation/zoom_modifier")) {
nav_mode = NAVIGATION_ZOOM;
- } else if (mod == KEY_ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
+ } else if (mod == Key::ALT || mod == _get_key_modifier_setting("editors/3d/navigation/orbit_modifier")) {
// Always allow Alt as a modifier to better support graphic tablets.
nav_mode = NAVIGATION_ORBIT;
}
@@ -2201,9 +2216,9 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (EditorSettings::get_singleton()->get("editors/3d/navigation/emulate_numpad")) {
- const uint32_t code = k->get_keycode();
- if (code >= KEY_0 && code <= KEY_9) {
- k->set_keycode(code - KEY_0 + KEY_KP_0);
+ const Key code = k->get_keycode();
+ if (code >= Key::KEY_0 && code <= Key::KEY_9) {
+ k->set_keycode(code - Key::KEY_0 + Key::KP_0);
}
}
@@ -2230,6 +2245,33 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (ED_IS_SHORTCUT("spatial_editor/right_view", p_event)) {
_menu_option(VIEW_RIGHT);
}
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_down", p_event)) {
+ // Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
+ cursor.x_rot = CLAMP(cursor.x_rot - Math_PI / 12.0, -1.57, 1.57);
+ view_type = VIEW_TYPE_USER;
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_up", p_event)) {
+ // Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
+ cursor.x_rot = CLAMP(cursor.x_rot + Math_PI / 12.0, -1.57, 1.57);
+ view_type = VIEW_TYPE_USER;
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_right", p_event)) {
+ cursor.y_rot -= Math_PI / 12.0;
+ view_type = VIEW_TYPE_USER;
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_left", p_event)) {
+ cursor.y_rot += Math_PI / 12.0;
+ view_type = VIEW_TYPE_USER;
+ _update_name();
+ }
+ if (ED_IS_SHORTCUT("spatial_editor/orbit_view_180", p_event)) {
+ cursor.y_rot += Math_PI;
+ view_type = VIEW_TYPE_USER;
+ _update_name();
+ }
if (ED_IS_SHORTCUT("spatial_editor/focus_origin", p_event)) {
_menu_option(VIEW_CENTER_TO_ORIGIN);
}
@@ -2252,7 +2294,7 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
return;
}
- if (!AnimationPlayerEditor::singleton->get_track_editor()->has_keying()) {
+ if (!AnimationPlayerEditor::get_singleton()->get_track_editor()->has_keying()) {
set_message(TTR("Keying is disabled (no key inserted)."));
return;
}
@@ -2275,15 +2317,27 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (!orthogonal && ED_IS_SHORTCUT("spatial_editor/freelook_toggle", p_event)) {
set_freelook_active(!is_freelook_active());
- } else if (k->get_keycode() == KEY_ESCAPE) {
+ } else if (k->get_keycode() == Key::ESCAPE) {
set_freelook_active(false);
}
- if (k->get_keycode() == KEY_SPACE) {
+ if (k->get_keycode() == Key::SPACE) {
if (!k->is_pressed()) {
emit_signal(SNAME("toggle_maximize_view"), this);
}
}
+
+ if (ED_IS_SHORTCUT("spatial_editor/decrease_fov", p_event)) {
+ scale_fov(-0.05);
+ }
+
+ if (ED_IS_SHORTCUT("spatial_editor/increase_fov", p_event)) {
+ scale_fov(0.05);
+ }
+
+ if (ED_IS_SHORTCUT("spatial_editor/reset_fov", p_event)) {
+ reset_fov();
+ }
}
// freelook uses most of the useful shortcuts, like save, so its ok
@@ -2451,6 +2505,16 @@ void Node3DEditorViewport::set_freelook_active(bool active_now) {
freelook_active = active_now;
}
+void Node3DEditorViewport::scale_fov(real_t p_fov_offset) {
+ cursor.fov_scale = CLAMP(cursor.fov_scale + p_fov_offset, 0.1, 2.5);
+ surface->update();
+}
+
+void Node3DEditorViewport::reset_fov() {
+ cursor.fov_scale = 1.0;
+ surface->update();
+}
+
void Node3DEditorViewport::scale_cursor_distance(real_t scale) {
real_t min_distance = MAX(camera->get_near() * 4, ZOOM_FREELOOK_MIN);
real_t max_distance = MIN(camera->get_far() / 4, ZOOM_FREELOOK_MAX);
@@ -2498,8 +2562,14 @@ static bool is_shortcut_pressed(const String &p_path) {
if (shortcut.is_null()) {
return false;
}
- InputEventKey *k = Object::cast_to<InputEventKey>(shortcut->get_event().ptr());
- if (k == nullptr) {
+
+ const Array shortcuts = shortcut->get_events();
+ Ref<InputEventKey> k;
+ if (shortcuts.size() > 0) {
+ k = shortcuts.front();
+ }
+
+ if (k.is_null()) {
return false;
}
const Input &input = *Input::get_singleton();
@@ -2616,6 +2686,9 @@ void Node3DEditorViewport::_project_settings_changed() {
const bool use_occlusion_culling = GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling");
viewport->set_use_occlusion_culling(use_occlusion_culling);
+
+ const float lod_threshold = GLOBAL_GET("rendering/mesh_lod/lod_change/threshold_pixels");
+ viewport->set_lod_threshold(lod_threshold);
}
void Node3DEditorViewport::_notification(int p_what) {
@@ -2678,8 +2751,8 @@ void Node3DEditorViewport::_notification(int p_what) {
bool changed = false;
bool exist = false;
- for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) {
- Node3D *sp = Object::cast_to<Node3D>(E->key());
+ for (const KeyValue<Node *, Object *> &E : selection) {
+ Node3D *sp = Object::cast_to<Node3D>(E.key);
if (!sp) {
continue;
}
@@ -2703,15 +2776,28 @@ void Node3DEditorViewport::_notification(int p_what) {
se->aabb = new_aabb;
- t.translate(se->aabb.position);
+ Transform3D t_offset = t;
// apply AABB scaling before item's global transform
- Basis aabb_s;
- aabb_s.scale(se->aabb.size);
- t.basis = t.basis * aabb_s;
+ {
+ const Vector3 offset(0.005, 0.005, 0.005);
+ Basis aabb_s;
+ aabb_s.scale(se->aabb.size + offset);
+ t.translate(se->aabb.position - offset / 2);
+ t.basis = t.basis * aabb_s;
+ }
+ {
+ const Vector3 offset(0.01, 0.01, 0.01);
+ Basis aabb_s;
+ aabb_s.scale(se->aabb.size + offset);
+ t_offset.translate(se->aabb.position - offset / 2);
+ t_offset.basis = t_offset.basis * aabb_s;
+ }
RenderingServer::get_singleton()->instance_set_transform(se->sbox_instance, t);
+ RenderingServer::get_singleton()->instance_set_transform(se->sbox_instance_offset, t_offset);
RenderingServer::get_singleton()->instance_set_transform(se->sbox_instance_xray, t);
+ RenderingServer::get_singleton()->instance_set_transform(se->sbox_instance_xray_offset, t_offset);
}
if (changed || (spatial_editor->is_gizmo_visible() && !exist)) {
@@ -2802,13 +2888,13 @@ void Node3DEditorViewport::_notification(int p_what) {
// Color labels depending on performance level ("good" = green, "OK" = yellow, "bad" = red).
// Middle point is at 15 ms.
- cpu_time_label->set_text(vformat(TTR("CPU Time: %s ms"), rtos(cpu_time).pad_decimals(1)));
+ cpu_time_label->set_text(vformat(TTR("CPU Time: %s ms"), rtos(cpu_time).pad_decimals(2)));
cpu_time_label->add_theme_color_override(
"font_color",
frame_time_gradient->get_color_at_offset(
Math::range_lerp(cpu_time, 0, 30, 0, 1)));
- gpu_time_label->set_text(vformat(TTR("GPU Time: %s ms"), rtos(gpu_time).pad_decimals(1)));
+ gpu_time_label->set_text(vformat(TTR("GPU Time: %s ms"), rtos(gpu_time).pad_decimals(2)));
// Middle point is at 15 ms.
gpu_time_label->add_theme_color_override(
"font_color",
@@ -2901,7 +2987,7 @@ static void draw_indicator_bar(Control &surface, real_t fill, const Ref<Texture2
surface.draw_texture(icon, icon_pos);
// Draw text below the bar (for speed/zoom information).
- surface.draw_string(font, Vector2(icon_pos.x, icon_pos.y + icon_size.y + 16 * EDSCALE), text, HALIGN_LEFT, -1.f, font_size);
+ surface.draw_string(font, Vector2(icon_pos.x, icon_pos.y + icon_size.y + 16 * EDSCALE), text, HORIZONTAL_ALIGNMENT_LEFT, -1.f, font_size);
}
void Node3DEditorViewport::_draw() {
@@ -2941,9 +3027,9 @@ void Node3DEditorViewport::_draw() {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
Point2 msgpos = Point2(5, get_size().y - 20);
- font->draw_string(ci, msgpos + Point2(1, 1), message, HALIGN_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
- font->draw_string(ci, msgpos + Point2(-1, -1), message, HALIGN_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
- font->draw_string(ci, msgpos, message, HALIGN_LEFT, -1, font_size, Color(1, 1, 1, 1));
+ font->draw_string(ci, msgpos + Point2(1, 1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
+ font->draw_string(ci, msgpos + Point2(-1, -1), message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(0, 0, 0, 0.8));
+ font->draw_string(ci, msgpos, message, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1, 1, 1, 1));
}
if (_edit.mode == TRANSFORM_ROTATE) {
@@ -3170,7 +3256,7 @@ void Node3DEditorViewport::_menu_option(int p_option) {
continue;
}
- undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_rotation());
+ undo_redo->add_do_method(sp, "set_rotation", camera_transform.basis.get_euler_normalized());
undo_redo->add_undo_method(sp, "set_rotation", sp->get_rotation());
}
undo_redo->commit_action();
@@ -3535,7 +3621,7 @@ void Node3DEditorViewport::_selection_result_pressed(int p_result) {
void Node3DEditorViewport::_selection_menu_hide() {
selection_results.clear();
selection_menu->clear();
- selection_menu->set_size(Vector2(0, 0));
+ selection_menu->reset_size();
}
void Node3DEditorViewport::set_can_preview(Camera3D *p_preview) {
@@ -3570,7 +3656,7 @@ void Node3DEditorViewport::update_transform_gizmo_view() {
const Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized();
const Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized();
- const Plane p(camera_xform.origin, camz);
+ const Plane p = Plane(camz, camera_xform.origin);
const real_t gizmo_d = MAX(Math::abs(p.distance_to(xform.origin)), CMP_EPSILON);
const real_t d0 = camera->unproject_position(camera_xform.origin + camz * gizmo_d).y;
const real_t d1 = camera->unproject_position(camera_xform.origin + camz * gizmo_d + camy).y;
@@ -3821,8 +3907,8 @@ void Node3DEditorViewport::focus_selection() {
}
if (se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *GE = se->subgizmos.front(); GE; GE = GE->next()) {
- center += se->gizmo->get_subgizmo_transform(GE->key()).origin;
+ for (const KeyValue<int, Transform3D> &GE : se->subgizmos) {
+ center += se->gizmo->get_subgizmo_transform(GE.key).origin;
count++;
}
}
@@ -3845,7 +3931,7 @@ void Node3DEditorViewport::assign_pending_data_pointers(Node3D *p_preview_node,
}
Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const {
- const float MAX_DISTANCE = 10;
+ const float MAX_DISTANCE = 50.0;
Vector3 world_ray = _get_ray(p_pos);
Vector3 world_pos = _get_ray_pos(p_pos);
@@ -3853,9 +3939,13 @@ Vector3 Node3DEditorViewport::_get_instance_position(const Point2 &p_pos) const
Vector3 point = world_pos + world_ray * MAX_DISTANCE;
PhysicsDirectSpaceState3D *ss = get_tree()->get_root()->get_world_3d()->get_direct_space_state();
- PhysicsDirectSpaceState3D::RayResult result;
- if (ss->intersect_ray(world_pos, world_pos + world_ray * MAX_DISTANCE, result)) {
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = world_pos;
+ ray_params.to = world_pos + world_ray * MAX_DISTANCE;
+
+ PhysicsDirectSpaceState3D::RayResult result;
+ if (ss->intersect_ray(ray_params, result)) {
point = result.position;
}
@@ -3932,7 +4022,7 @@ void Node3DEditorViewport::_remove_preview() {
}
bool Node3DEditorViewport::_cyclical_dependency_exists(const String &p_target_scene_path, Node *p_desired_node) {
- if (p_desired_node->get_filename() == p_target_scene_path) {
+ if (p_desired_node->get_scene_file_path() == p_target_scene_path) {
return true;
}
@@ -3959,7 +4049,23 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
if (mesh != nullptr) {
MeshInstance3D *mesh_instance = memnew(MeshInstance3D);
mesh_instance->set_mesh(mesh);
- mesh_instance->set_name(path.get_file().get_basename());
+
+ // Adjust casing according to project setting. The file name is expected to be in snake_case, but will work for others.
+ String name = path.get_file().get_basename();
+ switch (ProjectSettings::get_singleton()->get("editor/node_naming/name_casing").operator int()) {
+ case NAME_CASING_PASCAL_CASE:
+ name = name.capitalize().replace(" ", "");
+ break;
+ case NAME_CASING_CAMEL_CASE:
+ name = name.capitalize().replace(" ", "");
+ name[0] = name.to_lower()[0];
+ break;
+ case NAME_CASING_SNAKE_CASE:
+ name = name.capitalize().replace(" ", "_").to_lower();
+ break;
+ }
+ mesh_instance->set_name(name);
+
instantiated_scene = mesh_instance;
} else {
if (!scene.is_valid()) { // invalid scene
@@ -3974,15 +4080,15 @@ bool Node3DEditorViewport::_create_instance(Node *parent, String &path, const Po
return false;
}
- if (editor->get_edited_scene()->get_filename() != "") { // cyclical instancing
- if (_cyclical_dependency_exists(editor->get_edited_scene()->get_filename(), instantiated_scene)) {
+ if (!editor->get_edited_scene()->get_scene_file_path().is_empty()) { // cyclical instancing
+ if (_cyclical_dependency_exists(editor->get_edited_scene()->get_scene_file_path(), instantiated_scene)) {
memdelete(instantiated_scene);
return false;
}
}
if (scene != nullptr) {
- instantiated_scene->set_filename(ProjectSettings::get_singleton()->localize_path(path));
+ instantiated_scene->set_scene_file_path(ProjectSettings::get_singleton()->localize_path(path));
}
editor_data->get_undo_redo().add_do_method(parent, "add_child", instantiated_scene);
@@ -4109,8 +4215,8 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_
return;
}
- bool is_shift = Input::get_singleton()->is_key_pressed(KEY_SHIFT);
- bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool is_shift = Input::get_singleton()->is_key_pressed(Key::SHIFT);
+ bool is_ctrl = Input::get_singleton()->is_key_pressed(Key::CTRL);
selected_files.clear();
Dictionary d = p_data;
@@ -4132,10 +4238,9 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_
if (root_node) {
target_node = root_node;
} else {
- accept->set_text(TTR("Cannot drag and drop into scene with no root node."));
- accept->popup_centered();
- _remove_preview();
- return;
+ // Create a root node so we can add child nodes to it.
+ EditorNode::get_singleton()->get_scene_tree_dock()->add_root_node(memnew(Node3D));
+ target_node = get_tree()->get_edited_scene_root();
}
} else {
accept->set_text(TTR("Cannot drag and drop into multiple selected nodes."));
@@ -4194,7 +4299,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
VBoxContainer *vbox = memnew(VBoxContainer);
surface->add_child(vbox);
- vbox->set_position(Point2(10, 10) * EDSCALE);
+ vbox->set_offset(SIDE_LEFT, 10 * EDSCALE);
+ vbox->set_offset(SIDE_TOP, 10 * EDSCALE);
view_menu = memnew(MenuButton);
view_menu->set_flat(false);
@@ -4292,7 +4398,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
const int wireframe_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_WIREFRAME);
const int overdraw_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_OVERDRAW);
const int shadeless_idx = view_menu->get_popup()->get_item_index(VIEW_DISPLAY_SHADELESS);
- const String unsupported_tooltip = TTR("Not available when using the GLES2 renderer.");
+ const String unsupported_tooltip = TTR("Not available when using the OpenGL renderer.");
view_menu->get_popup()->set_item_disabled(normal_idx, true);
view_menu->get_popup()->set_item_tooltip(normal_idx, unsupported_tooltip);
@@ -4304,18 +4410,18 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
view_menu->get_popup()->set_item_tooltip(shadeless_idx, unsupported_tooltip);
}
- ED_SHORTCUT("spatial_editor/freelook_left", TTR("Freelook Left"), KEY_A);
- ED_SHORTCUT("spatial_editor/freelook_right", TTR("Freelook Right"), KEY_D);
- ED_SHORTCUT("spatial_editor/freelook_forward", TTR("Freelook Forward"), KEY_W);
- ED_SHORTCUT("spatial_editor/freelook_backwards", TTR("Freelook Backwards"), KEY_S);
- ED_SHORTCUT("spatial_editor/freelook_up", TTR("Freelook Up"), KEY_E);
- ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), KEY_Q);
- ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT);
- ED_SHORTCUT("spatial_editor/freelook_slow_modifier", TTR("Freelook Slow Modifier"), KEY_ALT);
+ ED_SHORTCUT("spatial_editor/freelook_left", TTR("Freelook Left"), Key::A);
+ ED_SHORTCUT("spatial_editor/freelook_right", TTR("Freelook Right"), Key::D);
+ ED_SHORTCUT("spatial_editor/freelook_forward", TTR("Freelook Forward"), Key::W);
+ ED_SHORTCUT("spatial_editor/freelook_backwards", TTR("Freelook Backwards"), Key::S);
+ ED_SHORTCUT("spatial_editor/freelook_up", TTR("Freelook Up"), Key::E);
+ ED_SHORTCUT("spatial_editor/freelook_down", TTR("Freelook Down"), Key::Q);
+ ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), Key::SHIFT);
+ ED_SHORTCUT("spatial_editor/freelook_slow_modifier", TTR("Freelook Slow Modifier"), Key::ALT);
preview_camera = memnew(CheckBox);
preview_camera->set_text(TTR("Preview"));
- preview_camera->set_shortcut(ED_SHORTCUT("spatial_editor/toggle_camera_preview", TTR("Toggle Camera Preview"), KEY_MASK_CMD | KEY_P));
+ preview_camera->set_shortcut(ED_SHORTCUT("spatial_editor/toggle_camera_preview", TTR("Toggle Camera Preview"), KeyModifierMask::CMD | Key::P));
vbox->add_child(preview_camera);
preview_camera->set_h_size_flags(0);
preview_camera->hide();
@@ -4338,7 +4444,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
cinema_label = memnew(Label);
cinema_label->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 10 * EDSCALE);
cinema_label->set_h_grow_direction(GROW_DIRECTION_END);
- cinema_label->set_align(Label::ALIGN_CENTER);
+ cinema_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
surface->add_child(cinema_label);
cinema_label->set_text(TTR("Cinematic Preview"));
cinema_label->hide();
@@ -4349,7 +4455,7 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
locked_label->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -10 * EDSCALE);
locked_label->set_h_grow_direction(GROW_DIRECTION_END);
locked_label->set_v_grow_direction(GROW_DIRECTION_BEGIN);
- locked_label->set_align(Label::ALIGN_CENTER);
+ locked_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
surface->add_child(locked_label);
locked_label->set_text(TTR("View Rotation Locked"));
locked_label->hide();
@@ -4429,7 +4535,7 @@ void Node3DEditorViewportContainer::gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
Vector2 size = get_size();
@@ -4736,9 +4842,15 @@ Node3DEditorSelectedItem::~Node3DEditorSelectedItem() {
if (sbox_instance.is_valid()) {
RenderingServer::get_singleton()->free(sbox_instance);
}
+ if (sbox_instance_offset.is_valid()) {
+ RenderingServer::get_singleton()->free(sbox_instance_offset);
+ }
if (sbox_instance_xray.is_valid()) {
RenderingServer::get_singleton()->free(sbox_instance_xray);
}
+ if (sbox_instance_xray_offset.is_valid()) {
+ RenderingServer::get_singleton()->free(sbox_instance_xray_offset);
+ }
}
void Node3DEditor::select_gizmo_highlight_axis(int p_axis) {
@@ -4761,8 +4873,8 @@ void Node3DEditor::update_transform_gizmo() {
Node3DEditorSelectedItem *se = selected ? editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(selected) : nullptr;
if (se && se->gizmo.is_valid()) {
- for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) {
- Transform3D xf = se->sp->get_global_transform() * se->gizmo->get_subgizmo_transform(E->key());
+ for (const KeyValue<int, Transform3D> &E : se->subgizmos) {
+ Transform3D xf = se->sp->get_global_transform() * se->gizmo->get_subgizmo_transform(E.key);
gizmo_center += xf.origin;
if (count == 0 && local_gizmo_coords) {
gizmo_basis = xf.basis;
@@ -4818,7 +4930,7 @@ void _update_all_gizmos(Node *p_node) {
}
void Node3DEditor::update_all_gizmos(Node *p_node) {
- if (!p_node && get_tree()) {
+ if (!p_node && is_inside_tree()) {
p_node = get_tree()->get_edited_scene_root();
}
@@ -4841,23 +4953,39 @@ Object *Node3DEditor::_get_editor_data(Object *p_what) {
si->sbox_instance = RenderingServer::get_singleton()->instance_create2(
selection_box->get_rid(),
sp->get_world_3d()->get_scenario());
+ si->sbox_instance_offset = RenderingServer::get_singleton()->instance_create2(
+ selection_box->get_rid(),
+ sp->get_world_3d()->get_scenario());
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(
si->sbox_instance,
RS::SHADOW_CASTING_SETTING_OFF);
+ RS::get_singleton()->instance_geometry_set_cast_shadows_setting(
+ si->sbox_instance_offset,
+ RS::SHADOW_CASTING_SETTING_OFF);
// Use the Edit layer to hide the selection box when View Gizmos is disabled, since it is a bit distracting.
// It's still possible to approximately guess what is selected by looking at the manipulation gizmo position.
RS::get_singleton()->instance_set_layer_mask(si->sbox_instance, 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER);
+ RS::get_singleton()->instance_set_layer_mask(si->sbox_instance_offset, 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER);
RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
+ RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance_offset, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
si->sbox_instance_xray = RenderingServer::get_singleton()->instance_create2(
selection_box_xray->get_rid(),
sp->get_world_3d()->get_scenario());
+ si->sbox_instance_xray_offset = RenderingServer::get_singleton()->instance_create2(
+ selection_box_xray->get_rid(),
+ sp->get_world_3d()->get_scenario());
RS::get_singleton()->instance_geometry_set_cast_shadows_setting(
si->sbox_instance_xray,
RS::SHADOW_CASTING_SETTING_OFF);
+ RS::get_singleton()->instance_geometry_set_cast_shadows_setting(
+ si->sbox_instance_xray_offset,
+ RS::SHADOW_CASTING_SETTING_OFF);
// Use the Edit layer to hide the selection box when View Gizmos is disabled, since it is a bit distracting.
// It's still possible to approximately guess what is selected by looking at the manipulation gizmo position.
RS::get_singleton()->instance_set_layer_mask(si->sbox_instance_xray, 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER);
- RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
+ RS::get_singleton()->instance_set_layer_mask(si->sbox_instance_xray_offset, 1 << Node3DEditorViewport::GIZMO_EDIT_LAYER);
+ RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance_xray, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
+ RS::get_singleton()->instance_geometry_set_flag(si->sbox_instance_xray_offset, RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, true);
return si;
}
@@ -4865,10 +4993,6 @@ Object *Node3DEditor::_get_editor_data(Object *p_what) {
void Node3DEditor::_generate_selection_boxes() {
// Use two AABBs to create the illusion of a slightly thicker line.
AABB aabb(Vector3(), Vector3(1, 1, 1));
- AABB aabb_offset(Vector3(), Vector3(1, 1, 1));
- // Grow the bounding boxes slightly to avoid Z-fighting with the mesh's edges.
- aabb.grow_by(0.005);
- aabb_offset.grow_by(0.01);
// Create a x-ray (visible through solid surfaces) and standard version of the selection box.
// Both will be drawn at the same position, but with different opacity.
@@ -4888,16 +5012,6 @@ void Node3DEditor::_generate_selection_boxes() {
st_xray->add_vertex(b);
}
- for (int i = 0; i < 12; i++) {
- Vector3 a, b;
- aabb_offset.get_edge(i, a, b);
-
- st->add_vertex(a);
- st->add_vertex(b);
- st_xray->add_vertex(a);
- st_xray->add_vertex(b);
- }
-
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
const Color selection_box_color = EDITOR_GET("editors/3d/selection_box_color");
@@ -6028,7 +6142,7 @@ void fragment() {
void Node3DEditor::_update_context_menu_stylebox() {
// This must be called when the theme changes to follow the new accent color.
Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat);
- const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color("accent_color", "Editor");
+ const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("accent_color"), SNAME("Editor"));
context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1));
// Add an underline to the StyleBox, but prevent its minimum vertical size from changing.
context_menu_stylebox->set_border_color(accent_color);
@@ -6144,7 +6258,7 @@ void Node3DEditor::_init_grid() {
if (orthogonal) {
camera_distance = camera->get_size() / 2.0;
Vector3 camera_direction = -camera->get_global_transform().get_basis().get_axis(2);
- Plane grid_plane = Plane(Vector3(), normal);
+ Plane grid_plane = Plane(normal);
Vector3 intersection;
if (grid_plane.intersects_ray(camera_position, camera_direction, &intersection)) {
camera_position = intersection;
@@ -6294,7 +6408,7 @@ void Node3DEditor::update_grid() {
// Gets a orthogonal or perspective position correctly (for the grid comparison)
const Vector3 camera_position = get_editor_viewport(0)->camera->get_position();
- if (!grid_init_draw || (camera_position - grid_camera_last_update_position).length() >= 10.0f) {
+ if (!grid_init_draw || grid_camera_last_update_position.distance_squared_to(camera_position) >= 100.0f) {
_finish_grid();
_init_grid();
grid_init_draw = true;
@@ -6442,7 +6556,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
// We add a bit of margin to the from position to avoid it from snapping
// when the spatial is already on a floor and there's another floor under
// it
- from = from + Vector3(0.0, 0.2, 0.0);
+ from = from + Vector3(0.0, 1, 0.0);
Dictionary d;
@@ -6458,7 +6572,7 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
Array keys = snap_data.keys();
// The maximum height an object can travel to be snapped
- const float max_snap_height = 20.0;
+ const float max_snap_height = 500.0;
// Will be set to `true` if at least one node from the selection was successfully snapped
bool snapped_to_floor = false;
@@ -6474,7 +6588,12 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
Vector3 to = from - Vector3(0.0, max_snap_height, 0.0);
Set<RID> excluded = _get_physics_bodies_rid(sp);
- if (ss->intersect_ray(from, to, result, excluded)) {
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = from;
+ ray_params.to = to;
+ ray_params.exclude = excluded;
+
+ if (ss->intersect_ray(ray_params, result)) {
snapped_to_floor = true;
}
}
@@ -6491,7 +6610,12 @@ void Node3DEditor::snap_selected_nodes_to_floor() {
Vector3 to = from - Vector3(0.0, max_snap_height, 0.0);
Set<RID> excluded = _get_physics_bodies_rid(sp);
- if (ss->intersect_ray(from, to, result, excluded)) {
+ PhysicsDirectSpaceState3D::RayParameters ray_params;
+ ray_params.from = from;
+ ray_params.to = to;
+ ray_params.exclude = excluded;
+
+ if (ss->intersect_ray(ray_params, result)) {
Vector3 position_offset = d["position_offset"];
Transform3D new_transform = sp->get_global_transform();
@@ -6517,19 +6641,20 @@ void Node3DEditor::unhandled_key_input(const Ref<InputEvent> &p_event) {
return;
}
- snap_key_enabled = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ snap_key_enabled = Input::get_singleton()->is_key_pressed(Key::CTRL);
}
void Node3DEditor::_sun_environ_settings_pressed() {
Vector2 pos = sun_environ_settings->get_screen_position() + sun_environ_settings->get_size();
sun_environ_popup->set_position(pos - Vector2(sun_environ_popup->get_contents_minimum_size().width / 2, 0));
+ sun_environ_popup->reset_size();
sun_environ_popup->popup();
}
void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) {
sun_environ_popup->hide();
- if (!p_already_added_environment && world_env_count == 0 && Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (!p_already_added_environment && world_env_count == 0 && Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
// Prevent infinite feedback loop between the sun and environment methods.
_add_environment_to_scene(true);
}
@@ -6544,7 +6669,7 @@ void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) {
Node *new_sun = preview_sun->duplicate();
undo_redo->create_action(TTR("Add Preview Sun to Scene"));
- undo_redo->add_do_method(base, "add_child", new_sun);
+ undo_redo->add_do_method(base, "add_child", new_sun, true);
// Move to the beginning of the scene tree since more "global" nodes
// generally look better when placed at the top.
undo_redo->add_do_method(base, "move_child", new_sun, 0);
@@ -6557,7 +6682,7 @@ void Node3DEditor::_add_sun_to_scene(bool p_already_added_environment) {
void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) {
sun_environ_popup->hide();
- if (!p_already_added_sun && directional_light_count == 0 && Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (!p_already_added_sun && directional_light_count == 0 && Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
// Prevent infinite feedback loop between the sun and environment methods.
_add_sun_to_scene(true);
}
@@ -6574,7 +6699,7 @@ void Node3DEditor::_add_environment_to_scene(bool p_already_added_sun) {
new_env->set_environment(preview_environment->get_environment()->duplicate(true));
undo_redo->create_action(TTR("Add Preview Environment to Scene"));
- undo_redo->add_do_method(base, "add_child", new_env);
+ undo_redo->add_do_method(base, "add_child", new_env, true);
// Move to the beginning of the scene tree since more "global" nodes
// generally look better when placed at the top.
undo_redo->add_do_method(base, "move_child", new_env, 0);
@@ -6688,8 +6813,8 @@ Vector<int> Node3DEditor::get_subgizmo_selection() {
Vector<int> ret;
if (se) {
- for (Map<int, Transform3D>::Element *E = se->subgizmos.front(); E; E = E->next()) {
- ret.push_back(E->key());
+ for (const KeyValue<int, Transform3D> &E : se->subgizmos) {
+ ret.push_back(E.key);
}
}
return ret;
@@ -6741,6 +6866,33 @@ void Node3DEditor::_request_gizmo(Object *p_obj) {
}
}
+void Node3DEditor::_set_subgizmo_selection(Object *p_obj, Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform) {
+ if (p_id == -1) {
+ _clear_subgizmo_selection(p_obj);
+ return;
+ }
+
+ Node3D *sp = nullptr;
+ if (p_obj) {
+ sp = Object::cast_to<Node3D>(p_obj);
+ } else {
+ sp = selected;
+ }
+
+ if (!sp) {
+ return;
+ }
+
+ Node3DEditorSelectedItem *se = editor_selection->get_node_editor_data<Node3DEditorSelectedItem>(sp);
+ if (se) {
+ se->subgizmos.clear();
+ se->subgizmos.insert(p_id, p_transform);
+ se->gizmo = p_gizmo;
+ sp->update_gizmos();
+ update_transform_gizmo();
+ }
+}
+
void Node3DEditor::_clear_subgizmo_selection(Object *p_obj) {
Node3D *sp = nullptr;
if (p_obj) {
@@ -6861,11 +7013,11 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<Camera3DGizmoPlugin>(memnew(Camera3DGizmoPlugin)));
add_gizmo_plugin(Ref<Light3DGizmoPlugin>(memnew(Light3DGizmoPlugin)));
add_gizmo_plugin(Ref<AudioStreamPlayer3DGizmoPlugin>(memnew(AudioStreamPlayer3DGizmoPlugin)));
+ add_gizmo_plugin(Ref<AudioListener3DGizmoPlugin>(memnew(AudioListener3DGizmoPlugin)));
add_gizmo_plugin(Ref<MeshInstance3DGizmoPlugin>(memnew(MeshInstance3DGizmoPlugin)));
add_gizmo_plugin(Ref<OccluderInstance3DGizmoPlugin>(memnew(OccluderInstance3DGizmoPlugin)));
- add_gizmo_plugin(Ref<SoftBody3DGizmoPlugin>(memnew(SoftBody3DGizmoPlugin)));
+ add_gizmo_plugin(Ref<SoftDynamicBody3DGizmoPlugin>(memnew(SoftDynamicBody3DGizmoPlugin)));
add_gizmo_plugin(Ref<Sprite3DGizmoPlugin>(memnew(Sprite3DGizmoPlugin)));
- add_gizmo_plugin(Ref<Skeleton3DGizmoPlugin>(memnew(Skeleton3DGizmoPlugin)));
add_gizmo_plugin(Ref<Position3DGizmoPlugin>(memnew(Position3DGizmoPlugin)));
add_gizmo_plugin(Ref<RayCast3DGizmoPlugin>(memnew(RayCast3DGizmoPlugin)));
add_gizmo_plugin(Ref<SpringArm3DGizmoPlugin>(memnew(SpringArm3DGizmoPlugin)));
@@ -6885,11 +7037,13 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<NavigationRegion3DGizmoPlugin>(memnew(NavigationRegion3DGizmoPlugin)));
add_gizmo_plugin(Ref<Joint3DGizmoPlugin>(memnew(Joint3DGizmoPlugin)));
add_gizmo_plugin(Ref<PhysicalBone3DGizmoPlugin>(memnew(PhysicalBone3DGizmoPlugin)));
+ add_gizmo_plugin(Ref<FogVolumeGizmoPlugin>(memnew(FogVolumeGizmoPlugin)));
}
void Node3DEditor::_bind_methods() {
ClassDB::bind_method("_get_editor_data", &Node3DEditor::_get_editor_data);
ClassDB::bind_method("_request_gizmo", &Node3DEditor::_request_gizmo);
+ ClassDB::bind_method("_set_subgizmo_selection", &Node3DEditor::_set_subgizmo_selection);
ClassDB::bind_method("_clear_subgizmo_selection", &Node3DEditor::_clear_subgizmo_selection);
ClassDB::bind_method("_refresh_menu_icons", &Node3DEditor::_refresh_menu_icons);
@@ -7011,7 +7165,7 @@ void Node3DEditor::_update_preview_environment() {
} else {
if (!preview_sun->get_parent()) {
- add_child(preview_sun);
+ add_child(preview_sun, true);
sun_state->hide();
sun_vb->show();
}
@@ -7047,7 +7201,7 @@ void Node3DEditor::_update_preview_environment() {
void Node3DEditor::_sun_direction_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
sun_rotation.x += mm->get_relative().y * (0.02 * EDSCALE);
sun_rotation.y -= mm->get_relative().x * (0.02 * EDSCALE);
sun_rotation.x = CLAMP(sun_rotation.x, -Math_TAU / 4, Math_TAU / 4);
@@ -7104,9 +7258,9 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_button[TOOL_MODE_SELECT]->set_pressed(true);
button_binds.write[0] = MENU_TOOL_SELECT;
tool_button[TOOL_MODE_SELECT]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
- tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), KEY_Q));
+ tool_button[TOOL_MODE_SELECT]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTR("Select Mode"), Key::Q));
tool_button[TOOL_MODE_SELECT]->set_shortcut_context(this);
- tool_button[TOOL_MODE_SELECT]->set_tooltip(keycode_get_string(KEY_MASK_CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
+ tool_button[TOOL_MODE_SELECT]->set_tooltip(keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Drag: Rotate selected node around pivot.") + "\n" + TTR("Alt+RMB: Show list of all nodes at position clicked, including locked."));
hbc_menu->add_child(memnew(VSeparator));
tool_button[TOOL_MODE_MOVE] = memnew(Button);
@@ -7115,7 +7269,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_button[TOOL_MODE_MOVE]->set_flat(true);
button_binds.write[0] = MENU_TOOL_MOVE;
tool_button[TOOL_MODE_MOVE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
- tool_button[TOOL_MODE_MOVE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_move", TTR("Move Mode"), KEY_W));
+ tool_button[TOOL_MODE_MOVE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_move", TTR("Move Mode"), Key::W));
tool_button[TOOL_MODE_MOVE]->set_shortcut_context(this);
tool_button[TOOL_MODE_ROTATE] = memnew(Button);
@@ -7124,7 +7278,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_button[TOOL_MODE_ROTATE]->set_flat(true);
button_binds.write[0] = MENU_TOOL_ROTATE;
tool_button[TOOL_MODE_ROTATE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
- tool_button[TOOL_MODE_ROTATE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Rotate Mode"), KEY_E));
+ tool_button[TOOL_MODE_ROTATE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_rotate", TTR("Rotate Mode"), Key::E));
tool_button[TOOL_MODE_ROTATE]->set_shortcut_context(this);
tool_button[TOOL_MODE_SCALE] = memnew(Button);
@@ -7133,7 +7287,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_button[TOOL_MODE_SCALE]->set_flat(true);
button_binds.write[0] = MENU_TOOL_SCALE;
tool_button[TOOL_MODE_SCALE]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
- tool_button[TOOL_MODE_SCALE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_scale", TTR("Scale Mode"), KEY_R));
+ tool_button[TOOL_MODE_SCALE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_scale", TTR("Scale Mode"), Key::R));
tool_button[TOOL_MODE_SCALE]->set_shortcut_context(this);
hbc_menu->add_child(memnew(VSeparator));
@@ -7153,7 +7307,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_button[TOOL_LOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
tool_button[TOOL_LOCK_SELECTED]->set_tooltip(TTR("Lock selected node, preventing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KEY_MASK_CMD | KEY_L));
+ tool_button[TOOL_LOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/lock_selected_nodes", TTR("Lock Selected Node(s)"), KeyModifierMask::CMD | Key::L));
tool_button[TOOL_UNLOCK_SELECTED] = memnew(Button);
hbc_menu->add_child(tool_button[TOOL_UNLOCK_SELECTED]);
@@ -7162,7 +7316,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock selected node, allowing selection and movement."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_L));
+ tool_button[TOOL_UNLOCK_SELECTED]->set_shortcut(ED_SHORTCUT("editor/unlock_selected_nodes", TTR("Unlock Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::L));
tool_button[TOOL_GROUP_SELECTED] = memnew(Button);
hbc_menu->add_child(tool_button[TOOL_GROUP_SELECTED]);
@@ -7171,7 +7325,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_button[TOOL_GROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
tool_button[TOOL_GROUP_SELECTED]->set_tooltip(TTR("Makes sure the object's children are not selectable."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KEY_MASK_CMD | KEY_G));
+ tool_button[TOOL_GROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/group_selected_nodes", TTR("Group Selected Node(s)"), KeyModifierMask::CMD | Key::G));
tool_button[TOOL_UNGROUP_SELECTED] = memnew(Button);
hbc_menu->add_child(tool_button[TOOL_UNGROUP_SELECTED]);
@@ -7180,7 +7334,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", callable_mp(this, &Node3DEditor::_menu_item_pressed), button_binds);
tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip(TTR("Restores the object's children's ability to be selected."));
// Define the shortcut globally (without a context) so that it works if the Scene tree dock is currently focused.
- tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G));
+ tool_button[TOOL_UNGROUP_SELECTED]->set_shortcut(ED_SHORTCUT("editor/ungroup_selected_nodes", TTR("Ungroup Selected Node(s)"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::G));
hbc_menu->add_child(memnew(VSeparator));
@@ -7190,7 +7344,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_flat(true);
button_binds.write[0] = MENU_TOOL_LOCAL_COORDS;
tool_option_button[TOOL_OPT_LOCAL_COORDS]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds);
- tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_shortcut(ED_SHORTCUT("spatial_editor/local_coords", TTR("Use Local Space"), KEY_T));
+ tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_shortcut(ED_SHORTCUT("spatial_editor/local_coords", TTR("Use Local Space"), Key::T));
tool_option_button[TOOL_OPT_LOCAL_COORDS]->set_shortcut_context(this);
tool_option_button[TOOL_OPT_USE_SNAP] = memnew(Button);
@@ -7199,7 +7353,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
tool_option_button[TOOL_OPT_USE_SNAP]->set_flat(true);
button_binds.write[0] = MENU_TOOL_USE_SNAP;
tool_option_button[TOOL_OPT_USE_SNAP]->connect("toggled", callable_mp(this, &Node3DEditor::_menu_item_toggled), button_binds);
- tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), KEY_Y));
+ tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut(ED_SHORTCUT("spatial_editor/snap", TTR("Use Snap"), Key::Y));
tool_option_button[TOOL_OPT_USE_SNAP]->set_shortcut_context(this);
hbc_menu->add_child(memnew(VSeparator));
@@ -7245,19 +7399,27 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
preview_node = memnew(Node3D);
preview_bounds = AABB();
- ED_SHORTCUT("spatial_editor/bottom_view", TTR("Bottom View"), KEY_MASK_ALT + KEY_KP_7);
- ED_SHORTCUT("spatial_editor/top_view", TTR("Top View"), KEY_KP_7);
- ED_SHORTCUT("spatial_editor/rear_view", TTR("Rear View"), KEY_MASK_ALT + KEY_KP_1);
- ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), KEY_KP_1);
- ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KEY_MASK_ALT + KEY_KP_3);
- ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), KEY_KP_3);
- ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal View"), KEY_KP_5);
- ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), KEY_K);
- ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), KEY_O);
- ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), KEY_F);
- ED_SHORTCUT("spatial_editor/align_transform_with_view", TTR("Align Transform with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_M);
- ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_F);
- ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KEY_MASK_SHIFT + KEY_F);
+ ED_SHORTCUT("spatial_editor/bottom_view", TTR("Bottom View"), KeyModifierMask::ALT + Key::KP_7);
+ ED_SHORTCUT("spatial_editor/top_view", TTR("Top View"), Key::KP_7);
+ ED_SHORTCUT("spatial_editor/rear_view", TTR("Rear View"), KeyModifierMask::ALT + Key::KP_1);
+ ED_SHORTCUT("spatial_editor/front_view", TTR("Front View"), Key::KP_1);
+ ED_SHORTCUT("spatial_editor/left_view", TTR("Left View"), KeyModifierMask::ALT + Key::KP_3);
+ ED_SHORTCUT("spatial_editor/right_view", TTR("Right View"), Key::KP_3);
+ ED_SHORTCUT("spatial_editor/orbit_view_down", TTR("Orbit View Down"), Key::KP_2);
+ ED_SHORTCUT("spatial_editor/orbit_view_left", TTR("Orbit View Left"), Key::KP_4);
+ ED_SHORTCUT("spatial_editor/orbit_view_right", TTR("Orbit View Right"), Key::KP_6);
+ ED_SHORTCUT("spatial_editor/orbit_view_up", TTR("Orbit View Up"), Key::KP_8);
+ ED_SHORTCUT("spatial_editor/orbit_view_180", TTR("Orbit View 180"), Key::KP_9);
+ ED_SHORTCUT("spatial_editor/switch_perspective_orthogonal", TTR("Switch Perspective/Orthogonal View"), Key::KP_5);
+ ED_SHORTCUT("spatial_editor/insert_anim_key", TTR("Insert Animation Key"), Key::K);
+ ED_SHORTCUT("spatial_editor/focus_origin", TTR("Focus Origin"), Key::O);
+ ED_SHORTCUT("spatial_editor/focus_selection", TTR("Focus Selection"), Key::F);
+ ED_SHORTCUT("spatial_editor/align_transform_with_view", TTR("Align Transform with View"), KeyModifierMask::ALT + KeyModifierMask::CMD + Key::M);
+ ED_SHORTCUT("spatial_editor/align_rotation_with_view", TTR("Align Rotation with View"), KeyModifierMask::ALT + KeyModifierMask::CMD + Key::F);
+ ED_SHORTCUT("spatial_editor/freelook_toggle", TTR("Toggle Freelook"), KeyModifierMask::SHIFT + Key::F);
+ ED_SHORTCUT("spatial_editor/decrease_fov", TTR("Decrease Field of View"), KeyModifierMask::CMD + Key::EQUAL); // Usually direct access key for `KEY_PLUS`.
+ ED_SHORTCUT("spatial_editor/increase_fov", TTR("Increase Field of View"), KeyModifierMask::CMD + Key::MINUS);
+ ED_SHORTCUT("spatial_editor/reset_fov", TTR("Reset Field of View to Default"), KeyModifierMask::CMD + Key::KEY_0);
PopupMenu *p;
@@ -7268,7 +7430,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
hbc_menu->add_child(transform_menu);
p = transform_menu->get_popup();
- p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), KEY_PAGEDOWN), MENU_SNAP_TO_FLOOR);
+ p->add_shortcut(ED_SHORTCUT("spatial_editor/snap_to_floor", TTR("Snap Object to Floor"), Key::PAGEDOWN), MENU_SNAP_TO_FLOOR);
p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG);
p->add_separator();
@@ -7300,19 +7462,19 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
accept = memnew(AcceptDialog);
editor->get_gui_base()->add_child(accept);
- p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KEY_MASK_CMD + KEY_1), MENU_VIEW_USE_1_VIEWPORT);
- p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
- p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
- p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
- p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KEY_MASK_ALT + KEY_MASK_CMD + KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
- p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KEY_MASK_CMD + KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/1_viewport", TTR("1 Viewport"), KeyModifierMask::CMD + Key::KEY_1), MENU_VIEW_USE_1_VIEWPORT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports", TTR("2 Viewports"), KeyModifierMask::CMD + Key::KEY_2), MENU_VIEW_USE_2_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/2_viewports_alt", TTR("2 Viewports (Alt)"), KeyModifierMask::ALT + KeyModifierMask::CMD + Key::KEY_2), MENU_VIEW_USE_2_VIEWPORTS_ALT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports", TTR("3 Viewports"), KeyModifierMask::CMD + Key::KEY_3), MENU_VIEW_USE_3_VIEWPORTS);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/3_viewports_alt", TTR("3 Viewports (Alt)"), KeyModifierMask::ALT + KeyModifierMask::CMD + Key::KEY_3), MENU_VIEW_USE_3_VIEWPORTS_ALT);
+ p->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/4_viewports", TTR("4 Viewports"), KeyModifierMask::CMD + Key::KEY_4), MENU_VIEW_USE_4_VIEWPORTS);
p->add_separator();
p->add_submenu_item(TTR("Gizmos"), "GizmosMenu");
p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_origin", TTR("View Origin")), MENU_VIEW_ORIGIN);
- p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid"), KEY_NUMBERSIGN), MENU_VIEW_GRID);
+ p->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_grid", TTR("View Grid"), Key::NUMBERSIGN), MENU_VIEW_GRID);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("spatial_editor/settings", TTR("Settings...")), MENU_VIEW_CAMERA_SETTINGS);
@@ -7405,7 +7567,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
settings_vbc->add_margin_child(TTR("View Z-Far:"), settings_zfar);
for (uint32_t i = 0; i < VIEWPORTS_COUNT; ++i) {
- settings_dialog->connect("confirmed", callable_mp(viewports[i], &Node3DEditorViewport::_update_camera), varray(0.0));
+ settings_dialog->connect("confirmed", callable_mp(viewports[i], &Node3DEditorViewport::_view_settings_confirmed), varray(0.0));
}
/* XFORM DIALOG */
@@ -7499,7 +7661,7 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) {
sun_title->set_theme_type_variation("HeaderSmall");
sun_vb->add_child(sun_title);
sun_title->set_text(TTR("Preview Sun"));
- sun_title->set_align(Label::ALIGN_CENTER);
+ sun_title->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
CenterContainer *sun_direction_center = memnew(CenterContainer);
sun_direction = memnew(Control);
@@ -7587,8 +7749,8 @@ void fragment() {
sun_state = memnew(Label);
sun_environ_hb->add_child(sun_state);
- sun_state->set_align(Label::ALIGN_CENTER);
- sun_state->set_valign(Label::VALIGN_CENTER);
+ sun_state->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ sun_state->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
sun_state->set_h_size_flags(SIZE_EXPAND_FILL);
VSeparator *sc = memnew(VSeparator);
@@ -7606,7 +7768,7 @@ void fragment() {
environ_vb->add_child(environ_title);
environ_title->set_text(TTR("Preview Environment"));
- environ_title->set_align(Label::ALIGN_CENTER);
+ environ_title->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
environ_sky_color = memnew(ColorPickerButton);
environ_sky_color->set_edit_alpha(false);
@@ -7654,8 +7816,8 @@ void fragment() {
environ_state = memnew(Label);
sun_environ_hb->add_child(environ_state);
- environ_state->set_align(Label::ALIGN_CENTER);
- environ_state->set_valign(Label::VALIGN_CENTER);
+ environ_state->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ environ_state->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
environ_state->set_h_size_flags(SIZE_EXPAND_FILL);
preview_sun = memnew(DirectionalLight3D);
@@ -7716,9 +7878,16 @@ Vector3 Node3DEditor::snap_point(Vector3 p_target, Vector3 p_start) const {
return p_target;
}
+bool Node3DEditor::is_gizmo_visible() const {
+ if (selected) {
+ return gizmo.visible && selected->is_transform_gizmo_visible();
+ }
+ return gizmo.visible;
+}
+
double Node3DEditor::get_translate_snap() const {
double snap_value;
- if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
snap_value = snap_translate->get_text().to_float() / 10.0;
} else {
snap_value = snap_translate->get_text().to_float();
@@ -7729,7 +7898,7 @@ double Node3DEditor::get_translate_snap() const {
double Node3DEditor::get_rotate_snap() const {
double snap_value;
- if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
snap_value = snap_rotate->get_text().to_float() / 3.0;
} else {
snap_value = snap_rotate->get_text().to_float();
@@ -7740,7 +7909,7 @@ double Node3DEditor::get_rotate_snap() const {
double Node3DEditor::get_scale_snap() const {
double snap_value;
- if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
snap_value = snap_scale->get_text().to_float() / 2.0;
} else {
snap_value = snap_scale->get_text().to_float();
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 59f3ec6fcd..8d647808ba 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -41,6 +41,7 @@
#include "scene/3d/world_environment.h"
#include "scene/gui/panel_container.h"
#include "scene/resources/environment.h"
+#include "scene/resources/fog_material.h"
#include "scene/resources/sky_material.h"
class Node3DEditor;
@@ -315,7 +316,7 @@ private:
struct Cursor {
Vector3 pos;
- real_t x_rot, y_rot, distance;
+ real_t x_rot, y_rot, distance, fov_scale;
Vector3 eye_pos; // Used in freelook mode
bool region_select;
Point2 region_begin, region_end;
@@ -325,6 +326,7 @@ private:
x_rot = 0.5;
y_rot = -0.5;
distance = 4;
+ fov_scale = 1.0;
region_select = false;
}
};
@@ -333,6 +335,8 @@ private:
Cursor cursor; // Immediate cursor
Cursor camera_cursor; // That one may be interpolated (don't modify this one except for smoothing purposes)
+ void scale_fov(real_t p_fov_offset);
+ void reset_fov();
void scale_cursor_distance(real_t scale);
void set_freelook_active(bool active_now);
@@ -349,6 +353,7 @@ private:
void set_message(String p_message, float p_time = 5);
+ void _view_settings_confirmed(real_t p_interp_delta);
void _update_camera(real_t p_interp_delta);
Transform3D to_camera_transform(const Cursor &p_cursor) const;
void _draw();
@@ -431,7 +436,9 @@ public:
bool last_xform_dirty;
Node3D *sp;
RID sbox_instance;
+ RID sbox_instance_offset;
RID sbox_instance_xray;
+ RID sbox_instance_xray_offset;
Ref<EditorNode3DGizmo> gizmo;
Map<int, Transform3D> subgizmos; // map ID -> initial transform
@@ -663,6 +670,7 @@ private:
Node3D *selected;
void _request_gizmo(Object *p_obj);
+ void _set_subgizmo_selection(Object *p_obj, Ref<Node3DGizmo> p_gizmo, int p_id, Transform3D p_transform = Transform3D());
void _clear_subgizmo_selection(Object *p_obj = nullptr);
static Node3DEditor *singleton;
@@ -674,8 +682,6 @@ private:
void _register_all_gizmos();
- Node3DEditor();
-
void _selection_changed();
void _refresh_menu_icons();
@@ -756,7 +762,7 @@ public:
float get_fov() const { return settings_fov->get_value(); }
Transform3D get_gizmo_transform() const { return gizmo.transform; }
- bool is_gizmo_visible() const { return gizmo.visible; }
+ bool is_gizmo_visible() const;
ToolMode get_tool_mode() const { return tool_mode; }
bool are_local_coords_enabled() const { return tool_option_button[Node3DEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); }
diff --git a/editor/plugins/occluder_instance_3d_editor_plugin.cpp b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
index ab88b9f00d..1e85b19a84 100644
--- a/editor/plugins/occluder_instance_3d_editor_plugin.cpp
+++ b/editor/plugins/occluder_instance_3d_editor_plugin.cpp
@@ -41,11 +41,11 @@ void OccluderInstance3DEditorPlugin::_bake_select_file(const String &p_file) {
switch (err) {
case OccluderInstance3D::BAKE_ERROR_NO_SAVE_PATH: {
- String scene_path = occluder_instance->get_filename();
- if (scene_path == String()) {
- scene_path = occluder_instance->get_owner()->get_filename();
+ String scene_path = occluder_instance->get_scene_file_path();
+ if (scene_path.is_empty()) {
+ scene_path = occluder_instance->get_owner()->get_scene_file_path();
}
- if (scene_path == String()) {
+ if (scene_path.is_empty()) {
EditorNode::get_singleton()->show_warning(TTR("Can't determine a save path for the occluder.\nSave your scene and try again."));
break;
}
diff --git a/editor/plugins/ot_features_plugin.cpp b/editor/plugins/ot_features_plugin.cpp
index fd42bce06e..7f3ebc01d5 100644
--- a/editor/plugins/ot_features_plugin.cpp
+++ b/editor/plugins/ot_features_plugin.cpp
@@ -136,6 +136,7 @@ void OpenTypeFeaturesAdd::update_property() {
void OpenTypeFeaturesAdd::_features_menu() {
Size2 size = get_size();
menu->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
+ menu->reset_size();
menu->popup();
}
@@ -185,12 +186,6 @@ bool EditorInspectorPluginOpenTypeFeatures::can_handle(Object *p_object) {
return (Object::cast_to<Control>(p_object) != nullptr);
}
-void EditorInspectorPluginOpenTypeFeatures::parse_begin(Object *p_object) {
-}
-
-void EditorInspectorPluginOpenTypeFeatures::parse_category(Object *p_object, const String &p_parse_category) {
-}
-
bool EditorInspectorPluginOpenTypeFeatures::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 == "opentype_features/_new") {
OpenTypeFeaturesAdd *editor = memnew(OpenTypeFeaturesAdd);
diff --git a/editor/plugins/ot_features_plugin.h b/editor/plugins/ot_features_plugin.h
index dbafa3bbf6..add491ed48 100644
--- a/editor/plugins/ot_features_plugin.h
+++ b/editor/plugins/ot_features_plugin.h
@@ -86,8 +86,6 @@ class EditorInspectorPluginOpenTypeFeatures : public EditorInspectorPlugin {
public:
virtual bool can_handle(Object *p_object) override;
- virtual void parse_begin(Object *p_object) override;
- virtual void parse_category(Object *p_object, const String &p_parse_category) override;
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;
};
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp
index 0a949c8610..53c5b8dd70 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.cpp
+++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp
@@ -31,6 +31,7 @@
#include "packed_scene_translation_parser_plugin.h"
#include "core/io/resource_loader.h"
+#include "scene/gui/option_button.h"
#include "scene/resources/packed_scene.h"
void PackedSceneEditorTranslationParserPlugin::get_recognized_extensions(List<String> *r_extensions) const {
@@ -50,21 +51,31 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
Ref<SceneState> state = Ref<PackedScene>(loaded_res)->get_state();
Vector<String> parsed_strings;
- String property_name;
- Variant property_value;
for (int i = 0; i < state->get_node_count(); i++) {
- if (!ClassDB::is_parent_class(state->get_node_type(i), "Control") && !ClassDB::is_parent_class(state->get_node_type(i), "Viewport")) {
+ String node_type = state->get_node_type(i);
+ if (!ClassDB::is_parent_class(node_type, "Control") && !ClassDB::is_parent_class(node_type, "Window")) {
continue;
}
+ // Find the `auto_translate` property, and abort the string parsing of the node if disabled.
+ bool auto_translating = true;
for (int j = 0; j < state->get_node_property_count(i); j++) {
- property_name = state->get_node_property_name(i, j);
- if (!lookup_properties.has(property_name)) {
- continue;
+ if (state->get_node_property_name(i, j) == "auto_translate" && (bool)state->get_node_property_value(i, j) == false) {
+ auto_translating = false;
+ break;
}
+ }
+ if (!auto_translating) {
+ continue;
+ }
- property_value = state->get_node_property_value(i, j);
+ for (int j = 0; j < state->get_node_property_count(i); j++) {
+ String property_name = state->get_node_property_name(i, j);
+ if (!lookup_properties.has(property_name) || (exception_list.has(node_type) && exception_list[node_type].has(property_name))) {
+ continue;
+ }
+ Variant property_value = state->get_node_property_value(i, j);
if (property_name == "script" && property_value.get_type() == Variant::OBJECT && !property_value.is_null()) {
// Parse built-in script.
Ref<Script> s = Object::cast_to<Script>(property_value);
@@ -76,7 +87,16 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
parsed_strings.append_array(temp);
r_ids_ctx_plural->append_array(ids_context_plural);
}
- } else if (property_name == "filters") {
+ } else if ((node_type == "MenuButton" || node_type == "OptionButton") && property_name == "items") {
+ Vector<String> str_values = property_value;
+ int incr_value = node_type == "MenuButton" ? PopupMenu::ITEM_PROPERTY_SIZE : OptionButton::ITEM_PROPERTY_SIZE;
+ for (int k = 0; k < str_values.size(); k += incr_value) {
+ String desc = str_values[k].get_slice(";", 1).strip_edges();
+ if (!desc.is_empty()) {
+ parsed_strings.push_back(desc);
+ }
+ }
+ } else if (node_type == "FileDialog" && property_name == "filters") {
// Extract FileDialog's filters property with values in format "*.png ; PNG Images","*.gd ; GDScript Files".
Vector<String> str_values = property_value;
for (int k = 0; k < str_values.size(); k++) {
@@ -105,12 +125,17 @@ PackedSceneEditorTranslationParserPlugin::PackedSceneEditorTranslationParserPlug
lookup_properties.insert("text");
lookup_properties.insert("hint_tooltip");
lookup_properties.insert("placeholder_text");
+ lookup_properties.insert("items");
+ lookup_properties.insert("title");
lookup_properties.insert("dialog_text");
lookup_properties.insert("filters");
lookup_properties.insert("script");
- //Add exception list (to prevent false positives)
- //line edit, text edit, richtextlabel
- //Set<String> exception_list;
- //exception_list.insert("RichTextLabel");
+ // Exception list (to prevent false positives).
+ exception_list.insert("LineEdit", Vector<StringName>());
+ exception_list["LineEdit"].append("text");
+ exception_list.insert("TextEdit", Vector<StringName>());
+ exception_list["TextEdit"].append("text");
+ exception_list.insert("CodeEdit", Vector<StringName>());
+ exception_list["CodeEdit"].append("text");
}
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.h b/editor/plugins/packed_scene_translation_parser_plugin.h
index e51d65414e..af0291b69c 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.h
+++ b/editor/plugins/packed_scene_translation_parser_plugin.h
@@ -37,7 +37,9 @@ class PackedSceneEditorTranslationParserPlugin : public EditorTranslationParserP
GDCLASS(PackedSceneEditorTranslationParserPlugin, EditorTranslationParserPlugin);
// Scene Node's properties that contain translation strings.
- Set<String> lookup_properties;
+ Set<StringName> lookup_properties;
+ // Properties from specific Nodes that should be ignored.
+ Map<StringName, Vector<StringName>> exception_list;
public:
virtual Error parse_file(const String &p_path, Vector<String> *r_ids, Vector<Vector<String>> *r_ids_ctx_plural) override;
diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp
index 119ecddf63..79f8ce95cd 100644
--- a/editor/plugins/path_2d_editor_plugin.cpp
+++ b/editor/plugins/path_2d_editor_plugin.cpp
@@ -88,7 +88,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
real_t dist_to_p_in = gpoint.distance_to(xform.xform(curve->get_point_position(i) + curve->get_point_in(i)));
// Check for point movement start (for point + in/out controls).
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mode == MODE_EDIT && !mb->is_shift_pressed() && dist_to_p < grab_threshold) {
// Points can only be moved in edit mode.
@@ -118,7 +118,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
// Check for point deletion.
- if ((mb->get_button_index() == MOUSE_BUTTON_RIGHT && mode == MODE_EDIT) || (mb->get_button_index() == MOUSE_BUTTON_LEFT && mode == MODE_DELETE)) {
+ if ((mb->get_button_index() == MouseButton::RIGHT && mode == MODE_EDIT) || (mb->get_button_index() == MouseButton::LEFT && mode == MODE_DELETE)) {
if (dist_to_p < grab_threshold) {
undo_redo->create_action(TTR("Remove Point from Curve"));
undo_redo->add_do_method(curve.ptr(), "remove_point", i);
@@ -149,7 +149,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
// Check for point creation.
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT && ((mb->is_command_pressed() && mode == MODE_EDIT) || mode == MODE_CREATE)) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && ((mb->is_command_pressed() && mode == MODE_EDIT) || mode == MODE_CREATE)) {
Ref<Curve2D> curve = node->get_curve();
undo_redo->create_action(TTR("Add Point to Curve"));
@@ -170,7 +170,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
// Check for segment split.
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT && mode == MODE_EDIT && on_edge) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && mode == MODE_EDIT && on_edge) {
Vector2 gpoint2 = mb->get_position();
Ref<Curve2D> curve = node->get_curve();
@@ -207,7 +207,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
}
// Check for point movement completion.
- if (!mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT && action != ACTION_NONE) {
+ if (!mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && action != ACTION_NONE) {
Ref<Curve2D> curve = node->get_curve();
Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
@@ -537,7 +537,7 @@ Path2DEditor::Path2DEditor(EditorNode *p_editor) {
curve_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveEdit"), SNAME("EditorIcons")));
curve_edit->set_toggle_mode(true);
curve_edit->set_focus_mode(Control::FOCUS_NONE);
- curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Click: Add Point") + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point"));
+ curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Click: Add Point") + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point"));
curve_edit->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected), varray(MODE_EDIT));
base_hb->add_child(curve_edit);
curve_edit_curve = memnew(Button);
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 13f7908170..e83f6481f9 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -101,7 +101,7 @@ void Path3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point
// Setting curve point positions
if (p_id < c->get_point_count()) {
- Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2));
+ const Plane p = Plane(p_camera->get_transform().basis.get_axis(2), gt.xform(original));
Vector3 inters;
@@ -125,7 +125,7 @@ void Path3DGizmo::set_handle(int p_id, Camera3D *p_camera, const Point2 &p_point
Vector3 base = c->get_point_position(idx);
- Plane p(gt.xform(original), p_camera->get_transform().basis.get_axis(2));
+ Plane p(p_camera->get_transform().basis.get_axis(2), gt.xform(original));
Vector3 inters;
@@ -294,13 +294,13 @@ Path3DGizmo::Path3DGizmo(Path3D *p_path) {
orig_out_length = 0;
}
-bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+EditorPlugin::AfterGUIInput Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!path) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Ref<Curve3D> c = path->get_curve();
if (c.is_null()) {
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
Transform3D gt = path->get_global_transform();
Transform3D it = gt.affine_inverse();
@@ -316,7 +316,7 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
set_handle_clicked(false);
}
- if (mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb->is_ctrl_pressed()))) {
+ if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && (curve_create->is_pressed() || (curve_edit->is_pressed() && mb->is_ctrl_pressed()))) {
//click into curve, break it down
Vector<Vector3> v3a = c->tessellate();
int idx = 0;
@@ -329,14 +329,14 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
const Vector3 *r = v3a.ptr();
if (p_camera->unproject_position(gt.xform(c->get_point_position(0))).distance_to(mbpos) < click_dist) {
- return false; //nope, existing
+ return EditorPlugin::AFTER_GUI_INPUT_PASS; //nope, existing
}
for (int i = 0; i < c->get_point_count() - 1; i++) {
//find the offset and point index of the place to break up
int j = idx;
if (p_camera->unproject_position(gt.xform(c->get_point_position(i + 1))).distance_to(mbpos) < click_dist) {
- return false; //nope, existing
+ return EditorPlugin::AFTER_GUI_INPUT_PASS; //nope, existing
}
while (j < rc && c->get_point_position(i + 1) != r[j]) {
@@ -386,16 +386,16 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "add_point", closest_seg_point, Vector3(), Vector3(), closest_seg + 1);
ur->add_undo_method(c.ptr(), "remove_point", closest_seg + 1);
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else {
- Vector3 org;
+ Vector3 origin;
if (c->get_point_count() == 0) {
- org = path->get_transform().get_origin();
+ origin = path->get_transform().get_origin();
} else {
- org = gt.xform(c->get_point_position(c->get_point_count() - 1));
+ origin = gt.xform(c->get_point_position(c->get_point_count() - 1));
}
- Plane p(org, p_camera->get_transform().basis.get_axis(2));
+ Plane p(p_camera->get_transform().basis.get_axis(2), origin);
Vector3 ray_from = p_camera->project_ray_origin(mbpos);
Vector3 ray_dir = p_camera->project_ray_normal(mbpos);
@@ -405,13 +405,13 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "add_point", it.xform(inters), Vector3(), Vector3(), -1);
ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
//add new at pos
}
- } else if (mb->is_pressed() && ((mb->get_button_index() == MOUSE_BUTTON_LEFT && curve_del->is_pressed()) || (mb->get_button_index() == MOUSE_BUTTON_RIGHT && curve_edit->is_pressed()))) {
+ } else if (mb->is_pressed() && ((mb->get_button_index() == MouseButton::LEFT && curve_del->is_pressed()) || (mb->get_button_index() == MouseButton::RIGHT && curve_edit->is_pressed()))) {
for (int i = 0; i < c->get_point_count(); i++) {
real_t dist_to_p = p_camera->unproject_position(gt.xform(c->get_point_position(i))).distance_to(mbpos);
real_t dist_to_p_out = p_camera->unproject_position(gt.xform(c->get_point_position(i) + c->get_point_out(i))).distance_to(mbpos);
@@ -425,27 +425,27 @@ bool Path3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref
ur->add_do_method(c.ptr(), "remove_point", i);
ur->add_undo_method(c.ptr(), "add_point", c->get_point_position(i), c->get_point_in(i), c->get_point_out(i), i);
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (dist_to_p_out < click_dist) {
UndoRedo *ur = editor->get_undo_redo();
ur->create_action(TTR("Remove Out-Control Point"));
ur->add_do_method(c.ptr(), "set_point_out", i, Vector3());
ur->add_undo_method(c.ptr(), "set_point_out", i, c->get_point_out(i));
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (dist_to_p_in < click_dist) {
UndoRedo *ur = editor->get_undo_redo();
ur->create_action(TTR("Remove In-Control Point"));
ur->add_do_method(c.ptr(), "set_point_in", i, Vector3());
ur->add_undo_method(c.ptr(), "set_point_in", i, c->get_point_in(i));
ur->commit_action();
- return true;
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
}
}
}
}
- return false;
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
void Path3DEditorPlugin::edit(Object *p_object) {
@@ -510,7 +510,14 @@ void Path3DEditorPlugin::_close_curve() {
if (c->get_point_count() < 2) {
return;
}
- c->add_point(c->get_point_position(0), c->get_point_in(0), c->get_point_out(0));
+ if (c->get_point_position(0) == c->get_point_position(c->get_point_count() - 1)) {
+ return;
+ }
+ UndoRedo *ur = editor->get_undo_redo();
+ ur->create_action(TTR("Close Curve"));
+ ur->add_do_method(c.ptr(), "add_point", c->get_point_position(0), c->get_point_in(0), c->get_point_out(0), -1);
+ ur->add_undo_method(c.ptr(), "remove_point", c->get_point_count());
+ ur->commit_action();
}
void Path3DEditorPlugin::_handle_option_pressed(int p_option) {
@@ -566,7 +573,7 @@ Path3DEditorPlugin::Path3DEditorPlugin(EditorNode *p_node) {
curve_edit->set_toggle_mode(true);
curve_edit->hide();
curve_edit->set_focus_mode(Control::FOCUS_NONE);
- curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string(KEY_MASK_CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point"));
+ curve_edit->set_tooltip(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + keycode_get_string((Key)KeyModifierMask::CMD) + TTR("Click: Add Point") + "\n" + TTR("Right Click: Delete Point"));
Node3DEditor::get_singleton()->add_control_to_menu_panel(curve_edit);
curve_create = memnew(Button);
curve_create->set_flat(true);
@@ -607,15 +614,6 @@ Path3DEditorPlugin::Path3DEditorPlugin(EditorNode *p_node) {
menu->connect("id_pressed", callable_mp(this, &Path3DEditorPlugin::_handle_option_pressed));
curve_edit->set_pressed(true);
- /*
- collision_polygon_editor = memnew( PathEditor(p_node) );
- editor->get_main_control()->add_child(collision_polygon_editor);
- collision_polygon_editor->set_margin(MARGIN_LEFT,200);
- collision_polygon_editor->set_margin(MARGIN_RIGHT,230);
- collision_polygon_editor->set_margin(MARGIN_TOP,0);
- collision_polygon_editor->set_margin(MARGIN_BOTTOM,10);
- collision_polygon_editor->hide();
- */
}
Path3DEditorPlugin::~Path3DEditorPlugin() {
diff --git a/editor/plugins/path_3d_editor_plugin.h b/editor/plugins/path_3d_editor_plugin.h
index b74d7cc59e..974234ba8f 100644
--- a/editor/plugins/path_3d_editor_plugin.h
+++ b/editor/plugins/path_3d_editor_plugin.h
@@ -100,7 +100,7 @@ public:
Path3D *get_edited_path() { return path; }
static Path3DEditorPlugin *singleton;
- virtual bool forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
virtual String get_name() const override { return "Path3D"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.cpp b/editor/plugins/physical_bone_3d_editor_plugin.cpp
index f92f50f826..71955cf62c 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.cpp
+++ b/editor/plugins/physical_bone_3d_editor_plugin.cpp
@@ -43,6 +43,7 @@ void PhysicalBone3DEditor::_on_toggle_button_transform_joint(bool p_is_pressed)
void PhysicalBone3DEditor::_set_move_joint() {
if (selected) {
selected->_set_gizmo_move_joint(button_transform_joint->is_pressed());
+ Node3DEditor::get_singleton()->update_transform_gizmo();
}
}
@@ -50,7 +51,7 @@ PhysicalBone3DEditor::PhysicalBone3DEditor(EditorNode *p_editor) :
editor(p_editor) {
spatial_editor_hb = memnew(HBoxContainer);
spatial_editor_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- spatial_editor_hb->set_alignment(BoxContainer::ALIGN_BEGIN);
+ spatial_editor_hb->set_alignment(BoxContainer::ALIGNMENT_BEGIN);
Node3DEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb);
spatial_editor_hb->add_child(memnew(VSeparator));
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 782152b002..affcab9e7d 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -170,7 +170,7 @@ void Polygon2DEditor::_update_bone_list() {
if (np.get_name_count()) {
name = np.get_name(np.get_name_count() - 1);
}
- if (name == String()) {
+ if (name.is_empty()) {
name = "Bone " + itos(i);
}
cb->set_text(name);
@@ -447,9 +447,9 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
- uv_drag_from = snap_point(Vector2(mb->get_position().x, mb->get_position().y));
+ uv_drag_from = snap_point(mb->get_position());
uv_drag = true;
points_prev = node->get_uv();
@@ -463,7 +463,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_move_current == UV_MODE_CREATE) {
if (!uv_create) {
points_prev.resize(0);
- Vector2 tuv = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y)));
+ Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position()));
points_prev.push_back(tuv);
uv_create_to = tuv;
point_drag_index = 0;
@@ -483,7 +483,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_edit_draw->update();
} else {
- Vector2 tuv = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y)));
+ Vector2 tuv = mtx.affine_inverse().xform(snap_point(mb->get_position()));
// Close the polygon if selected point is near start. Threshold for closing scaled by zoom level
if (points_prev.size() > 2 && tuv.distance_to(points_prev[0]) < (8 / uv_draw_zoom)) {
@@ -527,7 +527,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_create_bones_prev = node->call("_get_bones");
int internal_vertices = node->get_internal_vertex_count();
- Vector2 pos = mtx.affine_inverse().xform(snap_point(Vector2(mb->get_position().x, mb->get_position().y)));
+ Vector2 pos = mtx.affine_inverse().xform(snap_point(mb->get_position()));
uv_create_poly_prev.push_back(pos);
uv_create_uv_prev.push_back(pos);
@@ -573,7 +573,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
for (int i = points_prev.size() - internal_vertices; i < points_prev.size(); i++) {
Vector2 tuv = mtx.xform(uv_create_poly_prev[i]);
- real_t dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y));
+ real_t dist = tuv.distance_to(mb->get_position());
if (dist < 8 && dist < closest_dist) {
closest = i;
closest_dist = dist;
@@ -584,10 +584,10 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
return;
}
- uv_create_poly_prev.remove(closest);
- uv_create_uv_prev.remove(closest);
+ uv_create_poly_prev.remove_at(closest);
+ uv_create_uv_prev.remove_at(closest);
if (uv_create_colors_prev.size()) {
- uv_create_colors_prev.remove(closest);
+ uv_create_colors_prev.remove_at(closest);
}
undo_redo->create_action(TTR("Remove Internal Vertex"));
@@ -599,7 +599,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
undo_redo->add_undo_method(node, "set_vertex_colors", node->get_vertex_colors());
for (int i = 0; i < node->get_bone_count(); i++) {
Vector<float> bonew = node->get_bone_weights(i);
- bonew.remove(closest);
+ bonew.remove_at(closest);
undo_redo->add_do_method(node, "set_bone_weights", i, bonew);
undo_redo->add_undo_method(node, "set_bone_weights", i, node->get_bone_weights(i));
}
@@ -626,7 +626,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
point_drag_index = -1;
for (int i = 0; i < points_prev.size(); i++) {
Vector2 tuv = mtx.xform(points_prev[i]);
- if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) {
+ if (tuv.distance_to(mb->get_position()) < 8) {
uv_drag_from = tuv;
point_drag_index = i;
}
@@ -643,7 +643,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
for (int i = 0; i < points_prev.size(); i++) {
Vector2 tuv = mtx.xform(points_prev[i]);
- real_t dist = tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y));
+ real_t dist = tuv.distance_to(mb->get_position());
if (dist < 8 && dist < closest_dist) {
closest = i;
closest_dist = dist;
@@ -695,14 +695,14 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
polys.write[j] = mtx.xform(points_prev[idx]);
}
- if (Geometry2D::is_point_in_polygon(Vector2(mb->get_position().x, mb->get_position().y), polys)) {
+ if (Geometry2D::is_point_in_polygon(mb->get_position(), polys)) {
erase_index = i;
break;
}
}
if (erase_index != -1) {
- polygons.remove(erase_index);
+ polygons.remove_at(erase_index);
undo_redo->create_action(TTR("Remove Custom Polygon"));
undo_redo->add_do_method(node, "set_polygons", polygons);
undo_redo->add_undo_method(node, "set_polygons", node->get_polygons());
@@ -759,7 +759,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
bone_painting = false;
}
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
_cancel_editing();
if (bone_painting) {
@@ -768,9 +768,9 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_edit_draw->update();
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
uv_zoom->set_value(uv_zoom->get_value() / (1 - (0.1 * mb->get_factor())));
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) {
uv_zoom->set_value(uv_zoom->get_value() * (1 - (0.1 * mb->get_factor())));
}
}
@@ -778,8 +778,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
Ref<InputEventMouseMotion> mm = p_input;
if (mm.is_valid()) {
- if ((mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE) || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
- Vector2 drag(mm->get_relative().x, mm->get_relative().y);
+ if ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || Input::get_singleton()->is_key_pressed(Key::SPACE)) {
+ Vector2 drag = mm->get_relative();
uv_hscroll->set_value(uv_hscroll->get_value() - drag.x);
uv_vscroll->set_value(uv_vscroll->get_value() - drag.y);
@@ -791,7 +791,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
switch (uv_move_current) {
case UV_MODE_CREATE: {
if (uv_create) {
- uv_create_to = mtx.affine_inverse().xform(snap_point(Vector2(mm->get_position().x, mm->get_position().y)));
+ uv_create_to = mtx.affine_inverse().xform(snap_point(mm->get_position()));
}
} break;
case UV_MODE_EDIT_POINT: {
@@ -870,7 +870,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
} break;
case UV_MODE_PAINT_WEIGHT:
case UV_MODE_CLEAR_WEIGHT: {
- bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y);
+ bone_paint_pos = mm->get_position();
} break;
default: {
}
@@ -905,10 +905,10 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_edit_draw->update();
CanvasItemEditor::get_singleton()->update_viewport();
} else if (polygon_create.size()) {
- uv_create_to = mtx.affine_inverse().xform(Vector2(mm->get_position().x, mm->get_position().y));
+ uv_create_to = mtx.affine_inverse().xform(mm->get_position());
uv_edit_draw->update();
} else if (uv_mode == UV_MODE_PAINT_WEIGHT || uv_mode == UV_MODE_CLEAR_WEIGHT) {
- bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y);
+ bone_paint_pos = mm->get_position();
uv_edit_draw->update();
}
}
diff --git a/editor/plugins/resource_preloader_editor_plugin.cpp b/editor/plugins/resource_preloader_editor_plugin.cpp
index eae6916a92..30b7a26aab 100644
--- a/editor/plugins/resource_preloader_editor_plugin.cpp
+++ b/editor/plugins/resource_preloader_editor_plugin.cpp
@@ -110,7 +110,7 @@ void ResourcePreloaderEditor::_item_edited() {
return;
}
- if (new_name == "" || new_name.find("\\") != -1 || new_name.find("/") != -1 || preloader->has_resource(new_name)) {
+ if (new_name.is_empty() || new_name.find("\\") != -1 || new_name.find("/") != -1 || preloader->has_resource(new_name)) {
s->set_text(0, old_name);
return;
}
@@ -147,10 +147,10 @@ void ResourcePreloaderEditor::_paste_pressed() {
}
String name = r->get_name();
- if (name == "") {
+ if (name.is_empty()) {
name = r->get_path().get_file();
}
- if (name == "") {
+ if (name.is_empty()) {
name = r->get_class();
}
@@ -300,7 +300,7 @@ void ResourcePreloaderEditor::drop_data_fw(const Point2 &p_point, const Variant
if (r.is_valid()) {
String basename;
- if (r->get_name() != "") {
+ if (!r->get_name().is_empty()) {
basename = r->get_name();
} else if (r->get_path().is_resource_file()) {
basename = r->get_path().get_basename();
diff --git a/editor/plugins/resource_preloader_editor_plugin.h b/editor/plugins/resource_preloader_editor_plugin.h
index 04ab458eb5..943765d4e0 100644
--- a/editor/plugins/resource_preloader_editor_plugin.h
+++ b/editor/plugins/resource_preloader_editor_plugin.h
@@ -59,7 +59,6 @@ class ResourcePreloaderEditor : public PanelContainer {
ResourcePreloader *preloader;
void _load_pressed();
- void _load_scene_pressed();
void _files_load_request(const Vector<String> &p_paths);
void _paste_pressed();
void _remove_resource(const String &p_to_remove);
diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp
index ed91f174d1..d1830bf5af 100644
--- a/editor/plugins/root_motion_editor_plugin.cpp
+++ b/editor/plugins/root_motion_editor_plugin.cpp
@@ -89,7 +89,7 @@ void EditorPropertyRootMotion::_node_assign() {
String accum;
for (int i = 0; i < path.get_name_count(); i++) {
String name = path.get_name(i);
- if (accum != String()) {
+ if (!accum.is_empty()) {
accum += "/";
}
accum += name;
@@ -271,26 +271,18 @@ EditorPropertyRootMotion::EditorPropertyRootMotion() {
//////////////////////////
bool EditorInspectorRootMotionPlugin::can_handle(Object *p_object) {
- return true; //can handle everything
-}
-
-void EditorInspectorRootMotionPlugin::parse_begin(Object *p_object) {
- //do none
+ return true; // Can handle everything.
}
bool EditorInspectorRootMotionPlugin::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 == "root_motion_track" && p_object->is_class("AnimationTree") && p_type == Variant::NODE_PATH) {
EditorPropertyRootMotion *editor = memnew(EditorPropertyRootMotion);
- if (p_hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && p_hint_text != String()) {
+ if (p_hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && !p_hint_text.is_empty()) {
editor->setup(p_hint_text);
}
add_property_editor(p_path, editor);
return true;
}
- return false; //can be overridden, although it will most likely be last anyway
-}
-
-void EditorInspectorRootMotionPlugin::parse_end() {
- //do none
+ return false;
}
diff --git a/editor/plugins/root_motion_editor_plugin.h b/editor/plugins/root_motion_editor_plugin.h
index 1484af62e8..c05975b6c3 100644
--- a/editor/plugins/root_motion_editor_plugin.h
+++ b/editor/plugins/root_motion_editor_plugin.h
@@ -64,9 +64,7 @@ class EditorInspectorRootMotionPlugin : public EditorInspectorPlugin {
public:
virtual bool can_handle(Object *p_object) override;
- virtual void parse_begin(Object *p_object) override;
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;
- virtual void parse_end() override;
};
#endif // ROOT_MOTION_EDITOR_PLUGIN_H
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index a24249b95b..caaac2c7d3 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -37,6 +37,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "editor/debugger/editor_debugger_node.h"
+#include "editor/debugger/script_editor_debugger.h"
#include "editor/editor_node.h"
#include "editor/editor_run_script.h"
#include "editor/editor_scale.h"
@@ -45,6 +46,7 @@
#include "editor/find_in_files.h"
#include "editor/node_dock.h"
#include "editor/plugins/shader_editor_plugin.h"
+#include "modules/visual_script/editor/visual_script_editor.h"
#include "scene/main/window.h"
#include "scene/scene_string_names.h"
#include "script_text_editor.h"
@@ -177,7 +179,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
for (const String &comment : comments) {
String beg = comment.get_slice(" ", 0);
String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
- highlighter->add_color_region(beg, end, comment_color, end == "");
+ highlighter->add_color_region(beg, end, comment_color, end.is_empty());
}
/* Strings */
@@ -187,7 +189,7 @@ void EditorStandardSyntaxHighlighter::_update_cache() {
for (const String &string : strings) {
String beg = string.get_slice(" ", 0);
String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
- highlighter->add_color_region(beg, end, string_color, end == "");
+ highlighter->add_color_region(beg, end, string_color, end.is_empty());
}
}
}
@@ -212,6 +214,7 @@ Ref<EditorSyntaxHighlighter> EditorPlainTextSyntaxHighlighter::_create() const {
void ScriptEditorBase::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_base_editor"), &ScriptEditorBase::get_base_editor);
+ ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &ScriptEditorBase::add_syntax_highlighter);
ADD_SIGNAL(MethodInfo("name_changed"));
ADD_SIGNAL(MethodInfo("edited_script_changed"));
@@ -224,12 +227,6 @@ void ScriptEditorBase::_bind_methods() {
ADD_SIGNAL(MethodInfo("replace_in_files_requested", PropertyInfo(Variant::STRING, "text")));
}
-static bool _is_built_in_script(Script *p_script) {
- String path = p_script->get_path();
-
- return path.find("::") != -1;
-}
-
class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache {
struct Cache {
uint64_t time_loaded = 0;
@@ -312,10 +309,7 @@ void ScriptEditorQuickOpen::_text_changed(const String &p_newtext) {
void ScriptEditorQuickOpen::_sbox_input(const Ref<InputEvent> &p_ie) {
Ref<InputEventKey> k = p_ie;
- if (k.is_valid() && (k->get_keycode() == KEY_UP ||
- k->get_keycode() == KEY_DOWN ||
- k->get_keycode() == KEY_PAGEUP ||
- k->get_keycode() == KEY_PAGEDOWN)) {
+ if (k.is_valid() && (k->get_keycode() == Key::UP || k->get_keycode() == Key::DOWN || k->get_keycode() == Key::PAGEUP || k->get_keycode() == Key::PAGEDOWN)) {
search_options->gui_input(k);
search_box->accept_event();
}
@@ -327,7 +321,7 @@ void ScriptEditorQuickOpen::_update_search() {
for (int i = 0; i < functions.size(); i++) {
String file = functions[i];
- if ((search_box->get_text() == "" || file.findn(search_box->get_text()) != -1)) {
+ if ((search_box->get_text().is_empty() || file.findn(search_box->get_text()) != -1)) {
TreeItem *ti = search_options->create_item(root);
ti->set_text(0, file);
if (root->get_first_child() == ti) {
@@ -398,7 +392,7 @@ ScriptEditor *ScriptEditor::script_editor = nullptr;
String ScriptEditor::_get_debug_tooltip(const String &p_text, Node *_se) {
String val = EditorDebuggerNode::get_singleton()->get_var_value(p_text);
- if (val != String()) {
+ if (!val.is_empty()) {
return p_text + ": " + val;
} else {
return String();
@@ -479,6 +473,75 @@ void ScriptEditor::_clear_execution(REF p_script) {
}
}
+void ScriptEditor::_set_breakpoint(REF p_script, int p_line, bool p_enabled) {
+ Ref<Script> script = Object::cast_to<Script>(*p_script);
+ if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) {
+ // Update if open.
+ for (int i = 0; i < tab_container->get_child_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ if (se && se->get_edited_resource()->get_path() == script->get_path()) {
+ se->set_breakpoint(p_line, p_enabled);
+ return;
+ }
+ }
+
+ // Handle closed.
+ Dictionary state = script_editor_cache->get_value(script->get_path(), "state");
+ Array breakpoints;
+ if (state.has("breakpoints")) {
+ breakpoints = state["breakpoints"];
+ }
+
+ if (breakpoints.has(p_line)) {
+ if (!p_enabled) {
+ breakpoints.erase(p_line);
+ }
+ } else if (p_enabled) {
+ breakpoints.push_back(p_line);
+ }
+ state["breakpoints"] = breakpoints;
+ script_editor_cache->set_value(script->get_path(), "state", state);
+ EditorDebuggerNode::get_singleton()->set_breakpoint(script->get_path(), p_line + 1, false);
+ }
+}
+
+void ScriptEditor::_clear_breakpoints() {
+ for (int i = 0; i < tab_container->get_child_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ if (se) {
+ se->clear_breakpoints();
+ }
+ }
+
+ // Clear from closed scripts.
+ List<String> cached_editors;
+ script_editor_cache->get_sections(&cached_editors);
+ for (const String &E : cached_editors) {
+ Array breakpoints = _get_cached_breakpoints_for_script(E);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoints[i] + 1, false);
+ }
+
+ if (breakpoints.size() > 0) {
+ Dictionary state = script_editor_cache->get_value(E, "state");
+ state["breakpoints"] = Array();
+ script_editor_cache->set_value(E, "state", state);
+ }
+ }
+}
+
+Array ScriptEditor::_get_cached_breakpoints_for_script(const String &p_path) const {
+ if (!ResourceLoader::exists(p_path, "Script") || p_path.begins_with("local://") || !script_editor_cache->has_section_key(p_path, "state")) {
+ return Array();
+ }
+
+ Dictionary state = script_editor_cache->get_value(p_path, "state");
+ if (!state.has("breakpoints")) {
+ return Array();
+ }
+ return state["breakpoints"];
+}
+
ScriptEditorBase *ScriptEditor::_get_current_editor() const {
int selected = tab_container->get_current_tab();
if (selected < 0 || selected >= tab_container->get_child_count()) {
@@ -670,7 +733,7 @@ void ScriptEditor::_open_recent_script(int p_idx) {
return;
}
- rc.remove(p_idx);
+ rc.remove_at(p_idx);
EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc);
_update_recent_scripts();
_show_error_dialog(path);
@@ -691,20 +754,24 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tselected);
if (current) {
- Ref<Script> script = current->get_edited_resource();
- if (p_save && script.is_valid()) {
+ RES file = current->get_edited_resource();
+ if (p_save && file.is_valid()) {
// Do not try to save internal scripts, but prompt to save in-memory
// scripts which are not saved to disk yet (have empty path).
- if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
+ if (file->is_built_in()) {
save_current_script();
}
}
- if (script.is_valid()) {
- if (!script->get_path().is_empty()) {
+ if (file.is_valid()) {
+ if (!file->get_path().is_empty()) {
// Only saved scripts can be restored.
- previous_scripts.push_back(script->get_path());
+ previous_scripts.push_back(file->get_path());
+ }
+
+ Ref<Script> script = file;
+ if (script.is_valid()) {
+ notify_script_close(script);
}
- notify_script_close(script);
}
}
@@ -718,7 +785,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
for (int i = 0; i < history.size(); i++) {
if (history[i].control == tselected) {
- history.remove(i);
+ history.remove_at(i);
i--;
history_pos--;
}
@@ -731,6 +798,7 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
int idx = tab_container->get_current_tab();
if (current) {
current->clear_edit_menu();
+ _save_editor_state(current);
}
memdelete(tselected);
if (idx >= tab_container->get_child_count()) {
@@ -783,39 +851,35 @@ void ScriptEditor::_copy_script_path() {
}
void ScriptEditor::_close_other_tabs() {
- int child_count = tab_container->get_child_count();
int current_idx = tab_container->get_current_tab();
- for (int i = child_count - 1; i >= 0; i--) {
- if (i == current_idx) {
- continue;
- }
-
- tab_container->set_current_tab(i);
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
-
- if (se) {
- // Maybe there are unsaved changes
- if (se->is_unsaved()) {
- _ask_close_current_unsaved_tab(se);
- continue;
- }
+ for (int i = tab_container->get_child_count() - 1; i >= 0; i--) {
+ if (i != current_idx) {
+ script_close_queue.push_back(i);
}
-
- _close_current_tab(false);
}
+ _queue_close_tabs();
}
void ScriptEditor::_close_all_tabs() {
- int child_count = tab_container->get_child_count();
- for (int i = child_count - 1; i >= 0; i--) {
- tab_container->set_current_tab(i);
- ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ for (int i = tab_container->get_child_count() - 1; i >= 0; i--) {
+ script_close_queue.push_back(i);
+ }
+ _queue_close_tabs();
+}
+
+void ScriptEditor::_queue_close_tabs() {
+ while (!script_close_queue.is_empty()) {
+ int idx = script_close_queue.front()->get();
+ script_close_queue.pop_front();
+ tab_container->set_current_tab(idx);
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(idx));
if (se) {
- // Maybe there are unsaved changes
+ // Maybe there are unsaved changes.
if (se->is_unsaved()) {
_ask_close_current_unsaved_tab(se);
- continue;
+ erase_tab_confirm->connect(SceneStringNames::get_singleton()->visibility_changed, callable_mp(this, &ScriptEditor::_queue_close_tabs), varray(), CONNECT_ONESHOT);
+ break;
}
}
@@ -840,7 +904,7 @@ void ScriptEditor::_resave_scripts(const String &p_str) {
RES script = se->get_edited_resource();
- if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) {
+ if (script->is_built_in()) {
continue; //internal script, who cares
}
@@ -881,7 +945,7 @@ void ScriptEditor::_reload_scripts() {
RES edited_res = se->get_edited_resource();
- if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) {
+ if (edited_res->is_built_in()) {
continue; //internal script, who cares
}
@@ -925,10 +989,6 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
RES script = se->get_edited_resource();
- if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) {
- continue; //internal script, who cares
- }
-
if (script == p_res) {
se->tag_saved_version();
}
@@ -938,6 +998,31 @@ void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
_trigger_live_script_reload();
}
+void ScriptEditor::_scene_saved_callback(const String &p_path) {
+ // If scene was saved, mark all built-in scripts from that scene as saved.
+ for (int i = 0; i < tab_container->get_child_count(); i++) {
+ ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
+ if (!se) {
+ continue;
+ }
+
+ RES edited_res = se->get_edited_resource();
+
+ if (!edited_res->is_built_in()) {
+ continue; // External script, who cares.
+ }
+
+ if (edited_res->get_path().get_slice("::", 0) == p_path) {
+ se->tag_saved_version();
+ }
+
+ Ref<Script> scr = edited_res;
+ if (scr.is_valid() && scr->is_tool()) {
+ scr->reload(true);
+ }
+ }
+}
+
void ScriptEditor::_trigger_live_script_reload() {
if (!pending_auto_reload && auto_reload_running_scripts) {
call_deferred(SNAME("_live_auto_reload_running_scripts"));
@@ -967,7 +1052,7 @@ bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) {
continue;
}
- if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) {
+ if (edited_res->is_built_in()) {
continue; //internal script, who cares
}
@@ -1009,35 +1094,21 @@ void ScriptEditor::_file_dialog_action(String p_file) {
}
file->close();
memdelete(file);
- [[fallthrough]];
- }
- case FILE_OPEN: {
- List<String> extensions;
- ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
- if (extensions.find(p_file.get_extension())) {
- Ref<Script> scr = ResourceLoader::load(p_file);
- if (!scr.is_valid()) {
- editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
- file_dialog_option = -1;
- return;
- }
-
- edit(scr);
- file_dialog_option = -1;
- return;
- }
- Error error;
- Ref<TextFile> text_file = _load_text_file(p_file, &error);
- if (error != OK) {
- editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
+ if (EditorFileSystem::get_singleton()) {
+ if (textfile_extensions.has(p_file.get_extension())) {
+ EditorFileSystem::get_singleton()->update_file(p_file);
+ }
}
- if (text_file.is_valid()) {
- edit(text_file);
- file_dialog_option = -1;
+ if (!open_textfile_after_create) {
return;
}
+ [[fallthrough]];
+ }
+ case FILE_OPEN: {
+ open_file(p_file);
+ file_dialog_option = -1;
} break;
case FILE_SAVE_AS: {
ScriptEditorBase *current = _get_current_editor();
@@ -1112,8 +1183,12 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog_option = FILE_NEW_TEXTFILE;
file_dialog->clear_filters();
+ for (const String &E : textfile_extensions) {
+ file_dialog->add_filter("*." + E + " ; " + E.to_upper());
+ }
file_dialog->popup_file_dialog();
file_dialog->set_title(TTR("New Text File..."));
+ open_textfile_after_create = true;
} break;
case FILE_OPEN: {
file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
@@ -1127,6 +1202,10 @@ void ScriptEditor::_menu_option(int p_option) {
file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
}
+ for (const String &E : textfile_extensions) {
+ file_dialog->add_filter("*." + E + " ; " + E.to_upper());
+ }
+
file_dialog->popup_file_dialog();
file_dialog->set_title(TTR("Open File"));
return;
@@ -1212,14 +1291,15 @@ void ScriptEditor::_menu_option(int p_option) {
_update_script_names();
} break;
case TOGGLE_SCRIPTS_PANEL: {
+ toggle_scripts_panel();
if (current) {
- ScriptTextEditor *editor = Object::cast_to<ScriptTextEditor>(current);
- toggle_scripts_panel();
- if (editor) {
- editor->update_toggle_scripts_button();
- }
+ current->update_toggle_scripts_button();
} else {
- toggle_scripts_panel();
+ Control *tab = tab_container->get_current_tab_control();
+ EditorHelp *editor_help = Object::cast_to<EditorHelp>(tab);
+ if (editor_help) {
+ editor_help->update_toggle_scripts_button();
+ }
}
}
}
@@ -1335,8 +1415,12 @@ void ScriptEditor::_menu_option(int p_option) {
} break;
case SHOW_IN_FILE_SYSTEM: {
const RES script = current->get_edited_resource();
- const String path = script->get_path();
+ String path = script->get_path();
if (!path.is_empty()) {
+ if (script->is_built_in()) {
+ path = path.get_slice("::", 0); // Show the scene instead.
+ }
+
FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
file_system_dock->navigate_to_path(path);
// Ensure that the FileSystem dock is visible.
@@ -1466,6 +1550,8 @@ void ScriptEditor::_notification(int p_what) {
editor->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
editor->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
editor->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
+ editor->connect("scene_saved", callable_mp(this, &ScriptEditor::_scene_saved_callback));
+ editor->get_filesystem_dock()->connect("files_moved", callable_mp(this, &ScriptEditor::_files_moved));
editor->get_filesystem_dock()->connect("file_removed", callable_mp(this, &ScriptEditor::_file_removed));
script_list->connect("item_selected", callable_mp(this, &ScriptEditor::_script_selected));
@@ -1475,6 +1561,7 @@ void ScriptEditor::_notification(int p_what) {
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &ScriptEditor::_editor_settings_changed));
EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &ScriptEditor::_filesystem_changed));
+ _editor_settings_changed();
[[fallthrough]];
}
case NOTIFICATION_TRANSLATION_CHANGED:
@@ -1557,8 +1644,8 @@ void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
continue;
}
- if (script->get_path().find("::") != -1 && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
- _close_tab(i);
+ if (script->is_built_in() && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
+ _close_tab(i, false);
i--;
}
}
@@ -1578,6 +1665,7 @@ void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) {
}
void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
+ Set<String> loaded_scripts;
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
if (!se) {
@@ -1590,7 +1678,8 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
}
String base = script->get_path();
- if (base.begins_with("local://") || base == "") {
+ loaded_scripts.insert(base);
+ if (base.begins_with("local://") || base.is_empty()) {
continue;
}
@@ -1599,6 +1688,20 @@ void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
p_breakpoints->push_back(base + ":" + itos((int)bpoints[j] + 1));
}
}
+
+ // Load breakpoints that are in closed scripts.
+ List<String> cached_editors;
+ script_editor_cache->get_sections(&cached_editors);
+ for (const String &E : cached_editors) {
+ if (loaded_scripts.has(E)) {
+ continue;
+ }
+
+ Array breakpoints = _get_cached_breakpoints_for_script(E);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ p_breakpoints->push_back(E + ":" + itos((int)breakpoints[i] + 1));
+ }
+ }
}
void ScriptEditor::_members_overview_selected(int p_idx) {
@@ -1625,7 +1728,7 @@ void ScriptEditor::_help_overview_selected(int p_idx) {
}
void ScriptEditor::_script_selected(int p_idx) {
- grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(MOUSE_BUTTON_LEFT); //amazing hack, simply amazing
+ grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT); //amazing hack, simply amazing
_go_to_tab(script_list->get_item_metadata(p_idx));
grab_focus_block = false;
@@ -1728,7 +1831,7 @@ void ScriptEditor::_update_members_overview() {
for (int i = 0; i < functions.size(); i++) {
String filter = filter_methods->get_text();
String name = functions[i].get_slice(":", 0);
- if (filter == "" || filter.is_subsequence_ofi(name)) {
+ if (filter.is_empty() || filter.is_subsequence_ofi(name)) {
members_overview->add_item(name);
members_overview->set_item_metadata(members_overview->get_item_count() - 1, functions[i].get_slice(":", 1).to_int() - 1);
}
@@ -1849,20 +1952,7 @@ void ScriptEditor::_update_script_names() {
// to update original path to previously edited resource.
se->set_meta("_edit_res_path", path);
}
- bool built_in = !path.is_resource_file();
- String name;
-
- if (built_in) {
- name = path.get_file();
- const String &resource_name = se->get_edited_resource()->get_name();
- if (resource_name != "") {
- // If the built-in script has a custom resource name defined,
- // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
- name = vformat("%s (%s)", resource_name, name.substr(0, name.find("::", 0)));
- }
- } else {
- name = se->get_name();
- }
+ String name = se->get_name();
_ScriptEditorItemData sd;
sd.icon = icon;
@@ -1986,7 +2076,7 @@ void ScriptEditor::_update_script_names() {
Vector<_ScriptEditorItemData> sedata_filtered;
for (int i = 0; i < sedata.size(); i++) {
String filter = filter_scripts->get_text();
- if (filter == "" || filter.is_subsequence_ofi(sedata[i].name)) {
+ if (filter.is_empty() || filter.is_subsequence_ofi(sedata[i].name)) {
sedata_filtered.push_back(sedata[i]);
}
}
@@ -2034,7 +2124,7 @@ void ScriptEditor::_update_script_connections() {
}
}
-Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) {
+Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) const {
if (r_error) {
*r_error = ERR_FILE_CANT_OPEN;
}
@@ -2097,9 +2187,10 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
Ref<Script> script = p_resource;
// Don't open dominant script if using an external editor.
- const bool use_external_editor =
+ bool use_external_editor =
EditorSettings::get_singleton()->get("text_editor/external/use_external_editor") ||
(script.is_valid() && script->get_language()->overrides_external_editor());
+ use_external_editor = use_external_editor && !(script.is_valid() && script->is_built_in()); // Ignore external editor for built-in scripts.
const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/behavior/files/open_dominant_script_on_scene_change");
const bool should_open = (open_dominant && !use_external_editor) || !EditorNode::get_singleton()->is_changing_scene();
@@ -2256,6 +2347,10 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra
_add_recent_script(p_resource->get_path());
}
+ if (script_editor_cache->has_section(p_resource->get_path())) {
+ se->set_edit_state(script_editor_cache->get_value(p_resource->get_path(), "state"));
+ }
+
_sort_list_on_update = true;
_update_script_names();
_save_layout();
@@ -2321,7 +2416,17 @@ void ScriptEditor::save_current_script() {
}
}
- editor->save_resource(resource);
+ if (resource->is_built_in()) {
+ // If built-in script, save the scene instead.
+ const String scene_path = resource->get_path().get_slice("::", 0);
+ if (!scene_path.is_empty()) {
+ Vector<String> scene_to_save;
+ scene_to_save.push_back(scene_path);
+ editor->save_scene_list(scene_to_save);
+ }
+ } else {
+ editor->save_resource(resource);
+ }
if (script != nullptr) {
const Vector<DocData::ClassDoc> &documentations = script->get_documentation();
@@ -2334,6 +2439,8 @@ void ScriptEditor::save_current_script() {
}
void ScriptEditor::save_all_scripts() {
+ Vector<String> scenes_to_save;
+
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
if (!se) {
@@ -2363,7 +2470,7 @@ void ScriptEditor::save_all_scripts() {
se->apply_code();
}
- if (edited_res->get_path() != "" && edited_res->get_path().find("local://") == -1 && edited_res->get_path().find("::") == -1) {
+ if (!edited_res->is_built_in()) {
Ref<TextFile> text_file = edited_res;
Ref<Script> script = edited_res;
@@ -2392,9 +2499,19 @@ void ScriptEditor::save_all_scripts() {
update_doc(doc.name);
}
}
+ } else {
+ // For built-in scripts, save their scenes instead.
+ const String scene_path = edited_res->get_path().get_slice("::", 0);
+ if (!scenes_to_save.has(scene_path)) {
+ scenes_to_save.push_back(scene_path);
+ }
}
}
+ if (!scenes_to_save.is_empty()) {
+ editor->save_scene_list(scenes_to_save);
+ }
+
_update_script_names();
EditorFileSystem::get_singleton()->update_script_classes();
}
@@ -2414,6 +2531,41 @@ void ScriptEditor::open_script_create_dialog(const String &p_base_name, const St
script_create_dialog->config(p_base_name, p_base_path);
}
+void ScriptEditor::open_text_file_create_dialog(const String &p_base_path, const String &p_base_name) {
+ file_dialog->set_current_file(p_base_name);
+ file_dialog->set_current_dir(p_base_path);
+ _menu_option(FILE_NEW_TEXTFILE);
+ open_textfile_after_create = false;
+}
+
+RES ScriptEditor::open_file(const String &p_file) {
+ List<String> extensions;
+ ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
+ if (extensions.find(p_file.get_extension())) {
+ Ref<Script> scr = ResourceLoader::load(p_file);
+ if (!scr.is_valid()) {
+ editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
+ return RES();
+ }
+
+ edit(scr);
+ return scr;
+ }
+
+ Error error;
+ Ref<TextFile> text_file = _load_text_file(p_file, &error);
+ if (error != OK) {
+ editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
+ return RES();
+ }
+
+ if (text_file.is_valid()) {
+ edit(text_file);
+ return text_file;
+ }
+ return RES();
+}
+
void ScriptEditor::_editor_stop() {
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
@@ -2448,7 +2600,7 @@ void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const
script_list->select(script_list->find_metadata(i));
// Save the current script so the changes can be picked up by an external editor.
- if (!_is_built_in_script(script.ptr())) { // But only if it's not built-in script.
+ if (!script.ptr()->is_built_in()) { // But only if it's not built-in script.
save_current_script();
}
@@ -2456,6 +2608,20 @@ void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const
}
}
+void ScriptEditor::_save_editor_state(ScriptEditorBase *p_editor) {
+ if (restoring_layout) {
+ return;
+ }
+
+ const String &path = p_editor->get_edited_resource()->get_path();
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ script_editor_cache->set_value(path, "state", p_editor->get_edit_state());
+ // This is saved later when we save the editor layout.
+}
+
void ScriptEditor::_save_layout() {
if (restoring_layout) {
return;
@@ -2465,6 +2631,12 @@ void ScriptEditor::_save_layout() {
}
void ScriptEditor::_editor_settings_changed() {
+ textfile_extensions.clear();
+ const Vector<String> textfile_ext = ((String)(EditorSettings::get_singleton()->get("docks/filesystem/textfile_extensions"))).split(",", false);
+ for (const String &E : textfile_ext) {
+ textfile_extensions.insert(E);
+ }
+
trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/trim_trailing_whitespace_on_save");
convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/behavior/files/convert_indent_on_save");
use_space_indentation = EditorSettings::get_singleton()->get("text_editor/behavior/indent/type");
@@ -2476,7 +2648,7 @@ void ScriptEditor::_editor_settings_changed() {
_update_autosave_timer();
- if (current_theme == "") {
+ if (current_theme.is_empty()) {
current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
} else if (current_theme != String(EditorSettings::get_singleton()->get("text_editor/theme/color_theme"))) {
current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
@@ -2501,6 +2673,26 @@ void ScriptEditor::_filesystem_changed() {
_update_script_names();
}
+void ScriptEditor::_files_moved(const String &p_old_file, const String &p_new_file) {
+ if (!script_editor_cache->has_section(p_old_file)) {
+ return;
+ }
+ Variant state = script_editor_cache->get_value(p_old_file, "state");
+ script_editor_cache->erase_section(p_old_file);
+ script_editor_cache->set_value(p_new_file, "state", state);
+
+ // If Script, update breakpoints with debugger.
+ Array breakpoints = _get_cached_breakpoints_for_script(p_new_file);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ int line = (int)breakpoints[i] + 1;
+ EditorDebuggerNode::get_singleton()->set_breakpoint(p_old_file, line, false);
+ if (!p_new_file.begins_with("local://") && ResourceLoader::exists(p_new_file, "Script")) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(p_new_file, line, true);
+ }
+ }
+ // This is saved later when we save the editor layout.
+}
+
void ScriptEditor::_file_removed(const String &p_removed_file) {
for (int i = 0; i < tab_container->get_child_count(); i++) {
ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
@@ -2512,6 +2704,15 @@ void ScriptEditor::_file_removed(const String &p_removed_file) {
_close_tab(i, false, false);
}
}
+
+ // Check closed.
+ if (script_editor_cache->has_section(p_removed_file)) {
+ Array breakpoints = _get_cached_breakpoints_for_script(p_removed_file);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(p_removed_file, (int)breakpoints[i] + 1, false);
+ }
+ script_editor_cache->erase_section(p_removed_file);
+ }
}
void ScriptEditor::_update_find_replace_bar() {
@@ -2639,15 +2840,25 @@ bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data
for (int i = 0; i < files.size(); i++) {
String file = files[i];
- if (file == "" || !FileAccess::exists(file)) {
+ if (file.is_empty() || !FileAccess::exists(file)) {
continue;
}
- Ref<Script> scr = ResourceLoader::load(file);
- if (scr.is_valid()) {
- return true;
+ if (ResourceLoader::exists(file, "Script")) {
+ Ref<Script> scr = ResourceLoader::load(file);
+ if (scr.is_valid()) {
+ return true;
+ }
+ }
+
+ if (textfile_extensions.has(file.get_extension())) {
+ Error err;
+ Ref<TextFile> text_file = _load_text_file(file, &err);
+ if (text_file.is_valid() && err == OK) {
+ return true;
+ }
}
}
- return true;
+ return false;
}
return false;
@@ -2709,12 +2920,16 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
int num_tabs_before = tab_container->get_child_count();
for (int i = 0; i < files.size(); i++) {
String file = files[i];
- if (file == "" || !FileAccess::exists(file)) {
+ if (file.is_empty() || !FileAccess::exists(file)) {
continue;
}
- Ref<Script> scr = ResourceLoader::load(file);
- if (scr.is_valid()) {
- edit(scr);
+
+ if (!ResourceLoader::exists(file, "Script") && !textfile_extensions.has(file.get_extension())) {
+ continue;
+ }
+
+ RES res = open_file(file);
+ if (res.is_valid()) {
if (tab_container->get_child_count() > num_tabs_before) {
tab_container->move_child(tab_container->get_child(tab_container->get_child_count() - 1), new_index);
num_tabs_before = tab_container->get_child_count();
@@ -2728,6 +2943,29 @@ void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Co
}
}
+void ScriptEditor::input(const Ref<InputEvent> &p_event) {
+ // This is implemented in `input()` rather than `unhandled_input()` to allow
+ // the shortcut to be used regardless of the click location.
+ // This feature can be disabled to avoid interfering with other uses of the additional
+ // mouse buttons, such as push-to-talk in a VoIP program.
+ if (EDITOR_GET("interface/editor/mouse_extra_buttons_navigate_history")) {
+ const Ref<InputEventMouseButton> mb = p_event;
+
+ // Navigate the script history using additional mouse buttons present on some mice.
+ // This must be hardcoded as the editor shortcuts dialog doesn't allow assigning
+ // more than one shortcut per action.
+ if (mb.is_valid() && mb->is_pressed() && is_visible_in_tree()) {
+ if (mb->get_button_index() == MouseButton::MB_XBUTTON1) {
+ _history_back();
+ }
+
+ if (mb->get_button_index() == MouseButton::MB_XBUTTON2) {
+ _history_forward();
+ }
+ }
+ }
+}
+
void ScriptEditor::unhandled_key_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
@@ -2762,7 +3000,7 @@ void ScriptEditor::_script_list_gui_input(const Ref<InputEvent> &ev) {
Ref<InputEventMouseButton> mb = ev;
if (mb.is_valid() && mb->is_pressed()) {
switch (mb->get_button_index()) {
- case MOUSE_BUTTON_MIDDLE: {
+ case MouseButton::MIDDLE: {
// Right-click selects automatically; middle-click does not.
int idx = script_list->get_item_at_position(mb->get_position(), true);
if (idx >= 0) {
@@ -2772,7 +3010,7 @@ void ScriptEditor::_script_list_gui_input(const Ref<InputEvent> &ev) {
}
} break;
- case MOUSE_BUTTON_RIGHT: {
+ case MouseButton::RIGHT: {
_make_script_list_context_menu();
} break;
default:
@@ -2817,8 +3055,8 @@ void ScriptEditor::_make_script_list_context_menu() {
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_sort"), WINDOW_SORT);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/toggle_scripts_panel"), TOGGLE_SCRIPTS_PANEL);
- context_menu->set_position(get_global_transform().xform(get_local_mouse_position()));
- context_menu->set_size(Vector2(1, 1));
+ context_menu->set_position(get_screen_position() + get_local_mouse_position());
+ context_menu->reset_size();
context_menu->popup();
}
@@ -2839,6 +3077,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
restoring_layout = true;
+ Set<String> loaded_scripts;
List<String> extensions;
ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
@@ -2851,8 +3090,12 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
}
if (!FileAccess::exists(path)) {
+ if (script_editor_cache->has_section(path)) {
+ script_editor_cache->erase_section(path);
+ }
continue;
}
+ loaded_scripts.insert(path);
if (extensions.find(path.get_extension())) {
Ref<Script> scr = ResourceLoader::load(path);
@@ -2883,7 +3126,7 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
for (int i = 0; i < helps.size(); i++) {
String path = helps[i];
- if (path == "") { // invalid, skip
+ if (path.is_empty()) { // invalid, skip
continue;
}
_help_class_open(path);
@@ -2897,6 +3140,26 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
script_split->set_split_offset(p_layout->get_value("ScriptEditor", "split_offset"));
}
+ // Remove any deleted editors that have been removed between launches.
+ // and if a Script, register breakpoints with the debugger.
+ List<String> cached_editors;
+ script_editor_cache->get_sections(&cached_editors);
+ for (const String &E : cached_editors) {
+ if (loaded_scripts.has(E)) {
+ continue;
+ }
+
+ if (!FileAccess::exists(E)) {
+ script_editor_cache->erase_section(E);
+ continue;
+ }
+
+ Array breakpoints = _get_cached_breakpoints_for_script(E);
+ for (int i = 0; i < breakpoints.size(); i++) {
+ EditorDebuggerNode::get_singleton()->set_breakpoint(E, (int)breakpoints[i] + 1, true);
+ }
+ }
+
restoring_layout = false;
_update_script_names();
@@ -2914,11 +3177,8 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
continue;
}
- Dictionary script_info;
- script_info["path"] = path;
- script_info["state"] = se->get_edit_state();
-
- scripts.push_back(script_info);
+ _save_editor_state(se);
+ scripts.push_back(path);
}
EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
@@ -2931,10 +3191,13 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
p_layout->set_value("ScriptEditor", "open_scripts", scripts);
p_layout->set_value("ScriptEditor", "open_help", helps);
p_layout->set_value("ScriptEditor", "split_offset", script_split->get_split_offset());
+
+ // Save the cache.
+ script_editor_cache->save(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("script_editor_cache.cfg"));
}
void ScriptEditor::_help_class_open(const String &p_class) {
- if (p_class == "") {
+ if (p_class.is_empty()) {
return;
}
@@ -3017,15 +3280,15 @@ void ScriptEditor::_update_selected_editor_menu() {
EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_current_tab_control());
script_search_menu->get_popup()->clear();
if (eh) {
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND);
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT);
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3), HELP_SEARCH_FIND_PREVIOUS);
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KeyModifierMask::CMD | Key::F), HELP_SEARCH_FIND);
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), Key::F3), HELP_SEARCH_FIND_NEXT);
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KeyModifierMask::SHIFT | Key::F3), HELP_SEARCH_FIND_PREVIOUS);
script_search_menu->get_popup()->add_separator();
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES);
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::F), SEARCH_IN_FILES);
script_search_menu->show();
} else {
if (tab_container->get_child_count() == 0) {
- script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES);
+ script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::F), SEARCH_IN_FILES);
script_search_menu->show();
} else {
script_search_menu->hide();
@@ -3113,9 +3376,10 @@ Array ScriptEditor::_get_open_script_editors() const {
void ScriptEditor::set_scene_root_script(Ref<Script> p_script) {
// Don't open dominant script if using an external editor.
- const bool use_external_editor =
+ bool use_external_editor =
EditorSettings::get_singleton()->get("text_editor/external/use_external_editor") ||
(p_script.is_valid() && p_script->get_language()->overrides_external_editor());
+ use_external_editor = use_external_editor && !(p_script.is_valid() && p_script->is_built_in()); // Ignore external editor for built-in scripts.
const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/behavior/files/open_dominant_script_on_scene_change");
if (open_dominant && !use_external_editor && p_script.is_valid()) {
@@ -3205,6 +3469,9 @@ void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_numb
shader_editor->make_visible(true);
shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end);
return;
+ } else if (fpath.get_extension() == "tscn") {
+ editor->load_scene(fpath);
+ return;
} else {
Ref<Script> script = res;
if (script.is_valid()) {
@@ -3272,7 +3539,6 @@ void ScriptEditor::_bind_methods() {
ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections);
ClassDB::bind_method("_help_class_open", &ScriptEditor::_help_class_open);
ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts);
-
ClassDB::bind_method("_update_members_overview", &ScriptEditor::_update_members_overview);
ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts);
@@ -3298,6 +3564,9 @@ void ScriptEditor::_bind_methods() {
ScriptEditor::ScriptEditor(EditorNode *p_editor) {
current_theme = "";
+ script_editor_cache.instantiate();
+ script_editor_cache->load(EditorSettings::get_singleton()->get_project_settings_dir().plus_file("script_editor_cache.cfg"));
+
completion_cache = memnew(EditorScriptCodeCompletionCache);
restoring_layout = false;
waiting_update_names = false;
@@ -3403,11 +3672,13 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
find_replace_bar->hide();
ED_SHORTCUT("script_editor/window_sort", TTR("Sort"));
- ED_SHORTCUT("script_editor/window_move_up", TTR("Move Up"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_UP);
- ED_SHORTCUT("script_editor/window_move_down", TTR("Move Down"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_DOWN);
- ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD); // these should be KEY_GREATER and KEY_LESS but those don't work
- ED_SHORTCUT("script_editor/prev_script", TTR("Previous Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_COMMA);
- set_process_unhandled_key_input(true);
+ ED_SHORTCUT("script_editor/window_move_up", TTR("Move Up"), KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::UP);
+ ED_SHORTCUT("script_editor/window_move_down", TTR("Move Down"), KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::DOWN);
+ // FIXME: These should be `Key::GREATER` and `Key::LESS` but those don't work.
+ ED_SHORTCUT("script_editor/next_script", TTR("Next Script"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::PERIOD);
+ ED_SHORTCUT("script_editor/prev_script", TTR("Previous Script"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::COMMA);
+ set_process_input(true);
+ set_process_unhandled_input(true);
file_menu = memnew(MenuButton);
file_menu->set_text(TTR("File"));
@@ -3418,7 +3689,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script...")), FILE_NEW);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File...")), FILE_NEW_TEXTFILE);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open...")), FILE_OPEN);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T), FILE_REOPEN_CLOSED);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::T), FILE_REOPEN_CLOSED);
file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT);
recent_scripts = memnew(PopupMenu);
@@ -3428,17 +3699,17 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
_update_recent_scripts();
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KEY_MASK_ALT | KEY_MASK_CMD | KEY_S), FILE_SAVE);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KeyModifierMask::ALT | KeyModifierMask::CMD | Key::S), FILE_SAVE);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_S), FILE_SAVE_ALL);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KeyModifierMask::SHIFT | KeyModifierMask::ALT | Key::S), FILE_SAVE_ALL);
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_R), FILE_TOOL_RELOAD_SOFT);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KeyModifierMask::CMD | KeyModifierMask::ALT | Key::R), FILE_TOOL_RELOAD_SOFT);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM);
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV);
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KEY_MASK_ALT | KEY_RIGHT), WINDOW_NEXT);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KeyModifierMask::ALT | Key::LEFT), WINDOW_PREV);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KeyModifierMask::ALT | Key::RIGHT), WINDOW_NEXT);
file_menu->get_popup()->add_separator();
file_menu->get_popup()->add_submenu_item(TTR("Theme"), "Theme", FILE_THEME);
@@ -3455,16 +3726,16 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As...")), THEME_SAVE_AS);
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_file", TTR("Close"), KEY_MASK_CMD | KEY_W), FILE_CLOSE);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_file", TTR("Close"), KeyModifierMask::CMD | Key::W), FILE_CLOSE);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_all", TTR("Close All")), CLOSE_ALL);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_other_tabs", TTR("Close Other Tabs")), CLOSE_OTHER_TABS);
file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_docs", TTR("Close Docs")), CLOSE_DOCS);
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/run_file", TTR("Run"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_X), FILE_RUN);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/run_file", TTR("Run"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::X), FILE_RUN);
file_menu->get_popup()->add_separator();
- file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/toggle_scripts_panel", TTR("Toggle Scripts Panel"), KEY_MASK_CMD | KEY_BACKSLASH), TOGGLE_SCRIPTS_PANEL);
+ file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/toggle_scripts_panel", TTR("Toggle Scripts Panel"), KeyModifierMask::CMD | Key::BACKSLASH), TOGGLE_SCRIPTS_PANEL);
file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ScriptEditor::_menu_option));
script_search_menu = memnew(MenuButton);
@@ -3484,6 +3755,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) {
debugger->connect("set_execution", callable_mp(this, &ScriptEditor::_set_execution));
debugger->connect("clear_execution", callable_mp(this, &ScriptEditor::_clear_execution));
debugger->connect("breaked", callable_mp(this, &ScriptEditor::_breaked));
+ debugger->get_default_debugger()->connect("set_breakpoint", callable_mp(this, &ScriptEditor::_set_breakpoint));
+ debugger->get_default_debugger()->connect("clear_breakpoints", callable_mp(this, &ScriptEditor::_clear_breakpoints));
menu_hb->add_spacer();
@@ -3619,7 +3892,7 @@ void ScriptEditorPlugin::edit(Object *p_object) {
Script *p_script = Object::cast_to<Script>(p_object);
String res_path = p_script->get_path().get_slice("::", 0);
- if (_is_built_in_script(p_script)) {
+ if (p_script->is_built_in()) {
if (ResourceLoader::get_resource_type(res_path) == "PackedScene") {
if (!EditorNode::get_singleton()->is_scene_open(res_path)) {
EditorNode::get_singleton()->load_scene(res_path);
@@ -3715,7 +3988,7 @@ ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
EDITOR_DEF("text_editor/external/exec_flags", "{file}");
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}."));
- ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T);
+ ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::T);
ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts"));
}
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index a57aeea5c0..0adeca031e 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -155,10 +155,13 @@ public:
virtual void tag_saved_version() = 0;
virtual void reload(bool p_soft) {}
virtual Array get_breakpoints() = 0;
+ virtual void set_breakpoint(int p_line, bool p_enabled) = 0;
+ virtual void clear_breakpoints() = 0;
virtual void add_callback(const String &p_function, PackedStringArray p_args) = 0;
virtual void update_settings() = 0;
virtual void set_debugger_active(bool p_active) = 0;
virtual bool can_lose_focus_on_node_selection() { return true; }
+ virtual void update_toggle_scripts_button() {}
virtual bool show_members_overview() = 0;
@@ -306,6 +309,7 @@ class ScriptEditor : public PanelContainer {
int history_pos;
List<String> previous_scripts;
+ List<int> script_close_queue;
void _tab_changed(int p_which);
void _menu_option(int p_option);
@@ -338,6 +342,7 @@ class ScriptEditor : public PanelContainer {
void _close_docs_tab();
void _close_other_tabs();
void _close_all_tabs();
+ void _queue_close_tabs();
void _copy_script_path();
@@ -360,27 +365,32 @@ class ScriptEditor : public PanelContainer {
void _add_callback(Object *p_obj, const String &p_function, const PackedStringArray &p_args);
void _res_saved_callback(const Ref<Resource> &p_res);
+ void _scene_saved_callback(const String &p_path);
+ bool open_textfile_after_create = true;
bool trim_trailing_whitespace_on_save;
bool use_space_indentation;
bool convert_indent_on_save;
- void _trim_trailing_whitespace(TextEdit *tx);
-
void _goto_script_line2(int p_line);
void _goto_script_line(REF p_script, int p_line);
void _set_execution(REF p_script, int p_line);
void _clear_execution(REF p_script);
void _breaked(bool p_breaked, bool p_can_debug);
- void _update_window_menu();
void _script_created(Ref<Script> p_script);
+ void _set_breakpoint(REF p_scrpt, int p_line, bool p_enabled);
+ void _clear_breakpoints();
+ Array _get_cached_breakpoints_for_script(const String &p_path) const;
ScriptEditorBase *_get_current_editor() const;
Array _get_open_script_editors() const;
+ Ref<ConfigFile> script_editor_cache;
+ void _save_editor_state(ScriptEditorBase *p_editor);
void _save_layout();
void _editor_settings_changed();
void _filesystem_changed();
+ void _files_moved(const String &p_old_file, const String &p_new_file);
void _file_removed(const String &p_file);
void _autosave_scripts();
void _update_autosave_timer();
@@ -411,13 +421,13 @@ class ScriptEditor : public PanelContainer {
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ virtual void input(const Ref<InputEvent> &p_event) override;
virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
void _script_list_gui_input(const Ref<InputEvent> &ev);
void _make_script_list_context_menu();
void _help_search(String p_text);
- void _help_index(String p_text);
void _history_forward();
void _history_back();
@@ -440,7 +450,8 @@ class ScriptEditor : public PanelContainer {
Ref<Script> _get_current_script();
Array _get_open_scripts() const;
- Ref<TextFile> _load_text_file(const String &p_path, Error *r_error);
+ Set<String> textfile_extensions;
+ Ref<TextFile> _load_text_file(const String &p_path, Error *r_error) const;
Error _save_text_file(Ref<TextFile> p_text_file, const String &p_path);
void _on_find_in_files_requested(String text);
@@ -464,6 +475,8 @@ public:
bool is_scripts_panel_toggled();
void apply_scripts() const;
void open_script_create_dialog(const String &p_base_name, const String &p_base_path);
+ void open_text_file_create_dialog(const String &p_base_path, const String &p_base_name = "");
+ RES open_file(const String &p_file);
void ensure_select_current();
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 5f48106afc..d5af7c5b38 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -90,7 +90,7 @@ ConnectionInfoDialog::ConnectionInfoDialog() {
add_child(vbc);
method = memnew(Label);
- method->set_align(Label::ALIGN_CENTER);
+ method->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
vbc->add_child(method);
tree = memnew(Tree);
@@ -158,7 +158,6 @@ void ScriptTextEditor::enable_editor() {
editor_enabled = true;
_enable_code_editor();
- _set_theme_for_script();
_validate_script();
}
@@ -206,7 +205,7 @@ void ScriptTextEditor::_set_theme_for_script() {
String beg = string.get_slice(" ", 0);
String end = string.get_slice_count(" ") > 1 ? string.get_slice(" ", 1) : String();
if (!text_edit->has_string_delimiter(beg)) {
- text_edit->add_string_delimiter(beg, end, end == "");
+ text_edit->add_string_delimiter(beg, end, end.is_empty());
}
if (!end.is_empty() && !text_edit->has_auto_brace_completion_open_key(beg)) {
@@ -220,7 +219,7 @@ void ScriptTextEditor::_set_theme_for_script() {
for (const String &comment : comments) {
String beg = comment.get_slice(" ", 0);
String end = comment.get_slice_count(" ") > 1 ? comment.get_slice(" ", 1) : String();
- text_edit->add_comment_delimiter(beg, end, end == "");
+ text_edit->add_comment_delimiter(beg, end, end.is_empty());
if (!end.is_empty() && !text_edit->has_auto_brace_completion_open_key(beg)) {
text_edit->add_auto_brace_completion_pair(beg, end);
@@ -376,18 +375,21 @@ void ScriptTextEditor::ensure_focus() {
String ScriptTextEditor::get_name() {
String name;
- if (script->get_path().find("local://") == -1 && script->get_path().find("::") == -1) {
- name = script->get_path().get_file();
- if (is_unsaved()) {
- if (script->get_path().is_empty()) {
- name = TTR("[unsaved]");
- }
- name += "(*)";
+ name = script->get_path().get_file();
+ if (name.is_empty()) {
+ // This appears for newly created built-in scripts before saving the scene.
+ name = TTR("[unsaved]");
+ } else if (script->is_built_in()) {
+ const String &script_name = script->get_name();
+ if (!script_name.is_empty()) {
+ // If the built-in script has a custom resource name defined,
+ // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
+ name = vformat("%s (%s)", script_name, name.get_slice("::", 0));
}
- } else if (script->get_name() != "") {
- name = script->get_name();
- } else {
- name = script->get_class() + "(" + itos(script->get_instance_id()) + ")";
+ }
+
+ if (is_unsaved()) {
+ name += "(*)";
}
return name;
@@ -434,10 +436,12 @@ void ScriptTextEditor::_validate_script() {
int warning_nb = warnings.size();
warnings_panel->clear();
+ bool has_connections_table = false;
// Add missing connections.
if (GLOBAL_GET("debug/gdscript/warnings/enable").booleanize()) {
Node *base = get_tree()->get_edited_scene_root();
if (base && missing_connections.size() > 0) {
+ has_connections_table = true;
warnings_panel->push_table(1);
for (const Connection &connection : missing_connections) {
String base_path = base->get_name();
@@ -459,6 +463,10 @@ void ScriptTextEditor::_validate_script() {
code_editor->set_error_count(errors.size());
code_editor->set_warning_count(warning_nb);
+ if (has_connections_table) {
+ warnings_panel->add_newline();
+ }
+
// Add script warnings.
warnings_panel->push_table(3);
for (const ScriptLanguage::Warning &w : warnings) {
@@ -542,7 +550,7 @@ void ScriptTextEditor::_validate_script() {
void ScriptTextEditor::_update_bookmark_list() {
bookmarks_menu->clear();
- bookmarks_menu->set_size(Size2(1, 1));
+ bookmarks_menu->reset_size();
bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
bookmarks_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL);
@@ -653,7 +661,7 @@ void ScriptEditor::_update_modified_scripts_for_external_editor(Ref<Script> p_fo
continue;
}
- if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) {
+ if (script->is_built_in()) {
continue; //internal script, who cares, though weird
}
@@ -694,7 +702,7 @@ void ScriptTextEditor::_code_complete_script(const String &p_code, List<ScriptCo
void ScriptTextEditor::_update_breakpoint_list() {
breakpoints_menu->clear();
- breakpoints_menu->set_size(Size2(1, 1));
+ breakpoints_menu->reset_size();
breakpoints_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_breakpoint"), DEBUG_TOGGLE_BREAKPOINT);
breakpoints_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_breakpoints"), DEBUG_REMOVE_ALL_BREAKPOINTS);
@@ -831,7 +839,7 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c
if (info.is_singleton) {
EditorNode::get_singleton()->load_scene(info.path);
}
- } else if (p_symbol.is_rel_path()) {
+ } else if (p_symbol.is_relative_path()) {
// Every symbol other than absolute path is relative path so keep this condition at last.
String path = _get_absolute_path(p_symbol);
if (FileAccess::exists(path)) {
@@ -858,7 +866,7 @@ void ScriptTextEditor::_validate_symbol(const String &p_symbol) {
ScriptLanguage::LookupResult result;
if (ScriptServer::is_global_class(p_symbol) || p_symbol.is_resource_file() || script->get_language()->lookup_code(code_editor->get_text_editor()->get_text_for_symbol_lookup(), p_symbol, script->get_path(), base, result) == OK || (ProjectSettings::get_singleton()->has_autoload(p_symbol) && ProjectSettings::get_singleton()->get_autoload(p_symbol).is_singleton)) {
text_edit->set_symbol_lookup_word_as_valid(true);
- } else if (p_symbol.is_rel_path()) {
+ } else if (p_symbol.is_relative_path()) {
String path = _get_absolute_path(p_symbol);
if (FileAccess::exists(path)) {
text_edit->set_symbol_lookup_word_as_valid(true);
@@ -877,9 +885,7 @@ String ScriptTextEditor::_get_absolute_path(const String &rel_path) {
}
void ScriptTextEditor::update_toggle_scripts_button() {
- if (code_editor != nullptr) {
- code_editor->update_toggle_scripts_button();
- }
+ code_editor->update_toggle_scripts_button();
}
void ScriptTextEditor::_update_connected_methods() {
@@ -984,7 +990,7 @@ void ScriptTextEditor::_gutter_clicked(int p_line, int p_gutter) {
}
String method = code_editor->get_text_editor()->get_line_gutter_metadata(p_line, p_gutter);
- if (method == "") {
+ if (method.is_empty()) {
return;
}
@@ -1131,7 +1137,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
if (expression.parse(line) == OK) {
Variant result = expression.execute(Array(), Variant(), false);
- if (expression.get_error_text() == "") {
+ if (expression.get_error_text().is_empty()) {
results.push_back(whitespace + result.get_construct_string());
} else {
results.push_back(line);
@@ -1243,7 +1249,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
tx->set_caret_line(bpoints[bpoints.size() - 1]);
tx->center_viewport_to_caret();
} else {
- for (int i = bpoints.size(); i >= 0; i--) {
+ for (int i = bpoints.size() - 1; i >= 0; i--) {
int bline = bpoints[i];
if (bline < line) {
tx->unfold_line(bline);
@@ -1257,19 +1263,19 @@ void ScriptTextEditor::_edit_option(int p_op) {
} break;
case HELP_CONTEXTUAL: {
String text = tx->get_selected_text();
- if (text == "") {
+ if (text.is_empty()) {
text = tx->get_word_under_caret();
}
- if (text != "") {
+ if (!text.is_empty()) {
emit_signal(SNAME("request_help"), text);
}
} break;
case LOOKUP_SYMBOL: {
String text = tx->get_word_under_caret();
- if (text == "") {
+ if (text.is_empty()) {
text = tx->get_selected_text();
}
- if (text != "") {
+ if (!text.is_empty()) {
_lookup_symbol(text, tx->get_caret_line(), tx->get_caret_column());
}
} break;
@@ -1338,8 +1344,6 @@ void ScriptTextEditor::_bind_methods() {
ClassDB::bind_method("_get_drag_data_fw", &ScriptTextEditor::get_drag_data_fw);
ClassDB::bind_method("_can_drop_data_fw", &ScriptTextEditor::can_drop_data_fw);
ClassDB::bind_method("_drop_data_fw", &ScriptTextEditor::drop_data_fw);
-
- ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &ScriptTextEditor::add_syntax_highlighter);
}
Control *ScriptTextEditor::get_edit_menu() {
@@ -1370,6 +1374,14 @@ Array ScriptTextEditor::get_breakpoints() {
return code_editor->get_text_editor()->get_breakpointed_lines();
}
+void ScriptTextEditor::set_breakpoint(int p_line, bool p_enabled) {
+ code_editor->get_text_editor()->set_line_as_breakpoint(p_line, p_enabled);
+}
+
+void ScriptTextEditor::clear_breakpoints() {
+ code_editor->get_text_editor()->clear_breakpointed_lines();
+}
+
void ScriptTextEditor::set_tooltip_request_func(String p_method, Object *p_obj) {
code_editor->get_text_editor()->set_tooltip_request_func(p_obj, p_method, this);
}
@@ -1387,11 +1399,12 @@ Variant ScriptTextEditor::get_drag_data_fw(const Point2 &p_point, Control *p_fro
bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
Dictionary d = p_data;
- if (d.has("type") && (String(d["type"]) == "resource" ||
- String(d["type"]) == "files" ||
- String(d["type"]) == "nodes" ||
- String(d["type"]) == "obj_property" ||
- String(d["type"]) == "files_and_dirs")) {
+ if (d.has("type") &&
+ (String(d["type"]) == "resource" ||
+ String(d["type"]) == "files" ||
+ String(d["type"]) == "nodes" ||
+ String(d["type"]) == "obj_property" ||
+ String(d["type"]) == "files_and_dirs")) {
return true;
}
@@ -1449,7 +1462,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
Array files = d["files"];
String text_to_drop;
- bool preload = Input::get_singleton()->is_key_pressed(KEY_CTRL);
+ bool preload = Input::get_singleton()->is_key_pressed(Key::CTRL);
for (int i = 0; i < files.size(); i++) {
if (i > 0) {
text_to_drop += ", ";
@@ -1513,7 +1526,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
bool create_menu = false;
CodeEdit *tx = code_editor->get_text_editor();
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed()) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
local_pos = mb->get_global_position() - tx->get_global_position();
create_menu = true;
} else if (k.is_valid() && k->is_action("ui_menu", true)) {
@@ -1547,10 +1560,10 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
}
String word_at_pos = tx->get_word_at_pos(local_pos);
- if (word_at_pos == "") {
+ if (word_at_pos.is_empty()) {
word_at_pos = tx->get_word_under_caret();
}
- if (word_at_pos == "") {
+ if (word_at_pos.is_empty()) {
word_at_pos = tx->get_selected_text();
}
@@ -1598,7 +1611,7 @@ void ScriptTextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
float alpha = color.size() > 3 ? color[3] : 1.0f;
color_picker->set_pick_color(Color(color[0], color[1], color[2], alpha));
}
- color_panel->set_position(get_global_transform().xform(local_pos));
+ color_panel->set_position(get_screen_position() + local_pos);
} else {
has_color = false;
}
@@ -1616,10 +1629,7 @@ void ScriptTextEditor::_color_changed(const Color &p_color) {
}
String line = code_editor->get_text_editor()->get_line(color_position.x);
- int color_args_pos = line.find(color_args, color_position.y);
- String line_with_replaced_args = line;
- line_with_replaced_args.erase(color_args_pos, color_args.length());
- line_with_replaced_args = line_with_replaced_args.insert(color_args_pos, new_args);
+ String line_with_replaced_args = line.replace(color_args, new_args);
color_args = new_args;
code_editor->get_text_editor()->begin_complex_operation();
@@ -1678,8 +1688,8 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p
context_menu->set_item_disabled(context_menu->get_item_index(EDIT_UNDO), !tx->has_undo());
context_menu->set_item_disabled(context_menu->get_item_index(EDIT_REDO), !tx->has_redo());
- context_menu->set_position(get_global_transform().xform(p_pos));
- context_menu->set_size(Vector2(1, 1));
+ context_menu->set_position(get_screen_position() + p_pos);
+ context_menu->reset_size();
context_menu->popup();
}
@@ -1696,7 +1706,6 @@ void ScriptTextEditor::_enable_code_editor() {
code_editor->connect("show_warnings_panel", callable_mp(this, &ScriptTextEditor::_show_warnings_panel));
code_editor->connect("validate_script", callable_mp(this, &ScriptTextEditor::_validate_script));
code_editor->connect("load_theme_settings", callable_mp(this, &ScriptTextEditor::_load_theme_settings));
- code_editor->get_text_editor()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled));
code_editor->get_text_editor()->connect("symbol_lookup", callable_mp(this, &ScriptTextEditor::_lookup_symbol));
code_editor->get_text_editor()->connect("symbol_validate", callable_mp(this, &ScriptTextEditor::_validate_symbol));
code_editor->get_text_editor()->connect("gutter_added", callable_mp(this, &ScriptTextEditor::_update_gutter_indexes));
@@ -1795,9 +1804,9 @@ void ScriptTextEditor::_enable_code_editor() {
edit_menu->get_popup()->add_child(convert_case);
edit_menu->get_popup()->add_submenu_item(TTR("Convert Case"), "convert_case");
- convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KEY_MASK_SHIFT | KEY_F4), EDIT_TO_UPPERCASE);
- convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KEY_MASK_SHIFT | KEY_F5), EDIT_TO_LOWERCASE);
- convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KEY_MASK_SHIFT | KEY_F6), EDIT_CAPITALIZE);
+ convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_uppercase", TTR("Uppercase"), KeyModifierMask::SHIFT | Key::F4), EDIT_TO_UPPERCASE);
+ convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/convert_to_lowercase", TTR("Lowercase"), KeyModifierMask::SHIFT | Key::F5), EDIT_TO_LOWERCASE);
+ convert_case->add_shortcut(ED_SHORTCUT("script_text_editor/capitalize", TTR("Capitalize"), KeyModifierMask::SHIFT | Key::F6), EDIT_CAPITALIZE);
convert_case->connect("id_pressed", callable_mp(this, &ScriptTextEditor::_edit_option));
edit_menu->get_popup()->add_child(highlighter_menu);
@@ -1836,6 +1845,7 @@ ScriptTextEditor::ScriptTextEditor() {
code_editor->get_text_editor()->set_draw_breakpoints_gutter(true);
code_editor->get_text_editor()->set_draw_executing_lines_gutter(true);
+ code_editor->get_text_editor()->connect("breakpoint_toggled", callable_mp(this, &ScriptTextEditor::_breakpoint_toggled));
connection_gutter = 1;
code_editor->get_text_editor()->add_gutter(connection_gutter);
@@ -1943,70 +1953,60 @@ static ScriptEditorBase *create_editor(const RES &p_resource) {
}
void ScriptTextEditor::register_editor() {
- ED_SHORTCUT("script_text_editor/move_up", TTR("Move Up"), KEY_MASK_ALT | KEY_UP);
- ED_SHORTCUT("script_text_editor/move_down", TTR("Move Down"), KEY_MASK_ALT | KEY_DOWN);
- ED_SHORTCUT("script_text_editor/delete_line", TTR("Delete Line"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_K);
+ ED_SHORTCUT("script_text_editor/move_up", TTR("Move Up"), KeyModifierMask::ALT | Key::UP);
+ ED_SHORTCUT("script_text_editor/move_down", TTR("Move Down"), KeyModifierMask::ALT | Key::DOWN);
+ ED_SHORTCUT("script_text_editor/delete_line", TTR("Delete Line"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::K);
// Leave these at zero, same can be accomplished with tab/shift-tab, including selection.
// The next/previous in history shortcut in this case makes a lot more sense.
- ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), KEY_NONE);
- ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), KEY_NONE);
- ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD | KEY_K);
- ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KEY_MASK_ALT | KEY_F);
- ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), KEY_NONE);
- ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), KEY_NONE);
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C);
-#else
- ED_SHORTCUT("script_text_editor/duplicate_selection", TTR("Duplicate Selection"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_D);
-#endif
- ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_E);
- ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T);
- ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent to Spaces"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_Y);
- ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_I);
- ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KEY_MASK_CMD | KEY_I);
-
- ED_SHORTCUT_AND_COMMAND("script_text_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F);
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_MASK_CMD | KEY_G);
- ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_G);
- ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTR("Replace..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
-#else
- ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), KEY_F3);
- ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3);
- ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTR("Replace..."), KEY_MASK_CMD | KEY_R);
-#endif
-
- ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F);
- ED_SHORTCUT("script_text_editor/replace_in_files", TTR("Replace in Files..."), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R);
-
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_MASK_SHIFT | KEY_SPACE);
-#else
- ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_F1);
-#endif
-
- ED_SHORTCUT("script_text_editor/toggle_bookmark", TTR("Toggle Bookmark"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_B);
- ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTR("Go to Next Bookmark"), KEY_MASK_CMD | KEY_B);
- ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTR("Go to Previous Bookmark"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B);
- ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTR("Remove All Bookmarks"), KEY_NONE);
-
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KEY_MASK_CTRL | KEY_MASK_CMD | KEY_J);
-#else
- ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KEY_MASK_ALT | KEY_MASK_CMD | KEY_F);
-#endif
- ED_SHORTCUT("script_text_editor/goto_line", TTR("Go to Line..."), KEY_MASK_CMD | KEY_L);
-
-#ifdef OSX_ENABLED
- ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B);
-#else
- ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), KEY_F9);
-#endif
- ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F9);
- ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Go to Next Breakpoint"), KEY_MASK_CMD | KEY_PERIOD);
- ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Go to Previous Breakpoint"), KEY_MASK_CMD | KEY_COMMA);
+ ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), Key::NONE);
+ ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), Key::NONE);
+ ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KeyModifierMask::CMD | Key::K);
+ ED_SHORTCUT("script_text_editor/toggle_fold_line", TTR("Fold/Unfold Line"), KeyModifierMask::ALT | Key::F);
+ ED_SHORTCUT("script_text_editor/fold_all_lines", TTR("Fold All Lines"), Key::NONE);
+ ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), Key::NONE);
+ ED_SHORTCUT("script_text_editor/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::SHIFT | KeyModifierMask::CMD | Key::D);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/duplicate_selection", "macos", KeyModifierMask::SHIFT | KeyModifierMask::CMD | Key::C);
+ ED_SHORTCUT("script_text_editor/evaluate_selection", TTR("Evaluate Selection"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::E);
+ ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KeyModifierMask::CMD | KeyModifierMask::ALT | Key::T);
+ ED_SHORTCUT("script_text_editor/convert_indent_to_spaces", TTR("Convert Indent to Spaces"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::Y);
+ ED_SHORTCUT("script_text_editor/convert_indent_to_tabs", TTR("Convert Indent to Tabs"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::I);
+ ED_SHORTCUT("script_text_editor/auto_indent", TTR("Auto Indent"), KeyModifierMask::CMD | Key::I);
+
+ ED_SHORTCUT_AND_COMMAND("script_text_editor/find", TTR("Find..."), KeyModifierMask::CMD | Key::F);
+
+ ED_SHORTCUT("script_text_editor/find_next", TTR("Find Next"), Key::F3);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/find_next", "macos", KeyModifierMask::CMD | Key::G);
+
+ ED_SHORTCUT("script_text_editor/find_previous", TTR("Find Previous"), KeyModifierMask::SHIFT | Key::F3);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/find_previous", "macos", KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::G);
+
+ ED_SHORTCUT_AND_COMMAND("script_text_editor/replace", TTR("Replace..."), KeyModifierMask::CMD | Key::R);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/replace", "macos", KeyModifierMask::ALT | KeyModifierMask::CMD | Key::F);
+
+ ED_SHORTCUT("script_text_editor/find_in_files", TTR("Find in Files..."), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::F);
+ ED_SHORTCUT("script_text_editor/replace_in_files", TTR("Replace in Files..."), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::R);
+
+ ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KeyModifierMask::ALT | Key::F1);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/contextual_help", "macos", KeyModifierMask::ALT | KeyModifierMask::SHIFT | Key::SPACE);
+
+ ED_SHORTCUT("script_text_editor/toggle_bookmark", TTR("Toggle Bookmark"), KeyModifierMask::CMD | KeyModifierMask::ALT | Key::B);
+ ED_SHORTCUT("script_text_editor/goto_next_bookmark", TTR("Go to Next Bookmark"), KeyModifierMask::CMD | Key::B);
+ ED_SHORTCUT("script_text_editor/goto_previous_bookmark", TTR("Go to Previous Bookmark"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::B);
+ ED_SHORTCUT("script_text_editor/remove_all_bookmarks", TTR("Remove All Bookmarks"), Key::NONE);
+
+ ED_SHORTCUT("script_text_editor/goto_function", TTR("Go to Function..."), KeyModifierMask::ALT | KeyModifierMask::CMD | Key::F);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/goto_function", "macos", KeyModifierMask::CTRL | KeyModifierMask::CMD | Key::J);
+
+ ED_SHORTCUT("script_text_editor/goto_line", TTR("Go to Line..."), KeyModifierMask::CMD | Key::L);
+
+ ED_SHORTCUT("script_text_editor/toggle_breakpoint", TTR("Toggle Breakpoint"), Key::F9);
+ ED_SHORTCUT_OVERRIDE("script_text_editor/toggle_breakpoint", "macos", KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::B);
+
+ ED_SHORTCUT("script_text_editor/remove_all_breakpoints", TTR("Remove All Breakpoints"), KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::F9);
+ ED_SHORTCUT("script_text_editor/goto_next_breakpoint", TTR("Go to Next Breakpoint"), KeyModifierMask::CMD | Key::PERIOD);
+ ED_SHORTCUT("script_text_editor/goto_previous_breakpoint", TTR("Go to Previous Breakpoint"), KeyModifierMask::CMD | Key::COMMA);
ScriptEditor::register_create_script_editor_function(create_editor);
}
diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h
index 1ca6f56ea1..afe9a7453d 100644
--- a/editor/plugins/script_text_editor.h
+++ b/editor/plugins/script_text_editor.h
@@ -197,7 +197,7 @@ public:
virtual void add_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
virtual void set_syntax_highlighter(Ref<EditorSyntaxHighlighter> p_highlighter) override;
- void update_toggle_scripts_button();
+ void update_toggle_scripts_button() override;
virtual void apply_code() override;
virtual RES get_edited_resource() const override;
@@ -225,6 +225,8 @@ public:
virtual void reload(bool p_soft) override;
virtual Array get_breakpoints() override;
+ virtual void set_breakpoint(int p_line, bool p_enabled) override;
+ virtual void clear_breakpoints() override;
virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
virtual void update_settings() override;
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 22ca5592bd..fa23ccf200 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -34,6 +34,7 @@
#include "core/io/resource_saver.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
+#include "core/version_generated.gen.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/editor_settings.h"
@@ -131,9 +132,9 @@ void ShaderTextEditor::_load_theme_settings() {
List<String> built_ins;
if (shader.is_valid()) {
- for (const Map<StringName, ShaderLanguage::FunctionInfo>::Element *E = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())).front(); E; E = E->next()) {
- for (const Map<StringName, ShaderLanguage::BuiltInInfo>::Element *F = E->get().built_ins.front(); F; F = F->next()) {
- built_ins.push_back(F->key());
+ for (const KeyValue<StringName, ShaderLanguage::FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()))) {
+ for (const KeyValue<StringName, ShaderLanguage::BuiltInInfo> &F : E.value.built_ins) {
+ built_ins.push_back(F.key);
}
}
@@ -142,10 +143,10 @@ void ShaderTextEditor::_load_theme_settings() {
}
}
- const Color member_variable_color = EDITOR_GET("text_editor/theme/highlighting/member_variable_color");
+ const Color user_type_color = EDITOR_GET("text_editor/theme/highlighting/user_type_color");
for (const String &E : built_ins) {
- syntax_highlighter->add_keyword_color(E, member_variable_color);
+ syntax_highlighter->add_keyword_color(E, user_type_color);
}
// Colorize comments.
@@ -178,6 +179,10 @@ void ShaderTextEditor::_check_shader_mode() {
mode = Shader::MODE_CANVAS_ITEM;
} else if (type == "particles") {
mode = Shader::MODE_PARTICLES;
+ } else if (type == "sky") {
+ mode = Shader::MODE_SKY;
+ } else if (type == "fog") {
+ mode = Shader::MODE_FOG;
} else {
mode = Shader::MODE_SPATIAL;
}
@@ -199,7 +204,13 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptCo
ShaderLanguage sl;
String calltip;
- sl.complete(p_code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type, r_options, calltip);
+ ShaderLanguage::ShaderCompileInfo info;
+ info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()));
+ info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
+ info.shader_types = ShaderTypes::get_singleton()->get_types();
+ info.global_variable_type_func = _get_global_variable_type;
+
+ sl.complete(p_code, info, r_options, calltip);
get_text_editor()->set_code_hint(calltip);
}
@@ -211,12 +222,18 @@ void ShaderTextEditor::_validate_script() {
//List<StringName> params;
//shader->get_param_list(&params);
+ ShaderLanguage::ShaderCompileInfo info;
+ info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode()));
+ info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode()));
+ info.shader_types = ShaderTypes::get_singleton()->get_types();
+ info.global_variable_type_func = _get_global_variable_type;
+
ShaderLanguage sl;
sl.enable_warning_checking(saved_warnings_enabled);
sl.set_warning_flags(saved_warning_flags);
- Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type);
+ Error err = sl.compile(code, info);
if (err != OK) {
String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text();
@@ -380,7 +397,7 @@ void ShaderEditor::_menu_option(int p_option) {
shader_editor->remove_all_bookmarks();
} break;
case HELP_DOCS: {
- OS::get_singleton()->shell_open("https://docs.godotengine.org/en/latest/tutorials/shaders/shader_reference/index.html");
+ OS::get_singleton()->shell_open(vformat("%s/tutorials/shaders/shader_reference/index.html", VERSION_DOCS_URL));
} break;
}
if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) {
@@ -478,8 +495,7 @@ void ShaderEditor::_check_for_external_edit() {
return;
}
- // internal shader.
- if (shader->get_path() == "" || shader->get_path().find("local://") != -1 || shader->get_path().find("::") != -1) {
+ if (shader->is_built_in()) {
return;
}
@@ -526,7 +542,7 @@ void ShaderEditor::save_external_data(const String &p_str) {
}
apply_shaders();
- if (shader->get_path() != "" && shader->get_path().find("local://") == -1 && shader->get_path().find("::") == -1) {
+ if (!shader->is_built_in()) {
//external shader, save it
ResourceSaver::save(shader->get_path(), shader);
}
@@ -549,7 +565,7 @@ void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
Ref<InputEventMouseButton> mb = ev;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed()) {
+ if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
CodeEdit *tx = shader_editor->get_text_editor();
Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());
@@ -641,8 +657,8 @@ void ShaderEditor::_make_context_menu(bool p_selection, Vector2 p_position) {
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_comment"), EDIT_TOGGLE_COMMENT);
context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE);
- context_menu->set_position(get_global_transform().xform(p_position));
- context_menu->set_size(Vector2(1, 1));
+ context_menu->set_position(get_screen_position() + p_position);
+ context_menu->reset_size();
context_menu->popup();
}
diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp
index 1e62261244..517de1901f 100644
--- a/editor/plugins/shader_file_editor_plugin.cpp
+++ b/editor/plugins/shader_file_editor_plugin.cpp
@@ -97,7 +97,7 @@ void ShaderFileEditor::_version_selected(int p_option) {
error_text->push_font(get_theme_font(SNAME("source"), SNAME("EditorFonts")));
- if (error == String()) {
+ if (error.is_empty()) {
error_text->add_text(TTR("Shader stage compiled without errors."));
} else {
error_text->add_text(error);
@@ -107,7 +107,7 @@ void ShaderFileEditor::_version_selected(int p_option) {
void ShaderFileEditor::_update_options() {
ERR_FAIL_COND(shader_file.is_null());
- if (shader_file->get_base_error() != String()) {
+ if (!shader_file->get_base_error().is_empty()) {
stage_hb->hide();
versions->hide();
error_text->clear();
@@ -136,7 +136,7 @@ void ShaderFileEditor::_update_options() {
for (int i = 0; i < version_list.size(); i++) {
String title = version_list[i];
- if (title == "") {
+ if (title.is_empty()) {
title = "default";
}
@@ -148,7 +148,7 @@ void ShaderFileEditor::_update_options() {
bool failed = false;
for (int j = 0; j < RD::SHADER_STAGE_MAX; j++) {
String error = bytecode->get_stage_compile_error(RD::ShaderStage(j));
- if (error != String()) {
+ if (!error.is_empty()) {
failed = true;
}
}
@@ -182,7 +182,7 @@ void ShaderFileEditor::_update_options() {
for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {
Vector<uint8_t> bc = bytecode->get_stage_bytecode(RD::ShaderStage(i));
String error = bytecode->get_stage_compile_error(RD::ShaderStage(i));
- bool disable = error == String() && bc.is_empty();
+ bool disable = error.is_empty() && bc.is_empty();
stages[i]->set_disabled(disable);
if (!disable) {
if (stages[i]->is_pressed()) {
diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp
index 7ef680d7ef..510e264c48 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_2d_editor_plugin.cpp
@@ -52,34 +52,34 @@ void Skeleton2DEditor::_menu_option(int p_option) {
}
switch (p_option) {
- case MENU_OPTION_MAKE_REST: {
+ case MENU_OPTION_SET_REST: {
if (node->get_bone_count() == 0) {
err_dialog->set_text(TTR("This skeleton has no bones, create some children Bone2D nodes."));
err_dialog->popup_centered();
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- ur->create_action(TTR("Create Rest Pose from Bones"));
+ ur->create_action(TTR("Set Rest Pose to Bones"));
for (int i = 0; i < node->get_bone_count(); i++) {
Bone2D *bone = node->get_bone(i);
- ur->add_do_method(bone, "set_rest", bone->get_transform());
- ur->add_undo_method(bone, "set_rest", bone->get_rest());
+ ur->add_do_method(bone, "set_transform", bone->get_rest());
+ ur->add_undo_method(bone, "set_transform", bone->get_transform());
}
ur->commit_action();
} break;
- case MENU_OPTION_SET_REST: {
+ case MENU_OPTION_MAKE_REST: {
if (node->get_bone_count() == 0) {
err_dialog->set_text(TTR("This skeleton has no bones, create some children Bone2D nodes."));
err_dialog->popup_centered();
return;
}
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
- ur->create_action(TTR("Set Rest Pose to Bones"));
+ ur->create_action(TTR("Create Rest Pose from Bones"));
for (int i = 0; i < node->get_bone_count(); i++) {
Bone2D *bone = node->get_bone(i);
- ur->add_do_method(bone, "set_transform", bone->get_rest());
- ur->add_undo_method(bone, "set_transform", bone->get_transform());
+ ur->add_do_method(bone, "set_rest", bone->get_transform());
+ ur->add_undo_method(bone, "set_rest", bone->get_rest());
}
ur->commit_action();
@@ -98,9 +98,10 @@ Skeleton2DEditor::Skeleton2DEditor() {
options->set_text(TTR("Skeleton2D"));
options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton2D"), SNAME("EditorIcons")));
- options->get_popup()->add_item(TTR("Make Rest Pose (From Bones)"), MENU_OPTION_MAKE_REST);
+ options->get_popup()->add_item(TTR("Reset to Rest Pose"), MENU_OPTION_SET_REST);
options->get_popup()->add_separator();
- options->get_popup()->add_item(TTR("Set Bones to Rest Pose"), MENU_OPTION_SET_REST);
+ // Use the "Overwrite" word to highlight that this is a destructive operation.
+ options->get_popup()->add_item(TTR("Overwrite Rest Pose"), MENU_OPTION_MAKE_REST);
options->set_switch_on_hover(true);
options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton2DEditor::_menu_option));
diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h
index dacd8fe43f..066888f685 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.h
+++ b/editor/plugins/skeleton_2d_editor_plugin.h
@@ -40,8 +40,8 @@ class Skeleton2DEditor : public Control {
GDCLASS(Skeleton2DEditor, Control);
enum Menu {
- MENU_OPTION_MAKE_REST,
MENU_OPTION_SET_REST,
+ MENU_OPTION_MAKE_REST,
};
Skeleton2D *node;
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 309821b3dc..bb5ef0f6eb 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -37,298 +37,318 @@
#include "editor/plugins/animation_player_editor_plugin.h"
#include "node_3d_editor_plugin.h"
#include "scene/3d/collision_shape_3d.h"
+#include "scene/3d/joint_3d.h"
#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/physics_body_3d.h"
-#include "scene/3d/physics_joint_3d.h"
#include "scene/resources/capsule_shape_3d.h"
#include "scene/resources/sphere_shape_3d.h"
+#include "scene/resources/surface_tool.h"
void BoneTransformEditor::create_editors() {
const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
section = memnew(EditorInspectorSection);
section->setup("trf_properties", label, this, section_color, true);
+ section->unfold();
add_child(section);
- key_button = memnew(Button);
- key_button->set_text(TTR("Key Transform"));
- key_button->set_visible(keyable);
- key_button->set_icon(get_theme_icon(SNAME("Key"), SNAME("EditorIcons")));
- key_button->set_flat(true);
- section->get_vbox()->add_child(key_button);
-
- enabled_checkbox = memnew(CheckBox(TTR("Pose Enabled")));
- enabled_checkbox->set_flat(true);
- enabled_checkbox->set_visible(toggle_enabled);
+ enabled_checkbox = memnew(EditorPropertyCheck());
+ enabled_checkbox->set_label("Pose Enabled");
+ enabled_checkbox->set_selectable(false);
+ enabled_checkbox->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
section->get_vbox()->add_child(enabled_checkbox);
- // Translation property
- translation_property = memnew(EditorPropertyVector3());
- translation_property->setup(-10000, 10000, 0.001f, true);
- translation_property->set_label("Translation");
- translation_property->set_use_folding(true);
- translation_property->set_read_only(false);
- translation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
- section->get_vbox()->add_child(translation_property);
-
- // Rotation property
- rotation_property = memnew(EditorPropertyVector3());
+ // Position property.
+ position_property = memnew(EditorPropertyVector3());
+ position_property->setup(-10000, 10000, 0.001f, true);
+ position_property->set_label("Position");
+ position_property->set_selectable(false);
+ position_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+ position_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
+ section->get_vbox()->add_child(position_property);
+
+ // Rotation property.
+ rotation_property = memnew(EditorPropertyQuaternion());
rotation_property->setup(-10000, 10000, 0.001f, true);
- rotation_property->set_label("Rotation Degrees");
- rotation_property->set_use_folding(true);
- rotation_property->set_read_only(false);
- rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+ rotation_property->set_label("Rotation");
+ rotation_property->set_selectable(false);
+ rotation_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+ rotation_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
section->get_vbox()->add_child(rotation_property);
- // Scale property
+ // Scale property.
scale_property = memnew(EditorPropertyVector3());
scale_property->setup(-10000, 10000, 0.001f, true);
scale_property->set_label("Scale");
- scale_property->set_use_folding(true);
- scale_property->set_read_only(false);
- scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_vector3));
+ scale_property->set_selectable(false);
+ scale_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed));
+ scale_property->connect("property_keyed", callable_mp(this, &BoneTransformEditor::_property_keyed));
section->get_vbox()->add_child(scale_property);
- // Transform/Matrix section
- transform_section = memnew(EditorInspectorSection);
- transform_section->setup("trf_properties_transform", "Matrix", this, section_color, true);
- section->get_vbox()->add_child(transform_section);
-
- // Transform/Matrix property
- transform_property = memnew(EditorPropertyTransform3D());
- transform_property->setup(-10000, 10000, 0.001f, true);
- transform_property->set_label("Transform");
- transform_property->set_use_folding(true);
- transform_property->set_read_only(false);
- transform_property->connect("property_changed", callable_mp(this, &BoneTransformEditor::_value_changed_transform));
- transform_section->get_vbox()->add_child(transform_property);
+ // Transform/Matrix section.
+ rest_section = memnew(EditorInspectorSection);
+ rest_section->setup("trf_properties_transform", "Rest", this, section_color, true);
+ section->get_vbox()->add_child(rest_section);
+
+ // Transform/Matrix property.
+ rest_matrix = memnew(EditorPropertyTransform3D());
+ rest_matrix->setup(-10000, 10000, 0.001f, true);
+ rest_matrix->set_label("Transform");
+ rest_matrix->set_selectable(false);
+ rest_section->get_vbox()->add_child(rest_matrix);
}
void BoneTransformEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
create_editors();
- key_button->connect("pressed", callable_mp(this, &BoneTransformEditor::_key_button_pressed));
- enabled_checkbox->connect("toggled", callable_mp(this, &BoneTransformEditor::_checkbox_toggled));
- [[fallthrough]];
- }
- case NOTIFICATION_SORT_CHILDREN: {
- const Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Tree"));
- int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Tree"));
-
- Point2 buffer;
- buffer.x += get_theme_constant(SNAME("inspector_margin"), SNAME("Editor"));
- buffer.y += font->get_height(font_size);
- buffer.y += get_theme_constant(SNAME("vseparation"), SNAME("Tree"));
-
- const float vector_height = translation_property->get_size().y;
- const float transform_height = transform_property->get_size().y;
- const float button_height = key_button->get_size().y;
-
- const float width = get_size().x - get_theme_constant(SNAME("inspector_margin"), SNAME("Editor"));
- Vector<Rect2> input_rects;
- if (keyable && section->get_vbox()->is_visible()) {
- input_rects.push_back(Rect2(key_button->get_position() + buffer, Size2(width, button_height)));
- } else {
- input_rects.push_back(Rect2(0, 0, 0, 0));
- }
-
- if (section->get_vbox()->is_visible()) {
- input_rects.push_back(Rect2(translation_property->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(rotation_property->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(scale_property->get_position() + buffer, Size2(width, vector_height)));
- input_rects.push_back(Rect2(transform_property->get_position() + buffer, Size2(width, transform_height)));
- } else {
- const int32_t start = input_rects.size();
- const int32_t empty_input_rect_elements = 4;
- const int32_t end = start + empty_input_rect_elements;
- for (int i = start; i < end; ++i) {
- input_rects.push_back(Rect2(0, 0, 0, 0));
- }
- }
-
- for (int32_t i = 0; i < input_rects.size(); i++) {
- background_rects[i] = input_rects[i];
- }
-
- update();
- break;
- }
- case NOTIFICATION_DRAW: {
- const Color dark_color = get_theme_color(SNAME("dark_color_2"), SNAME("Editor"));
-
- for (int i = 0; i < 5; ++i) {
- draw_rect(background_rects[i], dark_color);
- }
-
break;
}
}
}
-void BoneTransformEditor::_value_changed(const double p_value) {
+void BoneTransformEditor::_value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
if (updating) {
return;
}
+ if (skeleton) {
+ undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+ undo_redo->add_undo_property(skeleton, p_property, skeleton->get(p_property));
+ undo_redo->add_do_property(skeleton, p_property, p_value);
+ undo_redo->commit_action();
+ }
+}
- Transform3D tform = compute_transform_from_vector3s();
- _change_transform(tform);
+BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
+ skeleton(p_skeleton) {
+ undo_redo = EditorNode::get_undo_redo();
}
-void BoneTransformEditor::_value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean) {
- if (updating) {
- return;
- }
- Transform3D tform = compute_transform_from_vector3s();
- _change_transform(tform);
+void BoneTransformEditor::set_keyable(const bool p_keyable) {
+ position_property->set_keying(p_keyable);
+ rotation_property->set_keying(p_keyable);
+ scale_property->set_keying(p_keyable);
}
-Transform3D BoneTransformEditor::compute_transform_from_vector3s() const {
- // Convert rotation from degrees to radians.
- Vector3 prop_rotation = rotation_property->get_vector();
- prop_rotation.x = Math::deg2rad(prop_rotation.x);
- prop_rotation.y = Math::deg2rad(prop_rotation.y);
- prop_rotation.z = Math::deg2rad(prop_rotation.z);
+void BoneTransformEditor::set_target(const String &p_prop) {
+ enabled_checkbox->set_object_and_property(skeleton, p_prop + "enabled");
+ enabled_checkbox->update_property();
- return Transform3D(
- Basis(prop_rotation, scale_property->get_vector()),
- translation_property->get_vector());
-}
+ position_property->set_object_and_property(skeleton, p_prop + "position");
+ position_property->update_property();
-void BoneTransformEditor::_value_changed_transform(const String p_property_name, const Transform3D p_transform, const StringName p_edited_property_name, const bool p_boolean) {
- if (updating) {
- return;
- }
- _change_transform(p_transform);
-}
+ rotation_property->set_object_and_property(skeleton, p_prop + "rotation");
+ rotation_property->update_property();
-void BoneTransformEditor::_change_transform(Transform3D p_new_transform) {
- if (property.get_slicec('/', 0) == "bones" && property.get_slicec('/', 2) == "custom_pose") {
- undo_redo->create_action(TTR("Set Custom Bone Pose Transform"), UndoRedo::MERGE_ENDS);
- undo_redo->add_undo_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), skeleton->get_bone_custom_pose(property.get_slicec('/', 1).to_int()));
- undo_redo->add_do_method(skeleton, "set_bone_custom_pose", property.get_slicec('/', 1).to_int(), p_new_transform);
- undo_redo->commit_action();
- } else if (property.get_slicec('/', 0) == "bones") {
- undo_redo->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
- undo_redo->add_undo_property(skeleton, property, skeleton->get(property));
- undo_redo->add_do_property(skeleton, property, p_new_transform);
- undo_redo->commit_action();
- }
+ scale_property->set_object_and_property(skeleton, p_prop + "scale");
+ scale_property->update_property();
+
+ rest_matrix->set_object_and_property(skeleton, p_prop + "rest");
+ rest_matrix->update_property();
}
-void BoneTransformEditor::update_enabled_checkbox() {
- if (enabled_checkbox) {
- const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
- const bool is_enabled = skeleton->get(path);
- enabled_checkbox->set_pressed(is_enabled);
+void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) {
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+ PackedStringArray split = p_path.split("/");
+ if (split.size() == 3 && split[0] == "bones") {
+ int bone_idx = split[1].to_int();
+ if (split[2] == "position") {
+ te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_POSITION_3D, skeleton->get(p_path));
+ }
+ if (split[2] == "rotation") {
+ te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_ROTATION_3D, skeleton->get(p_path));
+ }
+ if (split[2] == "scale") {
+ te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_SCALE_3D, skeleton->get(p_path));
+ }
}
}
void BoneTransformEditor::_update_properties() {
- if (updating) {
+ if (!skeleton) {
return;
}
-
- if (skeleton == nullptr) {
- return;
+ int selected = Skeleton3DEditor::get_singleton()->get_selected_bone();
+ List<PropertyInfo> props;
+ skeleton->get_property_list(&props);
+ for (const PropertyInfo &E : props) {
+ PackedStringArray split = E.name.split("/");
+ if (split.size() == 3 && split[0] == "bones") {
+ if (split[1].to_int() == selected) {
+ if (split[2] == "enabled") {
+ enabled_checkbox->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ enabled_checkbox->update_property();
+ enabled_checkbox->update();
+ }
+ if (split[2] == "position") {
+ position_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ position_property->update_property();
+ position_property->update();
+ }
+ if (split[2] == "rotation") {
+ rotation_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ rotation_property->update_property();
+ rotation_property->update();
+ }
+ if (split[2] == "scale") {
+ scale_property->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ scale_property->update_property();
+ scale_property->update();
+ }
+ if (split[2] == "rest") {
+ rest_matrix->set_read_only(E.usage & PROPERTY_USAGE_READ_ONLY);
+ rest_matrix->update_property();
+ rest_matrix->update();
+ }
+ }
+ }
}
-
- updating = true;
-
- Transform3D tform = skeleton->get(property);
- _update_transform_properties(tform);
}
-void BoneTransformEditor::_update_custom_pose_properties() {
- if (updating) {
- return;
- }
+Skeleton3DEditor *Skeleton3DEditor::singleton = nullptr;
- if (skeleton == nullptr) {
- return;
+void Skeleton3DEditor::set_keyable(const bool p_keyable) {
+ keyable = p_keyable;
+ if (p_keyable) {
+ animation_hb->show();
+ } else {
+ animation_hb->hide();
}
+};
- updating = true;
-
- Transform3D tform = skeleton->get_bone_custom_pose(property.to_int());
- _update_transform_properties(tform);
-}
-
-void BoneTransformEditor::_update_transform_properties(Transform3D tform) {
- Basis rotation_basis = tform.get_basis();
- Vector3 rotation_radians = rotation_basis.get_rotation_euler();
- Vector3 rotation_degrees = Vector3(Math::rad2deg(rotation_radians.x), Math::rad2deg(rotation_radians.y), Math::rad2deg(rotation_radians.z));
- Vector3 translation = tform.get_origin();
- Vector3 scale = tform.basis.get_scale();
-
- translation_property->update_using_vector(translation);
- rotation_property->update_using_vector(rotation_degrees);
- scale_property->update_using_vector(scale);
- transform_property->update_using_transform(tform);
-
- update_enabled_checkbox();
- updating = false;
-}
+void Skeleton3DEditor::set_bone_options_enabled(const bool p_bone_options_enabled) {
+ skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INIT_SELECTED_POSES, !p_bone_options_enabled);
+ skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_SELECTED_POSES_TO_RESTS, !p_bone_options_enabled);
+};
-BoneTransformEditor::BoneTransformEditor(Skeleton3D *p_skeleton) :
- skeleton(p_skeleton) {
- undo_redo = EditorNode::get_undo_redo();
-}
+void Skeleton3DEditor::_on_click_skeleton_option(int p_skeleton_option) {
+ if (!skeleton) {
+ return;
+ }
-void BoneTransformEditor::set_target(const String &p_prop) {
- property = p_prop;
+ switch (p_skeleton_option) {
+ case SKELETON_OPTION_INIT_ALL_POSES: {
+ init_pose(true);
+ break;
+ }
+ case SKELETON_OPTION_INIT_SELECTED_POSES: {
+ init_pose(false);
+ break;
+ }
+ case SKELETON_OPTION_ALL_POSES_TO_RESTS: {
+ pose_to_rest(true);
+ break;
+ }
+ case SKELETON_OPTION_SELECTED_POSES_TO_RESTS: {
+ pose_to_rest(false);
+ break;
+ }
+ case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {
+ create_physical_skeleton();
+ break;
+ }
+ }
}
-void BoneTransformEditor::set_keyable(const bool p_keyable) {
- keyable = p_keyable;
- if (key_button) {
- key_button->set_visible(p_keyable);
+void Skeleton3DEditor::init_pose(const bool p_all_bones) {
+ if (!skeleton) {
+ return;
+ }
+ const int bone_len = skeleton->get_bone_count();
+ if (!bone_len) {
+ return;
}
-}
-void BoneTransformEditor::set_toggle_enabled(const bool p_enabled) {
- toggle_enabled = p_enabled;
- if (enabled_checkbox) {
- enabled_checkbox->set_visible(p_enabled);
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
+ if (p_all_bones) {
+ for (int i = 0; i < bone_len; i++) {
+ Transform3D rest = skeleton->get_bone_rest(i);
+ ur->add_do_method(skeleton, "set_bone_pose_position", i, rest.origin);
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", i, rest.basis.get_rotation_quaternion());
+ ur->add_do_method(skeleton, "set_bone_pose_scale", i, rest.basis.get_scale());
+ ur->add_undo_method(skeleton, "set_bone_pose_position", i, skeleton->get_bone_pose_position(i));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));
+ ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));
+ }
+ } else {
+ // Todo: Do method with multiple bone selection.
+ if (selected_bone == -1) {
+ ur->commit_action();
+ return;
+ }
+ Transform3D rest = skeleton->get_bone_rest(selected_bone);
+ ur->add_do_method(skeleton, "set_bone_pose_position", selected_bone, rest.origin);
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", selected_bone, rest.basis.get_rotation_quaternion());
+ ur->add_do_method(skeleton, "set_bone_pose_scale", selected_bone, rest.basis.get_scale());
+ ur->add_undo_method(skeleton, "set_bone_pose_position", selected_bone, skeleton->get_bone_pose_position(selected_bone));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", selected_bone, skeleton->get_bone_pose_rotation(selected_bone));
+ ur->add_undo_method(skeleton, "set_bone_pose_scale", selected_bone, skeleton->get_bone_pose_scale(selected_bone));
}
+ ur->commit_action();
}
-void BoneTransformEditor::_key_button_pressed() {
- if (skeleton == nullptr) {
+void Skeleton3DEditor::insert_keys(const bool p_all_bones) {
+ if (!skeleton) {
return;
}
- const BoneId bone_id = property.get_slicec('/', 1).to_int();
- const String name = skeleton->get_bone_name(bone_id);
+ bool pos_enabled = key_loc_button->is_pressed();
+ bool rot_enabled = key_rot_button->is_pressed();
+ bool scl_enabled = key_scale_button->is_pressed();
- if (name.is_empty()) {
- return;
- }
+ int bone_len = skeleton->get_bone_count();
+ Node *root = EditorNode::get_singleton()->get_tree()->get_root();
+ String path = root->get_path_to(skeleton);
- // Need to normalize the basis before you key it
- Transform3D tform = compute_transform_from_vector3s();
- tform.orthonormalize();
- AnimationPlayerEditor::singleton->get_track_editor()->insert_transform_key(skeleton, name, tform);
-}
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+ te->make_insert_queue();
+ for (int i = 0; i < bone_len; i++) {
+ const String name = skeleton->get_bone_name(i);
-void BoneTransformEditor::_checkbox_toggled(const bool p_toggled) {
- if (enabled_checkbox) {
- const String path = "bones/" + property.get_slicec('/', 1) + "/enabled";
- skeleton->set(path, p_toggled);
+ if (name.is_empty()) {
+ continue;
+ }
+
+ if (pos_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_POSITION_3D))) {
+ te->insert_transform_key(skeleton, name, Animation::TYPE_POSITION_3D, skeleton->get_bone_pose_position(i));
+ }
+ if (rot_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_ROTATION_3D))) {
+ te->insert_transform_key(skeleton, name, Animation::TYPE_ROTATION_3D, skeleton->get_bone_pose_rotation(i));
+ }
+ if (scl_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_SCALE_3D))) {
+ te->insert_transform_key(skeleton, name, Animation::TYPE_SCALE_3D, skeleton->get_bone_pose_scale(i));
+ }
}
+ te->commit_insert_queue();
}
-void Skeleton3DEditor::_on_click_option(int p_option) {
+void Skeleton3DEditor::pose_to_rest(const bool p_all_bones) {
if (!skeleton) {
return;
}
+ const int bone_len = skeleton->get_bone_count();
+ if (!bone_len) {
+ return;
+ }
- switch (p_option) {
- case MENU_OPTION_CREATE_PHYSICAL_SKELETON: {
- create_physical_skeleton();
- break;
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
+ if (p_all_bones) {
+ for (int i = 0; i < bone_len; i++) {
+ ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_pose(i));
+ ur->add_undo_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i));
}
+ } else {
+ // Todo: Do method with multiple bone selection.
+ if (selected_bone == -1) {
+ ur->commit_action();
+ return;
+ }
+ ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));
+ ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
}
+ ur->commit_action();
}
void Skeleton3DEditor::create_physical_skeleton() {
@@ -356,7 +376,7 @@ void Skeleton3DEditor::create_physical_skeleton() {
bones_infos.write[bone_id].relative_rest = bones_infos[parent].relative_rest * skeleton->get_bone_rest(bone_id);
- /// create physical bone on parent
+ // Create physical bone on parent.
if (!bones_infos[parent].physical_bone) {
bones_infos.write[parent].physical_bone = create_physical_bone(parent, bone_id, bones_infos);
@@ -370,7 +390,7 @@ void Skeleton3DEditor::create_physical_skeleton() {
bones_infos[parent].physical_bone->set_owner(owner);
bones_infos[parent].physical_bone->get_child(0)->set_owner(owner); // set shape owner
- /// Create joint between parent of parent
+ // Create joint between parent of parent.
if (-1 != parent_parent) {
bones_infos[parent].physical_bone->set_joint_type(PhysicalBone3D::JOINT_TYPE_PIN);
}
@@ -483,7 +503,7 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se
ERR_FAIL_NULL(skeleton);
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
ur->create_action(TTR("Set Bone Parentage"));
- // If the target is a child of ourselves, we move only *us* and not our children
+ // If the target is a child of ourselves, we move only *us* and not our children.
if (skeleton->is_bone_parent_of(p_target_boneidx, p_selected_boneidx)) {
const BoneId parent_idx = skeleton->get_bone_parent(p_selected_boneidx);
const int bone_count = skeleton->get_bone_count();
@@ -505,43 +525,39 @@ void Skeleton3DEditor::move_skeleton_bone(NodePath p_skeleton_path, int32_t p_se
void Skeleton3DEditor::_joint_tree_selection_changed() {
TreeItem *selected = joint_tree->get_selected();
- const String path = selected->get_metadata(0);
-
- if (path.begins_with("bones/")) {
- const int b_idx = path.get_slicec('/', 1).to_int();
- const String bone_path = "bones/" + itos(b_idx) + "/";
+ if (selected) {
+ const String path = selected->get_metadata(0);
- pose_editor->set_target(bone_path + "pose");
- rest_editor->set_target(bone_path + "rest");
- custom_pose_editor->set_target(bone_path + "custom_pose");
+ if (path.begins_with("bones/")) {
+ const int b_idx = path.get_slicec('/', 1).to_int();
+ const String bone_path = "bones/" + itos(b_idx) + "/";
- _update_properties();
-
- pose_editor->set_visible(true);
- rest_editor->set_visible(true);
- custom_pose_editor->set_visible(true);
+ pose_editor->set_target(bone_path);
+ pose_editor->set_keyable(keyable);
+ selected_bone = b_idx;
+ }
}
+ pose_editor->set_visible(selected);
+ set_bone_options_enabled(selected);
+ _update_properties();
+ _update_gizmo_visible();
}
+// May be not used with single select mode.
void Skeleton3DEditor::_joint_tree_rmb_select(const Vector2 &p_pos) {
}
void Skeleton3DEditor::_update_properties() {
- if (rest_editor) {
- rest_editor->_update_properties();
- }
if (pose_editor) {
pose_editor->_update_properties();
}
- if (custom_pose_editor) {
- custom_pose_editor->_update_custom_pose_properties();
- }
+ Node3DEditor::get_singleton()->update_transform_gizmo();
}
void Skeleton3DEditor::update_joint_tree() {
joint_tree->clear();
- if (skeleton == nullptr) {
+ if (!skeleton) {
return;
}
@@ -569,7 +585,7 @@ void Skeleton3DEditor::update_joint_tree() {
joint_item->set_selectable(0, true);
joint_item->set_metadata(0, "bones/" + itos(current_bone_idx));
- // Add the bone's children to the list of bones to be processed
+ // Add the bone's children to the list of bones to be processed.
Vector<int> current_bone_child_bones = skeleton->get_bone_children(current_bone_idx);
int child_bone_size = current_bone_child_bones.size();
for (int i = 0; i < child_bone_size; i++) {
@@ -587,17 +603,96 @@ void Skeleton3DEditor::create_editors() {
set_focus_mode(FOCUS_ALL);
- // Create Top Menu Bar
- options = memnew(MenuButton);
- Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+ AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
+
+ // Create Top Menu Bar.
+ separator = memnew(VSeparator);
+ ne->add_control_to_menu_panel(separator);
- options->set_text(TTR("Skeleton3D"));
- options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton3D"), SNAME("EditorIcons")));
+ // Create Skeleton Option in Top Menu Bar.
+ skeleton_options = memnew(MenuButton);
+ ne->add_control_to_menu_panel(skeleton_options);
- options->get_popup()->add_item(TTR("Create physical skeleton"), MENU_OPTION_CREATE_PHYSICAL_SKELETON);
+ skeleton_options->set_text(TTR("Skeleton3D"));
+ skeleton_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Skeleton3D"), SNAME("EditorIcons")));
- options->get_popup()->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_option));
+ // Skeleton options.
+ PopupMenu *p = skeleton_options->get_popup();
+ p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_all_poses", TTR("Init all Poses")), SKELETON_OPTION_INIT_ALL_POSES);
+ p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_selected_poses", TTR("Init selected Poses")), SKELETON_OPTION_INIT_SELECTED_POSES);
+ p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/all_poses_to_rests", TTR("Apply all poses to rests")), SKELETON_OPTION_ALL_POSES_TO_RESTS);
+ p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/selected_poses_to_rests", TTR("Apply selected poses to rests")), SKELETON_OPTION_SELECTED_POSES_TO_RESTS);
+ p->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
+ p->connect("id_pressed", callable_mp(this, &Skeleton3DEditor::_on_click_skeleton_option));
+ set_bone_options_enabled(false);
+
+ Vector<Variant> button_binds;
+ button_binds.resize(1);
+
+ edit_mode_button = memnew(Button);
+ ne->add_control_to_menu_panel(edit_mode_button);
+ edit_mode_button->set_flat(true);
+ edit_mode_button->set_toggle_mode(true);
+ edit_mode_button->set_focus_mode(FOCUS_NONE);
+ edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
+ edit_mode_button->connect("toggled", callable_mp(this, &Skeleton3DEditor::edit_mode_toggled));
+
+ edit_mode = false;
+
+ if (skeleton) {
+ skeleton->add_child(handles_mesh_instance);
+ handles_mesh_instance->set_skeleton_path(NodePath(""));
+ }
+
+ // Keying buttons.
+ animation_hb = memnew(HBoxContainer);
+ ne->add_control_to_menu_panel(animation_hb);
+ animation_hb->add_child(memnew(VSeparator));
+ animation_hb->hide();
+
+ key_loc_button = memnew(Button);
+ key_loc_button->set_flat(true);
+ key_loc_button->set_toggle_mode(true);
+ key_loc_button->set_pressed(false);
+ key_loc_button->set_focus_mode(FOCUS_NONE);
+ key_loc_button->set_tooltip(TTR("Translation mask for inserting keys."));
+ animation_hb->add_child(key_loc_button);
+
+ key_rot_button = memnew(Button);
+ key_rot_button->set_flat(true);
+ key_rot_button->set_toggle_mode(true);
+ key_rot_button->set_pressed(true);
+ key_rot_button->set_focus_mode(FOCUS_NONE);
+ key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys."));
+ animation_hb->add_child(key_rot_button);
+
+ key_scale_button = memnew(Button);
+ key_scale_button->set_flat(true);
+ key_scale_button->set_toggle_mode(true);
+ key_scale_button->set_pressed(false);
+ key_scale_button->set_focus_mode(FOCUS_NONE);
+ key_scale_button->set_tooltip(TTR("Scale mask for inserting keys."));
+ animation_hb->add_child(key_scale_button);
+
+ key_insert_button = memnew(Button);
+ key_insert_button->set_flat(true);
+ key_insert_button->set_focus_mode(FOCUS_NONE);
+ key_insert_button->connect("pressed", callable_mp(this, &Skeleton3DEditor::insert_keys), varray(false));
+ key_insert_button->set_tooltip(TTR("Insert key of bone poses already exist track."));
+ key_insert_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_to_existing_tracks", TTR("Insert Key (Existing Tracks)"), Key::INSERT));
+ animation_hb->add_child(key_insert_button);
+
+ key_insert_all_button = memnew(Button);
+ key_insert_all_button->set_flat(true);
+ key_insert_all_button->set_focus_mode(FOCUS_NONE);
+ key_insert_all_button->connect("pressed", callable_mp(this, &Skeleton3DEditor::insert_keys), varray(true));
+ key_insert_all_button->set_tooltip(TTR("Insert key of all bone poses."));
+ key_insert_all_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_of_all_bones", TTR("Insert Key (All Bones)"), KeyModifierMask::CMD + Key::INSERT));
+ animation_hb->add_child(key_insert_all_button);
+
+ // Bone tree.
const Color section_color = get_theme_color(SNAME("prop_subsection"), SNAME("Editor"));
EditorInspectorSection *bones_section = memnew(EditorInspectorSection);
@@ -612,7 +707,7 @@ void Skeleton3DEditor::create_editors() {
joint_tree = memnew(Tree);
joint_tree->set_columns(1);
- joint_tree->set_focus_mode(Control::FocusMode::FOCUS_NONE);
+ joint_tree->set_focus_mode(Control::FOCUS_NONE);
joint_tree->set_select_mode(Tree::SELECT_SINGLE);
joint_tree->set_hide_root(true);
joint_tree->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -622,37 +717,37 @@ void Skeleton3DEditor::create_editors() {
s_con->add_child(joint_tree);
pose_editor = memnew(BoneTransformEditor(skeleton));
- pose_editor->set_label(TTR("Bone Pose"));
- pose_editor->set_keyable(AnimationPlayerEditor::singleton->get_track_editor()->has_keying());
- pose_editor->set_toggle_enabled(true);
+ pose_editor->set_label(TTR("Bone Transform"));
pose_editor->set_visible(false);
add_child(pose_editor);
- rest_editor = memnew(BoneTransformEditor(skeleton));
- rest_editor->set_label(TTR("Bone Rest"));
- rest_editor->set_visible(false);
- add_child(rest_editor);
-
- custom_pose_editor = memnew(BoneTransformEditor(skeleton));
- custom_pose_editor->set_label(TTR("Bone Custom Pose"));
- custom_pose_editor->set_visible(false);
- add_child(custom_pose_editor);
+ set_keyable(te->has_keying());
}
void Skeleton3DEditor::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_READY: {
+ edit_mode_button->set_icon(get_theme_icon(SNAME("ToolBoneSelect"), SNAME("EditorIcons")));
+ key_loc_button->set_icon(get_theme_icon(SNAME("KeyPosition"), SNAME("EditorIcons")));
+ key_rot_button->set_icon(get_theme_icon(SNAME("KeyRotation"), SNAME("EditorIcons")));
+ key_scale_button->set_icon(get_theme_icon(SNAME("KeyScale"), SNAME("EditorIcons")));
+ key_insert_button->set_icon(get_theme_icon(SNAME("Key"), SNAME("EditorIcons")));
+ key_insert_all_button->set_icon(get_theme_icon(SNAME("NewKey"), SNAME("EditorIcons")));
+ get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
+ break;
+ }
case NOTIFICATION_ENTER_TREE: {
create_editors();
update_joint_tree();
update_editors();
-
- get_tree()->connect("node_removed", callable_mp(this, &Skeleton3DEditor::_node_removed), Vector<Variant>(), Object::CONNECT_ONESHOT);
joint_tree->connect("item_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_selection_changed));
joint_tree->connect("item_rmb_selected", callable_mp(this, &Skeleton3DEditor::_joint_tree_rmb_select));
#ifdef TOOLS_ENABLED
+ skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_draw_gizmo));
skeleton->connect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
-#endif // TOOLS_ENABLED
-
+ skeleton->connect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_bone_enabled_changed));
+ skeleton->connect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_gizmo_visible));
+#endif
break;
}
}
@@ -661,7 +756,7 @@ void Skeleton3DEditor::_notification(int p_what) {
void Skeleton3DEditor::_node_removed(Node *p_node) {
if (skeleton && p_node == skeleton) {
skeleton = nullptr;
- options->hide();
+ skeleton_options->hide();
}
_update_properties();
@@ -672,23 +767,237 @@ void Skeleton3DEditor::_bind_methods() {
ClassDB::bind_method(D_METHOD("_joint_tree_selection_changed"), &Skeleton3DEditor::_joint_tree_selection_changed);
ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &Skeleton3DEditor::_joint_tree_rmb_select);
ClassDB::bind_method(D_METHOD("_update_properties"), &Skeleton3DEditor::_update_properties);
- ClassDB::bind_method(D_METHOD("_on_click_option"), &Skeleton3DEditor::_on_click_option);
+ ClassDB::bind_method(D_METHOD("_on_click_skeleton_option"), &Skeleton3DEditor::_on_click_skeleton_option);
+
+ ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw);
+ ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("drop_data_fw"), &Skeleton3DEditor::drop_data_fw);
- ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &Skeleton3DEditor::get_drag_data_fw);
- ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &Skeleton3DEditor::can_drop_data_fw);
- ClassDB::bind_method(D_METHOD("_drop_data_fw"), &Skeleton3DEditor::drop_data_fw);
ClassDB::bind_method(D_METHOD("move_skeleton_bone"), &Skeleton3DEditor::move_skeleton_bone);
+
+ ClassDB::bind_method(D_METHOD("_draw_gizmo"), &Skeleton3DEditor::_draw_gizmo);
+}
+
+void Skeleton3DEditor::edit_mode_toggled(const bool pressed) {
+ edit_mode = pressed;
+ _update_gizmo_visible();
}
Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *p_skeleton) :
editor(p_editor),
editor_plugin(e_plugin),
skeleton(p_skeleton) {
+ singleton = this;
+
+ // Handle.
+ handle_material = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+ handle_shader = Ref<Shader>(memnew(Shader));
+ handle_shader->set_code(R"(
+// Skeleton 3D gizmo handle shader.
+
+shader_type spatial;
+render_mode unshaded, shadows_disabled, depth_draw_always;
+uniform sampler2D texture_albedo : hint_albedo;
+uniform float point_size : hint_range(0,128) = 32;
+void vertex() {
+ if (!OUTPUT_IS_SRGB) {
+ COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );
+ }
+ VERTEX = VERTEX;
+ POSITION=PROJECTION_MATRIX*INV_CAMERA_MATRIX*WORLD_MATRIX*vec4(VERTEX.xyz,1.0);
+ POSITION.z = mix(POSITION.z, 0, 0.999);
+ POINT_SIZE = point_size;
+}
+void fragment() {
+ vec4 albedo_tex = texture(texture_albedo,POINT_COORD);
+ vec3 col = albedo_tex.rgb + COLOR.rgb;
+ col = vec3(min(col.r,1.0),min(col.g,1.0),min(col.b,1.0));
+ ALBEDO = col;
+ if (albedo_tex.a < 0.5) { discard; }
+ ALPHA = albedo_tex.a;
+}
+)");
+ handle_material->set_shader(handle_shader);
+ Ref<Texture2D> handle = editor->get_gui_base()->get_theme_icon("EditorBoneHandle", "EditorIcons");
+ handle_material->set_shader_param("point_size", handle->get_width());
+ handle_material->set_shader_param("texture_albedo", handle);
+
+ handles_mesh_instance = memnew(MeshInstance3D);
+ handles_mesh_instance->set_cast_shadows_setting(GeometryInstance3D::SHADOW_CASTING_SETTING_OFF);
+ handles_mesh.instantiate();
+ handles_mesh_instance->set_mesh(handles_mesh);
+}
+
+void Skeleton3DEditor::update_bone_original() {
+ if (!skeleton) {
+ return;
+ }
+ if (skeleton->get_bone_count() == 0 || selected_bone == -1) {
+ return;
+ }
+ bone_original_position = skeleton->get_bone_pose_position(selected_bone);
+ bone_original_rotation = skeleton->get_bone_pose_rotation(selected_bone);
+ bone_original_scale = skeleton->get_bone_pose_scale(selected_bone);
+}
+
+void Skeleton3DEditor::_hide_handles() {
+ handles_mesh_instance->hide();
+}
+
+void Skeleton3DEditor::_draw_gizmo() {
+ if (!skeleton) {
+ return;
+ }
+
+ // If you call get_bone_global_pose() while drawing the surface, such as toggle rest mode,
+ // the skeleton update will be done first and
+ // the drawing surface will be interrupted once and an error will occur.
+ skeleton->force_update_all_dirty_bones();
+
+ // Handles.
+ if (edit_mode) {
+ _draw_handles();
+ } else {
+ _hide_handles();
+ }
+}
+
+void Skeleton3DEditor::_draw_handles() {
+ handles_mesh_instance->show();
+
+ const int bone_len = skeleton->get_bone_count();
+ handles_mesh->clear_surfaces();
+ handles_mesh->surface_begin(Mesh::PRIMITIVE_POINTS);
+
+ for (int i = 0; i < bone_len; i++) {
+ Color c;
+ if (i == selected_bone) {
+ c = Color(1, 1, 0);
+ } else {
+ c = Color(0.1, 0.25, 0.8);
+ }
+ Vector3 point = skeleton->get_bone_global_pose(i).origin;
+ handles_mesh->surface_set_color(c);
+ handles_mesh->surface_add_vertex(point);
+ }
+ handles_mesh->surface_end();
+ handles_mesh->surface_set_material(0, handle_material);
+}
+
+TreeItem *Skeleton3DEditor::_find(TreeItem *p_node, const NodePath &p_path) {
+ if (!p_node) {
+ return nullptr;
+ }
+
+ NodePath np = p_node->get_metadata(0);
+ if (np == p_path) {
+ return p_node;
+ }
+
+ TreeItem *children = p_node->get_first_child();
+ while (children) {
+ TreeItem *n = _find(children, p_path);
+ if (n) {
+ return n;
+ }
+ children = children->get_next();
+ }
+
+ return nullptr;
+}
+
+void Skeleton3DEditor::_subgizmo_selection_change() {
+ if (!skeleton) {
+ return;
+ }
+
+ // Once validated by subgizmos_intersect_ray, but required if through inspector's bones tree.
+ if (!edit_mode) {
+ skeleton->clear_subgizmo_selection();
+ return;
+ }
+
+ int selected = -1;
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ if (se) {
+ selected = se->get_selected_bone();
+ }
+
+ if (selected >= 0) {
+ Vector<Ref<Node3DGizmo>> gizmos = skeleton->get_gizmos();
+ for (int i = 0; i < gizmos.size(); i++) {
+ Ref<EditorNode3DGizmo> gizmo = gizmos[i];
+ if (!gizmo.is_valid()) {
+ continue;
+ }
+ Ref<Skeleton3DGizmoPlugin> plugin = gizmo->get_plugin();
+ if (!plugin.is_valid()) {
+ continue;
+ }
+ skeleton->set_subgizmo_selection(gizmo, selected, skeleton->get_bone_global_pose(selected));
+ break;
+ }
+ } else {
+ skeleton->clear_subgizmo_selection();
+ }
+}
+
+void Skeleton3DEditor::select_bone(int p_idx) {
+ if (p_idx >= 0) {
+ TreeItem *ti = _find(joint_tree->get_root(), "bones/" + itos(p_idx));
+ if (ti) {
+ // Make visible when it's collapsed.
+ TreeItem *node = ti->get_parent();
+ while (node && node != joint_tree->get_root()) {
+ node->set_collapsed(false);
+ node = node->get_parent();
+ }
+ ti->select(0);
+ joint_tree->scroll_to_item(ti);
+ }
+ } else {
+ selected_bone = -1;
+ joint_tree->deselect_all();
+ _joint_tree_selection_changed();
+ }
}
Skeleton3DEditor::~Skeleton3DEditor() {
- if (options) {
- Node3DEditor::get_singleton()->remove_control_from_menu_panel(options);
+ if (skeleton) {
+ select_bone(-1);
+#ifdef TOOLS_ENABLED
+ skeleton->disconnect("show_rest_only_changed", callable_mp(this, &Skeleton3DEditor::_update_gizmo_visible));
+ skeleton->disconnect("bone_enabled_changed", callable_mp(this, &Skeleton3DEditor::_bone_enabled_changed));
+ skeleton->disconnect("pose_updated", callable_mp(this, &Skeleton3DEditor::_draw_gizmo));
+ skeleton->disconnect("pose_updated", callable_mp(this, &Skeleton3DEditor::_update_properties));
+ skeleton->set_transform_gizmo_visible(true);
+#endif
+ handles_mesh_instance->get_parent()->remove_child(handles_mesh_instance);
+ }
+ edit_mode_toggled(false);
+
+ handles_mesh_instance->queue_delete();
+
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+
+ if (animation_hb) {
+ ne->remove_control_from_menu_panel(animation_hb);
+ memdelete(animation_hb);
+ }
+
+ if (separator) {
+ ne->remove_control_from_menu_panel(separator);
+ memdelete(separator);
+ }
+
+ if (skeleton_options) {
+ ne->remove_control_from_menu_panel(skeleton_options);
+ memdelete(skeleton_options);
+ }
+
+ if (edit_mode_button) {
+ ne->remove_control_from_menu_panel(edit_mode_button);
+ memdelete(edit_mode_button);
}
}
@@ -700,16 +1009,432 @@ void EditorInspectorPluginSkeleton::parse_begin(Object *p_object) {
Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_object);
ERR_FAIL_COND(!skeleton);
- Skeleton3DEditor *skel_editor = memnew(Skeleton3DEditor(this, editor, skeleton));
+ skel_editor = memnew(Skeleton3DEditor(this, editor, skeleton));
add_custom_control(skel_editor);
}
Skeleton3DEditorPlugin::Skeleton3DEditorPlugin(EditorNode *p_node) {
editor = p_node;
- Ref<EditorInspectorPluginSkeleton> skeleton_plugin;
- skeleton_plugin.instantiate();
+ skeleton_plugin = memnew(EditorInspectorPluginSkeleton);
skeleton_plugin->editor = editor;
EditorInspector::add_inspector_plugin(skeleton_plugin);
+
+ Ref<Skeleton3DGizmoPlugin> gizmo_plugin = Ref<Skeleton3DGizmoPlugin>(memnew(Skeleton3DGizmoPlugin));
+ Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);
+}
+
+EditorPlugin::AfterGUIInput Skeleton3DEditorPlugin::forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+ if (se->is_edit_mode()) {
+ const Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
+ if (ne->get_tool_mode() != Node3DEditor::TOOL_MODE_SELECT) {
+ if (!ne->is_gizmo_visible()) {
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ if (mb->is_pressed()) {
+ se->update_bone_original();
+ }
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_DESELECT;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+}
+
+bool Skeleton3DEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("Skeleton3D");
+}
+
+void Skeleton3DEditor::_bone_enabled_changed(const int p_bone_id) {
+ _update_gizmo_visible();
+}
+
+void Skeleton3DEditor::_update_gizmo_visible() {
+ _subgizmo_selection_change();
+ if (edit_mode) {
+ if (selected_bone == -1) {
+#ifdef TOOLS_ENABLED
+ skeleton->set_transform_gizmo_visible(false);
+#endif
+ } else {
+#ifdef TOOLS_ENABLED
+ if (skeleton->is_bone_enabled(selected_bone) && !skeleton->is_show_rest_only()) {
+ skeleton->set_transform_gizmo_visible(true);
+ } else {
+ skeleton->set_transform_gizmo_visible(false);
+ }
+#endif
+ }
+ } else {
+#ifdef TOOLS_ENABLED
+ skeleton->set_transform_gizmo_visible(true);
+#endif
+ }
+ _draw_gizmo();
+}
+
+int Skeleton3DEditor::get_selected_bone() const {
+ return selected_bone;
+}
+
+Skeleton3DGizmoPlugin::Skeleton3DGizmoPlugin() {
+ unselected_mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ unselected_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ unselected_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ unselected_mat->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
+ unselected_mat->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
+
+ selected_mat = Ref<ShaderMaterial>(memnew(ShaderMaterial));
+ selected_sh = Ref<Shader>(memnew(Shader));
+ selected_sh->set_code(R"(
+// Skeleton 3D gizmo bones shader.
+
+shader_type spatial;
+render_mode unshaded, shadows_disabled;
+void vertex() {
+ if (!OUTPUT_IS_SRGB) {
+ COLOR.rgb = mix( pow((COLOR.rgb + vec3(0.055)) * (1.0 / (1.0 + 0.055)), vec3(2.4)), COLOR.rgb* (1.0 / 12.92), lessThan(COLOR.rgb,vec3(0.04045)) );
+ }
+ VERTEX = VERTEX;
+ POSITION=PROJECTION_MATRIX*INV_CAMERA_MATRIX*WORLD_MATRIX*vec4(VERTEX.xyz,1.0);
+ POSITION.z = mix(POSITION.z, 0, 0.998);
+}
+void fragment() {
+ ALBEDO = COLOR.rgb;
+ ALPHA = COLOR.a;
+}
+)");
+ selected_mat->set_shader(selected_sh);
+
+ // Regist properties in editor settings.
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/skeleton", Color(1, 0.8, 0.4));
+ EDITOR_DEF("editors/3d_gizmos/gizmo_colors/selected_bone", Color(0.8, 0.3, 0.0));
+ EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_axis_length", (float)0.1);
+ EDITOR_DEF("editors/3d_gizmos/gizmo_settings/bone_shape", 1);
+ EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "editors/3d_gizmos/gizmo_settings/bone_shape", PROPERTY_HINT_ENUM, "Wire,Octahedron"));
+}
+
+bool Skeleton3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
+ return Object::cast_to<Skeleton3D>(p_spatial) != nullptr;
+}
+
+String Skeleton3DGizmoPlugin::get_gizmo_name() const {
+ return "Skeleton3D";
+}
+
+int Skeleton3DGizmoPlugin::get_priority() const {
+ return -1;
+}
+
+int Skeleton3DGizmoPlugin::subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND_V(!skeleton, -1);
+
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+
+ if (!se->is_edit_mode()) {
+ return -1;
+ }
+
+ if (Node3DEditor::get_singleton()->get_tool_mode() != Node3DEditor::TOOL_MODE_SELECT) {
+ return -1;
+ }
+
+ // Select bone.
+ real_t grab_threshold = 4 * EDSCALE;
+ Vector3 ray_from = p_camera->get_global_transform().origin;
+ Transform3D gt = skeleton->get_global_transform();
+ int closest_idx = -1;
+ real_t closest_dist = 1e10;
+ const int bone_len = skeleton->get_bone_count();
+ for (int i = 0; i < bone_len; i++) {
+ Vector3 joint_pos_3d = gt.xform(skeleton->get_bone_global_pose(i).origin);
+ Vector2 joint_pos_2d = p_camera->unproject_position(joint_pos_3d);
+ real_t dist_3d = ray_from.distance_to(joint_pos_3d);
+ real_t dist_2d = p_point.distance_to(joint_pos_2d);
+ if (dist_2d < grab_threshold && dist_3d < closest_dist) {
+ closest_dist = dist_3d;
+ closest_idx = i;
+ }
+ }
+
+ if (closest_idx >= 0) {
+ WARN_PRINT("ray:");
+ WARN_PRINT(itos(closest_idx));
+ se->select_bone(closest_idx);
+ return closest_idx;
+ }
+
+ se->select_bone(-1);
+ return -1;
+}
+
+Transform3D Skeleton3DGizmoPlugin::get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND_V(!skeleton, Transform3D());
+
+ return skeleton->get_bone_global_pose(p_id);
+}
+
+void Skeleton3DGizmoPlugin::set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND(!skeleton);
+
+ // Prepare for global to local.
+ Transform3D original_to_local = Transform3D();
+ int parent_idx = skeleton->get_bone_parent(p_id);
+ if (parent_idx >= 0) {
+ original_to_local = original_to_local * skeleton->get_bone_global_pose(parent_idx);
+ }
+ Basis to_local = original_to_local.get_basis().inverse();
+
+ // Prepare transform.
+ Transform3D t = Transform3D();
+
+ // Basis.
+ t.basis = to_local * p_transform.get_basis();
+
+ // Origin.
+ Vector3 orig = Vector3();
+ orig = skeleton->get_bone_pose(p_id).origin;
+ Vector3 sub = p_transform.origin - skeleton->get_bone_global_pose(p_id).origin;
+ t.origin = orig + to_local.xform(sub);
+
+ // Apply transform.
+ skeleton->set_bone_pose_position(p_id, t.origin);
+ skeleton->set_bone_pose_rotation(p_id, t.basis.get_rotation_quaternion());
+ skeleton->set_bone_pose_scale(p_id, t.basis.get_scale());
+}
+
+void Skeleton3DGizmoPlugin::commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ ERR_FAIL_COND(!skeleton);
+
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ Node3DEditor *ne = Node3DEditor::get_singleton();
+
+ UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
+ ur->create_action(TTR("Set Bone Transform"));
+ if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) {
+ for (int i = 0; i < p_ids.size(); i++) {
+ ur->add_do_method(skeleton, "set_bone_pose_position", p_ids[i], skeleton->get_bone_pose_position(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose_position", p_ids[i], se->get_bone_original_position());
+ }
+ }
+ if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT || ne->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) {
+ for (int i = 0; i < p_ids.size(); i++) {
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", p_ids[i], skeleton->get_bone_pose_rotation(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", p_ids[i], se->get_bone_original_rotation());
+ }
+ }
+ if (ne->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) {
+ for (int i = 0; i < p_ids.size(); i++) {
+ // If the axis is swapped by scaling, the rotation can be changed.
+ ur->add_do_method(skeleton, "set_bone_pose_rotation", p_ids[i], skeleton->get_bone_pose_rotation(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose_rotation", p_ids[i], se->get_bone_original_rotation());
+ ur->add_do_method(skeleton, "set_bone_pose_scale", p_ids[i], skeleton->get_bone_pose_scale(p_ids[i]));
+ ur->add_undo_method(skeleton, "set_bone_pose_scale", p_ids[i], se->get_bone_original_scale());
+ }
+ }
+ ur->commit_action();
+}
+
+void Skeleton3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
+ Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_gizmo->get_spatial_node());
+ p_gizmo->clear();
+
+ int selected = -1;
+ Skeleton3DEditor *se = Skeleton3DEditor::get_singleton();
+ if (se) {
+ selected = se->get_selected_bone();
+ }
+
+ Color bone_color = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_colors/skeleton");
+ Color selected_bone_color = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_colors/selected_bone");
+ real_t bone_axis_length = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_settings/bone_axis_length");
+ int bone_shape = EditorSettings::get_singleton()->get("editors/3d_gizmos/gizmo_settings/bone_shape");
+
+ LocalVector<Color> axis_colors;
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_x_color"), SNAME("Editor")));
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_y_color"), SNAME("Editor")));
+ axis_colors.push_back(Node3DEditor::get_singleton()->get_theme_color(SNAME("axis_z_color"), SNAME("Editor")));
+
+ Ref<SurfaceTool> surface_tool(memnew(SurfaceTool));
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+
+ if (p_gizmo->is_selected()) {
+ surface_tool->set_material(selected_mat);
+ } else {
+ unselected_mat->set_albedo(bone_color);
+ surface_tool->set_material(unselected_mat);
+ }
+
+ Vector<Transform3D> grests;
+ grests.resize(skeleton->get_bone_count());
+
+ LocalVector<int> bones;
+ LocalVector<float> weights;
+ bones.resize(4);
+ weights.resize(4);
+ for (int i = 0; i < 4; i++) {
+ bones[i] = 0;
+ weights[i] = 0;
+ }
+ weights[0] = 1;
+
+ int current_bone_index = 0;
+ Vector<int> bones_to_process = skeleton->get_parentless_bones();
+
+ while (bones_to_process.size() > current_bone_index) {
+ int current_bone_idx = bones_to_process[current_bone_index];
+ current_bone_index++;
+
+ Color current_bone_color = (current_bone_idx == selected) ? selected_bone_color : bone_color;
+
+ Vector<int> child_bones_vector;
+ child_bones_vector = skeleton->get_bone_children(current_bone_idx);
+ int child_bones_size = child_bones_vector.size();
+
+ // You have children but no parent, then you must be a root/parentless bone.
+ if (skeleton->get_bone_parent(current_bone_idx) < 0) {
+ grests.write[current_bone_idx] = skeleton->get_bone_rest(current_bone_idx);
+ }
+
+ for (int i = 0; i < child_bones_size; i++) {
+ // Something wrong.
+ if (child_bones_vector[i] < 0) {
+ continue;
+ }
+
+ int child_bone_idx = child_bones_vector[i];
+
+ grests.write[child_bone_idx] = grests[current_bone_idx] * skeleton->get_bone_rest(child_bone_idx);
+
+ Vector3 v0 = grests[current_bone_idx].origin;
+ Vector3 v1 = grests[child_bone_idx].origin;
+ Vector3 d = (v1 - v0).normalized();
+ real_t dist = v0.distance_to(v1);
+
+ // Find closest axis.
+ int closest = -1;
+ real_t closest_d = 0.0;
+ for (int j = 0; j < 3; j++) {
+ real_t dp = Math::abs(grests[current_bone_idx].basis[j].normalized().dot(d));
+ if (j == 0 || dp > closest_d) {
+ closest = j;
+ }
+ }
+
+ // Draw bone.
+ switch (bone_shape) {
+ case 0: { // Wire shape.
+ surface_tool->set_color(current_bone_color);
+ bones[0] = current_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ bones[0] = child_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ } break;
+
+ case 1: { // Octahedron shape.
+ Vector3 first;
+ Vector3 points[6];
+ int point_idx = 0;
+ for (int j = 0; j < 3; j++) {
+ Vector3 axis;
+ if (first == Vector3()) {
+ axis = d.cross(d.cross(grests[current_bone_idx].basis[j])).normalized();
+ first = axis;
+ } else {
+ axis = d.cross(first).normalized();
+ }
+
+ surface_tool->set_color(current_bone_color);
+ for (int k = 0; k < 2; k++) {
+ if (k == 1) {
+ axis = -axis;
+ }
+ Vector3 point = v0 + d * dist * 0.2;
+ point += axis * dist * 0.1;
+
+ bones[0] = current_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(point);
+
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(point);
+ bones[0] = child_bone_idx;
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ points[point_idx++] = point;
+ }
+ }
+ surface_tool->set_color(current_bone_color);
+ SWAP(points[1], points[2]);
+ bones[0] = current_bone_idx;
+ for (int j = 0; j < 6; j++) {
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(points[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(points[(j + 1) % 6]);
+ }
+ } break;
+ }
+
+ // Axis as root of the bone.
+ for (int j = 0; j < 3; j++) {
+ bones[0] = current_bone_idx;
+ surface_tool->set_color(axis_colors[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v0 + (grests[current_bone_idx].basis.inverse())[j].normalized() * dist * bone_axis_length);
+
+ if (j == closest) {
+ continue;
+ }
+ }
+
+ // Axis at the end of the bone children.
+ if (i == child_bones_size - 1) {
+ for (int j = 0; j < 3; j++) {
+ bones[0] = child_bone_idx;
+ surface_tool->set_color(axis_colors[j]);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1);
+ surface_tool->set_bones(bones);
+ surface_tool->set_weights(weights);
+ surface_tool->add_vertex(v1 + (grests[child_bone_idx].basis.inverse())[j].normalized() * dist * bone_axis_length);
+
+ if (j == closest) {
+ continue;
+ }
+ }
+ }
+
+ // Add the bone's children to the list of bones to be processed.
+ bones_to_process.push_back(child_bones_vector[i]);
+ }
+ }
+
+ Ref<ArrayMesh> m = surface_tool->commit();
+ p_gizmo->add_mesh(m, Ref<Material>(), Transform3D(), skeleton->register_skin(skeleton->create_skin_from_rest_transforms()));
}
diff --git a/editor/plugins/skeleton_3d_editor_plugin.h b/editor/plugins/skeleton_3d_editor_plugin.h
index 9de52c6fa8..1dd2d2281d 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_3d_editor_plugin.h
@@ -33,39 +33,39 @@
#include "editor/editor_node.h"
#include "editor/editor_plugin.h"
+#include "editor/editor_properties.h"
+#include "node_3d_editor_plugin.h"
+#include "scene/3d/camera_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
#include "scene/3d/skeleton_3d.h"
+#include "scene/resources/immediate_mesh.h"
class EditorInspectorPluginSkeleton;
class Joint;
class PhysicalBone3D;
class Skeleton3DEditorPlugin;
class Button;
-class CheckBox;
-class EditorPropertyTransform3D;
-class EditorPropertyVector3;
class BoneTransformEditor : public VBoxContainer {
GDCLASS(BoneTransformEditor, VBoxContainer);
EditorInspectorSection *section = nullptr;
- EditorPropertyVector3 *translation_property = nullptr;
- EditorPropertyVector3 *rotation_property = nullptr;
+ EditorPropertyCheck *enabled_checkbox = nullptr;
+ EditorPropertyVector3 *position_property = nullptr;
+ EditorPropertyQuaternion *rotation_property = nullptr;
EditorPropertyVector3 *scale_property = nullptr;
- EditorInspectorSection *transform_section = nullptr;
- EditorPropertyTransform3D *transform_property = nullptr;
+
+ EditorInspectorSection *rest_section = nullptr;
+ EditorPropertyTransform3D *rest_matrix = nullptr;
Rect2 background_rects[5];
Skeleton3D *skeleton;
- String property;
+ // String property;
UndoRedo *undo_redo;
- Button *key_button = nullptr;
- CheckBox *enabled_checkbox = nullptr;
-
- bool keyable = false;
bool toggle_enabled = false;
bool updating = false;
@@ -73,18 +73,9 @@ class BoneTransformEditor : public VBoxContainer {
void create_editors();
- // Called when one of the EditorSpinSliders are changed.
- void _value_changed(const double p_value);
- // Called when the one of the EditorPropertyVector3 are updated.
- void _value_changed_vector3(const String p_property_name, const Vector3 p_vector, const StringName p_edited_property_name, const bool p_boolean);
- // Called when the transform_property is updated.
- void _value_changed_transform(const String p_property_name, const Transform3D p_transform, const StringName p_edited_property_name, const bool p_boolean);
- // Changes the transform to the given transform and updates the UI accordingly.
- void _change_transform(Transform3D p_new_transform);
- // Creates a Transform using the EditorPropertyVector3 properties.
- Transform3D compute_transform_from_vector3s() const;
+ void _value_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing);
- void update_enabled_checkbox();
+ void _property_keyed(const String &p_path, bool p_advance);
protected:
void _notification(int p_what);
@@ -92,28 +83,12 @@ protected:
public:
BoneTransformEditor(Skeleton3D *p_skeleton);
- // Which transform target to modify
+ // Which transform target to modify.
void set_target(const String &p_prop);
void set_label(const String &p_label) { label = p_label; }
-
- void _update_properties();
- void _update_custom_pose_properties();
- void _update_transform_properties(Transform3D p_transform);
-
- // Can/cannot modify the spinner values for the Transform
- void set_read_only(const bool p_read_only);
-
- // Transform can be keyed, whether or not to show the button
void set_keyable(const bool p_keyable);
- // Bone can be toggled enabled or disabled, whether or not to show the checkbox
- void set_toggle_enabled(const bool p_enabled);
-
- // Key Transform Button pressed
- void _key_button_pressed();
-
- // Bone Enabled Checkbox toggled
- void _checkbox_toggled(const bool p_toggled);
+ void _update_properties();
};
class Skeleton3DEditor : public VBoxContainer {
@@ -121,13 +96,17 @@ class Skeleton3DEditor : public VBoxContainer {
friend class Skeleton3DEditorPlugin;
- enum Menu {
- MENU_OPTION_CREATE_PHYSICAL_SKELETON
+ enum SkeletonOption {
+ SKELETON_OPTION_INIT_ALL_POSES,
+ SKELETON_OPTION_INIT_SELECTED_POSES,
+ SKELETON_OPTION_ALL_POSES_TO_RESTS,
+ SKELETON_OPTION_SELECTED_POSES_TO_RESTS,
+ SKELETON_OPTION_CREATE_PHYSICAL_SKELETON,
};
struct BoneInfo {
PhysicalBone3D *physical_bone = nullptr;
- Transform3D relative_rest; // Relative to skeleton node
+ Transform3D relative_rest; // Relative to skeleton node.
};
EditorNode *editor;
@@ -138,15 +117,30 @@ class Skeleton3DEditor : public VBoxContainer {
Tree *joint_tree = nullptr;
BoneTransformEditor *rest_editor = nullptr;
BoneTransformEditor *pose_editor = nullptr;
- BoneTransformEditor *custom_pose_editor = nullptr;
- MenuButton *options = nullptr;
+ VSeparator *separator;
+ MenuButton *skeleton_options = nullptr;
+ Button *edit_mode_button;
+
+ bool edit_mode = false;
+
+ HBoxContainer *animation_hb;
+ Button *key_loc_button;
+ Button *key_rot_button;
+ Button *key_scale_button;
+ Button *key_insert_button;
+ Button *key_insert_all_button;
+
EditorFileDialog *file_dialog = nullptr;
- UndoRedo *undo_redo = nullptr;
+ bool keyable;
- void _on_click_option(int p_option);
+ static Skeleton3DEditor *singleton;
+
+ void _on_click_skeleton_option(int p_skeleton_option);
void _file_selected(const String &p_file);
+ TreeItem *_find(TreeItem *p_node, const NodePath &p_path);
+ void edit_mode_toggled(const bool pressed);
EditorFileDialog *file_export_lib = nullptr;
@@ -155,6 +149,11 @@ class Skeleton3DEditor : public VBoxContainer {
void create_editors();
+ void init_pose(const bool p_all_bones);
+ void pose_to_rest(const bool p_all_bones);
+
+ void insert_keys(const bool p_all_bones);
+
void create_physical_skeleton();
PhysicalBone3D *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
@@ -162,20 +161,57 @@ class Skeleton3DEditor : public VBoxContainer {
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ void set_keyable(const bool p_keyable);
+ void set_bone_options_enabled(const bool p_bone_options_enabled);
+
+ // Handle.
+ MeshInstance3D *handles_mesh_instance;
+ Ref<ImmediateMesh> handles_mesh;
+ Ref<ShaderMaterial> handle_material;
+ Ref<Shader> handle_shader;
+
+ Vector3 bone_original_position;
+ Quaternion bone_original_rotation;
+ Vector3 bone_original_scale;
+
+ void _update_gizmo_visible();
+ void _bone_enabled_changed(const int p_bone_id);
+
+ void _hide_handles();
+
+ void _draw_gizmo();
+ void _draw_handles();
+
+ void _joint_tree_selection_changed();
+ void _joint_tree_rmb_select(const Vector2 &p_pos);
+ void _update_properties();
+
+ void _subgizmo_selection_change();
+
+ int selected_bone = -1;
+
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
static void _bind_methods();
public:
+ static Skeleton3DEditor *get_singleton() { return singleton; }
+
+ void select_bone(int p_idx);
+
+ int get_selected_bone() const;
+
void move_skeleton_bone(NodePath p_skeleton_path, int32_t p_selected_boneidx, int32_t p_target_boneidx);
Skeleton3D *get_skeleton() const { return skeleton; };
- void _joint_tree_selection_changed();
- void _joint_tree_rmb_select(const Vector2 &p_pos);
+ bool is_edit_mode() const { return edit_mode; }
- void _update_properties();
+ void update_bone_original();
+ Vector3 get_bone_original_position() const { return bone_original_position; };
+ Quaternion get_bone_original_rotation() const { return bone_original_rotation; };
+ Vector3 get_bone_original_scale() const { return bone_original_scale; };
Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNode *p_editor, Skeleton3D *skeleton);
~Skeleton3DEditor();
@@ -186,6 +222,7 @@ class EditorInspectorPluginSkeleton : public EditorInspectorPlugin {
friend class Skeleton3DEditorPlugin;
+ Skeleton3DEditor *skel_editor;
EditorNode *editor;
public:
@@ -196,12 +233,40 @@ public:
class Skeleton3DEditorPlugin : public EditorPlugin {
GDCLASS(Skeleton3DEditorPlugin, EditorPlugin);
+ EditorInspectorPluginSkeleton *skeleton_plugin;
EditorNode *editor;
public:
- Skeleton3DEditorPlugin(EditorNode *p_node);
+ virtual EditorPlugin::AfterGUIInput forward_spatial_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override;
+
+ bool has_main_screen() const override { return false; }
+ virtual bool handles(Object *p_object) const override;
virtual String get_name() const override { return "Skeleton3D"; }
+
+ Skeleton3DEditorPlugin(EditorNode *p_node);
+};
+
+class Skeleton3DGizmoPlugin : public EditorNode3DGizmoPlugin {
+ GDCLASS(Skeleton3DGizmoPlugin, EditorNode3DGizmoPlugin);
+
+ Ref<StandardMaterial3D> unselected_mat;
+ Ref<ShaderMaterial> selected_mat;
+ Ref<Shader> selected_sh;
+
+public:
+ bool has_gizmo(Node3D *p_spatial) override;
+ String get_gizmo_name() const override;
+ int get_priority() const override;
+
+ int subgizmos_intersect_ray(const EditorNode3DGizmo *p_gizmo, Camera3D *p_camera, const Vector2 &p_point) const override;
+ Transform3D get_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id) const override;
+ void set_subgizmo_transform(const EditorNode3DGizmo *p_gizmo, int p_id, Transform3D p_transform) override;
+ void commit_subgizmos(const EditorNode3DGizmo *p_gizmo, const Vector<int> &p_ids, const Vector<Transform3D> &p_restore, bool p_cancel) override;
+
+ void redraw(EditorNode3DGizmo *p_gizmo) override;
+
+ Skeleton3DGizmoPlugin();
};
#endif // SKELETON_3D_EDITOR_PLUGIN_H
diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp
index 0f889ce33d..48b10d5a8b 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -182,7 +182,7 @@ void Sprite2DEditor::_update_mesh_data() {
if (node->is_region_enabled()) {
rect = node->get_region_rect();
} else {
- rect.size = Size2(image->get_width(), image->get_height());
+ rect.size = image->get_size();
}
Ref<BitMap> bm;
@@ -209,7 +209,7 @@ void Sprite2DEditor::_update_mesh_data() {
computed_uv.clear();
computed_indices.clear();
- Size2 img_size = Vector2(image->get_width(), image->get_height());
+ Size2 img_size = image->get_size();
for (int i = 0; i < lines.size(); i++) {
lines.write[i] = expand(lines[i], rect, epsilon);
}
@@ -526,8 +526,6 @@ Sprite2DEditor::Sprite2DEditor() {
debug_uv_dialog->add_child(vb);
ScrollContainer *scroll = memnew(ScrollContainer);
scroll->set_custom_minimum_size(Size2(800, 500) * EDSCALE);
- scroll->set_enable_h_scroll(true);
- scroll->set_enable_v_scroll(true);
vb->add_margin_child(TTR("Preview:"), scroll, true);
debug_uv = memnew(Control);
debug_uv->connect("draw", callable_mp(this, &Sprite2DEditor::_debug_uv_draw));
diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp
index 400f9f560f..316ef7b195 100644
--- a/editor/plugins/sprite_frames_editor_plugin.cpp
+++ b/editor/plugins/sprite_frames_editor_plugin.cpp
@@ -54,22 +54,46 @@ void SpriteFramesEditor::_open_sprite_sheet() {
file_split_sheet->popup_file_dialog();
}
+int SpriteFramesEditor::_sheet_preview_position_to_frame_index(const Point2 &p_position) {
+ if (p_position.x < 0 || p_position.y < 0) {
+ return -1;
+ }
+
+ Size2i texture_size = split_sheet_preview->get_texture()->get_size();
+ int h = split_sheet_h->get_value();
+ int v = split_sheet_v->get_value();
+ if (h > texture_size.width || v > texture_size.height) {
+ return -1;
+ }
+
+ int x = int(p_position.x / sheet_zoom) / (texture_size.width / h);
+ int y = int(p_position.y / sheet_zoom) / (texture_size.height / v);
+ if (x >= h || y >= v) {
+ return -1;
+ }
+ return h * y + x;
+}
+
void SpriteFramesEditor::_sheet_preview_draw() {
- Size2i size = split_sheet_preview->get_size();
+ Size2i texture_size = split_sheet_preview->get_texture()->get_size();
int h = split_sheet_h->get_value();
int v = split_sheet_v->get_value();
- int width = size.width / h;
- int height = size.height / v;
+
+ real_t width = (texture_size.width / h) * sheet_zoom;
+ real_t height = (texture_size.height / v) * sheet_zoom;
const float a = 0.3;
- for (int i = 1; i < h; i++) {
- int x = i * width;
- split_sheet_preview->draw_line(Point2(x, 0), Point2(x, size.height), Color(1, 1, 1, a));
- split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, size.height), Color(0, 0, 0, a));
+
+ real_t y_end = v * height;
+ for (int i = 0; i <= h; i++) {
+ real_t x = i * width;
+ split_sheet_preview->draw_line(Point2(x, 0), Point2(x, y_end), Color(1, 1, 1, a));
+ split_sheet_preview->draw_line(Point2(x + 1, 0), Point2(x + 1, y_end), Color(0, 0, 0, a));
}
- for (int i = 1; i < v; i++) {
- int y = i * height;
- split_sheet_preview->draw_line(Point2(0, y), Point2(size.width, y), Color(1, 1, 1, a));
- split_sheet_preview->draw_line(Point2(0, y + 1), Point2(size.width, y + 1), Color(0, 0, 0, a));
+ real_t x_end = h * width;
+ for (int i = 0; i <= v; i++) {
+ real_t y = i * height;
+ split_sheet_preview->draw_line(Point2(0, y), Point2(x_end, y), Color(1, 1, 1, a));
+ split_sheet_preview->draw_line(Point2(0, y + 1), Point2(x_end, y + 1), Color(0, 0, 0, a));
}
if (frames_selected.size() == 0) {
@@ -83,9 +107,9 @@ void SpriteFramesEditor::_sheet_preview_draw() {
for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) {
int idx = E->get();
int xp = idx % h;
- int yp = (idx - xp) / h;
- int x = xp * width;
- int y = yp * height;
+ int yp = idx / h;
+ real_t x = xp * width;
+ real_t y = yp * height;
split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 0.35), true);
split_sheet_preview->draw_rect(Rect2(x + 0, y + 0, width - 0, height - 0), Color(0, 0, 0, 1), false);
@@ -102,66 +126,56 @@ void SpriteFramesEditor::_sheet_preview_draw() {
void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
- const Size2i size = split_sheet_preview->get_size();
- const int h = split_sheet_h->get_value();
- const int v = split_sheet_v->get_value();
-
- const int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1);
- const int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1);
-
- const int idx = h * y + x;
-
- if (mb->is_shift_pressed() && last_frame_selected >= 0) {
- //select multiple
- int from = idx;
- int to = last_frame_selected;
- if (from > to) {
- SWAP(from, to);
- }
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
+ const int idx = _sheet_preview_position_to_frame_index(mb->get_position());
+
+ if (idx != -1) {
+ if (mb->is_shift_pressed() && last_frame_selected >= 0) {
+ //select multiple
+ int from = idx;
+ int to = last_frame_selected;
+ if (from > to) {
+ SWAP(from, to);
+ }
+
+ for (int i = from; i <= to; i++) {
+ // Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
+ frames_toggled_by_mouse_hover.insert(idx);
- for (int i = from; i <= to; i++) {
+ if (mb->is_ctrl_pressed()) {
+ frames_selected.erase(i);
+ } else {
+ frames_selected.insert(i);
+ }
+ }
+ } else {
// Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
frames_toggled_by_mouse_hover.insert(idx);
- if (mb->is_ctrl_pressed()) {
- frames_selected.erase(i);
+ if (frames_selected.has(idx)) {
+ frames_selected.erase(idx);
} else {
- frames_selected.insert(i);
+ frames_selected.insert(idx);
}
}
- } else {
- // Prevent double-toggling the same frame when moving the mouse when the mouse button is still held.
- frames_toggled_by_mouse_hover.insert(idx);
-
- if (frames_selected.has(idx)) {
- frames_selected.erase(idx);
- } else {
- frames_selected.insert(idx);
- }
}
- last_frame_selected = idx;
- split_sheet_preview->update();
+ if (last_frame_selected != idx || idx != -1) {
+ last_frame_selected = idx;
+ split_sheet_preview->update();
+ }
}
- if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
frames_toggled_by_mouse_hover.clear();
}
const Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
// Select by holding down the mouse button on frames.
- const Size2i size = split_sheet_preview->get_size();
- const int h = split_sheet_h->get_value();
- const int v = split_sheet_v->get_value();
-
- const int x = CLAMP(int(mm->get_position().x) * h / size.width, 0, h - 1);
- const int y = CLAMP(int(mm->get_position().y) * v / size.height, 0, v - 1);
-
- const int idx = h * y + x;
+ const int idx = _sheet_preview_position_to_frame_index(mm->get_position());
- if (!frames_toggled_by_mouse_hover.has(idx)) {
+ if (idx != -1 && !frames_toggled_by_mouse_hover.has(idx)) {
// Only allow toggling each tile once per mouse hold.
// Otherwise, the selection would constantly "flicker" in and out when moving the mouse cursor.
// The mouse button must be released before it can be toggled again.
@@ -186,11 +200,11 @@ void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) {
// Zoom in/out using Ctrl + mouse wheel. This is done on the ScrollContainer
// to allow performing this action anywhere, even if the cursor isn't
// hovering the texture in the workspace.
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed() && mb->is_ctrl_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed() && mb->is_ctrl_pressed()) {
_sheet_zoom_in();
// Don't scroll up after zooming in.
accept_event();
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed() && mb->is_ctrl_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed() && mb->is_ctrl_pressed()) {
_sheet_zoom_out();
// Don't scroll down after zooming out.
accept_event();
@@ -199,17 +213,17 @@ void SpriteFramesEditor::_sheet_scroll_input(const Ref<InputEvent> &p_event) {
}
void SpriteFramesEditor::_sheet_add_frames() {
- Size2i size = split_sheet_preview->get_texture()->get_size();
+ Size2i texture_size = split_sheet_preview->get_texture()->get_size();
int frame_count_x = split_sheet_h->get_value();
int frame_count_y = split_sheet_v->get_value();
- Size2 frame_size(size.width / frame_count_x, size.height / frame_count_y);
+ Size2 frame_size(texture_size.width / frame_count_x, texture_size.height / frame_count_y);
undo_redo->create_action(TTR("Add Frame"));
int fc = frames->get_frame_count(edited_anim);
Point2 src_origin;
- Rect2 src_region(Point2(), size);
+ Rect2 src_region(Point2(), texture_size);
AtlasTexture *src_atlas = Object::cast_to<AtlasTexture>(*split_sheet_preview->get_texture());
if (src_atlas && src_atlas->get_atlas().is_valid()) {
@@ -732,11 +746,11 @@ void SpriteFramesEditor::_tree_input(const Ref<InputEvent> &p_event) {
const Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed() && mb->is_ctrl_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed() && mb->is_ctrl_pressed()) {
_zoom_in();
// Don't scroll up after zooming in.
accept_event();
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed() && mb->is_ctrl_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed() && mb->is_ctrl_pressed()) {
_zoom_out();
// Don't scroll down after zooming out.
accept_event();
@@ -992,7 +1006,7 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da
if (String(d["type"]) == "files") {
Vector<String> files = d["files"];
- if (Input::get_singleton()->is_key_pressed(KEY_CTRL)) {
+ if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
_prepare_sprite_sheet(files[0]);
} else {
_file_load_request(files, at_pos);
@@ -1224,8 +1238,6 @@ SpriteFramesEditor::SpriteFramesEditor() {
split_sheet_preview->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_preview_input));
split_sheet_scroll = memnew(ScrollContainer);
- split_sheet_scroll->set_enable_h_scroll(true);
- split_sheet_scroll->set_enable_v_scroll(true);
split_sheet_scroll->connect("gui_input", callable_mp(this, &SpriteFramesEditor::_sheet_scroll_input));
split_sheet_panel->add_child(split_sheet_scroll);
CenterContainer *cc = memnew(CenterContainer);
diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h
index 17e30f0cab..9732384000 100644
--- a/editor/plugins/sprite_frames_editor_plugin.h
+++ b/editor/plugins/sprite_frames_editor_plugin.h
@@ -100,7 +100,6 @@ class SpriteFramesEditor : public HSplitContainer {
float min_sheet_zoom;
void _load_pressed();
- void _load_scene_pressed();
void _file_load_request(const Vector<String> &p_path, int p_at_pos = -1);
void _copy_pressed();
void _paste_pressed();
@@ -128,13 +127,13 @@ class SpriteFramesEditor : public HSplitContainer {
UndoRedo *undo_redo;
- bool _is_drop_valid(const Dictionary &p_drag_data, const Dictionary &p_item_data) const;
Variant get_drag_data_fw(const Point2 &p_point, Control *p_from);
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
void _open_sprite_sheet();
void _prepare_sprite_sheet(const String &p_file);
+ int _sheet_preview_position_to_frame_index(const Vector2 &p_position);
void _sheet_preview_draw();
void _sheet_spin_changed(double);
void _sheet_preview_input(const Ref<InputEvent> &p_event);
diff --git a/editor/plugins/style_box_editor_plugin.cpp b/editor/plugins/style_box_editor_plugin.cpp
index 91c5e96f08..1c7f319280 100644
--- a/editor/plugins/style_box_editor_plugin.cpp
+++ b/editor/plugins/style_box_editor_plugin.cpp
@@ -44,13 +44,6 @@ void EditorInspectorPluginStyleBox::parse_begin(Object *p_object) {
add_custom_control(preview);
}
-bool EditorInspectorPluginStyleBox::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, const uint32_t p_usage, bool p_wide) {
- return false; //do not want
-}
-
-void EditorInspectorPluginStyleBox::parse_end() {
-}
-
void StyleBoxPreview::edit(const Ref<StyleBox> &p_stylebox) {
if (stylebox.is_valid()) {
stylebox->disconnect("changed", callable_mp(this, &StyleBoxPreview::_sb_changed));
diff --git a/editor/plugins/style_box_editor_plugin.h b/editor/plugins/style_box_editor_plugin.h
index 8ca348bd80..d82e5ab05e 100644
--- a/editor/plugins/style_box_editor_plugin.h
+++ b/editor/plugins/style_box_editor_plugin.h
@@ -61,8 +61,6 @@ class EditorInspectorPluginStyleBox : public EditorInspectorPlugin {
public:
virtual bool can_handle(Object *p_object) override;
virtual void parse_begin(Object *p_object) override;
- 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;
- virtual void parse_end() override;
};
class StyleBoxEditorPlugin : public EditorPlugin {
diff --git a/editor/plugins/text_control_editor_plugin.cpp b/editor/plugins/text_control_editor_plugin.cpp
new file mode 100644
index 0000000000..c878c83430
--- /dev/null
+++ b/editor/plugins/text_control_editor_plugin.cpp
@@ -0,0 +1,375 @@
+/*************************************************************************/
+/* text_control_editor_plugin.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "text_control_editor_plugin.h"
+
+#include "editor/editor_scale.h"
+
+void TextControlEditor::_notification(int p_notification) {
+ switch (p_notification) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (!EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts))) {
+ EditorFileSystem::get_singleton()->connect("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts), make_binds(""));
+ }
+ [[fallthrough]];
+ }
+ case NOTIFICATION_THEME_CHANGED: {
+ clear_formatting->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
+ } break;
+ case NOTIFICATION_EXIT_TREE: {
+ if (EditorFileSystem::get_singleton()->is_connected("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts))) {
+ EditorFileSystem::get_singleton()->disconnect("filesystem_changed", callable_mp(this, &TextControlEditor::_reload_fonts));
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+void TextControlEditor::_find_resources(EditorFileSystemDirectory *p_dir) {
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ _find_resources(p_dir->get_subdir(i));
+ }
+
+ for (int i = 0; i < p_dir->get_file_count(); i++) {
+ if (p_dir->get_file_type(i) == "FontData") {
+ Ref<FontData> fd = ResourceLoader::load(p_dir->get_file_path(i));
+ if (fd.is_valid()) {
+ String name = fd->get_font_name();
+ String sty = fd->get_font_style_name();
+ if (sty.is_empty()) {
+ sty = "Default";
+ }
+ fonts[name][sty] = p_dir->get_file_path(i);
+ }
+ }
+ }
+}
+
+void TextControlEditor::_reload_fonts(const String &p_path) {
+ fonts.clear();
+ _find_resources(EditorFileSystem::get_singleton()->get_filesystem());
+ _update_control();
+}
+
+void TextControlEditor::_update_fonts_menu() {
+ font_list->clear();
+ font_list->add_item(TTR("[Theme Default]"), FONT_INFO_THEME_DEFAULT);
+ if (custom_font.is_valid()) {
+ font_list->add_item(TTR("[Custom Font]"), FONT_INFO_USER_CUSTOM);
+ }
+
+ int id = FONT_INFO_ID;
+ for (Map<String, Map<String, String>>::Element *E = fonts.front(); E; E = E->next()) {
+ font_list->add_item(E->key(), id++);
+ }
+
+ if (font_list->get_item_count() > 1) {
+ font_list->show();
+ } else {
+ font_list->hide();
+ }
+}
+
+void TextControlEditor::_update_styles_menu() {
+ font_style_list->clear();
+ if ((font_list->get_selected_id() >= FONT_INFO_ID)) {
+ const String &name = font_list->get_item_text(font_list->get_selected());
+ for (Map<String, String>::Element *E = fonts[name].front(); E; E = E->next()) {
+ font_style_list->add_item(E->key());
+ }
+ } else {
+ font_style_list->add_item("Default");
+ }
+
+ if (font_style_list->get_item_count() > 1) {
+ font_style_list->show();
+ } else {
+ font_style_list->hide();
+ }
+}
+
+void TextControlEditor::_update_control() {
+ if (edited_control) {
+ // Get override names.
+ if (edited_control->is_class("RichTextLabel")) {
+ edited_color = "default_color";
+ edited_font = "normal_font";
+ edited_font_size = "normal_font_size";
+ } else {
+ edited_color = "font_color";
+ edited_font = "font";
+ edited_font_size = "font_size";
+ }
+
+ // Get font override.
+ Ref<Font> font;
+ if (edited_control->has_theme_font_override(edited_font)) {
+ font = edited_control->get_theme_font(edited_font);
+ }
+ if (font.is_valid()) {
+ if (font->get_data_count() != 1) {
+ // Composite font, save it to "custom_font" to allow undoing font change.
+ custom_font = font;
+ _update_fonts_menu();
+ font_list->select(FONT_INFO_USER_CUSTOM);
+ _update_styles_menu();
+ font_style_list->select(0);
+ } else {
+ // Single face font, search for the font with matching name and style.
+ String name = font->get_data(0)->get_font_name();
+ String style = font->get_data(0)->get_font_style_name();
+ if (fonts.has(name) && fonts[name].has(style)) {
+ _update_fonts_menu();
+ for (int i = 0; i < font_list->get_item_count(); i++) {
+ if (font_list->get_item_text(i) == name) {
+ font_list->select(i);
+ break;
+ }
+ }
+ _update_styles_menu();
+ for (int i = 0; i < font_style_list->get_item_count(); i++) {
+ if (font_style_list->get_item_text(i) == style) {
+ font_style_list->select(i);
+ break;
+ }
+ }
+ } else {
+ // Unknown font, save it to "custom_font" to allow undoing font change.
+ custom_font = font;
+ _update_fonts_menu();
+ font_list->select(FONT_INFO_USER_CUSTOM);
+ _update_styles_menu();
+ font_style_list->select(0);
+ }
+ }
+ } else {
+ // No font override, select "Theme Default".
+ _update_fonts_menu();
+ font_list->select(FONT_INFO_THEME_DEFAULT);
+ _update_styles_menu();
+ font_style_list->select(0);
+ }
+
+ // Get other theme overrides.
+ font_size_list->set_value(edited_control->get_theme_font_size(edited_font_size));
+ outline_size_list->set_value(edited_control->get_theme_constant("outline_size"));
+
+ font_color_picker->set_pick_color(edited_control->get_theme_color(edited_color));
+ outline_color_picker->set_pick_color(edited_control->get_theme_color("font_outline_color"));
+ }
+}
+
+void TextControlEditor::_font_selected(int p_id) {
+ _update_styles_menu();
+ _set_font();
+}
+
+void TextControlEditor::_font_style_selected(int p_id) {
+ _set_font();
+}
+
+void TextControlEditor::_set_font() {
+ if (edited_control) {
+ if (font_list->get_selected_id() == FONT_INFO_THEME_DEFAULT) {
+ // Remove font override.
+ edited_control->remove_theme_font_override(edited_font);
+ return;
+ } else if (font_list->get_selected_id() == FONT_INFO_USER_CUSTOM) {
+ // Restore "custom_font".
+ edited_control->add_theme_font_override(edited_font, custom_font);
+ return;
+ } else {
+ // Load new font resource using selected name and style.
+ String name = font_list->get_item_text(font_list->get_selected());
+ String sty = font_style_list->get_item_text(font_style_list->get_selected());
+ if (sty.is_empty()) {
+ sty = "Default";
+ }
+ if (fonts.has(name)) {
+ Ref<FontData> fd = ResourceLoader::load(fonts[name][sty]);
+ if (fd.is_valid()) {
+ Ref<Font> f;
+ f.instantiate();
+ f->add_data(fd);
+ edited_control->add_theme_font_override(edited_font, f);
+ }
+ }
+ }
+ }
+}
+
+void TextControlEditor::_font_size_selected(double p_size) {
+ if (edited_control) {
+ edited_control->add_theme_font_size_override(edited_font_size, p_size);
+ }
+}
+
+void TextControlEditor::_outline_size_selected(double p_size) {
+ if (edited_control) {
+ edited_control->add_theme_constant_override("outline_size", p_size);
+ }
+}
+
+void TextControlEditor::_font_color_changed(const Color &p_color) {
+ if (edited_control) {
+ edited_control->add_theme_color_override(edited_color, p_color);
+ }
+}
+
+void TextControlEditor::_outline_color_changed(const Color &p_color) {
+ if (edited_control) {
+ edited_control->add_theme_color_override("font_outline_color", p_color);
+ }
+}
+
+void TextControlEditor::_clear_formatting() {
+ if (edited_control) {
+ edited_control->begin_bulk_theme_override();
+ edited_control->remove_theme_font_override(edited_font);
+ edited_control->remove_theme_font_size_override(edited_font_size);
+ edited_control->remove_theme_color_override(edited_color);
+ edited_control->remove_theme_color_override("font_outline_color");
+ edited_control->remove_theme_constant_override("outline_size");
+ edited_control->end_bulk_theme_override();
+ _update_control();
+ }
+}
+
+void TextControlEditor::edit(Object *p_object) {
+ Control *ctrl = Object::cast_to<Control>(p_object);
+ if (!ctrl) {
+ edited_control = nullptr;
+ custom_font = Ref<Font>();
+ } else {
+ edited_control = ctrl;
+ custom_font = Ref<Font>();
+ _update_control();
+ }
+}
+
+bool TextControlEditor::handles(Object *p_object) const {
+ Control *ctrl = Object::cast_to<Control>(p_object);
+ if (!ctrl) {
+ return false;
+ } else {
+ bool valid = false;
+ ctrl->get("text", &valid);
+ return valid;
+ }
+}
+
+TextControlEditor::TextControlEditor() {
+ add_child(memnew(VSeparator));
+
+ font_list = memnew(OptionButton);
+ font_list->set_flat(true);
+ font_list->set_tooltip(TTR("Font"));
+ add_child(font_list);
+ font_list->connect("item_selected", callable_mp(this, &TextControlEditor::_font_selected));
+
+ font_style_list = memnew(OptionButton);
+ font_style_list->set_flat(true);
+ font_style_list->set_tooltip(TTR("Font style"));
+ font_style_list->set_toggle_mode(true);
+ add_child(font_style_list);
+ font_style_list->connect("item_selected", callable_mp(this, &TextControlEditor::_font_style_selected));
+
+ font_size_list = memnew(SpinBox);
+ font_size_list->set_tooltip(TTR("Font Size"));
+ font_size_list->get_line_edit()->add_theme_constant_override("minimum_character_width", 2);
+ font_size_list->set_min(6);
+ font_size_list->set_step(1);
+ font_size_list->set_max(96);
+ font_size_list->get_line_edit()->set_flat(true);
+ add_child(font_size_list);
+ font_size_list->connect("value_changed", callable_mp(this, &TextControlEditor::_font_size_selected));
+
+ font_color_picker = memnew(ColorPickerButton);
+ font_color_picker->set_custom_minimum_size(Size2(20, 0) * EDSCALE);
+ font_color_picker->set_flat(true);
+ font_color_picker->set_tooltip(TTR("Text Color"));
+ add_child(font_color_picker);
+ font_color_picker->connect("color_changed", callable_mp(this, &TextControlEditor::_font_color_changed));
+
+ add_child(memnew(VSeparator));
+
+ outline_size_list = memnew(SpinBox);
+ outline_size_list->set_tooltip(TTR("Outline Size"));
+ outline_size_list->get_line_edit()->add_theme_constant_override("minimum_character_width", 2);
+ outline_size_list->set_min(0);
+ outline_size_list->set_step(1);
+ outline_size_list->set_max(96);
+ outline_size_list->get_line_edit()->set_flat(true);
+ add_child(outline_size_list);
+ outline_size_list->connect("value_changed", callable_mp(this, &TextControlEditor::_outline_size_selected));
+
+ outline_color_picker = memnew(ColorPickerButton);
+ outline_color_picker->set_custom_minimum_size(Size2(20, 0) * EDSCALE);
+ outline_color_picker->set_flat(true);
+ outline_color_picker->set_tooltip(TTR("Outline Color"));
+ add_child(outline_color_picker);
+ outline_color_picker->connect("color_changed", callable_mp(this, &TextControlEditor::_outline_color_changed));
+
+ add_child(memnew(VSeparator));
+
+ clear_formatting = memnew(Button);
+ clear_formatting->set_flat(true);
+ clear_formatting->set_tooltip(TTR("Clear Formatting"));
+ add_child(clear_formatting);
+ clear_formatting->connect("pressed", callable_mp(this, &TextControlEditor::_clear_formatting));
+}
+
+/*************************************************************************/
+
+void TextControlEditorPlugin::edit(Object *p_object) {
+ text_ctl_editor->edit(p_object);
+}
+
+bool TextControlEditorPlugin::handles(Object *p_object) const {
+ return text_ctl_editor->handles(p_object);
+}
+
+void TextControlEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ text_ctl_editor->show();
+ } else {
+ text_ctl_editor->hide();
+ text_ctl_editor->edit(nullptr);
+ }
+}
+
+TextControlEditorPlugin::TextControlEditorPlugin(EditorNode *p_node) {
+ editor = p_node;
+ text_ctl_editor = memnew(TextControlEditor);
+ CanvasItemEditor::get_singleton()->add_control_to_menu_panel(text_ctl_editor);
+
+ text_ctl_editor->hide();
+}
diff --git a/editor/plugins/text_control_editor_plugin.h b/editor/plugins/text_control_editor_plugin.h
new file mode 100644
index 0000000000..7f4aa3754c
--- /dev/null
+++ b/editor/plugins/text_control_editor_plugin.h
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* text_control_editor_plugin.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef TEXT_CONTROL_EDITOR_PLUGIN_H
+#define TEXT_CONTROL_EDITOR_PLUGIN_H
+
+#include "canvas_item_editor_plugin.h"
+#include "editor/editor_file_system.h"
+#include "editor/editor_inspector.h"
+#include "editor/editor_node.h"
+#include "editor/editor_plugin.h"
+#include "scene/gui/color_rect.h"
+#include "scene/gui/menu_button.h"
+#include "scene/gui/option_button.h"
+#include "scene/gui/popup_menu.h"
+
+/*************************************************************************/
+
+class TextControlEditor : public HBoxContainer {
+ GDCLASS(TextControlEditor, HBoxContainer);
+
+ enum FontInfoID {
+ FONT_INFO_THEME_DEFAULT = 0,
+ FONT_INFO_USER_CUSTOM = 1,
+ FONT_INFO_ID = 100,
+ };
+
+ Map<String, Map<String, String>> fonts;
+
+ OptionButton *font_list = nullptr;
+ SpinBox *font_size_list = nullptr;
+ OptionButton *font_style_list = nullptr;
+ ColorPickerButton *font_color_picker = nullptr;
+ SpinBox *outline_size_list = nullptr;
+ ColorPickerButton *outline_color_picker = nullptr;
+ Button *clear_formatting = nullptr;
+
+ Control *edited_control = nullptr;
+ String edited_color;
+ String edited_font;
+ String edited_font_size;
+ Ref<Font> custom_font;
+
+protected:
+ void _notification(int p_notification);
+ static void _bind_methods(){};
+
+ void _find_resources(EditorFileSystemDirectory *p_dir);
+ void _reload_fonts(const String &p_path);
+
+ void _update_fonts_menu();
+ void _update_styles_menu();
+ void _update_control();
+
+ void _font_selected(int p_id);
+ void _font_style_selected(int p_id);
+ void _set_font();
+
+ void _font_size_selected(double p_size);
+ void _outline_size_selected(double p_size);
+
+ void _font_color_changed(const Color &p_color);
+ void _outline_color_changed(const Color &p_color);
+
+ void _clear_formatting();
+
+public:
+ void edit(Object *p_object);
+ bool handles(Object *p_object) const;
+
+ TextControlEditor();
+};
+
+/*************************************************************************/
+
+class TextControlEditorPlugin : public EditorPlugin {
+ GDCLASS(TextControlEditorPlugin, EditorPlugin);
+
+ TextControlEditor *text_ctl_editor;
+ EditorNode *editor;
+
+public:
+ virtual String get_name() const override { return "TextControlFontEditor"; }
+ 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;
+
+ TextControlEditorPlugin(EditorNode *p_node);
+};
+
+#endif // TEXT_CONTROL_EDITOR_PLUGIN_H
diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp
index 32bcc1a4e6..cbde382d67 100644
--- a/editor/plugins/text_editor.cpp
+++ b/editor/plugins/text_editor.cpp
@@ -65,18 +65,21 @@ void TextEditor::_load_theme_settings() {
String TextEditor::get_name() {
String name;
- if (text_file->get_path().find("local://") == -1 && text_file->get_path().find("::") == -1) {
- name = text_file->get_path().get_file();
- if (is_unsaved()) {
- if (text_file->get_path().is_empty()) {
- name = TTR("[unsaved]");
- }
- name += "(*)";
+ name = text_file->get_path().get_file();
+ if (name.is_empty()) {
+ // This appears for newly created built-in text_files before saving the scene.
+ name = TTR("[unsaved]");
+ } else if (text_file->is_built_in()) {
+ const String &text_file_name = text_file->get_name();
+ if (!text_file_name.is_empty()) {
+ // If the built-in text_file has a custom resource name defined,
+ // display the built-in text_file name as follows: `ResourceName (scene_file.tscn)`
+ name = vformat("%s (%s)", text_file_name, name.get_slice("::", 0));
}
- } else if (text_file->get_name() != "") {
- name = text_file->get_name();
- } else {
- name = text_file->get_class() + "(" + itos(text_file->get_instance_id()) + ")";
+ }
+
+ if (is_unsaved()) {
+ name += "(*)";
}
return name;
@@ -407,10 +410,6 @@ void TextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) {
code_editor->convert_case(p_case);
}
-void TextEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("add_syntax_highlighter", "highlighter"), &TextEditor::add_syntax_highlighter);
-}
-
static ScriptEditorBase *create_editor(const RES &p_resource) {
if (Object::cast_to<TextFile>(*p_resource)) {
return memnew(TextEditor);
@@ -426,7 +425,7 @@ void TextEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) {
Ref<InputEventMouseButton> mb = ev;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (mb->get_button_index() == MouseButton::RIGHT) {
CodeEdit *tx = code_editor->get_text_editor();
Point2i pos = tx->get_line_column_at_pos(mb->get_global_position() - tx->get_global_position());
@@ -508,11 +507,15 @@ void TextEditor::_make_context_menu(bool p_selection, bool p_can_fold, bool p_is
context_menu->set_item_disabled(context_menu->get_item_index(EDIT_UNDO), !tx->has_undo());
context_menu->set_item_disabled(context_menu->get_item_index(EDIT_REDO), !tx->has_redo());
- context_menu->set_position(get_global_transform().xform(p_position));
- context_menu->set_size(Vector2(1, 1));
+ context_menu->set_position(get_screen_position() + p_position);
+ context_menu->reset_size();
context_menu->popup();
}
+void TextEditor::update_toggle_scripts_button() {
+ code_editor->update_toggle_scripts_button();
+}
+
TextEditor::TextEditor() {
code_editor = memnew(CodeTextEditor);
add_child(code_editor);
@@ -521,6 +524,7 @@ TextEditor::TextEditor() {
code_editor->connect("validate_script", callable_mp(this, &TextEditor::_validate_script));
code_editor->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
code_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ code_editor->show_toggle_scripts_button();
update_settings();
diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h
index 839e1c5f7a..7404557f46 100644
--- a/editor/plugins/text_editor.h
+++ b/editor/plugins/text_editor.h
@@ -87,8 +87,6 @@ private:
};
protected:
- static void _bind_methods();
-
void _edit_option(int p_op);
void _make_context_menu(bool p_selection, bool p_can_fold, bool p_is_folded, Vector2 p_position);
void _text_edit_gui_input(const Ref<InputEvent> &ev);
@@ -121,6 +119,8 @@ public:
virtual void set_edit_state(const Variant &p_state) override;
virtual Vector<String> get_functions() override;
virtual Array get_breakpoints() override;
+ virtual void set_breakpoint(int p_line, bool p_enabled) override{};
+ virtual void clear_breakpoints() override{};
virtual void goto_line(int p_line, bool p_with_error = false) override;
void goto_line_selection(int p_line, int p_begin, int p_end);
virtual void set_executing_line(int p_line) override;
@@ -137,6 +137,7 @@ public:
virtual void set_debugger_active(bool p_active) override;
virtual void set_tooltip_request_func(String p_method, Object *p_obj) override;
virtual void add_callback(const String &p_function, PackedStringArray p_args) override;
+ void update_toggle_scripts_button() override;
virtual Control *get_edit_menu() override;
virtual void clear_edit_menu() override;
diff --git a/editor/plugins/texture_3d_editor_plugin.cpp b/editor/plugins/texture_3d_editor_plugin.cpp
index bd1923f4ab..b4e394a1c0 100644
--- a/editor/plugins/texture_3d_editor_plugin.cpp
+++ b/editor/plugins/texture_3d_editor_plugin.cpp
@@ -173,7 +173,7 @@ Texture3DEditor::Texture3DEditor() {
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
info->add_theme_color_override("font_color", Color(1, 1, 1, 1));
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.5));
- info->add_theme_constant_override("shadow_as_outline", 1);
+ info->add_theme_constant_override("shadow_outline_size", 1);
info->add_theme_constant_override("shadow_offset_x", 2);
info->add_theme_constant_override("shadow_offset_y", 2);
diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp
index 44db06bcfd..e25b0270b4 100644
--- a/editor/plugins/texture_editor_plugin.cpp
+++ b/editor/plugins/texture_editor_plugin.cpp
@@ -58,6 +58,21 @@ void TexturePreview::_notification(int p_what) {
}
}
+void TexturePreview::_update_metadata_label_text() {
+ Ref<Texture2D> texture = texture_display->get_texture();
+
+ String format;
+ if (Object::cast_to<ImageTexture>(*texture)) {
+ format = Image::get_format_name(Object::cast_to<ImageTexture>(*texture)->get_format());
+ } else if (Object::cast_to<StreamTexture2D>(*texture)) {
+ format = Image::get_format_name(Object::cast_to<StreamTexture2D>(*texture)->get_format());
+ } else {
+ format = texture->get_class();
+ }
+
+ metadata_label->set_text(itos(texture->get_width()) + "x" + itos(texture->get_height()) + " " + format);
+}
+
TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
checkerboard = memnew(TextureRect);
checkerboard->set_stretch_mode(TextureRect::STRETCH_TILE);
@@ -75,16 +90,8 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
if (p_show_metadata) {
metadata_label = memnew(Label);
- String format;
- if (Object::cast_to<ImageTexture>(*p_texture)) {
- format = Image::get_format_name(Object::cast_to<ImageTexture>(*p_texture)->get_format());
- } else if (Object::cast_to<StreamTexture2D>(*p_texture)) {
- format = Image::get_format_name(Object::cast_to<StreamTexture2D>(*p_texture)->get_format());
- } else {
- format = p_texture->get_class();
- }
-
- metadata_label->set_text(itos(p_texture->get_width()) + "x" + itos(p_texture->get_height()) + " " + format);
+ _update_metadata_label_text();
+ p_texture->connect("changed", callable_mp(this, &TexturePreview::_update_metadata_label_text));
// 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"));
@@ -94,7 +101,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) {
metadata_label->add_theme_color_override("font_outline_color", Color::named("black"));
metadata_label->add_theme_constant_override("outline_size", 2 * EDSCALE);
- metadata_label->add_theme_constant_override("shadow_as_outline", 1);
+ metadata_label->add_theme_constant_override("shadow_outline_size", 1);
metadata_label->set_h_size_flags(Control::SIZE_SHRINK_END);
metadata_label->set_v_size_flags(Control::SIZE_SHRINK_END);
diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h
index 36a5513ea6..60349febd7 100644
--- a/editor/plugins/texture_editor_plugin.h
+++ b/editor/plugins/texture_editor_plugin.h
@@ -44,6 +44,8 @@ private:
TextureRect *checkerboard = nullptr;
Label *metadata_label = nullptr;
+ void _update_metadata_label_text();
+
protected:
void _notification(int p_what);
diff --git a/editor/plugins/texture_layered_editor_plugin.cpp b/editor/plugins/texture_layered_editor_plugin.cpp
index 424e018a47..1f536d13cf 100644
--- a/editor/plugins/texture_layered_editor_plugin.cpp
+++ b/editor/plugins/texture_layered_editor_plugin.cpp
@@ -38,7 +38,7 @@ void TextureLayeredEditor::gui_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
Ref<InputEventMouseMotion> mm = p_event;
- if (mm.is_valid() && mm->get_button_mask() & MOUSE_BUTTON_MASK_LEFT) {
+ if (mm.is_valid() && (mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE) {
y_rot += -mm->get_relative().x * 0.01;
x_rot += mm->get_relative().y * 0.01;
_update_material();
@@ -249,7 +249,7 @@ TextureLayeredEditor::TextureLayeredEditor() {
info->set_v_grow_direction(GROW_DIRECTION_BEGIN);
info->add_theme_color_override("font_color", Color(1, 1, 1, 1));
info->add_theme_color_override("font_shadow_color", Color(0, 0, 0, 0.5));
- info->add_theme_constant_override("shadow_as_outline", 1);
+ info->add_theme_constant_override("shadow_outline_size", 1);
info->add_theme_constant_override("shadow_offset_x", 2);
info->add_theme_constant_override("shadow_offset_y", 2);
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 1a6eb7b63b..8e1c81a876 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -50,7 +50,7 @@ void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) {
EditorNode::get_singleton()->get_theme_base()->get_theme_color(SNAME("mono_color"), SNAME("Editor")).inverted() * Color(1, 1, 1, 0.5),
Math::round(2 * EDSCALE));
- while ((to - from).length_squared() > 200) {
+ while (from.distance_squared_to(to) > 200) {
edit_draw->draw_line(
from,
from + line,
@@ -63,16 +63,16 @@ void draw_margin_line(Control *edit_draw, Vector2 from, Vector2 to) {
void TextureRegionEditor::_region_draw() {
Ref<Texture2D> base_tex = nullptr;
- if (node_sprite) {
- base_tex = node_sprite->get_texture();
+ if (atlas_tex.is_valid()) {
+ base_tex = atlas_tex->get_atlas();
+ } else if (node_sprite_2d) {
+ base_tex = node_sprite_2d->get_texture();
} else if (node_sprite_3d) {
base_tex = node_sprite_3d->get_texture();
} else if (node_ninepatch) {
base_tex = node_ninepatch->get_texture();
} else if (obj_styleBox.is_valid()) {
base_tex = obj_styleBox->get_texture();
- } else if (atlas_tex.is_valid()) {
- base_tex = atlas_tex->get_atlas();
}
if (base_tex.is_null()) {
@@ -284,7 +284,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
Ref<InputEventMouseButton> mb = p_input;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (node_ninepatch || obj_styleBox.is_valid()) {
edited_margin = -1;
@@ -321,35 +321,38 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
prev_margin = margins[3];
}
if (edited_margin >= 0) {
- drag_from = Vector2(mb->get_position().x, mb->get_position().y);
+ drag_from = mb->get_position();
drag = true;
}
}
if (edited_margin < 0 && snap_mode == SNAP_AUTOSLICE) {
- Vector2 point = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
+ Vector2 point = mtx.affine_inverse().xform(mb->get_position());
for (const Rect2 &E : autoslice_cache) {
if (E.has_point(point)) {
rect = E;
- if (Input::get_singleton()->is_key_pressed(KEY_CTRL) && !(Input::get_singleton()->is_key_pressed(Key(KEY_SHIFT | KEY_ALT)))) {
+ if (Input::get_singleton()->is_key_pressed(Key::CTRL) && !(Input::get_singleton()->is_key_pressed(Key(Key::SHIFT | Key::ALT)))) {
Rect2 r;
- if (node_sprite) {
- r = node_sprite->get_region_rect();
+ if (atlas_tex.is_valid()) {
+ r = atlas_tex->get_region();
+ } else if (node_sprite_2d) {
+ r = node_sprite_2d->get_region_rect();
} else if (node_sprite_3d) {
r = node_sprite_3d->get_region_rect();
} else if (node_ninepatch) {
r = node_ninepatch->get_region_rect();
} else if (obj_styleBox.is_valid()) {
r = obj_styleBox->get_region_rect();
- } else if (atlas_tex.is_valid()) {
- r = atlas_tex->get_region();
}
rect.expand_to(r.position);
- rect.expand_to(r.position + r.size);
+ rect.expand_to(r.get_end());
}
undo_redo->create_action(TTR("Set Region Rect"));
- if (node_sprite) {
- undo_redo->add_do_method(node_sprite, "set_region_rect", rect);
- undo_redo->add_undo_method(node_sprite, "set_region_rect", node_sprite->get_region_rect());
+ if (atlas_tex.is_valid()) {
+ undo_redo->add_do_method(atlas_tex.ptr(), "set_region", rect);
+ undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
+ } else if (node_sprite_2d) {
+ undo_redo->add_do_method(node_sprite_2d, "set_region_rect", rect);
+ undo_redo->add_undo_method(node_sprite_2d, "set_region_rect", node_sprite_2d->get_region_rect());
} else if (node_sprite_3d) {
undo_redo->add_do_method(node_sprite_3d, "set_region_rect", rect);
undo_redo->add_undo_method(node_sprite_3d, "set_region_rect", node_sprite_3d->get_region_rect());
@@ -359,9 +362,6 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
} else if (obj_styleBox.is_valid()) {
undo_redo->add_do_method(obj_styleBox.ptr(), "set_region_rect", rect);
undo_redo->add_undo_method(obj_styleBox.ptr(), "set_region_rect", obj_styleBox->get_region_rect());
- } else if (atlas_tex.is_valid()) {
- undo_redo->add_do_method(atlas_tex.ptr(), "set_region", rect);
- undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
}
undo_redo->add_do_method(this, "_update_rect");
undo_redo->add_undo_method(this, "_update_rect");
@@ -372,28 +372,28 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
}
}
} else if (edited_margin < 0) {
- drag_from = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
+ drag_from = mtx.affine_inverse().xform(mb->get_position());
if (snap_mode == SNAP_PIXEL) {
drag_from = drag_from.snapped(Vector2(1, 1));
} else if (snap_mode == SNAP_GRID) {
drag_from = snap_point(drag_from);
}
drag = true;
- if (node_sprite) {
- rect_prev = node_sprite->get_region_rect();
+ if (atlas_tex.is_valid()) {
+ rect_prev = atlas_tex->get_region();
+ } else if (node_sprite_2d) {
+ rect_prev = node_sprite_2d->get_region_rect();
} else if (node_sprite_3d) {
rect_prev = node_sprite_3d->get_region_rect();
} else if (node_ninepatch) {
rect_prev = node_ninepatch->get_region_rect();
} else if (obj_styleBox.is_valid()) {
rect_prev = obj_styleBox->get_region_rect();
- } else if (atlas_tex.is_valid()) {
- rect_prev = atlas_tex->get_region();
}
for (int i = 0; i < 8; i++) {
Vector2 tuv = endpoints[i];
- if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < handle_radius) {
+ if (tuv.distance_to(mb->get_position()) < handle_radius) {
drag_index = i;
}
}
@@ -419,15 +419,15 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
edited_margin = -1;
} else {
undo_redo->create_action(TTR("Set Region Rect"));
- if (node_sprite) {
- undo_redo->add_do_method(node_sprite, "set_region_rect", node_sprite->get_region_rect());
- undo_redo->add_undo_method(node_sprite, "set_region_rect", rect_prev);
+ if (atlas_tex.is_valid()) {
+ undo_redo->add_do_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
+ undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", rect_prev);
+ } else if (node_sprite_2d) {
+ undo_redo->add_do_method(node_sprite_2d, "set_region_rect", node_sprite_2d->get_region_rect());
+ undo_redo->add_undo_method(node_sprite_2d, "set_region_rect", rect_prev);
} else if (node_sprite_3d) {
undo_redo->add_do_method(node_sprite_3d, "set_region_rect", node_sprite_3d->get_region_rect());
undo_redo->add_undo_method(node_sprite_3d, "set_region_rect", rect_prev);
- } else if (atlas_tex.is_valid()) {
- undo_redo->add_do_method(atlas_tex.ptr(), "set_region", atlas_tex->get_region());
- undo_redo->add_undo_method(atlas_tex.ptr(), "set_region", rect_prev);
} else if (node_ninepatch) {
undo_redo->add_do_method(node_ninepatch, "set_region_rect", node_ninepatch->get_region_rect());
undo_redo->add_undo_method(node_ninepatch, "set_region_rect", rect_prev);
@@ -446,7 +446,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
creating = false;
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT && mb->is_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {
if (drag) {
drag = false;
if (edited_margin >= 0) {
@@ -465,9 +465,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
drag_index = -1;
}
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) {
_zoom_on_position(draw_zoom * ((0.95 + (0.05 * mb->get_factor())) / 0.95), mb->get_position());
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) {
_zoom_on_position(draw_zoom * (1 - (0.05 * mb->get_factor())), mb->get_position());
}
}
@@ -475,7 +475,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
Ref<InputEventMouseMotion> mm = p_input;
if (mm.is_valid()) {
- if (mm->get_button_mask() & MOUSE_BUTTON_MASK_MIDDLE || Input::get_singleton()->is_key_pressed(KEY_SPACE)) {
+ if ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || Input::get_singleton()->is_key_pressed(Key::SPACE)) {
Vector2 dragged(mm->get_relative().x / draw_zoom, mm->get_relative().y / draw_zoom);
hscroll->set_value(hscroll->get_value() - dragged.x);
vscroll->set_value(vscroll->get_value() - dragged.y);
@@ -544,7 +544,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) {
switch (drag_index) {
case 0: {
- Vector2 p = rect_prev.position + rect_prev.size;
+ Vector2 p = rect_prev.get_end();
rect = Rect2(p, Size2());
rect.expand_to(new_pos);
apply_rect(rect);
@@ -674,8 +674,7 @@ void TextureRegionEditor::_zoom_on_position(float p_zoom, Point2 p_position) {
draw_zoom = p_zoom;
Point2 ofs = p_position;
ofs = ofs / prev_zoom - ofs / draw_zoom;
- draw_ofs.x = Math::round(draw_ofs.x + ofs.x);
- draw_ofs.y = Math::round(draw_ofs.y + ofs.y);
+ draw_ofs = (draw_ofs + ofs).round();
edit_draw->update();
}
@@ -693,22 +692,24 @@ void TextureRegionEditor::_zoom_out() {
}
void TextureRegionEditor::apply_rect(const Rect2 &p_rect) {
- if (node_sprite) {
- node_sprite->set_region_rect(p_rect);
+ if (atlas_tex.is_valid()) {
+ atlas_tex->set_region(p_rect);
+ } else if (node_sprite_2d) {
+ node_sprite_2d->set_region_rect(p_rect);
} else if (node_sprite_3d) {
node_sprite_3d->set_region_rect(p_rect);
} else if (node_ninepatch) {
node_ninepatch->set_region_rect(p_rect);
} else if (obj_styleBox.is_valid()) {
obj_styleBox->set_region_rect(p_rect);
- } else if (atlas_tex.is_valid()) {
- atlas_tex->set_region(p_rect);
}
}
void TextureRegionEditor::_update_rect() {
- if (node_sprite) {
- rect = node_sprite->get_region_rect();
+ if (atlas_tex.is_valid()) {
+ rect = atlas_tex->get_region();
+ } else if (node_sprite_2d) {
+ rect = node_sprite_2d->get_region_rect();
} else if (node_sprite_3d) {
rect = node_sprite_3d->get_region_rect();
} else if (node_ninepatch) {
@@ -718,8 +719,6 @@ void TextureRegionEditor::_update_rect() {
}
} else if (obj_styleBox.is_valid()) {
rect = obj_styleBox->get_region_rect();
- } else if (atlas_tex.is_valid()) {
- rect = atlas_tex->get_region();
}
}
@@ -728,16 +727,16 @@ void TextureRegionEditor::_update_autoslice() {
autoslice_cache.clear();
Ref<Texture2D> texture = nullptr;
- if (node_sprite) {
- texture = node_sprite->get_texture();
+ if (atlas_tex.is_valid()) {
+ texture = atlas_tex->get_atlas();
+ } else if (node_sprite_2d) {
+ texture = node_sprite_2d->get_texture();
} else if (node_sprite_3d) {
texture = node_sprite_3d->get_texture();
} else if (node_ninepatch) {
texture = node_ninepatch->get_texture();
} else if (obj_styleBox.is_valid()) {
texture = obj_styleBox->get_texture();
- } else if (atlas_tex.is_valid()) {
- texture = atlas_tex->get_atlas();
}
if (texture.is_null()) {
@@ -823,8 +822,8 @@ void TextureRegionEditor::_notification(int p_what) {
}
void TextureRegionEditor::_node_removed(Object *p_obj) {
- if (p_obj == node_sprite || p_obj == node_sprite_3d || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) {
- node_sprite = nullptr;
+ if (p_obj == node_sprite_2d || p_obj == node_sprite_3d || p_obj == node_ninepatch || p_obj == obj_styleBox.ptr() || p_obj == atlas_tex.ptr()) {
+ node_sprite_2d = nullptr;
node_sprite_3d = nullptr;
node_ninepatch = nullptr;
obj_styleBox = Ref<StyleBox>(nullptr);
@@ -852,17 +851,17 @@ bool TextureRegionEditor::is_ninepatch() {
return node_ninepatch != nullptr;
}
-Sprite3D *TextureRegionEditor::get_sprite_3d() {
- return node_sprite_3d;
+Sprite2D *TextureRegionEditor::get_sprite_2d() {
+ return node_sprite_2d;
}
-Sprite2D *TextureRegionEditor::get_sprite() {
- return node_sprite;
+Sprite3D *TextureRegionEditor::get_sprite_3d() {
+ return node_sprite_3d;
}
void TextureRegionEditor::edit(Object *p_obj) {
- if (node_sprite) {
- node_sprite->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
+ if (node_sprite_2d) {
+ node_sprite_2d->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
}
if (node_sprite_3d) {
node_sprite_3d->disconnect("texture_changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
@@ -877,7 +876,7 @@ void TextureRegionEditor::edit(Object *p_obj) {
atlas_tex->disconnect("changed", callable_mp(this, &TextureRegionEditor::_texture_changed));
}
if (p_obj) {
- node_sprite = Object::cast_to<Sprite2D>(p_obj);
+ node_sprite_2d = Object::cast_to<Sprite2D>(p_obj);
node_sprite_3d = Object::cast_to<Sprite3D>(p_obj);
node_ninepatch = Object::cast_to<NinePatchRect>(p_obj);
@@ -898,14 +897,14 @@ void TextureRegionEditor::edit(Object *p_obj) {
}
_edit_region();
} else {
- node_sprite = nullptr;
+ node_sprite_2d = nullptr;
node_sprite_3d = nullptr;
node_ninepatch = nullptr;
obj_styleBox = Ref<StyleBoxTexture>(nullptr);
atlas_tex = Ref<AtlasTexture>(nullptr);
}
edit_draw->update();
- if ((node_sprite && !node_sprite->is_region_enabled()) || (node_sprite_3d && !node_sprite_3d->is_region_enabled())) {
+ if ((node_sprite_2d && !node_sprite_2d->is_region_enabled()) || (node_sprite_3d && !node_sprite_3d->is_region_enabled())) {
set_process(true);
}
if (!p_obj) {
@@ -922,16 +921,16 @@ void TextureRegionEditor::_texture_changed() {
void TextureRegionEditor::_edit_region() {
Ref<Texture2D> texture = nullptr;
- if (node_sprite) {
- texture = node_sprite->get_texture();
+ if (atlas_tex.is_valid()) {
+ texture = atlas_tex->get_atlas();
+ } else if (node_sprite_2d) {
+ texture = node_sprite_2d->get_texture();
} else if (node_sprite_3d) {
texture = node_sprite_3d->get_texture();
} else if (node_ninepatch) {
texture = node_ninepatch->get_texture();
} else if (obj_styleBox.is_valid()) {
texture = obj_styleBox->get_texture();
- } else if (atlas_tex.is_valid()) {
- texture = atlas_tex->get_atlas();
}
if (texture.is_null()) {
@@ -967,7 +966,7 @@ Vector2 TextureRegionEditor::snap_point(Vector2 p_target) const {
}
TextureRegionEditor::TextureRegionEditor(EditorNode *p_editor) {
- node_sprite = nullptr;
+ node_sprite_2d = nullptr;
node_sprite_3d = nullptr;
node_ninepatch = nullptr;
obj_styleBox = Ref<StyleBoxTexture>(nullptr);
@@ -1122,7 +1121,9 @@ void TextureRegionEditorPlugin::_editor_visiblity_changed() {
void TextureRegionEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
texture_region_button->show();
- bool is_node_configured = region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch() || (region_editor->get_sprite() && region_editor->get_sprite()->is_region_enabled()) || (region_editor->get_sprite_3d() && region_editor->get_sprite_3d()->is_region_enabled());
+ bool is_node_configured = region_editor->is_stylebox() || region_editor->is_atlas_texture() || region_editor->is_ninepatch();
+ is_node_configured |= region_editor->get_sprite_2d() && region_editor->get_sprite_2d()->is_region_enabled();
+ is_node_configured |= region_editor->get_sprite_3d() && region_editor->get_sprite_3d()->is_region_enabled();
if ((is_node_configured && !manually_hidden) || texture_region_button->is_pressed()) {
editor->make_bottom_panel_item_visible(region_editor);
}
diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h
index d3db0a08a9..c043d6ae33 100644
--- a/editor/plugins/texture_region_editor_plugin.h
+++ b/editor/plugins/texture_region_editor_plugin.h
@@ -83,7 +83,7 @@ class TextureRegionEditor : public VBoxContainer {
Vector2 snap_step;
Vector2 snap_separation;
- Sprite2D *node_sprite;
+ Sprite2D *node_sprite_2d;
Sprite3D *node_sprite_3d;
NinePatchRect *node_ninepatch;
Ref<StyleBoxTexture> obj_styleBox;
@@ -134,8 +134,8 @@ public:
bool is_stylebox();
bool is_atlas_texture();
bool is_ninepatch();
+ Sprite2D *get_sprite_2d();
Sprite3D *get_sprite_3d();
- Sprite2D *get_sprite();
void edit(Object *p_obj);
TextureRegionEditor(EditorNode *p_editor);
diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp
index 165a381407..b5d2b571f5 100644
--- a/editor/plugins/theme_editor_plugin.cpp
+++ b/editor/plugins/theme_editor_plugin.cpp
@@ -437,8 +437,8 @@ void ThemeItemImportTree::_update_total_selected(Theme::DataType p_data_type) {
}
int count = 0;
- for (Map<ThemeItem, ItemCheckedState>::Element *E = selected_items.front(); E; E = E->next()) {
- ThemeItem ti = E->key();
+ for (const KeyValue<ThemeItem, ItemCheckedState> &E : selected_items) {
+ ThemeItem ti = E.key;
if (ti.data_type == p_data_type) {
count++;
}
@@ -759,7 +759,7 @@ void ThemeItemImportTree::_import_selected() {
ProgressDialog::get_singleton()->add_task("import_theme_items", TTR("Importing Theme Items"), selected_items.size() + 2);
int idx = 0;
- for (Map<ThemeItem, ItemCheckedState>::Element *E = selected_items.front(); E; E = E->next()) {
+ for (KeyValue<ThemeItem, ItemCheckedState> &E : selected_items) {
// Arbitrary number of items to skip from reporting.
// Reduces the number of UI updates that this causes when copying large themes.
if (idx % 10 == 0) {
@@ -769,8 +769,8 @@ void ThemeItemImportTree::_import_selected() {
ProgressDialog::get_singleton()->task_step("import_theme_items", TTR("Importing items {n}/{n}").format(arr, "{n}"), idx);
}
- ItemCheckedState cs = E->get();
- ThemeItem ti = E->key();
+ ItemCheckedState cs = E.value;
+ ThemeItem ti = E.key;
if (cs == SELECT_IMPORT_DEFINITION || cs == SELECT_IMPORT_FULL) {
Variant item_value = Variant();
@@ -941,7 +941,7 @@ ThemeItemImportTree::ThemeItemImportTree() {
ScrollContainer *import_bulk_sc = memnew(ScrollContainer);
import_bulk_sc->set_custom_minimum_size(Size2(260.0, 0.0) * EDSCALE);
- import_bulk_sc->set_enable_h_scroll(false);
+ import_bulk_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
import_main_hb->add_child(import_bulk_sc);
VBoxContainer *import_bulk_vb = memnew(VBoxContainer);
import_bulk_vb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
@@ -1115,7 +1115,7 @@ ThemeItemImportTree::ThemeItemImportTree() {
label_set->add_child(select_items_label);
HBoxContainer *button_set = memnew(HBoxContainer);
- button_set->set_alignment(BoxContainer::ALIGN_END);
+ button_set->set_alignment(BoxContainer::ALIGNMENT_END);
all_set->add_child(button_set);
select_all_items_button->set_flat(true);
select_all_items_button->set_tooltip(select_all_items_tooltip);
@@ -1130,7 +1130,7 @@ ThemeItemImportTree::ThemeItemImportTree() {
button_set->add_child(deselect_all_items_button);
deselect_all_items_button->connect("pressed", callable_mp(this, &ThemeItemImportTree::_deselect_all_data_type_pressed), varray(i));
- total_selected_items_label->set_align(Label::ALIGN_RIGHT);
+ total_selected_items_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
total_selected_items_label->hide();
import_bulk_vb->add_child(total_selected_items_label);
@@ -1711,13 +1711,13 @@ void ThemeItemEditorDialog::_edit_theme_item_gui_input(const Ref<InputEvent> &p_
}
switch (k->get_keycode()) {
- case KEY_KP_ENTER:
- case KEY_ENTER: {
+ case Key::KP_ENTER:
+ case Key::ENTER: {
_confirm_edit_theme_item();
edit_theme_item_dialog->hide();
edit_theme_item_dialog->set_input_as_handled();
} break;
- case KEY_ESCAPE: {
+ case Key::ESCAPE: {
edit_theme_item_dialog->hide();
edit_theme_item_dialog->set_input_as_handled();
} break;
@@ -1783,7 +1783,7 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() {
set_hide_on_ok(false); // Closing may require a confirmation in some cases.
tc = memnew(TabContainer);
- tc->set_tab_align(TabContainer::TabAlign::ALIGN_LEFT);
+ tc->set_tab_alignment(TabContainer::ALIGNMENT_LEFT);
add_child(tc);
// Edit Items tab.
@@ -1909,8 +1909,8 @@ ThemeItemEditorDialog::ThemeItemEditorDialog() {
edit_items_message = memnew(Label);
edit_items_message->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
edit_items_message->set_mouse_filter(Control::MOUSE_FILTER_STOP);
- edit_items_message->set_align(Label::ALIGN_CENTER);
- edit_items_message->set_valign(Label::VALIGN_CENTER);
+ edit_items_message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ edit_items_message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
edit_items_message->set_autowrap_mode(Label::AUTOWRAP_WORD);
edit_items_tree->add_child(edit_items_message);
@@ -1996,7 +1996,7 @@ void ThemeTypeDialog::_dialog_about_to_show() {
}
void ThemeTypeDialog::ok_pressed() {
- emit_signal(SNAME("type_selected"), add_type_filter->get_text().strip_edges());
+ _add_type_selected(add_type_filter->get_text().strip_edges());
}
void ThemeTypeDialog::_update_add_type_options(const String &p_filter) {
@@ -2042,12 +2042,25 @@ void ThemeTypeDialog::_add_type_options_cbk(int p_index) {
}
void ThemeTypeDialog::_add_type_dialog_entered(const String &p_value) {
- emit_signal(SNAME("type_selected"), p_value.strip_edges());
- hide();
+ _add_type_selected(p_value.strip_edges());
}
void ThemeTypeDialog::_add_type_dialog_activated(int p_index) {
- emit_signal(SNAME("type_selected"), add_type_options->get_item_text(p_index));
+ _add_type_selected(add_type_options->get_item_text(p_index));
+}
+
+void ThemeTypeDialog::_add_type_selected(const String &p_type_name) {
+ pre_submitted_value = p_type_name;
+ if (p_type_name.is_empty()) {
+ add_type_confirmation->popup_centered();
+ return;
+ }
+
+ _add_type_confirmed();
+}
+
+void ThemeTypeDialog::_add_type_confirmed() {
+ emit_signal(SNAME("type_selected"), pre_submitted_value);
hide();
}
@@ -2082,11 +2095,13 @@ void ThemeTypeDialog::set_include_own_types(bool p_enable) {
}
ThemeTypeDialog::ThemeTypeDialog() {
+ set_hide_on_ok(false);
+
VBoxContainer *add_type_vb = memnew(VBoxContainer);
add_child(add_type_vb);
Label *add_type_filter_label = memnew(Label);
- add_type_filter_label->set_text(TTR("Name:"));
+ add_type_filter_label->set_text(TTR("Filter the list of types or create a new custom type:"));
add_type_vb->add_child(add_type_filter_label);
add_type_filter = memnew(LineEdit);
@@ -2095,7 +2110,7 @@ ThemeTypeDialog::ThemeTypeDialog() {
add_type_filter->connect("text_submitted", callable_mp(this, &ThemeTypeDialog::_add_type_dialog_entered));
Label *add_type_options_label = memnew(Label);
- add_type_options_label->set_text(TTR("Node Types:"));
+ add_type_options_label->set_text(TTR("Available Node-based types:"));
add_type_vb->add_child(add_type_options_label);
add_type_options = memnew(ItemList);
@@ -2103,6 +2118,12 @@ ThemeTypeDialog::ThemeTypeDialog() {
add_type_vb->add_child(add_type_options);
add_type_options->connect("item_selected", callable_mp(this, &ThemeTypeDialog::_add_type_options_cbk));
add_type_options->connect("item_activated", callable_mp(this, &ThemeTypeDialog::_add_type_dialog_activated));
+
+ add_type_confirmation = memnew(ConfirmationDialog);
+ add_type_confirmation->set_title(TTR("Type name is empty!"));
+ add_type_confirmation->set_text(TTR("Are you sure you want to create an empty type?"));
+ add_type_confirmation->connect("confirmed", callable_mp(this, &ThemeTypeDialog::_add_type_confirmed));
+ add_child(add_type_confirmation);
}
VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) {
@@ -2113,7 +2134,7 @@ VBoxContainer *ThemeTypeEditor::_create_item_list(Theme::DataType p_data_type) {
ScrollContainer *items_sc = memnew(ScrollContainer);
items_sc->set_v_size_flags(SIZE_EXPAND_FILL);
- items_sc->set_enable_h_scroll(false);
+ items_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
items_tab->add_child(items_sc);
VBoxContainer *items_list = memnew(VBoxContainer);
items_list->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -2581,11 +2602,11 @@ void ThemeTypeEditor::_update_type_items() {
}
// Various type settings.
- if (ClassDB::class_exists(edited_type)) {
+ if (edited_type.is_empty() || ClassDB::class_exists(edited_type)) {
type_variation_edit->set_editable(false);
type_variation_edit->set_text("");
type_variation_button->hide();
- type_variation_locked->show();
+ type_variation_locked->set_visible(!edited_type.is_empty());
} else {
type_variation_edit->set_editable(true);
type_variation_edit->set_text(edited_theme->get_type_variation_base(edited_type));
@@ -2603,6 +2624,7 @@ void ThemeTypeEditor::_list_type_selected(int p_index) {
void ThemeTypeEditor::_add_type_button_cbk() {
add_type_mode = ADD_THEME_TYPE;
add_type_dialog->set_title(TTR("Add Item Type"));
+ add_type_dialog->get_ok_button()->set_text(TTR("Add Type"));
add_type_dialog->set_include_own_types(false);
add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE);
}
@@ -2848,7 +2870,7 @@ void ThemeTypeEditor::_font_size_item_changed(float p_value, String p_item_name)
edited_theme->set_font_size(p_item_name, edited_type, int(p_value));
}
-void ThemeTypeEditor::_edit_resource_item(RES p_resource) {
+void ThemeTypeEditor::_edit_resource_item(RES p_resource, bool p_edit) {
EditorNode::get_singleton()->edit_resource(p_resource);
}
@@ -2969,7 +2991,8 @@ void ThemeTypeEditor::_type_variation_changed(const String p_value) {
void ThemeTypeEditor::_add_type_variation_cbk() {
add_type_mode = ADD_VARIATION_BASE;
- add_type_dialog->set_title(TTR("Add Variation Base Type"));
+ add_type_dialog->set_title(TTR("Set Variation Base Type"));
+ add_type_dialog->get_ok_button()->set_text(TTR("Set Base Type"));
add_type_dialog->set_include_own_types(true);
add_type_dialog->popup_centered(Size2(560, 420) * EDSCALE);
}
@@ -3102,7 +3125,7 @@ ThemeTypeEditor::ThemeTypeEditor() {
ScrollContainer *type_settings_sc = memnew(ScrollContainer);
type_settings_sc->set_v_size_flags(SIZE_EXPAND_FILL);
- type_settings_sc->set_enable_h_scroll(false);
+ type_settings_sc->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
type_settings_tab->add_child(type_settings_sc);
VBoxContainer *type_settings_list = memnew(VBoxContainer);
type_settings_list->set_h_size_flags(SIZE_EXPAND_FILL);
@@ -3129,7 +3152,7 @@ ThemeTypeEditor::ThemeTypeEditor() {
type_variation_locked = memnew(Label);
type_variation_vb->add_child(type_variation_locked);
- type_variation_locked->set_align(Label::ALIGN_CENTER);
+ type_variation_locked->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
type_variation_locked->set_autowrap_mode(Label::AUTOWRAP_WORD);
type_variation_locked->set_text(TTR("A type associated with a built-in class cannot be marked as a variation of another type."));
type_variation_locked->hide();
@@ -3204,7 +3227,7 @@ void ThemeEditor::_add_preview_tab(ThemeEditorPreview *p_preview_tab, const Stri
preview_tabs->add_tab(p_preview_name, p_icon);
preview_tabs_content->add_child(p_preview_tab);
- preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("Tabs")));
+ preview_tabs->set_tab_right_button(preview_tabs->get_tab_count() - 1, EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("close"), SNAME("TabBar")));
p_preview_tab->connect("control_picked", callable_mp(this, &ThemeEditor::_preview_control_picked));
preview_tabs->set_current_tab(preview_tabs->get_tab_count() - 1);
@@ -3328,12 +3351,12 @@ ThemeEditor::ThemeEditor() {
preview_tabs_content->set_draw_behind_parent(true);
preview_tabs_vb->add_child(preview_tabs_content);
- preview_tabs = memnew(Tabs);
- preview_tabs->set_tab_align(Tabs::ALIGN_LEFT);
+ preview_tabs = memnew(TabBar);
+ preview_tabs->set_tab_alignment(TabBar::ALIGNMENT_LEFT);
preview_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
preview_tabbar_hb->add_child(preview_tabs);
preview_tabs->connect("tab_changed", callable_mp(this, &ThemeEditor::_change_preview_tab));
- preview_tabs->connect("right_button_pressed", callable_mp(this, &ThemeEditor::_remove_preview_tab));
+ preview_tabs->connect("tab_rmb_clicked", callable_mp(this, &ThemeEditor::_remove_preview_tab));
HBoxContainer *add_preview_button_hb = memnew(HBoxContainer);
preview_tabbar_hb->add_child(add_preview_button_hb);
diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h
index 5b0357e3f8..bd1bf216e1 100644
--- a/editor/plugins/theme_editor_plugin.h
+++ b/editor/plugins/theme_editor_plugin.h
@@ -31,10 +31,11 @@
#ifndef THEME_EDITOR_PLUGIN_H
#define THEME_EDITOR_PLUGIN_H
+#include "scene/gui/dialogs.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/option_button.h"
#include "scene/gui/scroll_container.h"
-#include "scene/gui/tabs.h"
+#include "scene/gui/tab_bar.h"
#include "scene/gui/texture_rect.h"
#include "scene/resources/theme.h"
#include "theme_editor_preview.h"
@@ -270,8 +271,11 @@ class ThemeTypeDialog : public ConfirmationDialog {
Ref<Theme> edited_theme;
bool include_own_types = false;
+ String pre_submitted_value;
+
LineEdit *add_type_filter;
ItemList *add_type_options;
+ ConfirmationDialog *add_type_confirmation;
void _dialog_about_to_show();
void ok_pressed() override;
@@ -283,6 +287,9 @@ class ThemeTypeDialog : public ConfirmationDialog {
void _add_type_dialog_entered(const String &p_value);
void _add_type_dialog_activated(int p_index);
+ void _add_type_selected(const String &p_type_name);
+ void _add_type_confirmed();
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -347,7 +354,6 @@ class ThemeTypeEditor : public MarginContainer {
void _update_type_items();
void _list_type_selected(int p_index);
- void _select_type(String p_type_name);
void _add_type_button_cbk();
void _add_default_type_items();
@@ -363,7 +369,7 @@ class ThemeTypeEditor : public MarginContainer {
void _color_item_changed(Color p_value, String p_item_name);
void _constant_item_changed(float p_value, String p_item_name);
void _font_size_item_changed(float p_value, String p_item_name);
- void _edit_resource_item(RES p_resource);
+ void _edit_resource_item(RES p_resource, bool p_edit);
void _font_item_changed(Ref<Font> p_value, String p_item_name);
void _icon_item_changed(Ref<Texture2D> p_value, String p_item_name);
void _stylebox_item_changed(Ref<StyleBox> p_value, String p_item_name);
@@ -391,7 +397,7 @@ class ThemeEditor : public VBoxContainer {
Ref<Theme> theme;
- Tabs *preview_tabs;
+ TabBar *preview_tabs;
PanelContainer *preview_tabs_content;
Button *add_preview_button;
EditorFileDialog *preview_scene_dialog;
diff --git a/editor/plugins/theme_editor_preview.cpp b/editor/plugins/theme_editor_preview.cpp
index 801ee0eac2..9a5ecd3802 100644
--- a/editor/plugins/theme_editor_preview.cpp
+++ b/editor/plugins/theme_editor_preview.cpp
@@ -47,7 +47,7 @@ void ThemeEditorPreview::add_preview_overlay(Control *p_overlay) {
void ThemeEditorPreview::_propagate_redraw(Control *p_at) {
p_at->notification(NOTIFICATION_THEME_CHANGED);
- p_at->minimum_size_changed();
+ p_at->update_minimum_size();
p_at->update();
for (int i = 0; i < p_at->get_child_count(); i++) {
Control *a = Object::cast_to<Control>(p_at->get_child(i));
@@ -71,6 +71,9 @@ void ThemeEditorPreview::_preview_visibility_changed() {
void ThemeEditorPreview::_picker_button_cbk() {
picker_overlay->set_visible(picker_button->is_pressed());
+ if (picker_button->is_pressed()) {
+ _reset_picker_overlay();
+ }
}
Control *ThemeEditorPreview::_find_hovered_control(Control *p_parent, Vector2 p_mouse_position) {
@@ -117,7 +120,7 @@ void ThemeEditorPreview::_draw_picker_overlay() {
}
Rect2 highlight_label_rect = highlight_rect;
- highlight_label_rect.size = theme_cache.preview_picker_font->get_string_size(highlight_name);
+ highlight_label_rect.size = theme_cache.preview_picker_font->get_string_size(highlight_name, theme_cache.font_size);
int margin_top = theme_cache.preview_picker_label->get_margin(SIDE_TOP);
int margin_left = theme_cache.preview_picker_label->get_margin(SIDE_LEFT);
@@ -126,14 +129,14 @@ void ThemeEditorPreview::_draw_picker_overlay() {
highlight_label_rect.size.x += margin_left + margin_right;
highlight_label_rect.size.y += margin_top + margin_bottom;
- highlight_label_rect.position.x = CLAMP(highlight_label_rect.position.x, 0.0, picker_overlay->get_size().width);
- highlight_label_rect.position.y = CLAMP(highlight_label_rect.position.y, 0.0, picker_overlay->get_size().height);
+ highlight_label_rect.position = highlight_label_rect.position.clamp(Vector2(), picker_overlay->get_size());
+
picker_overlay->draw_style_box(theme_cache.preview_picker_label, highlight_label_rect);
Point2 label_pos = highlight_label_rect.position;
label_pos.y += highlight_label_rect.size.y - margin_bottom;
label_pos.x += margin_left;
- picker_overlay->draw_string(theme_cache.preview_picker_font, label_pos, highlight_name);
+ picker_overlay->draw_string(theme_cache.preview_picker_font, label_pos, highlight_name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size);
}
}
@@ -144,7 +147,7 @@ void ThemeEditorPreview::_gui_input_picker_overlay(const Ref<InputEvent> &p_even
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (hovered_control) {
StringName theme_type = hovered_control->get_theme_type_variation();
if (theme_type == StringName()) {
@@ -154,6 +157,7 @@ void ThemeEditorPreview::_gui_input_picker_overlay(const Ref<InputEvent> &p_even
emit_signal(SNAME("control_picked"), theme_type);
picker_button->set_pressed(false);
picker_overlay->set_visible(false);
+ return;
}
}
@@ -164,6 +168,9 @@ void ThemeEditorPreview::_gui_input_picker_overlay(const Ref<InputEvent> &p_even
hovered_control = _find_hovered_control(preview_content, mp);
picker_overlay->update();
}
+
+ // Forward input to the scroll container underneath to allow scrolling.
+ preview_container->gui_input(p_event);
}
void ThemeEditorPreview::_reset_picker_overlay() {
@@ -188,6 +195,7 @@ void ThemeEditorPreview::_notification(int p_what) {
theme_cache.preview_picker_overlay_color = get_theme_color(SNAME("preview_picker_overlay_color"), SNAME("ThemeEditor"));
theme_cache.preview_picker_label = get_theme_stylebox(SNAME("preview_picker_label"), SNAME("ThemeEditor"));
theme_cache.preview_picker_font = get_theme_font(SNAME("status_source"), SNAME("EditorFonts"));
+ theme_cache.font_size = get_theme_font_size(SNAME("font_size"), SNAME("EditorFonts"));
} break;
case NOTIFICATION_PROCESS: {
time_left -= get_process_delta_time();
@@ -219,9 +227,7 @@ ThemeEditorPreview::ThemeEditorPreview() {
preview_body->set_v_size_flags(SIZE_EXPAND_FILL);
add_child(preview_body);
- ScrollContainer *preview_container = memnew(ScrollContainer);
- preview_container->set_enable_v_scroll(true);
- preview_container->set_enable_h_scroll(true);
+ preview_container = memnew(ScrollContainer);
preview_body->add_child(preview_container);
MarginContainer *preview_root = memnew(MarginContainer);
@@ -359,7 +365,7 @@ DefaultThemeEditorPreview::DefaultThemeEditorPreview() {
vhb->add_child(memnew(VSeparator));
VBoxContainer *hvb = memnew(VBoxContainer);
vhb->add_child(hvb);
- hvb->set_alignment(BoxContainer::ALIGN_CENTER);
+ hvb->set_alignment(BoxContainer::ALIGNMENT_CENTER);
hvb->set_h_size_flags(SIZE_EXPAND_FILL);
hvb->add_child(memnew(HSlider));
HScrollBar *hsb = memnew(HScrollBar);
diff --git a/editor/plugins/theme_editor_preview.h b/editor/plugins/theme_editor_preview.h
index 4e1b149e70..73422b4fba 100644
--- a/editor/plugins/theme_editor_preview.h
+++ b/editor/plugins/theme_editor_preview.h
@@ -55,6 +55,7 @@
class ThemeEditorPreview : public VBoxContainer {
GDCLASS(ThemeEditorPreview, VBoxContainer);
+ ScrollContainer *preview_container;
ColorRect *preview_bg;
MarginContainer *preview_overlay;
Control *picker_overlay;
@@ -65,6 +66,7 @@ class ThemeEditorPreview : public VBoxContainer {
Color preview_picker_overlay_color;
Ref<StyleBox> preview_picker_label;
Ref<Font> preview_picker_font;
+ int font_size = 16;
} theme_cache;
double time_left = 0;
diff --git a/editor/plugins/tiles/atlas_merging_dialog.cpp b/editor/plugins/tiles/atlas_merging_dialog.cpp
index d54906c98c..e4a16274fe 100644
--- a/editor/plugins/tiles/atlas_merging_dialog.cpp
+++ b/editor/plugins/tiles/atlas_merging_dialog.cpp
@@ -94,12 +94,14 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
}
// Copy the texture.
- Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id);
- Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size);
- if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
- output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
+ for (int frame = 0; frame < atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
+ Rect2i src_rect = atlas_source->get_tile_texture_region(tile_id, frame);
+ Rect2 dst_rect_wide = Rect2i(new_tile_rect_in_altas.position * new_texture_region_size, new_tile_rect_in_altas.size * new_texture_region_size);
+ if (dst_rect_wide.get_end().x > output_image->get_width() || dst_rect_wide.get_end().y > output_image->get_height()) {
+ output_image->crop(MAX(dst_rect_wide.get_end().x, output_image->get_width()), MAX(dst_rect_wide.get_end().y, output_image->get_height()));
+ }
+ output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, dst_rect_wide.get_center() - src_rect.size / 2);
}
- output_image->blit_rect(atlas_source->get_texture()->get_image(), src_rect, (dst_rect_wide.get_position() + dst_rect_wide.get_end()) / 2 - src_rect.size / 2);
}
// Compute the atlas offset.
@@ -116,6 +118,7 @@ void AtlasMergingDialog::_generate_merged(Vector<Ref<TileSetAtlasSource>> p_atla
output_image_texture.instantiate();
output_image_texture->create_from_image(output_image);
+ merged->set_name(p_atlas_sources[0]->get_name());
merged->set_texture(output_image_texture);
merged->set_texture_region_size(new_texture_region_size);
}
@@ -306,8 +309,8 @@ AtlasMergingDialog::AtlasMergingDialog() {
select_2_atlases_label = memnew(Label);
select_2_atlases_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
select_2_atlases_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- select_2_atlases_label->set_align(Label::ALIGN_CENTER);
- select_2_atlases_label->set_valign(Label::VALIGN_CENTER);
+ select_2_atlases_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ select_2_atlases_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
select_2_atlases_label->set_text(TTR("Please select two atlases or more."));
atlas_merging_right_panel->add_child(select_2_atlases_label);
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 0add83f64d..6a3261cbee 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -46,7 +46,7 @@ void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
if (mb.is_valid()) {
drag_type = DRAG_TYPE_NONE;
- Vector2i scroll_vec = Vector2((mb->get_button_index() == MOUSE_BUTTON_WHEEL_LEFT) - (mb->get_button_index() == MOUSE_BUTTON_WHEEL_RIGHT), (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP) - (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN));
+ Vector2i scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_LEFT) - (mb->get_button_index() == MouseButton::WHEEL_RIGHT), (mb->get_button_index() == MouseButton::WHEEL_UP) - (mb->get_button_index() == MouseButton::WHEEL_DOWN));
if (scroll_vec != Vector2()) {
if (mb->is_ctrl_pressed()) {
if (mb->is_shift_pressed()) {
@@ -69,7 +69,7 @@ void TileAtlasView::gui_input(const Ref<InputEvent> &p_event) {
}
}
- if (mb->get_button_index() == MOUSE_BUTTON_MIDDLE || mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (mb->get_button_index() == MouseButton::MIDDLE || mb->get_button_index() == MouseButton::RIGHT) {
if (mb->is_pressed()) {
drag_type = DRAG_TYPE_PAN;
} else {
@@ -97,15 +97,6 @@ Size2i TileAtlasView::_compute_base_tiles_control_size() {
if (texture.is_valid()) {
size = texture->get_size();
}
-
- // Extend the size to all existing tiles.
- Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
- for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
- Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
- grid_size = grid_size.max(tile_id + Vector2i(1, 1));
- }
- size = size.max(grid_size * (tile_set_atlas_source->get_texture_region_size() + tile_set_atlas_source->get_separation()) + tile_set_atlas_source->get_margins());
-
return size;
}
@@ -213,54 +204,89 @@ void TileAtlasView::_draw_base_tiles() {
Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
if (texture.is_valid()) {
Vector2i margins = tile_set_atlas_source->get_margins();
+ Vector2i separation = tile_set_atlas_source->get_separation();
Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
-
- // Draw the texture, square by square.
Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
+
+ // Draw the texture where there is no tile.
for (int x = 0; x < grid_size.x; x++) {
for (int y = 0; y < grid_size.y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_set_atlas_source->get_tile_at_coords(coords) == TileSetSource::INVALID_ATLAS_COORDS) {
- Rect2i rect = Rect2i(texture_region_size * coords + margins, texture_region_size);
- base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ Rect2i rect = Rect2i((texture_region_size + separation) * coords + margins, texture_region_size + separation);
+ rect = rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (rect.size.x > 0 && rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
}
}
}
// Draw the texture around the grid.
Rect2i rect;
+
// Top.
rect.position = Vector2i();
rect.set_end(Vector2i(texture->get_size().x, margins.y));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+
// Bottom
- int bottom_border = margins.y + (grid_size.y * texture_region_size.y);
+ int bottom_border = margins.y + (grid_size.y * (texture_region_size.y + separation.y));
if (bottom_border < texture->get_size().y) {
rect.position = Vector2i(0, bottom_border);
rect.set_end(texture->get_size());
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
+
// Left
rect.position = Vector2i(0, margins.y);
- rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * texture_region_size.y)));
+ rect.set_end(Vector2i(margins.x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
+
// Right.
- int right_border = margins.x + (grid_size.x * texture_region_size.x);
+ int right_border = margins.x + (grid_size.x * (texture_region_size.x + separation.x));
if (right_border < texture->get_size().x) {
rect.position = Vector2i(right_border, margins.y);
- rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * texture_region_size.y)));
+ rect.set_end(Vector2i(texture->get_size().x, margins.y + (grid_size.y * (texture_region_size.y + separation.y))));
base_tiles_draw->draw_texture_rect_region(texture, rect, rect);
+ base_tiles_draw->draw_rect(rect, Color(0.0, 0.0, 0.0, 0.5));
}
// Draw actual tiles, using their properties (modulation, etc...)
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
- // Update the y to max value.
- Vector2i offset_pos = (margins + (atlas_coords * texture_region_size) + tile_set_atlas_source->get_tile_texture_region(atlas_coords).size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0));
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(atlas_coords); frame++) {
+ // Update the y to max value.
+ Rect2i base_frame_rect = tile_set_atlas_source->get_tile_texture_region(atlas_coords, frame);
+ Vector2i offset_pos = base_frame_rect.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, 0);
+
+ // Draw the tile.
+ TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0, frame);
+
+ // Draw, the texture in the separation areas
+ if (separation.x > 0) {
+ Rect2i right_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(base_frame_rect.size.x, 0), Vector2i(separation.x, base_frame_rect.size.y));
+ right_sep_rect = right_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (right_sep_rect.size.x > 0 && right_sep_rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, right_sep_rect, right_sep_rect);
+ base_tiles_draw->draw_rect(right_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
+ }
- // Draw the tile.
- TileMap::draw_tile(base_tiles_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, 0);
+ if (separation.y > 0) {
+ Rect2i bottom_sep_rect = Rect2i(base_frame_rect.get_position() + Vector2i(0, base_frame_rect.size.y), Vector2i(base_frame_rect.size.x + separation.x, separation.y));
+ bottom_sep_rect = bottom_sep_rect.intersection(Rect2i(Vector2(), texture->get_size()));
+ if (bottom_sep_rect.size.x > 0 && bottom_sep_rect.size.y > 0) {
+ base_tiles_draw->draw_texture_rect_region(texture, bottom_sep_rect, bottom_sep_rect);
+ base_tiles_draw->draw_rect(bottom_sep_rect, Color(0.0, 0.0, 0.0, 0.5));
+ }
+ }
+ }
}
}
}
@@ -295,30 +321,6 @@ void TileAtlasView::_draw_base_tiles_texture_grid() {
}
}
-void TileAtlasView::_draw_base_tiles_dark() {
- Ref<Texture2D> texture = tile_set_atlas_source->get_texture();
- if (texture.is_valid()) {
- Vector2i margins = tile_set_atlas_source->get_margins();
- Vector2i separation = tile_set_atlas_source->get_separation();
- Vector2i texture_region_size = tile_set_atlas_source->get_texture_region_size();
-
- Size2i grid_size = tile_set_atlas_source->get_atlas_grid_size();
-
- // Draw each tile texture region.
- for (int x = 0; x < grid_size.x; x++) {
- for (int y = 0; y < grid_size.y; y++) {
- Vector2i origin = margins + (Vector2i(x, y) * (texture_region_size + separation));
- Vector2i base_tile_coords = tile_set_atlas_source->get_tile_at_coords(Vector2i(x, y));
-
- if (base_tile_coords == TileSetSource::INVALID_ATLAS_COORDS) {
- // Draw the grid.
- base_tiles_dark->draw_rect(Rect2i(origin, texture_region_size), Color(0.0, 0.0, 0.0, 0.5), true);
- }
- }
- }
- }
-}
-
void TileAtlasView::_draw_base_tiles_shape_grid() {
// Draw the shapes.
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
@@ -326,11 +328,18 @@ void TileAtlasView::_draw_base_tiles_shape_grid() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_effective_texture_offset(tile_id, 0);
- Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id);
- Vector2 origin = texture_region.position + (texture_region.size - tile_shape_size) / 2 + in_tile_base_offset;
- // Draw only if the tile shape fits in the texture region
- tile_set->draw_tile_shape(base_tiles_shape_grid, Rect2(origin, tile_shape_size), grid_color);
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
+ Color color = grid_color;
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(tile_id);
+ Transform2D tile_xform;
+ tile_xform.set_origin(texture_region.get_center() + in_tile_base_offset);
+ tile_xform.set_scale(tile_shape_size);
+ tile_set->draw_tile_shape(base_tiles_shape_grid, tile_xform, color);
+ }
}
}
@@ -358,7 +367,7 @@ void TileAtlasView::_draw_alternatives() {
Vector2i atlas_coords = tile_set_atlas_source->get_tile_id(i);
current_pos.x = 0;
int y_increment = 0;
- Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(atlas_coords);
+ Size2i texture_region_size = tile_set_atlas_source->get_tile_texture_region(atlas_coords).size;
int alternatives_count = tile_set_atlas_source->get_alternative_tiles_count(atlas_coords);
for (int j = 1; j < alternatives_count; j++) {
int alternative_id = tile_set_atlas_source->get_alternative_tile_id(atlas_coords, j);
@@ -368,18 +377,18 @@ void TileAtlasView::_draw_alternatives() {
// Update the y to max value.
Vector2i offset_pos = current_pos;
if (transposed) {
- offset_pos = (current_pos + Vector2(texture_region.size.y, texture_region.size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
- y_increment = MAX(y_increment, texture_region.size.x);
+ offset_pos = (current_pos + Vector2(texture_region_size.y, texture_region_size.x) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
+ y_increment = MAX(y_increment, texture_region_size.x);
} else {
- offset_pos = (current_pos + texture_region.size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
- y_increment = MAX(y_increment, texture_region.size.y);
+ offset_pos = (current_pos + texture_region_size / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(atlas_coords, alternative_id));
+ y_increment = MAX(y_increment, texture_region_size.y);
}
// Draw the tile.
TileMap::draw_tile(alternatives_draw->get_canvas_item(), offset_pos, tile_set, source_id, atlas_coords, alternative_id);
// Increment the x position.
- current_pos.x += transposed ? texture_region.size.y : texture_region.size.x;
+ current_pos.x += transposed ? texture_region_size.y : texture_region_size.x;
}
if (alternatives_count > 1) {
current_pos.y += y_increment;
@@ -442,7 +451,6 @@ void TileAtlasView::set_atlas_source(TileSet *p_tile_set, TileSetAtlasSource *p_
base_tiles_draw->update();
base_tiles_texture_grid->update();
base_tiles_shape_grid->update();
- base_tiles_dark->update();
alternatives_draw->update();
background_left->update();
background_right->update();
@@ -511,10 +519,10 @@ void TileAtlasView::_update_alternative_tiles_rect_cache() {
}
Vector3i TileAtlasView::get_alternative_tile_at_pos(const Vector2 p_pos) const {
- for (Map<Vector2, Map<int, Rect2i>>::Element *E_coords = alternative_tiles_rect_cache.front(); E_coords; E_coords = E_coords->next()) {
- for (Map<int, Rect2i>::Element *E_alternative = E_coords->value().front(); E_alternative; E_alternative = E_alternative->next()) {
- if (E_alternative->value().has_point(p_pos)) {
- return Vector3i(E_coords->key().x, E_coords->key().y, E_alternative->key());
+ for (const KeyValue<Vector2, Map<int, Rect2i>> &E_coords : alternative_tiles_rect_cache) {
+ for (const KeyValue<int, Rect2i> &E_alternative : E_coords.value) {
+ if (E_alternative.value.has_point(p_pos)) {
+ return Vector3i(E_coords.key.x, E_coords.key.y, E_alternative.key);
}
}
}
@@ -533,7 +541,6 @@ void TileAtlasView::update() {
base_tiles_draw->update();
base_tiles_texture_grid->update();
base_tiles_shape_grid->update();
- base_tiles_dark->update();
alternatives_draw->update();
background_left->update();
background_right->update();
@@ -609,7 +616,7 @@ TileAtlasView::TileAtlasView() {
Label *base_tile_label = memnew(Label);
base_tile_label->set_mouse_filter(Control::MOUSE_FILTER_PASS);
base_tile_label->set_text(TTR("Base Tiles"));
- base_tile_label->set_align(Label::ALIGN_CENTER);
+ base_tile_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
left_vbox->add_child(base_tile_label);
base_tiles_root_control = memnew(Control);
@@ -649,17 +656,11 @@ TileAtlasView::TileAtlasView() {
base_tiles_shape_grid->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_shape_grid));
base_tiles_drawing_root->add_child(base_tiles_shape_grid);
- base_tiles_dark = memnew(Control);
- base_tiles_dark->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- base_tiles_dark->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- base_tiles_dark->connect("draw", callable_mp(this, &TileAtlasView::_draw_base_tiles_dark));
- base_tiles_drawing_root->add_child(base_tiles_dark);
-
// Alternative tiles.
Label *alternative_tiles_label = memnew(Label);
alternative_tiles_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tiles_label->set_text(TTR("Alternative Tiles"));
- alternative_tiles_label->set_align(Label::ALIGN_CENTER);
+ alternative_tiles_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
right_vbox->add_child(alternative_tiles_label);
alternative_tiles_root_control = memnew(Control);
diff --git a/editor/plugins/tiles/tile_atlas_view.h b/editor/plugins/tiles/tile_atlas_view.h
index 5b0df366ae..e1ca3eebee 100644
--- a/editor/plugins/tiles/tile_atlas_view.h
+++ b/editor/plugins/tiles/tile_atlas_view.h
@@ -93,9 +93,6 @@ private:
Control *base_tiles_shape_grid;
void _draw_base_tiles_shape_grid();
- Control *base_tiles_dark;
- void _draw_base_tiles_dark();
-
Size2i _compute_base_tiles_control_size();
// Right side.
@@ -124,7 +121,6 @@ public:
// Left side.
void set_texture_grid_visible(bool p_visible) { base_tiles_texture_grid->set_visible(p_visible); };
- void set_dark_visible(bool p_visible) { base_tiles_dark->set_visible(p_visible); };
void set_tile_shape_grid_visible(bool p_visible) { base_tiles_shape_grid->set_visible(p_visible); };
Vector2i get_atlas_tile_coords_at_pos(const Vector2 p_pos) const;
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index d406c2514c..5ded607ab5 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -38,8 +38,16 @@
#include "editor/editor_properties.h"
#include "editor/editor_scale.h"
-void TileDataEditor::_call_tile_set_changed() {
- _tile_set_changed();
+void TileDataEditor::_tile_set_changed_plan_update() {
+ _tile_set_changed_update_needed = true;
+ call_deferred("_tile_set_changed_deferred_update");
+}
+
+void TileDataEditor::_tile_set_changed_deferred_update() {
+ if (_tile_set_changed_update_needed) {
+ _tile_set_changed();
+ _tile_set_changed_update_needed = false;
+ }
}
TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) {
@@ -59,18 +67,20 @@ TileData *TileDataEditor::_get_tile_data(TileMapCell p_cell) {
}
void TileDataEditor::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_tile_set_changed_deferred_update"), &TileDataEditor::_tile_set_changed_deferred_update);
+
ADD_SIGNAL(MethodInfo("needs_redraw"));
}
void TileDataEditor::set_tile_set(Ref<TileSet> p_tile_set) {
if (tile_set.is_valid()) {
- tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed));
+ tile_set->disconnect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
}
tile_set = p_tile_set;
if (tile_set.is_valid()) {
- tile_set->connect("changed", callable_mp(this, &TileDataEditor::_call_tile_set_changed));
+ tile_set->connect("changed", callable_mp(this, &TileDataEditor::_tile_set_changed_plan_update));
}
- _call_tile_set_changed();
+ _tile_set_changed_plan_update();
}
bool DummyObject::_set(const StringName &p_name, const Variant &p_value) {
@@ -115,7 +125,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
const Ref<Texture2D> handle = get_theme_icon(SNAME("EditorPathSharpHandle"), SNAME("EditorIcons"));
const Ref<Texture2D> add_handle = get_theme_icon(SNAME("EditorHandleAdd"), SNAME("EditorIcons"));
+ const Ref<StyleBox> focus_stylebox = get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"));
+
+ // Draw the focus rectangle.
+ if (base_control->has_focus()) {
+ base_control->draw_style_box(focus_stylebox, Rect2(Vector2(), base_control->get_size()));
+ }
+ // Draw tile-related things.
Size2 tile_size = tile_set->get_tile_size();
Transform2D xform;
@@ -124,7 +141,9 @@ void GenericTilePolygonEditor::_base_control_draw() {
base_control->draw_set_transform_matrix(xform);
// Draw the tile shape filled.
- tile_set->draw_tile_shape(base_control, Rect2(-tile_size / 2, tile_size), Color(1.0, 1.0, 1.0, 0.3), true);
+ Transform2D tile_xform;
+ tile_xform.set_scale(tile_size);
+ tile_set->draw_tile_shape(base_control, tile_xform, Color(1.0, 1.0, 1.0, 0.3), true);
// Draw the background.
if (background_texture.is_valid()) {
@@ -199,7 +218,7 @@ void GenericTilePolygonEditor::_base_control_draw() {
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
String text = multiple_polygon_mode ? vformat("%d:%d", tinted_polygon_index, tinted_point_index) : vformat("%d", tinted_point_index);
Size2 text_size = font->get_string_size(text, font_size);
- base_control->draw_string(font, xform.xform(polygons[tinted_polygon_index][tinted_point_index]) - text_size * 0.5, text, HALIGN_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5));
+ base_control->draw_string(font, xform.xform(polygons[tinted_polygon_index][tinted_point_index]) - text_size * 0.5, text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, Color(1.0, 1.0, 1.0, 0.5));
}
if (drag_type == DRAG_TYPE_CREATE_POINT) {
@@ -213,7 +232,7 @@ void GenericTilePolygonEditor::_base_control_draw() {
// Draw the tile shape line.
base_control->draw_set_transform_matrix(xform);
- tile_set->draw_tile_shape(base_control, Rect2(-tile_size / 2, tile_size), grid_color, false);
+ tile_set->draw_tile_shape(base_control, tile_xform, grid_color, false);
base_control->draw_set_transform_matrix(Transform2D());
}
@@ -228,11 +247,16 @@ void GenericTilePolygonEditor::_zoom_changed() {
}
void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
+ UndoRedo *undo_redo = use_undo_redo ? editor_undo_redo : memnew(UndoRedo);
switch (p_item_pressed) {
- case RESET_TO_DEFAULT_TILE:
- undo_redo->create_action(TTR("Edit Polygons"));
+ case RESET_TO_DEFAULT_TILE: {
+ undo_redo->create_action(TTR("Reset Polygons"));
undo_redo->add_do_method(this, "clear_polygons");
- undo_redo->add_do_method(this, "add_polygon", tile_set->get_tile_shape_polygon());
+ Vector<Vector2> polygon = tile_set->get_tile_shape_polygon();
+ for (int i = 0; i < polygon.size(); i++) {
+ polygon.write[i] = polygon[i] * tile_set->get_tile_size();
+ }
+ undo_redo->add_do_method(this, "add_polygon", polygon);
undo_redo->add_do_method(base_control, "update");
undo_redo->add_do_method(this, "emit_signal", "polygons_changed");
undo_redo->add_undo_method(this, "clear_polygons");
@@ -242,9 +266,9 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
undo_redo->add_undo_method(base_control, "update");
undo_redo->add_undo_method(this, "emit_signal", "polygons_changed");
undo_redo->commit_action(true);
- break;
- case CLEAR_TILE:
- undo_redo->create_action(TTR("Edit Polygons"));
+ } break;
+ case CLEAR_TILE: {
+ undo_redo->create_action(TTR("Clear Polygons"));
undo_redo->add_do_method(this, "clear_polygons");
undo_redo->add_do_method(base_control, "update");
undo_redo->add_do_method(this, "emit_signal", "polygons_changed");
@@ -255,10 +279,51 @@ void GenericTilePolygonEditor::_advanced_menu_item_pressed(int p_item_pressed) {
undo_redo->add_undo_method(base_control, "update");
undo_redo->add_undo_method(this, "emit_signal", "polygons_changed");
undo_redo->commit_action(true);
- break;
+ } break;
+ case ROTATE_RIGHT:
+ case ROTATE_LEFT:
+ case FLIP_HORIZONTALLY:
+ case FLIP_VERTICALLY: {
+ undo_redo->create_action(TTR("Rotate Polygons Left"));
+ for (unsigned int i = 0; i < polygons.size(); i++) {
+ Vector<Point2> new_polygon;
+ for (int point_index = 0; point_index < polygons[i].size(); point_index++) {
+ Vector2 point = polygons[i][point_index];
+ switch (p_item_pressed) {
+ case ROTATE_RIGHT: {
+ point = Vector2(-point.y, point.x);
+ } break;
+ case ROTATE_LEFT: {
+ point = Vector2(point.y, -point.x);
+ } break;
+ case FLIP_HORIZONTALLY: {
+ point = Vector2(-point.x, point.y);
+ } break;
+ case FLIP_VERTICALLY: {
+ point = Vector2(point.x, -point.y);
+ } break;
+ default:
+ break;
+ }
+ new_polygon.push_back(point);
+ }
+ undo_redo->add_do_method(this, "set_polygon", i, new_polygon);
+ }
+ undo_redo->add_do_method(base_control, "update");
+ undo_redo->add_do_method(this, "emit_signal", "polygons_changed");
+ for (unsigned int i = 0; i < polygons.size(); i++) {
+ undo_redo->add_undo_method(this, "set_polygon", polygons[i]);
+ }
+ undo_redo->add_undo_method(base_control, "update");
+ undo_redo->add_undo_method(this, "emit_signal", "polygons_changed");
+ undo_redo->commit_action(true);
+ } break;
default:
break;
}
+ if (!use_undo_redo) {
+ memdelete(undo_redo);
+ }
}
void GenericTilePolygonEditor::_grab_polygon_point(Vector2 p_pos, const Transform2D &p_polygon_xform, int &r_polygon_index, int &r_point_index) {
@@ -306,6 +371,9 @@ void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_cur
ERR_FAIL_COND(!tile_set.is_valid());
Vector<Point2> polygon = tile_set->get_tile_shape_polygon();
+ for (int i = 0; i < polygon.size(); i++) {
+ polygon.write[i] = polygon[i] * tile_set->get_tile_size();
+ }
Point2 snapped_point = r_point;
// Snap to polygon vertices.
@@ -340,6 +408,7 @@ void GenericTilePolygonEditor::_snap_to_half_pixel(Point2 &r_point) {
}
void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event) {
+ UndoRedo *undo_redo = use_undo_redo ? editor_undo_redo : memnew(UndoRedo);
real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
hovered_polygon_index = -1;
@@ -380,15 +449,15 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_UP && mb->is_ctrl_pressed()) {
+ if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_ctrl_pressed()) {
editor_zoom_widget->set_zoom_by_increments(1);
_zoom_changed();
accept_event();
- } else if (mb->get_button_index() == MOUSE_BUTTON_WHEEL_DOWN && mb->is_ctrl_pressed()) {
+ } else if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_ctrl_pressed()) {
editor_zoom_widget->set_zoom_by_increments(-1);
_zoom_changed();
accept_event();
- } else if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ } else if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (tools_button_group->get_pressed_button() != button_create) {
in_creation_polygon.clear();
@@ -447,7 +516,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
_grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point);
if (closest_polygon >= 0) {
PackedVector2Array old_polygon = polygons[closest_polygon];
- polygons[closest_polygon].remove(closest_point);
+ polygons[closest_polygon].remove_at(closest_point);
undo_redo->create_action(TTR("Edit Polygons"));
if (polygons[closest_polygon].size() < 3) {
remove_polygon(closest_polygon);
@@ -485,7 +554,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
drag_point_index = -1;
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT) {
if (mb->is_pressed()) {
if (tools_button_group->get_pressed_button() == button_edit) {
// Remove point or pan.
@@ -494,7 +563,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
_grab_polygon_point(mb->get_position(), xform, closest_polygon, closest_point);
if (closest_polygon >= 0) {
PackedVector2Array old_polygon = polygons[closest_polygon];
- polygons[closest_polygon].remove(closest_point);
+ polygons[closest_polygon].remove_at(closest_point);
undo_redo->create_action(TTR("Edit Polygons"));
if (polygons[closest_polygon].size() < 3) {
remove_polygon(closest_polygon);
@@ -519,7 +588,7 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
} else {
drag_type = DRAG_TYPE_NONE;
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_MIDDLE) {
+ } else if (mb->get_button_index() == MouseButton::MIDDLE) {
if (mb->is_pressed()) {
drag_type = DRAG_TYPE_PAN;
drag_last_pos = mb->get_position();
@@ -530,17 +599,47 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
}
base_control->update();
+
+ if (!use_undo_redo) {
+ memdelete(undo_redo);
+ }
+}
+
+void GenericTilePolygonEditor::set_use_undo_redo(bool p_use_undo_redo) {
+ use_undo_redo = p_use_undo_redo;
}
void GenericTilePolygonEditor::set_tile_set(Ref<TileSet> p_tile_set) {
- if (tile_set != p_tile_set) {
- // Set the default tile shape
- clear_polygons();
- if (p_tile_set.is_valid()) {
- add_polygon(p_tile_set->get_tile_shape_polygon());
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ if (tile_set == p_tile_set) {
+ return;
+ }
+
+ // Set the default tile shape
+ clear_polygons();
+ if (p_tile_set.is_valid()) {
+ Vector<Vector2> polygon = p_tile_set->get_tile_shape_polygon();
+ for (int i = 0; i < polygon.size(); i++) {
+ polygon.write[i] = polygon[i] * p_tile_set->get_tile_size();
}
+ add_polygon(polygon);
}
+
tile_set = p_tile_set;
+
+ // Set the default zoom value.
+ int default_control_y_size = 200 * EDSCALE;
+ Vector2 zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size();
+ while (zoomed_tile.y < default_control_y_size) {
+ editor_zoom_widget->set_zoom_by_increments(6, false);
+ zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size();
+ }
+ while (zoomed_tile.y > default_control_y_size) {
+ editor_zoom_widget->set_zoom_by_increments(-6, false);
+ zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size();
+ }
+ editor_zoom_widget->set_zoom_by_increments(-6, false);
+ _zoom_changed();
}
void GenericTilePolygonEditor::set_background(Ref<Texture2D> p_texture, Rect2 p_region, Vector2 p_offset, bool p_flip_h, bool p_flip_v, bool p_transpose, Color p_modulate) {
@@ -577,7 +676,7 @@ int GenericTilePolygonEditor::add_polygon(Vector<Point2> p_polygon, int p_index)
void GenericTilePolygonEditor::remove_polygon(int p_index) {
ERR_FAIL_INDEX(p_index, (int)polygons.size());
- polygons.remove(p_index);
+ polygons.remove_at(p_index);
if (polygons.size() == 0) {
button_create->set_pressed(true);
@@ -620,7 +719,13 @@ void GenericTilePolygonEditor::_notification(int p_what) {
button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CurveDelete"), SNAME("EditorIcons")));
button_center_view->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("CenterView"), SNAME("EditorIcons")));
button_pixel_snap->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("Snap"), SNAME("EditorIcons")));
- button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenu"), SNAME("EditorIcons")));
+ button_advanced_menu->set_icon(EditorNode::get_singleton()->get_gui_base()->get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons")));
+
+ PopupMenu *p = button_advanced_menu->get_popup();
+ p->set_item_icon(p->get_item_index(ROTATE_RIGHT), get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")));
+ p->set_item_icon(p->get_item_index(ROTATE_LEFT), get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons")));
+ p->set_item_icon(p->get_item_index(FLIP_HORIZONTALLY), get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons")));
+ p->set_item_icon(p->get_item_index(FLIP_VERTICALLY), get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons")));
break;
}
}
@@ -647,18 +752,21 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
button_create->set_toggle_mode(true);
button_create->set_button_group(tools_button_group);
button_create->set_pressed(true);
+ button_create->set_tooltip(TTR("Add polygon tool"));
toolbar->add_child(button_create);
button_edit = memnew(Button);
button_edit->set_flat(true);
button_edit->set_toggle_mode(true);
button_edit->set_button_group(tools_button_group);
+ button_edit->set_tooltip(TTR("Edit points tool"));
toolbar->add_child(button_edit);
button_delete = memnew(Button);
button_delete->set_flat(true);
button_delete->set_toggle_mode(true);
button_delete->set_button_group(tools_button_group);
+ button_delete->set_tooltip(TTR("Delete points tool"));
toolbar->add_child(button_delete);
button_advanced_menu = memnew(MenuButton);
@@ -666,7 +774,13 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
button_advanced_menu->set_toggle_mode(true);
button_advanced_menu->get_popup()->add_item(TTR("Reset to default tile shape"), RESET_TO_DEFAULT_TILE);
button_advanced_menu->get_popup()->add_item(TTR("Clear"), CLEAR_TILE);
+ button_advanced_menu->get_popup()->add_separator();
+ button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateRight"), SNAME("EditorIcons")), TTR("Rotate Right"), ROTATE_RIGHT);
+ button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("RotateLeft"), SNAME("EditorIcons")), TTR("Rotate Left"), ROTATE_LEFT);
+ button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorX"), SNAME("EditorIcons")), TTR("Flip Horizontally"), FLIP_HORIZONTALLY);
+ button_advanced_menu->get_popup()->add_icon_item(get_theme_icon(SNAME("MirrorY"), SNAME("EditorIcons")), TTR("Flip Vertically"), FLIP_VERTICALLY);
button_advanced_menu->get_popup()->connect("id_pressed", callable_mp(this, &GenericTilePolygonEditor::_advanced_menu_item_pressed));
+ button_advanced_menu->set_focus_mode(FOCUS_ALL);
toolbar->add_child(button_advanced_menu);
toolbar->add_child(memnew(VSeparator));
@@ -675,6 +789,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
button_pixel_snap->set_flat(true);
button_pixel_snap->set_toggle_mode(true);
button_pixel_snap->set_pressed(true);
+ button_pixel_snap->set_tooltip(TTR("Snap to half-pixel"));
toolbar->add_child(button_pixel_snap);
Control *root = memnew(Control);
@@ -694,6 +809,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() {
base_control->connect("draw", callable_mp(this, &GenericTilePolygonEditor::_base_control_draw));
base_control->connect("gui_input", callable_mp(this, &GenericTilePolygonEditor::_base_control_gui_input));
base_control->set_clip_contents(true);
+ base_control->set_focus_mode(Control::FOCUS_CLICK);
root->add_child(base_control);
editor_zoom_widget = memnew(EditorZoomWidget);
@@ -743,10 +859,10 @@ Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_s
}
void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
- for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) {
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E->key().alternative_tile, property), E->get());
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E->key().alternative_tile, property), p_new_value);
+ for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E.key.alternative_tile, property), E.value);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/%s", coords.x, coords.y, E.key.alternative_tile, property), p_new_value);
}
}
@@ -813,7 +929,7 @@ void TileDataDefaultEditor::forward_painting_atlas_gui_input(TileAtlasView *p_ti
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (picker_button->is_pressed()) {
Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position());
@@ -904,7 +1020,7 @@ void TileDataDefaultEditor::forward_painting_alternatives_gui_input(TileAtlasVie
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (picker_button->is_pressed()) {
Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position());
@@ -964,6 +1080,7 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2
p_canvas_item->draw_rect(rect, value);
} else {
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
+ int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
String text;
switch (value.get_type()) {
case Variant::INT:
@@ -995,8 +1112,8 @@ void TileDataDefaultEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2
}
}
- Vector2 string_size = font->get_string_size(text);
- p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1));
+ Vector2 string_size = font->get_string_size(text, font_size);
+ p_canvas_item->draw_string(font, p_transform.get_origin() + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color, 1, Color(0, 0, 0, 1));
}
}
@@ -1058,7 +1175,7 @@ TileDataDefaultEditor::TileDataDefaultEditor() {
picker_button = memnew(Button);
picker_button->set_flat(true);
picker_button->set_toggle_mode(true);
- picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P));
+ picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", Key::P));
toolbar->add_child(picker_button);
}
@@ -1072,14 +1189,15 @@ void TileDataTextureOffsetEditor::draw_over_tile(CanvasItem *p_canvas_item, Tran
ERR_FAIL_COND(!tile_data);
Vector2i tile_set_tile_size = tile_set->get_tile_size();
- Rect2i rect = Rect2i(-tile_set_tile_size / 2, tile_set_tile_size);
Color color = Color(1.0, 0.0, 0.0);
if (p_selected) {
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
color = selection_color;
}
- tile_set->draw_tile_shape(p_canvas_item, p_transform.xform(rect), color);
+ Transform2D tile_xform;
+ tile_xform.set_scale(tile_set_tile_size);
+ tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color);
}
void TileDataPositionEditor::draw_over_tile(CanvasItem *p_canvas_item, Transform2D p_transform, TileMapCell p_cell, bool p_selected) {
@@ -1177,10 +1295,10 @@ Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_
}
void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
- for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) {
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, occlusion_layer), E->get());
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, occlusion_layer), p_new_value);
+ for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, occlusion_layer), E.value);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/occlusion_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, occlusion_layer), p_new_value);
}
}
@@ -1262,17 +1380,21 @@ void TileDataCollisionEditor::_polygons_changed() {
}
Variant TileDataCollisionEditor::_get_painted_value() {
+ Dictionary dict;
+ dict["linear_velocity"] = dummy_object->get("linear_velocity");
+ dict["angular_velocity"] = dummy_object->get("angular_velocity");
Array array;
for (int i = 0; i < polygon_editor->get_polygon_count(); i++) {
ERR_FAIL_COND_V(polygon_editor->get_polygon(i).size() < 3, Variant());
- Dictionary dict;
- dict["points"] = polygon_editor->get_polygon(i);
- dict["one_way"] = dummy_object->get(vformat("polygon_%d_one_way", i));
- dict["one_way_margin"] = dummy_object->get(vformat("polygon_%d_one_way_margin", i));
- array.push_back(dict);
+ Dictionary polygon_dict;
+ polygon_dict["points"] = polygon_editor->get_polygon(i);
+ polygon_dict["one_way"] = dummy_object->get(vformat("polygon_%d_one_way", i));
+ polygon_dict["one_way_margin"] = dummy_object->get(vformat("polygon_%d_one_way_margin", i));
+ array.push_back(polygon_dict);
}
+ dict["polygons"] = array;
- return array;
+ return dict;
}
void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) {
@@ -1288,12 +1410,14 @@ void TileDataCollisionEditor::_set_painted_value(TileSetAtlasSource *p_tile_set_
}
_polygons_changed();
+ dummy_object->set("linear_velocity", tile_data->get_constant_linear_velocity(physics_layer));
+ dummy_object->set("angular_velocity", tile_data->get_constant_angular_velocity(physics_layer));
for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) {
dummy_object->set(vformat("polygon_%d_one_way", i), tile_data->is_collision_polygon_one_way(physics_layer, i));
dummy_object->set(vformat("polygon_%d_one_way_margin", i), tile_data->get_collision_polygon_one_way_margin(physics_layer, i));
}
- for (Map<StringName, EditorProperty *>::Element *E = property_editors.front(); E; E = E->next()) {
- E->get()->update_property();
+ for (const KeyValue<StringName, EditorProperty *> &E : property_editors) {
+ E.value->update_property();
}
polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate());
@@ -1303,13 +1427,16 @@ void TileDataCollisionEditor::_set_value(TileSetAtlasSource *p_tile_set_atlas_so
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
ERR_FAIL_COND(!tile_data);
- Array array = p_value;
+ Dictionary dict = p_value;
+ tile_data->set_constant_linear_velocity(physics_layer, dict["linear_velocity"]);
+ tile_data->set_constant_angular_velocity(physics_layer, dict["angular_velocity"]);
+ Array array = dict["polygons"];
tile_data->set_collision_polygons_count(physics_layer, array.size());
for (int i = 0; i < array.size(); i++) {
- Dictionary dict = array[i];
- tile_data->set_collision_polygon_points(physics_layer, i, dict["points"]);
- tile_data->set_collision_polygon_one_way(physics_layer, i, dict["one_way"]);
- tile_data->set_collision_polygon_one_way_margin(physics_layer, i, dict["one_way_margin"]);
+ Dictionary polygon_dict = array[i];
+ tile_data->set_collision_polygon_points(physics_layer, i, polygon_dict["points"]);
+ tile_data->set_collision_polygon_one_way(physics_layer, i, polygon_dict["one_way"]);
+ tile_data->set_collision_polygon_one_way_margin(physics_layer, i, polygon_dict["one_way_margin"]);
}
polygon_editor->set_background(p_tile_set_atlas_source->get_texture(), p_tile_set_atlas_source->get_tile_texture_region(p_coords), p_tile_set_atlas_source->get_tile_effective_texture_offset(p_coords, p_alternative_tile), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate());
@@ -1319,37 +1446,41 @@ Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(p_coords, p_alternative_tile));
ERR_FAIL_COND_V(!tile_data, Variant());
+ Dictionary dict;
+ dict["linear_velocity"] = tile_data->get_constant_linear_velocity(physics_layer);
+ dict["angular_velocity"] = tile_data->get_constant_angular_velocity(physics_layer);
Array array;
for (int i = 0; i < tile_data->get_collision_polygons_count(physics_layer); i++) {
- Dictionary dict;
- dict["points"] = tile_data->get_collision_polygon_points(physics_layer, i);
- dict["one_way"] = tile_data->is_collision_polygon_one_way(physics_layer, i);
- dict["one_way_margin"] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i);
- array.push_back(dict);
+ Dictionary polygon_dict;
+ polygon_dict["points"] = tile_data->get_collision_polygon_points(physics_layer, i);
+ polygon_dict["one_way"] = tile_data->is_collision_polygon_one_way(physics_layer, i);
+ polygon_dict["one_way_margin"] = tile_data->get_collision_polygon_one_way_margin(physics_layer, i);
+ array.push_back(polygon_dict);
}
- return array;
+ dict["polygons"] = array;
+ return dict;
}
void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
Array new_array = p_new_value;
- for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) {
- Array old_array = E->get();
+ for (KeyValue<TileMapCell, Variant> &E : p_previous_values) {
+ Array old_array = E.value;
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E->key().alternative_tile, physics_layer), old_array.size());
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), old_array.size());
for (int i = 0; i < old_array.size(); i++) {
Dictionary dict = old_array[i];
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["points"]);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way"]);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way_margin"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["points"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way"]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way_margin"]);
}
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E->key().alternative_tile, physics_layer), new_array.size());
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygons_count", coords.x, coords.y, E.key.alternative_tile, physics_layer), new_array.size());
for (int i = 0; i < new_array.size(); i++) {
Dictionary dict = new_array[i];
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["points"]);
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way"]);
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E->key().alternative_tile, physics_layer, i), dict["one_way_margin"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/points", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["points"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/physics_layer_%d/polygon_%d/one_way_margin", coords.x, coords.y, E.key.alternative_tile, physics_layer, i), dict["one_way_margin"]);
}
}
}
@@ -1375,6 +1506,27 @@ TileDataCollisionEditor::TileDataCollisionEditor() {
polygon_editor->connect("polygons_changed", callable_mp(this, &TileDataCollisionEditor::_polygons_changed));
add_child(polygon_editor);
+ dummy_object->add_dummy_property("linear_velocity");
+ dummy_object->set("linear_velocity", Vector2());
+ dummy_object->add_dummy_property("angular_velocity");
+ dummy_object->set("angular_velocity", 0.0);
+
+ EditorProperty *linear_velocity_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::VECTOR2, "linear_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
+ linear_velocity_editor->set_object_and_property(dummy_object, "linear_velocity");
+ linear_velocity_editor->set_label("linear_velocity");
+ linear_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
+ linear_velocity_editor->update_property();
+ add_child(linear_velocity_editor);
+ property_editors["linear_velocity"] = linear_velocity_editor;
+
+ EditorProperty *angular_velocity_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, Variant::FLOAT, "angular_velocity", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
+ angular_velocity_editor->set_object_and_property(dummy_object, "angular_velocity");
+ angular_velocity_editor->set_label("angular_velocity");
+ angular_velocity_editor->connect("property_changed", callable_mp(this, &TileDataCollisionEditor::_property_value_changed).unbind(1));
+ angular_velocity_editor->update_property();
+ add_child(angular_velocity_editor);
+ property_editors["angular_velocity"] = angular_velocity_editor;
+
_polygons_changed();
}
@@ -1465,12 +1617,13 @@ void TileDataTerrainsEditor::_tile_set_changed() {
ERR_FAIL_COND(!tile_set.is_valid());
// Fix if wrong values are selected.
- if (int(dummy_object->get("terrain_set")) > tile_set->get_terrain_sets_count()) {
+ int terrain_set = int(dummy_object->get("terrain_set"));
+ if (terrain_set >= tile_set->get_terrain_sets_count()) {
+ terrain_set = -1;
dummy_object->set("terrain_set", -1);
}
- int terrain_set = int(dummy_object->get("terrain"));
if (terrain_set >= 0) {
- if (int(dummy_object->get("terrain")) > tile_set->get_terrains_count(terrain_set)) {
+ if (int(dummy_object->get("terrain")) >= tile_set->get_terrains_count(terrain_set)) {
dummy_object->set("terrain", -1);
}
}
@@ -1491,7 +1644,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(hovered_coords, 0));
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(hovered_coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, 0);
if (terrain_set >= 0 && terrain_set == int(dummy_object->get("terrain_set"))) {
// Draw hovered bit.
@@ -1513,15 +1666,17 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
}
} else {
// Draw hovered tile.
- Vector2i tile_size = tile_set->get_tile_size();
- Rect2i rect = p_transform.xform(Rect2i(position - tile_size / 2, tile_size));
- tile_set->draw_tile_shape(p_canvas_item, rect, Color(1.0, 1.0, 1.0, 0.5), true);
+ Transform2D tile_xform;
+ tile_xform.set_origin(position);
+ tile_xform.set_scale(tile_set->get_tile_size());
+ tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
}
}
}
// Dim terrains with wrong terrain set.
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
+ int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i coords = p_tile_set_atlas_source->get_tile_id(i);
if (coords != hovered_coords) {
@@ -1535,7 +1690,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
// Text
p_canvas_item->draw_set_transform_matrix(Transform2D());
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
Color color = Color(1, 1, 1);
String text;
@@ -1544,8 +1699,8 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
} else {
text = "-";
}
- Vector2 string_size = font->get_string_size(text);
- p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1));
+ Vector2 string_size = font->get_string_size(text, font_size);
+ p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color, 1, Color(0, 0, 0, 1));
}
}
}
@@ -1627,7 +1782,7 @@ void TileDataTerrainsEditor::forward_draw_over_atlas(TileAtlasView *p_tile_atlas
Vector2i coords = E->get().get_atlas_coords();
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
@@ -1663,7 +1818,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(hovered_coords, hovered_alternative));
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(hovered_coords, hovered_alternative);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, hovered_alternative);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(hovered_coords, hovered_alternative);
if (terrain_set == int(dummy_object->get("terrain_set"))) {
// Draw hovered bit.
@@ -1685,15 +1840,17 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
}
} else {
// Draw hovered tile.
- Vector2i tile_size = tile_set->get_tile_size();
- Rect2i rect = p_transform.xform(Rect2i(position - tile_size / 2, tile_size));
- tile_set->draw_tile_shape(p_canvas_item, rect, Color(1.0, 1.0, 1.0, 0.5), true);
+ Transform2D tile_xform;
+ tile_xform.set_origin(position);
+ tile_xform.set_scale(tile_set->get_tile_size());
+ tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
}
}
}
// Dim terrains with wrong terrain set.
Ref<Font> font = TileSetEditor::get_singleton()->get_theme_font(SNAME("bold"), SNAME("EditorFonts"));
+ int font_size = TileSetEditor::get_singleton()->get_theme_font_size(SNAME("bold_size"), SNAME("EditorFonts"));
for (int i = 0; i < p_tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i coords = p_tile_set_atlas_source->get_tile_id(i);
for (int j = 1; j < p_tile_set_atlas_source->get_alternative_tiles_count(coords); j++) {
@@ -1709,7 +1866,7 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
// Text
p_canvas_item->draw_set_transform_matrix(Transform2D());
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
Color color = Color(1, 1, 1);
String text;
@@ -1718,8 +1875,8 @@ void TileDataTerrainsEditor::forward_draw_over_alternatives(TileAtlasView *p_til
} else {
text = "-";
}
- Vector2 string_size = font->get_string_size(text);
- p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HALIGN_CENTER, string_size.x, -1, color, 1, Color(0, 0, 0, 1));
+ Vector2 string_size = font->get_string_size(text, font_size);
+ p_canvas_item->draw_string(font, p_transform.xform(position) + Vector2i(-string_size.x / 2, string_size.y / 2), text, HORIZONTAL_ALIGNMENT_CENTER, string_size.x, font_size, color, 1, Color(0, 0, 0, 1));
}
}
}
@@ -1790,7 +1947,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Set the terrains bits.
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_data->is_valid_peering_bit_terrain(bit)) {
@@ -1809,7 +1966,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (picker_button->is_pressed()) {
Vector2i coords = p_tile_atlas_view->get_atlas_tile_coords_at_pos(mb->get_position());
@@ -1818,7 +1975,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, 0));
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
dummy_object->set("terrain_set", terrain_set);
dummy_object->set("terrain", -1);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
@@ -1916,7 +2073,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
// Set the terrain bit.
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
@@ -1970,16 +2127,16 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
drag_type = DRAG_TYPE_NONE;
} else if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) {
undo_redo->create_action(TTR("Painting Terrain Set"));
- for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) {
- Dictionary dict = E->get();
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), drag_painted_value);
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), dict["terrain_set"]);
+ for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
+ Dictionary dict = E.value;
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
}
@@ -1990,17 +2147,17 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
int terrain_set = int(painted["terrain_set"]);
int terrain = int(painted["terrain"]);
undo_redo->create_action(TTR("Painting Terrain"));
- for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) {
- Dictionary dict = E->get();
- Vector2i coords = E->key().get_atlas_coords();
+ for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
+ Dictionary dict = E.value;
+ Vector2i coords = E.key.get_atlas_coords();
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), terrain);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
}
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
}
@@ -2049,7 +2206,7 @@ void TileDataTerrainsEditor::forward_painting_atlas_gui_input(TileAtlasView *p_t
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
Rect2i texture_region = p_tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
Vector<Vector2> polygon = tile_set->get_terrain_bit_polygon(terrain_set, bit);
for (int j = 0; j < polygon.size(); j++) {
@@ -2132,7 +2289,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Set the terrains bits.
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(j);
if (tile_data->is_valid_peering_bit_terrain(bit)) {
@@ -2150,7 +2307,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
if (picker_button->is_pressed()) {
Vector3i tile = p_tile_atlas_view->get_alternative_tile_at_pos(mb->get_position());
@@ -2161,7 +2318,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
TileData *tile_data = Object::cast_to<TileData>(p_tile_set_atlas_source->get_tile_data(coords, alternative_tile));
int terrain_set = tile_data->get_terrain_set();
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
dummy_object->set("terrain_set", terrain_set);
dummy_object->set("terrain", -1);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
@@ -2236,7 +2393,7 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
// Set the terrain bit.
Rect2i texture_region = p_tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
+ Vector2i position = texture_region.get_center() + p_tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative_tile);
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
@@ -2253,14 +2410,14 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
} else {
if (drag_type == DRAG_TYPE_PAINT_TERRAIN_SET) {
undo_redo->create_action(TTR("Painting Tiles Property"));
- for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) {
- Dictionary dict = E->get();
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), dict["terrain_set"]);
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E->key().alternative_tile), drag_painted_value);
+ for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
+ Dictionary dict = E.value;
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), dict["terrain_set"]);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrain_set", coords.x, coords.y, E.key.alternative_tile), drag_painted_value);
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
undo_redo->commit_action(false);
@@ -2270,17 +2427,17 @@ void TileDataTerrainsEditor::forward_painting_alternatives_gui_input(TileAtlasVi
int terrain_set = int(painted["terrain_set"]);
int terrain = int(painted["terrain"]);
undo_redo->create_action(TTR("Painting Terrain"));
- for (Map<TileMapCell, Variant>::Element *E = drag_modified.front(); E; E = E->next()) {
- Dictionary dict = E->get();
- Vector2i coords = E->key().get_atlas_coords();
+ for (KeyValue<TileMapCell, Variant> &E : drag_modified) {
+ Dictionary dict = E.value;
+ Vector2i coords = E.key.get_atlas_coords();
Array array = dict["terrain_peering_bits"];
for (int i = 0; i < array.size(); i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), terrain);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), terrain);
}
if (tile_set->is_valid_peering_bit_terrain(dict["terrain_set"], bit)) {
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E->key().alternative_tile), array[i]);
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]), coords.x, coords.y, E.key.alternative_tile), array[i]);
}
}
}
@@ -2321,7 +2478,7 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() {
picker_button = memnew(Button);
picker_button->set_flat(true);
picker_button->set_toggle_mode(true);
- picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P));
+ picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", Key::P));
toolbar->add_child(picker_button);
// Setup
@@ -2392,10 +2549,10 @@ Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atla
}
void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, Map<TileMapCell, Variant> p_previous_values, Variant p_new_value) {
- for (Map<TileMapCell, Variant>::Element *E = p_previous_values.front(); E; E = E->next()) {
- Vector2i coords = E->key().get_atlas_coords();
- undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, navigation_layer), E->get());
- undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E->key().alternative_tile, navigation_layer), p_new_value);
+ for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) {
+ Vector2i coords = E.key.get_atlas_coords();
+ undo_redo->add_undo_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, navigation_layer), E.value);
+ undo_redo->add_do_property(p_tile_set_atlas_source, vformat("%d:%d/%d/navigation_layer_%d/polygon", coords.x, coords.y, E.key.alternative_tile, navigation_layer), p_new_value);
}
}
diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h
index 99998dc779..3fc5e738bb 100644
--- a/editor/plugins/tiles/tile_data_editors.h
+++ b/editor/plugins/tiles/tile_data_editors.h
@@ -45,7 +45,9 @@ class TileDataEditor : public VBoxContainer {
GDCLASS(TileDataEditor, VBoxContainer);
private:
- void _call_tile_set_changed();
+ bool _tile_set_changed_update_needed = false;
+ void _tile_set_changed_plan_update();
+ void _tile_set_changed_deferred_update();
protected:
Ref<TileSet> tile_set;
@@ -92,7 +94,8 @@ private:
LocalVector<Vector<Point2>> polygons;
bool multiple_polygon_mode = false;
- UndoRedo *undo_redo = EditorNode::get_undo_redo();
+ bool use_undo_redo = true;
+ UndoRedo *editor_undo_redo = EditorNode::get_undo_redo();
// UI
int hovered_polygon_index = -1;
@@ -106,7 +109,7 @@ private:
DRAG_TYPE_CREATE_POINT,
DRAG_TYPE_PAN,
};
- DragType drag_type;
+ DragType drag_type = DRAG_TYPE_NONE;
int drag_polygon_index;
int drag_point_index;
Vector2 drag_last_pos;
@@ -141,6 +144,10 @@ private:
enum AdvancedMenuOption {
RESET_TO_DEFAULT_TILE,
CLEAR_TILE,
+ ROTATE_RIGHT,
+ ROTATE_LEFT,
+ FLIP_HORIZONTALLY,
+ FLIP_VERTICALLY,
};
void _base_control_draw();
@@ -159,6 +166,8 @@ protected:
static void _bind_methods();
public:
+ void set_use_undo_redo(bool p_use_undo_redo);
+
void set_tile_set(Ref<TileSet> p_tile_set);
void set_background(Ref<Texture2D> p_texture, Rect2 p_region = Rect2(), Vector2 p_offset = Vector2(), bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, Color p_modulate = Color(1.0, 1.0, 1.0, 0.0));
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 77084f551a..b4d6f3659d 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -36,6 +36,7 @@
#include "editor/editor_scale.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
+#include "scene/2d/camera_2d.h"
#include "scene/gui/center_container.h"
#include "scene/gui/split_container.h"
@@ -43,31 +44,11 @@
#include "core/math/geometry_2d.h"
#include "core/os/keyboard.h"
-void TileMapEditorTilesPlugin::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED:
- select_tool_button->set_icon(get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons")));
- paint_tool_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
- line_tool_button->set_icon(get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")));
- rect_tool_button->set_icon(get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons")));
- bucket_tool_button->set_icon(get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons")));
-
- picker_button->set_icon(get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
- erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
-
- missing_atlas_texture_icon = get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons"));
- break;
- case NOTIFICATION_VISIBILITY_CHANGED:
- _stop_dragging();
- break;
- }
-}
-
void TileMapEditorTilesPlugin::tile_set_changed() {
_update_fix_selected_and_hovered();
_update_tile_set_sources_list();
- _update_bottom_panel();
+ _update_source_display();
+ _update_patterns_list();
}
void TileMapEditorTilesPlugin::_on_random_tile_checkbox_toggled(bool p_pressed) {
@@ -118,15 +99,26 @@ void TileMapEditorTilesPlugin::_update_toolbar() {
picker_button->show();
erase_button->show();
tools_settings_vsep_2->show();
- bucket_continuous_checkbox->show();
+ bucket_contiguous_checkbox->show();
random_tile_checkbox->show();
scatter_label->show();
scatter_spinbox->show();
}
}
-Control *TileMapEditorTilesPlugin::get_toolbar() const {
- return toolbar;
+Vector<TileMapEditorPlugin::TabData> TileMapEditorTilesPlugin::get_tabs() const {
+ Vector<TileMapEditorPlugin::TabData> tabs;
+ tabs.push_back({ toolbar, tiles_bottom_panel });
+ tabs.push_back({ toolbar, patterns_bottom_panel });
+ return tabs;
+}
+
+void TileMapEditorTilesPlugin::_tab_changed() {
+ if (tiles_bottom_panel->is_visible_in_tree()) {
+ _update_selection_pattern_from_tileset_tiles_selection();
+ } else if (patterns_bottom_panel->is_visible_in_tree()) {
+ _update_selection_pattern_from_tileset_pattern_selection();
+ }
}
void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
@@ -152,22 +144,31 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
Ref<Texture2D> texture;
String item_text;
+ // Common to all type of sources.
+ if (!source->get_name().is_empty()) {
+ item_text = vformat(TTR("%s (id:%d)"), source->get_name(), source_id);
+ }
+
// Atlas source.
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
texture = atlas_source->get_texture();
- if (texture.is_valid()) {
- item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id);
- } else {
- item_text = vformat("No Texture Atlas Source (ID: %d)", source_id);
+ if (item_text.is_empty()) {
+ if (texture.is_valid()) {
+ item_text = vformat("%s (ID: %d)", texture->get_path().get_file(), source_id);
+ } else {
+ item_text = vformat("No Texture Atlas Source (ID: %d)", source_id);
+ }
}
}
// Scene collection source.
TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
if (scene_collection_source) {
- texture = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
- item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
+ texture = tiles_bottom_panel->get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
+ if (item_text.is_empty()) {
+ item_text = vformat(TTR("Scene Collection Source (ID: %d)"), source_id);
+ }
}
// Use default if not valid.
@@ -193,10 +194,10 @@ void TileMapEditorTilesPlugin::_update_tile_set_sources_list() {
}
// Synchronize
- TilesEditor::get_singleton()->set_sources_lists_current(sources_list->get_current());
+ TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
}
-void TileMapEditorTilesPlugin::_update_bottom_panel() {
+void TileMapEditorTilesPlugin::_update_source_display() {
// Update the atlas display.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -243,6 +244,81 @@ void TileMapEditorTilesPlugin::_update_bottom_panel() {
}
}
+void TileMapEditorTilesPlugin::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ if (ED_IS_SHORTCUT("tiles_editor/paste", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
+ select_last_pattern = true;
+ int new_pattern_index = tile_set->get_patterns_count();
+ undo_redo->create_action(TTR("Add TileSet pattern"));
+ undo_redo->add_do_method(*tile_set, "add_pattern", tile_map_clipboard, new_pattern_index);
+ undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index);
+ undo_redo->commit_action();
+ patterns_item_list->accept_event();
+ }
+
+ if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
+ Vector<int> selected = patterns_item_list->get_selected_items();
+ undo_redo->create_action(TTR("Remove TileSet patterns"));
+ for (int i = 0; i < selected.size(); i++) {
+ int pattern_index = selected[i];
+ undo_redo->add_do_method(*tile_set, "remove_pattern", pattern_index);
+ undo_redo->add_undo_method(*tile_set, "add_pattern", tile_set->get_pattern(pattern_index), pattern_index);
+ }
+ undo_redo->commit_action();
+ patterns_item_list->accept_event();
+ }
+}
+
+void TileMapEditorTilesPlugin::_pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture) {
+ // TODO optimize ?
+ for (int i = 0; i < patterns_item_list->get_item_count(); i++) {
+ if (patterns_item_list->get_item_metadata(i) == p_pattern) {
+ patterns_item_list->set_item_icon(i, p_texture);
+ break;
+ }
+ }
+}
+
+void TileMapEditorTilesPlugin::_update_patterns_list() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ // Recreate the items.
+ patterns_item_list->clear();
+ for (int i = 0; i < tile_set->get_patterns_count(); i++) {
+ int id = patterns_item_list->add_item("");
+ patterns_item_list->set_item_metadata(id, tile_set->get_pattern(i));
+ TilesEditorPlugin::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileMapEditorTilesPlugin::_pattern_preview_done));
+ }
+
+ // Update the label visibility.
+ patterns_help_label->set_visible(patterns_item_list->get_item_count() == 0);
+
+ // Added a new pattern, thus select the last one.
+ if (select_last_pattern) {
+ patterns_item_list->select(tile_set->get_patterns_count() - 1);
+ patterns_item_list->grab_focus();
+ _update_selection_pattern_from_tileset_pattern_selection();
+ }
+ select_last_pattern = false;
+}
+
void TileMapEditorTilesPlugin::_update_atlas_view() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
@@ -260,7 +336,7 @@ void TileMapEditorTilesPlugin::_update_atlas_view() {
ERR_FAIL_COND(!atlas_source);
tile_atlas_view->set_atlas_source(*tile_map->get_tileset(), atlas_source, source_id);
- TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+ TilesEditorPlugin::get_singleton()->synchronize_atlas_view(tile_atlas_view);
tile_atlas_control->update();
}
@@ -295,7 +371,7 @@ void TileMapEditorTilesPlugin::_update_scenes_collection_view() {
Variant udata = i;
EditorResourcePreview::get_singleton()->queue_edited_resource_preview(scene, this, "_scene_thumbnail_done", udata);
} else {
- item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons")));
+ item_index = scene_tiles_list->add_item(TTR("Tile with Invalid Scene"), tiles_bottom_panel->get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons")));
}
scene_tiles_list->set_item_metadata(item_index, scene_id);
@@ -339,7 +415,7 @@ void TileMapEditorTilesPlugin::_scenes_list_multi_selected(int p_index, bool p_s
TileMapCell selected = TileMapCell(source_id, Vector2i(), scene_id);
// Clear the selection if shift is not pressed.
- if (!Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (!Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
tile_set_selection.clear();
}
@@ -351,19 +427,32 @@ void TileMapEditorTilesPlugin::_scenes_list_multi_selected(int p_index, bool p_s
}
}
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_tiles_selection();
}
void TileMapEditorTilesPlugin::_scenes_list_nothing_selected() {
scene_tiles_list->deselect_all();
tile_set_selection.clear();
tile_map_selection.clear();
- selection_pattern->clear();
- _update_selection_pattern_from_tileset_selection();
+ selection_pattern.instantiate();
+ _update_selection_pattern_from_tileset_tiles_selection();
+}
+
+void TileMapEditorTilesPlugin::_update_theme() {
+ select_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ToolSelect"), SNAME("EditorIcons")));
+ paint_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
+ line_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")));
+ rect_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons")));
+ bucket_tool_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons")));
+
+ picker_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
+ erase_button->set_icon(tiles_bottom_panel->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
+
+ missing_atlas_texture_icon = tiles_bottom_panel->get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons"));
}
bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
- if (!is_visible_in_tree()) {
+ if (!(tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree())) {
// If the bottom editor is not visible, we ignore inputs.
return false;
}
@@ -391,7 +480,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
if (ED_IS_SHORTCUT("tiles_editor/cut", p_event) || ED_IS_SHORTCUT("tiles_editor/copy", p_event)) {
// Fill in the clipboard.
if (!tile_map_selection.is_empty()) {
- memdelete(tile_map_clipboard);
+ tile_map_clipboard.instantiate();
TypedArray<Vector2i> coords_array;
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
coords_array.push_back(E->get());
@@ -454,35 +543,37 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
switch (drag_type) {
case DRAG_TYPE_PAINT: {
- Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos);
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_last_mouse_pos, mpos, drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- Vector2i coords = E->key();
+ Vector2i coords = E.key;
if (!drag_modified.has(coords)) {
drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
+ _fix_invalid_tiles_in_tile_map_selection();
} break;
case DRAG_TYPE_BUCKET: {
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
for (int i = 0; i < line.size(); i++) {
if (!drag_modified.has(line[i])) {
- Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- Vector2i coords = E->key();
+ Vector2i coords = E.key;
if (!drag_modified.has(coords)) {
drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
}
}
+ _fix_invalid_tiles_in_tile_map_selection();
} break;
default:
break;
@@ -499,15 +590,23 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
if (mb->is_pressed()) {
// Pressed
+ if (erase_button->is_pressed() || mb->get_button_index() == MouseButton::RIGHT) {
+ drag_erasing = true;
+ }
+
if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
- // Do nothing.
+ // Cancel tile pasting on right-click
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ drag_type = DRAG_TYPE_NONE;
+ }
} else if (tool_buttons_group->get_pressed_button() == select_tool_button) {
drag_start_mouse_pos = mpos;
if (tile_map_selection.has(tile_map->world_to_map(drag_start_mouse_pos)) && !mb->is_shift_pressed()) {
// Move the selection
+ _update_selection_pattern_from_tilemap_selection(); // Make sure the pattern is up to date before moving.
drag_type = DRAG_TYPE_MOVE;
drag_modified.clear();
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
@@ -521,31 +620,32 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
}
} else {
// Check if we are picking a tile.
- if (picker_button->is_pressed()) {
+ if (picker_button->is_pressed() || (Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
drag_type = DRAG_TYPE_PICK;
drag_start_mouse_pos = mpos;
} else {
// Paint otherwise.
- if (tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
drag_type = DRAG_TYPE_PAINT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
- Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos);
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, mpos, mpos, drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- Vector2i coords = E->key();
+ Vector2i coords = E.key;
if (!drag_modified.has(coords)) {
drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
- } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ _fix_invalid_tiles_in_tile_map_selection();
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) {
drag_type = DRAG_TYPE_LINE;
drag_start_mouse_pos = mpos;
drag_modified.clear();
- } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) {
drag_type = DRAG_TYPE_RECT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
@@ -556,19 +656,20 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
for (int i = 0; i < line.size(); i++) {
if (!drag_modified.has(line[i])) {
- Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_continuous_checkbox->is_pressed());
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- Vector2i coords = E->key();
+ Vector2i coords = E.key;
if (!drag_modified.has(coords)) {
drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
}
- tile_map->set_cell(tile_map_layer, coords, E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
}
}
+ _fix_invalid_tiles_in_tile_map_selection();
}
}
}
@@ -576,6 +677,7 @@ bool TileMapEditorTilesPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p
} else {
// Released
_stop_dragging();
+ drag_erasing = false;
}
CanvasItemEditor::get_singleton()->update_viewport();
@@ -612,9 +714,9 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
Vector2i tile_shape_size = tile_set->get_tile_size();
// Draw the selection.
- if (is_visible_in_tree() && tool_buttons_group->get_pressed_button() == select_tool_button) {
+ if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && tool_buttons_group->get_pressed_button() == select_tool_button) {
// In select mode, we only draw the current selection if we are modifying it (pressing control or shift).
- if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
+ if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
// Do nothing
} else {
Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
@@ -624,20 +726,22 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
}
// Handle the preview of the tiles to be placed.
- if (is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
+ if ((tiles_bottom_panel->is_visible_in_tree() || patterns_bottom_panel->is_visible_in_tree()) && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
Map<Vector2i, TileMapCell> preview;
Rect2i drawn_grid_rect;
if (drag_type == DRAG_TYPE_PICK) {
- // Draw the area being picvked.
+ // Draw the area being picked.
Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
rect.size += Vector2i(1, 1);
for (int x = rect.position.x; x < rect.get_end().x; x++) {
for (int y = rect.position.y; y < rect.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - tile_shape_size / 2, tile_shape_size));
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0), false);
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(coords));
+ tile_xform.set_scale(tile_shape_size);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0), false);
}
}
}
@@ -656,21 +760,23 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
}
tile_map->draw_cells_outline(p_overlay, to_draw, Color(1.0, 1.0, 1.0), xform);
} else if (drag_type == DRAG_TYPE_MOVE) {
- // Preview when moving.
- Vector2i top_left;
- if (!tile_map_selection.is_empty()) {
- top_left = tile_map_selection.front()->get();
- }
- for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
- top_left = top_left.min(E->get());
- }
- Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
- offset = tile_map->world_to_map(drag_last_mouse_pos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
+ if (!(patterns_item_list->is_visible_in_tree() && patterns_item_list->has_point(patterns_item_list->get_local_mouse_position()))) {
+ // Preview when moving.
+ Vector2i top_left;
+ if (!tile_map_selection.is_empty()) {
+ top_left = tile_map_selection.front()->get();
+ }
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ top_left = top_left.min(E->get());
+ }
+ Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
+ offset = tile_map->world_to_map(drag_last_mouse_pos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
- TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
- for (int i = 0; i < selection_used_cells.size(); i++) {
- Vector2i coords = tile_map->map_pattern(offset + top_left, selection_used_cells[i], selection_pattern);
- preview[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
+ TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ Vector2i coords = tile_map->map_pattern(offset + top_left, selection_used_cells[i], selection_pattern);
+ preview[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
+ }
}
} else if (drag_type == DRAG_TYPE_CLIPBOARD_PASTE) {
// Preview when pasting.
@@ -680,36 +786,36 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
Vector2i coords = tile_map->map_pattern(tile_map->world_to_map(drag_last_mouse_pos - mouse_offset), clipboard_used_cells[i], tile_map_clipboard);
preview[coords] = TileMapCell(tile_map_clipboard->get_cell_source_id(clipboard_used_cells[i]), tile_map_clipboard->get_cell_atlas_coords(clipboard_used_cells[i]), tile_map_clipboard->get_cell_alternative_tile(clipboard_used_cells[i]));
}
- } else if (!picker_button->is_pressed()) {
+ } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
bool expand_grid = false;
if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
// Preview for a single pattern.
- preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos);
+ preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos, erase_button->is_pressed());
expand_grid = true;
- } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || drag_type == DRAG_TYPE_LINE) {
if (drag_type == DRAG_TYPE_NONE) {
// Preview for a single pattern.
- preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos);
+ preview = _draw_line(drag_last_mouse_pos, drag_last_mouse_pos, drag_last_mouse_pos, erase_button->is_pressed());
expand_grid = true;
} else if (drag_type == DRAG_TYPE_LINE) {
// Preview for a line pattern.
- preview = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, drag_last_mouse_pos);
+ preview = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, drag_last_mouse_pos, drag_erasing);
expand_grid = true;
}
- } else if (tool_buttons_group->get_pressed_button() == rect_tool_button && drag_type == DRAG_TYPE_RECT) {
- // Preview for a line pattern.
- preview = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos));
+ } else if (drag_type == DRAG_TYPE_RECT) {
+ // Preview for a rect pattern.
+ preview = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos), drag_erasing);
expand_grid = true;
} else if (tool_buttons_group->get_pressed_button() == bucket_tool_button && drag_type == DRAG_TYPE_NONE) {
- // Preview for a line pattern.
- preview = _draw_bucket_fill(tile_map->world_to_map(drag_last_mouse_pos), bucket_continuous_checkbox->is_pressed());
+ // Preview for a fill pattern.
+ preview = _draw_bucket_fill(tile_map->world_to_map(drag_last_mouse_pos), bucket_contiguous_checkbox->is_pressed(), erase_button->is_pressed());
}
// Expand the grid if needed
if (expand_grid && !preview.is_empty()) {
drawn_grid_rect = Rect2i(preview.front()->key(), Vector2i(1, 1));
- for (Map<Vector2i, TileMapCell>::Element *E = preview.front(); E; E = E->next()) {
- drawn_grid_rect.expand_to(E->key());
+ for (const KeyValue<Vector2i, TileMapCell> &E : preview) {
+ drawn_grid_rect.expand_to(E.key);
}
}
}
@@ -734,33 +840,35 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(Vector2(x, y)));
+ tile_xform.set_scale(tile_shape_size);
Color color = grid_color;
color.a = color.a * opacity;
- tile_set->draw_tile_shape(p_overlay, cell_region, color, false);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
}
}
}
}
// Draw the preview.
- for (Map<Vector2i, TileMapCell>::Element *E = preview.front(); E; E = E->next()) {
- Vector2i size = tile_set->get_tile_size();
- Vector2 position = tile_map->map_to_world(E->key()) - size / 2;
- Rect2 cell_region = xform.xform(Rect2(position, size));
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true);
+ for (const KeyValue<Vector2i, TileMapCell> &E : preview) {
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(E.key));
+ tile_xform.set_scale(tile_set->get_tile_size());
+ if (!(drag_erasing || erase_button->is_pressed()) && random_tile_checkbox->is_pressed()) {
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
} else {
- if (tile_set->has_source(E->get().source_id)) {
- TileSetSource *source = *tile_set->get_source(E->get().source_id);
+ if (tile_set->has_source(E.value.source_id)) {
+ TileSetSource *source = *tile_set->get_source(E.value.source_id);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
// Get tile data.
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E.value.get_atlas_coords(), E.value.alternative_tile));
// Compute the offset
- Rect2i source_rect = atlas_source->get_tile_texture_region(E->get().get_atlas_coords());
- Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(E->get().get_atlas_coords(), E->get().alternative_tile);
+ Rect2i source_rect = atlas_source->get_tile_texture_region(E.value.get_atlas_coords());
+ Vector2i tile_offset = atlas_source->get_tile_effective_texture_offset(E.value.get_atlas_coords(), E.value.alternative_tile);
// Compute the destination rectangle in the CanvasItem.
Rect2 dest_rect;
@@ -768,9 +876,9 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
bool transpose = tile_data->get_transpose();
if (transpose) {
- dest_rect.position = (tile_map->map_to_world(E->key()) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
+ dest_rect.position = (tile_map->map_to_world(E.key) - Vector2(dest_rect.size.y, dest_rect.size.x) / 2 - tile_offset);
} else {
- dest_rect.position = (tile_map->map_to_world(E->key()) - dest_rect.size / 2 - tile_offset);
+ dest_rect.position = (tile_map->map_to_world(E.key) - dest_rect.size / 2 - tile_offset);
}
dest_rect = xform.xform(dest_rect);
@@ -786,15 +894,15 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
// Get the tile modulation.
Color modulate = tile_data->get_modulate();
Color self_modulate = tile_map->get_self_modulate();
- modulate = Color(modulate.r * self_modulate.r, modulate.g * self_modulate.g, modulate.b * self_modulate.b, modulate.a * self_modulate.a);
+ modulate *= self_modulate;
// Draw the tile.
p_overlay->draw_texture_rect_region(atlas_source->get_texture(), dest_rect, source_rect, modulate * Color(1.0, 1.0, 1.0, 0.5), transpose, tile_set->is_uv_clipping());
} else {
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 1.0, 1.0, 0.5), true);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
}
} else {
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(0.0, 0.0, 0.0, 0.5), true);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(0.0, 0.0, 0.0, 0.5), true);
}
}
}
@@ -807,7 +915,7 @@ void TileMapEditorTilesPlugin::_mouse_exited_viewport() {
CanvasItemEditor::get_singleton()->update_viewport();
}
-TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(const TileMapPattern *p_pattern) {
+TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(Ref<TileMapPattern> p_pattern) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return TileMapCell();
@@ -859,7 +967,7 @@ TileMapCell TileMapEditorTilesPlugin::_pick_random_tile(const TileMapPattern *p_
return TileMapCell();
}
-Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos) {
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return Map<Vector2i, TileMapCell>();
@@ -871,14 +979,15 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_
}
// Get or create the pattern.
- TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+ Ref<TileMapPattern> erase_pattern;
+ erase_pattern.instantiate();
+ erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
Map<Vector2i, TileMapCell> output;
if (!pattern->is_empty()) {
// Paint the tiles on the tile map.
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (!p_erase && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(p_from_mouse_pos), tile_map->world_to_map(p_to_mouse_pos));
for (int i = 0; i < line.size(); i++) {
@@ -907,7 +1016,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_line(Vector2 p_start_
return output;
}
-Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell) {
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return Map<Vector2i, TileMapCell>();
@@ -923,9 +1032,11 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
rect.size += Vector2i(1, 1);
// Get or create the pattern.
- TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+ Ref<TileMapPattern> erase_pattern;
+ erase_pattern.instantiate();
+ erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
+
Map<Vector2i, TileMapCell> err_output;
ERR_FAIL_COND_V(pattern->is_empty(), err_output);
@@ -936,7 +1047,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
Map<Vector2i, TileMapCell> output;
if (!pattern->is_empty()) {
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (!p_erase && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
for (int x = 0; x < rect.size.x; x++) {
for (int y = 0; y < rect.size.y; y++) {
@@ -964,7 +1075,7 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_rect(Vector2i p_start
return output;
}
-Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous) {
+Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return Map<Vector2i, TileMapCell>();
@@ -982,16 +1093,17 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
}
// Get or create the pattern.
- TileMapPattern erase_pattern;
- erase_pattern.set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
- TileMapPattern *pattern = erase_button->is_pressed() ? &erase_pattern : selection_pattern;
+ Ref<TileMapPattern> erase_pattern;
+ erase_pattern.instantiate();
+ erase_pattern->set_cell(Vector2i(0, 0), TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE);
+ Ref<TileMapPattern> pattern = p_erase ? erase_pattern : selection_pattern;
if (!pattern->is_empty()) {
- TileMapCell source = tile_map->get_cell(tile_map_layer, p_coords);
+ TileMapCell source_cell = tile_map->get_cell(tile_map_layer, p_coords);
// If we are filling empty tiles, compute the tilemap boundaries.
Rect2i boundaries;
- if (source.source_id == TileSet::INVALID_SOURCE) {
+ if (source_cell.source_id == TileSet::INVALID_SOURCE) {
boundaries = tile_map->get_used_rect();
}
@@ -1004,11 +1116,11 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
Vector2i coords = to_check.back()->get();
to_check.pop_back();
if (!already_checked.has(coords)) {
- if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
- source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
- source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
- (source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (source_cell.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
+ source_cell.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
+ source_cell.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
+ (source_cell.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
+ if (!p_erase && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
output.insert(coords, _pick_random_tile(pattern));
} else {
@@ -1035,9 +1147,9 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
} else {
// Replace all tiles like the source.
TypedArray<Vector2i> to_check;
- if (source.source_id == TileSet::INVALID_SOURCE) {
+ if (source_cell.source_id == TileSet::INVALID_SOURCE) {
Rect2i rect = tile_map->get_used_rect();
- if (rect.size.x <= 0 || rect.size.y <= 0) {
+ if (rect.has_no_area()) {
rect = Rect2i(p_coords, Vector2i(1, 1));
}
for (int x = boundaries.position.x; x < boundaries.get_end().x; x++) {
@@ -1050,11 +1162,11 @@ Map<Vector2i, TileMapCell> TileMapEditorTilesPlugin::_draw_bucket_fill(Vector2i
}
for (int i = 0; i < to_check.size(); i++) {
Vector2i coords = to_check[i];
- if (source.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
- source.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
- source.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
- (source.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
- if (!erase_button->is_pressed() && random_tile_checkbox->is_pressed()) {
+ if (source_cell.source_id == tile_map->get_cell_source_id(tile_map_layer, coords) &&
+ source_cell.get_atlas_coords() == tile_map->get_cell_atlas_coords(tile_map_layer, coords) &&
+ source_cell.alternative_tile == tile_map->get_cell_alternative_tile(tile_map_layer, coords) &&
+ (source_cell.source_id != TileSet::INVALID_SOURCE || boundaries.has_point(coords))) {
+ if (!p_erase && random_tile_checkbox->is_pressed()) {
// Paint a random tile.
output.insert(coords, _pick_random_tile(pattern));
} else {
@@ -1103,14 +1215,14 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
undo_redo->create_action(TTR("Change selection"));
undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
- if (!Input::get_singleton()->is_key_pressed(KEY_SHIFT) && !Input::get_singleton()->is_key_pressed(KEY_CTRL)) {
+ if (!Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
tile_map_selection.clear();
}
Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
for (int x = rect.position.x; x <= rect.get_end().x; x++) {
for (int y = rect.position.y; y <= rect.get_end().y; y++) {
Vector2i coords = Vector2i(x, y);
- if (Input::get_singleton()->is_key_pressed(KEY_CTRL)) {
+ if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
if (tile_map_selection.has(coords)) {
tile_map_selection.erase(coords);
}
@@ -1128,60 +1240,80 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
_update_tileset_selection_from_selection_pattern();
} break;
case DRAG_TYPE_MOVE: {
- Vector2i top_left;
- if (!tile_map_selection.is_empty()) {
- top_left = tile_map_selection.front()->get();
- }
- for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
- top_left = top_left.min(E->get());
- }
+ if (patterns_item_list->is_visible_in_tree() && patterns_item_list->has_point(patterns_item_list->get_local_mouse_position())) {
+ // Restore the cells.
+ for (KeyValue<Vector2i, TileMapCell> kv : drag_modified) {
+ tile_map->set_cell(tile_map_layer, kv.key, kv.value.source_id, kv.value.get_atlas_coords(), kv.value.alternative_tile);
+ }
+
+ // Creating a pattern in the pattern list.
+ select_last_pattern = true;
+ int new_pattern_index = tile_set->get_patterns_count();
+ undo_redo->create_action(TTR("Add TileSet pattern"));
+ undo_redo->add_do_method(*tile_set, "add_pattern", selection_pattern, new_pattern_index);
+ undo_redo->add_undo_method(*tile_set, "remove_pattern", new_pattern_index);
+ undo_redo->commit_action();
+ } else {
+ // Get the top-left cell.
+ Vector2i top_left;
+ if (!tile_map_selection.is_empty()) {
+ top_left = tile_map_selection.front()->get();
+ }
+ for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
+ top_left = top_left.min(E->get());
+ }
- Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
- offset = tile_map->world_to_map(mpos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
+ // Get the offset from the mouse.
+ Vector2i offset = drag_start_mouse_pos - tile_map->map_to_world(top_left);
+ offset = tile_map->world_to_map(mpos - offset) - tile_map->world_to_map(drag_start_mouse_pos - offset);
- TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
+ TypedArray<Vector2i> selection_used_cells = selection_pattern->get_used_cells();
- Vector2i coords;
- Map<Vector2i, TileMapCell> cells_undo;
- for (int i = 0; i < selection_used_cells.size(); i++) {
- coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
- cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
- coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
- cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
- }
+ // Build the list of cells to undo.
+ Vector2i coords;
+ Map<Vector2i, TileMapCell> cells_undo;
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
+ cells_undo[coords] = TileMapCell(drag_modified[coords].source_id, drag_modified[coords].get_atlas_coords(), drag_modified[coords].alternative_tile);
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ cells_undo[coords] = TileMapCell(tile_map->get_cell_source_id(tile_map_layer, coords), tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords));
+ }
- Map<Vector2i, TileMapCell> cells_do;
- for (int i = 0; i < selection_used_cells.size(); i++) {
- coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
- cells_do[coords] = TileMapCell();
- }
- for (int i = 0; i < selection_used_cells.size(); i++) {
- coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
- cells_do[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
- }
- undo_redo->create_action(TTR("Move tiles"));
- // Move the tiles.
- for (Map<Vector2i, TileMapCell>::Element *E = cells_do.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- }
- for (Map<Vector2i, TileMapCell>::Element *E = cells_undo.front(); E; E = E->next()) {
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- }
+ // Build the list of cells to do.
+ Map<Vector2i, TileMapCell> cells_do;
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left, selection_used_cells[i], selection_pattern);
+ cells_do[coords] = TileMapCell();
+ }
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ cells_do[coords] = TileMapCell(selection_pattern->get_cell_source_id(selection_used_cells[i]), selection_pattern->get_cell_atlas_coords(selection_used_cells[i]), selection_pattern->get_cell_alternative_tile(selection_used_cells[i]));
+ }
- // Update the selection.
- undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
- tile_map_selection.clear();
- for (int i = 0; i < selection_used_cells.size(); i++) {
- coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
- tile_map_selection.insert(coords);
+ // Move the tiles.
+ undo_redo->create_action(TTR("Move tiles"));
+ for (Map<Vector2i, TileMapCell>::Element *E = cells_do.front(); E; E = E->next()) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+ for (Map<Vector2i, TileMapCell>::Element *E = cells_undo.front(); E; E = E->next()) {
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ }
+
+ // Update the selection.
+ undo_redo->add_undo_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ tile_map_selection.clear();
+ for (int i = 0; i < selection_used_cells.size(); i++) {
+ coords = tile_map->map_pattern(top_left + offset, selection_used_cells[i], selection_pattern);
+ tile_map_selection.insert(coords);
+ }
+ undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
+ undo_redo->commit_action();
}
- undo_redo->add_do_method(this, "_set_tile_map_selection", _get_tile_map_selection());
- undo_redo->commit_action();
} break;
case DRAG_TYPE_PICK: {
Rect2i rect = Rect2i(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos) - tile_map->world_to_map(drag_start_mouse_pos)).abs();
rect.size += Vector2i(1, 1);
- memdelete(selection_pattern);
+
TypedArray<Vector2i> coords_array;
for (int x = rect.position.x; x < rect.get_end().x; x++) {
for (int y = rect.position.y; y < rect.get_end().y; y++) {
@@ -1191,51 +1323,50 @@ void TileMapEditorTilesPlugin::_stop_dragging() {
}
}
}
- selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
- if (!selection_pattern->is_empty()) {
+ Ref<TileMapPattern> new_selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
+ if (!new_selection_pattern->is_empty()) {
+ selection_pattern = new_selection_pattern;
_update_tileset_selection_from_selection_pattern();
- } else {
- _update_selection_pattern_from_tileset_selection();
}
picker_button->set_pressed(false);
} break;
case DRAG_TYPE_PAINT: {
undo_redo->create_action(TTR("Paint tiles"));
- for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
undo_redo->commit_action(false);
} break;
case DRAG_TYPE_LINE: {
- Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos);
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(drag_start_mouse_pos, drag_start_mouse_pos, mpos, drag_erasing);
undo_redo->create_action(TTR("Paint tiles"));
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
}
undo_redo->commit_action();
} break;
case DRAG_TYPE_RECT: {
- Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos));
+ Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing);
undo_redo->create_action(TTR("Paint tiles"));
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- if (!erase_button->is_pressed() && E->get().source_id == TileSet::INVALID_SOURCE) {
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
continue;
}
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
}
undo_redo->commit_action();
} break;
case DRAG_TYPE_BUCKET: {
undo_redo->create_action(TTR("Paint tiles"));
- for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
undo_redo->commit_action(false);
} break;
@@ -1263,8 +1394,9 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
+ patterns_item_list->deselect_all();
tile_map_selection.clear();
- selection_pattern->clear();
+ selection_pattern.instantiate();
return;
}
@@ -1274,8 +1406,9 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
+ patterns_item_list->deselect_all();
tile_map_selection.clear();
- selection_pattern->clear();
+ selection_pattern.instantiate();
return;
}
@@ -1285,8 +1418,9 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
hovered_tile.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
hovered_tile.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
tile_set_selection.clear();
+ patterns_item_list->deselect_all();
tile_map_selection.clear();
- selection_pattern->clear();
+ selection_pattern.instantiate();
return;
}
@@ -1314,8 +1448,29 @@ void TileMapEditorTilesPlugin::_update_fix_selected_and_hovered() {
if (!tile_map_selection.is_empty()) {
_update_selection_pattern_from_tilemap_selection();
+ } else if (tiles_bottom_panel->is_visible_in_tree()) {
+ _update_selection_pattern_from_tileset_tiles_selection();
} else {
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_pattern_selection();
+ }
+}
+
+void TileMapEditorTilesPlugin::_fix_invalid_tiles_in_tile_map_selection() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Set<Vector2i> to_remove;
+ for (Vector2i selected : tile_map_selection) {
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, selected);
+ if (cell.source_id == TileSet::INVALID_SOURCE && cell.get_atlas_coords() == TileSetSource::INVALID_ATLAS_COORDS && cell.alternative_tile == TileSetAtlasSource::INVALID_TILE_ALTERNATIVE) {
+ to_remove.insert(selected);
+ }
+ }
+
+ for (Vector2i cell : to_remove) {
+ tile_map_selection.erase(cell);
}
}
@@ -1325,9 +1480,14 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection(
return;
}
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
- memdelete(selection_pattern);
+ selection_pattern.instantiate();
TypedArray<Vector2i> coords_array;
for (Set<Vector2i>::Element *E = tile_map_selection.front(); E; E = E->next()) {
@@ -1336,7 +1496,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tilemap_selection(
selection_pattern = tile_map->get_pattern(tile_map_layer, coords_array);
}
-void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection() {
+void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_tiles_selection() {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
return;
@@ -1351,7 +1511,7 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
tile_map_selection.clear();
// Clear the selected pattern.
- selection_pattern->clear();
+ selection_pattern.instantiate();
// Group per source.
Map<int, List<const TileMapCell *>> per_source;
@@ -1360,17 +1520,17 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
}
int vertical_offset = 0;
- for (Map<int, List<const TileMapCell *>>::Element *E_source = per_source.front(); E_source; E_source = E_source->next()) {
+ for (const KeyValue<int, List<const TileMapCell *>> &E_source : per_source) {
// Per source.
List<const TileMapCell *> unorganized;
Rect2i encompassing_rect_coords;
Map<Vector2i, const TileMapCell *> organized_pattern;
- TileSetSource *source = *tile_set->get_source(E_source->key());
+ TileSetSource *source = *tile_set->get_source(E_source.key);
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
// Organize using coordinates.
- for (const TileMapCell *current : E_source->get()) {
+ for (const TileMapCell *current : E_source.value) {
if (current->alternative_tile == 0) {
organized_pattern[current->get_atlas_coords()] = current;
} else {
@@ -1389,14 +1549,14 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
}
} else {
// Add everything unorganized.
- for (const TileMapCell *cell : E_source->get()) {
+ for (const TileMapCell *cell : E_source.value) {
unorganized.push_back(cell);
}
}
// Now add everything to the output pattern.
- for (Map<Vector2i, const TileMapCell *>::Element *E_cell = organized_pattern.front(); E_cell; E_cell = E_cell->next()) {
- selection_pattern->set_cell(E_cell->key() - encompassing_rect_coords.position + Vector2i(0, vertical_offset), E_cell->get()->source_id, E_cell->get()->get_atlas_coords(), E_cell->get()->alternative_tile);
+ for (const KeyValue<Vector2i, const TileMapCell *> &E_cell : organized_pattern) {
+ selection_pattern->set_cell(E_cell.key - encompassing_rect_coords.position + Vector2i(0, vertical_offset), E_cell.value->source_id, E_cell.value->get_atlas_coords(), E_cell.value->alternative_tile);
}
Vector2i organized_size = selection_pattern->get_size();
int unorganized_index = 0;
@@ -1409,6 +1569,30 @@ void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_selection(
CanvasItemEditor::get_singleton()->update_viewport();
}
+void TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ // Clear the tilemap selection.
+ tile_map_selection.clear();
+
+ // Clear the selected pattern.
+ selection_pattern.instantiate();
+
+ if (patterns_item_list->get_selected_items().size() >= 1) {
+ selection_pattern = patterns_item_list->get_item_metadata(patterns_item_list->get_selected_items()[0]);
+ }
+
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern() {
tile_set_selection.clear();
TypedArray<Vector2i> used_cells = selection_pattern->get_used_cells();
@@ -1418,7 +1602,9 @@ void TileMapEditorTilesPlugin::_update_tileset_selection_from_selection_pattern(
tile_set_selection.insert(TileMapCell(selection_pattern->get_cell_source_id(coords), selection_pattern->get_cell_atlas_coords(coords), selection_pattern->get_cell_alternative_tile(coords)));
}
}
- _update_bottom_panel();
+ _update_source_display();
+ tile_atlas_control->update();
+ alternative_tiles_control->update();
}
void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
@@ -1452,13 +1638,25 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
if (E->get().source_id == source_id && E->get().alternative_tile == 0) {
- tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), selection_color, false);
+ for (int frame = 0; frame < atlas->get_tile_animation_frames_count(E->get().get_atlas_coords()); frame++) {
+ Color color = selection_color;
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords(), frame), color, false);
+ }
}
}
// Draw the hovered tile.
if (hovered_tile.get_atlas_coords() != TileSetSource::INVALID_ATLAS_COORDS && hovered_tile.alternative_tile == 0 && !tile_set_dragging_selection) {
- tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords()), Color(1.0, 1.0, 1.0), false);
+ for (int frame = 0; frame < atlas->get_tile_animation_frames_count(hovered_tile.get_atlas_coords()); frame++) {
+ Color color = Color(1.0, 1.0, 1.0);
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(hovered_tile.get_atlas_coords(), frame), color, false);
+ }
}
// Draw the selection rect.
@@ -1539,7 +1737,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
}
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) { // Pressed
tile_set_dragging_selection = true;
tile_set_drag_start_mouse_pos = tile_atlas_control->get_local_mouse_position();
@@ -1554,7 +1752,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
tile_set_selection.insert(TileMapCell(source_id, hovered_tile.get_atlas_coords(), 0));
}
}
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_tiles_selection();
} else { // Released
if (tile_set_dragging_selection) {
if (!mb->is_shift_pressed()) {
@@ -1591,7 +1789,7 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_gui_input(const Ref<InputEven
}
}
}
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_tiles_selection();
}
tile_set_dragging_selection = false;
}
@@ -1697,7 +1895,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<In
}
Ref<InputEventMouseButton> mb = p_event;
- if (mb.is_valid() && mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) { // Pressed
// Left click pressed.
if (!mb->is_shift_pressed()) {
@@ -1711,7 +1909,7 @@ void TileMapEditorTilesPlugin::_tile_alternatives_control_gui_input(const Ref<In
tile_set_selection.insert(TileMapCell(source_id, hovered_tile.get_atlas_coords(), hovered_tile.alternative_tile));
}
}
- _update_selection_pattern_from_tileset_selection();
+ _update_selection_pattern_from_tileset_tiles_selection();
}
tile_atlas_control->update();
alternative_tiles_control->update();
@@ -1744,8 +1942,9 @@ void TileMapEditorTilesPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer
// Clear the selection.
tile_set_selection.clear();
+ patterns_item_list->deselect_all();
tile_map_selection.clear();
- selection_pattern->clear();
+ selection_pattern.instantiate();
}
tile_map_layer = p_tile_map_layer;
@@ -1761,15 +1960,19 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
CanvasItemEditor::get_singleton()->get_viewport_control()->connect("mouse_exited", callable_mp(this, &TileMapEditorTilesPlugin::_mouse_exited_viewport));
// --- Shortcuts ---
- ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KEY_MASK_CMD | KEY_X);
- ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KEY_MASK_CMD | KEY_C);
- ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KEY_MASK_CMD | KEY_V);
- ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), KEY_ESCAPE);
- ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), KEY_DELETE);
+ ED_SHORTCUT("tiles_editor/cut", TTR("Cut"), KeyModifierMask::CMD | Key::X);
+ ED_SHORTCUT("tiles_editor/copy", TTR("Copy"), KeyModifierMask::CMD | Key::C);
+ ED_SHORTCUT("tiles_editor/paste", TTR("Paste"), KeyModifierMask::CMD | Key::V);
+ ED_SHORTCUT("tiles_editor/cancel", TTR("Cancel"), Key::ESCAPE);
+ ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE);
+
+ // --- Initialize references ---
+ tile_map_clipboard.instantiate();
+ selection_pattern.instantiate();
// --- Toolbar ---
toolbar = memnew(HBoxContainer);
- toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+ toolbar->set_h_size_flags(Control::SIZE_EXPAND_FILL);
HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
@@ -1779,7 +1982,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
select_tool_button->set_flat(true);
select_tool_button->set_toggle_mode(true);
select_tool_button->set_button_group(tool_buttons_group);
- select_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/selection_tool", "Selection", KEY_S));
+ select_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/selection_tool", "Selection", Key::S));
select_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(select_tool_button);
@@ -1787,7 +1990,8 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
paint_tool_button->set_flat(true);
paint_tool_button->set_toggle_mode(true);
paint_tool_button->set_button_group(tool_buttons_group);
- paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", KEY_D));
+ paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", Key::D));
+ paint_tool_button->set_tooltip(TTR("Shift: Draw line.") + "\n" + TTR("Shift+Ctrl: Draw rectangle."));
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
@@ -1795,7 +1999,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
line_tool_button->set_flat(true);
line_tool_button->set_toggle_mode(true);
line_tool_button->set_button_group(tool_buttons_group);
- line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", "Line", KEY_L));
+ line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", "Line", Key::L));
line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(line_tool_button);
@@ -1803,7 +2007,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
rect_tool_button->set_flat(true);
rect_tool_button->set_toggle_mode(true);
rect_tool_button->set_button_group(tool_buttons_group);
- rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", "Rect", KEY_R));
+ rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", "Rect", Key::R));
rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(rect_tool_button);
@@ -1811,7 +2015,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
bucket_tool_button->set_flat(true);
bucket_tool_button->set_toggle_mode(true);
bucket_tool_button->set_button_group(tool_buttons_group);
- bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", "Bucket", KEY_B));
+ bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", "Bucket", Key::B));
bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
toolbar->add_child(tilemap_tiles_tools_buttons);
@@ -1827,7 +2031,8 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
picker_button = memnew(Button);
picker_button->set_flat(true);
picker_button->set_toggle_mode(true);
- picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P));
+ picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", Key::P));
+ picker_button->set_tooltip(TTR("Alternatively hold Ctrl with other tools to pick tile."));
picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(picker_button);
@@ -1835,7 +2040,8 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
erase_button = memnew(Button);
erase_button->set_flat(true);
erase_button->set_toggle_mode(true);
- erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", KEY_E));
+ erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", Key::E));
+ erase_button->set_tooltip(TTR("Alternatively use RMB to erase tiles."));
erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
@@ -1844,10 +2050,11 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
tools_settings->add_child(tools_settings_vsep_2);
// Continuous checkbox.
- bucket_continuous_checkbox = memnew(CheckBox);
- bucket_continuous_checkbox->set_flat(true);
- bucket_continuous_checkbox->set_text(TTR("Contiguous"));
- tools_settings->add_child(bucket_continuous_checkbox);
+ bucket_contiguous_checkbox = memnew(CheckBox);
+ bucket_contiguous_checkbox->set_flat(true);
+ bucket_contiguous_checkbox->set_text(TTR("Contiguous"));
+ bucket_contiguous_checkbox->set_pressed(true);
+ tools_settings->add_child(bucket_contiguous_checkbox);
// Random tile checkbox.
random_tile_checkbox = memnew(CheckBox);
@@ -1877,42 +2084,47 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
paint_tool_button->set_pressed(true);
_update_toolbar();
- // --- Bottom panel ---
- set_name("Tiles");
+ // --- Bottom panel tiles ---
+ tiles_bottom_panel = memnew(VBoxContainer);
+ tiles_bottom_panel->connect("tree_entered", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme));
+ tiles_bottom_panel->connect("theme_changed", callable_mp(this, &TileMapEditorTilesPlugin::_update_theme));
+ tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_stop_dragging));
+ tiles_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_tab_changed));
+ tiles_bottom_panel->set_name(TTR("Tiles"));
missing_source_label = memnew(Label);
missing_source_label->set_text(TTR("This TileMap's TileSet has no source configured. Edit the TileSet resource to add one."));
- missing_source_label->set_h_size_flags(SIZE_EXPAND_FILL);
- missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
- missing_source_label->set_align(Label::ALIGN_CENTER);
- missing_source_label->set_valign(Label::VALIGN_CENTER);
+ missing_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ missing_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ missing_source_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ missing_source_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
missing_source_label->hide();
- add_child(missing_source_label);
+ tiles_bottom_panel->add_child(missing_source_label);
atlas_sources_split_container = memnew(HSplitContainer);
- atlas_sources_split_container->set_h_size_flags(SIZE_EXPAND_FILL);
- atlas_sources_split_container->set_v_size_flags(SIZE_EXPAND_FILL);
- add_child(atlas_sources_split_container);
+ atlas_sources_split_container->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ atlas_sources_split_container->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ tiles_bottom_panel->add_child(atlas_sources_split_container);
sources_list = memnew(ItemList);
sources_list->set_fixed_icon_size(Size2i(60, 60) * EDSCALE);
- sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ sources_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
sources_list->set_stretch_ratio(0.25);
sources_list->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_fix_selected_and_hovered).unbind(1));
- sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_bottom_panel).unbind(1));
- sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_sources_lists_current));
- sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_sources_list), varray(sources_list));
+ sources_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_source_display).unbind(1));
+ sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list));
atlas_sources_split_container->add_child(sources_list);
// Tile atlas source.
tile_atlas_view = memnew(TileAtlasView);
- tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL);
- tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL);
+ tile_atlas_view->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tile_atlas_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);
tile_atlas_view->set_texture_grid_visible(false);
tile_atlas_view->set_tile_shape_grid_visible(false);
- tile_atlas_view->connect("transform_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_view_transform));
+ tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_atlas_view_transform));
atlas_sources_split_container->add_child(tile_atlas_view);
tile_atlas_control = memnew(Control);
@@ -1929,8 +2141,8 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
// Scenes collection source.
scene_tiles_list = memnew(ItemList);
- scene_tiles_list->set_h_size_flags(SIZE_EXPAND_FILL);
- scene_tiles_list->set_v_size_flags(SIZE_EXPAND_FILL);
+ scene_tiles_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ scene_tiles_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
scene_tiles_list->set_drag_forwarding(this);
scene_tiles_list->set_select_mode(ItemList::SELECT_MULTI);
scene_tiles_list->connect("multi_selected", callable_mp(this, &TileMapEditorTilesPlugin::_scenes_list_multi_selected));
@@ -1941,30 +2153,42 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
// Invalid source label.
invalid_source_label = memnew(Label);
invalid_source_label->set_text(TTR("Invalid source selected."));
- invalid_source_label->set_h_size_flags(SIZE_EXPAND_FILL);
- invalid_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
- invalid_source_label->set_align(Label::ALIGN_CENTER);
- invalid_source_label->set_valign(Label::VALIGN_CENTER);
+ invalid_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ invalid_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ invalid_source_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ invalid_source_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
invalid_source_label->hide();
atlas_sources_split_container->add_child(invalid_source_label);
- _update_bottom_panel();
+ // --- Bottom panel patterns ---
+ patterns_bottom_panel = memnew(VBoxContainer);
+ patterns_bottom_panel->set_name(TTR("Patterns"));
+ patterns_bottom_panel->connect("visibility_changed", callable_mp(this, &TileMapEditorTilesPlugin::_tab_changed));
+
+ int thumbnail_size = 64;
+ patterns_item_list = memnew(ItemList);
+ patterns_item_list->set_max_columns(0);
+ patterns_item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
+ patterns_item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
+ patterns_item_list->set_max_text_lines(2);
+ patterns_item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
+ patterns_item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ patterns_item_list->connect("gui_input", callable_mp(this, &TileMapEditorTilesPlugin::_patterns_item_list_gui_input));
+ patterns_item_list->connect("item_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection).unbind(1));
+ patterns_item_list->connect("item_activated", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection));
+ patterns_item_list->connect("nothing_selected", callable_mp(this, &TileMapEditorTilesPlugin::_update_selection_pattern_from_tileset_pattern_selection));
+ patterns_bottom_panel->add_child(patterns_item_list);
+
+ patterns_help_label = memnew(Label);
+ patterns_help_label->set_text(TTR("Drag and drop or paste a TileMap selection here to store a pattern."));
+ patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
+ patterns_item_list->add_child(patterns_help_label);
+
+ // Update.
+ _update_source_display();
}
TileMapEditorTilesPlugin::~TileMapEditorTilesPlugin() {
- memdelete(selection_pattern);
- memdelete(tile_map_clipboard);
-}
-
-void TileMapEditorTerrainsPlugin::_notification(int p_what) {
- switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED:
- paint_tool_button->set_icon(get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
- picker_button->set_icon(get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
- erase_button->set_icon(get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
- break;
- }
}
void TileMapEditorTerrainsPlugin::tile_set_changed() {
@@ -1984,681 +2208,342 @@ void TileMapEditorTerrainsPlugin::_update_toolbar() {
tools_settings_vsep->show();
picker_button->show();
erase_button->show();
+ tools_settings_vsep_2->hide();
+ bucket_contiguous_checkbox->hide();
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->hide();
+ bucket_contiguous_checkbox->hide();
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->hide();
+ bucket_contiguous_checkbox->hide();
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
+ tools_settings_vsep->show();
+ picker_button->show();
+ erase_button->show();
+ tools_settings_vsep_2->show();
+ bucket_contiguous_checkbox->show();
}
}
-Control *TileMapEditorTerrainsPlugin::get_toolbar() const {
- return toolbar;
-}
-
-Map<Vector2i, TileSet::CellNeighbor> TileMapEditorTerrainsPlugin::Constraint::get_overlapping_coords_and_peering_bits() const {
- Map<Vector2i, TileSet::CellNeighbor> output;
- Ref<TileSet> tile_set = tile_map->get_tileset();
- ERR_FAIL_COND_V(!tile_set.is_valid(), output);
-
- TileSet::TileShape shape = tile_set->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE) {
- switch (bit) {
- case 0:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
- break;
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- switch (bit) {
- case 0:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- break;
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else {
- // Half offset shapes.
- TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
- if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- switch (bit) {
- case 0:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE;
- break;
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER;
- break;
- case 4:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- } else {
- switch (bit) {
- case 0:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- break;
- case 1:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE;
- break;
- case 2:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER;
- break;
- case 3:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE;
- break;
- case 4:
- output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE;
- output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE;
- break;
- default:
- ERR_FAIL_V(output);
- }
- }
- }
- return output;
-}
-
-TileMapEditorTerrainsPlugin::Constraint::Constraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) {
- // The way we build the constraint make it easy to detect conflicting constraints.
- tile_map = p_tile_map;
-
- Ref<TileSet> tile_set = tile_map->get_tileset();
- ERR_FAIL_COND(!tile_set.is_valid());
-
- TileSet::TileShape shape = tile_set->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE || shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
- case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
- bit = 0;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
- case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
- case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
- bit = 0;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_SIDE:
- case TileSet::CELL_NEIGHBOR_TOP_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, p_bit);
- break;
- default:
- ERR_FAIL();
- break;
- }
- } else {
- // Half-offset shapes
- TileSet::TileOffsetAxis offset_axis = tile_set->get_tile_offset_axis();
- if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_SIDE:
- bit = 0;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 4;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_SIDE:
- bit = 0;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_CORNER:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- default:
- ERR_FAIL();
- break;
- }
- } else {
- switch (p_bit) {
- case TileSet::CELL_NEIGHBOR_RIGHT_CORNER:
- bit = 0;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE:
- bit = 1;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER:
- bit = 2;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE:
- bit = 3;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER:
- bit = 0;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE:
- bit = 4;
- base_cell_coords = p_position;
- break;
- case TileSet::CELL_NEIGHBOR_LEFT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE:
- bit = 1;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER:
- bit = 0;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_SIDE:
- bit = 3;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER:
- bit = 2;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE);
- break;
- case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE:
- bit = 4;
- base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- break;
- default:
- ERR_FAIL();
- break;
- }
- }
- }
- terrain = p_terrain;
+Vector<TileMapEditorPlugin::TabData> TileMapEditorTerrainsPlugin::get_tabs() const {
+ Vector<TileMapEditorPlugin::TabData> tabs;
+ tabs.push_back({ toolbar, main_vbox_container });
+ return tabs;
}
-Set<TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugin::_get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<Constraint> p_constraints) const {
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
- return Set<TerrainsTilePattern>();
+ return Map<Vector2i, TileMapCell>();
}
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
- return Set<TerrainsTilePattern>();
- }
-
- // Returns all tiles compatible with the given constraints.
- Set<TerrainsTilePattern> compatible_terrain_tile_patterns;
- for (Map<TerrainsTilePattern, Set<TileMapCell>>::Element *E = per_terrain_terrains_tile_patterns_tiles[p_terrain_set].front(); E; E = E->next()) {
- int valid = true;
- int in_pattern_count = 0;
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
- // Check if the bit is compatible with the constraints.
- Constraint terrain_bit_constraint = Constraint(tile_map, p_position, bit, E->key()[in_pattern_count]);
-
- Set<Constraint>::Element *in_set_constraint_element = p_constraints.find(terrain_bit_constraint);
- if (in_set_constraint_element && in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) {
- valid = false;
- break;
- }
- in_pattern_count++;
- }
- }
-
- if (valid) {
- compatible_terrain_tile_patterns.insert(E->key());
- }
+ return Map<Vector2i, TileMapCell>();
}
- return compatible_terrain_tile_patterns;
-}
+ Map<Vector2i, TileMapCell> output;
-Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- return Set<Constraint>();
- }
+ // Add the constraints from the added tiles.
+ Set<TileMap::TerrainConstraint> added_tiles_constraints_set;
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+ Vector2i coords = E_to_paint.key;
+ TileSet::TerrainsPattern terrains_pattern = E_to_paint.value;
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return Set<Constraint>();
+ Set<TileMap::TerrainConstraint> cell_constraints = tile_map->get_terrain_constraints_from_added_tile(coords, p_terrain_set, terrains_pattern);
+ for (Set<TileMap::TerrainConstraint>::Element *E = cell_constraints.front(); E; E = E->next()) {
+ added_tiles_constraints_set.insert(E->get());
+ }
}
- ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), Set<Constraint>());
- ERR_FAIL_INDEX_V(tile_map_layer, tile_map->get_layers_count(), Set<Constraint>());
-
- // Build a set of dummy constraints get the constrained points.
- Set<Constraint> dummy_constraints;
- for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over sides.
- TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, bit)) {
- dummy_constraints.insert(Constraint(tile_map, E->get(), bit, -1));
+ // Build the list of potential tiles to replace.
+ Set<Vector2i> potential_to_replace;
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+ Vector2i coords = E_to_paint.key;
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
+ Vector2i neighbor = tile_map->get_neighbor_cell(coords, TileSet::CellNeighbor(i));
+ if (!p_to_paint.has(neighbor)) {
+ potential_to_replace.insert(neighbor);
+ }
}
}
}
- // For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it.
- Set<Constraint> constraints;
- for (Set<Constraint>::Element *E = dummy_constraints.front(); E; E = E->next()) {
- Constraint c = E->get();
+ // Set of tiles to replace
+ Set<Vector2i> to_replace;
- Map<int, int> terrain_count;
+ // Add the central tiles to the one to replace.
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+ to_replace.insert(E_to_paint.key);
+ }
- // Count the number of occurrences per terrain.
- Map<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits();
- for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_overlapping = overlapping_terrain_bits.front(); E_overlapping; E_overlapping = E_overlapping->next()) {
- if (!p_to_replace.has(E_overlapping->key())) {
- TileMapCell neighbor_cell = tile_map->get_cell(tile_map_layer, E_overlapping->key());
- TileData *neighbor_tile_data = nullptr;
- if (terrain_tiles.has(neighbor_cell) && terrain_tiles[neighbor_cell]->get_terrain_set() == p_terrain_set) {
- neighbor_tile_data = terrain_tiles[neighbor_cell];
- }
+ // Add the constraints from the surroundings of the modified areas.
+ Set<TileMap::TerrainConstraint> removed_cells_constraints_set;
+ bool to_replace_modified = true;
+ while (to_replace_modified) {
+ // Get the constraints from the removed cells.
+ removed_cells_constraints_set = tile_map->get_terrain_constraints_from_removed_cells_list(tile_map_layer, to_replace, p_terrain_set);
- int terrain = neighbor_tile_data ? neighbor_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(E_overlapping->get())) : -1;
- if (terrain_count.has(terrain)) {
- terrain_count[terrain] = 0;
+ // Filter the sources to make sure they are in the potential_to_replace.
+ Map<TileMap::TerrainConstraint, Set<Vector2i>> per_constraint_tiles;
+ for (Set<TileMap::TerrainConstraint>::Element *E = removed_cells_constraints_set.front(); E; E = E->next()) {
+ Map<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E->get().get_overlapping_coords_and_peering_bits();
+ for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_source_tile_of_constraint : sources_of_constraint) {
+ if (potential_to_replace.has(E_source_tile_of_constraint.key)) {
+ per_constraint_tiles[E->get()].insert(E_source_tile_of_constraint.key);
}
- terrain_count[terrain] += 1;
}
}
- // Get the terrain with the max number of occurrences.
- int max = 0;
- int max_terrain = -1;
- for (Map<int, int>::Element *E_terrain_count = terrain_count.front(); E_terrain_count; E_terrain_count = E_terrain_count->next()) {
- if (E_terrain_count->get() > max) {
- max = E_terrain_count->get();
- max_terrain = E_terrain_count->key();
+ to_replace_modified = false;
+ for (Set<TileMap::TerrainConstraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
+ TileMap::TerrainConstraint c = E->get();
+ // Check if we have a conflict in constraints.
+ if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) {
+ // If we do, we search for a neighbor to remove.
+ if (per_constraint_tiles.has(c) && !per_constraint_tiles[c].is_empty()) {
+ // Remove it.
+ Vector2i to_add_to_remove = per_constraint_tiles[c].front()->get();
+ potential_to_replace.erase(to_add_to_remove);
+ to_replace.insert(to_add_to_remove);
+ to_replace_modified = true;
+ for (KeyValue<TileMap::TerrainConstraint, Set<Vector2i>> &E_source_tiles_of_constraint : per_constraint_tiles) {
+ E_source_tiles_of_constraint.value.erase(to_add_to_remove);
+ }
+ break;
+ }
}
}
-
- // Set the adequate terrain.
- if (max > 0) {
- c.set_terrain(max_terrain);
- constraints.insert(c);
- }
}
- return constraints;
-}
+ // Combine all constraints together.
+ Set<TileMap::TerrainConstraint> constraints = removed_cells_constraints_set;
+ for (Set<TileMap::TerrainConstraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
+ constraints.insert(E->get());
+ }
-Set<TileMapEditorTerrainsPlugin::Constraint> TileMapEditorTerrainsPlugin::_get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const {
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- return Set<TileMapEditorTerrainsPlugin::Constraint>();
+ // Remove the central tiles from the ones to replace.
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+ to_replace.erase(E_to_paint.key);
}
- Ref<TileSet> tile_set = tile_map->get_tileset();
- if (!tile_set.is_valid()) {
- return Set<TileMapEditorTerrainsPlugin::Constraint>();
+ // Run WFC to fill the holes with the constraints.
+ Map<Vector2i, TileSet::TerrainsPattern> wfc_output = tile_map->terrain_wave_function_collapse(to_replace, p_terrain_set, constraints);
+
+ // Actually paint the tiles.
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E_to_paint : p_to_paint) {
+ output[E_to_paint.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E_to_paint.value);
}
- // Compute the constraints needed from the surrounding tiles.
- Set<TileMapEditorTerrainsPlugin::Constraint> output;
- int in_pattern_count = 0;
- for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(p_terrain_set, side)) {
- Constraint c = Constraint(tile_map, p_position, side, p_terrains_tile_pattern[in_pattern_count]);
- output.insert(c);
- in_pattern_count++;
- }
+ // Use the WFC run for the output.
+ for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &E : wfc_output) {
+ output[E.key] = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, E.value);
}
return output;
}
-Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern> TileMapEditorTerrainsPlugin::_wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const {
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
- return Map<Vector2i, TerrainsTilePattern>();
+ return Map<Vector2i, TileMapCell>();
}
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
- return Map<Vector2i, TileMapEditorTerrainsPlugin::TerrainsTilePattern>();
+ return Map<Vector2i, TileMapCell>();
}
- // Copy the constraints set.
- Set<TileMapEditorTerrainsPlugin::Constraint> constraints = p_constraints;
-
- // Compute all acceptable tiles for each cell.
- Map<Vector2i, Set<TerrainsTilePattern>> per_cell_acceptable_tiles;
- for (Set<Vector2i>::Element *E = p_to_replace.front(); E; E = E->next()) {
- per_cell_acceptable_tiles[E->get()] = _get_valid_terrains_tile_patterns_for_constraints(p_terrain_set, E->get(), constraints);
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
}
- // Output map.
- Map<Vector2i, TerrainsTilePattern> output;
-
- // Add all positions to a set.
- Set<Vector2i> to_replace = Set<Vector2i>(p_to_replace);
- while (!to_replace.is_empty()) {
- // Compute the minimum number of tile possibilities for each cell.
- int min_nb_possibilities = 100000000;
- for (Map<Vector2i, Set<TerrainsTilePattern>>::Element *E = per_cell_acceptable_tiles.front(); E; E = E->next()) {
- min_nb_possibilities = MIN(min_nb_possibilities, E->get().size());
- }
-
- // Get the set of possible cells to fill.
- LocalVector<Vector2i> to_choose_from;
- for (Map<Vector2i, Set<TerrainsTilePattern>>::Element *E = per_cell_acceptable_tiles.front(); E; E = E->next()) {
- if (E->get().size() == min_nb_possibilities) {
- to_choose_from.push_back(E->key());
- }
- }
-
- // Randomly pick a tile out of the most constrained.
- Vector2i selected_cell_to_replace = to_choose_from[Math::random(0, to_choose_from.size() - 1)];
-
- // Randomly select a tile out of them the put it in the grid.
- Set<TerrainsTilePattern> valid_tiles = per_cell_acceptable_tiles[selected_cell_to_replace];
- if (valid_tiles.is_empty()) {
- // No possibilities :/
- break;
- }
- int random_terrain_tile_pattern_index = Math::random(0, valid_tiles.size() - 1);
- Set<TerrainsTilePattern>::Element *E = valid_tiles.front();
- for (int i = 0; i < random_terrain_tile_pattern_index; i++) {
- E = E->next();
- }
- TerrainsTilePattern selected_terrain_tile_pattern = E->get();
-
- // Set the selected cell into the output.
- output[selected_cell_to_replace] = selected_terrain_tile_pattern;
- to_replace.erase(selected_cell_to_replace);
- per_cell_acceptable_tiles.erase(selected_cell_to_replace);
-
- // Add the new constraints from the added tiles.
- Set<TileMapEditorTerrainsPlugin::Constraint> new_constraints = _get_constraints_from_added_tile(selected_cell_to_replace, p_terrain_set, selected_terrain_tile_pattern);
- for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E_constraint = new_constraints.front(); E_constraint; E_constraint = E_constraint->next()) {
- constraints.insert(E_constraint->get());
- }
-
- // Compute valid tiles again for neighbors.
- for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
- if (tile_map->is_existing_neighbor(side)) {
- Vector2i neighbor = tile_map->get_neighbor_cell(selected_cell_to_replace, side);
- if (to_replace.has(neighbor)) {
- per_cell_acceptable_tiles[neighbor] = _get_valid_terrains_tile_patterns_for_constraints(p_terrain_set, neighbor, constraints);
- }
- }
- }
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, p_start_cell, p_end_cell);
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
+ for (int i = 0; i < line.size(); i++) {
+ to_draw[line[i]] = terrains_pattern;
}
- return output;
+ return _draw_terrains(to_draw, selected_terrain_set);
}
-TileMapCell TileMapEditorTerrainsPlugin::_get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const {
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
- return TileMapCell();
+ return Map<Vector2i, TileMapCell>();
}
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
- return TileMapCell();
+ return Map<Vector2i, TileMapCell>();
}
- // Count the sum of probabilities.
- double sum = 0.0;
- Set<TileMapCell> set = per_terrain_terrains_tile_patterns_tiles[p_terrain_set][p_terrain_tile_pattern];
- for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
- if (E->get().source_id >= 0) {
- Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
-
- Ref<TileSetAtlasSource> atlas_source = source;
- if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
- sum += tile_data->get_probability();
- } else {
- sum += 1.0;
- }
- } else {
- sum += 1.0;
- }
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
}
- // Generate a random number.
- double count = 0.0;
- double picked = Math::random(0.0, sum);
-
- // Pick the tile.
- for (Set<TileMapCell>::Element *E = set.front(); E; E = E->next()) {
- if (E->get().source_id >= 0) {
- Ref<TileSetSource> source = tile_set->get_source(E->get().source_id);
+ Rect2i rect;
+ rect.set_position(p_start_cell);
+ rect.set_end(p_end_cell);
+ rect = rect.abs();
- Ref<TileSetAtlasSource> atlas_source = source;
- if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E->get().get_atlas_coords(), E->get().alternative_tile));
- count += tile_data->get_probability();
- } else {
- count += 1.0;
- }
- } else {
- count += 1.0;
- }
-
- if (count >= picked) {
- return E->get();
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
+ for (int x = rect.position.x; x <= rect.get_end().x; x++) {
+ for (int y = rect.position.y; y <= rect.get_end().y; y++) {
+ to_draw[Vector2i(x, y)] = terrains_pattern;
}
}
-
- ERR_FAIL_V(TileMapCell());
+ return _draw_terrains(to_draw, selected_terrain_set);
}
-Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const {
+Set<Vector2i> TileMapEditorTerrainsPlugin::_get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
- return Map<Vector2i, TileMapCell>();
+ return Set<Vector2i>();
}
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
- return Map<Vector2i, TileMapCell>();
+ return Set<Vector2i>();
}
- Map<Vector2i, TileMapCell> output;
+ TileMapCell source_cell = tile_map->get_cell(tile_map_layer, p_coords);
- // Add the constraints from the added tiles.
- Set<TileMapEditorTerrainsPlugin::Constraint> added_tiles_constraints_set;
- for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
- Vector2i coords = E_to_paint->key();
- TerrainsTilePattern terrains_tile_pattern = E_to_paint->get();
+ TileSet::TerrainsPattern source_pattern(*tile_set, selected_terrain_set);
+ if (source_cell.source_id != TileSet::INVALID_SOURCE) {
+ TileData *tile_data = nullptr;
+ Ref<TileSetSource> source = tile_set->get_source(source_cell.source_id);
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(source_cell.get_atlas_coords(), source_cell.alternative_tile));
+ }
+ if (!tile_data) {
+ return Set<Vector2i>();
+ }
+ source_pattern = tile_data->get_terrains_pattern();
+ }
+
+ // If we are filling empty tiles, compute the tilemap boundaries.
+ Rect2i boundaries;
+ if (source_cell.source_id == TileSet::INVALID_SOURCE) {
+ boundaries = tile_map->get_used_rect();
+ }
+
+ Set<Vector2i> output;
+ if (p_contiguous) {
+ // Replace continuous tiles like the source.
+ Set<Vector2i> already_checked;
+ List<Vector2i> to_check;
+ to_check.push_back(p_coords);
+ while (!to_check.is_empty()) {
+ Vector2i coords = to_check.back()->get();
+ to_check.pop_back();
+ if (!already_checked.has(coords)) {
+ // Get the candidate cell pattern.
+ TileSet::TerrainsPattern candidate_pattern(*tile_set, selected_terrain_set);
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
+ TileData *tile_data = nullptr;
+ Ref<TileSetSource> source = tile_set->get_source(tile_map->get_cell_source_id(tile_map_layer, coords));
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)));
+ }
+ if (tile_data) {
+ candidate_pattern = tile_data->get_terrains_pattern();
+ }
+ }
- Set<TileMapEditorTerrainsPlugin::Constraint> cell_constraints = _get_constraints_from_added_tile(coords, p_terrain_set, terrains_tile_pattern);
- for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = cell_constraints.front(); E; E = E->next()) {
- added_tiles_constraints_set.insert(E->get());
- }
- }
+ // Draw.
+ if (candidate_pattern == source_pattern && (!source_pattern.is_erase_pattern() || boundaries.has_point(coords))) {
+ output.insert(coords);
- // Build the list of potential tiles to replace.
- Set<Vector2i> potential_to_replace;
- for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
- Vector2i coords = E_to_paint->key();
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- if (tile_map->is_existing_neighbor(TileSet::CellNeighbor(i))) {
- Vector2i neighbor = tile_map->get_neighbor_cell(coords, TileSet::CellNeighbor(i));
- if (!p_to_paint.has(neighbor)) {
- potential_to_replace.insert(neighbor);
+ // Get surrounding tiles (handles different tile shapes).
+ TypedArray<Vector2i> around = tile_map->get_surrounding_tiles(coords);
+ for (int i = 0; i < around.size(); i++) {
+ to_check.push_back(around[i]);
+ }
}
+ already_checked.insert(coords);
}
}
- }
-
- // Set of tiles to replace
- Set<Vector2i> to_replace;
-
- // Add the central tiles to the one to replace. TODO: maybe change that.
- for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
- to_replace.insert(E_to_paint->key());
- }
-
- // Add the constraints from the surroundings of the modified areas.
- Set<TileMapEditorTerrainsPlugin::Constraint> removed_cells_constraints_set;
- bool to_replace_modified = true;
- while (to_replace_modified) {
- // Get the constraints from the removed cells.
- removed_cells_constraints_set = _get_constraints_from_removed_cells_list(to_replace, p_terrain_set);
-
- // Filter the sources to make sure they are in the potential_to_replace.
- Map<Constraint, Set<Vector2i>> source_tiles_of_constraint;
- for (Set<Constraint>::Element *E = removed_cells_constraints_set.front(); E; E = E->next()) {
- Map<Vector2i, TileSet::CellNeighbor> sources_of_constraint = E->get().get_overlapping_coords_and_peering_bits();
- for (Map<Vector2i, TileSet::CellNeighbor>::Element *E_source_tile_of_constraint = sources_of_constraint.front(); E_source_tile_of_constraint; E_source_tile_of_constraint = E_source_tile_of_constraint->next()) {
- if (potential_to_replace.has(E_source_tile_of_constraint->key())) {
- source_tiles_of_constraint[E->get()].insert(E_source_tile_of_constraint->key());
+ } else {
+ // Replace all tiles like the source.
+ TypedArray<Vector2i> to_check;
+ if (source_cell.source_id == TileSet::INVALID_SOURCE) {
+ Rect2i rect = tile_map->get_used_rect();
+ if (rect.has_no_area()) {
+ rect = Rect2i(p_coords, Vector2i(1, 1));
+ }
+ for (int x = boundaries.position.x; x < boundaries.get_end().x; x++) {
+ for (int y = boundaries.position.y; y < boundaries.get_end().y; y++) {
+ to_check.append(Vector2i(x, y));
}
}
- }
-
- to_replace_modified = false;
- for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
- Constraint c = E->get();
- // Check if we have a conflict in constraints.
- if (removed_cells_constraints_set.has(c) && removed_cells_constraints_set.find(c)->get().get_terrain() != c.get_terrain()) {
- // If we do, we search for a neighbor to remove.
- if (source_tiles_of_constraint.has(c) && !source_tiles_of_constraint[c].is_empty()) {
- // Remove it.
- Vector2i to_add_to_remove = source_tiles_of_constraint[c].front()->get();
- potential_to_replace.erase(to_add_to_remove);
- to_replace.insert(to_add_to_remove);
- to_replace_modified = true;
- for (Map<Constraint, Set<Vector2i>>::Element *E_source_tiles_of_constraint = source_tiles_of_constraint.front(); E_source_tiles_of_constraint; E_source_tiles_of_constraint = E_source_tiles_of_constraint->next()) {
- E_source_tiles_of_constraint->get().erase(to_add_to_remove);
- }
- break;
+ } else {
+ to_check = tile_map->get_used_cells(tile_map_layer);
+ }
+ for (int i = 0; i < to_check.size(); i++) {
+ Vector2i coords = to_check[i];
+ // Get the candidate cell pattern.
+ TileSet::TerrainsPattern candidate_pattern;
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
+ TileData *tile_data = nullptr;
+ Ref<TileSetSource> source = tile_set->get_source(tile_map->get_cell_source_id(tile_map_layer, coords));
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_map->get_cell_atlas_coords(tile_map_layer, coords), tile_map->get_cell_alternative_tile(tile_map_layer, coords)));
+ }
+ if (tile_data) {
+ candidate_pattern = tile_data->get_terrains_pattern();
}
}
+
+ // Draw.
+ if (candidate_pattern == source_pattern && (!source_pattern.is_erase_pattern() || boundaries.has_point(coords))) {
+ output.insert(coords);
+ }
}
}
+ return output;
+}
- // Combine all constraints together.
- Set<TileMapEditorTerrainsPlugin::Constraint> constraints = removed_cells_constraints_set;
- for (Set<TileMapEditorTerrainsPlugin::Constraint>::Element *E = added_tiles_constraints_set.front(); E; E = E->next()) {
- constraints.insert(E->get());
+Map<Vector2i, TileMapCell> TileMapEditorTerrainsPlugin::_draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase) {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return Map<Vector2i, TileMapCell>();
}
- // Run WFC to fill the holes with the constraints.
- Map<Vector2i, TerrainsTilePattern> wfc_output = _wave_function_collapse(to_replace, p_terrain_set, constraints);
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return Map<Vector2i, TileMapCell>();
+ }
- // Use the WFC run for the output.
- for (Map<Vector2i, TerrainsTilePattern>::Element *E = wfc_output.front(); E; E = E->next()) {
- output[E->key()] = _get_random_tile_from_pattern(p_terrain_set, E->get());
+ TileSet::TerrainsPattern terrains_pattern;
+ if (p_erase) {
+ terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else {
+ terrains_pattern = selected_terrains_pattern;
}
- // Override the WFC results to make sure at least the painted tiles are actually painted.
- for (Map<Vector2i, TerrainsTilePattern>::Element *E_to_paint = p_to_paint.front(); E_to_paint; E_to_paint = E_to_paint->next()) {
- output[E_to_paint->key()] = _get_random_tile_from_pattern(p_terrain_set, E_to_paint->get());
+ Set<Vector2i> cells_to_draw = _get_cells_for_bucket_fill(p_coords, p_contiguous);
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
+ for (const Vector2i &coords : cells_to_draw) {
+ to_draw[coords] = terrains_pattern;
}
- return output;
+ return _draw_terrains(to_draw, selected_terrain_set);
}
void TileMapEditorTerrainsPlugin::_stop_dragging() {
@@ -2667,26 +2552,40 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
return;
}
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(CanvasItemEditor::get_singleton()->get_viewport_control()->get_local_mouse_position());
switch (drag_type) {
case DRAG_TYPE_PICK: {
Vector2i coords = tile_map->world_to_map(mpos);
- TileMapCell tile = tile_map->get_cell(tile_map_layer, coords);
+ TileMapCell cell = tile_map->get_cell(tile_map_layer, coords);
+ TileData *tile_data = nullptr;
+
+ Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
+ Ref<TileSetAtlasSource> atlas_source = source;
+ if (atlas_source.is_valid()) {
+ tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile));
+ }
- if (terrain_tiles.has(tile)) {
- Array terrains_tile_pattern = _build_terrains_tile_pattern(terrain_tiles[tile]);
+ if (tile_data) {
+ TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
// Find the tree item for the right terrain set.
bool need_tree_item_switch = true;
TreeItem *tree_item = terrains_tree->get_selected();
+ int new_terrain_set = -1;
if (tree_item) {
Dictionary metadata_dict = tree_item->get_metadata(0);
if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
int terrain_set = metadata_dict["terrain_set"];
int terrain_id = metadata_dict["terrain_id"];
- if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+ if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
+ new_terrain_set = terrain_set;
need_tree_item_switch = false;
}
}
@@ -2698,8 +2597,9 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
if (metadata_dict.has("terrain_set") && metadata_dict.has("terrain_id")) {
int terrain_set = metadata_dict["terrain_set"];
int terrain_id = metadata_dict["terrain_id"];
- if (per_terrain_terrains_tile_patterns[terrain_set][terrain_id].has(terrains_tile_pattern)) {
+ if (per_terrain_terrains_patterns[terrain_set][terrain_id].has(terrains_pattern)) {
// Found
+ new_terrain_set = terrain_set;
tree_item->select(0);
_update_tiles_list();
break;
@@ -2712,15 +2612,9 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
if (tree_item) {
for (int i = 0; i < terrains_tile_list->get_item_count(); i++) {
Dictionary metadata_dict = terrains_tile_list->get_item_metadata(i);
- TerrainsTilePattern in_meta_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
- bool equals = true;
- for (int j = 0; j < terrains_tile_pattern.size(); j++) {
- if (terrains_tile_pattern[j] != in_meta_terrains_tile_pattern[j]) {
- equals = false;
- break;
- }
- }
- if (equals) {
+ TileSet::TerrainsPattern in_meta_terrains_pattern(*tile_set, new_terrain_set);
+ in_meta_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
+ if (in_meta_terrains_pattern == terrains_pattern) {
terrains_tile_list->select(i);
break;
}
@@ -2733,20 +2627,90 @@ void TileMapEditorTerrainsPlugin::_stop_dragging() {
} break;
case DRAG_TYPE_PAINT: {
undo_redo->create_action(TTR("Paint terrain"));
- for (Map<Vector2i, TileMapCell>::Element *E = drag_modified.front(); E; E = E->next()) {
- undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E->key(), tile_map->get_cell_source_id(tile_map_layer, E->key()), tile_map->get_cell_atlas_coords(tile_map_layer, E->key()), tile_map->get_cell_alternative_tile(tile_map_layer, E->key()));
- undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
undo_redo->commit_action(false);
} break;
+ case DRAG_TYPE_LINE: {
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing);
+ undo_redo->create_action(TTR("Paint terrain"));
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
+ continue;
+ }
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ }
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_RECT: {
+ Map<Vector2i, TileMapCell> to_draw = _draw_rect(tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(mpos), drag_erasing);
+ undo_redo->create_action(TTR("Paint terrain"));
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
+ continue;
+ }
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ }
+ undo_redo->commit_action();
+ } break;
+ case DRAG_TYPE_BUCKET: {
+ undo_redo->create_action(TTR("Paint terrain"));
+ for (const KeyValue<Vector2i, TileMapCell> &E : drag_modified) {
+ undo_redo->add_do_method(tile_map, "set_cell", tile_map_layer, E.key, tile_map->get_cell_source_id(tile_map_layer, E.key), tile_map->get_cell_atlas_coords(tile_map_layer, E.key), tile_map->get_cell_alternative_tile(tile_map_layer, E.key));
+ undo_redo->add_undo_method(tile_map, "set_cell", tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ }
+ undo_redo->commit_action(false);
+ } break;
+
default:
break;
}
drag_type = DRAG_TYPE_NONE;
}
+void TileMapEditorTerrainsPlugin::_mouse_exited_viewport() {
+ has_mouse = false;
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+void TileMapEditorTerrainsPlugin::_update_selection() {
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (!tile_map) {
+ return;
+ }
+
+ Ref<TileSet> tile_set = tile_map->get_tileset();
+ if (!tile_set.is_valid()) {
+ return;
+ }
+
+ // Get the selected terrain.
+ selected_terrains_pattern = TileSet::TerrainsPattern();
+ selected_terrain_set = -1;
+
+ TreeItem *selected_tree_item = terrains_tree->get_selected();
+ if (selected_tree_item && selected_tree_item->get_metadata(0)) {
+ Dictionary metadata_dict = selected_tree_item->get_metadata(0);
+ // Selected terrain
+ selected_terrain_set = metadata_dict["terrain_set"];
+
+ // Selected tile
+ if (erase_button->is_pressed()) {
+ selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ } else if (terrains_tile_list->is_anything_selected()) {
+ metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
+ selected_terrains_pattern = TileSet::TerrainsPattern(*tile_set, selected_terrain_set);
+ selected_terrains_pattern.set_terrains_from_array(metadata_dict["terrains_pattern"]);
+ }
+ }
+}
+
bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
- if (!is_visible_in_tree()) {
+ if (!main_vbox_container->is_visible_in_tree()) {
// If the bottom editor is not visible, we ignore inputs.
return false;
}
@@ -2770,50 +2734,23 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
}
ERR_FAIL_COND_V(tile_map_layer >= tile_map->get_layers_count(), false);
- // Get the selected terrain.
- TerrainsTilePattern selected_terrains_tile_pattern;
- int selected_terrain_set = -1;
-
- TreeItem *selected_tree_item = terrains_tree->get_selected();
- if (selected_tree_item && selected_tree_item->get_metadata(0)) {
- Dictionary metadata_dict = selected_tree_item->get_metadata(0);
- // Selected terrain
- selected_terrain_set = metadata_dict["terrain_set"];
-
- // Selected tile
- if (erase_button->is_pressed()) {
- selected_terrains_tile_pattern.clear();
- for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- TileSet::CellNeighbor side = TileSet::CellNeighbor(i);
- if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, side)) {
- selected_terrains_tile_pattern.push_back(-1);
- }
- }
- } else if (terrains_tile_list->is_anything_selected()) {
- metadata_dict = terrains_tile_list->get_item_metadata(terrains_tile_list->get_selected_items()[0]);
- selected_terrains_tile_pattern = metadata_dict["terrains_tile_pattern"];
- }
- }
+ _update_selection();
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
+ has_mouse = true;
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(mm->get_position());
switch (drag_type) {
case DRAG_TYPE_PAINT: {
if (selected_terrain_set >= 0) {
- Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
- Map<Vector2i, TerrainsTilePattern> to_draw;
- for (int i = 0; i < line.size(); i++) {
- to_draw[line[i]] = selected_terrains_tile_pattern;
- }
- Map<Vector2i, TileMapCell> modified = _draw_terrains(to_draw, selected_terrain_set);
- for (Map<Vector2i, TileMapCell>::Element *E = modified.front(); E; E = E->next()) {
- if (!drag_modified.has(E->key())) {
- drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key());
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos), drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_modified.has(E.key)) {
+ drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
}
- tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
}
}
} break;
@@ -2828,35 +2765,79 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
+ has_mouse = true;
Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
Vector2 mpos = xform.affine_inverse().xform(mb->get_position());
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT || mb->get_button_index() == MouseButton::RIGHT) {
if (mb->is_pressed()) {
// Pressed
+ if (erase_button->is_pressed() || mb->get_button_index() == MouseButton::RIGHT) {
+ drag_erasing = true;
+ }
+
if (picker_button->is_pressed()) {
drag_type = DRAG_TYPE_PICK;
} else {
// Paint otherwise.
- if (selected_terrain_set >= 0 && !selected_terrains_tile_pattern.is_empty() && tool_buttons_group->get_pressed_button() == paint_tool_button) {
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && !Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
+ if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ return true;
+ }
+
drag_type = DRAG_TYPE_PAINT;
drag_start_mouse_pos = mpos;
drag_modified.clear();
-
- Map<Vector2i, TerrainsTilePattern> terrains_to_draw;
- terrains_to_draw[tile_map->world_to_map(mpos)] = selected_terrains_tile_pattern;
-
- Map<Vector2i, TileMapCell> to_draw = _draw_terrains(terrains_to_draw, selected_terrain_set);
- for (Map<Vector2i, TileMapCell>::Element *E = to_draw.front(); E; E = E->next()) {
- drag_modified[E->key()] = tile_map->get_cell(tile_map_layer, E->key());
- tile_map->set_cell(tile_map_layer, E->key(), E->get().source_id, E->get().get_atlas_coords(), E->get().alternative_tile);
+ Vector2i cell = tile_map->world_to_map(mpos);
+ Map<Vector2i, TileMapCell> to_draw = _draw_line(cell, cell, drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ drag_modified[E.key] = tile_map->get_cell(tile_map_layer, E.key);
+ tile_map->set_cell(tile_map_layer, E.key, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ }
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && !Input::get_singleton()->is_key_pressed(Key::CTRL))) {
+ if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ return true;
+ }
+ drag_type = DRAG_TYPE_LINE;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ } else if (tool_buttons_group->get_pressed_button() == rect_tool_button || (tool_buttons_group->get_pressed_button() == paint_tool_button && Input::get_singleton()->is_key_pressed(Key::SHIFT) && Input::get_singleton()->is_key_pressed(Key::CTRL))) {
+ if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ return true;
+ }
+ drag_type = DRAG_TYPE_RECT;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button) {
+ if (selected_terrain_set < 0 || !selected_terrains_pattern.is_valid()) {
+ return true;
+ }
+ drag_type = DRAG_TYPE_BUCKET;
+ drag_start_mouse_pos = mpos;
+ drag_modified.clear();
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_last_mouse_pos), tile_map->world_to_map(mpos));
+ for (int i = 0; i < line.size(); i++) {
+ if (!drag_modified.has(line[i])) {
+ Map<Vector2i, TileMapCell> to_draw = _draw_bucket_fill(line[i], bucket_contiguous_checkbox->is_pressed(), drag_erasing);
+ for (const KeyValue<Vector2i, TileMapCell> &E : to_draw) {
+ if (!drag_erasing && E.value.source_id == TileSet::INVALID_SOURCE) {
+ continue;
+ }
+ Vector2i coords = E.key;
+ if (!drag_modified.has(coords)) {
+ drag_modified.insert(coords, tile_map->get_cell(tile_map_layer, coords));
+ }
+ tile_map->set_cell(tile_map_layer, coords, E.value.source_id, E.value.get_atlas_coords(), E.value.alternative_tile);
+ }
+ }
}
}
}
} else {
// Released
_stop_dragging();
+ drag_erasing = false;
}
CanvasItemEditor::get_singleton()->update_viewport();
@@ -2869,24 +2850,133 @@ bool TileMapEditorTerrainsPlugin::forward_canvas_gui_input(const Ref<InputEvent>
return false;
}
-TileMapEditorTerrainsPlugin::TerrainsTilePattern TileMapEditorTerrainsPlugin::_build_terrains_tile_pattern(TileData *p_tile_data) {
+void TileMapEditorTerrainsPlugin::forward_canvas_draw_over_viewport(Control *p_overlay) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (!tile_map) {
- return TerrainsTilePattern();
+ return;
}
+ if (tile_map_layer < 0) {
+ return;
+ }
+ ERR_FAIL_INDEX(tile_map_layer, tile_map->get_layers_count());
+
Ref<TileSet> tile_set = tile_map->get_tileset();
if (!tile_set.is_valid()) {
- return TerrainsTilePattern();
+ return;
}
- TerrainsTilePattern output;
- for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
- if (tile_set->is_valid_peering_bit_terrain(p_tile_data->get_terrain_set(), TileSet::CellNeighbor(i))) {
- output.push_back(p_tile_data->get_peering_bit_terrain(TileSet::CellNeighbor(i)));
+ if (!tile_map->is_visible_in_tree()) {
+ return;
+ }
+
+ Transform2D xform = CanvasItemEditor::get_singleton()->get_canvas_transform() * tile_map->get_global_transform();
+ Vector2i tile_shape_size = tile_set->get_tile_size();
+
+ // Handle the preview of the tiles to be placed.
+ if (main_vbox_container->is_visible_in_tree() && has_mouse) { // Only if the tilemap editor is opened and the viewport is hovered.
+ Set<Vector2i> preview;
+ Rect2i drawn_grid_rect;
+
+ if (drag_type == DRAG_TYPE_PICK) {
+ // Draw the area being picked.
+ Vector2i coords = tile_map->world_to_map(drag_last_mouse_pos);
+ if (tile_map->get_cell_source_id(tile_map_layer, coords) != TileSet::INVALID_SOURCE) {
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(coords));
+ tile_xform.set_scale(tile_shape_size);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0), false);
+ }
+ } else if (!picker_button->is_pressed() && !(drag_type == DRAG_TYPE_NONE && Input::get_singleton()->is_key_pressed(Key::CTRL) && !Input::get_singleton()->is_key_pressed(Key::SHIFT))) {
+ bool expand_grid = false;
+ if (tool_buttons_group->get_pressed_button() == paint_tool_button && drag_type == DRAG_TYPE_NONE) {
+ // Preview for a single tile.
+ preview.insert(tile_map->world_to_map(drag_last_mouse_pos));
+ expand_grid = true;
+ } else if (tool_buttons_group->get_pressed_button() == line_tool_button || drag_type == DRAG_TYPE_LINE) {
+ if (drag_type == DRAG_TYPE_NONE) {
+ // Preview for a single tile.
+ preview.insert(tile_map->world_to_map(drag_last_mouse_pos));
+ } else if (drag_type == DRAG_TYPE_LINE) {
+ // Preview for a line.
+ Vector<Vector2i> line = TileMapEditor::get_line(tile_map, tile_map->world_to_map(drag_start_mouse_pos), tile_map->world_to_map(drag_last_mouse_pos));
+ for (int i = 0; i < line.size(); i++) {
+ preview.insert(line[i]);
+ }
+ expand_grid = true;
+ }
+ } else if (drag_type == DRAG_TYPE_RECT) {
+ // Preview for a rect.
+ Rect2i rect;
+ rect.set_position(tile_map->world_to_map(drag_start_mouse_pos));
+ rect.set_end(tile_map->world_to_map(drag_last_mouse_pos));
+ rect = rect.abs();
+
+ Map<Vector2i, TileSet::TerrainsPattern> to_draw;
+ for (int x = rect.position.x; x <= rect.get_end().x; x++) {
+ for (int y = rect.position.y; y <= rect.get_end().y; y++) {
+ preview.insert(Vector2i(x, y));
+ }
+ }
+ expand_grid = true;
+ } else if (tool_buttons_group->get_pressed_button() == bucket_tool_button && drag_type == DRAG_TYPE_NONE) {
+ // Preview for a fill.
+ preview = _get_cells_for_bucket_fill(tile_map->world_to_map(drag_last_mouse_pos), bucket_contiguous_checkbox->is_pressed());
+ }
+
+ // Expand the grid if needed
+ if (expand_grid && !preview.is_empty()) {
+ drawn_grid_rect = Rect2i(preview.front()->get(), Vector2i(1, 1));
+ for (const Vector2i &E : preview) {
+ drawn_grid_rect.expand_to(E);
+ }
+ }
+ }
+
+ if (!preview.is_empty()) {
+ const int fading = 5;
+
+ // Draw the lines of the grid behind the preview.
+ bool display_grid = EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid");
+ if (display_grid) {
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
+ if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) {
+ drawn_grid_rect = drawn_grid_rect.grow(fading);
+ for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) {
+ for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) {
+ Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position;
+
+ // Fade out the border of the grid.
+ float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
+
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(Vector2(x, y)));
+ tile_xform.set_scale(tile_shape_size);
+ Color color = grid_color;
+ color.a = color.a * opacity;
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
+ }
+ }
+ }
+ }
+
+ // Draw the preview.
+ for (const Vector2i &E : preview) {
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(E));
+ tile_xform.set_scale(tile_set->get_tile_size());
+ if (drag_erasing || erase_button->is_pressed()) {
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(0.0, 0.0, 0.0, 0.5), true);
+ } else {
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, Color(1.0, 1.0, 1.0, 0.5), true);
+ }
+ }
}
}
- return output;
}
void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
@@ -2900,45 +2990,12 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
return;
}
- // Compute the tile sides.
- tile_sides.clear();
- TileSet::TileShape shape = tile_set->get_tile_shape();
- if (shape == TileSet::TILE_SHAPE_SQUARE) {
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_SIDE);
- } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) {
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- } else {
- if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) {
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- } else {
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_SIDE);
- tile_sides.push_back(TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE);
- }
- }
-
// Organizes tiles into structures.
- per_terrain_terrains_tile_patterns_tiles.resize(tile_set->get_terrain_sets_count());
- per_terrain_terrains_tile_patterns.resize(tile_set->get_terrain_sets_count());
+ per_terrain_terrains_patterns.resize(tile_set->get_terrain_sets_count());
for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
- per_terrain_terrains_tile_patterns_tiles[i].clear();
- per_terrain_terrains_tile_patterns[i].resize(tile_set->get_terrains_count(i));
- for (int j = 0; j < (int)per_terrain_terrains_tile_patterns[i].size(); j++) {
- per_terrain_terrains_tile_patterns[i][j].clear();
+ per_terrain_terrains_patterns[i].resize(tile_set->get_terrains_count(i));
+ for (int j = 0; j < (int)per_terrain_terrains_patterns[i].size(); j++) {
+ per_terrain_terrains_patterns[i][j].clear();
}
}
@@ -2956,22 +3013,22 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(tile_id, alternative_id));
int terrain_set = tile_data->get_terrain_set();
if (terrain_set >= 0) {
- ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_tile_patterns.size());
+ ERR_FAIL_INDEX(terrain_set, (int)per_terrain_terrains_patterns.size());
TileMapCell cell;
cell.source_id = source_id;
cell.set_atlas_coords(tile_id);
cell.alternative_tile = alternative_id;
- TerrainsTilePattern terrains_tile_pattern = _build_terrains_tile_pattern(tile_data);
-
+ TileSet::TerrainsPattern terrains_pattern = tile_data->get_terrains_pattern();
// Terrain bits.
- for (int i = 0; i < terrains_tile_pattern.size(); i++) {
- int terrain = terrains_tile_pattern[i];
- if (terrain >= 0 && terrain < (int)per_terrain_terrains_tile_patterns[terrain_set].size()) {
- per_terrain_terrains_tile_patterns[terrain_set][terrain].insert(terrains_tile_pattern);
- terrain_tiles[cell] = tile_data;
- per_terrain_terrains_tile_patterns_tiles[terrain_set][terrains_tile_pattern].insert(cell);
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(terrain_set, bit)) {
+ int terrain = terrains_pattern.get_terrain(bit);
+ if (terrain >= 0 && terrain < (int)per_terrain_terrains_patterns[terrain_set].size()) {
+ per_terrain_terrains_patterns[terrain_set][terrain].insert(terrains_pattern);
+ }
}
}
}
@@ -2979,22 +3036,6 @@ void TileMapEditorTerrainsPlugin::_update_terrains_cache() {
}
}
}
-
- // Add the empty cell in the possible patterns and cells.
- for (int i = 0; i < tile_set->get_terrain_sets_count(); i++) {
- TerrainsTilePattern empty_pattern;
- for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) {
- if (tile_set->is_valid_peering_bit_terrain(i, TileSet::CellNeighbor(j))) {
- empty_pattern.push_back(-1);
- }
- }
-
- TileMapCell empty_cell;
- empty_cell.source_id = TileSet::INVALID_SOURCE;
- empty_cell.set_atlas_coords(TileSetSource::INVALID_ATLAS_COORDS);
- empty_cell.alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE;
- per_terrain_terrains_tile_patterns_tiles[i][empty_pattern].insert(empty_cell);
- }
}
void TileMapEditorTerrainsPlugin::_update_terrains_tree() {
@@ -3018,13 +3059,13 @@ void TileMapEditorTerrainsPlugin::_update_terrains_tree() {
TreeItem *terrain_set_tree_item = terrains_tree->create_item();
String matches;
if (tile_set->get_terrain_set_mode(terrain_set_index) == TileSet::TERRAIN_MODE_MATCH_CORNERS_AND_SIDES) {
- terrain_set_tree_item->set_icon(0, get_theme_icon(SNAME("TerrainMatchCornersAndSides"), SNAME("EditorIcons")));
+ terrain_set_tree_item->set_icon(0, main_vbox_container->get_theme_icon(SNAME("TerrainMatchCornersAndSides"), SNAME("EditorIcons")));
matches = String(TTR("Matches Corners and Sides"));
} else if (tile_set->get_terrain_set_mode(terrain_set_index) == TileSet::TERRAIN_MODE_MATCH_CORNERS) {
- terrain_set_tree_item->set_icon(0, get_theme_icon(SNAME("TerrainMatchCorners"), SNAME("EditorIcons")));
+ terrain_set_tree_item->set_icon(0, main_vbox_container->get_theme_icon(SNAME("TerrainMatchCorners"), SNAME("EditorIcons")));
matches = String(TTR("Matches Corners Only"));
} else {
- terrain_set_tree_item->set_icon(0, get_theme_icon(SNAME("TerrainMatchSides"), SNAME("EditorIcons")));
+ terrain_set_tree_item->set_icon(0, main_vbox_container->get_theme_icon(SNAME("TerrainMatchSides"), SNAME("EditorIcons")));
matches = String(TTR("Matches Sides Only"));
}
terrain_set_tree_item->set_text(0, vformat("Terrain Set %d (%s)", terrain_set_index, matches));
@@ -3063,26 +3104,28 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
Dictionary metadata_dict = selected_tree_item->get_metadata(0);
int selected_terrain_set = metadata_dict["terrain_set"];
int selected_terrain_id = metadata_dict["terrain_id"];
- ERR_FAIL_INDEX(selected_terrain_set, (int)per_terrain_terrains_tile_patterns.size());
- ERR_FAIL_INDEX(selected_terrain_id, (int)per_terrain_terrains_tile_patterns[selected_terrain_set].size());
+ ERR_FAIL_INDEX(selected_terrain_set, tile_set->get_terrain_sets_count());
+ ERR_FAIL_INDEX(selected_terrain_id, tile_set->get_terrains_count(selected_terrain_set));
// Sort the items in a map by the number of corresponding terrains.
- Map<int, Set<TerrainsTilePattern>> sorted;
- for (Set<TerrainsTilePattern>::Element *E = per_terrain_terrains_tile_patterns[selected_terrain_set][selected_terrain_id].front(); E; E = E->next()) {
+ Map<int, Set<TileSet::TerrainsPattern>> sorted;
+
+ for (Set<TileSet::TerrainsPattern>::Element *E = per_terrain_terrains_patterns[selected_terrain_set][selected_terrain_id].front(); E; E = E->next()) {
// Count the number of matching sides/terrains.
int count = 0;
- for (int i = 0; i < E->get().size(); i++) {
- if (int(E->get()[i]) == selected_terrain_id) {
+ for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
+ if (tile_set->is_valid_peering_bit_terrain(selected_terrain_set, bit) && E->get().get_terrain(bit) == selected_terrain_id) {
count++;
}
}
sorted[count].insert(E->get());
}
- for (Map<int, Set<TerrainsTilePattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) {
- for (Set<TerrainsTilePattern>::Element *E = E_set->get().front(); E; E = E->next()) {
- TerrainsTilePattern terrains_tile_pattern = E->get();
+ for (Map<int, Set<TileSet::TerrainsPattern>>::Element *E_set = sorted.back(); E_set; E_set = E_set->prev()) {
+ for (Set<TileSet::TerrainsPattern>::Element *E = E_set->get().front(); E; E = E->next()) {
+ TileSet::TerrainsPattern terrains_pattern = E->get();
// Get the icon.
Ref<Texture2D> icon;
@@ -3090,15 +3133,15 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
bool transpose = false;
double max_probability = -1.0;
- for (Set<TileMapCell>::Element *E_tile_map_cell = per_terrain_terrains_tile_patterns_tiles[selected_terrain_set][terrains_tile_pattern].front(); E_tile_map_cell; E_tile_map_cell = E_tile_map_cell->next()) {
- Ref<TileSetSource> source = tile_set->get_source(E_tile_map_cell->get().source_id);
+ for (const TileMapCell &cell : tile_set->get_tiles_for_terrains_pattern(selected_terrain_set, terrains_pattern)) {
+ Ref<TileSetSource> source = tile_set->get_source(cell.source_id);
Ref<TileSetAtlasSource> atlas_source = source;
if (atlas_source.is_valid()) {
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(E_tile_map_cell->get().get_atlas_coords(), E_tile_map_cell->get().alternative_tile));
+ TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile));
if (tile_data->get_probability() > max_probability) {
icon = atlas_source->get_texture();
- region = atlas_source->get_tile_texture_region(E_tile_map_cell->get().get_atlas_coords());
+ region = atlas_source->get_tile_texture_region(cell.get_atlas_coords());
if (tile_data->get_flip_h()) {
region.position.x += region.size.x;
region.size.x = -region.size.x;
@@ -3119,7 +3162,7 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
terrains_tile_list->set_item_icon_region(item_index, region);
terrains_tile_list->set_item_icon_transposed(item_index, transpose);
Dictionary list_metadata_dict;
- list_metadata_dict["terrains_tile_pattern"] = terrains_tile_pattern;
+ list_metadata_dict["terrains_pattern"] = terrains_pattern.get_terrains_as_array();
terrains_tile_list->set_item_metadata(item_index, list_metadata_dict);
}
}
@@ -3129,6 +3172,16 @@ void TileMapEditorTerrainsPlugin::_update_tiles_list() {
}
}
+void TileMapEditorTerrainsPlugin::_update_theme() {
+ paint_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Edit"), SNAME("EditorIcons")));
+ line_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")));
+ rect_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Rectangle"), SNAME("EditorIcons")));
+ bucket_tool_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Bucket"), SNAME("EditorIcons")));
+
+ picker_button->set_icon(main_vbox_container->get_theme_icon(SNAME("ColorPick"), SNAME("EditorIcons")));
+ erase_button->set_icon(main_vbox_container->get_theme_icon(SNAME("Eraser"), SNAME("EditorIcons")));
+}
+
void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_layer) {
_stop_dragging(); // Avoids staying in a wrong drag state.
@@ -3141,15 +3194,18 @@ void TileMapEditorTerrainsPlugin::edit(ObjectID p_tile_map_id, int p_tile_map_la
}
TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
- set_name("Terrains");
+ main_vbox_container = memnew(VBoxContainer);
+ main_vbox_container->connect("tree_entered", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme));
+ main_vbox_container->connect("theme_changed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_theme));
+ main_vbox_container->set_name("Terrains");
HSplitContainer *tilemap_tab_terrains = memnew(HSplitContainer);
- tilemap_tab_terrains->set_h_size_flags(SIZE_EXPAND_FILL);
- tilemap_tab_terrains->set_v_size_flags(SIZE_EXPAND_FILL);
- add_child(tilemap_tab_terrains);
+ tilemap_tab_terrains->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tilemap_tab_terrains->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ main_vbox_container->add_child(tilemap_tab_terrains);
terrains_tree = memnew(Tree);
- terrains_tree->set_h_size_flags(SIZE_EXPAND_FILL);
+ terrains_tree->set_h_size_flags(Control::SIZE_EXPAND_FILL);
terrains_tree->set_stretch_ratio(0.25);
terrains_tree->set_custom_minimum_size(Size2i(70, 0) * EDSCALE);
terrains_tree->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
@@ -3158,7 +3214,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
tilemap_tab_terrains->add_child(terrains_tree);
terrains_tile_list = memnew(ItemList);
- terrains_tile_list->set_h_size_flags(SIZE_EXPAND_FILL);
+ terrains_tile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
terrains_tile_list->set_max_columns(0);
terrains_tile_list->set_same_column_width(true);
terrains_tile_list->set_fixed_icon_size(Size2(30, 30) * EDSCALE);
@@ -3177,10 +3233,34 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
paint_tool_button->set_toggle_mode(true);
paint_tool_button->set_button_group(tool_buttons_group);
paint_tool_button->set_pressed(true);
- paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", KEY_E));
+ paint_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/paint_tool", "Paint", Key::D));
paint_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(paint_tool_button);
+ line_tool_button = memnew(Button);
+ line_tool_button->set_flat(true);
+ line_tool_button->set_toggle_mode(true);
+ line_tool_button->set_button_group(tool_buttons_group);
+ line_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/line_tool", "Line", Key::L));
+ line_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(line_tool_button);
+
+ rect_tool_button = memnew(Button);
+ rect_tool_button->set_flat(true);
+ rect_tool_button->set_toggle_mode(true);
+ rect_tool_button->set_button_group(tool_buttons_group);
+ rect_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/rect_tool", "Rect", Key::R));
+ rect_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(rect_tool_button);
+
+ bucket_tool_button = memnew(Button);
+ bucket_tool_button->set_flat(true);
+ bucket_tool_button->set_toggle_mode(true);
+ bucket_tool_button->set_button_group(tool_buttons_group);
+ bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", "Bucket", Key::B));
+ bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTerrainsPlugin::_update_toolbar));
+ tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
+
toolbar->add_child(tilemap_tiles_tools_buttons);
// -- TileMap tool settings --
@@ -3194,7 +3274,7 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
picker_button = memnew(Button);
picker_button->set_flat(true);
picker_button->set_toggle_mode(true);
- picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", KEY_P));
+ picker_button->set_shortcut(ED_SHORTCUT("tiles_editor/picker", "Picker", Key::P));
picker_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(picker_button);
@@ -3202,9 +3282,20 @@ TileMapEditorTerrainsPlugin::TileMapEditorTerrainsPlugin() {
erase_button = memnew(Button);
erase_button->set_flat(true);
erase_button->set_toggle_mode(true);
- erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", KEY_E));
+ erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", Key::E));
erase_button->connect("pressed", callable_mp(CanvasItemEditor::get_singleton(), &CanvasItemEditor::update_viewport));
tools_settings->add_child(erase_button);
+
+ // Separator 2.
+ tools_settings_vsep_2 = memnew(VSeparator);
+ tools_settings->add_child(tools_settings_vsep_2);
+
+ // Continuous checkbox.
+ bucket_contiguous_checkbox = memnew(CheckBox);
+ bucket_contiguous_checkbox->set_flat(true);
+ bucket_contiguous_checkbox->set_text(TTR("Contiguous"));
+ bucket_contiguous_checkbox->set_pressed(true);
+ tools_settings->add_child(bucket_contiguous_checkbox);
}
TileMapEditorTerrainsPlugin::~TileMapEditorTerrainsPlugin() {
@@ -3225,7 +3316,7 @@ void TileMapEditor::_notification(int p_what) {
if (is_visible_in_tree() && tileset_changed_needs_update) {
_update_bottom_panel();
_update_layers_selection();
- tile_map_editor_plugins[tabs->get_current_tab()]->tile_set_changed();
+ tabs_plugins[tabs_bar->get_current_tab()]->tile_set_changed();
CanvasItemEditor::get_singleton()->update_viewport();
tileset_changed_needs_update = false;
}
@@ -3271,7 +3362,11 @@ void TileMapEditor::_layers_selection_button_draw() {
clr = get_theme_color(SNAME("font_disabled_color"));
break;
default:
- clr = get_theme_color(SNAME("font_color"));
+ if (layers_selection_button->has_focus()) {
+ clr = get_theme_color(SNAME("font_focus_color"));
+ } else {
+ clr = get_theme_color(SNAME("font_color"));
+ }
}
}
@@ -3349,14 +3444,11 @@ void TileMapEditor::_update_bottom_panel() {
// Update the visibility of controls.
missing_tileset_label->set_visible(!tile_set.is_valid());
- if (!tile_set.is_valid()) {
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->hide();
- }
- } else {
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->set_visible(i == tabs->get_current_tab());
- }
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ tabs_data[tab_index].panel->hide();
+ }
+ if (tile_set.is_valid()) {
+ tabs_data[tabs_bar->get_current_tab()].panel->show();
}
}
@@ -3439,27 +3531,25 @@ void TileMapEditor::_tile_map_changed() {
void TileMapEditor::_tab_changed(int p_tab_id) {
// Make the plugin edit the correct tilemap.
- tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
+ tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
// Update toolbar.
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->get_toolbar()->set_visible(i == p_tab_id);
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ tabs_data[tab_index].toolbar->hide();
}
+ tabs_data[p_tab_id].toolbar->show();
// Update visible panel.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map || !tile_map->get_tileset().is_valid()) {
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->hide();
- }
- } else {
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->set_visible(i == tabs->get_current_tab());
- }
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ tabs_data[tab_index].panel->hide();
+ }
+ if (tile_map && tile_map->get_tileset().is_valid()) {
+ tabs_data[tabs_bar->get_current_tab()].panel->show();
}
// Graphical update.
- tile_map_editor_plugins[tabs->get_current_tab()]->update();
+ tabs_data[tabs_bar->get_current_tab()].panel->update();
CanvasItemEditor::get_singleton()->update_viewport();
}
@@ -3546,33 +3636,79 @@ void TileMapEditor::_update_layers_selection() {
layers_selection_button->set_custom_minimum_size(min_button_size);
layers_selection_button->update();
- tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
+ tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
}
-void TileMapEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
+void TileMapEditor::_move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
ERR_FAIL_COND(!undo_redo);
TileMap *tile_map = Object::cast_to<TileMap>(p_edited);
- if (tile_map) {
- if (p_property == "layers_count") {
- int new_layers_count = (int)p_new_value;
- if (new_layers_count < tile_map->get_layers_count()) {
- List<PropertyInfo> property_list;
- tile_map->get_property_list(&property_list);
-
- for (PropertyInfo property_info : property_list) {
- Vector<String> components = String(property_info.name).split("/", true, 2);
- if (components.size() == 2 && components[0].begins_with("layer_") && components[0].trim_prefix("layer_").is_valid_int()) {
- int index = components[0].trim_prefix("layer_").to_int();
- if (index >= new_layers_count) {
- undo_redo->add_undo_property(tile_map, property_info.name, tile_map->get(property_info.name));
- }
- }
+ if (!tile_map) {
+ return;
+ }
+
+ // Compute the array indices to save.
+ int begin = 0;
+ int end;
+ if (p_array_prefix == "layer_") {
+ end = tile_map->get_layers_count();
+ } else {
+ ERR_FAIL_MSG("Invalid array prefix for TileSet.");
+ }
+ if (p_from_index < 0) {
+ // Adding new.
+ if (p_to_pos >= 0) {
+ begin = p_to_pos;
+ } else {
+ end = 0; // Nothing to save when adding at the end.
+ }
+ } else if (p_to_pos < 0) {
+ // Removing.
+ begin = p_from_index;
+ } else {
+ // Moving.
+ begin = MIN(p_from_index, p_to_pos);
+ end = MIN(MAX(p_from_index, p_to_pos) + 1, end);
+ }
+
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
+ // Save layers' properties.
+ if (p_from_index < 0) {
+ undo_redo->add_undo_method(tile_map, "remove_layer", p_to_pos < 0 ? tile_map->get_layers_count() : p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_undo_method(tile_map, "add_layer", p_from_index);
+ }
+
+ List<PropertyInfo> properties;
+ tile_map->get_property_list(&properties);
+ for (PropertyInfo pi : properties) {
+ if (pi.name.begins_with(p_array_prefix)) {
+ String str = pi.name.trim_prefix(p_array_prefix);
+ int to_char_index = 0;
+ while (to_char_index < str.length()) {
+ if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ break;
+ }
+ to_char_index++;
+ }
+ if (to_char_index > 0) {
+ int array_index = str.left(to_char_index).to_int();
+ if (array_index >= begin && array_index < end) {
+ ADD_UNDO(tile_map, pi.name);
}
}
}
}
+#undef ADD_UNDO
+
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_map, "add_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_map, "remove_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_map, "move_layer", p_from_index, p_to_pos);
+ }
}
bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
@@ -3586,7 +3722,7 @@ bool TileMapEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
return true;
}
- return tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_gui_input(p_event);
+ return tabs_plugins[tabs_bar->get_current_tab()]->forward_canvas_gui_input(p_event);
}
void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
@@ -3643,12 +3779,14 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
0.8);
// Draw the scaled tile.
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(coords) - Vector2(tile_shape_size) / 2, Vector2(tile_shape_size)));
- tile_set->draw_tile_shape(p_overlay, cell_region, color, true, warning_pattern_texture);
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(coords));
+ tile_xform.set_scale(tile_shape_size);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, true, warning_pattern_texture);
}
// Draw the warning icon.
- int min_axis = missing_tile_texture->get_size().min_axis();
+ Vector2::Axis min_axis = missing_tile_texture->get_size().min_axis_index();
Vector2 icon_size;
icon_size[min_axis] = tile_set->get_tile_size()[min_axis] / 3;
icon_size[(min_axis + 1) % 2] = (icon_size[min_axis] * missing_tile_texture->get_size()[(min_axis + 1) % 2] / missing_tile_texture->get_size()[min_axis]);
@@ -3700,10 +3838,12 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
+ Transform2D tile_xform;
+ tile_xform.set_origin(tile_map->map_to_world(Vector2(x, y)));
+ tile_xform.set_scale(tile_shape_size);
Color color = grid_color;
color.a = color.a * opacity;
- tile_set->draw_tile_shape(p_overlay, cell_region, color, false);
+ tile_set->draw_tile_shape(p_overlay, xform * tile_xform, color, false);
}
}
}
@@ -3717,7 +3857,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
}*/
// Draw the plugins.
- tile_map_editor_plugins[tabs->get_current_tab()]->forward_canvas_draw_over_viewport(p_overlay);
+ tabs_plugins[tabs_bar->get_current_tab()]->forward_canvas_draw_over_viewport(p_overlay);
}
void TileMapEditor::edit(TileMap *p_tile_map) {
@@ -3751,7 +3891,7 @@ void TileMapEditor::edit(TileMap *p_tile_map) {
_update_layers_selection();
// Call the plugins.
- tile_map_editor_plugins[tabs->get_current_tab()]->edit(tile_map_id, tile_map_layer);
+ tabs_plugins[tabs_bar->get_current_tab()]->edit(tile_map_id, tile_map_layer);
_tile_map_changed();
}
@@ -3760,32 +3900,40 @@ TileMapEditor::TileMapEditor() {
set_process_internal(true);
// Shortcuts.
- ED_SHORTCUT("tiles_editor/select_next_layer", TTR("Select Next Tile Map Layer"), KEY_PAGEUP);
- ED_SHORTCUT("tiles_editor/select_previous_layer", TTR("Select Previous Tile Map Layer"), KEY_PAGEDOWN);
+ ED_SHORTCUT("tiles_editor/select_next_layer", TTR("Select Next Tile Map Layer"), Key::PAGEUP);
+ ED_SHORTCUT("tiles_editor/select_previous_layer", TTR("Select Previous Tile Map Layer"), Key::PAGEDOWN);
// TileMap editor plugins
tile_map_editor_plugins.push_back(memnew(TileMapEditorTilesPlugin));
tile_map_editor_plugins.push_back(memnew(TileMapEditorTerrainsPlugin));
- // Tabs.
- tabs = memnew(Tabs);
- tabs->set_clip_tabs(false);
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tabs->add_tab(tile_map_editor_plugins[i]->get_name());
+ // TabBar.
+ tabs_bar = memnew(TabBar);
+ tabs_bar->set_clip_tabs(false);
+ for (int plugin_index = 0; plugin_index < tile_map_editor_plugins.size(); plugin_index++) {
+ Vector<TileMapEditorPlugin::TabData> tabs_vector = tile_map_editor_plugins[plugin_index]->get_tabs();
+ for (int tab_index = 0; tab_index < tabs_vector.size(); tab_index++) {
+ tabs_bar->add_tab(tabs_vector[tab_index].panel->get_name());
+ tabs_data.push_back(tabs_vector[tab_index]);
+ tabs_plugins.push_back(tile_map_editor_plugins[plugin_index]);
+ }
}
- tabs->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed));
+ tabs_bar->connect("tab_changed", callable_mp(this, &TileMapEditor::_tab_changed));
// --- TileMap toolbar ---
tile_map_toolbar = memnew(HBoxContainer);
tile_map_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(tile_map_toolbar);
// Tabs.
- tile_map_toolbar->add_child(tabs);
+ tile_map_toolbar->add_child(tabs_bar);
// Tabs toolbars.
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- tile_map_editor_plugins[i]->get_toolbar()->hide();
- tile_map_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar());
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ tabs_data[tab_index].toolbar->hide();
+ if (!tabs_data[tab_index].toolbar->get_parent()) {
+ tile_map_toolbar->add_child(tabs_data[tab_index].toolbar);
+ }
}
// Wide empty separation control.
@@ -3836,23 +3984,26 @@ TileMapEditor::TileMapEditor() {
missing_tileset_label->set_text(TTR("The edited TileMap node has no TileSet resource."));
missing_tileset_label->set_h_size_flags(SIZE_EXPAND_FILL);
missing_tileset_label->set_v_size_flags(SIZE_EXPAND_FILL);
- missing_tileset_label->set_align(Label::ALIGN_CENTER);
- missing_tileset_label->set_valign(Label::VALIGN_CENTER);
+ missing_tileset_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ missing_tileset_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
missing_tileset_label->hide();
add_child(missing_tileset_label);
- for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
- add_child(tile_map_editor_plugins[i]);
- tile_map_editor_plugins[i]->set_h_size_flags(SIZE_EXPAND_FILL);
- tile_map_editor_plugins[i]->set_v_size_flags(SIZE_EXPAND_FILL);
- tile_map_editor_plugins[i]->set_visible(i == 0);
+ for (unsigned int tab_index = 0; tab_index < tabs_data.size(); tab_index++) {
+ add_child(tabs_data[tab_index].panel);
+ tabs_data[tab_index].panel->set_v_size_flags(SIZE_EXPAND_FILL);
+ tabs_data[tab_index].panel->set_visible(tab_index == 0);
+ tabs_data[tab_index].panel->set_h_size_flags(SIZE_EXPAND_FILL);
}
_tab_changed(0);
// Registers UndoRedo inspector callback.
- EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileMapEditor::_undo_redo_inspector_callback));
+ EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileMap"), callable_mp(this, &TileMapEditor::_move_tile_map_array_element));
}
TileMapEditor::~TileMapEditor() {
+ for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
+ memdelete(tile_map_editor_plugins[i]);
+ }
}
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index 6e2f2ce2ba..f462119727 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -33,17 +33,24 @@
#include "tile_atlas_view.h"
+#include "core/os/thread.h"
#include "core/typedefs.h"
#include "editor/editor_node.h"
#include "scene/2d/tile_map.h"
#include "scene/gui/box_container.h"
-#include "scene/gui/tabs.h"
+#include "scene/gui/tab_bar.h"
-class TileMapEditorPlugin : public VBoxContainer {
+class TileMapEditorPlugin : public Object {
public:
- virtual Control *get_toolbar() const {
- return memnew(Control);
+ struct TabData {
+ Control *toolbar;
+ Control *panel;
};
+
+ virtual Vector<TabData> get_tabs() const {
+ return Vector<TabData>();
+ };
+
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return false; };
virtual void forward_canvas_draw_over_viewport(Control *p_overlay){};
virtual void tile_set_changed(){};
@@ -68,14 +75,15 @@ private:
Button *line_tool_button;
Button *rect_tool_button;
Button *bucket_tool_button;
- Button *picker_button;
HBoxContainer *tools_settings;
+
VSeparator *tools_settings_vsep;
+ Button *picker_button;
Button *erase_button;
- CheckBox *bucket_continuous_checkbox;
VSeparator *tools_settings_vsep_2;
+ CheckBox *bucket_contiguous_checkbox;
CheckBox *random_tile_checkbox;
float scattering = 0.0;
Label *scatter_label;
@@ -101,31 +109,38 @@ private:
DRAG_TYPE_CLIPBOARD_PASTE,
};
DragType drag_type = DRAG_TYPE_NONE;
+ bool drag_erasing = false;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
Map<Vector2i, TileMapCell> drag_modified;
- TileMapCell _pick_random_tile(const TileMapPattern *p_pattern);
- Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos);
- Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell);
- Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous);
+ TileMapCell _pick_random_tile(Ref<TileMapPattern> p_pattern);
+ Map<Vector2i, TileMapCell> _draw_line(Vector2 p_start_drag_mouse_pos, Vector2 p_from_mouse_pos, Vector2 p_to_mouse_pos, bool p_erase);
+ Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+ Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
///// Selection system. /////
Set<Vector2i> tile_map_selection;
- TileMapPattern *tile_map_clipboard = memnew(TileMapPattern);
- TileMapPattern *selection_pattern = memnew(TileMapPattern);
+ Ref<TileMapPattern> tile_map_clipboard;
+ Ref<TileMapPattern> selection_pattern;
void _set_tile_map_selection(const TypedArray<Vector2i> &p_selection);
TypedArray<Vector2i> _get_tile_map_selection() const;
Set<TileMapCell> tile_set_selection;
void _update_selection_pattern_from_tilemap_selection();
- void _update_selection_pattern_from_tileset_selection();
+ void _update_selection_pattern_from_tileset_tiles_selection();
+ void _update_selection_pattern_from_tileset_pattern_selection();
void _update_tileset_selection_from_selection_pattern();
void _update_fix_selected_and_hovered();
+ void _fix_invalid_tiles_in_tile_map_selection();
- ///// Bottom panel. ////.
+ ///// Bottom panel common ////
+ void _tab_changed();
+
+ ///// Bottom panel tiles ////
+ VBoxContainer *tiles_bottom_panel;
Label *missing_source_label;
Label *invalid_source_label;
@@ -134,7 +149,7 @@ private:
Ref<Texture2D> missing_atlas_texture_icon;
void _update_tile_set_sources_list();
- void _update_bottom_panel();
+ void _update_source_display();
// Atlas sources.
TileMapCell hovered_tile;
@@ -164,15 +179,26 @@ private:
void _scenes_list_multi_selected(int p_index, bool p_selected);
void _scenes_list_nothing_selected();
+ ///// Bottom panel patterns ////
+ VBoxContainer *patterns_bottom_panel;
+ ItemList *patterns_item_list;
+ Label *patterns_help_label;
+ void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
+ void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
+ bool select_last_pattern = false;
+ void _update_patterns_list();
+
+ // General
+ void _update_theme();
+
// Update callback
virtual void tile_set_changed() override;
protected:
- void _notification(int p_what);
static void _bind_methods();
public:
- virtual Control *get_toolbar() const override;
+ virtual Vector<TabData> get_tabs() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
@@ -194,102 +220,73 @@ private:
Ref<ButtonGroup> tool_buttons_group;
Button *paint_tool_button;
+ Button *line_tool_button;
+ Button *rect_tool_button;
+ Button *bucket_tool_button;
HBoxContainer *tools_settings;
+
VSeparator *tools_settings_vsep;
Button *picker_button;
Button *erase_button;
+ VSeparator *tools_settings_vsep_2;
+ CheckBox *bucket_contiguous_checkbox;
void _update_toolbar();
+ // Main vbox.
+ VBoxContainer *main_vbox_container;
+
// TileMap editing.
+ bool has_mouse = false;
+ void _mouse_exited_viewport();
+
enum DragType {
DRAG_TYPE_NONE = 0,
DRAG_TYPE_PAINT,
+ DRAG_TYPE_LINE,
+ DRAG_TYPE_RECT,
+ DRAG_TYPE_BUCKET,
DRAG_TYPE_PICK,
};
DragType drag_type = DRAG_TYPE_NONE;
+ bool drag_erasing = false;
Vector2 drag_start_mouse_pos;
Vector2 drag_last_mouse_pos;
Map<Vector2i, TileMapCell> drag_modified;
// Painting
- class Constraint {
- private:
- const TileMap *tile_map;
- Vector2i base_cell_coords = Vector2i();
- int bit = -1;
- int terrain = -1;
-
- public:
- // TODO implement difference operator.
- bool operator<(const Constraint &p_other) const {
- if (base_cell_coords == p_other.base_cell_coords) {
- return bit < p_other.bit;
- }
- return base_cell_coords < p_other.base_cell_coords;
- }
-
- String to_string() const {
- return vformat("Constraint {pos:%s, bit:%d, terrain:%d}", base_cell_coords, bit, terrain);
- }
-
- Vector2i get_base_cell_coords() const {
- return base_cell_coords;
- }
-
- Map<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const;
-
- void set_terrain(int p_terrain) {
- terrain = p_terrain;
- }
-
- int get_terrain() const {
- return terrain;
- }
-
- Constraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain);
- Constraint() {}
- };
-
- typedef Array TerrainsTilePattern;
-
- Set<TerrainsTilePattern> _get_valid_terrains_tile_patterns_for_constraints(int p_terrain_set, const Vector2i &p_position, Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
- Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_removed_cells_list(const Set<Vector2i> &p_to_replace, int p_terrain_set) const;
- Set<TileMapEditorTerrainsPlugin::Constraint> _get_constraints_from_added_tile(Vector2i p_position, int p_terrain_set, TerrainsTilePattern p_terrains_tile_pattern) const;
- Map<Vector2i, TerrainsTilePattern> _wave_function_collapse(const Set<Vector2i> &p_to_replace, int p_terrain_set, const Set<TileMapEditorTerrainsPlugin::Constraint> p_constraints) const;
- TileMapCell _get_random_tile_from_pattern(int p_terrain_set, TerrainsTilePattern p_terrain_tile_pattern) const;
- Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TerrainsTilePattern> &p_to_paint, int p_terrain_set) const;
+ Map<Vector2i, TileMapCell> _draw_terrains(const Map<Vector2i, TileSet::TerrainsPattern> &p_to_paint, int p_terrain_set) const;
+ Map<Vector2i, TileMapCell> _draw_line(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+ Map<Vector2i, TileMapCell> _draw_rect(Vector2i p_start_cell, Vector2i p_end_cell, bool p_erase);
+ Set<Vector2i> _get_cells_for_bucket_fill(Vector2i p_coords, bool p_contiguous);
+ Map<Vector2i, TileMapCell> _draw_bucket_fill(Vector2i p_coords, bool p_contiguous, bool p_erase);
void _stop_dragging();
- // Cached data.
- TerrainsTilePattern _build_terrains_tile_pattern(TileData *p_tile_data);
- LocalVector<Map<TerrainsTilePattern, Set<TileMapCell>>> per_terrain_terrains_tile_patterns_tiles;
- LocalVector<LocalVector<Set<TerrainsTilePattern>>> per_terrain_terrains_tile_patterns;
-
- Map<TileMapCell, TileData *> terrain_tiles;
- LocalVector<TileSet::CellNeighbor> tile_sides;
+ int selected_terrain_set = -1;
+ TileSet::TerrainsPattern selected_terrains_pattern;
+ void _update_selection();
// Bottom panel.
Tree *terrains_tree;
ItemList *terrains_tile_list;
+ // Cache.
+ LocalVector<LocalVector<Set<TileSet::TerrainsPattern>>> per_terrain_terrains_patterns;
+
// Update functions.
void _update_terrains_cache();
void _update_terrains_tree();
void _update_tiles_list();
+ void _update_theme();
// Update callback
virtual void tile_set_changed() override;
-protected:
- void _notification(int p_what);
- // static void _bind_methods();
-
public:
- virtual Control *get_toolbar() const override;
+ virtual Vector<TabData> get_tabs() const override;
virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override;
- //virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
+ virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override;
TileMapEditorTerrainsPlugin();
~TileMapEditorTerrainsPlugin();
@@ -325,7 +322,9 @@ private:
// Bottom panel.
Label *missing_tileset_label;
- Tabs *tabs;
+ TabBar *tabs_bar;
+ LocalVector<TileMapEditorPlugin::TabData> tabs_data;
+ LocalVector<TileMapEditorPlugin *> tabs_plugins;
void _update_bottom_panel();
// TileMap.
@@ -341,7 +340,7 @@ private:
void _update_layers_selection();
// Inspector undo/redo callback.
- void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
+ void _move_tile_map_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos);
protected:
void _notification(int p_what);
@@ -352,7 +351,6 @@ public:
void forward_canvas_draw_over_viewport(Control *p_overlay);
void edit(TileMap *p_tile_map);
- Control *get_toolbar() { return tile_map_toolbar; };
TileMapEditor();
~TileMapEditor();
diff --git a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
index 9e47a44b34..60a66ab954 100644
--- a/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
+++ b/editor/plugins/tiles/tile_proxies_manager_dialog.cpp
@@ -34,7 +34,7 @@
void TileProxiesManagerDialog::_right_clicked(int p_item, Vector2 p_local_mouse_pos, Object *p_item_list) {
ItemList *item_list = Object::cast_to<ItemList>(p_item_list);
- popup_menu->set_size(Vector2(1, 1));
+ popup_menu->reset_size();
popup_menu->set_position(get_position() + item_list->get_global_mouse_position());
popup_menu->popup();
}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 432f48fa85..660eb94cf5 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -66,10 +66,15 @@ int TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::get_id() {
}
bool TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+ if (name == "name") {
+ // Use the resource_name property to store the source's name.
+ name = "resource_name";
+ }
bool valid = false;
- tile_set_atlas_source->set(p_name, p_value, &valid);
+ tile_set_atlas_source->set(name, p_value, &valid);
if (valid) {
- emit_signal(SNAME("changed"), String(p_name).utf8().get_data());
+ emit_signal(SNAME("changed"), String(name).utf8().get_data());
}
return valid;
}
@@ -78,16 +83,23 @@ bool TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get(const StringN
if (!tile_set_atlas_source) {
return false;
}
+ String name = p_name;
+ if (name == "name") {
+ // Use the resource_name property to store the source's name.
+ name = "resource_name";
+ }
bool valid = false;
- r_ret = tile_set_atlas_source->get(p_name, &valid);
+ r_ret = tile_set_atlas_source->get(name, &valid);
return valid;
}
void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, ""));
- p_list->push_back(PropertyInfo(Variant::VECTOR2I, "tile_size", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, ""));
}
void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_bind_methods() {
@@ -106,6 +118,10 @@ void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::edit(Ref<TileSet>
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_atlas_source);
+ if (p_tile_set == tile_set && p_tile_set_atlas_source == tile_set_atlas_source && p_source_id == source_id) {
+ return;
+ }
+
// Disconnect to changes.
if (tile_set_atlas_source) {
tile_set_atlas_source->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
@@ -131,52 +147,127 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_set(const StringName &p_na
return false;
}
+ // ID and size related properties.
if (tiles.size() == 1) {
const Vector2i &coords = tiles.front()->get().tile;
const int &alternative = tiles.front()->get().alternative;
- if (alternative == 0 && p_name == "atlas_coords") {
- Vector2i as_vector2i = Vector2i(p_value);
- ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, as_vector2i), false);
+ if (alternative == 0) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (p_name == "atlas_coords") {
+ Vector2i as_vector2i = Vector2i(p_value);
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(as_vector2i, tile_set_atlas_source->get_tile_size_in_atlas(coords), tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
+ ERR_FAIL_COND_V(!has_room_for_tile, false);
+
+ if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) {
+ tiles_set_atlas_source_editor->selection.clear();
+ tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 });
+ tiles_set_atlas_source_editor->_update_tile_id_label();
+ }
- if (tiles_set_atlas_source_editor->selection.front()->get().tile == coords) {
- tiles_set_atlas_source_editor->selection.clear();
- tiles_set_atlas_source_editor->selection.insert({ as_vector2i, 0 });
- tiles_set_atlas_source_editor->_update_tile_id_label();
+ tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i);
+ tiles.clear();
+ tiles.insert({ as_vector2i, 0 });
+ emit_signal(SNAME("changed"), "atlas_coords");
+ return true;
+ } else if (p_name == "size_in_atlas") {
+ Vector2i as_vector2i = Vector2i(p_value);
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(coords, as_vector2i, tile_set_atlas_source->get_tile_animation_columns(coords), tile_set_atlas_source->get_tile_animation_separation(coords), tile_set_atlas_source->get_tile_animation_frames_count(coords), coords);
+ ERR_FAIL_COND_V_EDMSG(!has_room_for_tile, false, "Invalid size or not enough room in the atlas for the tile.");
+ tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
+ emit_signal(SNAME("changed"), "size_in_atlas");
+ return true;
}
+ } else if (alternative > 0) {
+ if (p_name == "alternative_id") {
+ int as_int = int(p_value);
+ ERR_FAIL_COND_V(as_int < 0, false);
+ ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords));
+
+ if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) {
+ tiles_set_atlas_source_editor->selection.clear();
+ tiles_set_atlas_source_editor->selection.insert({ coords, as_int });
+ }
- tile_set_atlas_source->move_tile_in_atlas(coords, as_vector2i);
- tiles.clear();
- tiles.insert({ as_vector2i, 0 });
- emit_signal(SNAME("changed"), "atlas_coords");
- return true;
- } else if (alternative == 0 && p_name == "size_in_atlas") {
- Vector2i as_vector2i = Vector2i(p_value);
- ERR_FAIL_COND_V(!tile_set_atlas_source->can_move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i), false);
-
- tile_set_atlas_source->move_tile_in_atlas(coords, TileSetSource::INVALID_ATLAS_COORDS, as_vector2i);
- emit_signal(SNAME("changed"), "size_in_atlas");
- return true;
- } else if (alternative > 0 && p_name == "alternative_id") {
- int as_int = int(p_value);
- ERR_FAIL_COND_V(as_int < 0, false);
- ERR_FAIL_COND_V_MSG(tile_set_atlas_source->has_alternative_tile(coords, as_int), false, vformat("Cannot change alternative tile ID. Another alternative exists with id %d for tile at coords %s.", as_int, coords));
+ int previous_alternative_tile = alternative;
+ tiles.clear();
+ tiles.insert({ coords, as_int }); // tiles must be updated before.
+ tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int);
- if (tiles_set_atlas_source_editor->selection.front()->get().alternative == alternative) {
- tiles_set_atlas_source_editor->selection.clear();
- tiles_set_atlas_source_editor->selection.insert({ coords, as_int });
+ emit_signal(SNAME("changed"), "alternative_id");
+ return true;
}
+ }
+ }
- int previous_alternative_tile = alternative;
- tiles.clear();
- tiles.insert({ coords, as_int }); // tiles must be updated before.
- tile_set_atlas_source->set_alternative_tile_id(coords, previous_alternative_tile, as_int);
+ // Animation.
+ // Check if all tiles have an alternative_id of 0.
+ bool all_alternatve_id_zero = true;
+ for (TileSelection tile : tiles) {
+ if (tile.alternative != 0) {
+ all_alternatve_id_zero = false;
+ break;
+ }
+ }
- emit_signal(SNAME("changed"), "alternative_id");
+ if (all_alternatve_id_zero) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (p_name == "animation_columns") {
+ for (TileSelection tile : tiles) {
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), p_value, tile_set_atlas_source->get_tile_animation_separation(tile.tile), tile_set_atlas_source->get_tile_animation_frames_count(tile.tile), tile.tile);
+ if (!has_room_for_tile) {
+ ERR_PRINT("No room for tile");
+ } else {
+ tile_set_atlas_source->set_tile_animation_columns(tile.tile, p_value);
+ }
+ }
+ emit_signal(SNAME("changed"), "animation_columns");
+ return true;
+ } else if (p_name == "animation_separation") {
+ for (TileSelection tile : tiles) {
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), p_value, tile_set_atlas_source->get_tile_animation_frames_count(tile.tile), tile.tile);
+ if (!has_room_for_tile) {
+ ERR_PRINT("No room for tile");
+ } else {
+ tile_set_atlas_source->set_tile_animation_separation(tile.tile, p_value);
+ }
+ }
+ emit_signal(SNAME("changed"), "animation_separation");
return true;
+ } else if (p_name == "animation_speed") {
+ for (TileSelection tile : tiles) {
+ tile_set_atlas_source->set_tile_animation_speed(tile.tile, p_value);
+ }
+ emit_signal(SNAME("changed"), "animation_speed");
+ return true;
+ } else if (p_name == "animation_frames_count") {
+ for (TileSelection tile : tiles) {
+ bool has_room_for_tile = tile_set_atlas_source->has_room_for_tile(tile.tile, tile_set_atlas_source->get_tile_size_in_atlas(tile.tile), tile_set_atlas_source->get_tile_animation_columns(tile.tile), tile_set_atlas_source->get_tile_animation_separation(tile.tile), p_value, tile.tile);
+ if (!has_room_for_tile) {
+ ERR_PRINT("No room for tile");
+ } else {
+ tile_set_atlas_source->set_tile_animation_frames_count(tile.tile, p_value);
+ }
+ }
+ notify_property_list_changed();
+ emit_signal(SNAME("changed"), "animation_separation");
+ return true;
+ } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) {
+ for (TileSelection tile : tiles) {
+ int frame = components[0].trim_prefix("animation_frame_").to_int();
+ if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(tile.tile)) {
+ ERR_PRINT(vformat("No tile animation frame with index %d", frame));
+ } else {
+ if (components[1] == "duration") {
+ tile_set_atlas_source->set_tile_animation_frame_duration(tile.tile, frame, p_value);
+ return true;
+ }
+ }
+ }
}
}
+ // Other properties.
bool any_valid = false;
for (Set<TileSelection>::Element *E = tiles.front(); E; E = E->next()) {
const Vector2i &coords = E->get().tile;
@@ -202,19 +293,63 @@ bool TileSetAtlasSourceEditor::AtlasTileProxyObject::_get(const StringName &p_na
return false;
}
+ // ID and size related properties.s
if (tiles.size() == 1) {
const Vector2i &coords = tiles.front()->get().tile;
const int &alternative = tiles.front()->get().alternative;
- if (alternative == 0 && p_name == "atlas_coords") {
- r_ret = coords;
+ if (alternative == 0) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (p_name == "atlas_coords") {
+ r_ret = coords;
+ return true;
+ } else if (p_name == "size_in_atlas") {
+ r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords);
+ return true;
+ }
+ } else if (alternative > 0) {
+ if (p_name == "alternative_id") {
+ r_ret = alternative;
+ return true;
+ }
+ }
+ }
+
+ // Animation.
+ // Check if all tiles have an alternative_id of 0.
+ bool all_alternatve_id_zero = true;
+ for (TileSelection tile : tiles) {
+ if (tile.alternative != 0) {
+ all_alternatve_id_zero = false;
+ break;
+ }
+ }
+
+ if (all_alternatve_id_zero) {
+ const Vector2i &coords = tiles.front()->get().tile;
+
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (p_name == "animation_columns") {
+ r_ret = tile_set_atlas_source->get_tile_animation_columns(coords);
return true;
- } else if (alternative == 0 && p_name == "size_in_atlas") {
- r_ret = tile_set_atlas_source->get_tile_size_in_atlas(coords);
+ } else if (p_name == "animation_separation") {
+ r_ret = tile_set_atlas_source->get_tile_animation_separation(coords);
return true;
- } else if (alternative > 0 && p_name == "alternative_id") {
- r_ret = alternative;
+ } else if (p_name == "animation_speed") {
+ r_ret = tile_set_atlas_source->get_tile_animation_speed(coords);
return true;
+ } else if (p_name == "animation_frames_count") {
+ r_ret = tile_set_atlas_source->get_tile_animation_frames_count(coords);
+ return true;
+ } else if (components.size() == 2 && components[0].begins_with("animation_frame_") && components[0].trim_prefix("animation_frame_").is_valid_int()) {
+ int frame = components[0].trim_prefix("animation_frame_").to_int();
+ if (components[1] == "duration") {
+ if (frame < 0 || frame >= tile_set_atlas_source->get_tile_animation_frames_count(coords)) {
+ return false;
+ }
+ r_ret = tile_set_atlas_source->get_tile_animation_frame_duration(coords, frame);
+ return true;
+ }
}
}
@@ -242,6 +377,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
return;
}
+ // ID and size related properties.
if (tiles.size() == 1) {
if (tiles.front()->get().alternative == 0) {
p_list->push_back(PropertyInfo(Variant::VECTOR2I, "atlas_coords", PROPERTY_HINT_NONE, ""));
@@ -251,6 +387,32 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
}
}
+ // Animation.
+ // Check if all tiles have an alternative_id of 0.
+ bool all_alternatve_id_zero = true;
+ for (TileSelection tile : tiles) {
+ if (tile.alternative != 0) {
+ all_alternatve_id_zero = false;
+ break;
+ }
+ }
+
+ if (all_alternatve_id_zero) {
+ p_list->push_back(PropertyInfo(Variant::NIL, "Animation", PROPERTY_HINT_NONE, "animation_", PROPERTY_USAGE_GROUP));
+ p_list->push_back(PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_"));
+ // Not optimal, but returns value for the first tile. This is similar to what MultiNodeEdit does.
+ if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY));
+ } else {
+ for (int i = 0; i < tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile); i++) {
+ p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, ""));
+ }
+ }
+ }
+
// Get the list of properties common to all tiles (similar to what's done in MultiNodeEdit).
struct PropertyId {
int occurence_id = 0;
@@ -374,9 +536,6 @@ void TileSetAtlasSourceEditor::_update_tile_id_label() {
void TileSetAtlasSourceEditor::_update_source_inspector() {
// Update the proxy object.
atlas_source_proxy_object->edit(tile_set, tile_set_atlas_source, tile_set_atlas_source_id);
-
- // Update the "clear outside texture" button.
- tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, !tile_set_atlas_source->has_tiles_outside_texture());
}
void TileSetAtlasSourceEditor::_update_fix_selected_and_hovered_tiles() {
@@ -598,9 +757,9 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
#undef ADD_TILE_DATA_EDITOR
// Add tile data editors as children.
- for (Map<String, TileDataEditor *>::Element *E = tile_data_editors.front(); E; E = E->next()) {
+ for (KeyValue<String, TileDataEditor *> &E : tile_data_editors) {
// Tile Data Editor.
- TileDataEditor *tile_data_editor = E->get();
+ TileDataEditor *tile_data_editor = E.value;
if (!tile_data_editor->is_inside_tree()) {
tile_data_painting_editor_container->add_child(tile_data_editor);
}
@@ -617,7 +776,11 @@ void TileSetAtlasSourceEditor::_update_tile_data_editors() {
// Update visibility.
bool is_visible = tools_button_group->get_pressed_button() == tool_paint_button;
tile_data_editor_dropdown_button->set_visible(is_visible);
- tile_data_editor_dropdown_button->set_text(TTR("Select a property editor"));
+ if (tile_data_editors_tree->get_selected()) {
+ tile_data_editor_dropdown_button->set_text(tile_data_editors_tree->get_selected()->get_text(0));
+ } else {
+ tile_data_editor_dropdown_button->set_text(TTR("Select a property editor"));
+ }
tile_data_editors_label->set_visible(is_visible);
}
@@ -640,9 +803,9 @@ void TileSetAtlasSourceEditor::_update_current_tile_data_editor() {
}
// Hide all editors but the current one.
- for (Map<String, TileDataEditor *>::Element *E = tile_data_editors.front(); E; E = E->next()) {
- E->get()->hide();
- E->get()->get_toolbar()->hide();
+ for (const KeyValue<String, TileDataEditor *> &E : tile_data_editors) {
+ E.value->hide();
+ E.value->get_toolbar()->hide();
}
if (tile_data_editors.has(property)) {
current_tile_data_editor = tile_data_editors[property];
@@ -679,7 +842,11 @@ void TileSetAtlasSourceEditor::_tile_data_editor_dropdown_button_draw() {
clr = get_theme_color(SNAME("font_disabled_color"));
break;
default:
- clr = get_theme_color(SNAME("font_color"));
+ if (tile_data_editor_dropdown_button->has_focus()) {
+ clr = get_theme_color(SNAME("font_focus_color"));
+ } else {
+ clr = get_theme_color(SNAME("font_color"));
+ }
}
}
@@ -762,7 +929,7 @@ void TileSetAtlasSourceEditor::_update_atlas_view() {
tile_atlas_view->update();
// Synchronize atlas view.
- TilesEditor::get_singleton()->synchronize_atlas_view(tile_atlas_view);
+ TilesEditorPlugin::get_singleton()->synchronize_atlas_view(tile_atlas_view);
}
void TileSetAtlasSourceEditor::_update_toolbar() {
@@ -813,7 +980,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
current_tile_data_editor->forward_painting_atlas_gui_input(tile_atlas_view, tile_set_atlas_source, p_event);
}
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
tile_atlas_control->update();
tile_atlas_control_unscaled->update();
@@ -846,15 +1013,15 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
CursorShape cursor_shape = CURSOR_ARROW;
bool can_grow[4];
for (int i = 0; i < 4; i++) {
- can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
+ can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
for (int i = 0; i < 4; i++) {
- Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
+ Vector2 pos = rect.position + rect.size * coords[i];
if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
}
- Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
+ Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
cursor_shape = (i % 2) ? CURSOR_HSIZE : CURSOR_VSIZE;
}
@@ -869,7 +1036,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Rect2i new_rect = Rect2i(start_base_tiles_coords, new_base_tiles_coords - start_base_tiles_coords).abs();
new_rect.size += Vector2i(1, 1);
// Check if the new tile can fit in the new rect.
- if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) {
+ if (tile_set_atlas_source->has_room_for_tile(new_rect.position, new_rect.size, tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
// Move and resize the tile.
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size);
drag_current_tile = new_rect.position;
@@ -908,14 +1075,14 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Vector2 mouse_offset = (Vector2(tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile)) / 2.0 - Vector2(0.5, 0.5)) * tile_set->get_tile_size();
Vector2i coords = tile_atlas_view->get_atlas_tile_coords_at_pos(tile_atlas_control->get_local_mouse_position() - mouse_offset);
coords = coords.max(Vector2i(0, 0)).min(grid_size - Vector2i(1, 1));
- if (drag_current_tile != coords && tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, coords)) {
+ if (drag_current_tile != coords && tile_set_atlas_source->has_room_for_tile(coords, tile_set_atlas_source->get_tile_size_in_atlas(drag_current_tile), tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, coords);
selection.clear();
selection.insert({ coords, 0 });
drag_current_tile = coords;
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
_update_tile_inspector();
_update_atlas_view();
_update_tile_id_label();
@@ -948,14 +1115,14 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
new_rect.set_end(Vector2i(new_rect.get_end().x, MAX(new_base_tiles_coords.y, old_rect.position.y + 1)));
}
- if (tile_set_atlas_source->can_move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size)) {
+ if (tile_set_atlas_source->has_room_for_tile(new_rect.position, new_rect.size, tile_set_atlas_source->get_tile_animation_columns(drag_current_tile), tile_set_atlas_source->get_tile_animation_separation(drag_current_tile), tile_set_atlas_source->get_tile_animation_frames_count(drag_current_tile), drag_current_tile)) {
tile_set_atlas_source->move_tile_in_atlas(drag_current_tile, new_rect.position, new_rect.size);
selection.clear();
selection.insert({ new_rect.position, 0 });
drag_current_tile = new_rect.position;
// Update only what's needed.
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
_update_tile_inspector();
_update_atlas_view();
_update_tile_id_label();
@@ -975,7 +1142,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
Vector2 mouse_local_pos = tile_atlas_control->get_local_mouse_position();
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
// Left click pressed.
if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) {
@@ -1056,11 +1223,11 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
CursorShape cursor_shape = CURSOR_ARROW;
bool can_grow[4];
for (int i = 0; i < 4; i++) {
- can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
+ can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
for (int i = 0; i < 4; i++) {
- Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
+ Vector2 pos = rect.position + rect.size * coords[i];
if (can_grow[i] && can_grow[(i + 3) % 4] && Rect2(pos, zoomed_size).has_point(mouse_local_pos)) {
drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP_LEFT + i * 2);
drag_start_mouse_pos = mouse_local_pos;
@@ -1069,7 +1236,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
drag_start_tile_shape = Rect2i(selected.tile, tile_set_atlas_source->get_tile_size_in_atlas(selected.tile));
cursor_shape = (i % 2) ? CURSOR_BDIAGSIZE : CURSOR_FDIAGSIZE;
}
- Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
+ Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
if (can_grow[i] && Rect2((pos + next_pos) / 2.0, zoomed_size).has_point(mouse_local_pos)) {
drag_type = (DragType)((int)DRAG_TYPE_RESIZE_TOP + i * 2);
drag_start_mouse_pos = mouse_local_pos;
@@ -1121,7 +1288,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_gui_input(const Ref<InputEven
alternative_tiles_control_unscaled->update();
tile_atlas_view->update();
return;
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT) {
// Right click pressed.
if (mb->is_pressed()) {
drag_type = DRAG_TYPE_MAY_POPUP_MENU;
@@ -1260,7 +1427,7 @@ void TileSetAtlasSourceEditor::_end_dragging() {
// Determine if we clear, then add or remove to the selection.
bool add_to_selection = true;
- if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) {
+ if (Input::get_singleton()->is_key_pressed(Key::SHIFT)) {
Vector2i coords = tile_set_atlas_source->get_tile_at_coords(start_base_tiles_coords);
if (coords != TileSetSource::INVALID_ATLAS_COORDS) {
if (selection.has({ coords, 0 })) {
@@ -1454,9 +1621,6 @@ void TileSetAtlasSourceEditor::_menu_option(int p_option) {
undo_redo->commit_action();
_update_tile_id_label();
} break;
- case ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE: {
- tile_set_atlas_source->clear_tiles_outside_texture();
- } break;
case ADVANCED_AUTO_CREATE_TILES: {
_auto_create_tiles();
} break;
@@ -1511,37 +1675,45 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
TileSelection selected = E->get();
if (selected.alternative == 0) {
// Draw the rect.
- Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
- tile_atlas_control->draw_rect(region, selection_color, false);
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(selected.tile); frame++) {
+ Color color = selection_color;
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile, frame);
+ tile_atlas_control->draw_rect(region, color, false);
+ }
}
}
if (selection.size() == 1) {
// Draw the resize handles (only when it's possible to expand).
TileSelection selected = selection.front()->get();
- Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
- Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
- Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
- Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
- Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
- Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
- bool can_grow[4];
- for (int i = 0; i < 4; i++) {
- can_grow[i] = tile_set_atlas_source->can_move_tile_in_atlas(selected.tile, selected.tile + directions[i]);
- can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
- }
- for (int i = 0; i < 4; i++) {
- Vector2 pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[i];
- if (can_grow[i] && can_grow[(i + 3) % 4]) {
- tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false);
- } else {
- tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false);
+ if (selected.alternative == 0) {
+ Vector2i size_in_atlas = tile_set_atlas_source->get_tile_size_in_atlas(selected.tile);
+ Size2 zoomed_size = resize_handle->get_size() / tile_atlas_view->get_zoom();
+ Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
+ Rect2 rect = region.grow_individual(zoomed_size.x, zoomed_size.y, 0, 0);
+ Vector2i coords[] = { Vector2i(0, 0), Vector2i(1, 0), Vector2i(1, 1), Vector2i(0, 1) };
+ Vector2i directions[] = { Vector2i(0, -1), Vector2i(1, 0), Vector2i(0, 1), Vector2i(-1, 0) };
+ bool can_grow[4];
+ for (int i = 0; i < 4; i++) {
+ can_grow[i] = tile_set_atlas_source->has_room_for_tile(selected.tile + directions[i], tile_set_atlas_source->get_tile_size_in_atlas(selected.tile), tile_set_atlas_source->get_tile_animation_columns(selected.tile), tile_set_atlas_source->get_tile_animation_separation(selected.tile), tile_set_atlas_source->get_tile_animation_frames_count(selected.tile), selected.tile);
+ can_grow[i] |= (i % 2 == 0) ? size_in_atlas.y > 1 : size_in_atlas.x > 1;
}
- Vector2 next_pos = rect.position + Vector2(rect.size.x, rect.size.y) * coords[(i + 1) % 4];
- if (can_grow[i]) {
- tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
- } else {
- tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
+ for (int i = 0; i < 4; i++) {
+ Vector2 pos = rect.position + rect.size * coords[i];
+ if (can_grow[i] && can_grow[(i + 3) % 4]) {
+ tile_atlas_control->draw_texture_rect(resize_handle, Rect2(pos, zoomed_size), false);
+ } else {
+ tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2(pos, zoomed_size), false);
+ }
+ Vector2 next_pos = rect.position + rect.size * coords[(i + 1) % 4];
+ if (can_grow[i]) {
+ tile_atlas_control->draw_texture_rect(resize_handle, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
+ } else {
+ tile_atlas_control->draw_texture_rect(resize_handle_disabled, Rect2((pos + next_pos) / 2.0, zoomed_size), false);
+ }
}
}
}
@@ -1550,7 +1722,9 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
if (drag_type == DRAG_TYPE_REMOVE_TILES) {
// Draw the tiles to be removed.
for (Set<Vector2i>::Element *E = drag_modified_tiles.front(); E; E = E->next()) {
- tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get()), Color(0.0, 0.0, 0.0), false);
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(E->get()); frame++) {
+ tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(E->get(), frame), Color(0.0, 0.0, 0.0), false);
+ }
}
} else if (drag_type == DRAG_TYPE_RECT_SELECT || drag_type == DRAG_TYPE_REMOVE_TILES_USING_RECT) {
// Draw tiles to be removed.
@@ -1617,7 +1791,13 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Vector2i hovered_tile = tile_set_atlas_source->get_tile_at_coords(hovered_base_tile_coords);
if (hovered_tile != TileSetSource::INVALID_ATLAS_COORDS) {
// Draw existing hovered tile.
- tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile), Color(1.0, 1.0, 1.0), false);
+ for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(hovered_tile); frame++) {
+ Color color = Color(1.0, 1.0, 1.0);
+ if (frame > 0) {
+ color.a *= 0.3;
+ }
+ tile_atlas_control->draw_rect(tile_set_atlas_source->get_tile_texture_region(hovered_tile, frame), color, false);
+ }
} else {
// Draw empty tile, only in add/remove tiles mode.
if (tools_button_group->get_pressed_button() == tool_setup_atlas_source_button) {
@@ -1638,7 +1818,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i coords = tile_set_atlas_source->get_tile_id(i);
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(coords);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
+ Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(coords, 0);
Transform2D xform = tile_atlas_control->get_parent_control()->get_transform();
xform.translate(position);
@@ -1661,7 +1841,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_unscaled_draw() {
continue;
}
Rect2i texture_region = tile_set_atlas_source->get_tile_texture_region(E->get().tile);
- Vector2i position = (texture_region.position + texture_region.get_end()) / 2 + tile_set_atlas_source->get_tile_effective_texture_offset(E->get().tile, 0);
+ Vector2i position = texture_region.get_center() + tile_set_atlas_source->get_tile_effective_texture_offset(E->get().tile, 0);
Transform2D xform = tile_atlas_control->get_parent_control()->get_transform();
xform.translate(position);
@@ -1712,7 +1892,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In
drag_type = DRAG_TYPE_NONE;
Vector2 mouse_local_pos = alternative_tiles_control->get_local_mouse_position();
- if (mb->get_button_index() == MOUSE_BUTTON_LEFT) {
+ if (mb->get_button_index() == MouseButton::LEFT) {
if (mb->is_pressed()) {
// Left click pressed.
if (tools_button_group->get_pressed_button() == tool_select_button) {
@@ -1728,7 +1908,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_gui_input(const Ref<In
_update_tile_id_label();
}
}
- } else if (mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ } else if (mb->get_button_index() == MouseButton::RIGHT) {
if (mb->is_pressed()) {
// Right click pressed
Vector3 tile = tile_atlas_view->get_alternative_tile_at_pos(mouse_local_pos);
@@ -1805,7 +1985,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
continue;
}
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(coords, alternative_tile);
- Vector2 position = (rect.get_position() + rect.get_end()) / 2;
+ Vector2 position = rect.get_center();
Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform();
xform.translate(position);
@@ -1829,7 +2009,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
continue;
}
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(E->get().tile, E->get().alternative);
- Vector2 position = (rect.get_position() + rect.get_end()) / 2;
+ Vector2 position = rect.get_center();
Transform2D xform = alternative_tiles_control->get_parent_control()->get_transform();
xform.translate(position);
@@ -1850,8 +2030,13 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_unscaled_draw() {
}
}
-void TileSetAtlasSourceEditor::_tile_set_atlas_source_changed() {
- tile_set_atlas_source_changed_needs_update = true;
+void TileSetAtlasSourceEditor::_tile_set_changed() {
+ tile_set_changed_needs_update = true;
+}
+
+void TileSetAtlasSourceEditor::_tile_proxy_object_changed(String p_what) {
+ tile_set_changed_needs_update = false; // Avoid updating too many things.
+ _update_atlas_view();
}
void TileSetAtlasSourceEditor::_atlas_source_proxy_object_changed(String p_what) {
@@ -1866,32 +2051,68 @@ void TileSetAtlasSourceEditor::_undo_redo_inspector_callback(Object *p_undo_redo
UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
ERR_FAIL_COND(!undo_redo);
-#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property));
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
- AtlasTileProxyObject *tile_data = Object::cast_to<AtlasTileProxyObject>(p_edited);
- if (tile_data) {
+ undo_redo->start_force_keep_in_merge_ends();
+ AtlasTileProxyObject *tile_data_proxy = Object::cast_to<AtlasTileProxyObject>(p_edited);
+ if (tile_data_proxy) {
Vector<String> components = String(p_property).split("/", true, 2);
if (components.size() == 2 && components[1] == "polygons_count") {
int layer_index = components[0].trim_prefix("physics_layer_").to_int();
int new_polygons_count = p_new_value;
- int old_polygons_count = tile_data->get(vformat("physics_layer_%d/polygons_count", layer_index));
+ int old_polygons_count = tile_data_proxy->get(vformat("physics_layer_%d/polygons_count", layer_index));
if (new_polygons_count < old_polygons_count) {
- for (int i = new_polygons_count - 1; i < old_polygons_count; i++) {
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, i));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i));
+ for (int i = new_polygons_count; i < old_polygons_count; i++) {
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/points", layer_index, i));
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, i));
+ ADD_UNDO(tile_data_proxy, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, i));
}
}
} else if (p_property == "terrain_set") {
- int current_terrain_set = tile_data->get("terrain_set");
+ int current_terrain_set = tile_data_proxy->get("terrain_set");
for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(i);
if (tile_set->is_valid_peering_bit_terrain(current_terrain_set, bit)) {
- ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
+ ADD_UNDO(tile_data_proxy, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[i]));
+ }
+ }
+ }
+ }
+
+ TileSetAtlasSourceProxyObject *atlas_source_proxy = Object::cast_to<TileSetAtlasSourceProxyObject>(p_edited);
+ if (atlas_source_proxy) {
+ TileSetAtlasSource *atlas_source = atlas_source_proxy->get_edited();
+ ERR_FAIL_COND(!atlas_source);
+
+ PackedVector2Array arr;
+ if (p_property == "texture") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(p_new_value, atlas_source->get_margins(), atlas_source->get_separation(), atlas_source->get_texture_region_size());
+ } else if (p_property == "margins") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), p_new_value, atlas_source->get_separation(), atlas_source->get_texture_region_size());
+ } else if (p_property == "separation") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), p_new_value, atlas_source->get_texture_region_size());
+ } else if (p_property == "texture_region_size") {
+ arr = atlas_source->get_tiles_to_be_removed_on_change(atlas_source->get_texture(), atlas_source->get_margins(), atlas_source->get_separation(), p_new_value);
+ }
+
+ if (!arr.is_empty()) {
+ // Get all properties assigned to a tile.
+ List<PropertyInfo> properties;
+ atlas_source->get_property_list(&properties);
+
+ for (int i = 0; i < arr.size(); i++) {
+ Vector2i coords = arr[i];
+ String prefix = vformat("%d:%d/", coords.x, coords.y);
+ for (PropertyInfo pi : properties) {
+ if (pi.name.begins_with(prefix)) {
+ ADD_UNDO(atlas_source, pi.name);
+ }
}
}
}
}
+ undo_redo->end_force_keep_in_merge_ends();
+
#undef ADD_UNDO
}
@@ -1906,8 +2127,8 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
}
// Remove listener for old objects.
- if (tile_set_atlas_source) {
- tile_set_atlas_source->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed));
+ if (tile_set.is_valid()) {
+ tile_set->disconnect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
// Clear the selection.
@@ -1919,8 +2140,8 @@ void TileSetAtlasSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetAtlasSource
tile_set_atlas_source_id = p_source_id;
// Add the listener again.
- if (tile_set_atlas_source) {
- tile_set_atlas_source->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_atlas_source_changed));
+ if (tile_set.is_valid()) {
+ tile_set->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_set_changed));
}
// Update everything.
@@ -2061,7 +2282,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
resize_handle_disabled = get_theme_icon(SNAME("EditorHandleDisabled"), SNAME("EditorIcons"));
break;
case NOTIFICATION_INTERNAL_PROCESS:
- if (tile_set_atlas_source_changed_needs_update) {
+ if (tile_set_changed_needs_update) {
// Update everything.
_update_source_inspector();
@@ -2074,7 +2295,7 @@ void TileSetAtlasSourceEditor::_notification(int p_what) {
_update_tile_data_editors();
_update_current_tile_data_editor();
- tile_set_atlas_source_changed_needs_update = false;
+ tile_set_changed_needs_update = false;
}
break;
default:
@@ -2100,7 +2321,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
// Middle panel.
ScrollContainer *middle_panel = memnew(ScrollContainer);
- middle_panel->set_enable_h_scroll(false);
+ middle_panel->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
middle_panel->set_custom_minimum_size(Size2i(200, 0) * EDSCALE);
split_container_right_side->add_child(middle_panel);
@@ -2114,18 +2335,18 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
middle_vbox_container->add_child(tile_inspector_label);
tile_proxy_object = memnew(AtlasTileProxyObject(this));
- tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_update_atlas_view).unbind(1));
+ tile_proxy_object->connect("changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_proxy_object_changed));
tile_inspector = memnew(EditorInspector);
tile_inspector->set_undo_redo(undo_redo);
- tile_inspector->set_enable_v_scroll(false);
+ tile_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
tile_inspector->edit(tile_proxy_object);
tile_inspector->set_use_folding(true);
tile_inspector->connect("property_selected", callable_mp(this, &TileSetAtlasSourceEditor::_inspector_property_selected));
middle_vbox_container->add_child(tile_inspector);
tile_inspector_no_tile_selected_label = memnew(Label);
- tile_inspector_no_tile_selected_label->set_align(Label::ALIGN_CENTER);
+ tile_inspector_no_tile_selected_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
tile_inspector_no_tile_selected_label->set_text(TTR("No tile selected."));
middle_vbox_container->add_child(tile_inspector_no_tile_selected_label);
@@ -2164,7 +2385,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
atlas_source_inspector = memnew(EditorInspector);
atlas_source_inspector->set_undo_redo(undo_redo);
- atlas_source_inspector->set_enable_v_scroll(false);
+ atlas_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
atlas_source_inspector->edit(atlas_source_proxy_object);
middle_vbox_container->add_child(atlas_source_inspector);
@@ -2233,14 +2454,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tools_settings_erase_button = memnew(Button);
tools_settings_erase_button->set_flat(true);
tools_settings_erase_button->set_toggle_mode(true);
- tools_settings_erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", KEY_E));
+ tools_settings_erase_button->set_shortcut(ED_SHORTCUT("tiles_editor/eraser", "Eraser", Key::E));
tools_settings_erase_button->set_shortcut_context(this);
tool_settings->add_child(tools_settings_erase_button);
tool_advanced_menu_buttom = memnew(MenuButton);
tool_advanced_menu_buttom->set_flat(true);
- tool_advanced_menu_buttom->get_popup()->add_item(TTR("Cleanup Tiles Outside Texture"), ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE);
- tool_advanced_menu_buttom->get_popup()->set_item_disabled(0, true);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Create Tiles in Non-Transparent Texture Regions"), ADVANCED_AUTO_CREATE_TILES);
tool_advanced_menu_buttom->get_popup()->add_item(TTR("Remove Tiles in Fully Transparent Texture Regions"), ADVANCED_AUTO_REMOVE_TILES);
tool_advanced_menu_buttom->get_popup()->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
@@ -2262,12 +2481,12 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view = memnew(TileAtlasView);
tile_atlas_view->set_h_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view->set_v_size_flags(SIZE_EXPAND_FILL);
- tile_atlas_view->connect("transform_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_atlas_view_transform));
+ tile_atlas_view->connect("transform_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_atlas_view_transform));
tile_atlas_view->connect("transform_changed", callable_mp(this, &TileSetAtlasSourceEditor::_tile_atlas_view_transform_changed).unbind(2));
right_panel->add_child(tile_atlas_view);
base_tile_popup_menu = memnew(PopupMenu);
- base_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), KEY_DELETE), TILE_DELETE);
+ base_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE);
base_tile_popup_menu->add_item(TTR("Create an Alternative Tile"), TILE_CREATE_ALTERNATIVE);
base_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
tile_atlas_view->add_child(base_tile_popup_menu);
@@ -2290,7 +2509,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_control_unscaled->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
alternative_tile_popup_menu = memnew(PopupMenu);
- alternative_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete_tile", TTR("Delete"), KEY_DELETE), TILE_DELETE);
+ alternative_tile_popup_menu->add_shortcut(ED_SHORTCUT("tiles_editor/delete_tile", TTR("Delete"), Key::KEY_DELETE), TILE_DELETE);
alternative_tile_popup_menu->connect("id_pressed", callable_mp(this, &TileSetAtlasSourceEditor::_menu_option));
tile_atlas_view->add_child(alternative_tile_popup_menu);
@@ -2308,15 +2527,202 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_atlas_view_missing_source_label = memnew(Label);
tile_atlas_view_missing_source_label->set_text(TTR("Add or select an atlas texture to the left panel."));
- tile_atlas_view_missing_source_label->set_align(Label::ALIGN_CENTER);
- tile_atlas_view_missing_source_label->set_valign(Label::VALIGN_CENTER);
+ tile_atlas_view_missing_source_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ tile_atlas_view_missing_source_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
tile_atlas_view_missing_source_label->set_h_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view_missing_source_label->set_v_size_flags(SIZE_EXPAND_FILL);
tile_atlas_view_missing_source_label->hide();
right_panel->add_child(tile_atlas_view_missing_source_label);
+
+ EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetAtlasSourceEditor::_undo_redo_inspector_callback));
+
+ // Inspector plugin.
+ Ref<EditorInspectorPluginTileData> tile_data_inspector_plugin;
+ tile_data_inspector_plugin.instantiate();
+ EditorInspector::add_inspector_plugin(tile_data_inspector_plugin);
}
TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {
memdelete(tile_proxy_object);
memdelete(atlas_source_proxy_object);
}
+
+////// EditorPropertyTilePolygon //////
+
+void EditorPropertyTilePolygon::_add_focusable_children(Node *p_node) {
+ Control *control = Object::cast_to<Control>(p_node);
+ if (control && control->get_focus_mode() != Control::FOCUS_NONE) {
+ add_focusable(control);
+ }
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+ _add_focusable_children(p_node->get_child(i));
+ }
+}
+
+void EditorPropertyTilePolygon::_polygons_changed() {
+ if (String(count_property).is_empty()) {
+ if (base_type == "OccluderPolygon2D") {
+ // Single OccluderPolygon2D.
+ Ref<OccluderPolygon2D> occluder;
+ if (generic_tile_polygon_editor->get_polygon_count() >= 1) {
+ occluder.instantiate();
+ occluder->set_polygon(generic_tile_polygon_editor->get_polygon(0));
+ }
+ emit_changed(get_edited_property(), occluder);
+ } else if (base_type == "NavigationPolygon") {
+ Ref<NavigationPolygon> navigation_polygon;
+ if (generic_tile_polygon_editor->get_polygon_count() >= 1) {
+ navigation_polygon.instantiate();
+ for (int i = 0; i < generic_tile_polygon_editor->get_polygon_count(); i++) {
+ Vector<Vector2> polygon = generic_tile_polygon_editor->get_polygon(i);
+ navigation_polygon->add_outline(polygon);
+ }
+ navigation_polygon->make_polygons_from_outlines();
+ }
+ emit_changed(get_edited_property(), navigation_polygon);
+ }
+ } else {
+ if (base_type.is_empty()) {
+ // Multiple array of vertices.
+ Vector<String> changed_properties;
+ Array values;
+ int count = generic_tile_polygon_editor->get_polygon_count();
+ changed_properties.push_back(count_property);
+ values.push_back(count);
+ for (int i = 0; i < count; i++) {
+ changed_properties.push_back(vformat(element_pattern, i));
+ values.push_back(generic_tile_polygon_editor->get_polygon(i));
+ }
+ emit_signal("multiple_properties_changed", changed_properties, values, false);
+ }
+ }
+}
+
+void EditorPropertyTilePolygon::update_property() {
+ TileSetAtlasSourceEditor::AtlasTileProxyObject *atlas_tile_proxy_object = Object::cast_to<TileSetAtlasSourceEditor::AtlasTileProxyObject>(get_edited_object());
+ ERR_FAIL_COND(!atlas_tile_proxy_object);
+ ERR_FAIL_COND(atlas_tile_proxy_object->get_edited_tiles().is_empty());
+
+ TileSetAtlasSource *tile_set_atlas_source = atlas_tile_proxy_object->get_edited_tile_set_atlas_source();
+ generic_tile_polygon_editor->set_tile_set(Ref<TileSet>(tile_set_atlas_source->get_tile_set()));
+
+ // Set the background
+ Vector2i coords = atlas_tile_proxy_object->get_edited_tiles().front()->get().tile;
+ int alternative = atlas_tile_proxy_object->get_edited_tiles().front()->get().alternative;
+ TileData *tile_data = Object::cast_to<TileData>(tile_set_atlas_source->get_tile_data(coords, alternative));
+ generic_tile_polygon_editor->set_background(tile_set_atlas_source->get_texture(), tile_set_atlas_source->get_tile_texture_region(coords), tile_set_atlas_source->get_tile_effective_texture_offset(coords, alternative), tile_data->get_flip_h(), tile_data->get_flip_v(), tile_data->get_transpose(), tile_data->get_modulate());
+
+ // Reset the polygons.
+ generic_tile_polygon_editor->clear_polygons();
+
+ if (String(count_property).is_empty()) {
+ if (base_type == "OccluderPolygon2D") {
+ // Single OccluderPolygon2D.
+ Ref<OccluderPolygon2D> occluder = get_edited_object()->get(get_edited_property());
+ generic_tile_polygon_editor->clear_polygons();
+ if (occluder.is_valid()) {
+ generic_tile_polygon_editor->add_polygon(occluder->get_polygon());
+ }
+ } else if (base_type == "NavigationPolygon") {
+ // Single OccluderPolygon2D.
+ Ref<NavigationPolygon> navigation_polygon = get_edited_object()->get(get_edited_property());
+ generic_tile_polygon_editor->clear_polygons();
+ if (navigation_polygon.is_valid()) {
+ for (int i = 0; i < navigation_polygon->get_outline_count(); i++) {
+ generic_tile_polygon_editor->add_polygon(navigation_polygon->get_outline(i));
+ }
+ }
+ }
+ } else {
+ int count = get_edited_object()->get(count_property);
+ if (base_type.is_empty()) {
+ // Multiple array of vertices.
+ generic_tile_polygon_editor->clear_polygons();
+ for (int i = 0; i < count; i++) {
+ generic_tile_polygon_editor->add_polygon(get_edited_object()->get(vformat(element_pattern, i)));
+ }
+ }
+ }
+}
+
+void EditorPropertyTilePolygon::setup_single_mode(const StringName &p_property, const String &p_base_type) {
+ set_object_and_property(nullptr, p_property);
+ base_type = p_base_type;
+
+ generic_tile_polygon_editor->set_multiple_polygon_mode(false);
+}
+
+void EditorPropertyTilePolygon::setup_multiple_mode(const StringName &p_property, const StringName &p_count_property, const String &p_element_pattern, const String &p_base_type) {
+ set_object_and_property(nullptr, p_property);
+ count_property = p_count_property;
+ element_pattern = p_element_pattern;
+ base_type = p_base_type;
+
+ generic_tile_polygon_editor->set_multiple_polygon_mode(true);
+}
+
+EditorPropertyTilePolygon::EditorPropertyTilePolygon() {
+ // Setup the polygon editor.
+ generic_tile_polygon_editor = memnew(GenericTilePolygonEditor);
+ generic_tile_polygon_editor->set_use_undo_redo(false);
+ generic_tile_polygon_editor->clear_polygons();
+ add_child(generic_tile_polygon_editor);
+ generic_tile_polygon_editor->connect("polygons_changed", callable_mp(this, &EditorPropertyTilePolygon::_polygons_changed));
+
+ // Add all focussable children of generic_tile_polygon_editor as focussable.
+ _add_focusable_children(generic_tile_polygon_editor);
+}
+
+////// EditorInspectorPluginTileData //////
+
+bool EditorInspectorPluginTileData::can_handle(Object *p_object) {
+ return Object::cast_to<TileSetAtlasSourceEditor::AtlasTileProxyObject>(p_object) != nullptr;
+}
+
+bool EditorInspectorPluginTileData::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) {
+ Vector<String> components = String(p_path).split("/", true, 2);
+ if (components.size() == 2 && components[0].begins_with("occlusion_layer_") && components[0].trim_prefix("occlusion_layer_").is_valid_int()) {
+ // Occlusion layers.
+ int layer_index = components[0].trim_prefix("occlusion_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components[1] == "polygon") {
+ EditorPropertyTilePolygon *ep = memnew(EditorPropertyTilePolygon);
+ ep->setup_single_mode(p_path, "OccluderPolygon2D");
+ add_property_editor(p_path, ep);
+ return true;
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("physics_layer_") && components[0].trim_prefix("physics_layer_").is_valid_int()) {
+ // Physics layers.
+ int layer_index = components[0].trim_prefix("physics_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components[1] == "polygons_count") {
+ EditorPropertyTilePolygon *ep = memnew(EditorPropertyTilePolygon);
+ ep->setup_multiple_mode(vformat("physics_layer_%d/polygons", layer_index), vformat("physics_layer_%d/polygons_count", layer_index), vformat("physics_layer_%d/polygon_%%d/points", layer_index), "");
+ Vector<String> properties;
+ properties.push_back(p_path);
+ int count = p_object->get(vformat("physics_layer_%d/polygons_count", layer_index));
+ for (int i = 0; i < count; i++) {
+ properties.push_back(vformat(vformat("physics_layer_%d/polygon_%d/points", layer_index, i)));
+ }
+ add_property_editor_for_multiple_properties("Polygons", properties, ep);
+ return true;
+ } else if (components.size() == 3 && components[1].begins_with("polygon_") && components[1].trim_prefix("polygon_").is_valid_int()) {
+ int polygon_index = components[1].trim_prefix("polygon_").to_int();
+ ERR_FAIL_COND_V(polygon_index < 0, false);
+ if (components[2] == "points") {
+ return true;
+ }
+ }
+ } else if (components.size() == 2 && components[0].begins_with("navigation_layer_") && components[0].trim_prefix("navigation_layer_").is_valid_int()) {
+ // Navigation layers.
+ int layer_index = components[0].trim_prefix("navigation_layer_").to_int();
+ ERR_FAIL_COND_V(layer_index < 0, false);
+ if (components[1] == "polygon") {
+ EditorPropertyTilePolygon *ep = memnew(EditorPropertyTilePolygon);
+ ep->setup_single_mode(p_path, "NavigationPolygon");
+ add_property_editor(p_path, ep);
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index 501416c340..bd1fd2e7d0 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -43,7 +43,7 @@ class TileSet;
class TileSetAtlasSourceEditor : public HBoxContainer {
GDCLASS(TileSetAtlasSourceEditor, HBoxContainer);
-private:
+public:
// A class to store which tiles are selected.
struct TileSelection {
Vector2i tile = TileSetSource::INVALID_ATLAS_COORDS;
@@ -78,6 +78,7 @@ private:
int get_id();
void edit(Ref<TileSet> p_tile_set, TileSetAtlasSource *p_tile_set_atlas_source, int p_source_id);
+ TileSetAtlasSource *get_edited() { return tile_set_atlas_source; };
};
// -- Proxy object for a tile, needed by the inspector --
@@ -98,6 +99,9 @@ private:
static void _bind_methods();
public:
+ TileSetAtlasSource *get_edited_tile_set_atlas_source() const { return tile_set_atlas_source; };
+ Set<TileSelection> get_edited_tiles() const { return tiles; };
+
// Update the proxyed object.
void edit(TileSetAtlasSource *p_tile_set_atlas_source, Set<TileSelection> p_tiles = Set<TileSelection>());
@@ -106,13 +110,14 @@ private:
}
};
+private:
Ref<TileSet> tile_set;
TileSetAtlasSource *tile_set_atlas_source = nullptr;
int tile_set_atlas_source_id = TileSet::INVALID_SOURCE;
UndoRedo *undo_redo = EditorNode::get_undo_redo();
- bool tile_set_atlas_source_changed_needs_update = false;
+ bool tile_set_changed_needs_update = false;
// -- Properties painting --
VBoxContainer *tile_data_painting_editor_container;
@@ -189,7 +194,6 @@ private:
TILE_CREATE_ALTERNATIVE,
TILE_DELETE,
- ADVANCED_CLEANUP_TILES_OUTSIDE_TEXTURE,
ADVANCED_AUTO_CREATE_TILES,
ADVANCED_AUTO_REMOVE_TILES,
};
@@ -263,7 +267,8 @@ private:
void _auto_remove_tiles();
AcceptDialog *confirm_auto_create_tiles;
- void _tile_set_atlas_source_changed();
+ void _tile_set_changed();
+ void _tile_proxy_object_changed(String p_what);
void _atlas_source_proxy_object_changed(String p_what);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
@@ -280,4 +285,34 @@ public:
~TileSetAtlasSourceEditor();
};
+class EditorPropertyTilePolygon : public EditorProperty {
+ GDCLASS(EditorPropertyTilePolygon, EditorProperty);
+
+ StringName count_property;
+ String element_pattern;
+ String base_type;
+
+ void _add_focusable_children(Node *p_node);
+
+ GenericTilePolygonEditor *generic_tile_polygon_editor;
+ void _polygons_changed();
+
+public:
+ virtual void update_property() override;
+ void setup_single_mode(const StringName &p_property, const String &p_base_type);
+ void setup_multiple_mode(const StringName &p_property, const StringName &p_count_property, const String &p_element_pattern, const String &p_base_type);
+ EditorPropertyTilePolygon();
+};
+
+class EditorInspectorPluginTileData : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorPluginTileData, EditorInspectorPlugin);
+
+ void _occlusion_polygon_set_callback();
+ void _polygons_changed(Object *p_generic_tile_polygon_editor, Object *p_object, const String &p_path);
+
+public:
+ virtual bool can_handle(Object *p_object) override;
+ 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;
+};
+
#endif // TILE_SET_ATLAS_SOURCE_EDITOR_H
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index ba98a7d6b3..b7495dbea9 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -41,10 +41,10 @@
TileSetEditor *TileSetEditor::singleton = nullptr;
-void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+void TileSetEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
ERR_FAIL_COND(!tile_set.is_valid());
- if (!can_drop_data_fw(p_point, p_data, p_from)) {
+ if (!_can_drop_data_fw(p_point, p_data, p_from)) {
return;
}
@@ -81,7 +81,7 @@ void TileSetEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, C
}
}
-bool TileSetEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+bool TileSetEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
ERR_FAIL_COND_V(!tile_set.is_valid(), false);
if (p_from == sources_list) {
@@ -145,14 +145,21 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
Ref<Texture2D> texture;
String item_text;
+ // Common to all type of sources.
+ if (!source->get_name().is_empty()) {
+ item_text = vformat(TTR("%s (id:%d)"), source->get_name(), source_id);
+ }
+
// Atlas source.
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
if (atlas_source) {
texture = atlas_source->get_texture();
- if (texture.is_valid()) {
- item_text = vformat("%s (id:%d)", texture->get_path().get_file(), source_id);
- } else {
- item_text = vformat(TTR("No Texture Atlas Source (id:%d)"), source_id);
+ if (item_text.is_empty()) {
+ if (texture.is_valid()) {
+ item_text = vformat("%s (ID:%d)", texture->get_path().get_file(), source_id);
+ } else {
+ item_text = vformat(TTR("No Texture Atlas Source (ID:%d)"), source_id);
+ }
}
}
@@ -160,12 +167,14 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
TileSetScenesCollectionSource *scene_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source);
if (scene_collection_source) {
texture = get_theme_icon(SNAME("PackedScene"), SNAME("EditorIcons"));
- item_text = vformat(TTR("Scene Collection Source (id:%d)"), source_id);
+ if (item_text.is_empty()) {
+ item_text = vformat(TTR("Scene Collection Source (ID:%d)"), source_id);
+ }
}
// Use default if not valid.
if (item_text.is_empty()) {
- item_text = vformat(TTR("Unknown Type Source (id:%d)"), source_id);
+ item_text = vformat(TTR("Unknown Type Source (ID:%d)"), source_id);
}
if (!texture.is_valid()) {
texture = missing_texture_texture;
@@ -200,7 +209,7 @@ void TileSetEditor::_update_sources_list(int force_selected_id) {
_source_selected(sources_list->get_current());
// Synchronize the lists.
- TilesEditor::get_singleton()->set_sources_lists_current(sources_list->get_current());
+ TilesEditorPlugin::get_singleton()->set_sources_lists_current(sources_list->get_current());
}
void TileSetEditor::_source_selected(int p_source_index) {
@@ -318,6 +327,7 @@ void TileSetEditor::_notification(int p_what) {
tile_set->set_edited(true);
}
_update_sources_list();
+ _update_patterns_list();
tile_set_changed_needs_update = false;
}
break;
@@ -326,15 +336,242 @@ void TileSetEditor::_notification(int p_what) {
}
}
+void TileSetEditor::_patterns_item_list_gui_input(const Ref<InputEvent> &p_event) {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ if (ED_IS_SHORTCUT("tiles_editor/delete", p_event) && p_event->is_pressed() && !p_event->is_echo()) {
+ Vector<int> selected = patterns_item_list->get_selected_items();
+ undo_redo->create_action(TTR("Remove TileSet patterns"));
+ for (int i = 0; i < selected.size(); i++) {
+ int pattern_index = selected[i];
+ undo_redo->add_do_method(*tile_set, "remove_pattern", pattern_index);
+ undo_redo->add_undo_method(*tile_set, "add_pattern", tile_set->get_pattern(pattern_index), pattern_index);
+ }
+ undo_redo->commit_action();
+ patterns_item_list->accept_event();
+ }
+}
+
+void TileSetEditor::_pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture) {
+ // TODO optimize ?
+ for (int i = 0; i < patterns_item_list->get_item_count(); i++) {
+ if (patterns_item_list->get_item_metadata(i) == p_pattern) {
+ patterns_item_list->set_item_icon(i, p_texture);
+ break;
+ }
+ }
+}
+
+void TileSetEditor::_update_patterns_list() {
+ ERR_FAIL_COND(!tile_set.is_valid());
+
+ // Recreate the items.
+ patterns_item_list->clear();
+ for (int i = 0; i < tile_set->get_patterns_count(); i++) {
+ int id = patterns_item_list->add_item("");
+ patterns_item_list->set_item_metadata(id, tile_set->get_pattern(i));
+ TilesEditorPlugin::get_singleton()->queue_pattern_preview(tile_set, tile_set->get_pattern(i), callable_mp(this, &TileSetEditor::_pattern_preview_done));
+ }
+
+ // Update the label visibility.
+ patterns_help_label->set_visible(patterns_item_list->get_item_count() == 0);
+}
+
void TileSetEditor::_tile_set_changed() {
tile_set_changed_needs_update = true;
}
+void TileSetEditor::_tab_changed(int p_tab_changed) {
+ split_container->set_visible(p_tab_changed == 0);
+ patterns_item_list->set_visible(p_tab_changed == 1);
+}
+
+void TileSetEditor::_move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos) {
+ UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
+ ERR_FAIL_COND(!undo_redo);
+
+ TileSet *tile_set = Object::cast_to<TileSet>(p_edited);
+ if (!tile_set) {
+ return;
+ }
+
+ Vector<String> components = String(p_array_prefix).split("/", true, 2);
+
+ // Compute the array indices to save.
+ int begin = 0;
+ int end;
+ if (p_array_prefix == "occlusion_layer_") {
+ end = tile_set->get_occlusion_layers_count();
+ } else if (p_array_prefix == "physics_layer_") {
+ end = tile_set->get_physics_layers_count();
+ } else if (p_array_prefix == "terrain_set_") {
+ end = tile_set->get_terrain_sets_count();
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
+ int terrain_set = components[0].trim_prefix("terrain_set_").to_int();
+ end = tile_set->get_terrains_count(terrain_set);
+ } else if (p_array_prefix == "navigation_layer_") {
+ end = tile_set->get_navigation_layers_count();
+ } else if (p_array_prefix == "custom_data_layer_") {
+ end = tile_set->get_custom_data_layers_count();
+ } else {
+ ERR_FAIL_MSG("Invalid array prefix for TileSet.");
+ }
+ if (p_from_index < 0) {
+ // Adding new.
+ if (p_to_pos >= 0) {
+ begin = p_to_pos;
+ } else {
+ end = 0; // Nothing to save when adding at the end.
+ }
+ } else if (p_to_pos < 0) {
+ // Removing.
+ begin = p_from_index;
+ } else {
+ // Moving.
+ begin = MIN(p_from_index, p_to_pos);
+ end = MIN(MAX(p_from_index, p_to_pos) + 1, end);
+ }
+
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
+ // Save layers' properties.
+ List<PropertyInfo> properties;
+ tile_set->get_property_list(&properties);
+ for (PropertyInfo pi : properties) {
+ if (pi.name.begins_with(p_array_prefix)) {
+ String str = pi.name.trim_prefix(p_array_prefix);
+ int to_char_index = 0;
+ while (to_char_index < str.length()) {
+ if (str[to_char_index] < '0' || str[to_char_index] > '9') {
+ break;
+ }
+ to_char_index++;
+ }
+ if (to_char_index > 0) {
+ int array_index = str.left(to_char_index).to_int();
+ if (array_index >= begin && array_index < end) {
+ ADD_UNDO(tile_set, pi.name);
+ }
+ }
+ }
+ }
+
+ // Save properties for TileSetAtlasSources tile data
+ for (int i = 0; i < tile_set->get_source_count(); i++) {
+ int source_id = tile_set->get_source_id(i);
+
+ Ref<TileSetAtlasSource> tas = tile_set->get_source(source_id);
+ if (tas.is_valid()) {
+ for (int j = 0; j < tas->get_tiles_count(); j++) {
+ Vector2i tile_id = tas->get_tile_id(j);
+ for (int k = 0; k < tas->get_alternative_tiles_count(tile_id); k++) {
+ int alternative_id = tas->get_alternative_tile_id(tile_id, k);
+ TileData *tile_data = Object::cast_to<TileData>(tas->get_tile_data(tile_id, alternative_id));
+ ERR_FAIL_COND(!tile_data);
+
+ // Actually saving stuff.
+ if (p_array_prefix == "occlusion_layer_") {
+ for (int layer_index = begin; layer_index < end; layer_index++) {
+ ADD_UNDO(tile_data, vformat("occlusion_layer_%d/polygon", layer_index));
+ }
+ } else if (p_array_prefix == "physics_layer_") {
+ for (int layer_index = begin; layer_index < end; layer_index++) {
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/polygons_count", layer_index));
+ for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(layer_index); polygon_index++) {
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", layer_index, polygon_index));
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", layer_index, polygon_index));
+ ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", layer_index, polygon_index));
+ }
+ }
+ } else if (p_array_prefix == "terrain_set_") {
+ ADD_UNDO(tile_data, "terrain_set");
+ for (int terrain_set_index = begin; terrain_set_index < end; terrain_set_index++) {
+ for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
+ if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
+ }
+ }
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
+ for (int terrain_index = 0; terrain_index < TileSet::CELL_NEIGHBOR_MAX; terrain_index++) {
+ TileSet::CellNeighbor bit = TileSet::CellNeighbor(terrain_index);
+ if (tile_data->is_valid_peering_bit_terrain(bit)) {
+ ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[terrain_index]));
+ }
+ }
+ } else if (p_array_prefix == "navigation_layer_") {
+ for (int layer_index = begin; layer_index < end; layer_index++) {
+ ADD_UNDO(tile_data, vformat("navigation_layer_%d/polygon", layer_index));
+ }
+ } else if (p_array_prefix == "custom_data_layer_") {
+ for (int layer_index = begin; layer_index < end; layer_index++) {
+ ADD_UNDO(tile_data, vformat("custom_data_%d", layer_index));
+ }
+ }
+ }
+ }
+ }
+ }
+#undef ADD_UNDO
+
+ // Add do method.
+ if (p_array_prefix == "occlusion_layer_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_occlusion_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_occlusion_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_occlusion_layer", p_from_index, p_to_pos);
+ }
+ } else if (p_array_prefix == "physics_layer_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_physics_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_physics_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_physics_layer", p_from_index, p_to_pos);
+ }
+ } else if (p_array_prefix == "terrain_set_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_terrain_set", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_terrain_set", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_terrain_set", p_from_index, p_to_pos);
+ }
+ } else if (components.size() >= 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrain_") {
+ int terrain_set = components[0].trim_prefix("terrain_set_").to_int();
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_terrain", terrain_set, p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_terrain", terrain_set, p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_terrain", terrain_set, p_from_index, p_to_pos);
+ }
+ } else if (p_array_prefix == "navigation_layer_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_navigation_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_navigation_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_navigation_layer", p_from_index, p_to_pos);
+ }
+ } else if (p_array_prefix == "custom_data_layer_") {
+ if (p_from_index < 0) {
+ undo_redo->add_do_method(tile_set, "add_custom_data_layer", p_to_pos);
+ } else if (p_to_pos < 0) {
+ undo_redo->add_do_method(tile_set, "remove_custom_data_layer", p_from_index);
+ } else {
+ undo_redo->add_do_method(tile_set, "move_custom_data_layer", p_from_index, p_to_pos);
+ }
+ }
+}
+
void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value) {
UndoRedo *undo_redo = Object::cast_to<UndoRedo>(p_undo_redo);
ERR_FAIL_COND(!undo_redo);
-#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, tile_data->get(property));
+#define ADD_UNDO(obj, property) undo_redo->add_undo_property(obj, property, obj->get(property));
TileSet *tile_set = Object::cast_to<TileSet>(p_edited);
if (tile_set) {
Vector<String> components = p_property.split("/", true, 3);
@@ -350,30 +587,7 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
TileData *tile_data = Object::cast_to<TileData>(tas->get_tile_data(tile_id, alternative_id));
ERR_FAIL_COND(!tile_data);
- if (p_property == "occlusion_layers_count") {
- int new_layer_count = p_new_value;
- int old_layer_count = tile_set->get_occlusion_layers_count();
- if (new_layer_count < old_layer_count) {
- for (int occclusion_layer_index = new_layer_count - 1; occclusion_layer_index < old_layer_count; occclusion_layer_index++) {
- ADD_UNDO(tile_data, vformat("occlusion_layer_%d/polygon", occclusion_layer_index));
- }
- }
- } else if (p_property == "physics_layers_count") {
- int new_layer_count = p_new_value;
- int old_layer_count = tile_set->get_physics_layers_count();
- if (new_layer_count < old_layer_count) {
- for (int physics_layer_index = new_layer_count - 1; physics_layer_index < old_layer_count; physics_layer_index++) {
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygons_count", physics_layer_index));
- for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(physics_layer_index); polygon_index++) {
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/points", physics_layer_index, polygon_index));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way", physics_layer_index, polygon_index));
- ADD_UNDO(tile_data, vformat("physics_layer_%d/polygon_%d/one_way_margin", physics_layer_index, polygon_index));
- }
- }
- }
- } else if ((p_property == "terrains_sets_count" && tile_data->get_terrain_set() >= (int)p_new_value) ||
- (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") ||
- (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "terrains_count" && tile_data->get_terrain_set() == components[0].trim_prefix("terrain_set_").to_int() && (int)p_new_value < tile_set->get_terrains_count(tile_data->get_terrain_set()))) {
+ if (components.size() == 2 && components[0].begins_with("terrain_set_") && components[0].trim_prefix("terrain_set_").is_valid_int() && components[1] == "mode") {
ADD_UNDO(tile_data, "terrain_set");
for (int l = 0; l < TileSet::CELL_NEIGHBOR_MAX; l++) {
TileSet::CellNeighbor bit = TileSet::CellNeighbor(l);
@@ -381,22 +595,6 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
ADD_UNDO(tile_data, "terrains_peering_bit/" + String(TileSet::CELL_NEIGHBOR_ENUM_TO_TEXT[l]));
}
}
- } else if (p_property == "navigation_layers_count") {
- int new_layer_count = p_new_value;
- int old_layer_count = tile_set->get_navigation_layers_count();
- if (new_layer_count < old_layer_count) {
- for (int navigation_layer_index = new_layer_count - 1; navigation_layer_index < old_layer_count; navigation_layer_index++) {
- ADD_UNDO(tile_data, vformat("navigation_layer_%d/polygon", navigation_layer_index));
- }
- }
- } else if (p_property == "custom_data_layers_count") {
- int new_layer_count = p_new_value;
- int old_layer_count = tile_set->get_custom_data_layers_count();
- if (new_layer_count < old_layer_count) {
- for (int custom_data_layer_index = new_layer_count - 1; custom_data_layer_index < old_layer_count; custom_data_layer_index++) {
- ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer_index));
- }
- }
} else if (components.size() == 2 && components[0].begins_with("custom_data_layer_") && components[0].trim_prefix("custom_data_layer_").is_valid_int() && components[1] == "type") {
int custom_data_layer = components[0].trim_prefix("custom_data_layer_").is_valid_int();
ADD_UNDO(tile_data, vformat("custom_data_%d", custom_data_layer));
@@ -410,8 +608,8 @@ void TileSetEditor::_undo_redo_inspector_callback(Object *p_undo_redo, Object *p
}
void TileSetEditor::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TileSetEditor::can_drop_data_fw);
- ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetEditor::drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TileSetEditor::_can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetEditor::_drop_data_fw);
}
void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
@@ -431,6 +629,7 @@ void TileSetEditor::edit(Ref<TileSet> p_tile_set) {
if (tile_set.is_valid()) {
tile_set->connect("changed", callable_mp(this, &TileSetEditor::_tile_set_changed));
_update_sources_list();
+ _update_patterns_list();
}
tile_set_atlas_source_editor->hide();
@@ -443,8 +642,21 @@ TileSetEditor::TileSetEditor() {
set_process_internal(true);
+ // TabBar.
+ tabs_bar = memnew(TabBar);
+ tabs_bar->set_clip_tabs(false);
+ tabs_bar->add_tab(TTR("Tiles"));
+ tabs_bar->add_tab(TTR("Patterns"));
+ tabs_bar->connect("tab_changed", callable_mp(this, &TileSetEditor::_tab_changed));
+
+ tile_set_toolbar = memnew(HBoxContainer);
+ tile_set_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+ tile_set_toolbar->add_child(tabs_bar);
+ add_child(tile_set_toolbar);
+
+ //// Tiles ////
// Split container.
- HSplitContainer *split_container = memnew(HSplitContainer);
+ split_container = memnew(HSplitContainer);
split_container->set_name(TTR("Tiles"));
split_container->set_h_size_flags(SIZE_EXPAND_FILL);
split_container->set_v_size_flags(SIZE_EXPAND_FILL);
@@ -463,14 +675,14 @@ TileSetEditor::TileSetEditor() {
sources_list->set_h_size_flags(SIZE_EXPAND_FILL);
sources_list->set_v_size_flags(SIZE_EXPAND_FILL);
sources_list->connect("item_selected", callable_mp(this, &TileSetEditor::_source_selected));
- sources_list->connect("item_selected", callable_mp(TilesEditor::get_singleton(), &TilesEditor::set_sources_lists_current));
- sources_list->connect("visibility_changed", callable_mp(TilesEditor::get_singleton(), &TilesEditor::synchronize_sources_list), varray(sources_list));
+ sources_list->connect("item_selected", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::set_sources_lists_current));
+ sources_list->connect("visibility_changed", callable_mp(TilesEditorPlugin::get_singleton(), &TilesEditorPlugin::synchronize_sources_list), varray(sources_list));
sources_list->set_texture_filter(CanvasItem::TEXTURE_FILTER_NEAREST);
sources_list->set_drag_forwarding(this);
split_container_left_side->add_child(sources_list);
HBoxContainer *sources_bottom_actions = memnew(HBoxContainer);
- sources_bottom_actions->set_alignment(HBoxContainer::ALIGN_END);
+ sources_bottom_actions->set_alignment(BoxContainer::ALIGNMENT_END);
split_container_left_side->add_child(sources_bottom_actions);
sources_delete_button = memnew(Button);
@@ -510,8 +722,8 @@ TileSetEditor::TileSetEditor() {
no_source_selected_label->set_text(TTR("No TileSet source selected. Select or create a TileSet source."));
no_source_selected_label->set_h_size_flags(SIZE_EXPAND_FILL);
no_source_selected_label->set_v_size_flags(SIZE_EXPAND_FILL);
- no_source_selected_label->set_align(Label::ALIGN_CENTER);
- no_source_selected_label->set_valign(Label::VALIGN_CENTER);
+ no_source_selected_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ no_source_selected_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
split_container_right_side->add_child(no_source_selected_label);
// Atlases editor.
@@ -530,7 +742,26 @@ TileSetEditor::TileSetEditor() {
split_container_right_side->add_child(tile_set_scenes_collection_source_editor);
tile_set_scenes_collection_source_editor->hide();
+ //// Patterns ////
+ int thumbnail_size = 64;
+ patterns_item_list = memnew(ItemList);
+ patterns_item_list->set_max_columns(0);
+ patterns_item_list->set_icon_mode(ItemList::ICON_MODE_TOP);
+ patterns_item_list->set_fixed_column_width(thumbnail_size * 3 / 2);
+ patterns_item_list->set_max_text_lines(2);
+ patterns_item_list->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size));
+ patterns_item_list->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ patterns_item_list->connect("gui_input", callable_mp(this, &TileSetEditor::_patterns_item_list_gui_input));
+ add_child(patterns_item_list);
+ patterns_item_list->hide();
+
+ patterns_help_label = memnew(Label);
+ patterns_help_label->set_text(TTR("Add new patterns in the TileMap editing mode."));
+ patterns_help_label->set_anchors_and_offsets_preset(Control::PRESET_CENTER);
+ patterns_item_list->add_child(patterns_help_label);
+
// Registers UndoRedo inspector callback.
+ EditorNode::get_singleton()->get_editor_data().add_move_array_element_function(SNAME("TileSet"), callable_mp(this, &TileSetEditor::_move_tile_set_array_element));
EditorNode::get_singleton()->get_editor_data().add_undo_redo_inspector_hook_callback(callable_mp(this, &TileSetEditor::_undo_redo_inspector_callback));
}
diff --git a/editor/plugins/tiles/tile_set_editor.h b/editor/plugins/tiles/tile_set_editor.h
index 970e3fabb6..58312ce3df 100644
--- a/editor/plugins/tiles/tile_set_editor.h
+++ b/editor/plugins/tiles/tile_set_editor.h
@@ -46,13 +46,22 @@ class TileSetEditor : public VBoxContainer {
private:
Ref<TileSet> tile_set;
bool tile_set_changed_needs_update = false;
+ HSplitContainer *split_container;
+ // TabBar.
+ HBoxContainer *tile_set_toolbar;
+ TabBar *tabs_bar;
+
+ // Tiles.
Label *no_source_selected_label;
TileSetAtlasSourceEditor *tile_set_atlas_source_editor;
TileSetScenesCollectionSourceEditor *tile_set_scenes_collection_source_editor;
UndoRedo *undo_redo = EditorNode::get_undo_redo();
+ void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+
void _update_sources_list(int force_selected_id = -1);
// Sources management.
@@ -69,8 +78,18 @@ private:
AtlasMergingDialog *atlas_merging_dialog;
TileProxiesManagerDialog *tile_proxies_manager_dialog;
+ // Patterns.
+ ItemList *patterns_item_list;
+ Label *patterns_help_label;
+ void _patterns_item_list_gui_input(const Ref<InputEvent> &p_event);
+ void _pattern_preview_done(Ref<TileMapPattern> p_pattern, Ref<Texture2D> p_texture);
+ bool select_last_pattern = false;
+ void _update_patterns_list();
+
void _tile_set_changed();
+ void _tab_changed(int p_tab_changed);
+ void _move_tile_set_array_element(Object *p_undo_redo, Object *p_edited, String p_array_prefix, int p_from_index, int p_to_pos);
void _undo_redo_inspector_callback(Object *p_undo_redo, Object *p_edited, String p_property, Variant p_new_value);
protected:
@@ -81,8 +100,6 @@ public:
_FORCE_INLINE_ static TileSetEditor *get_singleton() { return singleton; }
void edit(Ref<TileSet> p_tile_set);
- void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
- bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
TileSetEditor();
~TileSetEditor();
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
index f74b3bf9c2..035fe57d95 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -56,10 +56,15 @@ int TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::get
}
bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_set(const StringName &p_name, const Variant &p_value) {
+ String name = p_name;
+ if (name == "name") {
+ // Use the resource_name property to store the source's name.
+ name = "resource_name";
+ }
bool valid = false;
- tile_set_scenes_collection_source->set(p_name, p_value, &valid);
+ tile_set_scenes_collection_source->set(name, p_value, &valid);
if (valid) {
- emit_signal(SNAME("changed"), String(p_name).utf8().get_data());
+ emit_signal(SNAME("changed"), String(name).utf8().get_data());
}
return valid;
}
@@ -68,11 +73,20 @@ bool TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_g
if (!tile_set_scenes_collection_source) {
return false;
}
+ String name = p_name;
+ if (name == "name") {
+ // Use the resource_name property to store the source's name.
+ name = "resource_name";
+ }
bool valid = false;
- r_ret = tile_set_scenes_collection_source->get(p_name, &valid);
+ r_ret = tile_set_scenes_collection_source->get(name, &valid);
return valid;
}
+void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
+ p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
+}
+
void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::_bind_methods() {
// -- Shape and layout --
ClassDB::bind_method(D_METHOD("set_id", "id"), &TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::set_id);
@@ -89,6 +103,10 @@ void TileSetScenesCollectionSourceEditor::TileSetScenesCollectionProxyObject::ed
ERR_FAIL_COND(p_source_id < 0);
ERR_FAIL_COND(p_tile_set->get_source(p_source_id) != p_tile_set_scenes_collection_source);
+ if (tile_set == p_tile_set && tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && source_id == p_source_id) {
+ return;
+ }
+
// Disconnect to changes.
if (tile_set_scenes_collection_source) {
tile_set_scenes_collection_source->disconnect(CoreStringNames::get_singleton()->property_list_changed, callable_mp((Object *)this, &Object::notify_property_list_changed));
@@ -174,6 +192,10 @@ void TileSetScenesCollectionSourceEditor::SceneTileProxyObject::edit(TileSetScen
ERR_FAIL_COND(!p_tile_set_scenes_collection_source);
ERR_FAIL_COND(!p_tile_set_scenes_collection_source->has_scene_tile_id(p_scene_id));
+ if (tile_set_scenes_collection_source == p_tile_set_scenes_collection_source && scene_id == p_scene_id) {
+ return;
+ }
+
tile_set_scenes_collection_source = p_tile_set_scenes_collection_source;
scene_id = p_scene_id;
@@ -363,8 +385,8 @@ void TileSetScenesCollectionSourceEditor::edit(Ref<TileSet> p_tile_set, TileSetS
_update_tile_inspector();
}
-void TileSetScenesCollectionSourceEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
- if (!can_drop_data_fw(p_point, p_data, p_from)) {
+void TileSetScenesCollectionSourceEditor::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
+ if (!_can_drop_data_fw(p_point, p_data, p_from)) {
return;
}
@@ -390,7 +412,7 @@ void TileSetScenesCollectionSourceEditor::drop_data_fw(const Point2 &p_point, co
}
}
-bool TileSetScenesCollectionSourceEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
+bool TileSetScenesCollectionSourceEditor::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
if (p_from == scene_tiles_list) {
Dictionary d = p_data;
@@ -425,8 +447,8 @@ void TileSetScenesCollectionSourceEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("source_id_changed", PropertyInfo(Variant::INT, "source_id")));
ClassDB::bind_method(D_METHOD("_scene_thumbnail_done"), &TileSetScenesCollectionSourceEditor::_scene_thumbnail_done);
- ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &TileSetScenesCollectionSourceEditor::can_drop_data_fw);
- ClassDB::bind_method(D_METHOD("drop_data_fw"), &TileSetScenesCollectionSourceEditor::drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TileSetScenesCollectionSourceEditor::_can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TileSetScenesCollectionSourceEditor::_drop_data_fw);
}
TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
@@ -437,7 +459,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
// Middle panel.
ScrollContainer *middle_panel = memnew(ScrollContainer);
- middle_panel->set_enable_h_scroll(false);
+ middle_panel->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
middle_panel->set_custom_minimum_size(Size2i(200, 0) * EDSCALE);
split_container_right_side->add_child(middle_panel);
@@ -455,7 +477,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
scenes_collection_source_inspector = memnew(EditorInspector);
scenes_collection_source_inspector->set_undo_redo(undo_redo);
- scenes_collection_source_inspector->set_enable_v_scroll(false);
+ scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
scenes_collection_source_inspector->edit(scenes_collection_source_proxy_object);
middle_vbox_container->add_child(scenes_collection_source_inspector);
@@ -471,7 +493,7 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
tile_inspector = memnew(EditorInspector);
tile_inspector->set_undo_redo(undo_redo);
- tile_inspector->set_enable_v_scroll(false);
+ tile_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
tile_inspector->edit(tile_proxy_object);
tile_inspector->set_use_folding(true);
middle_vbox_container->add_child(tile_inspector);
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
index 195aa79bc4..4e33128be5 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.h
@@ -51,6 +51,7 @@ private:
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
@@ -124,14 +125,15 @@ private:
void _update_scenes_list();
void _update_action_buttons();
+ void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
+ bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
+
protected:
void _notification(int p_what);
static void _bind_methods();
public:
void edit(Ref<TileSet> p_tile_set, TileSetScenesCollectionSource *p_tile_set_scenes_collection_source, int p_source_id);
- void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
- bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
TileSetScenesCollectionSourceEditor();
~TileSetScenesCollectionSourceEditor();
};
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 79b869b511..47dfc57b0f 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -30,99 +30,184 @@
#include "tiles_editor_plugin.h"
+#include "core/os/mutex.h"
+
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
#include "editor/plugins/canvas_item_editor_plugin.h"
#include "scene/2d/tile_map.h"
-#include "scene/resources/tile_set.h"
-
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/control.h"
#include "scene/gui/separator.h"
+#include "scene/resources/tile_set.h"
#include "tile_set_editor.h"
-TilesEditor *TilesEditor::singleton = nullptr;
+TilesEditorPlugin *TilesEditorPlugin::singleton = nullptr;
+
+void TilesEditorPlugin::_preview_frame_started() {
+ RS::get_singleton()->request_frame_drawn_callback(callable_mp(const_cast<TilesEditorPlugin *>(this), &TilesEditorPlugin::_pattern_preview_done));
+}
+
+void TilesEditorPlugin::_pattern_preview_done() {
+ pattern_preview_done.post();
+}
+
+void TilesEditorPlugin::_thread_func(void *ud) {
+ TilesEditorPlugin *te = (TilesEditorPlugin *)ud;
+ te->_thread();
+}
+
+void TilesEditorPlugin::_thread() {
+ pattern_thread_exited.clear();
+ while (!pattern_thread_exit.is_set()) {
+ pattern_preview_sem.wait();
+
+ pattern_preview_mutex.lock();
+ if (pattern_preview_queue.size()) {
+ QueueItem item = pattern_preview_queue.front()->get();
+ pattern_preview_queue.pop_front();
+ pattern_preview_mutex.unlock();
+
+ int thumbnail_size = EditorSettings::get_singleton()->get("filesystem/file_dialog/thumbnail_size");
+ thumbnail_size *= EDSCALE;
+ Vector2 thumbnail_size2 = Vector2(thumbnail_size, thumbnail_size);
+
+ if (item.pattern.is_valid() && !item.pattern->is_empty()) {
+ // Generate the pattern preview
+ SubViewport *viewport = memnew(SubViewport);
+ viewport->set_size(thumbnail_size2);
+ viewport->set_disable_input(true);
+ viewport->set_transparent_background(true);
+ viewport->set_update_mode(SubViewport::UPDATE_ONCE);
+
+ TileMap *tile_map = memnew(TileMap);
+ tile_map->set_tileset(item.tile_set);
+ tile_map->set_pattern(0, Vector2(), item.pattern);
+ viewport->add_child(tile_map);
+
+ TypedArray<Vector2i> used_cells = tile_map->get_used_cells(0);
+
+ Rect2 encompassing_rect = Rect2();
+ encompassing_rect.set_position(tile_map->map_to_world(used_cells[0]));
+ for (int i = 0; i < used_cells.size(); i++) {
+ Vector2i cell = used_cells[i];
+ Vector2 world_pos = tile_map->map_to_world(cell);
+ encompassing_rect.expand_to(world_pos);
+
+ // Texture.
+ Ref<TileSetAtlasSource> atlas_source = tile_set->get_source(tile_map->get_cell_source_id(0, cell));
+ if (atlas_source.is_valid()) {
+ Vector2i coords = tile_map->get_cell_atlas_coords(0, cell);
+ int alternative = tile_map->get_cell_alternative_tile(0, cell);
+
+ Vector2 center = world_pos - atlas_source->get_tile_effective_texture_offset(coords, alternative);
+ encompassing_rect.expand_to(center - atlas_source->get_tile_texture_region(coords).size / 2);
+ encompassing_rect.expand_to(center + atlas_source->get_tile_texture_region(coords).size / 2);
+ }
+ }
+
+ Vector2 scale = thumbnail_size2 / MAX(encompassing_rect.size.x, encompassing_rect.size.y);
+ tile_map->set_scale(scale);
+ tile_map->set_position(-(scale * encompassing_rect.get_center()) + thumbnail_size2 / 2);
+
+ // Add the viewport at the lasst moment to avoid rendering too early.
+ EditorNode::get_singleton()->add_child(viewport);
-void TilesEditor::_notification(int p_what) {
+ RS::get_singleton()->connect(SNAME("frame_pre_draw"), callable_mp(const_cast<TilesEditorPlugin *>(this), &TilesEditorPlugin::_preview_frame_started), Vector<Variant>(), Object::CONNECT_ONESHOT);
+
+ pattern_preview_done.wait();
+
+ Ref<Image> image = viewport->get_texture()->get_image();
+ Ref<ImageTexture> image_texture;
+ image_texture.instantiate();
+ image_texture->create_from_image(image);
+
+ // Find the index for the given pattern. TODO: optimize.
+ Variant args[] = { item.pattern, image_texture };
+ const Variant *args_ptr[] = { &args[0], &args[1] };
+ Variant r;
+ Callable::CallError error;
+ item.callback.call(args_ptr, 2, r, error);
+
+ viewport->queue_delete();
+ } else {
+ pattern_preview_mutex.unlock();
+ }
+ }
+ }
+ pattern_thread_exited.set();
+}
+
+void TilesEditorPlugin::_tile_map_changed() {
+ tile_map_changed_needs_update = true;
+}
+
+void TilesEditorPlugin::_update_editors() {
+ // If tile_map is not edited, we change the edited only if we are not editing a tile_set.
+ tileset_editor->edit(tile_set);
+ TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
+ if (tile_map) {
+ tilemap_editor->edit(tile_map);
+ } else {
+ tilemap_editor->edit(nullptr);
+ }
+
+ // Update the viewport.
+ CanvasItemEditor::get_singleton()->update_viewport();
+}
+
+void TilesEditorPlugin::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
- case NOTIFICATION_THEME_CHANGED: {
- tileset_tilemap_switch_button->set_icon(get_theme_icon(SNAME("TileSet"), SNAME("EditorIcons")));
- } break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (tile_map_changed_needs_update) {
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
tile_set = tile_map->get_tileset();
}
- _update_switch_button();
_update_editors();
+ tile_map_changed_needs_update = false;
}
} break;
}
}
-void TilesEditor::_tile_map_changed() {
- tile_map_changed_needs_update = true;
-}
-
-void TilesEditor::_update_switch_button() {
- // Force the buttons status if needed.
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (tile_map && !tile_set.is_valid()) {
- tileset_tilemap_switch_button->set_pressed(false);
- } else if (!tile_map && tile_set.is_valid()) {
- tileset_tilemap_switch_button->set_pressed(true);
- }
-}
-
-void TilesEditor::_update_editors() {
- // Set editors visibility.
- tilemap_toolbar->set_visible(!tileset_tilemap_switch_button->is_pressed());
- tilemap_editor->set_visible(!tileset_tilemap_switch_button->is_pressed());
- tileset_editor->set_visible(tileset_tilemap_switch_button->is_pressed());
-
- // Enable/disable the switch button.
- if (!tileset_tilemap_switch_button->is_pressed()) {
- if (!tile_set.is_valid()) {
- tileset_tilemap_switch_button->set_disabled(true);
- tileset_tilemap_switch_button->set_tooltip(TTR("This TileMap has no assigned TileSet, assign a TileSet to this TileMap to edit it."));
- } else {
- tileset_tilemap_switch_button->set_disabled(false);
- tileset_tilemap_switch_button->set_tooltip(TTR("Switch between TileSet/TileMap editor."));
- }
- } else {
+void TilesEditorPlugin::make_visible(bool p_visible) {
+ if (p_visible) {
+ // Disable and hide invalid editors.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (!tile_map) {
- tileset_tilemap_switch_button->set_disabled(true);
- tileset_tilemap_switch_button->set_tooltip(TTR("You are editing a TileSet resource. Select a TileMap node to paint."));
+ tileset_editor_button->set_visible(tile_set.is_valid());
+ tilemap_editor_button->set_visible(tile_map);
+ if (tile_map) {
+ editor_node->make_bottom_panel_item_visible(tilemap_editor);
} else {
- tileset_tilemap_switch_button->set_disabled(false);
- tileset_tilemap_switch_button->set_tooltip(TTR("Switch between TileSet/TileMap editor."));
+ editor_node->make_bottom_panel_item_visible(tileset_editor);
}
- }
- // If tile_map is not edited, we change the edited only if we are not editing a tile_set.
- TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
- if (tile_map) {
- tilemap_editor->edit(tile_map);
} else {
- tilemap_editor->edit(nullptr);
+ tileset_editor_button->hide();
+ tilemap_editor_button->hide();
+ editor_node->hide_bottom_panel();
}
- tileset_editor->edit(tile_set);
+}
- // Update the viewport
- CanvasItemEditor::get_singleton()->update_viewport();
+void TilesEditorPlugin::queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback) {
+ ERR_FAIL_COND(!p_tile_set.is_valid());
+ ERR_FAIL_COND(!p_pattern.is_valid());
+ {
+ MutexLock lock(pattern_preview_mutex);
+ pattern_preview_queue.push_back({ p_tile_set, p_pattern, p_callback });
+ }
+ pattern_preview_sem.post();
}
-void TilesEditor::set_sources_lists_current(int p_current) {
+void TilesEditorPlugin::set_sources_lists_current(int p_current) {
atlas_sources_lists_current = p_current;
}
-void TilesEditor::synchronize_sources_list(Object *p_current) {
+void TilesEditorPlugin::synchronize_sources_list(Object *p_current) {
ItemList *item_list = Object::cast_to<ItemList>(p_current);
ERR_FAIL_COND(!item_list);
@@ -136,25 +221,25 @@ void TilesEditor::synchronize_sources_list(Object *p_current) {
}
}
-void TilesEditor::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
+void TilesEditorPlugin::set_atlas_view_transform(float p_zoom, Vector2 p_scroll) {
atlas_view_zoom = p_zoom;
atlas_view_scroll = p_scroll;
}
-void TilesEditor::synchronize_atlas_view(Object *p_current) {
+void TilesEditorPlugin::synchronize_atlas_view(Object *p_current) {
TileAtlasView *tile_atlas_view = Object::cast_to<TileAtlasView>(p_current);
ERR_FAIL_COND(!tile_atlas_view);
if (tile_atlas_view->is_visible_in_tree()) {
- tile_atlas_view->set_transform(atlas_view_zoom, Vector2(atlas_view_scroll.x, atlas_view_scroll.y));
+ tile_atlas_view->set_transform(atlas_view_zoom, atlas_view_scroll);
}
}
-void TilesEditor::edit(Object *p_object) {
+void TilesEditorPlugin::edit(Object *p_object) {
// Disconnect to changes.
TileMap *tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
if (tile_map) {
- tile_map->disconnect("changed", callable_mp(this, &TilesEditor::_tile_map_changed));
+ tile_map->disconnect("changed", callable_mp(this, &TilesEditorPlugin::_tile_map_changed));
}
// Update edited objects.
@@ -164,114 +249,75 @@ void TilesEditor::edit(Object *p_object) {
tile_map_id = p_object->get_instance_id();
tile_map = Object::cast_to<TileMap>(ObjectDB::get_instance(tile_map_id));
tile_set = tile_map->get_tileset();
+ editor_node->make_bottom_panel_item_visible(tilemap_editor);
} else if (p_object->is_class("TileSet")) {
tile_set = Ref<TileSet>(p_object);
if (tile_map) {
- if (tile_map->get_tileset() != tile_set) {
+ if (tile_map->get_tileset() != tile_set || !tile_map->is_inside_tree()) {
tile_map = nullptr;
+ tile_map_id = ObjectID();
}
}
- }
-
- // Update pressed status button.
- if (p_object->is_class("TileMap")) {
- tileset_tilemap_switch_button->set_pressed(false);
- } else if (p_object->is_class("TileSet")) {
- tileset_tilemap_switch_button->set_pressed(true);
+ editor_node->make_bottom_panel_item_visible(tileset_editor);
}
}
// Update the editors.
- _update_switch_button();
_update_editors();
// Add change listener.
if (tile_map) {
- tile_map->connect("changed", callable_mp(this, &TilesEditor::_tile_map_changed));
+ tile_map->connect("changed", callable_mp(this, &TilesEditorPlugin::_tile_map_changed));
}
}
-void TilesEditor::_bind_methods() {
+bool TilesEditorPlugin::handles(Object *p_object) const {
+ return p_object->is_class("TileMap") || p_object->is_class("TileSet");
}
-TilesEditor::TilesEditor(EditorNode *p_editor) {
+TilesEditorPlugin::TilesEditorPlugin(EditorNode *p_node) {
set_process_internal(true);
// Update the singleton.
singleton = this;
- // Toolbar.
- HBoxContainer *toolbar = memnew(HBoxContainer);
- toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
- add_child(toolbar);
+ editor_node = p_node;
- // Switch button.
- tileset_tilemap_switch_button = memnew(Button);
- tileset_tilemap_switch_button->set_flat(true);
- tileset_tilemap_switch_button->set_toggle_mode(true);
- tileset_tilemap_switch_button->connect("toggled", callable_mp(this, &TilesEditor::_update_editors).unbind(1));
- toolbar->add_child(tileset_tilemap_switch_button);
+ // Tileset editor.
+ tileset_editor = memnew(TileSetEditor);
+ tileset_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tileset_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ tileset_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
+ tileset_editor->hide();
// Tilemap editor.
tilemap_editor = memnew(TileMapEditor);
- tilemap_editor->set_h_size_flags(SIZE_EXPAND_FILL);
- tilemap_editor->set_v_size_flags(SIZE_EXPAND_FILL);
+ tilemap_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ tilemap_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ tilemap_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
tilemap_editor->hide();
- add_child(tilemap_editor);
- tilemap_toolbar = tilemap_editor->get_toolbar();
- toolbar->add_child(tilemap_toolbar);
+ // Pattern preview generation thread.
+ pattern_preview_thread.start(_thread_func, this);
- // Tileset editor.
- tileset_editor = memnew(TileSetEditor);
- tileset_editor->set_h_size_flags(SIZE_EXPAND_FILL);
- tileset_editor->set_v_size_flags(SIZE_EXPAND_FILL);
- tileset_editor->hide();
- add_child(tileset_editor);
+ // Bottom buttons.
+ tileset_editor_button = p_node->add_bottom_panel_item(TTR("TileSet"), tileset_editor);
+ tileset_editor_button->hide();
+ tilemap_editor_button = p_node->add_bottom_panel_item(TTR("TileMap"), tilemap_editor);
+ tilemap_editor_button->hide();
// Initialization.
- _update_switch_button();
_update_editors();
}
-TilesEditor::~TilesEditor() {
-}
-
-///////////////////////////////////////////////////////////////
-
-void TilesEditorPlugin::_notification(int p_what) {
-}
-
-void TilesEditorPlugin::make_visible(bool p_visible) {
- if (p_visible) {
- tiles_editor_button->show();
- editor_node->make_bottom_panel_item_visible(tiles_editor);
- //get_tree()->connect_compat("idle_frame", tileset_editor, "_on_workspace_process");
- } else {
- editor_node->hide_bottom_panel();
- tiles_editor_button->hide();
- //get_tree()->disconnect_compat("idle_frame", tileset_editor, "_on_workspace_process");
- }
-}
-
-void TilesEditorPlugin::edit(Object *p_object) {
- tiles_editor->edit(p_object);
-}
-
-bool TilesEditorPlugin::handles(Object *p_object) const {
- return p_object->is_class("TileMap") || p_object->is_class("TileSet");
-}
-
-TilesEditorPlugin::TilesEditorPlugin(EditorNode *p_node) {
- editor_node = p_node;
-
- tiles_editor = memnew(TilesEditor(p_node));
- tiles_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
- tiles_editor->hide();
-
- tiles_editor_button = p_node->add_bottom_panel_item(TTR("Tiles"), tiles_editor);
- tiles_editor_button->hide();
-}
-
TilesEditorPlugin::~TilesEditorPlugin() {
+ if (pattern_preview_thread.is_started()) {
+ pattern_thread_exit.set();
+ pattern_preview_sem.post();
+ while (!pattern_thread_exited.is_set()) {
+ OS::get_singleton()->delay_usec(10000);
+ RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
+ }
+ pattern_preview_thread.wait_to_finish();
+ }
}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.h b/editor/plugins/tiles/tiles_editor_plugin.h
index f976d68938..33493040f6 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.h
+++ b/editor/plugins/tiles/tiles_editor_plugin.h
@@ -38,24 +38,24 @@
#include "tile_map_editor.h"
#include "tile_set_editor.h"
-class TilesEditor : public VBoxContainer {
- GDCLASS(TilesEditor, VBoxContainer);
+class TilesEditorPlugin : public EditorPlugin {
+ GDCLASS(TilesEditorPlugin, EditorPlugin);
- static TilesEditor *singleton;
+ static TilesEditorPlugin *singleton;
private:
+ EditorNode *editor_node;
+
bool tile_map_changed_needs_update = false;
ObjectID tile_map_id;
Ref<TileSet> tile_set;
- Button *tileset_tilemap_switch_button;
-
- Control *tilemap_toolbar;
+ Button *tilemap_editor_button;
TileMapEditor *tilemap_editor;
+ Button *tileset_editor_button;
TileSetEditor *tileset_editor;
- void _update_switch_button();
void _update_editors();
// For synchronization.
@@ -65,15 +65,35 @@ private:
void _tile_map_changed();
+ // Patterns preview generation.
+ struct QueueItem {
+ Ref<TileSet> tile_set;
+ Ref<TileMapPattern> pattern;
+ Callable callback;
+ };
+ List<QueueItem> pattern_preview_queue;
+ Mutex pattern_preview_mutex;
+ Semaphore pattern_preview_sem;
+ Thread pattern_preview_thread;
+ SafeFlag pattern_thread_exit;
+ SafeFlag pattern_thread_exited;
+ Semaphore pattern_preview_done;
+ void _preview_frame_started();
+ void _pattern_preview_done();
+ static void _thread_func(void *ud);
+ void _thread();
+
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
- _FORCE_INLINE_ static TilesEditor *get_singleton() { return singleton; }
+ _FORCE_INLINE_ static TilesEditorPlugin *get_singleton() { return singleton; }
- bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) { return tilemap_editor->forward_canvas_gui_input(p_event); }
- void forward_canvas_draw_over_viewport(Control *p_overlay) { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); }
+ virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tilemap_editor->forward_canvas_gui_input(p_event); }
+ virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tilemap_editor->forward_canvas_draw_over_viewport(p_overlay); }
+
+ // Pattern preview API.
+ void queue_pattern_preview(Ref<TileSet> p_tile_set, Ref<TileMapPattern> p_pattern, Callable p_callback);
// To synchronize the atlas sources lists.
void set_sources_lists_current(int p_current);
@@ -82,27 +102,6 @@ public:
void set_atlas_view_transform(float p_zoom, Vector2 p_scroll);
void synchronize_atlas_view(Object *p_current);
- void edit(Object *p_object);
-
- TilesEditor(EditorNode *p_editor);
- ~TilesEditor();
-};
-
-class TilesEditorPlugin : public EditorPlugin {
- GDCLASS(TilesEditorPlugin, EditorPlugin);
-
-private:
- EditorNode *editor_node;
- TilesEditor *tiles_editor;
- Button *tiles_editor_button;
-
-protected:
- void _notification(int p_what);
-
-public:
- virtual bool forward_canvas_gui_input(const Ref<InputEvent> &p_event) override { return tiles_editor->forward_canvas_gui_input(p_event); }
- virtual void forward_canvas_draw_over_viewport(Control *p_overlay) override { tiles_editor->forward_canvas_draw_over_viewport(p_overlay); }
-
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/version_control_editor_plugin.cpp b/editor/plugins/version_control_editor_plugin.cpp
index aaa29bcb7a..eea0d2789d 100644
--- a/editor/plugins/version_control_editor_plugin.cpp
+++ b/editor/plugins/version_control_editor_plugin.cpp
@@ -49,6 +49,11 @@ void VersionControlEditorPlugin::_bind_methods() {
BIND_ENUM_CONSTANT(CHANGE_TYPE_TYPECHANGE);
}
+void VersionControlEditorPlugin::_create_vcs_metadata_files() {
+ String dir = "res://";
+ EditorVCSInterface::create_vcs_metadata_files(EditorVCSInterface::VCSMetadata(metadata_selection->get_selected()), dir);
+}
+
void VersionControlEditorPlugin::_selected_a_vcs(int p_id) {
List<StringName> available_addons = get_available_vcs_names();
const StringName selected_vcs = set_up_choice->get_item_text(p_id);
@@ -71,6 +76,10 @@ VersionControlEditorPlugin *VersionControlEditorPlugin::get_singleton() {
return singleton ? singleton : memnew(VersionControlEditorPlugin);
}
+void VersionControlEditorPlugin::popup_vcs_metadata_dialog() {
+ metadata_dialog->popup_centered();
+}
+
void VersionControlEditorPlugin::popup_vcs_set_up_dialog(const Control *p_gui_base) {
fetch_available_vcs_addon_names();
List<StringName> available_addons = get_available_vcs_names();
@@ -257,7 +266,7 @@ void VersionControlEditorPlugin::_display_file_diff(String p_file_path) {
void VersionControlEditorPlugin::_refresh_file_diff() {
String open_file = diff_file_name->get_text();
- if (open_file != "") {
+ if (!open_file.is_empty()) {
_display_file_diff(diff_file_name->get_text());
}
}
@@ -290,7 +299,7 @@ void VersionControlEditorPlugin::_update_commit_status() {
}
void VersionControlEditorPlugin::_update_commit_button() {
- commit_button->set_disabled(commit_message->get_text().strip_edges() == "");
+ commit_button->set_disabled(commit_message->get_text().strip_edges().is_empty());
}
void VersionControlEditorPlugin::_commit_message_gui_input(const Ref<InputEvent> &p_event) {
@@ -374,6 +383,30 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
version_control_actions = memnew(PopupMenu);
+ metadata_dialog = memnew(ConfirmationDialog);
+ metadata_dialog->set_title(TTR("Create Version Control Metadata"));
+ metadata_dialog->set_min_size(Size2(200, 40));
+ version_control_actions->add_child(metadata_dialog);
+
+ VBoxContainer *metadata_vb = memnew(VBoxContainer);
+ HBoxContainer *metadata_hb = memnew(HBoxContainer);
+ metadata_hb->set_custom_minimum_size(Size2(200, 20));
+ Label *l = memnew(Label);
+ l->set_text(TTR("Create VCS metadata files for:"));
+ metadata_hb->add_child(l);
+ metadata_selection = memnew(OptionButton);
+ metadata_selection->set_custom_minimum_size(Size2(100, 20));
+ metadata_selection->add_item("None", (int)EditorVCSInterface::VCSMetadata::NONE);
+ metadata_selection->add_item("Git", (int)EditorVCSInterface::VCSMetadata::GIT);
+ metadata_selection->select((int)EditorVCSInterface::VCSMetadata::GIT);
+ metadata_dialog->get_ok_button()->connect("pressed", callable_mp(this, &VersionControlEditorPlugin::_create_vcs_metadata_files));
+ metadata_hb->add_child(metadata_selection);
+ metadata_vb->add_child(metadata_hb);
+ l = memnew(Label);
+ l->set_text(TTR("Existing VCS metadata files will be overwritten."));
+ metadata_vb->add_child(l);
+ metadata_dialog->add_child(metadata_vb);
+
set_up_dialog = memnew(AcceptDialog);
set_up_dialog->set_title(TTR("Set Up Version Control"));
set_up_dialog->set_min_size(Size2(400, 100));
@@ -383,11 +416,11 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
set_up_ok_button->set_text(TTR("Close"));
set_up_vbc = memnew(VBoxContainer);
- set_up_vbc->set_alignment(VBoxContainer::ALIGN_CENTER);
+ set_up_vbc->set_alignment(BoxContainer::ALIGNMENT_CENTER);
set_up_dialog->add_child(set_up_vbc);
set_up_hbc = memnew(HBoxContainer);
- set_up_hbc->set_h_size_flags(HBoxContainer::SIZE_EXPAND_FILL);
+ set_up_hbc->set_h_size_flags(BoxContainer::SIZE_EXPAND_FILL);
set_up_vbc->add_child(set_up_hbc);
set_up_vcs_status = memnew(RichTextLabel);
@@ -414,7 +447,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
version_commit_dock->set_visible(false);
commit_box_vbc = memnew(VBoxContainer);
- commit_box_vbc->set_alignment(VBoxContainer::ALIGN_BEGIN);
+ commit_box_vbc->set_alignment(VBoxContainer::ALIGNMENT_BEGIN);
commit_box_vbc->set_h_size_flags(VBoxContainer::SIZE_EXPAND_FILL);
commit_box_vbc->set_v_size_flags(VBoxContainer::SIZE_EXPAND_FILL);
version_commit_dock->add_child(commit_box_vbc);
@@ -488,7 +521,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
commit_message->connect("text_changed", callable_mp(this, &VersionControlEditorPlugin::_update_commit_button));
commit_message->connect("gui_input", callable_mp(this, &VersionControlEditorPlugin::_commit_message_gui_input));
commit_box_vbc->add_child(commit_message);
- ED_SHORTCUT("version_control/commit", TTR("Commit"), KEY_MASK_CMD | KEY_ENTER);
+ ED_SHORTCUT("version_control/commit", TTR("Commit"), KeyModifierMask::CMD | Key::ENTER);
commit_button = memnew(Button);
commit_button->set_text(TTR("Commit Changes"));
@@ -497,7 +530,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
commit_box_vbc->add_child(commit_button);
commit_status = memnew(Label);
- commit_status->set_align(Label::ALIGN_CENTER);
+ commit_status->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
commit_box_vbc->add_child(commit_status);
version_control_dock = memnew(PanelContainer);
@@ -522,7 +555,7 @@ VersionControlEditorPlugin::VersionControlEditorPlugin() {
diff_file_name = memnew(Label);
diff_file_name->set_text(TTR("No file diff is active"));
diff_file_name->set_h_size_flags(Label::SIZE_EXPAND_FILL);
- diff_file_name->set_align(Label::ALIGN_RIGHT);
+ diff_file_name->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
diff_hbc->add_child(diff_file_name);
diff_refresh_button = memnew(Button);
diff --git a/editor/plugins/version_control_editor_plugin.h b/editor/plugins/version_control_editor_plugin.h
index d2ba63c86c..2782c1d9dc 100644
--- a/editor/plugins/version_control_editor_plugin.h
+++ b/editor/plugins/version_control_editor_plugin.h
@@ -57,6 +57,8 @@ private:
List<StringName> available_addons;
PopupMenu *version_control_actions;
+ ConfirmationDialog *metadata_dialog;
+ OptionButton *metadata_selection;
AcceptDialog *set_up_dialog;
VBoxContainer *set_up_vbc;
HBoxContainer *set_up_hbc;
@@ -98,6 +100,7 @@ private:
RichTextLabel *diff;
void _populate_available_vcs_names();
+ void _create_vcs_metadata_files();
void _selected_a_vcs(int p_id);
void _initialize_vcs();
void _send_commit_msg();
@@ -121,6 +124,7 @@ protected:
public:
static VersionControlEditorPlugin *get_singleton();
+ void popup_vcs_metadata_dialog();
void popup_vcs_set_up_dialog(const Control *p_gui_base);
void set_version_control_tool_button(Button *p_button) { version_control_dock_button = p_button; }
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 5b1da11f12..365cfd9232 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -31,6 +31,7 @@
#include "visual_shader_editor_plugin.h"
#include "core/config/project_settings.h"
+#include "core/core_string_names.h"
#include "core/input/input.h"
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
@@ -103,7 +104,6 @@ void VisualShaderGraphPlugin::_bind_methods() {
ClassDB::bind_method("connect_nodes", &VisualShaderGraphPlugin::connect_nodes);
ClassDB::bind_method("disconnect_nodes", &VisualShaderGraphPlugin::disconnect_nodes);
ClassDB::bind_method("set_node_position", &VisualShaderGraphPlugin::set_node_position);
- ClassDB::bind_method("set_node_size", &VisualShaderGraphPlugin::set_node_size);
ClassDB::bind_method("update_node", &VisualShaderGraphPlugin::update_node);
ClassDB::bind_method("update_node_deferred", &VisualShaderGraphPlugin::update_node_deferred);
ClassDB::bind_method("set_input_port_default_value", &VisualShaderGraphPlugin::set_input_port_default_value);
@@ -123,16 +123,16 @@ void VisualShaderGraphPlugin::set_connections(List<VisualShader::Connection> &p_
void VisualShaderGraphPlugin::show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id) {
if (visual_shader->get_shader_type() == p_type && links.has(p_node_id) && links[p_node_id].output_ports.has(p_port_id)) {
- for (Map<int, Port>::Element *E = links[p_node_id].output_ports.front(); E; E = E->next()) {
- if (E->value().preview_button != nullptr) {
- E->value().preview_button->set_pressed(false);
+ for (const KeyValue<int, Port> &E : links[p_node_id].output_ports) {
+ if (E.value.preview_button != nullptr) {
+ E.value.preview_button->set_pressed(false);
}
}
if (links[p_node_id].preview_visible && !is_dirty() && links[p_node_id].preview_box != nullptr) {
links[p_node_id].graph_node->remove_child(links[p_node_id].preview_box);
memdelete(links[p_node_id].preview_box);
- links[p_node_id].graph_node->set_size(Vector2(-1, -1));
+ links[p_node_id].graph_node->reset_size();
links[p_node_id].preview_visible = false;
}
@@ -212,19 +212,27 @@ void VisualShaderGraphPlugin::set_uniform_name(VisualShader::Type p_type, int p_
void VisualShaderGraphPlugin::update_curve(int p_node_id) {
if (links.has(p_node_id) && links[p_node_id].curve_editors[0]) {
- if (((VisualShaderNodeCurveTexture *)links[p_node_id].visual_node)->get_texture().is_valid()) {
- links[p_node_id].curve_editors[0]->set_curve(((VisualShaderNodeCurveTexture *)links[p_node_id].visual_node)->get_texture()->get_curve());
+ Ref<VisualShaderNodeCurveTexture> tex = Object::cast_to<VisualShaderNodeCurveTexture>(links[p_node_id].visual_node);
+ ERR_FAIL_COND(!tex.is_valid());
+
+ if (tex->get_texture().is_valid()) {
+ links[p_node_id].curve_editors[0]->set_curve(tex->get_texture()->get_curve());
}
+ tex->emit_signal(CoreStringNames::get_singleton()->changed);
}
}
void VisualShaderGraphPlugin::update_curve_xyz(int p_node_id) {
if (links.has(p_node_id) && links[p_node_id].curve_editors[0] && links[p_node_id].curve_editors[1] && links[p_node_id].curve_editors[2]) {
- if (((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture().is_valid()) {
- links[p_node_id].curve_editors[0]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_x());
- links[p_node_id].curve_editors[1]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_y());
- links[p_node_id].curve_editors[2]->set_curve(((VisualShaderNodeCurveXYZTexture *)links[p_node_id].visual_node)->get_texture()->get_curve_z());
+ Ref<VisualShaderNodeCurveXYZTexture> tex = Object::cast_to<VisualShaderNodeCurveXYZTexture>(links[p_node_id].visual_node);
+ ERR_FAIL_COND(!tex.is_valid());
+
+ if (tex->get_texture().is_valid()) {
+ links[p_node_id].curve_editors[0]->set_curve(tex->get_texture()->get_curve_x());
+ links[p_node_id].curve_editors[1]->set_curve(tex->get_texture()->get_curve_y());
+ links[p_node_id].curve_editors[2]->set_curve(tex->get_texture()->get_curve_z());
}
+ tex->emit_signal(CoreStringNames::get_singleton()->changed);
}
}
@@ -248,7 +256,7 @@ void VisualShaderGraphPlugin::update_node_size(int p_node_id) {
if (!links.has(p_node_id)) {
return;
}
- links[p_node_id].graph_node->set_size(Size2(-1, -1));
+ links[p_node_id].graph_node->reset_size();
}
void VisualShaderGraphPlugin::register_default_input_button(int p_node_id, int p_port_id, Button *p_button) {
@@ -264,11 +272,11 @@ void VisualShaderGraphPlugin::register_curve_editor(int p_node_id, int p_index,
}
void VisualShaderGraphPlugin::update_uniform_refs() {
- for (Map<int, Link>::Element *E = links.front(); E; E = E->next()) {
- VisualShaderNodeUniformRef *ref = Object::cast_to<VisualShaderNodeUniformRef>(E->get().visual_node);
+ for (KeyValue<int, Link> &E : links) {
+ VisualShaderNodeUniformRef *ref = Object::cast_to<VisualShaderNodeUniformRef>(E.value.visual_node);
if (ref) {
- remove_node(E->get().type, E->key());
- add_node(E->get().type, E->key());
+ remove_node(E.value.type, E.key);
+ add_node(E.value.type, E.key);
}
}
}
@@ -283,12 +291,6 @@ void VisualShaderGraphPlugin::set_node_position(VisualShader::Type p_type, int p
}
}
-void VisualShaderGraphPlugin::set_node_size(VisualShader::Type p_type, int p_id, const Vector2 &p_size) {
- if (visual_shader->get_shader_type() == p_type && links.has(p_id)) {
- links[p_id].graph_node->set_size(p_size);
- }
-}
-
bool VisualShaderGraphPlugin::is_preview_visible(int p_id) const {
return links[p_id].preview_visible;
}
@@ -491,6 +493,35 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
bool is_curve = curve.is_valid() || curve_xyz.is_valid();
if (is_curve) {
+ // a default value handling
+ {
+ Variant default_value;
+ bool port_left_used = false;
+
+ for (const VisualShader::Connection &E : connections) {
+ if (E.to_node == p_id && E.to_port == 0) {
+ port_left_used = true;
+ break;
+ }
+ }
+
+ if (!port_left_used) {
+ default_value = vsnode->get_input_port_default_value(0);
+ }
+
+ Button *button = memnew(Button);
+ custom_editor->add_child(button);
+ register_default_input_button(p_id, 0, button);
+ custom_editor->move_child(button, 0);
+
+ button->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_edit_port_default_input), varray(button, p_id, 0));
+ if (default_value.get_type() != Variant::NIL) {
+ set_input_port_default_value(p_type, p_id, 0, default_value);
+ } else {
+ button->hide();
+ }
+ }
+
VisualShaderEditor::get_singleton()->graph->add_child(node);
VisualShaderEditor::get_singleton()->_update_created_node(node);
@@ -560,7 +591,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
if (vsnode->is_use_prop_slots()) {
String error = vsnode->get_warning(visual_shader->get_mode(), p_type);
- if (error != String()) {
+ if (!error.is_empty()) {
Label *error_label = memnew(Label);
error_label->add_theme_color_override("font_color", VisualShaderEditor::get_singleton()->get_theme_color(SNAME("error_color"), SNAME("Editor")));
error_label->set_text(error);
@@ -643,6 +674,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
for (const VisualShader::Connection &E : connections) {
if (E.to_node == p_id && E.to_port == j) {
port_left_used = true;
+ break;
}
}
}
@@ -777,7 +809,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
expand->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_expand_output_port), varray(p_id, i, !vsnode->_is_output_port_expanded(i)), CONNECT_DEFERRED);
hb->add_child(expand);
}
- if (visual_shader->get_shader_type() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) {
+ if (vsnode->has_output_port_preview(i) && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) {
TextureButton *preview = memnew(TextureButton);
preview->set_toggle_mode(true);
preview->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon(SNAME("GuiVisibilityHidden"), SNAME("EditorIcons")));
@@ -845,7 +877,7 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
node->add_child(offset);
String error = vsnode->get_warning(visual_shader->get_mode(), p_type);
- if (error != String()) {
+ if (!error.is_empty()) {
Label *error_label = memnew(Label);
error_label->add_theme_color_override("font_color", VisualShaderEditor::get_singleton()->get_theme_color(SNAME("error_color"), SNAME("Editor")));
error_label->set_text(error);
@@ -1008,7 +1040,6 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) {
hide();
} else {
if (changed) { // to avoid tree collapse
- _clear_buffer();
_update_options_menu();
_update_preview();
_update_graph();
@@ -1030,7 +1061,7 @@ void VisualShaderEditor::remove_plugin(const Ref<VisualShaderNodePlugin> &p_plug
void VisualShaderEditor::clear_custom_types() {
for (int i = 0; i < add_options.size(); i++) {
if (add_options[i].is_custom) {
- add_options.remove(i);
+ add_options.remove_at(i);
i--;
}
}
@@ -1160,7 +1191,7 @@ void VisualShaderEditor::update_custom_nodes() {
category = category.rstrip("/");
category = category.lstrip("/");
category = "Addons/" + category;
- if (subcategory != "") {
+ if (!subcategory.is_empty()) {
category += "/" + subcategory;
}
@@ -1207,7 +1238,7 @@ void VisualShaderEditor::_update_options_menu() {
Color unsupported_color = get_theme_color(SNAME("error_color"), SNAME("Editor"));
Color supported_color = get_theme_color(SNAME("warning_color"), SNAME("Editor"));
- static bool low_driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "GLES2";
+ static bool low_driver = ProjectSettings::get_singleton()->get("rendering/driver/driver_name") == "opengl3";
Map<String, TreeItem *> folders;
@@ -1387,13 +1418,23 @@ void VisualShaderEditor::_set_mode(int p_which) {
edit_type_standard->set_visible(false);
edit_type_particles->set_visible(false);
edit_type_sky->set_visible(true);
+ edit_type_fog->set_visible(false);
edit_type = edit_type_sky;
custom_mode_box->set_visible(false);
mode = MODE_FLAGS_SKY;
+ } else if (p_which == VisualShader::MODE_FOG) {
+ edit_type_standard->set_visible(false);
+ edit_type_particles->set_visible(false);
+ edit_type_sky->set_visible(false);
+ edit_type_fog->set_visible(true);
+ edit_type = edit_type_fog;
+ custom_mode_box->set_visible(false);
+ mode = MODE_FLAGS_FOG;
} else if (p_which == VisualShader::MODE_PARTICLES) {
edit_type_standard->set_visible(false);
edit_type_particles->set_visible(true);
edit_type_sky->set_visible(false);
+ edit_type_fog->set_visible(false);
edit_type = edit_type_particles;
if ((edit_type->get_selected() + 3) > VisualShader::TYPE_PROCESS) {
custom_mode_box->set_visible(false);
@@ -1405,6 +1446,7 @@ void VisualShaderEditor::_set_mode(int p_which) {
edit_type_particles->set_visible(false);
edit_type_standard->set_visible(true);
edit_type_sky->set_visible(false);
+ edit_type_fog->set_visible(false);
edit_type = edit_type_standard;
custom_mode_box->set_visible(false);
mode = MODE_FLAGS_SPATIAL_CANVASITEM;
@@ -1562,6 +1604,8 @@ VisualShader::Type VisualShaderEditor::get_current_shader_type() const {
type = VisualShader::Type(edit_type->get_selected() + 3 + (custom_mode_enabled ? 3 : 0));
} else if (mode & MODE_FLAGS_SKY) {
type = VisualShader::Type(edit_type->get_selected() + 8);
+ } else if (mode & MODE_FLAGS_FOG) {
+ type = VisualShader::Type(edit_type->get_selected() + 9);
} else {
type = VisualShader::Type(edit_type->get_selected());
}
@@ -1643,7 +1687,7 @@ void VisualShaderEditor::_change_input_port_name(const String &p_text, Object *p
ERR_FAIL_COND(!line_edit);
String validated_name = visual_shader->validate_port_name(p_text, node.ptr(), p_port_id, false);
- if (validated_name == String() || prev_name == validated_name) {
+ if (validated_name.is_empty() || prev_name == validated_name) {
line_edit->set_text(node->get_input_port_name(p_port_id));
return;
}
@@ -1671,7 +1715,7 @@ void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *
ERR_FAIL_COND(!line_edit);
String validated_name = visual_shader->validate_port_name(p_text, node.ptr(), p_port_id, true);
- if (validated_name == String() || prev_name == validated_name) {
+ if (validated_name.is_empty() || prev_name == validated_name) {
line_edit->set_text(node->get_output_port_name(p_port_id));
return;
}
@@ -1932,7 +1976,7 @@ void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p
}
gn->set_custom_minimum_size(size);
- gn->set_size(Size2(1, 1));
+ gn->reset_size();
if (!expression_node.is_null() && text_box) {
Size2 box_size = size;
@@ -1945,8 +1989,8 @@ void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p
box_size.x -= 28 * EDSCALE;
box_size.y -= text_box->get_offset(SIDE_TOP);
box_size.y -= 28 * EDSCALE;
- text_box->set_custom_minimum_size(Size2(box_size.x, box_size.y));
- text_box->set_size(Size2(1, 1));
+ text_box->set_custom_minimum_size(box_size);
+ text_box->reset_size();
}
}
}
@@ -1994,8 +2038,8 @@ void VisualShaderEditor::_comment_title_popup_show(const Point2 &p_position, int
}
void VisualShaderEditor::_comment_title_text_changed(const String &p_new_text) {
- comment_title_change_edit->set_size(Size2(-1, -1));
- comment_title_change_popup->set_size(Size2(-1, -1));
+ comment_title_change_edit->reset_size();
+ comment_title_change_popup->reset_size();
}
void VisualShaderEditor::_comment_title_text_submitted(const String &p_new_text) {
@@ -2034,13 +2078,14 @@ void VisualShaderEditor::_comment_desc_popup_show(const Point2 &p_position, int
}
comment_desc_change_edit->set_text(node->get_description());
comment_desc_change_popup->set_meta("id", p_node_id);
+ comment_desc_change_popup->reset_size();
comment_desc_change_popup->popup();
comment_desc_change_popup->set_position(p_position);
}
void VisualShaderEditor::_comment_desc_text_changed() {
- comment_desc_change_edit->set_size(Size2(-1, -1));
- comment_desc_change_popup->set_size(Size2(-1, -1));
+ comment_desc_change_edit->reset_size();
+ comment_desc_change_popup->reset_size();
}
void VisualShaderEditor::_comment_desc_confirm() {
@@ -2365,7 +2410,7 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx, String p_resource_pa
bool is_custom = add_options[p_idx].is_custom;
- if (!is_custom && add_options[p_idx].type != String()) {
+ if (!is_custom && !add_options[p_idx].type.is_empty()) {
VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instantiate(add_options[p_idx].type));
ERR_FAIL_COND(!vsn);
@@ -2547,6 +2592,7 @@ void VisualShaderEditor::_add_node(int p_idx, int p_op_idx, String p_resource_pa
}
}
}
+ _member_cancel();
VisualShaderNodeUniform *uniform = Object::cast_to<VisualShaderNodeUniform>(vsnode.ptr());
if (uniform) {
@@ -2712,9 +2758,6 @@ void VisualShaderEditor::_delete_nodes(int p_type, const List<int> &p_nodes) {
undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, F), F);
undo_redo->add_undo_method(graph_plugin.ptr(), "add_node", type, F);
- undo_redo->add_do_method(this, "_clear_buffer");
- undo_redo->add_undo_method(this, "_clear_buffer");
-
// restore size, inputs and outputs if node is group
VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
if (group) {
@@ -3009,7 +3052,7 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
Ref<InputEventMouseButton> mb = p_event;
VisualShader::Type type = get_current_shader_type();
- if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MOUSE_BUTTON_RIGHT) {
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
selected_constants.clear();
selected_uniforms.clear();
selected_comment = -1;
@@ -3050,13 +3093,15 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
selected_float_constant = -1;
}
- if (to_change.is_empty() && copy_nodes_buffer.is_empty()) {
+ if (to_change.is_empty() && copy_items_buffer.is_empty()) {
_show_members_dialog(true);
} else {
+ popup_menu->set_item_disabled(NodeMenuOptions::CUT, to_change.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::COPY, to_change.is_empty());
- popup_menu->set_item_disabled(NodeMenuOptions::PASTE, copy_nodes_buffer.is_empty());
+ popup_menu->set_item_disabled(NodeMenuOptions::PASTE, copy_items_buffer.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::DELETE, to_change.is_empty());
popup_menu->set_item_disabled(NodeMenuOptions::DUPLICATE, to_change.is_empty());
+ popup_menu->set_item_disabled(NodeMenuOptions::CLEAR_COPY_BUFFER, copy_items_buffer.is_empty());
int temp = popup_menu->get_item_index(NodeMenuOptions::SEPARATOR2);
if (temp != -1) {
@@ -3121,9 +3166,9 @@ void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> &p_event) {
}
menu_point = graph->get_local_mouse_position();
- Point2 gpos = Input::get_singleton()->get_mouse_position();
+ Point2 gpos = get_screen_position() + get_local_mouse_position();
popup_menu->set_position(gpos);
- popup_menu->set_size(Size2(-1, -1));
+ popup_menu->reset_size();
popup_menu->popup();
}
}
@@ -3140,37 +3185,27 @@ void VisualShaderEditor::_show_members_dialog(bool at_mouse_pos, VisualShaderNod
saved_node_pos_dirty = true;
saved_node_pos = graph->get_local_mouse_position();
- Point2 gpos = Input::get_singleton()->get_mouse_position();
- members_dialog->popup();
+ Point2 gpos = get_screen_position() + get_local_mouse_position();
members_dialog->set_position(gpos);
} else {
- members_dialog->popup();
saved_node_pos_dirty = false;
- members_dialog->set_position(graph->get_global_position() + Point2(5 * EDSCALE, 65 * EDSCALE));
+ members_dialog->set_position(graph->get_screen_position() + Point2(5 * EDSCALE, 65 * EDSCALE));
}
+ members_dialog->popup();
- // keep dialog within window bounds
- Size2 window_size = DisplayServer::get_singleton()->window_get_size();
+ // Keep dialog within window bounds.
+ Rect2 window_rect = Rect2(DisplayServer::get_singleton()->window_get_position(), DisplayServer::get_singleton()->window_get_size());
Rect2 dialog_rect = Rect2(members_dialog->get_position(), members_dialog->get_size());
- if (dialog_rect.position.y + dialog_rect.size.y > window_size.y) {
- int difference = dialog_rect.position.y + dialog_rect.size.y - window_size.y;
- members_dialog->set_position(members_dialog->get_position() - Point2(0, difference));
- }
- if (dialog_rect.position.x + dialog_rect.size.x > window_size.x) {
- int difference = dialog_rect.position.x + dialog_rect.size.x - window_size.x;
- members_dialog->set_position(members_dialog->get_position() - Point2(difference, 0));
- }
+ Vector2 difference = (dialog_rect.get_end() - window_rect.get_end()).max(Vector2());
+ members_dialog->set_position(members_dialog->get_position() - difference);
- node_filter->call_deferred(SNAME("grab_focus")); // still not visible
+ node_filter->call_deferred(SNAME("grab_focus")); // Still not visible.
node_filter->select_all();
}
void VisualShaderEditor::_sbox_input(const Ref<InputEvent> &p_ie) {
Ref<InputEventKey> ie = p_ie;
- if (ie.is_valid() && (ie->get_keycode() == KEY_UP ||
- ie->get_keycode() == KEY_DOWN ||
- ie->get_keycode() == KEY_ENTER ||
- ie->get_keycode() == KEY_KP_ENTER)) {
+ if (ie.is_valid() && (ie->get_keycode() == Key::UP || ie->get_keycode() == Key::DOWN || ie->get_keycode() == Key::ENTER || ie->get_keycode() == Key::KP_ENTER)) {
members->gui_input(ie);
node_filter->accept_event();
}
@@ -3279,69 +3314,88 @@ void VisualShaderEditor::_node_changed(int p_id) {
}
}
-void VisualShaderEditor::_dup_update_excluded(int p_type, Set<int> &r_excluded) {
- r_excluded.clear();
- VisualShader::Type type = (VisualShader::Type)p_type;
-
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- int id = String(gn->get_name()).to_int();
- Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
- Ref<VisualShaderNodeOutput> output = node;
- if (output.is_valid()) {
- r_excluded.insert(id);
- continue;
- }
- r_excluded.insert(id);
- }
- }
-}
-
-void VisualShaderEditor::_dup_copy_nodes(int p_type, List<int> &r_nodes, Set<int> &r_excluded) {
+void VisualShaderEditor::_dup_copy_nodes(int p_type, List<CopyItem> &r_items, List<VisualShader::Connection> &r_connections) {
VisualShader::Type type = (VisualShader::Type)p_type;
selection_center.x = 0.0f;
selection_center.y = 0.0f;
+ Set<int> nodes;
+
for (int i = 0; i < graph->get_child_count(); i++) {
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
if (gn) {
int id = String(gn->get_name()).to_int();
+
Ref<VisualShaderNode> node = visual_shader->get_node(type, id);
Ref<VisualShaderNodeOutput> output = node;
if (output.is_valid()) { // can't duplicate output
- r_excluded.insert(id);
continue;
}
+
if (node.is_valid() && gn->is_selected()) {
Vector2 pos = visual_shader->get_node_position(type, id);
selection_center += pos;
- r_nodes.push_back(id);
+
+ CopyItem item;
+ item.id = id;
+ item.node = visual_shader->get_node(type, id)->duplicate();
+ item.position = visual_shader->get_node_position(type, id);
+
+ Ref<VisualShaderNodeResizableBase> resizable_base = node;
+ if (resizable_base.is_valid()) {
+ item.size = resizable_base->get_size();
+ }
+
+ Ref<VisualShaderNodeGroupBase> group = node;
+ if (group.is_valid()) {
+ item.group_inputs = group->get_inputs();
+ item.group_outputs = group->get_outputs();
+ }
+
+ Ref<VisualShaderNodeExpression> expression = node;
+ if (expression.is_valid()) {
+ item.expression = expression->get_expression();
+ }
+
+ r_items.push_back(item);
+
+ nodes.insert(id);
}
- r_excluded.insert(id);
}
}
- selection_center /= (float)r_nodes.size();
+ List<VisualShader::Connection> connections;
+ visual_shader->get_node_connections(type, &connections);
+
+ for (const VisualShader::Connection &E : connections) {
+ if (nodes.has(E.from_node) && nodes.has(E.to_node)) {
+ r_connections.push_back(E);
+ }
+ }
+
+ selection_center /= (float)r_items.size();
}
-void VisualShaderEditor::_dup_paste_nodes(int p_type, int p_pasted_type, List<int> &r_nodes, Set<int> &r_excluded, const Vector2 &p_offset, bool p_select) {
+void VisualShaderEditor::_dup_paste_nodes(int p_type, List<CopyItem> &r_items, const List<VisualShader::Connection> &p_connections, const Vector2 &p_offset, bool p_duplicate) {
+ if (p_duplicate) {
+ undo_redo->create_action(TTR("Duplicate VisualShader Node(s)"));
+ } else {
+ undo_redo->create_action(TTR("Paste VisualShader Node(s)"));
+ }
+
VisualShader::Type type = (VisualShader::Type)p_type;
- VisualShader::Type pasted_type = (VisualShader::Type)p_pasted_type;
int base_id = visual_shader->get_valid_node_id(type);
int id_from = base_id;
Map<int, int> connection_remap;
Set<int> unsupported_set;
+ Set<int> added_set;
- for (int &E : r_nodes) {
- connection_remap[E] = id_from;
- Ref<VisualShaderNode> node = visual_shader->get_node(pasted_type, E);
-
+ for (CopyItem &item : r_items) {
bool unsupported = false;
for (int i = 0; i < add_options.size(); i++) {
- if (add_options[i].type == node->get_class_name()) {
+ if (add_options[i].type == item.node->get_class_name()) {
if (!_is_available(add_options[i].mode)) {
unsupported = true;
}
@@ -3349,48 +3403,47 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, int p_pasted_type, List<in
}
}
if (unsupported) {
- unsupported_set.insert(E);
+ unsupported_set.insert(item.id);
continue;
}
+ connection_remap[item.id] = id_from;
+ Ref<VisualShaderNode> node = item.node->duplicate();
- Ref<VisualShaderNode> dupli = node->duplicate();
-
- undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(pasted_type, E) + p_offset, id_from);
- undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_from);
+ Ref<VisualShaderNodeResizableBase> resizable_base = Object::cast_to<VisualShaderNodeResizableBase>(node.ptr());
+ if (resizable_base.is_valid()) {
+ undo_redo->add_do_method(node.ptr(), "set_size", item.size);
+ }
- // duplicate size, inputs and outputs if node is group
Ref<VisualShaderNodeGroupBase> group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr());
- if (!group.is_null()) {
- undo_redo->add_do_method(dupli.ptr(), "set_size", group->get_size());
- undo_redo->add_do_method(graph_plugin.ptr(), "set_node_size", type, id_from, group->get_size());
- undo_redo->add_do_method(dupli.ptr(), "set_inputs", group->get_inputs());
- undo_redo->add_do_method(dupli.ptr(), "set_outputs", group->get_outputs());
+ if (group.is_valid()) {
+ undo_redo->add_do_method(node.ptr(), "set_inputs", item.group_inputs);
+ undo_redo->add_do_method(node.ptr(), "set_outputs", item.group_outputs);
}
- // duplicate expression text if node is expression
+
Ref<VisualShaderNodeExpression> expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr());
- if (!expression.is_null()) {
- undo_redo->add_do_method(dupli.ptr(), "set_expression", expression->get_expression());
+ if (expression.is_valid()) {
+ undo_redo->add_do_method(node.ptr(), "set_expression", item.expression);
}
+ undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, node, item.position + p_offset, id_from);
+ undo_redo->add_do_method(graph_plugin.ptr(), "add_node", type, id_from);
+
+ added_set.insert(id_from);
id_from++;
}
- List<VisualShader::Connection> conns;
- visual_shader->get_node_connections(pasted_type, &conns);
-
- for (const VisualShader::Connection &E : conns) {
+ for (const VisualShader::Connection &E : p_connections) {
if (unsupported_set.has(E.from_node) || unsupported_set.has(E.to_node)) {
continue;
}
- if (connection_remap.has(E.from_node) && connection_remap.has(E.to_node)) {
- undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
- undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
- undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
- }
+
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
+ undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, connection_remap[E.from_node], E.from_port, connection_remap[E.to_node], E.to_port);
}
id_from = base_id;
- for (int i = 0; i < r_nodes.size(); i++) {
+ for (int i = 0; i < r_items.size(); i++) {
undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from);
undo_redo->add_undo_method(graph_plugin.ptr(), "remove_node", type, id_from);
id_from++;
@@ -3398,54 +3451,61 @@ void VisualShaderEditor::_dup_paste_nodes(int p_type, int p_pasted_type, List<in
undo_redo->commit_action();
- if (p_select) {
- // reselect duplicated nodes by excluding the other ones
- for (int i = 0; i < graph->get_child_count(); i++) {
- GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
- if (gn) {
- int id = String(gn->get_name()).to_int();
- if (!r_excluded.has(id)) {
- gn->set_selected(true);
- } else {
- gn->set_selected(false);
- }
+ // reselect nodes by excluding the other ones
+ for (int i = 0; i < graph->get_child_count(); i++) {
+ GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i));
+ if (gn) {
+ int id = String(gn->get_name()).to_int();
+ if (added_set.has(id)) {
+ gn->set_selected(true);
+ } else {
+ gn->set_selected(false);
}
}
}
}
-void VisualShaderEditor::_clear_buffer() {
- copy_nodes_buffer.clear();
- copy_nodes_excluded_buffer.clear();
+void VisualShaderEditor::_clear_copy_buffer() {
+ copy_items_buffer.clear();
+ copy_connections_buffer.clear();
}
void VisualShaderEditor::_duplicate_nodes() {
int type = get_current_shader_type();
- List<int> nodes;
- Set<int> excluded;
+ List<CopyItem> items;
+ List<VisualShader::Connection> connections;
- _dup_copy_nodes(type, nodes, excluded);
+ _dup_copy_nodes(type, items, connections);
- if (nodes.is_empty()) {
+ if (items.is_empty()) {
return;
}
- undo_redo->create_action(TTR("Duplicate VisualShader Node(s)"));
-
- _dup_paste_nodes(type, type, nodes, excluded, Vector2(10, 10) * EDSCALE, true);
+ _dup_paste_nodes(type, items, connections, Vector2(10, 10) * EDSCALE, true);
}
-void VisualShaderEditor::_copy_nodes() {
- copy_type = get_current_shader_type();
+void VisualShaderEditor::_copy_nodes(bool p_cut) {
+ _clear_copy_buffer();
+
+ _dup_copy_nodes(get_current_shader_type(), copy_items_buffer, copy_connections_buffer);
+
+ if (p_cut) {
+ undo_redo->create_action(TTR("Cut VisualShader Node(s)"));
+
+ List<int> ids;
+ for (const CopyItem &E : copy_items_buffer) {
+ ids.push_back(E.id);
+ }
- _clear_buffer();
+ _delete_nodes(get_current_shader_type(), ids);
- _dup_copy_nodes(copy_type, copy_nodes_buffer, copy_nodes_excluded_buffer);
+ undo_redo->commit_action();
+ }
}
void VisualShaderEditor::_paste_nodes(bool p_use_custom_position, const Vector2 &p_custom_position) {
- if (copy_nodes_buffer.is_empty()) {
+ if (copy_items_buffer.is_empty()) {
return;
}
@@ -3460,11 +3520,7 @@ void VisualShaderEditor::_paste_nodes(bool p_use_custom_position, const Vector2
mpos = graph->get_local_mouse_position();
}
- undo_redo->create_action(TTR("Paste VisualShader Node(s)"));
-
- _dup_paste_nodes(type, copy_type, copy_nodes_buffer, copy_nodes_excluded_buffer, (graph->get_scroll_ofs() / scale + mpos / scale - selection_center), false);
-
- _dup_update_excluded(type, copy_nodes_excluded_buffer); // to prevent selection of previous copies at new paste
+ _dup_paste_nodes(type, copy_items_buffer, copy_connections_buffer, graph->get_scroll_ofs() / scale + mpos / scale - selection_center, false);
}
void VisualShaderEditor::_mode_selected(int p_id) {
@@ -3483,11 +3539,15 @@ void VisualShaderEditor::_mode_selected(int p_id) {
}
} else if (mode & MODE_FLAGS_SKY) {
offset = 8;
+ } else if (mode & MODE_FLAGS_FOG) {
+ offset = 9;
}
visual_shader->set_shader_type(VisualShader::Type(p_id + offset));
_update_options_menu();
_update_graph();
+
+ graph->grab_focus();
}
void VisualShaderEditor::_custom_mode_toggled(bool p_enabled) {
@@ -3690,8 +3750,11 @@ void VisualShaderEditor::_node_menu_id_pressed(int p_idx) {
case NodeMenuOptions::ADD:
_show_members_dialog(true);
break;
+ case NodeMenuOptions::CUT:
+ _copy_nodes(true);
+ break;
case NodeMenuOptions::COPY:
- _copy_nodes();
+ _copy_nodes(false);
break;
case NodeMenuOptions::PASTE:
_paste_nodes(true, menu_point);
@@ -3702,6 +3765,9 @@ void VisualShaderEditor::_node_menu_id_pressed(int p_idx) {
case NodeMenuOptions::DUPLICATE:
_duplicate_nodes();
break;
+ case NodeMenuOptions::CLEAR_COPY_BUFFER:
+ _clear_copy_buffer();
+ break;
case NodeMenuOptions::CONVERT_CONSTANTS_TO_UNIFORMS:
_convert_constants_to_uniforms(false);
break;
@@ -3709,10 +3775,10 @@ void VisualShaderEditor::_node_menu_id_pressed(int p_idx) {
_convert_constants_to_uniforms(true);
break;
case NodeMenuOptions::SET_COMMENT_TITLE:
- _comment_title_popup_show(get_global_mouse_position(), selected_comment);
+ _comment_title_popup_show(get_screen_position() + get_local_mouse_position(), selected_comment);
break;
case NodeMenuOptions::SET_COMMENT_DESCRIPTION:
- _comment_desc_popup_show(get_global_mouse_position(), selected_comment);
+ _comment_desc_popup_show(get_screen_position() + get_local_mouse_position(), selected_comment);
break;
default:
break;
@@ -3877,9 +3943,15 @@ void VisualShaderEditor::_update_preview() {
preview_text->set_text(code);
+ ShaderLanguage::ShaderCompileInfo info;
+ info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode()));
+ info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode()));
+ info.shader_types = ShaderTypes::get_singleton()->get_types();
+ info.global_variable_type_func = _get_global_variable_type;
+
ShaderLanguage sl;
- Error err = sl.compile(code, ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(visual_shader->get_mode())), ShaderLanguage::VaryingFunctionNames(), ShaderTypes::get_singleton()->get_types(), _get_global_variable_type);
+ Error err = sl.compile(code, info);
for (int i = 0; i < preview_text->get_line_count(); i++) {
preview_text->set_line_background_color(i, Color(0, 0, 0, 0));
@@ -3916,7 +3988,7 @@ void VisualShaderEditor::_bind_methods() {
ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item);
ClassDB::bind_method("_uniform_select_item", &VisualShaderEditor::_uniform_select_item);
ClassDB::bind_method("_set_node_size", &VisualShaderEditor::_set_node_size);
- ClassDB::bind_method("_clear_buffer", &VisualShaderEditor::_clear_buffer);
+ ClassDB::bind_method("_clear_copy_buffer", &VisualShaderEditor::_clear_copy_buffer);
ClassDB::bind_method("_update_uniforms", &VisualShaderEditor::_update_uniforms);
ClassDB::bind_method("_set_mode", &VisualShaderEditor::_set_mode);
ClassDB::bind_method("_nodes_dragged", &VisualShaderEditor::_nodes_dragged);
@@ -3970,7 +4042,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph->connect("node_selected", callable_mp(this, &VisualShaderEditor::_node_selected));
graph->connect("scroll_offset_changed", callable_mp(this, &VisualShaderEditor::_scroll_changed));
graph->connect("duplicate_nodes_request", callable_mp(this, &VisualShaderEditor::_duplicate_nodes));
- graph->connect("copy_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes));
+ graph->connect("copy_nodes_request", callable_mp(this, &VisualShaderEditor::_copy_nodes), varray(false));
graph->connect("paste_nodes_request", callable_mp(this, &VisualShaderEditor::_paste_nodes), varray(false, Point2()));
graph->connect("delete_nodes_request", callable_mp(this, &VisualShaderEditor::_delete_nodes_request));
graph->connect("gui_input", callable_mp(this, &VisualShaderEditor::_graph_gui_input));
@@ -4025,6 +4097,11 @@ VisualShaderEditor::VisualShaderEditor() {
edit_type_sky->select(0);
edit_type_sky->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected));
+ edit_type_fog = memnew(OptionButton);
+ edit_type_fog->add_item(TTR("Fog"));
+ edit_type_fog->select(0);
+ edit_type_fog->connect("item_selected", callable_mp(this, &VisualShaderEditor::_mode_selected));
+
edit_type = edit_type_standard;
graph->get_zoom_hbox()->add_child(custom_mode_box);
@@ -4035,13 +4112,15 @@ VisualShaderEditor::VisualShaderEditor() {
graph->get_zoom_hbox()->move_child(edit_type_particles, 0);
graph->get_zoom_hbox()->add_child(edit_type_sky);
graph->get_zoom_hbox()->move_child(edit_type_sky, 0);
+ graph->get_zoom_hbox()->add_child(edit_type_fog);
+ graph->get_zoom_hbox()->move_child(edit_type_fog, 0);
add_node = memnew(Button);
add_node->set_flat(true);
graph->get_zoom_hbox()->add_child(add_node);
add_node->set_text(TTR("Add Node..."));
graph->get_zoom_hbox()->move_child(add_node, 0);
- add_node->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_members_dialog), varray(false));
+ add_node->connect("pressed", callable_mp(this, &VisualShaderEditor::_show_members_dialog), varray(false, VisualShaderNode::PORT_TYPE_MAX, VisualShaderNode::PORT_TYPE_MAX));
preview_shader = memnew(Button);
preview_shader->set_flat(true);
@@ -4089,10 +4168,12 @@ VisualShaderEditor::VisualShaderEditor() {
add_child(popup_menu);
popup_menu->add_item(TTR("Add Node"), NodeMenuOptions::ADD);
popup_menu->add_separator();
+ popup_menu->add_item(TTR("Cut"), NodeMenuOptions::CUT);
popup_menu->add_item(TTR("Copy"), NodeMenuOptions::COPY);
popup_menu->add_item(TTR("Paste"), NodeMenuOptions::PASTE);
popup_menu->add_item(TTR("Delete"), NodeMenuOptions::DELETE);
popup_menu->add_item(TTR("Duplicate"), NodeMenuOptions::DUPLICATE);
+ popup_menu->add_item(TTR("Clear Copy Buffer"), NodeMenuOptions::CLEAR_COPY_BUFFER);
popup_menu->connect("id_pressed", callable_mp(this, &VisualShaderEditor::_node_menu_id_pressed));
///////////////////////////////////////
@@ -4166,8 +4247,8 @@ VisualShaderEditor::VisualShaderEditor() {
alert = memnew(AcceptDialog);
alert->get_label()->set_autowrap_mode(Label::AUTOWRAP_WORD);
- alert->get_label()->set_align(Label::ALIGN_CENTER);
- alert->get_label()->set_valign(Label::VALIGN_CENTER);
+ alert->get_label()->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ alert->get_label()->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
alert->get_label()->set_custom_minimum_size(Size2(400, 60) * EDSCALE);
add_child(alert);
@@ -4177,8 +4258,8 @@ VisualShaderEditor::VisualShaderEditor() {
comment_title_change_edit->connect("text_changed", callable_mp(this, &VisualShaderEditor::_comment_title_text_changed));
comment_title_change_edit->connect("text_submitted", callable_mp(this, &VisualShaderEditor::_comment_title_text_submitted));
comment_title_change_popup->add_child(comment_title_change_edit);
- comment_title_change_edit->set_size(Size2(-1, -1));
- comment_title_change_popup->set_size(Size2(-1, -1));
+ comment_title_change_edit->reset_size();
+ comment_title_change_popup->reset_size();
comment_title_change_popup->connect("focus_exited", callable_mp(this, &VisualShaderEditor::_comment_title_popup_focus_out));
comment_title_change_popup->connect("popup_hide", callable_mp(this, &VisualShaderEditor::_comment_title_popup_hide));
add_child(comment_title_change_popup);
@@ -4190,8 +4271,8 @@ VisualShaderEditor::VisualShaderEditor() {
comment_desc_change_edit->connect("text_changed", callable_mp(this, &VisualShaderEditor::_comment_desc_text_changed));
comment_desc_vbox->add_child(comment_desc_change_edit);
comment_desc_change_edit->set_custom_minimum_size(Size2(300 * EDSCALE, 150 * EDSCALE));
- comment_desc_change_edit->set_size(Size2(-1, -1));
- comment_desc_change_popup->set_size(Size2(-1, -1));
+ comment_desc_change_edit->reset_size();
+ comment_desc_change_popup->reset_size();
comment_desc_change_popup->connect("focus_exited", callable_mp(this, &VisualShaderEditor::_comment_desc_confirm));
comment_desc_change_popup->connect("popup_hide", callable_mp(this, &VisualShaderEditor::_comment_desc_popup_hide));
Button *comment_desc_confirm_button = memnew(Button);
@@ -4303,6 +4384,7 @@ VisualShaderEditor::VisualShaderEditor() {
const String input_param_for_fragment_and_light_shader_modes = TTR("'%s' input parameter for fragment and light shader modes.");
const String input_param_for_fragment_shader_mode = TTR("'%s' input parameter for fragment shader mode.");
const String input_param_for_sky_shader_mode = TTR("'%s' input parameter for sky shader mode.");
+ const String input_param_for_fog_shader_mode = TTR("'%s' input parameter for fog shader mode.");
const String input_param_for_light_shader_mode = TTR("'%s' input parameter for light shader mode.");
const String input_param_for_vertex_shader_mode = TTR("'%s' input parameter for vertex shader mode.");
const String input_param_for_start_shader_mode = TTR("'%s' input parameter for start shader mode.");
@@ -4422,6 +4504,16 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("SkyCoords", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "sky_coords"), "sky_coords", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
add_options.push_back(AddOption("Time", "Input", "Sky", "VisualShaderNodeInput", vformat(input_param_for_sky_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_SKY, Shader::MODE_SKY));
+ // FOG INPUTS
+
+ add_options.push_back(AddOption("WorldPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "world_position"), "world_position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("ObjectPosition", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "object_position"), "object_position", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("UVW", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "uvw"), "uvw", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("Extents", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "extents"), "extents", VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("Transform", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "transform"), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("SDF", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "sdf"), "sdf", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+ add_options.push_back(AddOption("Time", "Input", "Fog", "VisualShaderNodeInput", vformat(input_param_for_fog_shader_mode, "time"), "time", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_FOG, Shader::MODE_FOG));
+
// PARTICLES INPUTS
add_options.push_back(AddOption("CollisionDepth", "Input", "Collide", "VisualShaderNodeInput", vformat(input_param_for_collide_shader_mode, "collision_depth"), "collision_depth", VisualShaderNode::PORT_TYPE_SCALAR, TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
@@ -4435,6 +4527,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("MultiplyByAxisAngle", "Particles", "Transform", "VisualShaderNodeParticleMultiplyByAxisAngle", "A node for help to multiply a position input vector by rotation using specific axis. Intended to work with emitters.", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT | TYPE_FLAGS_PROCESS | TYPE_FLAGS_COLLIDE, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("BoxEmitter", "Particles", "Emitters", "VisualShaderNodeParticleBoxEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
+ add_options.push_back(AddOption("MeshEmitter", "Particles", "Emitters", "VisualShaderNodeParticleMeshEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("RingEmitter", "Particles", "Emitters", "VisualShaderNodeParticleRingEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
add_options.push_back(AddOption("SphereEmitter", "Particles", "Emitters", "VisualShaderNodeParticleSphereEmitter", "", -1, VisualShaderNode::PORT_TYPE_VECTOR, TYPE_FLAGS_EMIT, Shader::MODE_PARTICLES));
@@ -4463,6 +4556,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("ATan", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeFloatFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("ATan2", "Scalar", "Functions", "VisualShaderNodeFloatOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeFloatOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("ATanH", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeFloatFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("BitwiseNOT", "Scalar", "Functions", "VisualShaderNodeIntFunc", TTR("Returns the result of bitwise NOT (~a) operation on the integer."), VisualShaderNodeIntFunc::FUNC_BITWISE_NOT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
add_options.push_back(AddOption("Ceil", "Scalar", "Functions", "VisualShaderNodeFloatFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeFloatFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), VisualShaderNodeClamp::OP_TYPE_FLOAT, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeClamp", TTR("Constrains a value to lie between two further values."), VisualShaderNodeClamp::OP_TYPE_INT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
@@ -4502,6 +4596,11 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Sums two floating-point scalars."), VisualShaderNodeFloatOp::OP_ADD, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Sums two integer scalars."), VisualShaderNodeIntOp::OP_ADD, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseAND", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise AND (a & b) operation for two integers."), VisualShaderNodeIntOp::OP_BITWISE_AND, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseLeftShift", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise left shift (a << b) operation on the integer."), VisualShaderNodeIntOp::OP_BITWISE_LEFT_SHIFT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseOR", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise OR (a | b) operation for two integers."), VisualShaderNodeIntOp::OP_BITWISE_OR, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseRightShift", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise right shift (a >> b) operation on the integer."), VisualShaderNodeIntOp::OP_BITWISE_RIGHT_SHIFT, VisualShaderNode::PORT_TYPE_SCALAR_INT));
+ add_options.push_back(AddOption("BitwiseXOR", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Returns the result of bitwise XOR (a ^ b) operation on the integer."), VisualShaderNodeIntOp::OP_BITWISE_XOR, VisualShaderNode::PORT_TYPE_SCALAR_INT));
add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Divides two floating-point scalars."), VisualShaderNodeFloatOp::OP_DIV, VisualShaderNode::PORT_TYPE_SCALAR));
add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeIntOp", TTR("Divides two integer scalars."), VisualShaderNodeIntOp::OP_DIV, VisualShaderNode::PORT_TYPE_SCALAR_INT));
add_options.push_back(AddOption("Multiply", "Scalar", "Operators", "VisualShaderNodeFloatOp", TTR("Multiplies two floating-point scalars."), VisualShaderNodeFloatOp::OP_MUL, VisualShaderNode::PORT_TYPE_SCALAR));
@@ -4885,7 +4984,7 @@ public:
}
}
- void setup(Ref<Resource> p_parent_resource, Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, Ref<VisualShaderNode> p_node) {
+ void setup(Ref<Resource> p_parent_resource, Vector<EditorProperty *> p_properties, const Vector<StringName> &p_names, const Map<StringName, String> &p_overrided_names, Ref<VisualShaderNode> p_node) {
parent_resource = p_parent_resource;
updating = false;
node = p_node;
@@ -4901,7 +5000,11 @@ public:
Label *prop_name = memnew(Label);
String prop_name_str = p_names[i];
- prop_name_str = prop_name_str.capitalize() + ":";
+ if (p_overrided_names.has(p_names[i])) {
+ prop_name_str = p_overrided_names[p_names[i]] + ":";
+ } else {
+ prop_name_str = prop_name_str.capitalize() + ":";
+ }
prop_name->set_text(prop_name_str);
prop_name->set_visible(false);
hbox->add_child(prop_name);
@@ -4993,7 +5096,7 @@ Control *VisualShaderNodePluginDefault::create_editor(const Ref<Resource> &p_par
properties.push_back(pinfo[i].name);
}
VisualShaderNodePluginDefaultEditor *editor = memnew(VisualShaderNodePluginDefaultEditor);
- editor->setup(p_parent_resource, editors, properties, p_node);
+ editor->setup(p_parent_resource, editors, properties, p_node->get_editable_properties_names(), p_node);
return editor;
}
@@ -5090,11 +5193,7 @@ EditorPropertyShaderMode::EditorPropertyShaderMode() {
}
bool EditorInspectorShaderModePlugin::can_handle(Object *p_object) {
- return true; //can handle everything
-}
-
-void EditorInspectorShaderModePlugin::parse_begin(Object *p_object) {
- //do none
+ 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) {
@@ -5107,11 +5206,7 @@ bool EditorInspectorShaderModePlugin::parse_property(Object *p_object, const Var
return true;
}
- return false; //can be overridden, although it will most likely be last anyway
-}
-
-void EditorInspectorShaderModePlugin::parse_end() {
- //do none
+ return false;
}
//////////////////////////////////
@@ -5128,7 +5223,9 @@ void VisualShaderNodePortPreview::_shader_changed() {
preview_shader.instantiate();
preview_shader->set_code(shader_code);
for (int i = 0; i < default_textures.size(); i++) {
- preview_shader->set_default_texture_param(default_textures[i].name, default_textures[i].param);
+ for (int j = 0; j < default_textures[i].params.size(); j++) {
+ preview_shader->set_default_texture_param(default_textures[i].name, default_textures[i].params[j], j);
+ }
}
Ref<ShaderMaterial> material;
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 9f24c5af72..74ccda3c9a 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -111,7 +111,6 @@ public:
void disconnect_nodes(VisualShader::Type p_type, int p_from_node, int p_from_port, int p_to_node, int p_to_port);
void show_port_preview(VisualShader::Type p_type, int p_node_id, int p_port_id);
void set_node_position(VisualShader::Type p_type, int p_id, const Vector2 &p_position);
- void set_node_size(VisualShader::Type p_type, int p_id, const Vector2 &p_size);
void refresh_node_ports(VisualShader::Type p_type, int p_node);
void set_input_port_default_value(VisualShader::Type p_type, int p_node_id, int p_port_id, Variant p_value);
void update_uniform_refs();
@@ -145,6 +144,7 @@ class VisualShaderEditor : public VBoxContainer {
OptionButton *edit_type_standard;
OptionButton *edit_type_particles;
OptionButton *edit_type_sky;
+ OptionButton *edit_type_fog;
CheckBox *custom_mode_box;
bool custom_mode_enabled = false;
@@ -180,7 +180,8 @@ class VisualShaderEditor : public VBoxContainer {
enum ShaderModeFlags {
MODE_FLAGS_SPATIAL_CANVASITEM = 1,
MODE_FLAGS_SKY = 2,
- MODE_FLAGS_PARTICLES = 4
+ MODE_FLAGS_PARTICLES = 4,
+ MODE_FLAGS_FOG = 8,
};
int mode = MODE_FLAGS_SPATIAL_CANVASITEM;
@@ -203,6 +204,10 @@ class VisualShaderEditor : public VBoxContainer {
TYPE_FLAGS_SKY = 1,
};
+ enum FogTypeFlags {
+ TYPE_FLAGS_FOG = 1,
+ };
+
enum ToolsMenuOptions {
EXPAND_ALL,
COLLAPSE_ALL
@@ -211,10 +216,12 @@ class VisualShaderEditor : public VBoxContainer {
enum NodeMenuOptions {
ADD,
SEPARATOR, // ignore
+ CUT,
COPY,
PASTE,
DELETE,
DUPLICATE,
+ CLEAR_COPY_BUFFER,
SEPARATOR2, // ignore
FLOAT_CONSTANTS,
CONVERT_CONSTANTS_TO_UNIFORMS,
@@ -335,8 +342,6 @@ class VisualShaderEditor : public VBoxContainer {
void _delete_node_request(int p_type, int p_node);
void _delete_nodes_request();
- void _removed_from_graph();
-
void _node_changed(int p_id);
void _edit_port_default_input(Object *p_button, int p_node, int p_port);
@@ -376,19 +381,27 @@ class VisualShaderEditor : public VBoxContainer {
void _port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output);
- void _dup_copy_nodes(int p_type, List<int> &r_nodes, Set<int> &r_excluded);
- void _dup_update_excluded(int p_type, Set<int> &r_excluded);
- void _dup_paste_nodes(int p_type, int p_pasted_type, List<int> &r_nodes, Set<int> &r_excluded, const Vector2 &p_offset, bool p_select);
+ struct CopyItem {
+ int id;
+ Ref<VisualShaderNode> node;
+ Vector2 position;
+ Vector2 size;
+ String group_inputs;
+ String group_outputs;
+ String expression;
+ };
+
+ void _dup_copy_nodes(int p_type, List<CopyItem> &r_nodes, List<VisualShader::Connection> &r_connections);
+ void _dup_paste_nodes(int p_type, List<CopyItem> &r_items, const List<VisualShader::Connection> &p_connections, const Vector2 &p_offset, bool p_duplicate);
void _duplicate_nodes();
Vector2 selection_center;
- int copy_type; // shader type
- List<int> copy_nodes_buffer;
- Set<int> copy_nodes_excluded_buffer;
+ List<CopyItem> copy_items_buffer;
+ List<VisualShader::Connection> copy_connections_buffer;
- void _clear_buffer();
- void _copy_nodes();
+ void _clear_copy_buffer();
+ void _copy_nodes(bool p_cut);
void _paste_nodes(bool p_use_custom_position = false, const Vector2 &p_custom_position = Vector2());
Vector<Ref<VisualShaderNodePlugin>> plugins;
@@ -510,9 +523,7 @@ class EditorInspectorShaderModePlugin : public EditorInspectorPlugin {
public:
virtual bool can_handle(Object *p_object) override;
- virtual void parse_begin(Object *p_object) override;
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;
- virtual void parse_end() override;
};
class VisualShaderNodePortPreview : public Control {
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index 5bbc0c9dd5..4e81fc4e77 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -33,8 +33,8 @@
void VoxelGIEditorPlugin::_bake() {
if (voxel_gi) {
if (voxel_gi->get_probe_data().is_null()) {
- String path = get_tree()->get_edited_scene_root()->get_filename();
- if (path == String()) {
+ String path = get_tree()->get_edited_scene_root()->get_scene_file_path();
+ if (path.is_empty()) {
path = "res://" + voxel_gi->get_name() + "_data.res";
} else {
String ext = path.get_extension();
@@ -67,31 +67,36 @@ void VoxelGIEditorPlugin::_notification(int p_what) {
return;
}
+ // Set information tooltip on the Bake button. This information is useful
+ // to optimize performance (video RAM size) and reduce light leaking (individual cell size).
+
const Vector3i size = voxel_gi->get_estimated_cell_size();
- String text = vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z);
+
+ const Vector3 extents = voxel_gi->get_extents();
+
const int data_size = 4;
const double size_mb = size.x * size.y * size.z * data_size / (1024.0 * 1024.0);
- text += " - " + vformat(TTR("VRAM Size: %s MB"), String::num(size_mb, 2));
-
- if (bake_info->get_text() == text) {
- return;
+ // Add a qualitative measurement to help the user assess whether a VoxelGI node is using a lot of VRAM.
+ String size_quality;
+ if (size_mb < 16.0) {
+ size_quality = TTR("Low");
+ } else if (size_mb < 64.0) {
+ size_quality = TTR("Moderate");
+ } else {
+ size_quality = TTR("High");
}
- // Color the label depending on the estimated performance level.
- Color color;
- if (size_mb <= 16.0 + CMP_EPSILON) {
- // Fast.
- color = bake_info->get_theme_color(SNAME("success_color"), SNAME("Editor"));
- } else if (size_mb <= 64.0 + CMP_EPSILON) {
- // Medium.
- color = bake_info->get_theme_color(SNAME("warning_color"), SNAME("Editor"));
- } else {
- // Slow.
- color = bake_info->get_theme_color(SNAME("error_color"), SNAME("Editor"));
+ String text;
+ text += vformat(TTR("Subdivisions: %s"), vformat(String::utf8("%d × %d × %d"), size.x, size.y, size.z)) + "\n";
+ text += vformat(TTR("Cell size: %s"), vformat(String::utf8("%.3f × %.3f × %.3f"), extents.x / size.x, extents.y / size.y, extents.z / size.z)) + "\n";
+ text += vformat(TTR("Video RAM size: %s MB (%s)"), String::num(size_mb, 2), size_quality);
+
+ // Only update the tooltip when needed to avoid constant redrawing.
+ if (bake->get_tooltip(Point2()) == text) {
+ return;
}
- bake_info->add_theme_color_override("font_color", color);
- bake_info->set_text(text);
+ bake->set_tooltip(text);
}
}
@@ -147,10 +152,6 @@ VoxelGIEditorPlugin::VoxelGIEditorPlugin(EditorNode *p_node) {
bake->set_text(TTR("Bake GI Probe"));
bake->connect("pressed", callable_mp(this, &VoxelGIEditorPlugin::_bake));
bake_hb->add_child(bake);
- bake_info = memnew(Label);
- bake_info->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- bake_info->set_clip_text(true);
- bake_hb->add_child(bake_info);
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, bake_hb);
voxel_gi = nullptr;
diff --git a/editor/plugins/voxel_gi_editor_plugin.h b/editor/plugins/voxel_gi_editor_plugin.h
index 4d3cfe90f6..ed66728557 100644
--- a/editor/plugins/voxel_gi_editor_plugin.h
+++ b/editor/plugins/voxel_gi_editor_plugin.h
@@ -42,7 +42,6 @@ class VoxelGIEditorPlugin : public EditorPlugin {
VoxelGI *voxel_gi;
HBoxContainer *bake_hb;
- Label *bake_info;
Button *bake;
EditorNode *editor;