diff options
Diffstat (limited to 'editor/plugins')
68 files changed, 5946 insertions, 1379 deletions
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp index 41e2062ab2..1afd7df049 100644 --- a/editor/plugins/abstract_polygon_2d_editor.cpp +++ b/editor/plugins/abstract_polygon_2d_editor.cpp @@ -201,6 +201,8 @@ void AbstractPolygon2DEditor::_notification(int p_what) { case NOTIFICATION_READY: { + disable_polygon_editing(false, String()); + button_create->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveCreate", "EditorIcons")); button_edit->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveEdit", "EditorIcons")); button_delete->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("CurveDelete", "EditorIcons")); @@ -275,9 +277,30 @@ void AbstractPolygon2DEditor::_wip_close() { selected_point = Vertex(); } +void AbstractPolygon2DEditor::disable_polygon_editing(bool p_disable, String p_reason) { + + _polygon_editing_enabled = !p_disable; + + button_create->set_disabled(p_disable); + button_edit->set_disabled(p_disable); + button_delete->set_disabled(p_disable); + + if (p_disable) { + + button_create->set_tooltip(p_reason); + button_edit->set_tooltip(p_reason); + button_delete->set_tooltip(p_reason); + } else { + + button_create->set_tooltip(TTR("Create points.")); + button_edit->set_tooltip(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point")); + button_delete->set_tooltip(TTR("Erase points.")); + } +} + bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { - if (!_get_node()) + if (!_get_node() || !_polygon_editing_enabled) return false; Ref<InputEventMouseButton> mb = p_event; @@ -324,16 +347,16 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) return true; } else { - Vector<Vector2> vertices = _get_polygon(insert.polygon); - pre_move_edit = vertices; + Vector<Vector2> vertices2 = _get_polygon(insert.polygon); + pre_move_edit = vertices2; printf("setting pre_move_edit\n"); edited_point = PosVertex(insert.polygon, insert.vertex + 1, xform.affine_inverse().xform(insert.pos)); - vertices.insert(edited_point.vertex, edited_point.pos); + vertices2.insert(edited_point.vertex, edited_point.pos); selected_point = edited_point; edge_point = PosVertex(); undo_redo->create_action(TTR("Insert Point")); - _action_set_polygon(insert.polygon, vertices); + _action_set_polygon(insert.polygon, vertices2); _commit_action(); return true; } @@ -458,6 +481,17 @@ bool AbstractPolygon2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) if (edited_point.valid() && (wip_active || (mm->get_button_mask() & BUTTON_MASK_LEFT))) { Vector2 cpoint = _get_node()->get_global_transform().affine_inverse().xform(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. + if (mode == MODE_EDIT && mm->get_shift()) { + Vector2 old_point = pre_move_edit.get(selected_point.vertex); + if (ABS(cpoint.x - old_point.x) > ABS(cpoint.y - old_point.y)) { + cpoint.y = old_point.y; + } else { + cpoint.x = old_point.x; + } + } + edited_point = PosVertex(edited_point, cpoint); if (!wip_active) { @@ -615,6 +649,13 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl const Color modulate = vertex == active_point ? Color(0.5, 1, 2) : Color(1, 1, 1); p_overlay->draw_texture(handle, point - handle->get_size() * 0.5, modulate); + + if (vertex == hover_point) { + Ref<Font> font = get_font("font", "Label"); + String num = String::num(vertex.vertex); + Size2 num_size = font->get_string_size(num); + p_overlay->draw_string(font, point - num_size * 0.5, num, Color(1.0, 1.0, 1.0, 0.5)); + } } } @@ -627,9 +668,8 @@ void AbstractPolygon2DEditor::forward_canvas_draw_over_viewport(Control *p_overl void AbstractPolygon2DEditor::edit(Node *p_polygon) { - if (!canvas_item_editor) { + if (!canvas_item_editor) canvas_item_editor = CanvasItemEditor::get_singleton(); - } if (p_polygon) { @@ -648,7 +688,6 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) { selected_point = Vertex(); canvas_item_editor->update_viewport(); - } else { _set_node(NULL); @@ -783,19 +822,16 @@ AbstractPolygon2DEditor::AbstractPolygon2DEditor(EditorNode *p_editor, bool p_wi add_child(button_create); button_create->connect("pressed", this, "_menu_option", varray(MODE_CREATE)); button_create->set_toggle_mode(true); - button_create->set_tooltip(TTR("Create points.")); button_edit = memnew(ToolButton); add_child(button_edit); button_edit->connect("pressed", this, "_menu_option", varray(MODE_EDIT)); button_edit->set_toggle_mode(true); - button_edit->set_tooltip(TTR("Edit points.\nLMB: Move Point\nRMB: Erase Point")); button_delete = memnew(ToolButton); add_child(button_delete); button_delete->connect("pressed", this, "_menu_option", varray(MODE_DELETE)); button_delete->set_toggle_mode(true); - button_delete->set_tooltip(TTR("Erase points.")); create_resource = memnew(ConfirmationDialog); add_child(create_resource); diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h index 289c2785b1..97244fa4e9 100644 --- a/editor/plugins/abstract_polygon_2d_editor.h +++ b/editor/plugins/abstract_polygon_2d_editor.h @@ -81,6 +81,8 @@ class AbstractPolygon2DEditor : public HBoxContainer { bool wip_active; bool wip_destructive; + bool _polygon_editing_enabled; + CanvasItemEditor *canvas_item_editor; EditorNode *editor; Panel *panel; @@ -135,6 +137,8 @@ protected: virtual void _create_resource(); public: + void disable_polygon_editing(bool p_disable, String p_reason); + bool forward_gui_input(const Ref<InputEvent> &p_event); void forward_canvas_draw_over_viewport(Control *p_overlay); diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 3029ad978a..2ae39d90de 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -142,7 +142,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven } updating = true; - undo_redo->create_action("Move Node Point"); + undo_redo->create_action(TTR("Move Node Point")); undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); undo_redo->add_do_method(this, "_update_space"); @@ -325,7 +325,7 @@ void AnimationNodeBlendSpace1DEditor::_config_changed(double) { return; updating = true; - undo_redo->create_action("Change BlendSpace1D Limits"); + undo_redo->create_action(TTR("Change BlendSpace1D Limits")); undo_redo->add_do_method(blend_space.ptr(), "set_max_space", max_value->get_value()); undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space()); undo_redo->add_do_method(blend_space.ptr(), "set_min_space", min_value->get_value()); @@ -345,7 +345,7 @@ void AnimationNodeBlendSpace1DEditor::_labels_changed(String) { return; updating = true; - undo_redo->create_action("Change BlendSpace1D Labels", UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Change BlendSpace1D Labels"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(blend_space.ptr(), "set_value_label", label_value->get_text()); undo_redo->add_undo_method(blend_space.ptr(), "set_value_label", blend_space->get_value_label()); undo_redo->add_do_method(this, "_update_space"); @@ -401,7 +401,7 @@ void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) { } updating = true; - undo_redo->create_action("Add Node Point"); + undo_redo->create_action(TTR("Add Node Point")); undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos); undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); undo_redo->add_do_method(this, "_update_space"); @@ -419,7 +419,7 @@ void AnimationNodeBlendSpace1DEditor::_add_animation_type(int p_index) { anim->set_animation(animations_to_add[p_index]); updating = true; - undo_redo->create_action("Add Animation Point"); + undo_redo->create_action(TTR("Add Animation Point")); undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos); undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); undo_redo->add_do_method(this, "_update_space"); @@ -489,7 +489,7 @@ void AnimationNodeBlendSpace1DEditor::_erase_selected() { if (selected_point != -1) { updating = true; - undo_redo->create_action("Remove BlendSpace1D Point"); + undo_redo->create_action(TTR("Remove BlendSpace1D Point")); undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point); undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point); undo_redo->add_do_method(this, "_update_space"); @@ -507,7 +507,7 @@ void AnimationNodeBlendSpace1DEditor::_edit_point_pos(double) { return; updating = true; - undo_redo->create_action("Move BlendSpace1D Node Point"); + undo_redo->create_action(TTR("Move BlendSpace1D Node Point")); undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, edit_value->get_value()); undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); undo_redo->add_do_method(this, "_update_space"); diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 038e2d0b3e..5e8fb8e059 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -195,12 +195,12 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven //add triangle! if (blend_space->has_triangle(making_triangle[0], making_triangle[1], making_triangle[2])) { making_triangle.clear(); - EditorNode::get_singleton()->show_warning(TTR("Triangle already exists")); + EditorNode::get_singleton()->show_warning(TTR("Triangle already exists.")); return; } updating = true; - undo_redo->create_action("Add Triangle"); + undo_redo->create_action(TTR("Add Triangle")); undo_redo->add_do_method(blend_space.ptr(), "add_triangle", making_triangle[0], making_triangle[1], making_triangle[2]); undo_redo->add_undo_method(blend_space.ptr(), "remove_triangle", blend_space->get_triangle_count()); undo_redo->add_do_method(this, "_update_space"); @@ -225,7 +225,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven } updating = true; - undo_redo->create_action("Move Node Point"); + undo_redo->create_action(TTR("Move Node Point")); undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, point); undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); undo_redo->add_do_method(this, "_update_space"); @@ -333,7 +333,7 @@ void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) { } updating = true; - undo_redo->create_action("Add Node Point"); + undo_redo->create_action(TTR("Add Node Point")); undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", node, add_point_pos); undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); undo_redo->add_do_method(this, "_update_space"); @@ -352,7 +352,7 @@ void AnimationNodeBlendSpace2DEditor::_add_animation_type(int p_index) { anim->set_animation(animations_to_add[p_index]); updating = true; - undo_redo->create_action("Add Animation Point"); + undo_redo->create_action(TTR("Add Animation Point")); undo_redo->add_do_method(blend_space.ptr(), "add_blend_point", anim, add_point_pos); undo_redo->add_undo_method(blend_space.ptr(), "remove_blend_point", blend_space->get_blend_point_count()); undo_redo->add_do_method(this, "_update_space"); @@ -639,7 +639,7 @@ void AnimationNodeBlendSpace2DEditor::_config_changed(double) { return; updating = true; - undo_redo->create_action("Change BlendSpace2D Limits"); + undo_redo->create_action(TTR("Change BlendSpace2D Limits")); undo_redo->add_do_method(blend_space.ptr(), "set_max_space", Vector2(max_x_value->get_value(), max_y_value->get_value())); undo_redo->add_undo_method(blend_space.ptr(), "set_max_space", blend_space->get_max_space()); undo_redo->add_do_method(blend_space.ptr(), "set_min_space", Vector2(min_x_value->get_value(), min_y_value->get_value())); @@ -661,7 +661,7 @@ void AnimationNodeBlendSpace2DEditor::_labels_changed(String) { return; updating = true; - undo_redo->create_action("Change BlendSpace2D Labels", UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Change BlendSpace2D Labels"), UndoRedo::MERGE_ENDS); undo_redo->add_do_method(blend_space.ptr(), "set_x_label", label_x->get_text()); undo_redo->add_undo_method(blend_space.ptr(), "set_x_label", blend_space->get_x_label()); undo_redo->add_do_method(blend_space.ptr(), "set_y_label", label_y->get_text()); @@ -677,7 +677,7 @@ void AnimationNodeBlendSpace2DEditor::_erase_selected() { if (selected_point != -1) { updating = true; - undo_redo->create_action("Remove BlendSpace2D Point"); + undo_redo->create_action(TTR("Remove BlendSpace2D Point")); undo_redo->add_do_method(blend_space.ptr(), "remove_blend_point", selected_point); undo_redo->add_undo_method(blend_space.ptr(), "add_blend_point", blend_space->get_blend_point_node(selected_point), blend_space->get_blend_point_position(selected_point), selected_point); @@ -700,7 +700,7 @@ void AnimationNodeBlendSpace2DEditor::_erase_selected() { } else if (selected_triangle != -1) { updating = true; - undo_redo->create_action("Remove BlendSpace2D Triangle"); + undo_redo->create_action(TTR("Remove BlendSpace2D Triangle")); undo_redo->add_do_method(blend_space.ptr(), "remove_triangle", selected_triangle); undo_redo->add_undo_method(blend_space.ptr(), "add_triangle", blend_space->get_triangle_point(selected_triangle, 0), blend_space->get_triangle_point(selected_triangle, 1), blend_space->get_triangle_point(selected_triangle, 2), selected_triangle); @@ -737,7 +737,7 @@ void AnimationNodeBlendSpace2DEditor::_edit_point_pos(double) { if (updating) return; updating = true; - undo_redo->create_action("Move Node Point"); + undo_redo->create_action(TTR("Move Node Point")); undo_redo->add_do_method(blend_space.ptr(), "set_blend_point_position", selected_point, Vector2(edit_x->get_value(), edit_y->get_value())); undo_redo->add_undo_method(blend_space.ptr(), "set_blend_point_position", selected_point, blend_space->get_blend_point_position(selected_point)); undo_redo->add_do_method(this, "_update_space"); @@ -814,7 +814,7 @@ void AnimationNodeBlendSpace2DEditor::_removed_from_graph() { void AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled() { - undo_redo->create_action("Toggle Auto Triangles"); + undo_redo->create_action(TTR("Toggle Auto Triangles")); undo_redo->add_do_method(blend_space.ptr(), "set_auto_triangles", auto_triangles->is_pressed()); undo_redo->add_undo_method(blend_space.ptr(), "set_auto_triangles", blend_space->get_auto_triangles()); undo_redo->add_do_method(this, "_update_space"); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 13c80e5394..bfee76492b 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -80,6 +80,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; } Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { @@ -91,7 +92,7 @@ void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_propert AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_tree(); updating = true; - undo_redo->create_action("Parameter Changed: " + String(p_property), UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Parameter Changed") + ": " + String(p_property), UndoRedo::MERGE_ENDS); undo_redo->add_do_property(tree, p_property, p_value); undo_redo->add_undo_property(tree, p_property, tree->get(p_property)); undo_redo->add_do_method(this, "_update_graph"); @@ -231,6 +232,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { } pb->set_percent_visible(false); + pb->set_custom_minimum_size(Vector2(0, 14) * EDSCALE); animations[E->get()] = pb; node->add_child(pb); @@ -239,7 +241,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { Ref<StyleBoxFlat> sb = node->get_stylebox("frame", "GraphNode"); - Color c = sb->get_border_color(MARGIN_TOP); + Color c = sb->get_border_color(); Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); mono_color.a = 0.85; c = mono_color; @@ -316,7 +318,15 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { EditorNode::get_singleton()->show_warning(TTR("Output node can't be added to the blend tree.")); return; } - Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5; + + Point2 instance_pos = graph->get_scroll_ofs(); + if (use_popup_menu_position) { + instance_pos += popup_menu_position; + } else { + instance_pos += graph->get_size() * 0.5; + } + + instance_pos /= graph->get_zoom(); int base = 1; String name = base_name; @@ -325,7 +335,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { name = base_name + " " + itos(base); } - undo_redo->create_action("Add Node to BlendTree"); + 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); undo_redo->add_do_method(this, "_update_graph"); @@ -336,7 +346,7 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) { updating = true; - undo_redo->create_action("Node Moved"); + undo_redo->create_action(TTR("Node Moved")); undo_redo->add_do_method(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE); undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE); undo_redo->add_do_method(this, "_update_graph"); @@ -354,7 +364,7 @@ void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int return; } - undo_redo->create_action("Nodes Connected"); + undo_redo->create_action(TTR("Nodes Connected")); undo_redo->add_do_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from); undo_redo->add_undo_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index); undo_redo->add_do_method(this, "_update_graph"); @@ -367,7 +377,7 @@ void AnimationNodeBlendTreeEditor::_disconnection_request(const String &p_from, graph->disconnect_node(p_from, p_from_index, p_to, p_to_index); updating = true; - undo_redo->create_action("Nodes Disconnected"); + undo_redo->create_action(TTR("Nodes Disconnected")); undo_redo->add_do_method(blend_tree.ptr(), "disconnect_node", p_to, p_to_index); undo_redo->add_undo_method(blend_tree.ptr(), "connect_node", p_to, p_to_index, p_from); undo_redo->add_do_method(this, "_update_graph"); @@ -383,7 +393,7 @@ void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options, Ref<AnimationNodeAnimation> anim = blend_tree->get_node(p_node); ERR_FAIL_COND(!anim.is_valid()); - undo_redo->create_action("Set Animation"); + undo_redo->create_action(TTR("Set Animation")); undo_redo->add_do_method(anim.ptr(), "set_animation", option); undo_redo->add_undo_method(anim.ptr(), "set_animation", anim->get_animation()); undo_redo->add_do_method(this, "_update_graph"); @@ -393,7 +403,7 @@ void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options, void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { - undo_redo->create_action("Delete Node"); + undo_redo->create_action(TTR("Delete Node")); undo_redo->add_do_method(blend_tree.ptr(), "remove_node", p_which); undo_redo->add_undo_method(blend_tree.ptr(), "add_node", p_which, blend_tree->get_node(p_which), blend_tree.ptr()->get_node_position(p_which)); @@ -411,6 +421,40 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { undo_redo->commit_action(); } +void AnimationNodeBlendTreeEditor::_delete_nodes_request() { + + List<StringName> to_erase; + + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected() && gn->is_close_button_visible()) { + to_erase.push_back(gn->get_name()); + } + } + } + + if (to_erase.empty()) + return; + + undo_redo->create_action(TTR("Delete Node(s)")); + + for (List<StringName>::Element *F = to_erase.front(); F; F = F->next()) { + _delete_request(F->get()); + } + + 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); @@ -434,7 +478,7 @@ void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) { void AnimationNodeBlendTreeEditor::_filter_toggled() { updating = true; - undo_redo->create_action("Toggle filter on/off"); + undo_redo->create_action(TTR("Toggle Filter On/Off")); undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_enabled", filter_enabled->is_pressed()); undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_enabled", _filter_edit->is_filter_enabled()); undo_redo->add_do_method(this, "_update_filters", _filter_edit); @@ -452,7 +496,7 @@ void AnimationNodeBlendTreeEditor::_filter_edited() { bool filtered = edited->is_checked(0); updating = true; - undo_redo->create_action("Change filter"); + undo_redo->create_action(TTR("Change Filter")); undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", edited_path, filtered); undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", edited_path, _filter_edit->is_path_filtered(edited_path)); undo_redo->add_do_method(this, "_update_filters", _filter_edit); @@ -729,6 +773,8 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { ClassDB::bind_method("_open_in_editor", &AnimationNodeBlendTreeEditor::_open_in_editor); ClassDB::bind_method("_scroll_changed", &AnimationNodeBlendTreeEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &AnimationNodeBlendTreeEditor::_delete_request); + ClassDB::bind_method("_delete_nodes_request", &AnimationNodeBlendTreeEditor::_delete_nodes_request); + ClassDB::bind_method("_popup_request", &AnimationNodeBlendTreeEditor::_popup_request); ClassDB::bind_method("_edit_filters", &AnimationNodeBlendTreeEditor::_edit_filters); ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters); ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited); @@ -769,7 +815,7 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima String base_path = AnimationTreeEditor::get_singleton()->get_base_path(); updating = true; - undo_redo->create_action("Node Renamed"); + undo_redo->create_action(TTR("Node Renamed")); undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name); undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name); undo_redo->add_do_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + prev_name, base_path + name); @@ -785,8 +831,8 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima for (int i = 0; i < visible_properties.size(); i++) { String pname = visible_properties[i]->get_edited_property().operator String(); if (pname.begins_with(base_path + prev_name)) { - String new_name = pname.replace_first(base_path + prev_name, base_path + name); - visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name); + String new_name2 = pname.replace_first(base_path + prev_name, base_path + name); + visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name2); } } @@ -849,6 +895,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { singleton = this; updating = false; + use_popup_menu_position = false; graph = memnew(GraphEdit); add_child(graph); @@ -859,6 +906,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { graph->connect("disconnection_request", this, "_disconnection_request", varray(), CONNECT_DEFERRED); graph->connect("node_selected", this, "_node_selected"); graph->connect("scroll_offset_changed", this, "_scroll_changed"); + graph->connect("delete_nodes_request", this, "_delete_nodes_request"); + graph->connect("popup_request", this, "_popup_request"); VSeparator *vs = memnew(VSeparator); graph->get_zoom_hbox()->add_child(vs); @@ -900,7 +949,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { filter_dialog->add_child(filter_vbox); filter_enabled = memnew(CheckBox); - filter_enabled->set_text(TTR("Enable filtering")); + filter_enabled->set_text(TTR("Enable Filtering")); filter_enabled->connect("pressed", this, "_filter_toggled"); filter_vbox->add_child(filter_enabled); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index f2a77cecb4..cb40159a40 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -51,6 +51,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { Ref<AnimationNodeBlendTree> blend_tree; GraphEdit *graph; MenuButton *add_node; + Vector2 popup_menu_position; + bool use_popup_menu_position; PanelContainer *error_panel; Label *error_label; @@ -97,6 +99,8 @@ class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { void _open_in_editor(const String &p_which); 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); diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index b32b537d3d..951e971615 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -54,13 +54,9 @@ void AnimationPlayerEditor::_node_removed(Node *p_node) { track_editor->set_root(NULL); track_editor->show_select_node_warning(true); _update_player(); - //editor->animation_editor_make_visible(false); } } -void AnimationPlayerEditor::_gui_input(Ref<InputEvent> p_event) { -} - void AnimationPlayerEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_PROCESS: { @@ -129,8 +125,10 @@ void AnimationPlayerEditor::_notification(int p_what) { autoplay_icon = get_icon("AutoPlay", "EditorIcons"); stop->set_icon(get_icon("Stop", "EditorIcons")); + onion_toggle->set_icon(get_icon("Onion", "EditorIcons")); + onion_skinning->set_icon(get_icon("GuiMiniTabMenu", "EditorIcons")); + pin->set_icon(get_icon("Pin", "EditorIcons")); - onion_skinning->set_icon(get_icon("Onion", "EditorIcons")); tool_anim->add_style_override("normal", get_stylebox("normal", "Button")); track_editor->get_edit_menu()->add_style_override("normal", get_stylebox("normal", "Button")); @@ -291,25 +289,40 @@ void AnimationPlayerEditor::_pause_pressed() { //player->set_pause( pause->is_pressed() ); } -void AnimationPlayerEditor::_animation_selected(int p_which) { - if (updating) - return; +String AnimationPlayerEditor::_get_current_animation() const { + // when selecting an animation, the idea is that the only interesting behavior // ui-wise is that it should play/blend the next one if currently playing - String current; if (animation->get_selected() >= 0 && animation->get_selected() < animation->get_item_count()) { - current = animation->get_item_text(animation->get_selected()); + return animation->get_item_text(animation->get_selected()); } - if (current != "") { + return ""; +} - player->set_assigned_animation(current); +void AnimationPlayerEditor::_animation_selected(int p_which) { + + if (updating) + return; + + _current_animation_updated(); +} + +void AnimationPlayerEditor::_current_animation_updated() { + String current = _get_current_animation(); + + if (current != "") { Ref<Animation> anim = player->get_animation(current); + + player->set_assigned_animation(current); { + if (!anim->is_connected("changed", this, "_current_animation_updated")) + anim->connect("changed", this, "_current_animation_updated"); + track_editor->set_animation(anim); Node *root = player->get_node(player->get_root()); if (root) { @@ -670,25 +683,29 @@ Dictionary AnimationPlayerEditor::get_state() const { if (EditorNode::get_singleton()->get_edited_scene() && is_visible_in_tree() && player) { d["player"] = EditorNode::get_singleton()->get_edited_scene()->get_path_to(player); d["animation"] = player->get_assigned_animation(); + d["track_editor_state"] = track_editor->get_state(); } return d; } void AnimationPlayerEditor::set_state(const Dictionary &p_state) { - if (p_state.has("visible") && p_state["visible"]) { + if (!p_state.has("visible") || !p_state["visible"]) { + return; + } + if (!EditorNode::get_singleton()->get_edited_scene()) { + return; + } - if (!EditorNode::get_singleton()->get_edited_scene()) - return; + if (p_state.has("player")) { Node *n = EditorNode::get_singleton()->get_edited_scene()->get_node(p_state["player"]); if (Object::cast_to<AnimationPlayer>(n) && EditorNode::get_singleton()->get_editor_selection()->is_selected(n)) { player = Object::cast_to<AnimationPlayer>(n); _update_player(); - show(); + editor->make_bottom_panel_item_visible(this); set_process(true); ensure_visibility(); - //EditorNode::get_singleton()->animation_panel_make_visible(true); if (p_state.has("animation")) { String anim = p_state["animation"]; @@ -697,6 +714,10 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) { } } } + + if (p_state.has("track_editor_state")) { + track_editor->set_state(p_state["track_editor_state"]); + } } void AnimationPlayerEditor::_animation_resource_edit() { @@ -714,17 +735,17 @@ void AnimationPlayerEditor::_animation_edit() { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); track_editor->set_animation(anim); + Node *root = player->get_node(player->get_root()); if (root) { track_editor->set_root(root); } - } else { - track_editor->set_animation(Ref<Animation>()); track_editor->set_root(NULL); } } + void AnimationPlayerEditor::_dialog_action(String p_file) { switch (current_option) { @@ -841,6 +862,12 @@ void AnimationPlayerEditor::_update_player() { onion_skinning->set_disabled(player == NULL); pin->set_disabled(player == NULL); + if (!player) { + AnimationPlayerEditor::singleton->get_track_editor()->update_keying(); + EditorNode::get_singleton()->update_keying(); + return; + } + int active_idx = -1; for (List<StringName>::Element *E = animlist.front(); E; E = E->next()) { @@ -853,12 +880,6 @@ void AnimationPlayerEditor::_update_player() { active_idx = animation->get_item_count() - 1; } - if (!player) { - AnimationPlayerEditor::singleton->get_track_editor()->update_keying(); - EditorNode::get_singleton()->update_keying(); - return; - } - updating = false; if (active_idx != -1) { animation->select(active_idx); @@ -874,8 +895,6 @@ void AnimationPlayerEditor::_update_player() { _animation_selected(0); } - //pause->set_pressed(player->is_paused()); - if (animation->get_item_count()) { String current = animation->get_item_text(animation->get_selected()); Ref<Animation> anim = player->get_animation(current); @@ -903,8 +922,6 @@ void AnimationPlayerEditor::edit(AnimationPlayer *p_player) { track_editor->show_select_node_warning(false); } else { track_editor->show_select_node_warning(true); - - //hide(); } } @@ -1061,17 +1078,19 @@ void AnimationPlayerEditor::_list_changed() { _update_player(); } -void AnimationPlayerEditor::_animation_key_editor_anim_len_changed(float p_len) { - - frame->set_max(p_len); -} - void AnimationPlayerEditor::_animation_key_editor_anim_step_changed(float p_len) { if (p_len) frame->set_step(p_len); else frame->set_step(0.00001); + + String current = _get_current_animation(); + + if (current != "") { + Ref<Animation> anim = player->get_animation(current); + anim->_change_notify("step"); + } } void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag) { @@ -1103,7 +1122,6 @@ void AnimationPlayerEditor::_hide_anim_editors() { track_editor->set_animation(Ref<Animation>()); track_editor->set_root(NULL); track_editor->show_select_node_warning(true); - //editor->animation_editor_make_visible(false); } void AnimationPlayerEditor::_animation_about_to_show_menu() { @@ -1160,22 +1178,22 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { return; } - String current = animation->get_item_text(animation->get_selected()); - Ref<Animation> anim = player->get_animation(current); - //editor->edit_resource(anim); - EditorSettings::get_singleton()->set_resource_clipboard(anim); + String current2 = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim2 = player->get_animation(current2); + //editor->edit_resource(anim2); + EditorSettings::get_singleton()->set_resource_clipboard(anim2); } break; case TOOL_PASTE_ANIM: { - Ref<Animation> anim = EditorSettings::get_singleton()->get_resource_clipboard(); - if (!anim.is_valid()) { + Ref<Animation> anim2 = EditorSettings::get_singleton()->get_resource_clipboard(); + if (!anim2.is_valid()) { error_dialog->set_text(TTR("No animation resource on clipboard!")); error_dialog->popup_centered_minsize(); return; } - String name = anim->get_name(); + String name = anim2->get_name(); if (name == "") { name = TTR("Pasted Animation"); } @@ -1189,7 +1207,7 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { } undo_redo->create_action(TTR("Paste Animation")); - undo_redo->add_do_method(player, "add_animation", name, anim); + undo_redo->add_do_method(player, "add_animation", name, anim2); undo_redo->add_undo_method(player, "remove_animation", name); undo_redo->add_do_method(this, "_animation_player_changed", player); undo_redo->add_undo_method(this, "_animation_player_changed", player); @@ -1206,9 +1224,9 @@ void AnimationPlayerEditor::_animation_tool_menu(int p_option) { return; } - String current = animation->get_item_text(animation->get_selected()); - Ref<Animation> anim = player->get_animation(current); - editor->edit_resource(anim); + String current2 = animation->get_item_text(animation->get_selected()); + Ref<Animation> anim2 = player->get_animation(current2); + editor->edit_resource(anim2); } break; } @@ -1224,7 +1242,6 @@ void AnimationPlayerEditor::_onion_skinning_menu(int p_option) { case ONION_SKINNING_ENABLE: { onion.enabled = !onion.enabled; - menu->set_item_checked(idx, onion.enabled); if (onion.enabled) _start_onion_skinning(); @@ -1425,6 +1442,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { new_state["show_rulers"] = false; new_state["show_guides"] = false; new_state["show_helpers"] = false; + new_state["show_zoom_control"] = false; // TODO: Save/restore only affected entries CanvasItemEditor::get_singleton()->set_state(new_state); } @@ -1477,7 +1495,7 @@ void AnimationPlayerEditor::_prepare_onion_layers_2() { if (valid) { player->seek(pos, true); get_tree()->flush_transform_notifications(); // Needed for transforms of Spatials - values_backup.update_skeletons(); // Needed for Skeletons + values_backup.update_skeletons(); // Needed for Skeletons (2D & 3D) VS::get_singleton()->viewport_set_active(onion.captures[cidx], true); VS::get_singleton()->viewport_set_parent_viewport(root_vp, onion.captures[cidx]); @@ -1541,7 +1559,6 @@ void AnimationPlayerEditor::_pin_pressed() { void AnimationPlayerEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_gui_input"), &AnimationPlayerEditor::_gui_input); ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationPlayerEditor::_node_removed); ClassDB::bind_method(D_METHOD("_play_pressed"), &AnimationPlayerEditor::_play_pressed); ClassDB::bind_method(D_METHOD("_play_from_pressed"), &AnimationPlayerEditor::_play_from_pressed); @@ -1551,6 +1568,7 @@ void AnimationPlayerEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_autoplay_pressed"), &AnimationPlayerEditor::_autoplay_pressed); ClassDB::bind_method(D_METHOD("_pause_pressed"), &AnimationPlayerEditor::_pause_pressed); ClassDB::bind_method(D_METHOD("_animation_selected"), &AnimationPlayerEditor::_animation_selected); + ClassDB::bind_method(D_METHOD("_current_animation_updated"), &AnimationPlayerEditor::_current_animation_updated); ClassDB::bind_method(D_METHOD("_animation_name_edited"), &AnimationPlayerEditor::_animation_name_edited); ClassDB::bind_method(D_METHOD("_animation_new"), &AnimationPlayerEditor::_animation_new); ClassDB::bind_method(D_METHOD("_animation_rename"), &AnimationPlayerEditor::_animation_rename); @@ -1570,7 +1588,6 @@ void AnimationPlayerEditor::_bind_methods() { //ClassDB::bind_method(D_METHOD("_editor_load_all"),&AnimationPlayerEditor::_editor_load_all); ClassDB::bind_method(D_METHOD("_list_changed"), &AnimationPlayerEditor::_list_changed); ClassDB::bind_method(D_METHOD("_animation_key_editor_seek"), &AnimationPlayerEditor::_animation_key_editor_seek); - ClassDB::bind_method(D_METHOD("_animation_key_editor_anim_len_changed"), &AnimationPlayerEditor::_animation_key_editor_anim_len_changed); ClassDB::bind_method(D_METHOD("_animation_key_editor_anim_step_changed"), &AnimationPlayerEditor::_animation_key_editor_anim_step_changed); ClassDB::bind_method(D_METHOD("_hide_anim_editors"), &AnimationPlayerEditor::_hide_anim_editors); ClassDB::bind_method(D_METHOD("_animation_duplicate"), &AnimationPlayerEditor::_animation_duplicate); @@ -1604,12 +1621,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay player = NULL; - Label *l; - - /*l= memnew( Label ); - l->set_text("Animation Player:"); - add_child(l);*/ - HBoxContainer *hb = memnew(HBoxContainer); add_child(hb); @@ -1634,10 +1645,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay play_from->set_tooltip(TTR("Play selected animation from current pos. (D)")); hb->add_child(play_from); - //pause = memnew( Button ); - //pause->set_toggle_mode(true); - //hb->add_child(pause); - frame = memnew(SpinBox); hb->add_child(frame); frame->set_custom_minimum_size(Size2(60, 0)); @@ -1663,7 +1670,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay tool_anim = memnew(MenuButton); tool_anim->set_flat(false); - //tool_anim->set_flat(false); tool_anim->set_tooltip(TTR("Animation Tools")); tool_anim->set_text(TTR("Animation")); tool_anim->get_popup()->add_shortcut(ED_SHORTCUT("animation_player_editor/new_animation", TTR("New")), TOOL_NEW_ANIM); @@ -1694,28 +1700,27 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay hb->add_child(autoplay); autoplay->set_tooltip(TTR("Autoplay on Load")); - //tool_anim->get_popup()->add_separator(); - //tool_anim->get_popup()->add_item("Edit Anim Resource",TOOL_PASTE_ANIM); - hb->add_child(memnew(VSeparator)); track_editor = memnew(AnimationTrackEditor); hb->add_child(track_editor->get_edit_menu()); + hb->add_child(memnew(VSeparator)); + + onion_toggle = memnew(ToolButton); + onion_toggle->set_toggle_mode(true); + onion_toggle->set_tooltip(TTR("Enable Onion Skinning")); + onion_toggle->connect("pressed", this, "_onion_skinning_menu", varray(ONION_SKINNING_ENABLE)); + hb->add_child(onion_toggle); + onion_skinning = memnew(MenuButton); - //onion_skinning->set_flat(false); - onion_skinning->set_tooltip(TTR("Onion Skinning")); - onion_skinning->get_popup()->add_check_shortcut(ED_SHORTCUT("animation_player_editor/onion_skinning", TTR("Enable Onion Skinning")), ONION_SKINNING_ENABLE); - onion_skinning->get_popup()->add_separator(); - onion_skinning->get_popup()->add_item(TTR("Directions"), -1); - onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->set_tooltip(TTR("Onion Skinning Options")); + onion_skinning->get_popup()->add_separator(TTR("Directions")); onion_skinning->get_popup()->add_check_item(TTR("Past"), ONION_SKINNING_PAST); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); onion_skinning->get_popup()->add_check_item(TTR("Future"), ONION_SKINNING_FUTURE); - onion_skinning->get_popup()->add_separator(); - onion_skinning->get_popup()->add_item(TTR("Depth"), -1); - onion_skinning->get_popup()->set_item_disabled(onion_skinning->get_popup()->get_item_count() - 1, true); + onion_skinning->get_popup()->add_separator(TTR("Depth")); onion_skinning->get_popup()->add_radio_check_item(TTR("1 step"), ONION_SKINNING_1_STEP); onion_skinning->get_popup()->set_item_checked(onion_skinning->get_popup()->get_item_count() - 1, true); onion_skinning->get_popup()->add_radio_check_item(TTR("2 steps"), ONION_SKINNING_2_STEPS); @@ -1726,6 +1731,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay onion_skinning->get_popup()->add_check_item(TTR("Include Gizmos (3D)"), ONION_SKINNING_INCLUDE_GIZMOS); hb->add_child(onion_skinning); + hb->add_child(memnew(VSeparator)); + pin = memnew(ToolButton); pin->set_toggle_mode(true); pin->set_tooltip(TTR("Pin AnimationPlayer")); @@ -1742,10 +1749,8 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay VBoxContainer *vb = memnew(VBoxContainer); name_dialog->add_child(vb); - l = memnew(Label); - l->set_text(TTR("Animation Name:")); - vb->add_child(l); - name_title = l; + name_title = memnew(Label(TTR("Animation Name:"))); + vb->add_child(name_title); name = memnew(LineEdit); vb->add_child(name); @@ -1764,7 +1769,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay blend_editor.dialog->set_hide_on_ok(true); VBoxContainer *blend_vb = memnew(VBoxContainer); blend_editor.dialog->add_child(blend_vb); - //blend_editor.dialog->set_child_rect(blend_vb); blend_editor.tree = memnew(Tree); blend_editor.tree->set_columns(2); blend_vb->add_margin_child(TTR("Blend Times:"), blend_editor.tree, true); @@ -1782,8 +1786,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay play_bw->connect("pressed", this, "_play_bw_pressed"); play_bw_from->connect("pressed", this, "_play_bw_from_pressed"); stop->connect("pressed", this, "_stop_pressed"); - //pause->connect("pressed", this,"_pause_pressed"); - //frame->connect("text_entered", this,"_seek_frame_changed"); animation->connect("item_selected", this, "_animation_selected", Vector<Variant>(), true); @@ -1799,7 +1801,6 @@ AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor, AnimationPlay add_child(track_editor); track_editor->set_v_size_flags(SIZE_EXPAND_FILL); track_editor->connect("timeline_changed", this, "_animation_key_editor_seek"); - track_editor->connect("animation_len_changed", this, "_animation_key_editor_anim_len_changed"); track_editor->connect("animation_step_changed", this, "_animation_key_editor_anim_step_changed"); _update_player(); @@ -1887,11 +1888,6 @@ void AnimationPlayerEditorPlugin::make_visible(bool p_visible) { editor->make_bottom_panel_item_visible(anim_editor); anim_editor->set_process(true); anim_editor->ensure_visibility(); - //editor->animation_panel_make_visible(true); - } else { - - //anim_editor->hide(); - //anim_editor->set_idle_process(false); } } @@ -1900,16 +1896,7 @@ AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin(EditorNode *p_node) { editor = p_node; anim_editor = memnew(AnimationPlayerEditor(editor, this)); anim_editor->set_undo_redo(editor->get_undo_redo()); - editor->add_bottom_panel_item(TTR("Animation"), anim_editor); - /* - editor->get_viewport()->add_child(anim_editor); - anim_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); - anim_editor->set_anchor( MARGIN_TOP, Control::ANCHOR_END); - anim_editor->set_margin( MARGIN_TOP, 75 ); - anim_editor->set_anchor( MARGIN_RIGHT, Control::ANCHOR_END); - anim_editor->set_margin( MARGIN_RIGHT, 0 );*/ - anim_editor->hide(); } AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() { diff --git a/editor/plugins/animation_player_editor_plugin.h b/editor/plugins/animation_player_editor_plugin.h index 9085c70410..b1026b5b9f 100644 --- a/editor/plugins/animation_player_editor_plugin.h +++ b/editor/plugins/animation_player_editor_plugin.h @@ -97,11 +97,10 @@ class AnimationPlayerEditor : public VBoxContainer { Button *play_from; Button *play_bw; Button *play_bw_from; - - //Button *pause; Button *autoplay; MenuButton *tool_anim; + ToolButton *onion_toggle; MenuButton *onion_skinning; ToolButton *pin; SpinBox *frame; @@ -172,7 +171,9 @@ class AnimationPlayerEditor : public VBoxContainer { void _autoplay_pressed(); void _stop_pressed(); void _pause_pressed(); + String _get_current_animation() const; void _animation_selected(int p_which); + void _current_animation_updated(); void _animation_new(); void _animation_rename(); void _animation_name_edited(); @@ -228,7 +229,6 @@ class AnimationPlayerEditor : public VBoxContainer { protected: void _notification(int p_what); - void _gui_input(Ref<InputEvent> p_event); void _node_removed(Node *p_node); static void _bind_methods(); diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index 197ee77254..f06b4b2828 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -226,7 +226,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<AnimationNode> an = state_machine->get_node(selected_node); updating = true; - undo_redo->create_action("Move Node"); + undo_redo->create_action(TTR("Move Node")); undo_redo->add_do_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE); undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node)); undo_redo->add_do_method(this, "_update_graph"); @@ -271,7 +271,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected())); updating = true; - undo_redo->create_action("Add Transition"); + undo_redo->create_action(TTR("Add Transition")); undo_redo->add_do_method(state_machine.ptr(), "add_transition", connecting_from, connecting_to_node, tr); undo_redo->add_undo_method(state_machine.ptr(), "remove_transition", connecting_from, connecting_to_node); undo_redo->add_do_method(this, "_update_graph"); @@ -463,7 +463,7 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { } updating = true; - undo_redo->create_action("Add Node"); + undo_redo->create_action(TTR("Add Node")); undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); @@ -490,7 +490,7 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) { } updating = true; - undo_redo->create_action("Add Node"); + undo_redo->create_action(TTR("Add Node")); undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); @@ -714,12 +714,12 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { tl.to += offset; } - for (int i = 0; i < node_rects.size(); i++) { - if (node_rects[i].node_name == tl.from_node) { - _clip_src_line_to_rect(tl.from, tl.to, node_rects[i].node); + for (int j = 0; j < node_rects.size(); j++) { + if (node_rects[j].node_name == tl.from_node) { + _clip_src_line_to_rect(tl.from, tl.to, node_rects[j].node); } - if (node_rects[i].node_name == tl.to_node) { - _clip_dst_line_to_rect(tl.from, tl.to, node_rects[i].node); + if (node_rects[j].node_name == tl.to_node) { + _clip_dst_line_to_rect(tl.from, tl.to, node_rects[j].node); } } @@ -874,9 +874,9 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, playback->get_current_length()); + float len = MAX(0.0001, current_length); - float pos = CLAMP(playback->get_current_play_pos(), 0, len); + float pos = CLAMP(play_pos, 0, len); float c = pos / len; Color fg = get_color("font_color", "Label"); Color bg = fg; @@ -1011,7 +1011,8 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { bool is_playing = false; StringName current_node; StringName blend_from_node; - float play_pos = 0; + play_pos = 0; + current_length = 0; if (playback.is_valid()) { tp = playback->get_travel_path(); @@ -1019,6 +1020,7 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { current_node = playback->get_current_node(); blend_from_node = playback->get_blend_from_node(); play_pos = playback->get_current_play_pos(); + current_length = playback->get_current_length(); } { @@ -1046,6 +1048,27 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { state_machine_play_pos->update(); } + { + if (current_node != StringName() && state_machine->has_node(current_node)) { + + String next = current_node; + Ref<AnimationNodeStateMachine> anodesm = state_machine->get_node(next); + Ref<AnimationNodeStateMachinePlayback> current_node_playback; + + while (anodesm.is_valid()) { + current_node_playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + next + "/playback"); + next += "/" + current_node_playback->get_current_node(); + anodesm = anodesm->get_node(current_node_playback->get_current_node()); + } + + // when current_node is a state machine, use playback of current_node to set play_pos + if (current_node_playback.is_valid()) { + play_pos = current_node_playback->get_current_play_pos(); + current_length = current_node_playback->get_current_length(); + } + } + } + if (last_play_pos != play_pos) { last_play_pos = play_pos; @@ -1088,7 +1111,7 @@ void AnimationNodeStateMachineEditor::_name_edited(const String &p_text) { } updating = true; - undo_redo->create_action("Node Renamed"); + undo_redo->create_action(TTR("Node Renamed")); undo_redo->add_do_method(state_machine.ptr(), "rename_node", prev_name, name); undo_redo->add_undo_method(state_machine.ptr(), "rename_node", name, prev_name); undo_redo->add_do_method(this, "_update_graph"); @@ -1119,7 +1142,7 @@ void AnimationNodeStateMachineEditor::_erase_selected() { if (selected_node != StringName() && state_machine->has_node(selected_node)) { updating = true; - undo_redo->create_action("Node Removed"); + undo_redo->create_action(TTR("Node Removed")); undo_redo->add_do_method(state_machine.ptr(), "remove_node", selected_node); undo_redo->add_undo_method(state_machine.ptr(), "add_node", selected_node, state_machine->get_node(selected_node), state_machine->get_node_position(selected_node)); for (int i = 0; i < state_machine->get_transition_count(); i++) { @@ -1143,7 +1166,7 @@ void AnimationNodeStateMachineEditor::_erase_selected() { Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(state_machine->find_transition(selected_transition_from, selected_transition_to)); updating = true; - undo_redo->create_action("Transition Removed"); + undo_redo->create_action(TTR("Transition Removed")); undo_redo->add_do_method(state_machine.ptr(), "remove_transition", selected_transition_from, selected_transition_to); undo_redo->add_undo_method(state_machine.ptr(), "add_transition", selected_transition_from, selected_transition_to, tr); undo_redo->add_do_method(this, "_update_graph"); @@ -1169,7 +1192,7 @@ void AnimationNodeStateMachineEditor::_autoplay_selected() { } updating = true; - undo_redo->create_action("Set Start Node (Autoplay)"); + undo_redo->create_action(TTR("Set Start Node (Autoplay)")); undo_redo->add_do_method(state_machine.ptr(), "set_start_node", new_start_node); undo_redo->add_undo_method(state_machine.ptr(), "set_start_node", state_machine->get_start_node()); undo_redo->add_do_method(this, "_update_graph"); @@ -1192,7 +1215,7 @@ void AnimationNodeStateMachineEditor::_end_selected() { } updating = true; - undo_redo->create_action("Set Start Node (Autoplay)"); + undo_redo->create_action(TTR("Set Start Node (Autoplay)")); undo_redo->add_do_method(state_machine.ptr(), "set_end_node", new_end_node); undo_redo->add_undo_method(state_machine.ptr(), "set_end_node", state_machine->get_end_node()); undo_redo->add_do_method(this, "_update_graph"); diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index 1c4c06090a..8b0a5a0b00 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -160,6 +160,8 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { StringName last_current_node; Vector<StringName> last_travel_path; float last_play_pos; + float play_pos; + float current_length; float error_time; String error_text; diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp index e2a44069d9..f5d21ffb26 100644 --- a/editor/plugins/animation_tree_player_editor_plugin.cpp +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -200,7 +200,8 @@ void AnimationTreePlayerEditor::_edit_dialog_changed() { if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); } break; - default: {} + default: { + } } } @@ -457,7 +458,8 @@ void AnimationTreePlayerEditor::_popup_edit_dialog() { edit_dialog->set_size(Size2(150, 100)); } break; - default: {} + default: { + } } } @@ -555,7 +557,8 @@ void AnimationTreePlayerEditor::_draw_node(const StringName &p_node) { text += "->"; break; - default: {} + default: { + } } font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); @@ -740,7 +743,8 @@ void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { //open editor //_node_edit_property(click_node); } break; - default: {} + default: { + } } } if (mb->get_button_index() == 2) { @@ -817,7 +821,8 @@ void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { anim_tree->node_set_position(click_node, new_pos); } break; - default: {} + default: { + } } click_type = CLICK_NONE; @@ -835,7 +840,7 @@ void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { click_motion = Point2(mm->get_position().x, mm->get_position().y); update(); } - if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + if (mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index a42765df6b..0dfb53b34a 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -174,8 +174,11 @@ void EditorAssetLibraryItemDescription::set_image(int p_type, int p_index, const if (preview_images[i].is_video) { Ref<Image> overlay = get_icon("PlayOverlay", "EditorIcons")->get_data(); Ref<Image> thumbnail = p_image->get_data(); - Point2 overlay_pos = Point2((thumbnail->get_width() - overlay->get_width()) / 2, (thumbnail->get_height() - overlay->get_height()) / 2); + thumbnail = thumbnail->duplicate(); + Point2 overlay_pos = Point2((thumbnail->get_width() - overlay->get_width() / 2) / 2, (thumbnail->get_height() - overlay->get_height() / 2) / 2); + // Overlay and thumbnail need the same format for `blend_rect` to work. + thumbnail->convert(Image::FORMAT_RGBA8); thumbnail->lock(); thumbnail->blend_rect(overlay, overlay->get_used_rect(), overlay_pos); thumbnail->unlock(); @@ -307,15 +310,20 @@ EditorAssetLibraryItemDescription::EditorAssetLibraryItemDescription() { description = memnew(RichTextLabel); description->connect("meta_clicked", this, "_link_click"); + description->set_custom_minimum_size(Size2(440 * EDSCALE, 300 * EDSCALE)); desc_bg->add_child(description); + VBoxContainer *previews_vbox = memnew(VBoxContainer); + hbox->add_child(previews_vbox); + previews_vbox->add_constant_override("separation", 15 * EDSCALE); + preview = memnew(TextureRect); preview->set_custom_minimum_size(Size2(640 * EDSCALE, 345 * EDSCALE)); - hbox->add_child(preview); + previews_vbox->add_child(preview); previews_bg = memnew(PanelContainer); - vbox->add_child(previews_bg); - previews_bg->set_custom_minimum_size(Size2(0, 101 * EDSCALE)); + previews_vbox->add_child(previews_bg); + previews_bg->set_custom_minimum_size(Size2(640 * EDSCALE, 101 * EDSCALE)); previews = memnew(ScrollContainer); previews_bg->add_child(previews); @@ -451,7 +459,8 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) { progress->set_max(1); progress->set_value(0); } break; - default: {} + default: { + } } prev_status = cstatus; } @@ -609,7 +618,8 @@ void EditorAssetLibrary::_notification(int p_what) { case HTTPClient::STATUS_BODY: { load_status->set_value(0.4); } break; - default: {} + default: { + } } } @@ -815,7 +825,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons _image_update(p_code == HTTPClient::RESPONSE_NOT_MODIFIED, true, p_data, p_queue_id); } else { - // WARN_PRINTS("Error getting image file from URL: " + image_queue[p_queue_id].image_url); + WARN_PRINTS("Error getting image file from URL: " + image_queue[p_queue_id].image_url); Object *obj = ObjectDB::get_instance(image_queue[p_queue_id].target); if (obj) { obj->call("set_image", image_queue[p_queue_id].image_type, image_queue[p_queue_id].image_index, get_icon("DefaultProjectIcon", "EditorIcons")); @@ -1337,6 +1347,7 @@ void EditorAssetLibrary::_bind_methods() { EditorAssetLibrary::EditorAssetLibrary(bool p_templates_only) { + requesting = REQUESTING_NONE; templates_only = p_templates_only; VBoxContainer *library_main = memnew(VBoxContainer); diff --git a/editor/plugins/audio_stream_editor_plugin.h b/editor/plugins/audio_stream_editor_plugin.h index e60bf6a38f..12e4faef94 100644 --- a/editor/plugins/audio_stream_editor_plugin.h +++ b/editor/plugins/audio_stream_editor_plugin.h @@ -33,7 +33,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" -#include "scene/audio/audio_player.h" +#include "scene/audio/audio_stream_player.h" #include "scene/gui/color_rect.h" #include "scene/resources/texture.h" diff --git a/editor/plugins/baked_lightmap_editor_plugin.cpp b/editor/plugins/baked_lightmap_editor_plugin.cpp index d90ff95c1f..d75f06de12 100644 --- a/editor/plugins/baked_lightmap_editor_plugin.cpp +++ b/editor/plugins/baked_lightmap_editor_plugin.cpp @@ -50,7 +50,8 @@ void BakedLightmapEditorPlugin::_bake() { case BakedLightmap::BAKE_ERROR_CANT_CREATE_IMAGE: EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images, make sure path is writable.")); break; - default: {} + default: { + } } } } diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 2913c4ce56..92cc12d931 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -42,9 +42,9 @@ #include "scene/2d/light_2d.h" #include "scene/2d/particles_2d.h" #include "scene/2d/polygon_2d.h" -#include "scene/2d/screen_button.h" #include "scene/2d/skeleton_2d.h" #include "scene/2d/sprite.h" +#include "scene/2d/touch_screen_button.h" #include "scene/gui/grid_container.h" #include "scene/gui/nine_patch_rect.h" #include "scene/main/canvas_layer.h" @@ -84,7 +84,6 @@ public: container = memnew(VBoxContainer); add_child(container); - //set_child_rect(container); child_container = memnew(GridContainer); child_container->set_columns(3); @@ -99,12 +98,14 @@ public: grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE); grid_offset_x->set_max(SPIN_BOX_GRID_RANGE); grid_offset_x->set_suffix("px"); + grid_offset_x->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_offset_x); grid_offset_y = memnew(SpinBox); grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE); grid_offset_y->set_max(SPIN_BOX_GRID_RANGE); grid_offset_y->set_suffix("px"); + grid_offset_y->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_offset_y); label = memnew(Label); @@ -116,12 +117,14 @@ public: grid_step_x->set_min(0.01); grid_step_x->set_max(SPIN_BOX_GRID_RANGE); grid_step_x->set_suffix("px"); + grid_step_x->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_step_x); grid_step_y = memnew(SpinBox); grid_step_y->set_min(0.01); grid_step_y->set_max(SPIN_BOX_GRID_RANGE); grid_step_y->set_suffix("px"); + grid_step_y->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(grid_step_y); container->add_child(memnew(HSeparator)); @@ -139,6 +142,7 @@ public: rotation_offset->set_min(-SPIN_BOX_ROTATION_RANGE); rotation_offset->set_max(SPIN_BOX_ROTATION_RANGE); rotation_offset->set_suffix("deg"); + rotation_offset->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(rotation_offset); label = memnew(Label); @@ -150,6 +154,7 @@ public: rotation_step->set_min(-SPIN_BOX_ROTATION_RANGE); rotation_step->set_max(SPIN_BOX_ROTATION_RANGE); rotation_step->set_suffix("deg"); + rotation_step->set_h_size_flags(SIZE_EXPAND_FILL); child_container->add_child(rotation_step); } @@ -398,10 +403,10 @@ Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_li // Expand with the other ones for (List<CanvasItem *>::Element *E = p_list.front(); E; E = E->next()) { - CanvasItem *canvas_item = E->get(); - Transform2D xform = canvas_item->get_global_transform_with_canvas(); + CanvasItem *canvas_item2 = E->get(); + Transform2D xform = canvas_item2->get_global_transform_with_canvas(); - Rect2 current_rect = canvas_item->_edit_get_rect(); + Rect2 current_rect = canvas_item2->_edit_get_rect(); rect.expand_to(xform.xform(current_rect.position)); rect.expand_to(xform.xform(current_rect.position + Vector2(current_rect.size.x, 0))); rect.expand_to(xform.xform(current_rect.position + current_rect.size)); @@ -816,10 +821,10 @@ void CanvasItemEditor::_commit_canvas_item_state(List<CanvasItem *> p_canvas_ite undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state()); undo_redo->add_undo_method(canvas_item, "_edit_set_state", se->undo_state); if (commit_bones) { - for (List<Dictionary>::Element *E = se->pre_drag_bones_undo_state.front(); E; E = E->next()) { + for (List<Dictionary>::Element *F = se->pre_drag_bones_undo_state.front(); F; F = F->next()) { canvas_item = Object::cast_to<CanvasItem>(canvas_item->get_parent()); undo_redo->add_do_method(canvas_item, "_edit_set_state", canvas_item->_edit_get_state()); - undo_redo->add_undo_method(canvas_item, "_edit_set_state", E->get()); + undo_redo->add_undo_method(canvas_item, "_edit_set_state", F->get()); } } } @@ -1072,36 +1077,36 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { } } - if (drag_type == DRAG_NONE) { + if (!panning) { if (b->is_pressed() && (b->get_button_index() == BUTTON_MIDDLE || (b->get_button_index() == BUTTON_LEFT && tool == TOOL_PAN) || (b->get_button_index() == BUTTON_LEFT && !EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) { // Pan the viewport - drag_type = DRAG_PAN; + panning = true; } } - if (drag_type == DRAG_PAN) { + if (panning) { if (!b->is_pressed()) { // Stop panning the viewport (for any mouse button press) - drag_type = DRAG_NONE; + panning = false; } } } Ref<InputEventKey> k = p_event; if (k.is_valid()) { - if (k->get_scancode() == KEY_SPACE && EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning")) { - if (drag_type == DRAG_NONE) { + if (k->get_scancode() == KEY_SPACE && (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || drag_type != DRAG_NONE)) { + if (!panning) { if (k->is_pressed() && !k->is_echo()) { //Pan the viewport - drag_type = DRAG_PAN; + panning = true; } - } else if (drag_type == DRAG_PAN) { + } else if (panning) { if (!k->is_pressed()) { // Stop panning the viewport (for any mouse button press) - drag_type = DRAG_NONE; + panning = false; } } } @@ -1109,7 +1114,7 @@ bool CanvasItemEditor::_gui_input_zoom_or_pan(const Ref<InputEvent> &p_event) { Ref<InputEventMouseMotion> m = p_event; if (m.is_valid()) { - if (drag_type == DRAG_PAN) { + if (panning) { // Pan the viewport Point2i relative; if (bool(EditorSettings::get_singleton()->get("editors/2d/warped_mouse_panning"))) { @@ -1340,6 +1345,10 @@ bool CanvasItemEditor::_gui_input_rotate(const Ref<InputEvent> &p_event) { // Confirms the node rotation if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Rotate CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, true, false, true); + } + drag_type = DRAG_NONE; return true; } @@ -1378,7 +1387,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() == BUTTON_LEFT && b->is_pressed() && tool == TOOL_SELECT && show_helpers) { + if (b.is_valid() && b->get_button_index() == BUTTON_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]); @@ -1462,10 +1471,14 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) { if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, false); break; case DRAG_ANCHOR_ALL: - if (!use_single_axis || !use_y) control->set_anchor(MARGIN_LEFT, new_anchor.x, false, true); - if (!use_single_axis || !use_y) control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, true); - if (!use_single_axis || use_y) control->set_anchor(MARGIN_TOP, new_anchor.y, false, true); - if (!use_single_axis || use_y) control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, true); + if (!use_single_axis || !use_y) { + control->set_anchor(MARGIN_LEFT, new_anchor.x, false, true); + control->set_anchor(MARGIN_RIGHT, new_anchor.x, false, true); + } + if (!use_single_axis || use_y) { + control->set_anchor(MARGIN_TOP, new_anchor.y, false, true); + control->set_anchor(MARGIN_BOTTOM, new_anchor.y, false, true); + } break; default: break; @@ -1641,6 +1654,9 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) { // Confirm resize if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Resize CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, false, true, true); + } drag_type = DRAG_NONE; viewport->update(); return true; @@ -1747,6 +1763,10 @@ bool CanvasItemEditor::_gui_input_scale(const Ref<InputEvent> &p_event) { // Confirm resize if (b.is_valid() && b->get_button_index() == BUTTON_LEFT && !b->is_pressed()) { _commit_canvas_item_state(drag_selection, TTR("Scale CanvasItem")); + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(false, false, true, true); + } + drag_type = DRAG_NONE; viewport->update(); return true; @@ -1847,11 +1867,14 @@ 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() == BUTTON_LEFT && (drag_type == DRAG_MOVE)) { + if (b.is_valid() && !b->is_pressed() && b->get_button_index() == BUTTON_LEFT) { if (transform.affine_inverse().xform(b->get_position()) != drag_from) { _commit_canvas_item_state(drag_selection, TTR("Move CanvasItem"), true); } + if (key_auto_insert_button->is_pressed()) { + _insert_animation_keys(true, false, false, true); + } drag_type = DRAG_NONE; viewport->update(); return true; @@ -1921,9 +1944,9 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if (drag_selection.size() == 1) { Node2D *node_2d = Object::cast_to<Node2D>(drag_selection[0]); if (node_2d && move_local_base_rotated) { - Transform2D m; - m.rotate(node_2d->get_rotation()); - new_pos += m.xform(drag_to); + Transform2D m2; + m2.rotate(node_2d->get_rotation()); + new_pos += m2.xform(drag_to); } else if (move_local_base) { new_pos += drag_to; } else { @@ -2065,18 +2088,18 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { // Start dragging if (still_selected) { // Drag the node(s) if requested - List<CanvasItem *> selection = _get_edited_canvas_items(); + List<CanvasItem *> selection2 = _get_edited_canvas_items(); // Remove not movable nodes - for (int i = 0; i < selection.size(); i++) { - if (!_is_node_movable(selection[i], true)) { - selection.erase(selection[i]); + for (int i = 0; i < selection2.size(); i++) { + if (!_is_node_movable(selection2[i], true)) { + selection2.erase(selection2[i]); } } - if (selection.size() > 0) { + if (selection2.size() > 0) { drag_type = DRAG_MOVE; - drag_selection = selection; + drag_selection = selection2; drag_from = click; _save_canvas_item_state(drag_selection); } @@ -2190,32 +2213,35 @@ bool CanvasItemEditor::_gui_input_hover(const Ref<InputEvent> &p_event) { void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { bool accepted = false; - if ((accepted = _gui_input_rulers_and_guides(p_event))) { - //printf("Rulers and guides\n"); - } else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) { - //printf("Plugin\n"); - } else if ((accepted = _gui_input_open_scene_on_double_click(p_event))) { - //printf("Open scene on double click\n"); - } else if ((accepted = _gui_input_anchors(p_event))) { - //printf("Anchors\n"); - } else if ((accepted = _gui_input_scale(p_event))) { - //printf("Set scale\n"); - } else if ((accepted = _gui_input_pivot(p_event))) { - //printf("Set pivot\n"); - } else if ((accepted = _gui_input_resize(p_event))) { - //printf("Resize\n"); - } else if ((accepted = _gui_input_rotate(p_event))) { - //printf("Rotate\n"); - } else if ((accepted = _gui_input_move(p_event))) { - //printf("Move\n"); - } else if ((accepted = _gui_input_zoom_or_pan(p_event))) { - //printf("Zoom or pan\n"); - } else if ((accepted = _gui_input_select(p_event))) { - //printf("Selection\n"); - } else { - //printf("Not accepted\n"); + + if (EditorSettings::get_singleton()->get("editors/2d/simple_spacebar_panning") || !Input::get_singleton()->is_key_pressed(KEY_SPACE)) { + if ((accepted = _gui_input_rulers_and_guides(p_event))) { + //printf("Rulers and guides\n"); + } else if ((accepted = editor->get_editor_plugins_over()->forward_gui_input(p_event))) { + //printf("Plugin\n"); + } else if ((accepted = _gui_input_open_scene_on_double_click(p_event))) { + //printf("Open scene on double click\n"); + } else if ((accepted = _gui_input_anchors(p_event))) { + //printf("Anchors\n"); + } else if ((accepted = _gui_input_scale(p_event))) { + //printf("Set scale\n"); + } else if ((accepted = _gui_input_pivot(p_event))) { + //printf("Set pivot\n"); + } else if ((accepted = _gui_input_resize(p_event))) { + //printf("Resize\n"); + } else if ((accepted = _gui_input_rotate(p_event))) { + //printf("Rotate\n"); + } else if ((accepted = _gui_input_move(p_event))) { + //printf("Move\n"); + } else if ((accepted = _gui_input_select(p_event))) { + //printf("Selection\n"); + } else { + //printf("Not accepted\n"); + } } + accepted = (_gui_input_zoom_or_pan(p_event) || accepted); + if (accepted) accept_event(); @@ -2259,8 +2285,6 @@ void CanvasItemEditor::_gui_input_viewport(const Ref<InputEvent> &p_event) { case DRAG_MOVE: c = CURSOR_MOVE; break; - case DRAG_PAN: - c = CURSOR_DRAG; default: break; } @@ -2489,20 +2513,50 @@ void CanvasItemEditor::_draw_grid() { } } -void CanvasItemEditor::_draw_control_helpers(Control *control) { +void CanvasItemEditor::_draw_control_anchors(Control *control) { Transform2D xform = transform * control->get_global_transform_with_canvas(); RID ci = viewport->get_canvas_item(); + if (tool == TOOL_SELECT && !Object::cast_to<Container>(control->get_parent())) { + + // Compute the anchors + float anchors_values[4]; + anchors_values[0] = control->get_anchor(MARGIN_LEFT); + anchors_values[1] = control->get_anchor(MARGIN_TOP); + anchors_values[2] = control->get_anchor(MARGIN_RIGHT); + anchors_values[3] = control->get_anchor(MARGIN_BOTTOM); + + Vector2 anchors_pos[4]; + for (int i = 0; i < 4; i++) { + Vector2 value = Vector2((i % 2 == 0) ? anchors_values[i] : anchors_values[(i + 1) % 4], (i % 2 == 1) ? anchors_values[i] : anchors_values[(i + 1) % 4]); + anchors_pos[i] = xform.xform(_anchor_to_position(control, value)); + } + + // Draw the anchors handles + Rect2 anchor_rects[4]; + anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size()); + anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y)); + anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size()); + anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y)); + + for (int i = 0; i < 4; i++) { + anchor_handle->draw_rect(ci, anchor_rects[i]); + } + } +} + +void CanvasItemEditor::_draw_control_helpers(Control *control) { + Transform2D xform = transform * control->get_global_transform_with_canvas(); if (tool == TOOL_SELECT && show_helpers && !Object::cast_to<Container>(control->get_parent())) { // Draw the helpers Color color_base = Color(0.8, 0.8, 0.8, 0.5); + // Compute the anchors float anchors_values[4]; anchors_values[0] = control->get_anchor(MARGIN_LEFT); anchors_values[1] = control->get_anchor(MARGIN_TOP); anchors_values[2] = control->get_anchor(MARGIN_RIGHT); anchors_values[3] = control->get_anchor(MARGIN_BOTTOM); - // Draw the anchors Vector2 anchors[4]; Vector2 anchors_pos[4]; for (int i = 0; i < 4; i++) { @@ -2569,16 +2623,6 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { _draw_percentage_at_position(percent_val, (line_ends[(dragged_anchor + 1) % 4] + anchors_pos[dragged_anchor]) / 2, (Margin)((dragged_anchor + 1) % 4)); } - Rect2 anchor_rects[4]; - anchor_rects[0] = Rect2(anchors_pos[0] - anchor_handle->get_size(), anchor_handle->get_size()); - anchor_rects[1] = Rect2(anchors_pos[1] - Vector2(0.0, anchor_handle->get_size().y), Point2(-anchor_handle->get_size().x, anchor_handle->get_size().y)); - anchor_rects[2] = Rect2(anchors_pos[2], -anchor_handle->get_size()); - anchor_rects[3] = Rect2(anchors_pos[3] - Vector2(anchor_handle->get_size().x, 0.0), Point2(anchor_handle->get_size().x, -anchor_handle->get_size().y)); - - for (int i = 0; i < 4; i++) { - anchor_handle->draw_rect(ci, anchor_rects[i]); - } - // Draw the margin values and the node width/height when dragging control side float ratio = 0.33; Transform2D parent_transform = xform * control->get_transform().affine_inverse(); @@ -2597,6 +2641,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_LEFT: case DRAG_BOTTOM_LEFT: _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(node_pos_in_parent[0], Math::lerp(node_pos_in_parent[1], node_pos_in_parent[3], ratio)); end = start - Vector2(control->get_margin(MARGIN_LEFT), 0); @@ -2611,6 +2656,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_RIGHT: case DRAG_BOTTOM_RIGHT: _draw_margin_at_position(control->get_size().width, parent_transform.xform(Vector2((node_pos_in_parent[0] + node_pos_in_parent[2]) / 2, node_pos_in_parent[3])) + Vector2(0, 5), MARGIN_BOTTOM); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(node_pos_in_parent[2], Math::lerp(node_pos_in_parent[3], node_pos_in_parent[1], ratio)); end = start - Vector2(control->get_margin(MARGIN_RIGHT), 0); @@ -2625,6 +2671,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_TOP_LEFT: case DRAG_TOP_RIGHT: _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2)) + Vector2(5, 0), MARGIN_RIGHT); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(Math::lerp(node_pos_in_parent[0], node_pos_in_parent[2], ratio), node_pos_in_parent[1]); end = start - Vector2(0, control->get_margin(MARGIN_TOP)); @@ -2639,6 +2686,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) { case DRAG_BOTTOM_LEFT: case DRAG_BOTTOM_RIGHT: _draw_margin_at_position(control->get_size().height, parent_transform.xform(Vector2(node_pos_in_parent[2], (node_pos_in_parent[1] + node_pos_in_parent[3]) / 2) + Vector2(5, 0)), MARGIN_RIGHT); + FALLTHROUGH; case DRAG_MOVE: start = Vector2(Math::lerp(node_pos_in_parent[2], node_pos_in_parent[0], ratio), node_pos_in_parent[3]); end = start - Vector2(0, control->get_margin(MARGIN_BOTTOM)); @@ -2741,16 +2789,18 @@ void CanvasItemEditor::_draw_selection() { if (canvas_item->_edit_use_pivot()) { // Draw the node's pivot - Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position())).orthonormalized(); + Transform2D unscaled_transform = (xform * canvas_item->get_transform().affine_inverse() * Transform2D(canvas_item->_edit_get_rotation(), canvas_item->_edit_get_position() + canvas_item->_edit_get_pivot())).orthonormalized(); Transform2D simple_xform = viewport->get_transform() * unscaled_transform; + viewport->draw_set_transform_matrix(simple_xform); - viewport->draw_texture(pivot_icon, canvas_item->_edit_get_pivot() - (pivot_icon->get_size() / 2).floor()); + viewport->draw_texture(pivot_icon, -(pivot_icon->get_size() / 2).floor()); viewport->draw_set_transform_matrix(viewport->get_transform()); } // Draw control-related helpers Control *control = Object::cast_to<Control>(canvas_item); if (control && _is_node_movable(control)) { + _draw_control_anchors(control); _draw_control_helpers(control); } @@ -2827,13 +2877,20 @@ void CanvasItemEditor::_draw_selection() { Point2 bsfrom = transform.xform(drag_from); Point2 bsto = transform.xform(box_selecting_to); - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(bsfrom, bsto - bsfrom), Color(0.7, 0.7, 1.0, 0.3)); + VisualServer::get_singleton()->canvas_item_add_rect( + ci, + Rect2(bsfrom, bsto - bsfrom), + get_color("accent_color", "Editor") * Color(1, 1, 1, 0.375)); } - Color rotate_color(0.4, 0.7, 1.0, 0.8); if (drag_type == DRAG_ROTATE) { // Draw the line when rotating a node - viewport->draw_line(transform.xform(drag_rotation_center), transform.xform(drag_to), rotate_color); + viewport->draw_line( + transform.xform(drag_rotation_center), + transform.xform(drag_to), + get_color("accent_color", "Editor") * Color(1, 1, 1, 0.6), + Math::round(2 * EDSCALE), + true); } } @@ -3276,8 +3333,33 @@ void CanvasItemEditor::_notification(int p_what) { // Activate / Deactivate the pivot tool pivot_button->set_disabled(nb_having_pivot == 0); - // Show / Hide the layout button - presets_menu->set_visible(nb_control > 0 && nb_control == selection.size()); + // Show / Hide the layout and anchors mode buttons + if (nb_control > 0 && nb_control == selection.size()) { + presets_menu->set_visible(true); + presets_menu->set_tooltip(TTR("Presets for the anchors and margins values of a Control node.")); + anchor_mode_button->set_visible(true); + anchor_mode_button->set_tooltip(TTR("When active, moving Control nodes changes their anchors instead of their margins.")); + + // Set the pressed state of the node + anchor_mode_button->set_pressed(anchors_mode); + + // Disable if the selected node is child of a container + presets_menu->set_disabled(false); + anchor_mode_button->set_disabled(false); + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + Control *control = Object::cast_to<Control>(E->get()); + if (!control || Object::cast_to<Container>(control->get_parent())) { + presets_menu->set_disabled(true); + presets_menu->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent.")); + anchor_mode_button->set_disabled(true); + anchor_mode_button->set_tooltip(TTR("Children of containers have their anchors and margins values overridden by their parent.")); + break; + } + } + } else { + presets_menu->set_visible(false); + anchor_mode_button->set_visible(false); + } // Update the viewport if bones changes for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) { @@ -3355,6 +3437,7 @@ void CanvasItemEditor::_notification(int p_what) { key_rot_button->set_icon(get_icon("KeyRotation", "EditorIcons")); key_scale_button->set_icon(get_icon("KeyScale", "EditorIcons")); key_insert_button->set_icon(get_icon("Key", "EditorIcons")); + key_auto_insert_button->set_icon(get_icon("AutoKey", "EditorIcons")); zoom_minus->set_icon(get_icon("ZoomLess", "EditorIcons")); zoom_reset->set_icon(get_icon("ZoomReset", "EditorIcons")); @@ -3383,9 +3466,10 @@ void CanvasItemEditor::_notification(int p_what) { p->add_icon_item(get_icon("ControlHcenterWide", "EditorIcons"), "HCenter Wide ", ANCHORS_AND_MARGINS_PRESET_HCENTER_WIDE); p->add_separator(); p->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_AND_MARGINS_PRESET_WIDE); + p->add_icon_item(get_icon("Anchor", "EditorIcons"), "Keep Ratio", ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO); p->add_separator(); p->add_submenu_item(TTR("Anchors only"), "Anchors"); - p->set_item_icon(20, get_icon("Anchor", "EditorIcons")); + p->set_item_icon(21, get_icon("Anchor", "EditorIcons")); anchors_popup->clear(); anchors_popup->add_icon_item(get_icon("ControlAlignTopLeft", "EditorIcons"), "Top Left", ANCHORS_PRESET_TOP_LEFT); @@ -3407,9 +3491,31 @@ void CanvasItemEditor::_notification(int p_what) { anchors_popup->add_icon_item(get_icon("ControlHcenterWide", "EditorIcons"), "HCenter Wide ", ANCHORS_PRESET_HCENTER_WIDE); anchors_popup->add_separator(); anchors_popup->add_icon_item(get_icon("ControlAlignWide", "EditorIcons"), "Full Rect", ANCHORS_PRESET_WIDE); + + anchor_mode_button->set_icon(get_icon("Anchor", "EditorIcons")); } } +void CanvasItemEditor::_selection_changed() { + // Update the anchors_mode + int nbValidControls = 0; + int nbAnchorsMode = 0; + List<Node *> selection = editor_selection->get_selected_node_list(); + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + Control *control = Object::cast_to<Control>(E->get()); + if (!control) + continue; + if (Object::cast_to<Container>(control->get_parent())) + continue; + + nbValidControls++; + if (control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_")) { + nbAnchorsMode++; + } + } + anchors_mode = (nbValidControls == nbAnchorsMode); +} + void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { Array selection = editor_selection->get_selected_nodes(); @@ -3627,6 +3733,36 @@ void CanvasItemEditor::_set_anchors_and_margins_preset(Control::LayoutPreset p_p } undo_redo->commit_action(); + + anchors_mode = false; +} + +void CanvasItemEditor::_set_anchors_and_margins_to_keep_ratio() { + List<Node *> selection = editor_selection->get_selected_node_list(); + + undo_redo->create_action(TTR("Change Anchors and Margins")); + + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + + Control *control = Object::cast_to<Control>(E->get()); + if (control) { + Point2 top_left_anchor = _position_to_anchor(control, Point2()); + Point2 bottom_right_anchor = _position_to_anchor(control, control->get_size()); + undo_redo->add_do_method(control, "set_anchor", MARGIN_LEFT, top_left_anchor.x, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_RIGHT, bottom_right_anchor.x, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_TOP, top_left_anchor.y, false, true); + undo_redo->add_do_method(control, "set_anchor", MARGIN_BOTTOM, bottom_right_anchor.y, false, true); + undo_redo->add_do_method(control, "set_meta", "_edit_use_anchors_", true); + + bool use_anchors = control->has_meta("_edit_use_anchors_") && control->get_meta("_edit_use_anchors_"); + undo_redo->add_undo_method(control, "_edit_set_state", control->_edit_get_state()); + undo_redo->add_undo_method(control, "set_meta", "_edit_use_anchors_", use_anchors); + + anchors_mode = true; + } + } + + undo_redo->commit_action(); } void CanvasItemEditor::_set_anchors_preset(Control::LayoutPreset p_preset) { @@ -3687,6 +3823,92 @@ void CanvasItemEditor::_button_tool_select(int p_index) { tool = (Tool)p_index; } +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()); + if (!canvas_item || !canvas_item->is_visible_in_tree()) + continue; + + if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + if (Object::cast_to<Node2D>(canvas_item)) { + 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); + if (key_rot && p_rotation) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(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); + + if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) { + //look for an IK chain + List<Node2D *> ik_chain; + + Node2D *n = Object::cast_to<Node2D>(n2d->get_parent_item()); + bool has_chain = false; + + while (n) { + + ik_chain.push_back(n); + if (n->has_meta("_edit_ik_")) { + has_chain = true; + break; + } + + if (!n->get_parent_item()) + break; + n = Object::cast_to<Node2D>(n->get_parent_item()); + } + + if (has_chain && ik_chain.size()) { + + for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) { + + if (key_pos) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), p_on_existing); + if (key_rot) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), p_on_existing); + if (key_scale) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), p_on_existing); + } + } + } + + } else if (Object::cast_to<Control>(canvas_item)) { + + 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); + if (key_rot) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), p_on_existing); + if (key_scale) + AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), p_on_existing); + } + } +} + +void CanvasItemEditor::_button_toggle_anchor_mode(bool p_status) { + List<CanvasItem *> selection = _get_edited_canvas_items(false, false); + for (List<CanvasItem *>::Element *E = selection.front(); E; E = E->next()) { + Control *control = Object::cast_to<Control>(E->get()); + if (!control || Object::cast_to<Container>(control->get_parent())) + continue; + + control->set_meta("_edit_use_anchors_", p_status); + } + + anchors_mode = p_status; + + viewport->update(); +} + void CanvasItemEditor::_popup_callback(int p_op) { last_option = MenuOption(p_op); @@ -3787,6 +4009,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { show_rulers = !show_rulers; int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); view_menu->get_popup()->set_item_checked(idx, show_rulers); + _update_scrollbars(); viewport->update(); } break; case SHOW_GUIDES: { @@ -3796,6 +4019,8 @@ void CanvasItemEditor::_popup_callback(int p_op) { viewport->update(); } break; case LOCK_SELECTED: { + undo_redo->create_action(TTR("Lock Selected")); + List<Node *> selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); @@ -3804,12 +4029,18 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - canvas_item->set_meta("_edit_lock_", true); - emit_signal("item_lock_status_changed"); + undo_redo->add_do_method(canvas_item, "set_meta", "_edit_lock_", true); + undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_lock_"); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); } - viewport->update(); + undo_redo->add_do_method(viewport, "update", Variant()); + undo_redo->add_undo_method(viewport, "update", Variant()); + undo_redo->commit_action(); } break; case UNLOCK_SELECTED: { + undo_redo->create_action(TTR("Unlock Selected")); + List<Node *> selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); @@ -3818,12 +4049,18 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - canvas_item->set_meta("_edit_lock_", Variant()); - emit_signal("item_lock_status_changed"); + undo_redo->add_do_method(canvas_item, "remove_meta", "_edit_lock_"); + undo_redo->add_undo_method(canvas_item, "set_meta", "_edit_lock_", true); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); } - viewport->update(); + undo_redo->add_do_method(viewport, "update", Variant()); + undo_redo->add_undo_method(viewport, "update", Variant()); + undo_redo->commit_action(); } break; case GROUP_SELECTED: { + undo_redo->create_action(TTR("Group Selected")); + List<Node *> selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); @@ -3832,12 +4069,18 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - canvas_item->set_meta("_edit_group_", true); - emit_signal("item_group_status_changed"); + undo_redo->add_do_method(canvas_item, "set_meta", "_edit_group_", true); + undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_group_"); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); } - viewport->update(); + undo_redo->add_do_method(viewport, "update", Variant()); + undo_redo->add_undo_method(viewport, "update", Variant()); + undo_redo->commit_action(); } break; case UNGROUP_SELECTED: { + undo_redo->create_action(TTR("Ungroup Selected")); + List<Node *> selection = editor_selection->get_selected_node_list(); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); @@ -3846,10 +4089,14 @@ void CanvasItemEditor::_popup_callback(int p_op) { if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - canvas_item->set_meta("_edit_group_", Variant()); - emit_signal("item_group_status_changed"); + undo_redo->add_do_method(canvas_item, "remove_meta", "_edit_group_"); + undo_redo->add_undo_method(canvas_item, "set_meta", "_edit_group_", true); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); } - viewport->update(); + undo_redo->add_do_method(viewport, "update", Variant()); + undo_redo->add_undo_method(viewport, "update", Variant()); + undo_redo->commit_action(); } break; case ANCHORS_AND_MARGINS_PRESET_TOP_LEFT: { _set_anchors_and_margins_preset(PRESET_TOP_LEFT); @@ -3899,6 +4146,9 @@ void CanvasItemEditor::_popup_callback(int p_op) { case ANCHORS_AND_MARGINS_PRESET_WIDE: { _set_anchors_and_margins_preset(Control::PRESET_WIDE); } break; + case ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO: { + _set_anchors_and_margins_to_keep_ratio(); + } break; case ANCHORS_PRESET_TOP_LEFT: { _set_anchors_preset(PRESET_TOP_LEFT); @@ -3954,73 +4204,7 @@ void CanvasItemEditor::_popup_callback(int p_op) { bool existing = p_op == ANIM_INSERT_KEY_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()); - if (!canvas_item || !canvas_item->is_visible_in_tree()) - continue; - - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) - continue; - - if (Object::cast_to<Node2D>(canvas_item)) { - Node2D *n2d = Object::cast_to<Node2D>(canvas_item); - - if (key_pos) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "position", n2d->get_position(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "rotation_degrees", Math::rad2deg(n2d->get_rotation()), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(n2d, "scale", n2d->get_scale(), existing); - - if (n2d->has_meta("_edit_bone_") && n2d->get_parent_item()) { - //look for an IK chain - List<Node2D *> ik_chain; - - Node2D *n = Object::cast_to<Node2D>(n2d->get_parent_item()); - bool has_chain = false; - - while (n) { - - ik_chain.push_back(n); - if (n->has_meta("_edit_ik_")) { - has_chain = true; - break; - } - - if (!n->get_parent_item()) - break; - n = Object::cast_to<Node2D>(n->get_parent_item()); - } - - if (has_chain && ik_chain.size()) { - - for (List<Node2D *>::Element *F = ik_chain.front(); F; F = F->next()) { - - if (key_pos) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "position", F->get()->get_position(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "rotation_degrees", Math::rad2deg(F->get()->get_rotation()), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(F->get(), "scale", F->get()->get_scale(), existing); - } - } - } - - } else if (Object::cast_to<Control>(canvas_item)) { - - 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(), existing); - if (key_rot) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_rotation", ctrl->get_rotation_degrees(), existing); - if (key_scale) - AnimationPlayerEditor::singleton->get_track_editor()->insert_node_value_key(ctrl, "rect_size", ctrl->get_size(), existing); - } - } + _insert_animation_keys(true, true, true, existing); } break; case ANIM_INSERT_POS: { @@ -4126,10 +4310,19 @@ void CanvasItemEditor::_popup_callback(int p_op) { _focus_selection(p_op); } break; + case PREVIEW_CANVAS_SCALE: { + + bool preview = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(PREVIEW_CANVAS_SCALE)); + preview = !preview; + VS::get_singleton()->canvas_set_disable_scale(!preview); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(PREVIEW_CANVAS_SCALE), preview); + + } break; case SKELETON_MAKE_BONES: { Map<Node *, Object *> &selection = editor_selection->get_selection(); + undo_redo->create_action(TTR("Create Custom Bone(s) from Node(s)")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { Node2D *n2d = Object::cast_to<Node2D>(E->key()); @@ -4139,18 +4332,24 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->get_parent_item()) continue; + if (n2d->has_meta("_edit_bone_") && (bool)n2d->get_meta("_edit_bone_") == true) + continue; - n2d->set_meta("_edit_bone_", true); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "set_meta", "_edit_bone_", true); + undo_redo->add_undo_method(n2d, "remove_meta", "_edit_bone_"); } - viewport->update(); + undo_redo->add_do_method(this, "_queue_update_bone_list"); + undo_redo->add_undo_method(this, "_queue_update_bone_list"); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_CLEAR_BONES: { Map<Node *, Object *> &selection = editor_selection->get_selection(); + undo_redo->create_action(TTR("Clear Bones")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { Node2D *n2d = Object::cast_to<Node2D>(E->key()); @@ -4158,39 +4357,47 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->is_visible_in_tree()) continue; + if (!n2d->has_meta("_edit_bone_")) + continue; - n2d->set_meta("_edit_bone_", Variant()); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "remove_meta", "_edit_bone_"); + undo_redo->add_undo_method(n2d, "set_meta", "_edit_bone_", n2d->get_meta("_edit_bone_")); } - viewport->update(); + undo_redo->add_do_method(this, "_queue_update_bone_list"); + undo_redo->add_undo_method(this, "_queue_update_bone_list"); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_SET_IK_CHAIN: { List<Node *> selection = editor_selection->get_selected_node_list(); + undo_redo->create_action(TTR("Make IK Chain")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *canvas_item = Object::cast_to<CanvasItem>(E->get()); if (!canvas_item || !canvas_item->is_visible_in_tree()) continue; - if (canvas_item->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; + if (canvas_item->has_meta("_edit_ik_") && (bool)canvas_item->get_meta("_edit_ik_") == true) + continue; - canvas_item->set_meta("_edit_ik_", true); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(canvas_item, "set_meta", "_edit_ik_", true); + undo_redo->add_undo_method(canvas_item, "remove_meta", "_edit_ik_"); } - - viewport->update(); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; case SKELETON_CLEAR_IK_CHAIN: { Map<Node *, Object *> &selection = editor_selection->get_selection(); + undo_redo->create_action(TTR("Clear IK Chain")); for (Map<Node *, Object *>::Element *E = selection.front(); E; E = E->next()) { CanvasItem *n2d = Object::cast_to<CanvasItem>(E->key()); @@ -4198,12 +4405,15 @@ void CanvasItemEditor::_popup_callback(int p_op) { continue; if (!n2d->is_visible_in_tree()) continue; + if (!n2d->has_meta("_edit_ik_")) + continue; - n2d->set_meta("_edit_ik_", Variant()); - if (!skeleton_show_bones) - skeleton_menu->get_popup()->activate_item(skeleton_menu->get_popup()->get_item_index(SKELETON_SHOW_BONES)); + undo_redo->add_do_method(n2d, "remove_meta", "_edit_ik_"); + undo_redo->add_undo_method(n2d, "set_meta", "_edit_ik_", n2d->get_meta("_edit_ik_")); } - viewport->update(); + undo_redo->add_do_method(viewport, "update"); + undo_redo->add_undo_method(viewport, "update"); + undo_redo->commit_action(); } break; } @@ -4274,6 +4484,7 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_button_zoom_reset", &CanvasItemEditor::_button_zoom_reset); ClassDB::bind_method("_button_zoom_plus", &CanvasItemEditor::_button_zoom_plus); ClassDB::bind_method("_button_toggle_snap", &CanvasItemEditor::_button_toggle_snap); + ClassDB::bind_method("_button_toggle_anchor_mode", &CanvasItemEditor::_button_toggle_anchor_mode); ClassDB::bind_method("_update_scroll", &CanvasItemEditor::_update_scroll); ClassDB::bind_method("_update_scrollbars", &CanvasItemEditor::_update_scrollbars); ClassDB::bind_method("_popup_callback", &CanvasItemEditor::_popup_callback); @@ -4284,8 +4495,10 @@ void CanvasItemEditor::_bind_methods() { ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport); ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport); ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed); + ClassDB::bind_method("_queue_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list); ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed); + ClassDB::bind_method("_selection_changed", &CanvasItemEditor::_selection_changed); ClassDB::bind_method("_popup_warning_depop", &CanvasItemEditor::_popup_warning_depop); ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed); ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide); @@ -4319,6 +4532,7 @@ Dictionary CanvasItemEditor::get_state() const { state["show_rulers"] = show_rulers; state["show_guides"] = show_guides; state["show_helpers"] = show_helpers; + state["show_zoom_control"] = zoom_hb->is_visible(); state["show_edit_locks"] = show_edit_locks; state["snap_rotation"] = snap_rotation; state["snap_relative"] = snap_relative; @@ -4329,6 +4543,7 @@ Dictionary CanvasItemEditor::get_state() const { void CanvasItemEditor::set_state(const Dictionary &p_state) { + bool update_scrollbars = false; Dictionary state = p_state; if (state.has("zoom")) { zoom = p_state["zoom"]; @@ -4337,7 +4552,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { if (state.has("ofs")) { view_offset = p_state["ofs"]; previous_update_view_offset = view_offset; - _update_scrollbars(); + update_scrollbars = true; } if (state.has("grid_offset")) { @@ -4425,6 +4640,7 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { show_rulers = state["show_rulers"]; int idx = view_menu->get_popup()->get_item_index(SHOW_RULERS); view_menu->get_popup()->set_item_checked(idx, show_rulers); + update_scrollbars = true; } if (state.has("show_guides")) { @@ -4445,6 +4661,11 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { view_menu->get_popup()->set_item_checked(idx, show_edit_locks); } + if (state.has("show_zoom_control")) { + // This one is not user-controllable, but instrumentable + zoom_hb->set_visible(state["show_zoom_control"]); + } + if (state.has("snap_rotation")) { snap_rotation = state["snap_rotation"]; int idx = snap_config_menu->get_popup()->get_item_index(SNAP_USE_ROTATION); @@ -4469,6 +4690,9 @@ void CanvasItemEditor::set_state(const Dictionary &p_state) { skeleton_menu->get_popup()->set_item_checked(idx, skeleton_show_bones); } + if (update_scrollbars) { + _update_scrollbars(); + } viewport->update(); } @@ -4541,8 +4765,11 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { snap_grid = true; snap_guides = true; snap_rotation = false; + snap_relative = false; snap_pixel = false; + anchors_mode = false; + skeleton_show_bones = true; drag_type = DRAG_NONE; @@ -4550,6 +4777,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { drag_to = Vector2(); dragged_guide_pos = Point2(); dragged_guide_index = -1; + panning = false; bone_last_frame = 0; @@ -4560,6 +4788,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { editor_selection = p_editor->get_editor_selection(); editor_selection->add_editor_plugin(this); editor_selection->connect("selection_changed", this, "update"); + editor_selection->connect("selection_changed", this, "_selection_changed"); hb = memnew(HBoxContainer); add_child(hb); @@ -4716,28 +4945,29 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { hb->add_child(snap_config_menu); snap_config_menu->set_h_size_flags(SIZE_SHRINK_END); snap_config_menu->set_tooltip(TTR("Snapping Options")); + snap_config_menu->set_switch_on_hover(true); PopupMenu *p = snap_config_menu->get_popup(); p->connect("id_pressed", this, "_popup_callback"); p->set_hide_on_checkable_item_selection(false); - p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_grid", TTR("Snap to grid")), SNAP_USE_GRID); + p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_grid", TTR("Snap to Grid")), SNAP_USE_GRID); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_rotation_snap", TTR("Use Rotation Snap")), SNAP_USE_ROTATION); p->add_shortcut(ED_SHORTCUT("canvas_item_editor/configure_snap", TTR("Configure Snap...")), SNAP_CONFIGURE); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_relative", TTR("Snap Relative")), SNAP_RELATIVE); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/use_pixel_snap", TTR("Use Pixel Snap")), SNAP_USE_PIXEL); - p->add_submenu_item(TTR("Smart snapping"), "SmartSnapping"); + p->add_submenu_item(TTR("Smart Snapping"), "SmartSnapping"); smartsnap_config_popup = memnew(PopupMenu); p->add_child(smartsnap_config_popup); smartsnap_config_popup->set_name("SmartSnapping"); smartsnap_config_popup->connect("id_pressed", this, "_popup_callback"); smartsnap_config_popup->set_hide_on_checkable_item_selection(false); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_parent", TTR("Snap to parent")), SNAP_USE_NODE_PARENT); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_anchors", TTR("Snap to node anchor")), SNAP_USE_NODE_ANCHORS); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_sides", TTR("Snap to node sides")), SNAP_USE_NODE_SIDES); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_center", TTR("Snap to node center")), SNAP_USE_NODE_CENTER); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to other nodes")), SNAP_USE_OTHER_NODES); - smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_guides", TTR("Snap to guides")), SNAP_USE_GUIDES); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_parent", TTR("Snap to Parent")), SNAP_USE_NODE_PARENT); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_anchors", TTR("Snap to Node Anchor")), SNAP_USE_NODE_ANCHORS); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_sides", TTR("Snap to Node Sides")), SNAP_USE_NODE_SIDES); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_node_center", TTR("Snap to Node Center")), SNAP_USE_NODE_CENTER); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_other_nodes", TTR("Snap to Other Nodes")), SNAP_USE_OTHER_NODES); + smartsnap_config_popup->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/snap_guides", TTR("Snap to Guides")), SNAP_USE_GUIDES); hb->add_child(memnew(VSeparator)); @@ -4767,6 +4997,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { skeleton_menu = memnew(MenuButton); hb->add_child(skeleton_menu); skeleton_menu->set_tooltip(TTR("Skeleton Options")); + skeleton_menu->set_switch_on_hover(true); p = skeleton_menu->get_popup(); p->set_hide_on_checkable_item_selection(false); @@ -4785,8 +5016,10 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { view_menu->set_text(TTR("View")); hb->add_child(view_menu); view_menu->get_popup()->connect("id_pressed", this, "_popup_callback"); + view_menu->set_switch_on_hover(true); 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("Show Grid"), KEY_G), 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"), KEY_R), SHOW_RULERS); @@ -4798,11 +5031,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { 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_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); presets_menu = memnew(MenuButton); presets_menu->set_text(TTR("Layout")); hb->add_child(presets_menu); presets_menu->hide(); + presets_menu->set_switch_on_hover(true); p = presets_menu->get_popup(); p->connect("id_pressed", this, "_popup_callback"); @@ -4812,6 +5048,12 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { anchors_popup->set_name("Anchors"); anchors_popup->connect("id_pressed", this, "_popup_callback"); + anchor_mode_button = memnew(ToolButton); + hb->add_child(anchor_mode_button); + anchor_mode_button->set_toggle_mode(true); + anchor_mode_button->hide(); + anchor_mode_button->connect("toggled", this, "_button_toggle_anchor_mode"); + animation_hb = memnew(HBoxContainer); hb->add_child(animation_hb); animation_hb->add_child(memnew(VSeparator)); @@ -4823,6 +5065,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_loc_button->set_pressed(true); key_loc_button->set_focus_mode(FOCUS_NONE); key_loc_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_POS)); + 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_toggle_mode(true); @@ -4830,26 +5073,36 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { key_rot_button->set_pressed(true); key_rot_button->set_focus_mode(FOCUS_NONE); key_rot_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_ROT)); + 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_toggle_mode(true); key_scale_button->set_flat(true); key_scale_button->set_focus_mode(FOCUS_NONE); key_scale_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_SCALE)); + 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", this, "_popup_callback", varray(ANIM_INSERT_KEY)); - key_insert_button->set_tooltip(TTR("Insert keys.")); + 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)); - animation_hb->add_child(key_insert_button); + key_auto_insert_button = memnew(Button); + key_auto_insert_button->set_flat(true); + key_auto_insert_button->set_toggle_mode(true); + key_auto_insert_button->set_focus_mode(FOCUS_NONE); + //key_auto_insert_button->connect("pressed", this, "_popup_callback", varray(ANIM_INSERT_KEY)); + key_auto_insert_button->set_tooltip(TTR("Auto insert keys when objects are translated, rotated on scaled (based on mask).\nKeys are only added to existing tracks, no new tracks will be created.\nKeys must be inserted manually for the first time.")); + key_auto_insert_button->set_shortcut(ED_SHORTCUT("canvas_item_editor/anim_auto_insert_key", TTR("Auto Insert Key"))); + animation_hb->add_child(key_auto_insert_button); animation_menu = memnew(MenuButton); animation_menu->set_text(TTR("Animation")); animation_hb->add_child(animation_menu); animation_menu->get_popup()->connect("id_pressed", this, "_popup_callback"); + animation_menu->set_switch_on_hover(true); p = animation_menu->get_popup(); @@ -4903,7 +5156,6 @@ void CanvasItemEditorPlugin::make_visible(bool p_visible) { canvas_item_editor->show(); canvas_item_editor->set_physics_process(true); VisualServer::get_singleton()->viewport_set_hide_canvas(editor->get_scene_root()->get_viewport_rid(), false); - canvas_item_editor->viewport->grab_focus(); } else { @@ -5359,7 +5611,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte selector = memnew(AcceptDialog); editor->get_gui_base()->add_child(selector); - selector->set_title(TTR("Change default type")); + selector->set_title(TTR("Change Default Type")); selector->connect("confirmed", this, "_on_change_type_confirmed"); selector->connect("popup_hide", this, "_on_change_type_closed"); @@ -5396,6 +5648,7 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte label_desc->add_constant_override("line_spacing", 0); label_desc->hide(); editor->get_gui_base()->add_child(label_desc); + VS::get_singleton()->canvas_set_disable_scale(true); } CanvasItemEditorViewport::~CanvasItemEditorViewport() { diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 7b6563944e..e098d261c0 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -129,6 +129,7 @@ private: ANCHORS_AND_MARGINS_PRESET_VCENTER_WIDE, ANCHORS_AND_MARGINS_PRESET_HCENTER_WIDE, ANCHORS_AND_MARGINS_PRESET_WIDE, + ANCHORS_AND_MARGINS_PRESET_KEEP_RATIO, ANCHORS_PRESET_TOP_LEFT, ANCHORS_PRESET_TOP_RIGHT, ANCHORS_PRESET_BOTTOM_LEFT, @@ -171,6 +172,7 @@ private: ANIM_CLEAR_POSE, VIEW_CENTER_TO_SELECTION, VIEW_FRAME_TO_SELECTION, + PREVIEW_CANVAS_SCALE, SKELETON_MAKE_BONES, SKELETON_CLEAR_BONES, SKELETON_SHOW_BONES, @@ -204,8 +206,7 @@ private: DRAG_V_GUIDE, DRAG_H_GUIDE, DRAG_DOUBLE_GUIDE, - DRAG_KEY_MOVE, - DRAG_PAN + DRAG_KEY_MOVE }; EditorSelection *editor_selection; @@ -240,6 +241,8 @@ private: Point2 view_offset; Point2 previous_update_view_offset; + bool anchors_mode; + Point2 grid_offset; Point2 grid_step; int grid_step_multiplier; @@ -261,6 +264,7 @@ private: bool key_pos; bool key_rot; bool key_scale; + bool panning; MenuOption last_option; @@ -346,10 +350,13 @@ private: PopupMenu *anchors_and_margins_popup; PopupMenu *anchors_popup; + ToolButton *anchor_mode_button; + Button *key_loc_button; Button *key_rot_button; Button *key_scale_button; Button *key_insert_button; + Button *key_auto_insert_button; PopupMenu *selection_menu; @@ -421,6 +428,8 @@ private: Object *_get_editor_data(Object *p_what); + void _insert_animation_keys(bool p_location, bool p_rotation, bool p_scale, bool p_on_existing); + void _keying_changed(); void _unhandled_key_input(const Ref<InputEvent> &p_ev); @@ -434,6 +443,7 @@ private: void _draw_guides(); void _draw_focus(); void _draw_grid(); + void _draw_control_anchors(Control *control); void _draw_control_helpers(Control *control); void _draw_selection(); void _draw_axis(); @@ -458,6 +468,8 @@ private: void _gui_input_viewport(const Ref<InputEvent> &p_event); + void _selection_changed(); + void _focus_selection(int p_op); void _solve_IK(Node2D *leaf_node, Point2 target_position); @@ -469,6 +481,9 @@ private: void _set_anchors_preset(Control::LayoutPreset p_preset); void _set_margins_preset(Control::LayoutPreset p_preset); void _set_anchors_and_margins_preset(Control::LayoutPreset p_preset); + void _set_anchors_and_margins_to_keep_ratio(); + + void _button_toggle_anchor_mode(bool p_status); HBoxContainer *zoom_hb; void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); @@ -571,6 +586,8 @@ public: void focus_selection(); + bool is_anchors_mode_enabled() { return anchors_mode; }; + CanvasItemEditor(EditorNode *p_editor); }; diff --git a/editor/plugins/collision_shape_2d_editor_plugin.cpp b/editor/plugins/collision_shape_2d_editor_plugin.cpp index fc572f54e1..c8561d22a4 100644 --- a/editor/plugins/collision_shape_2d_editor_plugin.cpp +++ b/editor/plugins/collision_shape_2d_editor_plugin.cpp @@ -35,9 +35,9 @@ #include "scene/resources/circle_shape_2d.h" #include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" +#include "scene/resources/line_shape_2d.h" #include "scene/resources/rectangle_shape_2d.h" #include "scene/resources/segment_shape_2d.h" -#include "scene/resources/shape_line_2d.h" Variant CollisionShape2DEditor::get_handle_value(int idx) const { @@ -93,7 +93,7 @@ Variant CollisionShape2DEditor::get_handle_value(int idx) const { case RECTANGLE_SHAPE: { Ref<RectangleShape2D> rect = node->get_shape(); - if (idx < 2) { + if (idx < 3) { return rect->get_extents().abs(); } @@ -175,12 +175,15 @@ void CollisionShape2DEditor::set_handle(int idx, Point2 &p_point) { } break; case RECTANGLE_SHAPE: { - if (idx < 2) { + if (idx < 3) { Ref<RectangleShape2D> rect = node->get_shape(); Vector2 extents = rect->get_extents(); - extents[idx] = p_point[idx]; - + if (idx == 2) { + extents = p_point; + } else { + extents[idx] = p_point[idx]; + } rect->set_extents(extents.abs()); canvas_item_editor->update_viewport(); @@ -496,13 +499,15 @@ void CollisionShape2DEditor::forward_canvas_draw_over_viewport(Control *p_overla case RECTANGLE_SHAPE: { Ref<RectangleShape2D> shape = node->get_shape(); - handles.resize(2); + handles.resize(3); Vector2 ext = shape->get_extents(); handles.write[0] = Point2(ext.x, 0); handles.write[1] = Point2(0, -ext.y); + handles.write[2] = Point2(ext.x, -ext.y); p_overlay->draw_texture(h, gt.xform(handles[0]) - size); p_overlay->draw_texture(h, gt.xform(handles[1]) - size); + p_overlay->draw_texture(h, gt.xform(handles[2]) - size); } break; diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp new file mode 100644 index 0000000000..1622ce17b2 --- /dev/null +++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp @@ -0,0 +1,308 @@ +/*************************************************************************/ +/* cpu_particles_2d_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 "cpu_particles_2d_editor_plugin.h" + +#include "canvas_item_editor_plugin.h" +#include "core/io/image_loader.h" +#include "scene/2d/cpu_particles_2d.h" +#include "scene/gui/separator.h" +#include "scene/resources/particles_material.h" + +void CPUParticles2DEditorPlugin::edit(Object *p_object) { + + particles = Object::cast_to<CPUParticles2D>(p_object); +} + +bool CPUParticles2DEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("CPUParticles2D"); +} + +void CPUParticles2DEditorPlugin::make_visible(bool p_visible) { + + if (p_visible) { + + toolbar->show(); + } else { + + toolbar->hide(); + } +} + +void CPUParticles2DEditorPlugin::_file_selected(const String &p_file) { + + source_emission_file = p_file; + emission_mask->popup_centered_minsize(); +} + +void CPUParticles2DEditorPlugin::_menu_callback(int p_idx) { + + switch (p_idx) { + case MENU_LOAD_EMISSION_MASK: { + + file->popup_centered_ratio(); + + } break; + case MENU_CLEAR_EMISSION_MASK: { + + emission_mask->popup_centered_minsize(); + } break; + } +} + +void CPUParticles2DEditorPlugin::_generate_emission_mask() { + + Ref<Image> img; + img.instance(); + Error err = ImageLoader::load_image(source_emission_file, img); + ERR_EXPLAIN(TTR("Error loading image:") + " " + source_emission_file); + ERR_FAIL_COND(err != OK); + + if (img->is_compressed()) { + img->decompress(); + } + img->convert(Image::FORMAT_RGBA8); + ERR_FAIL_COND(img->get_format() != Image::FORMAT_RGBA8); + Size2i s = Size2(img->get_width(), img->get_height()); + ERR_FAIL_COND(s.width == 0 || s.height == 0); + + Vector<Point2> valid_positions; + Vector<Point2> valid_normals; + Vector<uint8_t> valid_colors; + + valid_positions.resize(s.width * s.height); + + EmissionMode emode = (EmissionMode)emission_mask_mode->get_selected(); + + if (emode == EMISSION_MODE_BORDER_DIRECTED) { + valid_normals.resize(s.width * s.height); + } + + bool capture_colors = emission_colors->is_pressed(); + + if (capture_colors) { + valid_colors.resize(s.width * s.height * 4); + } + + int vpc = 0; + + { + PoolVector<uint8_t> data = img->get_data(); + PoolVector<uint8_t>::Read r = data.read(); + + for (int i = 0; i < s.width; i++) { + for (int j = 0; j < s.height; j++) { + + uint8_t a = r[(j * s.width + i) * 4 + 3]; + + if (a > 128) { + + if (emode == EMISSION_MODE_SOLID) { + + if (capture_colors) { + valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; + valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; + valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; + valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; + } + valid_positions.write[vpc++] = Point2(i, j); + + } else { + + bool on_border = false; + for (int x = i - 1; x <= i + 1; x++) { + for (int y = j - 1; y <= j + 1; y++) { + + if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { + on_border = true; + break; + } + } + + if (on_border) + break; + } + + if (on_border) { + valid_positions.write[vpc] = Point2(i, j); + + if (emode == EMISSION_MODE_BORDER_DIRECTED) { + Vector2 normal; + for (int x = i - 2; x <= i + 2; x++) { + for (int y = j - 2; y <= j + 2; y++) { + + if (x == i && y == j) + continue; + + if (x < 0 || y < 0 || x >= s.width || y >= s.height || r[(y * s.width + x) * 4 + 3] <= 128) { + normal += Vector2(x - i, y - j).normalized(); + } + } + } + + normal.normalize(); + valid_normals.write[vpc] = normal; + } + + if (capture_colors) { + valid_colors.write[vpc * 4 + 0] = r[(j * s.width + i) * 4 + 0]; + valid_colors.write[vpc * 4 + 1] = r[(j * s.width + i) * 4 + 1]; + valid_colors.write[vpc * 4 + 2] = r[(j * s.width + i) * 4 + 2]; + valid_colors.write[vpc * 4 + 3] = r[(j * s.width + i) * 4 + 3]; + } + + vpc++; + } + } + } + } + } + } + + valid_positions.resize(vpc); + if (valid_normals.size()) { + valid_normals.resize(vpc); + } + + ERR_EXPLAIN(TTR("No pixels with transparency > 128 in image...")); + ERR_FAIL_COND(valid_positions.size() == 0); + + if (capture_colors) { + PoolColorArray pca; + pca.resize(vpc); + PoolColorArray::Write pcaw = pca.write(); + for (int i = 0; i < vpc; i += 1) { + Color color; + color.r = valid_colors[i * 4 + 0] / 255.0f; + color.g = valid_colors[i * 4 + 1] / 255.0f; + color.b = valid_colors[i * 4 + 2] / 255.0f; + color.a = valid_colors[i * 4 + 3] / 255.0f; + pcaw[i] = color; + } + particles->set_emission_colors(pca); + } + + if (valid_normals.size()) { + particles->set_emission_shape(CPUParticles2D::EMISSION_SHAPE_DIRECTED_POINTS); + PoolVector2Array norms; + norms.resize(valid_normals.size()); + PoolVector2Array::Write normsw = norms.write(); + for (int i = 0; i < valid_normals.size(); i += 1) { + normsw[i] = valid_normals[i]; + } + particles->set_emission_normals(norms); + } else { + particles->set_emission_shape(CPUParticles2D::EMISSION_SHAPE_POINTS); + } + + { + PoolVector2Array points; + points.resize(valid_positions.size()); + PoolVector2Array::Write pointsw = points.write(); + for (int i = 0; i < valid_positions.size(); i += 1) { + pointsw[i] = valid_positions[i]; + } + particles->set_emission_points(points); + } +} + +void CPUParticles2DEditorPlugin::_notification(int p_what) { + + if (p_what == NOTIFICATION_ENTER_TREE) { + + menu->get_popup()->connect("id_pressed", this, "_menu_callback"); + menu->set_icon(menu->get_popup()->get_icon("Particles2D", "EditorIcons")); + file->connect("file_selected", this, "_file_selected"); + } +} + +void CPUParticles2DEditorPlugin::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_menu_callback"), &CPUParticles2DEditorPlugin::_menu_callback); + ClassDB::bind_method(D_METHOD("_file_selected"), &CPUParticles2DEditorPlugin::_file_selected); + ClassDB::bind_method(D_METHOD("_generate_emission_mask"), &CPUParticles2DEditorPlugin::_generate_emission_mask); +} + +CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin(EditorNode *p_node) { + + particles = NULL; + editor = p_node; + undo_redo = editor->get_undo_redo(); + + toolbar = memnew(HBoxContainer); + add_control_to_container(CONTAINER_CANVAS_EDITOR_MENU, toolbar); + toolbar->hide(); + + toolbar->add_child(memnew(VSeparator)); + + menu = memnew(MenuButton); + menu->get_popup()->add_item(TTR("Load Emission Mask"), MENU_LOAD_EMISSION_MASK); + // menu->get_popup()->add_item(TTR("Clear Emission Mask"), MENU_CLEAR_EMISSION_MASK); + menu->set_text(TTR("Particles")); + menu->set_switch_on_hover(true); + toolbar->add_child(menu); + + file = memnew(EditorFileDialog); + List<String> ext; + ImageLoader::get_recognized_extensions(&ext); + for (List<String>::Element *E = ext.front(); E; E = E->next()) { + file->add_filter("*." + E->get() + "; " + E->get().to_upper()); + } + file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + toolbar->add_child(file); + + epoints = memnew(SpinBox); + epoints->set_min(1); + epoints->set_max(8192); + epoints->set_step(1); + epoints->set_value(512); + file->get_vbox()->add_margin_child(TTR("Generated Point Count:"), epoints); + + emission_mask = memnew(ConfirmationDialog); + emission_mask->set_title(TTR("Load Emission Mask")); + VBoxContainer *emvb = memnew(VBoxContainer); + emission_mask->add_child(emvb); + emission_mask_mode = memnew(OptionButton); + emvb->add_margin_child(TTR("Emission Mask"), emission_mask_mode); + emission_mask_mode->add_item("Solid Pixels", EMISSION_MODE_SOLID); + emission_mask_mode->add_item("Border Pixels", EMISSION_MODE_BORDER); + emission_mask_mode->add_item("Directed Border Pixels", EMISSION_MODE_BORDER_DIRECTED); + emission_colors = memnew(CheckBox); + emission_colors->set_text(TTR("Capture from Pixel")); + emvb->add_margin_child(TTR("Emission Colors"), emission_colors); + + toolbar->add_child(emission_mask); + + emission_mask->connect("confirmed", this, "_generate_emission_mask"); +} + +CPUParticles2DEditorPlugin::~CPUParticles2DEditorPlugin() { +} diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h new file mode 100644 index 0000000000..f715abd87b --- /dev/null +++ b/editor/plugins/cpu_particles_2d_editor_plugin.h @@ -0,0 +1,92 @@ +/*************************************************************************/ +/* cpu_particles_2d_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2019 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 CPU_PARTICLES_2D_EDITOR_PLUGIN_H +#define CPU_PARTICLES_2D_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/2d/cpu_particles_2d.h" +#include "scene/gui/box_container.h" +#include "scene/gui/file_dialog.h" + +class CPUParticles2DEditorPlugin : public EditorPlugin { + + GDCLASS(CPUParticles2DEditorPlugin, EditorPlugin); + + enum { + MENU_LOAD_EMISSION_MASK, + MENU_CLEAR_EMISSION_MASK + }; + + enum EmissionMode { + EMISSION_MODE_SOLID, + EMISSION_MODE_BORDER, + EMISSION_MODE_BORDER_DIRECTED + }; + + CPUParticles2D *particles; + + EditorFileDialog *file; + EditorNode *editor; + + HBoxContainer *toolbar; + MenuButton *menu; + + SpinBox *epoints; + + ConfirmationDialog *emission_mask; + OptionButton *emission_mask_mode; + CheckBox *emission_colors; + + String source_emission_file; + + UndoRedo *undo_redo; + void _file_selected(const String &p_file); + void _menu_callback(int p_idx); + void _generate_emission_mask(); + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + virtual String get_name() const { return "CPUParticles2D"; } + bool has_main_screen() const { return false; } + virtual void edit(Object *p_object); + virtual bool handles(Object *p_object) const; + virtual void make_visible(bool p_visible); + + CPUParticles2DEditorPlugin(EditorNode *p_node); + ~CPUParticles2DEditorPlugin(); +}; + +#endif // CPU_PARTICLES_2D_EDITOR_PLUGIN_H diff --git a/editor/plugins/cpu_particles_editor_plugin.cpp b/editor/plugins/cpu_particles_editor_plugin.cpp index 7222525dd7..70be9b95bb 100644 --- a/editor/plugins/cpu_particles_editor_plugin.cpp +++ b/editor/plugins/cpu_particles_editor_plugin.cpp @@ -101,6 +101,7 @@ CPUParticlesEditor::CPUParticlesEditor() { particles_editor_hb = memnew(HBoxContainer); SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); options = memnew(MenuButton); + options->set_switch_on_hover(true); particles_editor_hb->add_child(options); particles_editor_hb->hide(); diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 3d76b5da21..55feb40c2c 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -53,11 +53,11 @@ CurveEditor::CurveEditor() { _presets_menu = memnew(PopupMenu); _presets_menu->set_name("_presets_menu"); - _presets_menu->add_item(TTR("Flat0"), PRESET_FLAT0); - _presets_menu->add_item(TTR("Flat1"), PRESET_FLAT1); + _presets_menu->add_item(TTR("Flat 0"), PRESET_FLAT0); + _presets_menu->add_item(TTR("Flat 1"), PRESET_FLAT1); _presets_menu->add_item(TTR("Linear"), PRESET_LINEAR); - _presets_menu->add_item(TTR("Ease in"), PRESET_EASE_IN); - _presets_menu->add_item(TTR("Ease out"), PRESET_EASE_OUT); + _presets_menu->add_item(TTR("Ease In"), PRESET_EASE_IN); + _presets_menu->add_item(TTR("Ease Out"), PRESET_EASE_OUT); _presets_menu->add_item(TTR("Smoothstep"), PRESET_SMOOTHSTEP); _presets_menu->connect("id_pressed", this, "_on_preset_item_selected"); _context_menu->add_child(_presets_menu); @@ -194,7 +194,7 @@ void CurveEditor::on_gui_input(const Ref<InputEvent> &p_event) { Vector2 dir = (control_pos - point_pos).normalized(); real_t tangent; - if (Math::abs(dir.x) > CMP_EPSILON) + if (!Math::is_zero_approx(dir.x)) tangent = dir.y / dir.x; else tangent = 9999 * (dir.y >= 0 ? 1 : -1); @@ -330,10 +330,10 @@ void CurveEditor::open_context_menu(Vector2 pos) { _context_menu->clear(); if (_curve_ref.is_valid()) { - _context_menu->add_item(TTR("Add point"), CONTEXT_ADD_POINT); + _context_menu->add_item(TTR("Add Point"), CONTEXT_ADD_POINT); if (_selected_point >= 0) { - _context_menu->add_item(TTR("Remove point"), CONTEXT_REMOVE_POINT); + _context_menu->add_item(TTR("Remove Point"), CONTEXT_REMOVE_POINT); if (_selected_tangent != TANGENT_NONE) { _context_menu->add_separator(); @@ -351,12 +351,12 @@ void CurveEditor::open_context_menu(Vector2 pos) { _context_menu->add_separator(); if (_selected_point > 0) { - _context_menu->add_check_item(TTR("Left linear"), CONTEXT_LEFT_LINEAR); + _context_menu->add_check_item(TTR("Left Linear"), CONTEXT_LEFT_LINEAR); _context_menu->set_item_checked(_context_menu->get_item_index(CONTEXT_LEFT_LINEAR), _curve_ref->get_point_left_mode(_selected_point) == Curve::TANGENT_LINEAR); } if (_selected_point + 1 < _curve_ref->get_point_count()) { - _context_menu->add_check_item(TTR("Right linear"), CONTEXT_RIGHT_LINEAR); + _context_menu->add_check_item(TTR("Right Linear"), CONTEXT_RIGHT_LINEAR); _context_menu->set_item_checked(_context_menu->get_item_index(CONTEXT_RIGHT_LINEAR), _curve_ref->get_point_right_mode(_selected_point) == Curve::TANGENT_LINEAR); } @@ -366,7 +366,7 @@ void CurveEditor::open_context_menu(Vector2 pos) { _context_menu->add_separator(); } - _context_menu->add_submenu_item(TTR("Load preset"), _presets_menu->get_name()); + _context_menu->add_submenu_item(TTR("Load Preset"), _presets_menu->get_name()); _context_menu->set_size(Size2(0, 0)); _context_menu->popup(); diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp index 8464dfd0aa..28e57ac48a 100644 --- a/editor/plugins/editor_preview_plugins.cpp +++ b/editor/plugins/editor_preview_plugins.cpp @@ -36,7 +36,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "scene/resources/bit_mask.h" +#include "scene/resources/bit_map.h" #include "scene/resources/dynamic_font.h" #include "scene/resources/material.h" #include "scene/resources/mesh.h" @@ -78,7 +78,7 @@ bool EditorTexturePreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Texture"); } -bool EditorTexturePreviewPlugin::should_generate_small_preview() const { +bool EditorTexturePreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -92,19 +92,26 @@ Ref<Texture> EditorTexturePreviewPlugin::generate(const RES &p_from, const Size2 if (!tex.is_valid()) { return Ref<Texture>(); } + Ref<Image> atlas = tex->get_data(); + if (!atlas.is_valid()) { + return Ref<Texture>(); + } + img = atlas->get_rect(atex->get_region()); } else if (ltex.is_valid()) { img = ltex->to_image(); } else { Ref<Texture> tex = p_from; img = tex->get_data(); + if (img.is_valid()) { + img = img->duplicate(); + } } if (img.is_null() || img->empty()) return Ref<Texture>(); - img = img->duplicate(); img->clear_mipmaps(); if (img->is_compressed()) { @@ -179,7 +186,7 @@ Ref<Texture> EditorImagePreviewPlugin::generate(const RES &p_from, const Size2 p EditorImagePreviewPlugin::EditorImagePreviewPlugin() { } -bool EditorImagePreviewPlugin::should_generate_small_preview() const { +bool EditorImagePreviewPlugin::generate_small_preview_automatically() const { return true; } //////////////////////////////////////////////////////////////////////////// @@ -243,7 +250,7 @@ Ref<Texture> EditorBitmapPreviewPlugin::generate(const RES &p_from, const Size2 return ptex; } -bool EditorBitmapPreviewPlugin::should_generate_small_preview() const { +bool EditorBitmapPreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -310,7 +317,7 @@ bool EditorMaterialPreviewPlugin::handles(const String &p_type) const { return ClassDB::is_parent_class(p_type, "Material"); //any material } -bool EditorMaterialPreviewPlugin::should_generate_small_preview() const { +bool EditorMaterialPreviewPlugin::generate_small_preview_automatically() const { return true; } @@ -332,7 +339,7 @@ Ref<Texture> EditorMaterialPreviewPlugin::generate(const RES &p_from, const Size OS::get_singleton()->delay_usec(10); } - Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture); + Ref<Image> img = VS::get_singleton()->texture_get_data(viewport_texture); VS::get_singleton()->mesh_surface_set_material(sphere, 0, RID()); ERR_FAIL_COND_V(!img.is_valid(), Ref<ImageTexture>()); @@ -732,7 +739,7 @@ Ref<Texture> EditorMeshPreviewPlugin::generate(const RES &p_from, const Size2 p_ OS::get_singleton()->delay_usec(10); } - Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture); + Ref<Image> img = VS::get_singleton()->texture_get_data(viewport_texture); ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>()); VS::get_singleton()->instance_set_base(mesh_instance, RID()); @@ -843,16 +850,17 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path, c font->draw(canvas_item, pos, sampled_text); - VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture - preview_done = false; + VS::get_singleton()->viewport_set_update_mode(viewport, VS::VIEWPORT_UPDATE_ONCE); //once used for capture VS::get_singleton()->request_frame_drawn_callback(const_cast<EditorFontPreviewPlugin *>(this), "_preview_done", Variant()); while (!preview_done) { OS::get_singleton()->delay_usec(10); } - Ref<Image> img = VS::get_singleton()->VS::get_singleton()->texture_get_data(viewport_texture); + VS::get_singleton()->canvas_item_clear(canvas_item); + + Ref<Image> img = VS::get_singleton()->texture_get_data(viewport_texture); ERR_FAIL_COND_V(img.is_null(), Ref<ImageTexture>()); img->convert(Image::FORMAT_RGBA8); @@ -876,7 +884,11 @@ Ref<Texture> EditorFontPreviewPlugin::generate_from_path(const String &p_path, c Ref<Texture> EditorFontPreviewPlugin::generate(const RES &p_from, const Size2 p_size) const { - return generate_from_path(p_from->get_path(), p_size); + String path = p_from->get_path(); + if (!FileAccess::exists(path)) { + return Ref<Texture>(); + } + return generate_from_path(path, p_size); } EditorFontPreviewPlugin::EditorFontPreviewPlugin() { diff --git a/editor/plugins/editor_preview_plugins.h b/editor/plugins/editor_preview_plugins.h index ed2c003a0a..16b1f3082b 100644 --- a/editor/plugins/editor_preview_plugins.h +++ b/editor/plugins/editor_preview_plugins.h @@ -39,7 +39,7 @@ class EditorTexturePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorTexturePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorTexturePreviewPlugin(); @@ -49,7 +49,7 @@ class EditorImagePreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorImagePreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorImagePreviewPlugin(); @@ -59,7 +59,7 @@ class EditorBitmapPreviewPlugin : public EditorResourcePreviewGenerator { GDCLASS(EditorBitmapPreviewPlugin, EditorResourcePreviewGenerator) public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorBitmapPreviewPlugin(); @@ -98,7 +98,7 @@ protected: public: virtual bool handles(const String &p_type) const; - virtual bool should_generate_small_preview() const; + virtual bool generate_small_preview_automatically() const; virtual Ref<Texture> generate(const RES &p_from, const Size2 p_size) const; EditorMaterialPreviewPlugin(); diff --git a/editor/plugins/gradient_editor_plugin.cpp b/editor/plugins/gradient_editor_plugin.cpp index 62a9ce719b..e85c475ad7 100644 --- a/editor/plugins/gradient_editor_plugin.cpp +++ b/editor/plugins/gradient_editor_plugin.cpp @@ -51,7 +51,7 @@ void GradientEditor::_ramp_changed() { editing = true; UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - undo_redo->create_action("Gradient Edited"); + 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_undo_method(gradient.ptr(), "set_offsets", gradient->get_offsets()); diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp index db0816064c..b3f3ccf501 100644 --- a/editor/plugins/item_list_editor_plugin.cpp +++ b/editor/plugins/item_list_editor_plugin.cpp @@ -353,6 +353,7 @@ void ItemListEditor::_bind_methods() { ItemListEditor::ItemListEditor() { selected_idx = -1; + item_list = NULL; toolbar_button = memnew(ToolButton); toolbar_button->set_text(TTR("Items")); diff --git a/editor/plugins/item_list_editor_plugin.h b/editor/plugins/item_list_editor_plugin.h index 679235e316..701632e576 100644 --- a/editor/plugins/item_list_editor_plugin.h +++ b/editor/plugins/item_list_editor_plugin.h @@ -157,7 +157,7 @@ public: virtual void set_item_enabled(int p_idx, int p_enabled) { pp->set_item_disabled(p_idx, !p_enabled); } virtual bool is_item_enabled(int p_idx) const { return !pp->is_item_disabled(p_idx); } - virtual void set_item_id(int p_idx, int p_id) { pp->set_item_id(p_idx, p_idx); } + virtual void set_item_id(int p_idx, int p_id) { pp->set_item_id(p_idx, p_id); } virtual int get_item_id(int p_idx) const { return pp->get_item_id(p_idx); } virtual void set_item_separator(int p_idx, bool p_separator) { pp->set_item_as_separator(p_idx, p_separator); } diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp index ce8cc77802..ebacccb03c 100644 --- a/editor/plugins/material_editor_plugin.cpp +++ b/editor/plugins/material_editor_plugin.cpp @@ -32,6 +32,210 @@ #include "scene/resources/particles_material.h" +void MaterialEditor::_notification(int p_what) { + + if (p_what == NOTIFICATION_PHYSICS_PROCESS) { + } + + if (p_what == NOTIFICATION_READY) { + + //get_scene()->connect("node_removed",this,"_node_removed"); + + if (first_enter) { + //it's in propertyeditor so.. could be moved around + + light_1_switch->set_normal_texture(get_icon("MaterialPreviewLight1", "EditorIcons")); + light_1_switch->set_pressed_texture(get_icon("MaterialPreviewLight1Off", "EditorIcons")); + light_2_switch->set_normal_texture(get_icon("MaterialPreviewLight2", "EditorIcons")); + light_2_switch->set_pressed_texture(get_icon("MaterialPreviewLight2Off", "EditorIcons")); + + sphere_switch->set_normal_texture(get_icon("MaterialPreviewSphereOff", "EditorIcons")); + sphere_switch->set_pressed_texture(get_icon("MaterialPreviewSphere", "EditorIcons")); + box_switch->set_normal_texture(get_icon("MaterialPreviewCubeOff", "EditorIcons")); + box_switch->set_pressed_texture(get_icon("MaterialPreviewCube", "EditorIcons")); + + first_enter = false; + } + } + + if (p_what == NOTIFICATION_DRAW) { + + Ref<Texture> checkerboard = get_icon("Checkerboard", "EditorIcons"); + Size2 size = get_size(); + + draw_texture_rect(checkerboard, Rect2(Point2(), size), true); + } +} + +void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_env) { + + material = p_material; + camera->set_environment(p_env); + if (!material.is_null()) { + sphere_instance->set_material_override(material); + box_instance->set_material_override(material); + } else { + + hide(); + } +} + +void MaterialEditor::_button_pressed(Node *p_button) { + + if (p_button == light_1_switch) { + light1->set_visible(!light_1_switch->is_pressed()); + } + + if (p_button == light_2_switch) { + light2->set_visible(!light_2_switch->is_pressed()); + } + + if (p_button == box_switch) { + box_instance->show(); + sphere_instance->hide(); + box_switch->set_pressed(true); + sphere_switch->set_pressed(false); + } + + if (p_button == sphere_switch) { + box_instance->hide(); + sphere_instance->show(); + box_switch->set_pressed(false); + sphere_switch->set_pressed(true); + } +} + +void MaterialEditor::_bind_methods() { + + ClassDB::bind_method(D_METHOD("_button_pressed"), &MaterialEditor::_button_pressed); +} + +MaterialEditor::MaterialEditor() { + + vc = memnew(ViewportContainer); + vc->set_stretch(true); + add_child(vc); + vc->set_anchors_and_margins_preset(PRESET_WIDE); + viewport = memnew(Viewport); + Ref<World> world; + world.instance(); + viewport->set_world(world); //use own world + vc->add_child(viewport); + viewport->set_disable_input(true); + viewport->set_transparent_background(true); + viewport->set_msaa(Viewport::MSAA_4X); + + camera = memnew(Camera); + camera->set_transform(Transform(Basis(), Vector3(0, 0, 3))); + camera->set_perspective(45, 0.1, 10); + camera->make_current(); + viewport->add_child(camera); + + light1 = memnew(DirectionalLight); + light1->set_transform(Transform().looking_at(Vector3(-1, -1, -1), Vector3(0, 1, 0))); + viewport->add_child(light1); + + light2 = memnew(DirectionalLight); + light2->set_transform(Transform().looking_at(Vector3(0, 1, 0), Vector3(0, 0, 1))); + light2->set_color(Color(0.7, 0.7, 0.7)); + viewport->add_child(light2); + + sphere_instance = memnew(MeshInstance); + viewport->add_child(sphere_instance); + + box_instance = memnew(MeshInstance); + viewport->add_child(box_instance); + + Transform box_xform; + box_xform.basis.rotate(Vector3(1, 0, 0), Math::deg2rad(25.0)); + box_xform.basis = box_xform.basis * Basis().rotated(Vector3(0, 1, 0), Math::deg2rad(-25.0)); + box_xform.basis.scale(Vector3(0.8, 0.8, 0.8)); + box_xform.origin.y = 0.2; + box_instance->set_transform(box_xform); + + sphere_mesh.instance(); + sphere_instance->set_mesh(sphere_mesh); + box_mesh.instance(); + box_instance->set_mesh(box_mesh); + box_instance->hide(); + + set_custom_minimum_size(Size2(1, 150) * EDSCALE); + + HBoxContainer *hb = memnew(HBoxContainer); + add_child(hb); + hb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 2); + + VBoxContainer *vb_shape = memnew(VBoxContainer); + hb->add_child(vb_shape); + + sphere_switch = memnew(TextureButton); + sphere_switch->set_toggle_mode(true); + sphere_switch->set_pressed(true); + vb_shape->add_child(sphere_switch); + sphere_switch->connect("pressed", this, "_button_pressed", varray(sphere_switch)); + + box_switch = memnew(TextureButton); + box_switch->set_toggle_mode(true); + box_switch->set_pressed(false); + vb_shape->add_child(box_switch); + box_switch->connect("pressed", this, "_button_pressed", varray(box_switch)); + + hb->add_spacer(); + + VBoxContainer *vb_light = memnew(VBoxContainer); + hb->add_child(vb_light); + + light_1_switch = memnew(TextureButton); + light_1_switch->set_toggle_mode(true); + vb_light->add_child(light_1_switch); + light_1_switch->connect("pressed", this, "_button_pressed", varray(light_1_switch)); + + light_2_switch = memnew(TextureButton); + light_2_switch->set_toggle_mode(true); + vb_light->add_child(light_2_switch); + light_2_switch->connect("pressed", this, "_button_pressed", varray(light_2_switch)); + + first_enter = true; +} + +/////////////////////// + +bool EditorInspectorPluginMaterial::can_handle(Object *p_object) { + + Material *material = Object::cast_to<Material>(p_object); + if (!material) + return false; + + return material->get_shader_mode() == Shader::MODE_SPATIAL; +} + +void EditorInspectorPluginMaterial::parse_begin(Object *p_object) { + + Material *material = Object::cast_to<Material>(p_object); + if (!material) { + return; + } + Ref<Material> m(material); + + MaterialEditor *editor = memnew(MaterialEditor); + editor->edit(m, env); + add_custom_control(editor); +} + +EditorInspectorPluginMaterial::EditorInspectorPluginMaterial() { + env.instance(); + Ref<ProceduralSky> proc_sky = memnew(ProceduralSky(true)); + env->set_sky(proc_sky); + env->set_background(Environment::BG_COLOR_SKY); +} + +MaterialEditorPlugin::MaterialEditorPlugin(EditorNode *p_node) { + + Ref<EditorInspectorPluginMaterial> plugin; + plugin.instance(); + add_inspector_plugin(plugin); +} + String SpatialMaterialConversionPlugin::converts_to() const { return "ShaderMaterial"; diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h index 39935d3e12..c3f14c27e5 100644 --- a/editor/plugins/material_editor_plugin.h +++ b/editor/plugins/material_editor_plugin.h @@ -32,6 +32,71 @@ #define MATERIAL_EDITOR_PLUGIN_H #include "editor/property_editor.h" +#include "scene/resources/primitive_meshes.h" + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "scene/3d/camera.h" +#include "scene/3d/light.h" +#include "scene/3d/mesh_instance.h" +#include "scene/resources/material.h" + +class MaterialEditor : public Control { + + GDCLASS(MaterialEditor, Control); + + ViewportContainer *vc; + Viewport *viewport; + MeshInstance *sphere_instance; + MeshInstance *box_instance; + DirectionalLight *light1; + DirectionalLight *light2; + Camera *camera; + + Ref<SphereMesh> sphere_mesh; + Ref<CubeMesh> box_mesh; + + TextureButton *sphere_switch; + TextureButton *box_switch; + + TextureButton *light_1_switch; + TextureButton *light_2_switch; + + Ref<Material> material; + + void _button_pressed(Node *p_button); + bool first_enter; + +protected: + void _notification(int p_what); + + static void _bind_methods(); + +public: + void edit(Ref<Material> p_material, const Ref<Environment> &p_env); + MaterialEditor(); +}; + +class EditorInspectorPluginMaterial : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginMaterial, EditorInspectorPlugin) + Ref<Environment> env; + +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); + + EditorInspectorPluginMaterial(); +}; + +class MaterialEditorPlugin : public EditorPlugin { + + GDCLASS(MaterialEditorPlugin, EditorPlugin); + +public: + virtual String get_name() const { return "Material"; } + + MaterialEditorPlugin(EditorNode *p_node); +}; class SpatialMaterialConversionPlugin : public EditorResourceConversionPlugin { GDCLASS(SpatialMaterialConversionPlugin, EditorResourceConversionPlugin) diff --git a/editor/plugins/mesh_editor_plugin.cpp b/editor/plugins/mesh_editor_plugin.cpp index fcf515e3fc..6203035e25 100644 --- a/editor/plugins/mesh_editor_plugin.cpp +++ b/editor/plugins/mesh_editor_plugin.cpp @@ -80,26 +80,21 @@ void MeshEditor::edit(Ref<Mesh> p_mesh) { mesh = p_mesh; mesh_instance->set_mesh(mesh); - if (mesh.is_null()) { - - hide(); - } else { - rot_x = 0; - rot_y = 0; - _update_rotation(); - - AABB aabb = mesh->get_aabb(); - Vector3 ofs = aabb.position + aabb.size * 0.5; - float m = aabb.get_longest_axis_size(); - if (m != 0) { - m = 1.0 / m; - m *= 0.5; - Transform xform; - xform.basis.scale(Vector3(m, m, m)); - xform.origin = -xform.basis.xform(ofs); //-ofs*m; - //xform.origin.z -= aabb.get_longest_axis_size() * 2; - mesh_instance->set_transform(xform); - } + rot_x = Math::deg2rad(-15.0); + rot_y = Math::deg2rad(30.0); + _update_rotation(); + + AABB aabb = mesh->get_aabb(); + Vector3 ofs = aabb.position + aabb.size * 0.5; + float m = aabb.get_longest_axis_size(); + if (m != 0) { + m = 1.0 / m; + m *= 0.5; + Transform xform; + xform.basis.scale(Vector3(m, m, m)); + xform.origin = -xform.basis.xform(ofs); //-ofs*m; + //xform.origin.z -= aabb.get_longest_axis_size() * 2; + mesh_instance->set_transform(xform); } } @@ -128,8 +123,8 @@ MeshEditor::MeshEditor() { viewport->set_world(world); //use own world add_child(viewport); viewport->set_disable_input(true); + viewport->set_msaa(Viewport::MSAA_2X); set_stretch(true); - camera = memnew(Camera); camera->set_transform(Transform(Basis(), Vector3(0, 0, 1.1))); camera->set_perspective(45, 0.1, 10); @@ -176,39 +171,29 @@ MeshEditor::MeshEditor() { rot_y = 0; } -void MeshEditorPlugin::edit(Object *p_object) { - - Mesh *s = Object::cast_to<Mesh>(p_object); - if (!s) - return; - - mesh_editor->edit(Ref<Mesh>(s)); -} +/////////////////////// -bool MeshEditorPlugin::handles(Object *p_object) const { +bool EditorInspectorPluginMesh::can_handle(Object *p_object) { - return p_object->is_class("Mesh"); + return Object::cast_to<Mesh>(p_object) != NULL; } -void MeshEditorPlugin::make_visible(bool p_visible) { +void EditorInspectorPluginMesh::parse_begin(Object *p_object) { - if (p_visible) { - mesh_editor->show(); - //mesh_editor->set_process(true); - } else { - - mesh_editor->hide(); - //mesh_editor->set_process(false); + Mesh *mesh = Object::cast_to<Mesh>(p_object); + if (!mesh) { + return; } + Ref<Mesh> m(mesh); + + MeshEditor *editor = memnew(MeshEditor); + editor->edit(m); + add_custom_control(editor); } MeshEditorPlugin::MeshEditorPlugin(EditorNode *p_node) { - editor = p_node; - mesh_editor = memnew(MeshEditor); - add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM, mesh_editor); - mesh_editor->hide(); -} - -MeshEditorPlugin::~MeshEditorPlugin() { + Ref<EditorInspectorPluginMesh> plugin; + plugin.instance(); + add_inspector_plugin(plugin); } diff --git a/editor/plugins/mesh_editor_plugin.h b/editor/plugins/mesh_editor_plugin.h index 0275f45be9..8ada2dac90 100644 --- a/editor/plugins/mesh_editor_plugin.h +++ b/editor/plugins/mesh_editor_plugin.h @@ -72,22 +72,21 @@ public: MeshEditor(); }; +class EditorInspectorPluginMesh : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginMesh, EditorInspectorPlugin) +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); +}; + class MeshEditorPlugin : public EditorPlugin { GDCLASS(MeshEditorPlugin, EditorPlugin); - MeshEditor *mesh_editor; - EditorNode *editor; - public: virtual String get_name() const { return "Mesh"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); MeshEditorPlugin(EditorNode *p_node); - ~MeshEditorPlugin(); }; #endif diff --git a/editor/plugins/mesh_instance_editor_plugin.cpp b/editor/plugins/mesh_instance_editor_plugin.cpp index 40ec03bc96..635b934333 100644 --- a/editor/plugins/mesh_instance_editor_plugin.cpp +++ b/editor/plugins/mesh_instance_editor_plugin.cpp @@ -95,10 +95,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { return; } - if (trimesh_shape) - ur->create_action(TTR("Create Static Trimesh Body")); - else - ur->create_action(TTR("Create Static Convex Body")); + ur->create_action(TTR("Create Static Trimesh Body")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { @@ -132,8 +129,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; - case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: - case MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE: { + case MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE: { if (node == get_tree()->get_edited_scene_root()) { err_dialog->set_text(TTR("This doesn't work on scene root!")); @@ -141,9 +137,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { return; } - bool trimesh_shape = (p_option == MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); - - Ref<Shape> shape = trimesh_shape ? mesh->create_trimesh_shape() : mesh->create_convex_shape(); + Ref<Shape> shape = mesh->create_trimesh_shape(); if (shape.is_null()) return; @@ -154,10 +148,7 @@ void MeshInstanceEditor::_menu_option(int p_option) { UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - if (trimesh_shape) - ur->create_action(TTR("Create Trimesh Shape")); - else - ur->create_action(TTR("Create Convex Shape")); + 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(), "move_child", cshape, node->get_index() + 1); @@ -165,6 +156,40 @@ void MeshInstanceEditor::_menu_option(int p_option) { ur->add_do_reference(cshape); ur->add_undo_method(node->get_parent(), "remove_child", cshape); ur->commit_action(); + } break; + case MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE: { + + if (node == get_tree()->get_edited_scene_root()) { + err_dialog->set_text(TTR("This doesn't work on scene root!")); + err_dialog->popup_centered_minsize(); + return; + } + + Vector<Ref<Shape> > shapes = mesh->convex_decompose(); + + if (!shapes.size()) { + err_dialog->set_text(TTR("Failed creating shapes!")); + err_dialog->popup_centered_minsize(); + return; + } + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + + ur->create_action(TTR("Create Convex Shape(s)")); + + for (int i = 0; i < shapes.size(); i++) { + + CollisionShape *cshape = memnew(CollisionShape); + cshape->set_shape(shapes[i]); + + Node *owner = node->get_owner(); + + ur->add_do_method(node->get_parent(), "add_child", cshape); + 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); + ur->add_undo_method(node->get_parent(), "remove_child", cshape); + } + ur->commit_action(); } break; @@ -198,14 +223,14 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; case MENU_OPTION_CREATE_UV2: { - Ref<ArrayMesh> mesh = node->get_mesh(); - if (!mesh.is_valid()) { + Ref<ArrayMesh> mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { err_dialog->set_text(TTR("Contained Mesh is not of type ArrayMesh.")); err_dialog->popup_centered_minsize(); return; } - Error err = mesh->lightmap_unwrap(node->get_global_transform()); + Error err = mesh2->lightmap_unwrap(node->get_global_transform()); if (err != OK) { err_dialog->set_text(TTR("UV Unwrap failed, mesh may not be manifold?")); err_dialog->popup_centered_minsize(); @@ -214,8 +239,8 @@ void MeshInstanceEditor::_menu_option(int p_option) { } break; case MENU_OPTION_DEBUG_UV1: { - Ref<Mesh> mesh = node->get_mesh(); - if (!mesh.is_valid()) { + Ref<Mesh> mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { err_dialog->set_text(TTR("No mesh to debug.")); err_dialog->popup_centered_minsize(); return; @@ -223,8 +248,8 @@ void MeshInstanceEditor::_menu_option(int p_option) { _create_uv_lines(0); } break; case MENU_OPTION_DEBUG_UV2: { - Ref<Mesh> mesh = node->get_mesh(); - if (!mesh.is_valid()) { + Ref<Mesh> mesh2 = node->get_mesh(); + if (!mesh2.is_valid()) { err_dialog->set_text(TTR("No mesh to debug.")); err_dialog->popup_centered_minsize(); return; @@ -387,16 +412,16 @@ void MeshInstanceEditor::_bind_methods() { MeshInstanceEditor::MeshInstanceEditor() { options = memnew(MenuButton); + options->set_switch_on_hover(true); SpatialEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text(TTR("Mesh")); options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("MeshInstance", "EditorIcons")); options->get_popup()->add_item(TTR("Create Trimesh Static Body"), MENU_OPTION_CREATE_STATIC_TRIMESH_BODY); - options->get_popup()->add_item(TTR("Create Convex Static Body"), MENU_OPTION_CREATE_STATIC_CONVEX_BODY); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Trimesh Collision Sibling"), MENU_OPTION_CREATE_TRIMESH_COLLISION_SHAPE); - options->get_popup()->add_item(TTR("Create Convex Collision Sibling"), MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE); + options->get_popup()->add_item(TTR("Create Convex Collision Sibling(s)"), MENU_OPTION_CREATE_CONVEX_COLLISION_SHAPE); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Create Navigation Mesh"), MENU_OPTION_CREATE_NAVMESH); options->get_popup()->add_separator(); diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp index aedac7b45d..18586b2fe5 100644 --- a/editor/plugins/mesh_library_editor_plugin.cpp +++ b/editor/plugins/mesh_library_editor_plugin.cpp @@ -61,7 +61,8 @@ void MeshLibraryEditor::_menu_confirm() { _import_scene_cbk(existing); } break; - default: {}; + default: { + }; } } @@ -127,7 +128,7 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, continue; MeshLibrary::ShapeData shape_data; shape_data.shape = collision; - shape_data.local_transform = sb->shape_owner_get_transform(E->get()); + shape_data.local_transform = sb->get_transform() * sb->shape_owner_get_transform(E->get()); collisions.push_back(shape_data); } } @@ -136,17 +137,20 @@ void MeshLibraryEditor::_import_scene(Node *p_scene, Ref<MeshLibrary> p_library, p_library->set_item_shapes(id, collisions); Ref<NavigationMesh> navmesh; + Transform navmesh_transform; for (int j = 0; j < mi->get_child_count(); j++) { Node *child2 = mi->get_child(j); if (!Object::cast_to<NavigationMeshInstance>(child2)) continue; NavigationMeshInstance *sb = Object::cast_to<NavigationMeshInstance>(child2); navmesh = sb->get_navigation_mesh(); + navmesh_transform = sb->get_transform(); if (!navmesh.is_null()) break; } if (!navmesh.is_null()) { p_library->set_item_navmesh(id, navmesh); + p_library->set_item_navmesh_transform(id, navmesh_transform); } } diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp index 8ff6080443..fc0a425bfc 100644 --- a/editor/plugins/multimesh_editor_plugin.cpp +++ b/editor/plugins/multimesh_editor_plugin.cpp @@ -293,6 +293,7 @@ void MultiMeshEditor::_bind_methods() { MultiMeshEditor::MultiMeshEditor() { options = memnew(MenuButton); + options->set_switch_on_hover(true); SpatialEditor::get_singleton()->add_control_to_menu_panel(options); options->set_text("MultiMesh"); diff --git a/editor/plugins/particles_2d_editor_plugin.cpp b/editor/plugins/particles_2d_editor_plugin.cpp index a944674cbd..50bdf4512b 100644 --- a/editor/plugins/particles_2d_editor_plugin.cpp +++ b/editor/plugins/particles_2d_editor_plugin.cpp @@ -91,6 +91,7 @@ void Particles2DEditorPlugin::_menu_callback(int p_idx) { cpu_particles->set_transform(particles->get_transform()); cpu_particles->set_visible(particles->is_visible()); cpu_particles->set_pause_mode(particles->get_pause_mode()); + cpu_particles->set_z_index(particles->get_z_index()); EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(particles, cpu_particles, false); @@ -132,7 +133,10 @@ void Particles2DEditorPlugin::_generate_visibility_rect() { particles->set_emitting(false); } - particles->set_visibility_rect(rect); + undo_redo->create_action(TTR("Generate Visibility Rect")); + undo_redo->add_do_method(particles, "set_visibility_rect", rect); + undo_redo->add_undo_method(particles, "set_visibility_rect", particles->get_visibility_rect()); + undo_redo->commit_action(); } void Particles2DEditorPlugin::_generate_emission_mask() { @@ -371,6 +375,7 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { menu->get_popup()->add_separator(); menu->get_popup()->add_item(TTR("Convert to CPUParticles"), MENU_OPTION_CONVERT_TO_CPU_PARTICLES); menu->set_text(TTR("Particles")); + menu->set_switch_on_hover(true); toolbar->add_child(menu); file = memnew(EditorFileDialog); @@ -404,7 +409,7 @@ Particles2DEditorPlugin::Particles2DEditorPlugin(EditorNode *p_node) { generate_visibility_rect->connect("confirmed", this, "_generate_visibility_rect"); emission_mask = memnew(ConfirmationDialog); - emission_mask->set_title(TTR("Generate Visibility Rect")); + emission_mask->set_title(TTR("Load Emission Mask")); VBoxContainer *emvb = memnew(VBoxContainer); emission_mask->add_child(emvb); emission_mask_mode = memnew(OptionButton); diff --git a/editor/plugins/particles_editor_plugin.cpp b/editor/plugins/particles_editor_plugin.cpp index 95828064ac..09180edf2a 100644 --- a/editor/plugins/particles_editor_plugin.cpp +++ b/editor/plugins/particles_editor_plugin.cpp @@ -353,7 +353,11 @@ void ParticlesEditor::_generate_aabb() { node->set_emitting(false); } - node->set_visibility_aabb(rect); + UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); + ur->create_action(TTR("Generate Visibility AABB")); + ur->add_do_method(node, "set_visibility_aabb", rect); + ur->add_undo_method(node, "set_visibility_aabb", node->get_visibility_aabb()); + ur->commit_action(); } void ParticlesEditor::edit(Particles *p_particles) { @@ -450,6 +454,7 @@ ParticlesEditor::ParticlesEditor() { particles_editor_hb = memnew(HBoxContainer); SpatialEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb); options = memnew(MenuButton); + options->set_switch_on_hover(true); particles_editor_hb->add_child(options); particles_editor_hb->hide(); diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index 354475d268..a10eddb131 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -180,11 +180,11 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { // Check for segment split. if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT && mode == MODE_EDIT && on_edge == true) { - Vector2 gpoint = mb->get_position(); + Vector2 gpoint2 = mb->get_position(); Ref<Curve2D> curve = node->get_curve(); int insertion_point = -1; - float mbLength = curve->get_closest_offset(xform.affine_inverse().xform(gpoint)); + float mbLength = curve->get_closest_offset(xform.affine_inverse().xform(gpoint2)); int len = curve->get_point_count(); for (int i = 0; i < len - 1; i++) { float compareLength = curve->get_closest_offset(curve->get_point_position(i + 1)); @@ -195,7 +195,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { insertion_point = curve->get_point_count() - 2; undo_redo->create_action(TTR("Split Curve")); - undo_redo->add_do_method(curve.ptr(), "add_point", xform.affine_inverse().xform(gpoint), Vector2(0, 0), Vector2(0, 0), insertion_point + 1); + undo_redo->add_do_method(curve.ptr(), "add_point", xform.affine_inverse().xform(gpoint2), Vector2(0, 0), Vector2(0, 0), insertion_point + 1); undo_redo->add_undo_method(curve.ptr(), "remove_point", insertion_point + 1); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); @@ -204,7 +204,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { action = ACTION_MOVING_POINT; action_point = insertion_point + 1; moving_from = curve->get_point_position(action_point); - moving_screen_from = gpoint; + moving_screen_from = gpoint2; canvas_item_editor->update_viewport(); diff --git a/editor/plugins/path_editor_plugin.cpp b/editor/plugins/path_editor_plugin.cpp index 6efa76ef80..88dc258c5f 100644 --- a/editor/plugins/path_editor_plugin.cpp +++ b/editor/plugins/path_editor_plugin.cpp @@ -638,6 +638,10 @@ String PathSpatialGizmoPlugin::get_name() const { return "Path"; } +int PathSpatialGizmoPlugin::get_priority() const { + return -1; +} + PathSpatialGizmoPlugin::PathSpatialGizmoPlugin() { Color path_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/path", Color(0.5, 0.5, 1.0, 0.8)); diff --git a/editor/plugins/path_editor_plugin.h b/editor/plugins/path_editor_plugin.h index ce3d3799d4..5482d09377 100644 --- a/editor/plugins/path_editor_plugin.h +++ b/editor/plugins/path_editor_plugin.h @@ -62,6 +62,7 @@ protected: public: String get_name() const; + int get_priority() const; PathSpatialGizmoPlugin(); }; diff --git a/editor/plugins/physical_bone_plugin.cpp b/editor/plugins/physical_bone_plugin.cpp index 9ea37aceb9..96681a105f 100644 --- a/editor/plugins/physical_bone_plugin.cpp +++ b/editor/plugins/physical_bone_plugin.cpp @@ -61,7 +61,7 @@ PhysicalBoneEditor::PhysicalBoneEditor(EditorNode *p_editor) : button_transform_joint = memnew(ToolButton); spatial_editor_hb->add_child(button_transform_joint); - button_transform_joint->set_text(TTR("Move joint")); + button_transform_joint->set_text(TTR("Move Joint")); button_transform_joint->set_icon(SpatialEditor::get_singleton()->get_icon("PhysicalBone", "EditorIcons")); button_transform_joint->set_toggle_mode(true); button_transform_joint->connect("toggled", this, "_on_toggle_button_transform_joint"); diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 1d7b4ffa41..712b1a0ae4 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -45,6 +45,7 @@ Node2D *Polygon2DEditor::_get_node() const { void Polygon2DEditor::_set_node(Node *p_polygon) { node = Object::cast_to<Polygon2D>(p_polygon); + _update_polygon_editing_state(); } Vector2 Polygon2DEditor::_get_offset(int p_idx) const { @@ -53,6 +54,7 @@ Vector2 Polygon2DEditor::_get_offset(int p_idx) const { } int Polygon2DEditor::_get_polygon_count() const { + if (node->get_internal_vertex_count() > 0) { return 0; //do not edit if internal vertices exist } else { @@ -365,6 +367,8 @@ void Polygon2DEditor::_cancel_editing() { node->set_vertex_colors(uv_create_colors_prev); node->call("_set_bones", uv_create_bones_prev); node->set_polygons(polygons_prev); + + _update_polygon_editing_state(); } else if (uv_drag) { uv_drag = false; if (uv_edit_mode[0]->is_pressed()) { // Edit UV. @@ -377,9 +381,20 @@ void Polygon2DEditor::_cancel_editing() { polygon_create.clear(); } +void Polygon2DEditor::_update_polygon_editing_state() { + + if (!_get_node()) + return; + + if (node->get_internal_vertex_count() > 0) + disable_polygon_editing(true, TTR("Polygon 2D has internal vertices, so it can no longer be edited in the viewport.")); + else + disable_polygon_editing(false, String()); +} + void Polygon2DEditor::_commit_action() { - // Makes that undo/redoing actions made outside of the UV editor still affects its polygon. + // Makes that undo/redoing actions made outside of the UV editor still affect its polygon. undo_redo->add_do_method(uv_edit_draw, "update"); undo_redo->add_undo_method(uv_edit_draw, "update"); undo_redo->add_do_method(CanvasItemEditor::get_singleton(), "update_viewport"); @@ -480,6 +495,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { uv_create_colors_prev = node->get_vertex_colors(); uv_create_bones_prev = node->call("_get_bones"); polygons_prev = node->get_polygons(); + disable_polygon_editing(false, String()); node->set_polygon(points_prev); node->set_uv(points_prev); node->set_internal_vertex_count(0); @@ -501,6 +517,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { undo_redo->add_undo_method(node, "set_vertex_colors", uv_create_colors_prev); undo_redo->add_do_method(node, "clear_bones"); undo_redo->add_undo_method(node, "_set_bones", uv_create_bones_prev); + undo_redo->add_do_method(this, "_update_polygon_editing_state"); + undo_redo->add_undo_method(this, "_update_polygon_editing_state"); undo_redo->add_do_method(uv_edit_draw, "update"); undo_redo->add_undo_method(uv_edit_draw, "update"); undo_redo->commit_action(); @@ -552,7 +570,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } undo_redo->add_do_method(node, "set_internal_vertex_count", internal_vertices + 1); undo_redo->add_undo_method(node, "set_internal_vertex_count", internal_vertices); - + undo_redo->add_do_method(this, "_update_polygon_editing_state"); + undo_redo->add_undo_method(this, "_update_polygon_editing_state"); undo_redo->add_do_method(uv_edit_draw, "update"); undo_redo->add_undo_method(uv_edit_draw, "update"); undo_redo->commit_action(); @@ -606,7 +625,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { } undo_redo->add_do_method(node, "set_internal_vertex_count", internal_vertices - 1); undo_redo->add_undo_method(node, "set_internal_vertex_count", internal_vertices); - + undo_redo->add_do_method(this, "_update_polygon_editing_state"); + undo_redo->add_undo_method(this, "_update_polygon_editing_state"); undo_redo->add_do_method(uv_edit_draw, "update"); undo_redo->add_undo_method(uv_edit_draw, "update"); undo_redo->commit_action(); @@ -885,7 +905,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) { bone_paint_pos = Vector2(mm->get_position().x, mm->get_position().y); } break; - default: {} + default: { + } } if (bone_painting) { @@ -1028,6 +1049,9 @@ void Polygon2DEditor::_uv_draw() { Ref<Texture> internal_handle = get_icon("EditorInternalHandle", "EditorIcons"); Color poly_line_color = Color(0.9, 0.5, 0.5); + if (polygons.size() || polygon_create.size()) { + poly_line_color.a *= 0.25; + } Color polygon_line_color = Color(0.5, 0.5, 0.9); Vector<Color> polygon_fill_color; { @@ -1036,19 +1060,42 @@ void Polygon2DEditor::_uv_draw() { polygon_fill_color.push_back(pf); } Color prev_color = Color(0.5, 0.5, 0.5); - Rect2 rect(Point2(), mtx.basis_xform(base_tex->get_size())); - rect.expand_to(mtx.basis_xform(uv_edit_draw->get_size())); + Rect2 rect; int uv_draw_max = uvs.size(); + uv_draw_max -= node->get_internal_vertex_count(); + if (uv_draw_max < 0) { + uv_draw_max = 0; + } + + for (int i = 0; i < uvs.size(); i++) { + + int next = uv_draw_max > 0 ? (i + 1) % uv_draw_max : 0; + + if (i < uv_draw_max && uv_drag && uv_move_current == UV_MODE_EDIT_POINT && EDITOR_DEF("editors/poly_editor/show_previous_outline", true)) { + uv_edit_draw->draw_line(mtx.xform(points_prev[i]), mtx.xform(points_prev[next]), prev_color, 2 * EDSCALE); + } + + Vector2 next_point = uvs[next]; + if (uv_create && i == uvs.size() - 1) { + next_point = uv_create_to; + } + if (i < uv_draw_max /*&& polygons.size() == 0 && polygon_create.size() == 0*/) { //if using or creating polygons, do not show outline (will show polygons instead) + uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), poly_line_color, 2 * EDSCALE); + } + + rect.expand_to(mtx.basis_xform(uvs[i])); + } + for (int i = 0; i < polygons.size(); i++) { PoolVector<int> points = polygons[i]; Vector<Vector2> polypoints; - for (int i = 0; i < points.size(); i++) { - int next = (i + 1) % points.size(); + for (int j = 0; j < points.size(); j++) { + int next = (j + 1) % points.size(); - int idx = points[i]; + int idx = points[j]; int idx_next = points[next]; if (idx < 0 || idx >= uvs.size()) continue; @@ -1063,27 +1110,8 @@ void Polygon2DEditor::_uv_draw() { } } - uv_draw_max -= node->get_internal_vertex_count(); - if (uv_draw_max < 0) { - uv_draw_max = 0; - } - for (int i = 0; i < uvs.size(); i++) { - int next = uv_draw_max > 0 ? (i + 1) % uv_draw_max : 0; - - if (i < uv_draw_max && uv_drag && uv_move_current == UV_MODE_EDIT_POINT && EDITOR_DEF("editors/poly_editor/show_previous_outline", true)) { - uv_edit_draw->draw_line(mtx.xform(points_prev[i]), mtx.xform(points_prev[next]), prev_color, 2 * EDSCALE); - } - - Vector2 next_point = uvs[next]; - if (uv_create && i == uvs.size() - 1) { - next_point = uv_create_to; - } - if (i < uv_draw_max && polygons.size() == 0 && polygon_create.size() == 0) { //if using or creating polygons, do not show outline (will show polygons instead) - uv_edit_draw->draw_line(mtx.xform(uvs[i]), mtx.xform(next_point), poly_line_color, 2 * EDSCALE); - } - if (weight_r.ptr()) { Vector2 draw_pos = mtx.xform(uvs[i]); float weight = weight_r[i]; @@ -1095,14 +1123,13 @@ void Polygon2DEditor::_uv_draw() { uv_edit_draw->draw_texture(internal_handle, mtx.xform(uvs[i]) - internal_handle->get_size() * 0.5); } } - rect.expand_to(mtx.basis_xform(uvs[i])); } if (polygon_create.size()) { for (int i = 0; i < polygon_create.size(); i++) { Vector2 from = uvs[polygon_create[i]]; Vector2 to = (i + 1) < polygon_create.size() ? uvs[polygon_create[i + 1]] : uv_create_to; - uv_edit_draw->draw_line(mtx.xform(from), mtx.xform(to), poly_line_color, 2); + uv_edit_draw->draw_line(mtx.xform(from), mtx.xform(to), polygon_line_color, 2); } } @@ -1176,7 +1203,8 @@ void Polygon2DEditor::_uv_draw() { uv_edit_draw->draw_circle(bone_paint_pos, bone_paint_radius->get_value() * EDSCALE, Color(1, 1, 1, 0.1)); } - rect = rect.grow(200); + rect.position -= uv_edit_draw->get_size(); + rect.size += uv_edit_draw->get_size() * 2.0; updating_uv_scroll = true; uv_hscroll->set_min(rect.position.x); uv_hscroll->set_max(rect.position.x + rect.size.x); @@ -1216,6 +1244,7 @@ void Polygon2DEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_uv_edit_popup_hide"), &Polygon2DEditor::_uv_edit_popup_hide); ClassDB::bind_method(D_METHOD("_sync_bones"), &Polygon2DEditor::_sync_bones); ClassDB::bind_method(D_METHOD("_update_bone_list"), &Polygon2DEditor::_update_bone_list); + ClassDB::bind_method(D_METHOD("_update_polygon_editing_state"), &Polygon2DEditor::_update_polygon_editing_state); ClassDB::bind_method(D_METHOD("_bone_paint_selected"), &Polygon2DEditor::_bone_paint_selected); } diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h index d1849dd09b..24ca2ea3f4 100644 --- a/editor/plugins/polygon_2d_editor_plugin.h +++ b/editor/plugins/polygon_2d_editor_plugin.h @@ -128,6 +128,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor { virtual void _menu_option(int p_option); void _cancel_editing(); + void _update_polygon_editing_state(); void _uv_scroll_changed(float); void _uv_input(const Ref<InputEvent> &p_input); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index 9e65d9de10..839c9483d7 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -312,6 +312,38 @@ void ScriptEditor::_goto_script_line(REF p_script, int p_line) { } } +void ScriptEditor::_set_execution(REF p_script, int p_line) { + Ref<Script> script = Object::cast_to<Script>(*p_script); + if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { + 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; + + if ((script != NULL && se->get_edited_resource() == p_script) || se->get_edited_resource()->get_path() == script->get_path()) { + se->set_executing_line(p_line); + } + } + } +} + +void ScriptEditor::_clear_execution(REF p_script) { + Ref<Script> script = Object::cast_to<Script>(*p_script); + if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { + 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; + + if ((script != NULL && se->get_edited_resource() == p_script) || se->get_edited_resource()->get_path() == script->get_path()) { + se->clear_executing_line(); + } + } + } +} + ScriptEditorBase *ScriptEditor::_get_current_editor() const { int selected = tab_container->get_current_tab(); @@ -571,6 +603,8 @@ void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) { idx = history[history_pos].control->get_index(); } tab_container->set_current_tab(idx); + } else { + _update_selected_editor_menu(); } _update_history_arrows(); @@ -849,8 +883,7 @@ void ScriptEditor::_file_dialog_action(String p_file) { } file->close(); memdelete(file); - - // fallthrough to open the file. + FALLTHROUGH; } case FILE_OPEN: { @@ -971,6 +1004,9 @@ void ScriptEditor::_menu_option(int p_option) { save_all_scripts(); } break; + case SEARCH_IN_FILES: { + _on_find_in_files_requested(""); + } break; case SEARCH_HELP: { help_search_dialog->popup_dialog(); @@ -979,6 +1015,10 @@ void ScriptEditor::_menu_option(int p_option) { OS::get_singleton()->shell_open("https://docs.godotengine.org/"); } break; + case REQUEST_DOCS: { + + OS::get_singleton()->shell_open("https://github.com/godotengine/godot-docs/issues/new"); + } break; case WINDOW_NEXT: { @@ -1308,6 +1348,7 @@ void ScriptEditor::_notification(int p_what) { EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed"); help_search->set_icon(get_icon("HelpSearch", "EditorIcons")); site_search->set_icon(get_icon("Instance", "EditorIcons")); + request_docs->set_icon(get_icon("Issue", "EditorIcons")); script_forward->set_icon(get_icon("Forward", "EditorIcons")); script_back->set_icon(get_icon("Back", "EditorIcons")); @@ -1517,7 +1558,15 @@ struct _ScriptEditorItemData { bool operator<(const _ScriptEditorItemData &id) const { - return category == id.category ? sort_key < id.sort_key : category < id.category; + if (category == id.category) { + if (sort_key == id.sort_key) { + return index < id.index; + } else { + return sort_key < id.sort_key; + } + } else { + return category < id.category; + } } }; @@ -1731,7 +1780,7 @@ void ScriptEditor::_update_script_names() { String name = eh->get_class(); Ref<Texture> icon = get_icon("Help", "EditorIcons"); - String tooltip = name + TTR(" Class Reference"); + String tooltip = vformat(TTR("%s Class Reference"), name); _ScriptEditorItemData sd; sd.icon = icon; @@ -1797,6 +1846,16 @@ void ScriptEditor::_update_script_names() { _update_script_colors(); } +void ScriptEditor::_update_script_connections() { + for (int i = 0; i < tab_container->get_child_count(); i++) { + ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(tab_container->get_child(i)); + if (!ste) { + continue; + } + ste->_update_connected_methods(); + } +} + Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) { if (r_error) { *r_error = ERR_FILE_CANT_OPEN; @@ -1951,8 +2010,9 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra if (is_visible_in_tree()) se->ensure_focus(); - if (p_line >= 0) + if (p_line > 0) { se->goto_line(p_line - 1); + } } return true; } @@ -2012,8 +2072,9 @@ bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_gra _test_script_times_on_disk(p_resource); _update_modified_scripts_for_external_editor(p_resource); - if (p_line >= 0) + if (p_line > 0) { se->goto_line(p_line - 1); + } notify_script_changed(p_resource); _add_recent_script(p_resource->get_path()); @@ -2197,6 +2258,7 @@ void ScriptEditor::_tree_changed() { waiting_update_names = true; call_deferred("_update_script_names"); + call_deferred("_update_script_connections"); } void ScriptEditor::_script_split_dragged(float) { @@ -2206,6 +2268,9 @@ void ScriptEditor::_script_split_dragged(float) { Variant ScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + if (tab_container->get_child_count() == 0) + return Variant(); + Node *cur_node = tab_container->get_child(tab_container->get_current_tab()); HBoxContainer *drag_preview = memnew(HBoxContainer); @@ -2473,22 +2538,39 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) { for (int i = 0; i < scripts.size(); i++) { String path = scripts[i]; + + Dictionary script_info = scripts[i]; + if (!script_info.empty()) { + path = script_info["path"]; + } + if (!FileAccess::exists(path)) continue; if (extensions.find(path.get_extension())) { Ref<Script> scr = ResourceLoader::load(path); - if (scr.is_valid()) { - edit(scr); + if (!scr.is_valid()) { + continue; + } + if (!edit(scr)) { + continue; + } + } else { + Error error; + Ref<TextFile> text_file = _load_text_file(path, &error); + if (error != OK || !text_file.is_valid()) { + continue; + } + if (!edit(text_file)) { continue; } } - Error error; - Ref<TextFile> text_file = _load_text_file(path, &error); - if (error == OK && text_file.is_valid()) { - edit(text_file); - continue; + if (!script_info.empty()) { + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_tab_count() - 1)); + if (se) { + se->set_edit_state(script_info["state"]); + } } } @@ -2528,7 +2610,11 @@ void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) { if (!path.is_resource_file()) continue; - scripts.push_back(path); + Dictionary script_info; + script_info["path"] = path; + script_info["state"] = se->get_edit_state(); + + scripts.push_back(script_info); } EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i)); @@ -2621,10 +2707,22 @@ 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_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->show(); } else { - script_search_menu->hide(); + + 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->show(); + } else { + script_search_menu->hide(); + } } } @@ -2837,6 +2935,8 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_res_saved_callback", &ScriptEditor::_res_saved_callback); ClassDB::bind_method("_goto_script_line", &ScriptEditor::_goto_script_line); ClassDB::bind_method("_goto_script_line2", &ScriptEditor::_goto_script_line2); + ClassDB::bind_method("_set_execution", &ScriptEditor::_set_execution); + ClassDB::bind_method("_clear_execution", &ScriptEditor::_clear_execution); ClassDB::bind_method("_help_search", &ScriptEditor::_help_search); ClassDB::bind_method("_save_history", &ScriptEditor::_save_history); ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path); @@ -2847,6 +2947,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts); ClassDB::bind_method("_editor_settings_changed", &ScriptEditor::_editor_settings_changed); ClassDB::bind_method("_update_script_names", &ScriptEditor::_update_script_names); + ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections); ClassDB::bind_method("_tree_changed", &ScriptEditor::_tree_changed); ClassDB::bind_method("_members_overview_selected", &ScriptEditor::_members_overview_selected); ClassDB::bind_method("_help_overview_selected", &ScriptEditor::_help_overview_selected); @@ -2874,6 +2975,7 @@ void ScriptEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw); ClassDB::bind_method(D_METHOD("drop_data_fw", "point", "data", "from"), &ScriptEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("goto_line", "line_number"), &ScriptEditor::_goto_script_line2); ClassDB::bind_method(D_METHOD("get_current_script"), &ScriptEditor::_get_current_script); ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts); ClassDB::bind_method(D_METHOD("open_script_create_dialog", "base_name", "base_path"), &ScriptEditor::open_script_create_dialog); @@ -2953,7 +3055,6 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { members_overview->set_custom_minimum_size(Size2(0, 90) * EDSCALE); //need to give a bit of limit to avoid it from disappearing members_overview->set_v_size_flags(SIZE_EXPAND_FILL); members_overview->set_allow_rmb_select(true); - members_overview->set_drag_forwarding(this); help_overview = memnew(ItemList); overview_vbox->add_child(help_overview); @@ -3032,10 +3133,7 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { script_search_menu->set_text(TTR("Search")); script_search_menu->set_switch_on_hover(true); script_search_menu->get_popup()->set_hide_on_window_lose_focus(true); - 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()->connect("id_pressed", this, "_menu_option"); - script_search_menu->hide(); debug_menu = memnew(MenuButton); menu_hb->add_child(debug_menu); @@ -3074,7 +3172,13 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { site_search->set_text(TTR("Online Docs")); site_search->connect("pressed", this, "_menu_option", varray(SEARCH_WEBSITE)); menu_hb->add_child(site_search); - site_search->set_tooltip(TTR("Open Godot online documentation")); + site_search->set_tooltip(TTR("Open Godot online documentation.")); + + request_docs = memnew(ToolButton); + request_docs->set_text(TTR("Request Docs")); + request_docs->connect("pressed", this, "_menu_option", varray(REQUEST_DOCS)); + menu_hb->add_child(request_docs); + request_docs->set_tooltip(TTR("Help improve the Godot documentation by giving feedback.")); help_search = memnew(ToolButton); help_search->set_text(TTR("Search Help")); @@ -3120,6 +3224,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debugger = memnew(ScriptEditorDebugger(editor)); debugger->connect("goto_script_line", this, "_goto_script_line"); + debugger->connect("set_execution", this, "_set_execution"); + debugger->connect("clear_execution", this, "_clear_execution"); debugger->connect("show_debugger", this, "_show_debugger"); disk_changed = memnew(ConfirmationDialog); @@ -3206,9 +3312,7 @@ void ScriptEditorPlugin::edit(Object *p_object) { } else { script_editor->edit(p_script); } - } - - if (Object::cast_to<TextFile>(p_object)) { + } else if (Object::cast_to<TextFile>(p_object)) { script_editor->edit(Object::cast_to<TextFile>(p_object)); } } diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index 11f4589f00..683fa881f8 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -96,6 +96,8 @@ public: virtual Variant get_edit_state() = 0; virtual void set_edit_state(const Variant &p_state) = 0; virtual void goto_line(int p_line, bool p_with_error = false) = 0; + virtual void set_executing_line(int p_line) = 0; + virtual void clear_executing_line() = 0; virtual void trim_trailing_whitespace() = 0; virtual void convert_indent_to_spaces() = 0; virtual void convert_indent_to_tabs() = 0; @@ -155,8 +157,10 @@ class ScriptEditor : public PanelContainer { DEBUG_SHOW, DEBUG_SHOW_KEEP_OPEN, DEBUG_WITH_EXTERNAL_EDITOR, + SEARCH_IN_FILES, SEARCH_HELP, SEARCH_WEBSITE, + REQUEST_DOCS, HELP_SEARCH_FIND, HELP_SEARCH_FIND_NEXT, WINDOW_MOVE_UP, @@ -200,6 +204,7 @@ class ScriptEditor : public PanelContainer { Button *help_search; Button *site_search; + Button *request_docs; EditorHelpSearch *help_search_dialog; ItemList *script_list; @@ -316,6 +321,8 @@ class ScriptEditor : public PanelContainer { 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 _show_debugger(bool p_show); void _update_window_menu(); @@ -331,6 +338,7 @@ class ScriptEditor : public PanelContainer { void _update_members_overview(); void _toggle_members_overview_alpha_sort(bool p_alphabetic_sort); void _update_script_names(); + void _update_script_connections(); bool _sort_list_on_update; void _members_overview_selected(int p_idx); diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index 60dc156782..f66ae0465f 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -35,6 +35,76 @@ #include "editor/editor_settings.h" #include "editor/script_editor_debugger.h" +void ConnectionInfoDialog::ok_pressed() { +} + +void ConnectionInfoDialog::popup_connections(String p_method, Vector<Node *> p_nodes) { + method->set_text(p_method); + + tree->clear(); + TreeItem *root = tree->create_item(); + + for (int i = 0; i < p_nodes.size(); i++) { + List<Connection> all_connections; + p_nodes[i]->get_signals_connected_to_this(&all_connections); + + for (List<Connection>::Element *E = all_connections.front(); E; E = E->next()) { + Connection connection = E->get(); + + if (connection.method != p_method) { + continue; + } + + TreeItem *node_item = tree->create_item(root); + + node_item->set_text(0, Object::cast_to<Node>(connection.source)->get_name()); + node_item->set_icon(0, EditorNode::get_singleton()->get_object_icon(connection.source, "Node")); + node_item->set_selectable(0, false); + node_item->set_editable(0, false); + + node_item->set_text(1, connection.signal); + node_item->set_icon(1, get_parent_control()->get_icon("Slot", "EditorIcons")); + node_item->set_selectable(1, false); + node_item->set_editable(1, false); + + node_item->set_text(2, Object::cast_to<Node>(connection.target)->get_name()); + node_item->set_icon(2, EditorNode::get_singleton()->get_object_icon(connection.target, "Node")); + node_item->set_selectable(2, false); + node_item->set_editable(2, false); + } + } + + popup_centered(Size2(400, 300) * EDSCALE); +} + +ConnectionInfoDialog::ConnectionInfoDialog() { + set_title(TTR("Connections to method:")); + + VBoxContainer *vbc = memnew(VBoxContainer); + vbc->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -8 * EDSCALE); + vbc->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -8 * EDSCALE); + add_child(vbc); + + method = memnew(Label); + method->set_align(Label::ALIGN_CENTER); + vbc->add_child(method); + + tree = memnew(Tree); + tree->set_columns(3); + tree->set_hide_root(true); + tree->set_column_titles_visible(true); + tree->set_column_title(0, TTR("Source")); + tree->set_column_title(1, TTR("Signal")); + tree->set_column_title(2, TTR("Target")); + vbc->add_child(tree); + tree->set_v_size_flags(SIZE_EXPAND_FILL); + tree->set_allow_rmb_select(true); +} + +//////////////////////////////////////////////////////////////////////////////// + Vector<String> ScriptTextEditor::get_functions() { String errortxt; @@ -81,6 +151,8 @@ void ScriptTextEditor::set_edited_resource(const RES &p_res) { emit_signal("name_changed"); code_editor->update_line_and_column(); + + _validate_script(); } void ScriptTextEditor::_update_member_keywords() { @@ -141,7 +213,9 @@ void ScriptTextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); + Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); @@ -172,7 +246,9 @@ void ScriptTextEditor::_load_theme_settings() { text_edit->add_color_override("number_color", number_color); text_edit->add_color_override("function_color", function_color); text_edit->add_color_override("member_variable_color", member_variable_color); + text_edit->add_color_override("bookmark_color", bookmark_color); text_edit->add_color_override("breakpoint_color", breakpoint_color); + text_edit->add_color_override("executing_line_color", executing_line_color); text_edit->add_color_override("mark_color", mark_color); text_edit->add_color_override("code_folding_color", code_folding_color); text_edit->add_color_override("search_result_color", search_result_color); @@ -273,18 +349,12 @@ void ScriptTextEditor::_set_theme_for_script() { } } -void ScriptTextEditor::_toggle_warning_pannel(const Ref<InputEvent> &p_event) { - Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - warnings_panel->set_visible(!warnings_panel->is_visible()); - } +void ScriptTextEditor::_show_warnings_panel(bool p_show) { + warnings_panel->set_visible(p_show); } -void ScriptTextEditor::_error_pressed(const Ref<InputEvent> &p_event) { - Ref<InputEventMouseButton> mb = p_event; - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - code_editor->goto_error(); - } +void ScriptTextEditor::_error_pressed() { + code_editor->goto_error(); } void ScriptTextEditor::_warning_clicked(Variant p_line) { @@ -292,7 +362,7 @@ void ScriptTextEditor::_warning_clicked(Variant p_line) { code_editor->get_text_edit()->cursor_set_line(p_line.operator int64_t()); } else if (p_line.get_type() == Variant::DICTIONARY) { Dictionary meta = p_line.operator Dictionary(); - code_editor->get_text_edit()->insert_at("#warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1); + code_editor->get_text_edit()->insert_at("# warning-ignore:" + meta["code"].operator String(), meta["line"].operator int64_t() - 1); _validate_script(); } } @@ -308,7 +378,6 @@ void ScriptTextEditor::reload_text() { int v = te->get_v_scroll(); te->set_text(script->get_source_code()); - te->clear_undo_history(); te->cursor_set_line(row); te->cursor_set_column(column); te->set_h_scroll(h); @@ -324,7 +393,6 @@ void ScriptTextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: _load_theme_settings(); - _change_syntax_highlighter(EditorSettings::get_singleton()->get_project_metadata("script_text_editor", "syntax_highlighter", 0)); break; } } @@ -369,6 +437,14 @@ Variant ScriptTextEditor::get_edit_state() { void ScriptTextEditor::set_edit_state(const Variant &p_state) { code_editor->set_edit_state(p_state); + + Dictionary state = p_state; + if (state.has("syntax_highlighter")) { + int idx = highlighter_menu->get_item_idx_from_text(state["syntax_highlighter"]); + if (idx >= 0) { + _change_syntax_highlighter(idx); + } + } } void ScriptTextEditor::_convert_case(CodeTextEditor::CaseStyle p_case) { @@ -406,6 +482,14 @@ void ScriptTextEditor::goto_line_selection(int p_line, int p_begin, int p_end) { code_editor->goto_line_selection(p_line, p_begin, p_end); } +void ScriptTextEditor::set_executing_line(int p_line) { + code_editor->set_executing_line(p_line); +} + +void ScriptTextEditor::clear_executing_line() { + code_editor->clear_executing_line(); +} + void ScriptTextEditor::ensure_focus() { code_editor->get_text_edit()->grab_focus(); @@ -467,9 +551,32 @@ void ScriptTextEditor::_validate_script() { functions.push_back(E->get()); } } + _update_connected_methods(); - code_editor->get_warning_count_label()->set_text(itos(warnings.size())); + code_editor->set_warning_nb(missing_connections.size() + warnings.size()); warnings_panel->clear(); + + // add missing connections + Node *base = get_tree()->get_edited_scene_root(); + if (base && missing_connections.size() > 0) { + warnings_panel->push_table(1); + for (List<Connection>::Element *E = missing_connections.front(); E; E = E->next()) { + Connection connection = E->get(); + + String base_path = base->get_name(); + String source_path = base == connection.source ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.source))); + String target_path = base == connection.target ? base_path : base_path + "/" + String(base->get_path_to(Object::cast_to<Node>(connection.target))); + + warnings_panel->push_cell(); + warnings_panel->push_color(warnings_panel->get_color("warning_color", "Editor")); + warnings_panel->add_text(vformat(TTR("Missing connected method '%s' for signal '%s' from node '%s' to node '%s'"), connection.method, connection.signal, source_path, target_path)); + warnings_panel->pop(); // Color + warnings_panel->pop(); // Cell + } + warnings_panel->pop(); // Table + } + + // add script warnings warnings_panel->push_table(3); for (List<ScriptLanguage::Warning>::Element *E = warnings.front(); E; E = E->next()) { ScriptLanguage::Warning w = E->get(); @@ -523,6 +630,27 @@ void ScriptTextEditor::_validate_script() { emit_signal("edited_script_changed"); } +static Vector<Node *> _find_all_node_for_script(Node *p_base, Node *p_current, const Ref<Script> &p_script) { + + Vector<Node *> nodes; + + if (p_current->get_owner() != p_base && p_base != p_current) { + return nodes; + } + + Ref<Script> c = p_current->get_script(); + if (c == p_script) { + nodes.push_back(p_current); + } + + for (int i = 0; i < p_current->get_child_count(); i++) { + Vector<Node *> found = _find_all_node_for_script(p_base, p_current->get_child(i), p_script); + nodes.append_array(found); + } + + return nodes; +} + static Node *_find_node_for_script(Node *p_base, Node *p_current, const Ref<Script> &p_script) { if (p_current->get_owner() != p_base && p_base != p_current) @@ -626,7 +754,9 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c } ScriptLanguage::LookupResult result; - if (p_symbol.is_resource_file()) { + if (ScriptServer::is_global_class(p_symbol)) { + EditorNode::get_singleton()->load_resource(ScriptServer::get_global_class_path(p_symbol)); + } else if (p_symbol.is_resource_file()) { List<String> scene_extensions; ResourceLoader::get_recognized_extensions_for_type("PackedScene", &scene_extensions); @@ -716,6 +846,47 @@ void ScriptTextEditor::_lookup_symbol(const String &p_symbol, int p_row, int p_c } } +void ScriptTextEditor::_update_connected_methods() { + TextEdit *text_edit = code_editor->get_text_edit(); + text_edit->clear_info_icons(); + missing_connections.clear(); + + Node *base = get_tree()->get_edited_scene_root(); + if (!base) { + return; + } + + Vector<Node *> nodes = _find_all_node_for_script(base, base, script); + for (int i = 0; i < nodes.size(); i++) { + List<Connection> connections; + nodes[i]->get_signals_connected_to_this(&connections); + + for (List<Connection>::Element *E = connections.front(); E; E = E->next()) { + Connection connection = E->get(); + if (!(connection.flags & CONNECT_PERSIST)) { + continue; + } + + int line = script->get_language()->find_function(connection.method, text_edit->get_text()); + if (line < 0) { + missing_connections.push_back(connection); + continue; + } + text_edit->set_line_info_icon(line - 1, get_parent_control()->get_icon("Slot", "EditorIcons"), connection.method); + } + } +} + +void ScriptTextEditor::_lookup_connections(int p_row, String p_method) { + Node *base = get_tree()->get_edited_scene_root(); + if (!base) { + return; + } + + Vector<Node *> nodes = _find_all_node_for_script(base, base, script); + connection_info_dialog->popup_connections(p_method, nodes); +} + void ScriptTextEditor::_edit_option(int p_op) { TextEdit *tx = code_editor->get_text_edit(); @@ -800,92 +971,7 @@ void ScriptTextEditor::_edit_option(int p_op) { } break; case EDIT_TOGGLE_COMMENT: { - Ref<Script> scr = script; - if (scr.is_null()) - return; - - String delimiter = "#"; - List<String> comment_delimiters; - scr->get_language()->get_comment_delimiters(&comment_delimiters); - - for (List<String>::Element *E = comment_delimiters.front(); E; E = E->next()) { - String script_delimiter = E->get(); - if (script_delimiter.find(" ") == -1) { - delimiter = script_delimiter; - break; - } - } - - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - - // End of selection ends on the first column of the last line, ignore it. - if (tx->get_selection_to_column() == 0) - end -= 1; - - int col_to = tx->get_selection_to_column(); - int cursor_pos = tx->cursor_get_column(); - - // Check if all lines in the selected block are commented - bool is_commented = true; - for (int i = begin; i <= end; i++) { - if (!tx->get_line(i).begins_with(delimiter)) { - is_commented = false; - break; - } - } - for (int i = begin; i <= end; i++) { - String line_text = tx->get_line(i); - - if (line_text.strip_edges().empty()) { - line_text = delimiter; - } else { - if (is_commented) { - line_text = line_text.substr(delimiter.length(), line_text.length()); - } else { - line_text = delimiter + line_text; - } - } - tx->set_line(i, line_text); - } - - // Adjust selection & cursor position. - int offset = is_commented ? -1 : 1; - int col_from = tx->get_selection_from_column() > 0 ? tx->get_selection_from_column() + offset : 0; - - if (is_commented && tx->cursor_get_column() == tx->get_line(tx->cursor_get_line()).length() + 1) - cursor_pos += 1; - - if (tx->get_selection_to_column() != 0 && col_to != tx->get_line(tx->get_selection_to_line()).length() + 1) - col_to += offset; - - if (tx->cursor_get_column() != 0) - cursor_pos += offset; - - tx->select(begin, col_from, tx->get_selection_to_line(), col_to); - tx->cursor_set_column(cursor_pos); - - } else { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - - int col = tx->cursor_get_column(); - if (line_text.begins_with(delimiter)) { - line_text = line_text.substr(delimiter.length(), line_text.length()); - col -= 1; - } else { - line_text = delimiter + line_text; - col += 1; - } - - tx->set_line(begin, line_text); - tx->cursor_set_column(col); - } - tx->end_complex_operation(); - tx->update(); - + _edit_option_toggle_inline_comment(); } break; case EDIT_COMPLETE: { @@ -981,6 +1067,22 @@ void ScriptTextEditor::_edit_option(int p_op) { goto_line_dialog->popup_find_line(tx); } break; + case BOOKMARK_TOGGLE: { + + code_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + code_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + code_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + code_editor->remove_all_bookmarks(); + } break; case DEBUG_TOGGLE_BREAKPOINT: { int line = tx->cursor_get_line(); @@ -999,7 +1101,7 @@ void ScriptTextEditor::_edit_option(int p_op) { tx->set_line_as_breakpoint(line, dobreak); ScriptEditor::get_singleton()->get_debugger()->set_breakpoint(script->get_path(), line + 1, dobreak); } - } + } break; case DEBUG_GOTO_NEXT_BREAKPOINT: { List<int> bpoints; @@ -1072,6 +1174,25 @@ void ScriptTextEditor::_edit_option(int p_op) { } } +void ScriptTextEditor::_edit_option_toggle_inline_comment() { + if (script.is_null()) + return; + + String delimiter = "#"; + List<String> comment_delimiters; + script->get_language()->get_comment_delimiters(&comment_delimiters); + + for (List<String>::Element *E = comment_delimiters.front(); E; E = E->next()) { + String script_delimiter = E->get(); + if (script_delimiter.find(" ") == -1) { + delimiter = script_delimiter; + break; + } + } + + code_editor->toggle_inline_comment(delimiter); +} + void ScriptTextEditor::add_syntax_highlighter(SyntaxHighlighter *p_highlighter) { highlighters[p_highlighter->get_name()] = p_highlighter; highlighter_menu->add_radio_check_item(p_highlighter->get_name()); @@ -1094,7 +1215,6 @@ void ScriptTextEditor::_change_syntax_highlighter(int p_idx) { } // highlighter_menu->set_item_checked(p_idx, true); set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); - EditorSettings::get_singleton()->set_project_metadata("script_text_editor", "syntax_highlighter", p_idx); } void ScriptTextEditor::_bind_methods() { @@ -1102,12 +1222,14 @@ void ScriptTextEditor::_bind_methods() { ClassDB::bind_method("_validate_script", &ScriptTextEditor::_validate_script); ClassDB::bind_method("_load_theme_settings", &ScriptTextEditor::_load_theme_settings); ClassDB::bind_method("_breakpoint_toggled", &ScriptTextEditor::_breakpoint_toggled); + ClassDB::bind_method("_lookup_connections", &ScriptTextEditor::_lookup_connections); + ClassDB::bind_method("_update_connected_methods", &ScriptTextEditor::_update_connected_methods); ClassDB::bind_method("_change_syntax_highlighter", &ScriptTextEditor::_change_syntax_highlighter); ClassDB::bind_method("_edit_option", &ScriptTextEditor::_edit_option); ClassDB::bind_method("_goto_line", &ScriptTextEditor::_goto_line); ClassDB::bind_method("_lookup_symbol", &ScriptTextEditor::_lookup_symbol); ClassDB::bind_method("_text_edit_gui_input", &ScriptTextEditor::_text_edit_gui_input); - ClassDB::bind_method("_toggle_warning_pannel", &ScriptTextEditor::_toggle_warning_pannel); + ClassDB::bind_method("_show_warnings_panel", &ScriptTextEditor::_show_warnings_panel); ClassDB::bind_method("_error_pressed", &ScriptTextEditor::_error_pressed); ClassDB::bind_method("_warning_clicked", &ScriptTextEditor::_warning_clicked); ClassDB::bind_method("_color_changed", &ScriptTextEditor::_color_changed); @@ -1161,7 +1283,8 @@ bool ScriptTextEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_ Dictionary d = p_data; if (d.has("type") && (String(d["type"]) == "resource" || String(d["type"]) == "files" || - String(d["type"]) == "nodes")) { + String(d["type"]) == "nodes" || + String(d["type"]) == "files_and_dirs")) { return true; } @@ -1223,7 +1346,7 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data te->insert_text_at_cursor(res->get_path()); } - if (d.has("type") && String(d["type"]) == "files") { + if (d.has("type") && (String(d["type"]) == "files" || String(d["type"]) == "files_and_dirs")) { Array files = d["files"]; @@ -1394,6 +1517,7 @@ void ScriptTextEditor::_make_context_menu(bool p_selection, bool p_color, bool p context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_left"), EDIT_INDENT_LEFT); context_menu->add_shortcut(ED_GET_SHORTCUT("script_text_editor/indent_right"), EDIT_INDENT_RIGHT); 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); if (p_selection) { context_menu->add_separator(); @@ -1427,13 +1551,14 @@ ScriptTextEditor::ScriptTextEditor() { code_editor = memnew(CodeTextEditor); editor_box->add_child(code_editor); - code_editor->add_constant_override("separation", 0); + code_editor->add_constant_override("separation", 2); code_editor->set_anchors_and_margins_preset(Control::PRESET_WIDE); code_editor->connect("validate_script", this, "_validate_script"); code_editor->connect("load_theme_settings", this, "_load_theme_settings"); code_editor->set_code_complete_func(_code_complete_scripts, this); code_editor->get_text_edit()->connect("breakpoint_toggled", this, "_breakpoint_toggled"); code_editor->get_text_edit()->connect("symbol_lookup", this, "_lookup_symbol"); + code_editor->get_text_edit()->connect("info_clicked", this, "_lookup_connections"); code_editor->set_v_size_flags(SIZE_EXPAND_FILL); warnings_panel = memnew(RichTextLabel); @@ -1445,9 +1570,8 @@ ScriptTextEditor::ScriptTextEditor() { warnings_panel->set_focus_mode(FOCUS_CLICK); warnings_panel->hide(); - code_editor->get_error_label()->connect("gui_input", this, "_error_pressed"); - code_editor->get_warning_label()->connect("gui_input", this, "_toggle_warning_pannel"); - code_editor->get_warning_count_label()->connect("gui_input", this, "_toggle_warning_pannel"); + code_editor->connect("error_pressed", this, "_error_pressed"); + code_editor->connect("show_warnings_panel", this, "_show_warnings_panel"); warnings_panel->connect("meta_clicked", this, "_warning_clicked"); update_settings(); @@ -1546,6 +1670,16 @@ ScriptTextEditor::ScriptTextEditor() { search_menu->get_popup()->connect("id_pressed", this, "_edit_option"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + edit_hb->add_child(edit_menu); quick_open = memnew(ScriptEditorQuickOpen); @@ -1555,6 +1689,9 @@ ScriptTextEditor::ScriptTextEditor() { goto_line_dialog = memnew(GotoLineDialog); add_child(goto_line_dialog); + connection_info_dialog = memnew(ConnectionInfoDialog); + add_child(connection_info_dialog); + code_editor->get_text_edit()->set_drag_forwarding(this); } @@ -1584,6 +1721,10 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/indent_left", TTR("Indent Left"), 0); ED_SHORTCUT("script_text_editor/indent_right", TTR("Indent Right"), 0); ED_SHORTCUT("script_text_editor/toggle_comment", TTR("Toggle Comment"), KEY_MASK_CMD | KEY_K); + 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"), 0); 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"), 0); ED_SHORTCUT("script_text_editor/unfold_all_lines", TTR("Unfold All Lines"), 0); @@ -1591,7 +1732,7 @@ void ScriptTextEditor::register_editor() { ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_C); ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CTRL | KEY_SPACE); #else - ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_B); + ED_SHORTCUT("script_text_editor/clone_down", TTR("Clone Down"), KEY_MASK_CMD | KEY_D); ED_SHORTCUT("script_text_editor/complete_symbol", TTR("Complete Symbol"), KEY_MASK_CMD | KEY_SPACE); #endif ED_SHORTCUT("script_text_editor/trim_trailing_whitespace", TTR("Trim Trailing Whitespace"), KEY_MASK_CMD | KEY_MASK_ALT | KEY_T); @@ -1631,7 +1772,7 @@ void ScriptTextEditor::register_editor() { #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_SHIFT | KEY_F1); + ED_SHORTCUT("script_text_editor/contextual_help", TTR("Contextual Help"), KEY_MASK_ALT | KEY_F1); #endif 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 856e442d82..bdfdf18d45 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -32,8 +32,25 @@ #define SCRIPT_TEXT_EDITOR_H #include "scene/gui/color_picker.h" +#include "scene/gui/dialogs.h" +#include "scene/gui/tree.h" #include "script_editor_plugin.h" +class ConnectionInfoDialog : public AcceptDialog { + + GDCLASS(ConnectionInfoDialog, AcceptDialog); + + Label *method; + Tree *tree; + + virtual void ok_pressed(); + +public: + void popup_connections(String p_method, Vector<Node *> p_nodes); + + ConnectionInfoDialog(); +}; + class ScriptTextEditor : public ScriptEditorBase { GDCLASS(ScriptTextEditor, ScriptEditorBase); @@ -45,6 +62,8 @@ class ScriptTextEditor : public ScriptEditorBase { Vector<String> functions; + List<Connection> missing_connections; + Vector<String> member_keywords; HBoxContainer *edit_hb; @@ -56,6 +75,7 @@ class ScriptTextEditor : public ScriptEditorBase { GotoLineDialog *goto_line_dialog; ScriptEditorQuickOpen *quick_open; + ConnectionInfoDialog *connection_info_dialog; PopupPanel *color_panel; ColorPicker *color_picker; @@ -108,6 +128,10 @@ class ScriptTextEditor : public ScriptEditorBase { SEARCH_LOCATE_FUNCTION, SEARCH_GOTO_LINE, SEARCH_IN_FILES, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, DEBUG_TOGGLE_BREAKPOINT, DEBUG_REMOVE_ALL_BREAKPOINTS, DEBUG_GOTO_NEXT_BREAKPOINT, @@ -125,8 +149,8 @@ protected: void _code_complete_script(const String &p_code, List<String> *r_options, bool &r_force); void _load_theme_settings(); void _set_theme_for_script(); - void _toggle_warning_pannel(const Ref<InputEvent> &p_event); - void _error_pressed(const Ref<InputEvent> &p_event); + void _show_warnings_panel(bool p_show); + void _error_pressed(); void _warning_clicked(Variant p_line); void _notification(int p_what); @@ -136,6 +160,7 @@ protected: void _change_syntax_highlighter(int p_idx); void _edit_option(int p_op); + void _edit_option_toggle_inline_comment(); void _make_context_menu(bool p_selection, bool p_color, bool p_foldable, bool p_open_docs, bool p_goto_definition); void _text_edit_gui_input(const Ref<InputEvent> &ev); void _color_changed(const Color &p_color); @@ -143,6 +168,8 @@ protected: void _goto_line(int p_line) { goto_line(p_line); } void _lookup_symbol(const String &p_symbol, int p_row, int p_column); + void _lookup_connections(int p_row, String p_method); + void _convert_case(CodeTextEditor::CaseStyle p_case); Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); @@ -150,6 +177,8 @@ protected: void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from); public: + void _update_connected_methods(); + virtual void add_syntax_highlighter(SyntaxHighlighter *p_highlighter); virtual void set_syntax_highlighter(SyntaxHighlighter *p_highlighter); @@ -171,6 +200,8 @@ public: virtual void goto_line(int p_line, bool p_with_error = false); void goto_line_selection(int p_line, int p_begin, int p_end); + virtual void set_executing_line(int p_line); + virtual void clear_executing_line(); virtual void reload(bool p_soft); virtual void get_breakpoints(List<int> *p_breakpoints); diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index cc2e65cf6d..a795405dfc 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -47,12 +47,16 @@ Ref<Shader> ShaderTextEditor::get_edited_shader() const { } void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader) { + if (shader == p_shader) { + return; + } shader = p_shader; _load_theme_settings(); get_text_edit()->set_text(p_shader->get_code()); + _validate_script(); _line_col_changed(); } @@ -80,7 +84,9 @@ void ShaderTextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); + Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); @@ -108,7 +114,9 @@ void ShaderTextEditor::_load_theme_settings() { get_text_edit()->add_color_override("function_color", function_color); get_text_edit()->add_color_override("member_variable_color", member_variable_color); get_text_edit()->add_color_override("mark_color", mark_color); + get_text_edit()->add_color_override("bookmark_color", bookmark_color); get_text_edit()->add_color_override("breakpoint_color", breakpoint_color); + get_text_edit()->add_color_override("executing_line_color", executing_line_color); get_text_edit()->add_color_override("code_folding_color", code_folding_color); get_text_edit()->add_color_override("search_result_color", search_result_color); get_text_edit()->add_color_override("search_result_border_color", search_result_border_color); @@ -244,19 +252,19 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_INDENT_LEFT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; + TextEdit *tx = shader_editor->get_text_edit(); tx->indent_left(); } break; case EDIT_INDENT_RIGHT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; + TextEdit *tx = shader_editor->get_text_edit(); tx->indent_right(); } break; @@ -268,54 +276,10 @@ void ShaderEditor::_menu_option(int p_option) { } break; case EDIT_TOGGLE_COMMENT: { - TextEdit *tx = shader_editor->get_text_edit(); if (shader.is_null()) return; - tx->begin_complex_operation(); - if (tx->is_selection_active()) { - int begin = tx->get_selection_from_line(); - int end = tx->get_selection_to_line(); - - // End of selection ends on the first column of the last line, ignore it. - if (tx->get_selection_to_column() == 0) - end -= 1; - - // Check if all lines in the selected block are commented - bool is_commented = true; - for (int i = begin; i <= end; i++) { - if (!tx->get_line(i).begins_with("//")) { - is_commented = false; - break; - } - } - for (int i = begin; i <= end; i++) { - String line_text = tx->get_line(i); - - if (line_text.strip_edges().empty()) { - line_text = "//"; - } else { - if (is_commented) { - line_text = line_text.substr(2, line_text.length()); - } else { - line_text = "//" + line_text; - } - } - tx->set_line(i, line_text); - } - } else { - int begin = tx->cursor_get_line(); - String line_text = tx->get_line(begin); - - if (line_text.begins_with("//")) - line_text = line_text.substr(2, line_text.length()); - else - line_text = "//" + line_text; - tx->set_line(begin, line_text); - } - tx->end_complex_operation(); - tx->update(); - //tx->deselect(); + shader_editor->toggle_inline_comment("//"); } break; case EDIT_COMPLETE: { @@ -342,6 +306,22 @@ void ShaderEditor::_menu_option(int p_option) { goto_line_dialog->popup_find_line(shader_editor->get_text_edit()); } break; + case BOOKMARK_TOGGLE: { + + shader_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + shader_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + shader_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + shader_editor->remove_all_bookmarks(); + } break; } if (p_option != SEARCH_FIND && p_option != SEARCH_REPLACE && p_option != SEARCH_GOTO_LINE) { shader_editor->get_text_edit()->call_deferred("grab_focus"); @@ -351,8 +331,8 @@ void ShaderEditor::_menu_option(int p_option) { void ShaderEditor::_notification(int p_what) { if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - if (is_visible_in_tree()) - shader_editor->get_text_edit()->grab_focus(); + //if (is_visible_in_tree()) + // shader_editor->get_text_edit()->grab_focus(); } } @@ -414,6 +394,9 @@ void ShaderEditor::edit(const Ref<Shader> &p_shader) { if (p_shader.is_null() || !p_shader->is_text_shader()) return; + if (shader == p_shader) + return; + shader = p_shader; shader_editor->set_edited_shader(p_shader); @@ -437,8 +420,12 @@ void ShaderEditor::save_external_data() { void ShaderEditor::apply_shaders() { if (shader.is_valid()) { - shader->set_code(shader_editor->get_text_edit()->get_text()); - shader->set_edited(true); + String shader_code = shader->get_code(); + String editor_code = shader_editor->get_text_edit()->get_text(); + if (shader_code != editor_code) { + shader->set_code(editor_code); + shader->set_edited(true); + } } } @@ -566,6 +553,16 @@ ShaderEditor::ShaderEditor(EditorNode *p_node) { search_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_line"), SEARCH_GOTO_LINE); search_menu->get_popup()->connect("id_pressed", this, "_menu_option"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + add_child(main_container); main_container->add_child(hbc); hbc->add_child(search_menu); diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index 46c78c1d33..28ac9faaa5 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -88,6 +88,10 @@ class ShaderEditor : public PanelContainer { SEARCH_FIND_PREV, SEARCH_REPLACE, SEARCH_GOTO_LINE, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, }; diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp index 619d54b202..0ccb60e39e 100644 --- a/editor/plugins/skeleton_2d_editor_plugin.cpp +++ b/editor/plugins/skeleton_2d_editor_plugin.cpp @@ -63,7 +63,7 @@ void Skeleton2DEditor::_menu_option(int p_option) { return; } UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action("Create Rest Pose from 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_rest", bone->get_transform()); @@ -79,7 +79,7 @@ void Skeleton2DEditor::_menu_option(int p_option) { return; } UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo(); - ur->create_action("Set Rest Pose to 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_transform", bone->get_rest()); @@ -108,6 +108,7 @@ Skeleton2DEditor::Skeleton2DEditor() { options->get_popup()->add_item(TTR("Make Rest Pose (From Bones)"), MENU_OPTION_MAKE_REST); options->get_popup()->add_separator(); options->get_popup()->add_item(TTR("Set Bones to Rest Pose"), MENU_OPTION_SET_REST); + options->set_switch_on_hover(true); options->get_popup()->connect("id_pressed", this, "_menu_option"); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 276ca2e451..60f1248ace 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -35,7 +35,7 @@ #include "core/os/keyboard.h" #include "core/print_string.h" #include "core/project_settings.h" -#include "core/sort.h" +#include "core/sort_array.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/plugins/animation_player_editor_plugin.h" @@ -269,11 +269,21 @@ void SpatialEditorViewport::_select_clicked(bool p_append, bool p_single) { if (!clicked) return; - Spatial *sp = Object::cast_to<Spatial>(ObjectDB::get_instance(clicked)); - if (!sp) + Node *node = Object::cast_to<Node>(ObjectDB::get_instance(clicked)); + Spatial *selected = Object::cast_to<Spatial>(node); + if (!selected) return; - _select(sp, clicked_wants_append, true); + // Replace the node by the group if grouped + while (node && node != editor->get_edited_scene()->get_parent()) { + Spatial *selected_tmp = Object::cast_to<Spatial>(node); + if (selected_tmp && node->has_meta("_edit_group_")) { + selected = selected_tmp; + } + node = node->get_parent(); + } + + _select(selected, clicked_wants_append, true); } void SpatialEditorViewport::_select(Node *p_node, bool p_append, bool p_single) { @@ -511,6 +521,19 @@ void SpatialEditorViewport::_select_region() { item = item->get_owner(); } + // Replace the node by the group if grouped + if (item->is_class("Spatial")) { + Spatial *sel = Object::cast_to<Spatial>(item); + while (item && item != editor->get_edited_scene()->get_parent()) { + Spatial *selected_tmp = Object::cast_to<Spatial>(item); + if (selected_tmp && item->has_meta("_edit_group_")) { + sel = selected_tmp; + } + item = item->get_parent(); + } + item = sel; + } + if (selected.find(item) != -1) continue; Ref<EditorSpatialGizmo> seg = sp->get_gizmo(); @@ -1021,7 +1044,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } _edit.mouse_pos = b->get_position(); - _edit.snap = false; + _edit.snap = spatial_editor->is_snap_enabled(); _edit.mode = TRANSFORM_NONE; //gizmo has priority over everything @@ -1637,7 +1660,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { surface->update(); } break; - default: {} + default: { + } } } @@ -1704,7 +1728,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } break; - default: {} + default: { + } } } @@ -1760,7 +1785,8 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { } break; - default: {} + default: { + } } } @@ -1772,7 +1798,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (ED_IS_SHORTCUT("spatial_editor/snap", p_event)) { if (_edit.mode != TRANSFORM_NONE) { - _edit.snap = true; + _edit.snap = !_edit.snap; } } if (ED_IS_SHORTCUT("spatial_editor/bottom_view", p_event)) { @@ -1824,7 +1850,7 @@ void SpatialEditorViewport::_sinput(const Ref<InputEvent> &p_event) { if (!sp) continue; - emit_signal("transform_key_request", sp, "", sp->get_transform()); + spatial_editor->emit_signal("transform_key_request", sp, "", sp->get_transform()); } set_message(TTR("Animation Key Inserted.")); @@ -2108,9 +2134,11 @@ void SpatialEditorViewport::_notification(int p_what) { set_process(visible); - if (visible) + if (visible) { _update_camera(0); - + } else { + set_freelook_active(false); + } call_deferred("update_transform_gizmo_view"); } @@ -2262,7 +2290,7 @@ void SpatialEditorViewport::_notification(int p_what) { if (show_fps) { String text; const float temp_fps = Engine::get_singleton()->get_frames_per_second(); - text += TTR("FPS") + ": " + itos(temp_fps) + " (" + String::num(1000.0f / temp_fps, 2) + " ms)"; + text += TTR(vformat("FPS: %d (%s ms)", temp_fps, String::num(1000.0f / temp_fps, 2))); fps_label->set_text(text); } @@ -2380,7 +2408,10 @@ void SpatialEditorViewport::_draw() { if (cursor.region_select) { - VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(cursor.region_begin, cursor.region_end - cursor.region_begin), Color(0.7, 0.7, 1.0, 0.3)); + VisualServer::get_singleton()->canvas_item_add_rect( + ci, + Rect2(cursor.region_begin, cursor.region_end - cursor.region_begin), + get_color("accent_color", "Editor") * Color(1, 1, 1, 0.375)); } if (message_time > 0) { @@ -2394,7 +2425,13 @@ void SpatialEditorViewport::_draw() { if (_edit.mode == TRANSFORM_ROTATE) { Point2 center = _point_to_screen(_edit.center); - VisualServer::get_singleton()->canvas_item_add_line(ci, _edit.mouse_pos, center, Color(0.4, 0.7, 1.0, 0.8)); + VisualServer::get_singleton()->canvas_item_add_line( + ci, + _edit.mouse_pos, + center, + get_color("accent_color", "Editor") * Color(1, 1, 1, 0.6), + Math::round(2 * EDSCALE), + true); } if (previewing) { @@ -2436,7 +2473,7 @@ void SpatialEditorViewport::_draw() { real_t max_speed = camera->get_zfar(); real_t scale_length = (max_speed - min_speed); - if (Math::abs(scale_length) > CMP_EPSILON) { + if (!Math::is_zero_approx(scale_length)) { real_t logscale_t = 1.0 - Math::log(1 + freelook_speed - min_speed) / Math::log(1 + scale_length); // There is no real maximum speed so that factor can become negative, @@ -2454,7 +2491,7 @@ void SpatialEditorViewport::_draw() { real_t max_distance = camera->get_zfar(); real_t scale_length = (max_distance - min_distance); - if (Math::abs(scale_length) > CMP_EPSILON) { + if (!Math::is_zero_approx(scale_length)) { real_t logscale_t = 1.0 - Math::log(1 + cursor.distance - min_distance) / Math::log(1 + scale_length); // There is no real maximum distance so that factor can become negative, @@ -2546,7 +2583,7 @@ void SpatialEditorViewport::_menu_option(int p_option) { List<Node *> &selection = editor_selection->get_selected_node_list(); - undo_redo->create_action(TTR("Align with view")); + undo_redo->create_action(TTR("Align with View")); for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { Spatial *sp = Object::cast_to<Spatial>(E->get()); @@ -2865,6 +2902,18 @@ void SpatialEditorViewport::update_transform_gizmo_view() { Transform xform = spatial_editor->get_gizmo_transform(); Transform camera_xform = camera->get_transform(); + + if (xform.origin.distance_squared_to(camera_xform.origin) < 0.01) { + for (int i = 0; i < 3; i++) { + VisualServer::get_singleton()->instance_set_visible(move_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(move_plane_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(rotate_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(scale_gizmo_instance[i], false); + VisualServer::get_singleton()->instance_set_visible(scale_plane_gizmo_instance[i], false); + } + return; + } + Vector3 camz = -camera_xform.get_basis().get_axis(2).normalized(); Vector3 camy = -camera_xform.get_basis().get_axis(1).normalized(); Plane p(camera_xform.origin, camz); @@ -2987,7 +3036,7 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) { previewing = Object::cast_to<Camera>(pv); previewing->connect("tree_exiting", this, "_preview_exited_scene"); VS::get_singleton()->viewport_attach_camera(viewport->get_viewport_rid(), previewing->get_camera()); //replace - view_menu->hide(); + view_menu->set_disabled(true); surface->update(); preview_camera->set_pressed(true); preview_camera->show(); @@ -3461,10 +3510,14 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed camera->make_current(); surface->set_focus_mode(FOCUS_ALL); + VBoxContainer *vbox = memnew(VBoxContainer); + surface->add_child(vbox); + vbox->set_position(Point2(10, 10) * EDSCALE); + view_menu = memnew(MenuButton); view_menu->set_flat(false); - surface->add_child(view_menu); - view_menu->set_position(Point2(10, 10) * EDSCALE); + vbox->add_child(view_menu); + view_menu->set_h_size_flags(0); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/top_view"), VIEW_TOP); view_menu->get_popup()->add_shortcut(ED_GET_SHORTCUT("spatial_editor/bottom_view"), VIEW_BOTTOM); @@ -3517,9 +3570,9 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed ED_SHORTCUT("spatial_editor/freelook_speed_modifier", TTR("Freelook Speed Modifier"), KEY_SHIFT); preview_camera = memnew(CheckBox); - preview_camera->set_position(Point2(10, 38) * EDSCALE); // Below the 'view_menu' MenuButton. preview_camera->set_text(TTR("Preview")); - surface->add_child(preview_camera); + vbox->add_child(preview_camera); + preview_camera->set_h_size_flags(0); preview_camera->hide(); preview_camera->connect("toggled", this, "_toggle_camera_preview"); previewing = NULL; @@ -3542,6 +3595,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed fps_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); fps_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, -10 * EDSCALE); fps_label->set_h_grow_direction(GROW_DIRECTION_BEGIN); + fps_label->set_tooltip(TTR("Note: The FPS value displayed is the editor's framerate.\nIt cannot be used as a reliable indication of in-game performance.")); + fps_label->set_mouse_filter(MOUSE_FILTER_PASS); // Otherwise tooltip doesn't show. surface->add_child(fps_label); fps_label->hide(); @@ -4101,10 +4156,10 @@ Dictionary SpatialEditor::get_state() const { d["zfar"] = get_zfar(); Dictionary gizmos_status; - for (int i = 0; i < gizmo_plugins.size(); i++) { - if (!gizmo_plugins[i]->can_be_hidden()) continue; + for (int i = 0; i < gizmo_plugins_by_name.size(); i++) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; int state = gizmos_menu->get_item_state(gizmos_menu->get_item_index(i)); - String name = gizmo_plugins[i]->get_name(); + String name = gizmo_plugins_by_name[i]->get_name(); gizmos_status[name] = state; } @@ -4154,9 +4209,13 @@ void SpatialEditor::set_state(const Dictionary &p_state) { if (d.has("viewports")) { Array vp = d["viewports"]; - ERR_FAIL_COND(vp.size() > 4); + uint32_t vp_size = static_cast<uint32_t>(vp.size()); + if (vp_size > VIEWPORTS_COUNT) { + WARN_PRINT("Ignoring superfluous viewport settings from spatial editor state.") + vp_size = VIEWPORTS_COUNT; + } - for (uint32_t i = 0; i < VIEWPORTS_COUNT; i++) { + for (uint32_t i = 0; i < vp_size; i++) { viewports[i]->set_state(vp[i]); } } @@ -4189,32 +4248,19 @@ void SpatialEditor::set_state(const Dictionary &p_state) { List<Variant> keys; gizmos_status.get_key_list(&keys); - for (int j = 0; j < gizmo_plugins.size(); ++j) { - if (!gizmo_plugins[j]->can_be_hidden()) continue; - int state = EditorSpatialGizmoPlugin::ON_TOP; + for (int j = 0; j < gizmo_plugins_by_name.size(); ++j) { + if (!gizmo_plugins_by_name[j]->can_be_hidden()) continue; + int state = EditorSpatialGizmoPlugin::VISIBLE; for (int i = 0; i < keys.size(); i++) { - if (gizmo_plugins.write[j]->get_name() == keys[i]) { + if (gizmo_plugins_by_name.write[j]->get_name() == keys[i]) { state = gizmos_status[keys[i]]; + break; } } - const int idx = gizmos_menu->get_item_index(j); - - gizmos_menu->set_item_multistate(idx, state); - gizmo_plugins.write[j]->set_state(state); - - switch (state) { - case EditorSpatialGizmoPlugin::VISIBLE: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible")); - break; - case EditorSpatialGizmoPlugin::ON_TOP: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray")); - break; - case EditorSpatialGizmoPlugin::HIDDEN: - gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden")); - break; - } + gizmo_plugins_by_name.write[j]->set_state(state); } + _update_gizmos_menu(); } } @@ -4328,7 +4374,7 @@ void SpatialEditor::_menu_gizmo_toggled(int p_option) { break; } - gizmo_plugins.write[p_option]->set_state(state); + gizmo_plugins_by_name.write[p_option]->set_state(state); update_all_gizmos(); } @@ -4473,6 +4519,7 @@ void SpatialEditor::_menu_item_pressed(int p_option) { snap_selected_nodes_to_floor(); } break; case MENU_LOCK_SELECTED: { + undo_redo->create_action(TTR("Lock Selected")); List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -4485,13 +4532,18 @@ void SpatialEditor::_menu_item_pressed(int p_option) { if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - spatial->set_meta("_edit_lock_", true); - emit_signal("item_lock_status_changed"); + undo_redo->add_do_method(spatial, "set_meta", "_edit_lock_", true); + undo_redo->add_undo_method(spatial, "remove_meta", "_edit_lock_"); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); } - _refresh_menu_icons(); + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); } break; case MENU_UNLOCK_SELECTED: { + undo_redo->create_action(TTR("Unlock Selected")); List<Node *> &selection = editor_selection->get_selected_node_list(); @@ -4504,11 +4556,62 @@ void SpatialEditor::_menu_item_pressed(int p_option) { if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) continue; - spatial->set_meta("_edit_lock_", Variant()); - emit_signal("item_lock_status_changed"); + undo_redo->add_do_method(spatial, "remove_meta", "_edit_lock_"); + undo_redo->add_undo_method(spatial, "set_meta", "_edit_lock_", true); + undo_redo->add_do_method(this, "emit_signal", "item_lock_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_lock_status_changed"); } - _refresh_menu_icons(); + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); + } break; + case MENU_GROUP_SELECTED: { + undo_redo->create_action(TTR("Group Selected")); + + List<Node *> &selection = editor_selection->get_selected_node_list(); + + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + + Spatial *spatial = Object::cast_to<Spatial>(E->get()); + if (!spatial || !spatial->is_visible_in_tree()) + continue; + + if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + undo_redo->add_do_method(spatial, "set_meta", "_edit_group_", true); + undo_redo->add_undo_method(spatial, "remove_meta", "_edit_group_"); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); + } + + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); + } break; + case MENU_UNGROUP_SELECTED: { + undo_redo->create_action(TTR("Ungroup Selected")); + List<Node *> &selection = editor_selection->get_selected_node_list(); + + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + + Spatial *spatial = Object::cast_to<Spatial>(E->get()); + if (!spatial || !spatial->is_visible_in_tree()) + continue; + + if (spatial->get_viewport() != EditorNode::get_singleton()->get_scene_root()) + continue; + + undo_redo->add_do_method(spatial, "remove_meta", "_edit_group_"); + undo_redo->add_undo_method(spatial, "set_meta", "_edit_group_", true); + undo_redo->add_do_method(this, "emit_signal", "item_group_status_changed"); + undo_redo->add_undo_method(this, "emit_signal", "item_group_status_changed"); + } + + undo_redo->add_do_method(this, "_refresh_menu_icons", Variant()); + undo_redo->add_undo_method(this, "_refresh_menu_icons", Variant()); + undo_redo->commit_action(); } break; } } @@ -4681,10 +4784,10 @@ void SpatialEditor::_init_indicators() { plane_mat->set_on_top_of_alpha(); plane_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); plane_mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); - Color col; - col[i] = 1.0; - col.a = gizmo_alph; - plane_mat->set_albedo(col); + Color col2; + col2[i] = 1.0; + col2.a = gizmo_alph; + plane_mat->set_albedo(col2); plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides surftool->set_material(plane_mat); surftool->commit(move_plane_gizmo[i]); @@ -4810,10 +4913,10 @@ void SpatialEditor::_init_indicators() { plane_mat->set_on_top_of_alpha(); plane_mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true); plane_mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); - Color col; - col[i] = 1.0; - col.a = gizmo_alph; - plane_mat->set_albedo(col); + Color col2; + col2[i] = 1.0; + col2.a = gizmo_alph; + plane_mat->set_albedo(col2); plane_gizmo_color[i] = plane_mat; // needed, so we can draw planes from both sides surftool->set_material(plane_mat); surftool->commit(scale_plane_gizmo[i]); @@ -4824,23 +4927,46 @@ void SpatialEditor::_init_indicators() { _generate_selection_box(); } -struct _GizmoPluginComparator { - - bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const { - return p_a->get_name() < p_b->get_name(); - } -}; - void SpatialEditor::_update_gizmos_menu() { gizmos_menu->clear(); - gizmo_plugins.sort_custom<_GizmoPluginComparator>(); - for (int i = 0; i < gizmo_plugins.size(); ++i) { - if (!gizmo_plugins[i]->can_be_hidden()) continue; - String plugin_name = gizmo_plugins[i]->get_name(); - gizmos_menu->add_multistate_item(TTR(plugin_name), 3, EditorSpatialGizmoPlugin::VISIBLE, i); - gizmos_menu->set_item_icon(gizmos_menu->get_item_index(i), gizmos_menu->get_icon("visibility_visible")); + for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; + String plugin_name = gizmo_plugins_by_name[i]->get_name(); + const int plugin_state = gizmo_plugins_by_name[i]->get_state(); + gizmos_menu->add_multistate_item(TTR(plugin_name), 3, plugin_state, i); + const int idx = gizmos_menu->get_item_index(i); + switch (plugin_state) { + case EditorSpatialGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible")); + break; + case EditorSpatialGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray")); + break; + case EditorSpatialGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden")); + break; + } + } +} + +void SpatialEditor::_update_gizmos_menu_theme() { + for (int i = 0; i < gizmo_plugins_by_name.size(); ++i) { + if (!gizmo_plugins_by_name[i]->can_be_hidden()) continue; + const int plugin_state = gizmo_plugins_by_name[i]->get_state(); + const int idx = gizmos_menu->get_item_index(i); + switch (plugin_state) { + case EditorSpatialGizmoPlugin::VISIBLE: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_visible")); + break; + case EditorSpatialGizmoPlugin::ON_TOP: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_xray")); + break; + case EditorSpatialGizmoPlugin::HIDDEN: + gizmos_menu->set_item_icon(idx, gizmos_menu->get_icon("visibility_hidden")); + break; + } } } @@ -4929,11 +5055,13 @@ bool SpatialEditor::is_any_freelook_active() const { void SpatialEditor::_refresh_menu_icons() { bool all_locked = true; + bool all_grouped = true; List<Node *> &selection = editor_selection->get_selected_node_list(); if (selection.empty()) { all_locked = false; + all_grouped = false; } else { for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { if (Object::cast_to<Spatial>(E->get()) && !Object::cast_to<Spatial>(E->get())->has_meta("_edit_lock_")) { @@ -4941,11 +5069,21 @@ void SpatialEditor::_refresh_menu_icons() { break; } } + for (List<Node *>::Element *E = selection.front(); E; E = E->next()) { + if (Object::cast_to<Spatial>(E->get()) && !Object::cast_to<Spatial>(E->get())->has_meta("_edit_group_")) { + all_grouped = false; + break; + } + } } tool_button[TOOL_LOCK_SELECTED]->set_visible(!all_locked); tool_button[TOOL_LOCK_SELECTED]->set_disabled(selection.empty()); tool_button[TOOL_UNLOCK_SELECTED]->set_visible(all_locked); + + tool_button[TOOL_GROUP_SELECTED]->set_visible(!all_grouped); + tool_button[TOOL_GROUP_SELECTED]->set_disabled(selection.empty()); + tool_button[TOOL_UNGROUP_SELECTED]->set_visible(all_grouped); } template <typename T> @@ -5034,7 +5172,7 @@ void SpatialEditor::snap_selected_nodes_to_floor() { Array keys = snap_data.keys(); if (keys.size()) { - undo_redo->create_action("Snap Nodes To Floor"); + undo_redo->create_action(TTR("Snap Nodes To Floor")); for (int i = 0; i < keys.size(); i++) { Node *node = keys[i]; @@ -5115,6 +5253,8 @@ void SpatialEditor::_notification(int p_what) { tool_button[SpatialEditor::TOOL_MODE_LIST_SELECT]->set_icon(get_icon("ListSelect", "EditorIcons")); tool_button[SpatialEditor::TOOL_LOCK_SELECTED]->set_icon(get_icon("Lock", "EditorIcons")); tool_button[SpatialEditor::TOOL_UNLOCK_SELECTED]->set_icon(get_icon("Unlock", "EditorIcons")); + tool_button[SpatialEditor::TOOL_GROUP_SELECTED]->set_icon(get_icon("Group", "EditorIcons")); + tool_button[SpatialEditor::TOOL_UNGROUP_SELECTED]->set_icon(get_icon("Ungroup", "EditorIcons")); tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->set_icon(get_icon("Object", "EditorIcons")); tool_option_button[SpatialEditor::TOOL_OPT_USE_SNAP]->set_icon(get_icon("Snap", "EditorIcons")); @@ -5133,20 +5273,17 @@ void SpatialEditor::_notification(int p_what) { get_tree()->connect("node_removed", this, "_node_removed"); EditorNode::get_singleton()->get_scene_tree_dock()->get_tree_editor()->connect("node_changed", this, "_refresh_menu_icons"); editor_selection->connect("selection_changed", this, "_refresh_menu_icons"); - } - - if (p_what == NOTIFICATION_ENTER_TREE) { + } else if (p_what == NOTIFICATION_ENTER_TREE) { _register_all_gizmos(); _update_gizmos_menu(); _init_indicators(); - } - - if (p_what == NOTIFICATION_EXIT_TREE) { + } else if (p_what == NOTIFICATION_THEME_CHANGED) { + _update_gizmos_menu_theme(); + } else if (p_what == NOTIFICATION_EXIT_TREE) { _finish_indicators(); - } - if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { + } else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) { tool_button[SpatialEditor::TOOL_MODE_SELECT]->set_icon(get_icon("ToolSelect", "EditorIcons")); tool_button[SpatialEditor::TOOL_MODE_MOVE]->set_icon(get_icon("ToolMove", "EditorIcons")); tool_button[SpatialEditor::TOOL_MODE_ROTATE]->set_icon(get_icon("ToolRotate", "EditorIcons")); @@ -5205,8 +5342,8 @@ void SpatialEditor::_request_gizmo(Object *p_obj) { Ref<EditorSpatialGizmo> seg; - for (int i = 0; i < gizmo_plugins.size(); ++i) { - seg = gizmo_plugins.write[i]->get_gizmo(sp); + for (int i = 0; i < gizmo_plugins_by_priority.size(); ++i) { + seg = gizmo_plugins_by_priority.write[i]->get_gizmo(sp); if (seg.is_valid()) { sp->set_gizmo(seg); @@ -5312,6 +5449,7 @@ void SpatialEditor::_bind_methods() { ADD_SIGNAL(MethodInfo("transform_key_request")); ADD_SIGNAL(MethodInfo("item_lock_status_changed")); + ADD_SIGNAL(MethodInfo("item_group_status_changed")); } void SpatialEditor::clear() { @@ -5425,6 +5563,18 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { tool_button[TOOL_UNLOCK_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); tool_button[TOOL_UNLOCK_SELECTED]->set_tooltip(TTR("Unlock the selected object (can be moved).")); + tool_button[TOOL_GROUP_SELECTED] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_GROUP_SELECTED]); + button_binds.write[0] = MENU_GROUP_SELECTED; + tool_button[TOOL_GROUP_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); + tool_button[TOOL_GROUP_SELECTED]->set_tooltip(TTR("Makes sure the object's children are not selectable.")); + + tool_button[TOOL_UNGROUP_SELECTED] = memnew(ToolButton); + hbc_menu->add_child(tool_button[TOOL_UNGROUP_SELECTED]); + button_binds.write[0] = MENU_UNGROUP_SELECTED; + tool_button[TOOL_UNGROUP_SELECTED]->connect("pressed", this, "_menu_item_pressed", button_binds); + tool_button[TOOL_UNGROUP_SELECTED]->set_tooltip(TTR("Restores the object's children's ability to be selected.")); + hbc_menu->add_child(memnew(VSeparator)); tool_option_button[TOOL_OPT_LOCAL_COORDS] = memnew(ToolButton); @@ -5459,7 +5609,7 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { 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/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); @@ -5480,7 +5630,7 @@ SpatialEditor::SpatialEditor(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/configure_snap", TTR("Configure Snap...")), MENU_TRANSFORM_CONFIGURE_SNAP); p->add_separator(); p->add_shortcut(ED_SHORTCUT("spatial_editor/transform_dialog", TTR("Transform Dialog...")), MENU_TRANSFORM_DIALOG); @@ -5559,11 +5709,11 @@ SpatialEditor::SpatialEditor(EditorNode *p_editor) { snap_dialog_vbc->add_margin_child(TTR("Translate Snap:"), snap_translate); snap_rotate = memnew(LineEdit); - snap_rotate->set_text("5"); + snap_rotate->set_text("15"); snap_dialog_vbc->add_margin_child(TTR("Rotate Snap (deg.):"), snap_rotate); snap_scale = memnew(LineEdit); - snap_scale->set_text("5"); + snap_scale->set_text("10"); snap_dialog_vbc->add_margin_child(TTR("Scale Snap (%):"), snap_scale); /* SETTINGS DIALOG */ @@ -5721,6 +5871,39 @@ Vector3 SpatialEditor::snap_point(Vector3 p_target, Vector3 p_start) const { return p_target; } +float SpatialEditor::get_translate_snap() const { + float snap_value; + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_translate->get_text().to_double() / 10.0; + } else { + snap_value = snap_translate->get_text().to_double(); + } + + return snap_value; +} + +float SpatialEditor::get_rotate_snap() const { + float snap_value; + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_rotate->get_text().to_double() / 3.0; + } else { + snap_value = snap_rotate->get_text().to_double(); + } + + return snap_value; +} + +float SpatialEditor::get_scale_snap() const { + float snap_value; + if (Input::get_singleton()->is_key_pressed(KEY_SHIFT)) { + snap_value = snap_scale->get_text().to_double() / 2.0; + } else { + snap_value = snap_scale->get_text().to_double(); + } + + return snap_value; +} + void SpatialEditorPlugin::_bind_methods() { ClassDB::bind_method("snap_cursor_to_plane", &SpatialEditorPlugin::snap_cursor_to_plane); @@ -5731,14 +5914,39 @@ void SpatialEditorPlugin::snap_cursor_to_plane(const Plane &p_plane) { spatial_editor->snap_cursor_to_plane(p_plane); } +struct _GizmoPluginPriorityComparator { + + bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const { + if (p_a->get_priority() == p_b->get_priority()) { + return p_a->get_name() < p_b->get_name(); + } + return p_a->get_priority() > p_b->get_priority(); + } +}; + +struct _GizmoPluginNameComparator { + + bool operator()(const Ref<EditorSpatialGizmoPlugin> &p_a, const Ref<EditorSpatialGizmoPlugin> &p_b) const { + return p_a->get_name() < p_b->get_name(); + } +}; + void SpatialEditor::add_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin) { - gizmo_plugins.push_back(p_plugin); + ERR_FAIL_NULL(p_plugin.ptr()); + + gizmo_plugins_by_priority.push_back(p_plugin); + gizmo_plugins_by_priority.sort_custom<_GizmoPluginPriorityComparator>(); + + gizmo_plugins_by_name.push_back(p_plugin); + gizmo_plugins_by_name.sort_custom<_GizmoPluginNameComparator>(); + _update_gizmos_menu(); SpatialEditor::get_singleton()->update_all_gizmos(); } void SpatialEditor::remove_gizmo_plugin(Ref<EditorSpatialGizmoPlugin> p_plugin) { - gizmo_plugins.erase(p_plugin); + gizmo_plugins_by_priority.erase(p_plugin); + gizmo_plugins_by_name.erase(p_plugin); _update_gizmos_menu(); } @@ -5750,7 +5958,7 @@ SpatialEditorPlugin::SpatialEditorPlugin(EditorNode *p_node) { editor->get_viewport()->add_child(spatial_editor); spatial_editor->hide(); - spatial_editor->connect("transform_key_request", editor, "_transform_keyed"); + spatial_editor->connect("transform_key_request", editor->get_inspector_dock(), "_transform_keyed"); } SpatialEditorPlugin::~SpatialEditorPlugin() { @@ -5869,7 +6077,7 @@ Ref<SpatialMaterial> EditorSpatialGizmoPlugin::get_material(const String &p_name ERR_FAIL_COND_V(!materials.has(p_name), Ref<SpatialMaterial>()); ERR_FAIL_COND_V(materials[p_name].size() == 0, Ref<SpatialMaterial>()); - if (p_gizmo.is_null()) return materials[p_name][0]; + if (p_gizmo.is_null() || materials[p_name].size() == 1) return materials[p_name][0]; int index = (p_gizmo->is_selected() ? 1 : 0) + (p_gizmo->is_editable() ? 2 : 0); @@ -5888,7 +6096,14 @@ String EditorSpatialGizmoPlugin::get_name() const { if (get_script_instance() && get_script_instance()->has_method("get_name")) { return get_script_instance()->call("get_name"); } - return TTR("Name-less gizmo"); + return TTR("Nameless gizmo"); +} + +int EditorSpatialGizmoPlugin::get_priority() const { + if (get_script_instance() && get_script_instance()->has_method("get_priority")) { + return get_script_instance()->call("get_priority"); + } + return 0; } Ref<EditorSpatialGizmo> EditorSpatialGizmoPlugin::get_gizmo(Spatial *p_spatial) { @@ -5923,6 +6138,7 @@ void EditorSpatialGizmoPlugin::_bind_methods() { ClassDB::bind_method(D_METHOD("get_material", "name", "gizmo"), &EditorSpatialGizmoPlugin::get_material); //, DEFVAL(Ref<EditorSpatialGizmo>())); BIND_VMETHOD(MethodInfo(Variant::STRING, "get_name")); + BIND_VMETHOD(MethodInfo(Variant::STRING, "get_priority")); BIND_VMETHOD(MethodInfo(Variant::BOOL, "can_be_hidden")); BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_selectable_when_hidden")); @@ -5938,7 +6154,7 @@ void EditorSpatialGizmoPlugin::_bind_methods() { cm.default_arguments.push_back(false); BIND_VMETHOD(cm); - BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_gizmo_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); + BIND_VMETHOD(MethodInfo(Variant::BOOL, "is_handle_highlighted", GIZMO_REF, PropertyInfo(Variant::INT, "index"))); #undef GIZMO_REF } @@ -6008,9 +6224,9 @@ void EditorSpatialGizmoPlugin::commit_handle(EditorSpatialGizmo *p_gizmo, int p_ } } -bool EditorSpatialGizmoPlugin::is_gizmo_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int p_idx) const { - if (get_script_instance() && get_script_instance()->has_method("is_gizmo_handle_highlighted")) { - return get_script_instance()->call("is_gizmo_handle_highlighted", p_gizmo, p_idx); +bool EditorSpatialGizmoPlugin::is_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int p_idx) const { + if (get_script_instance() && get_script_instance()->has_method("is_handle_highlighted")) { + return get_script_instance()->call("is_handle_highlighted", p_gizmo, p_idx); } return false; } @@ -6022,6 +6238,10 @@ void EditorSpatialGizmoPlugin::set_state(int p_state) { } } +int EditorSpatialGizmoPlugin::get_state() const { + return current_state; +} + void EditorSpatialGizmoPlugin::unregister_gizmo(EditorSpatialGizmo *p_gizmo) { current_gizmos.erase(p_gizmo); } diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 87cd089428..f3a1e657cc 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -60,6 +60,7 @@ public: RID instance; Ref<ArrayMesh> mesh; + Ref<Material> material; RID skeleton; bool billboard; bool unscaled; @@ -103,13 +104,14 @@ protected: public: void add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard = false); - void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const RID &p_skeleton = RID()); + void add_mesh(const Ref<ArrayMesh> &p_mesh, bool p_billboard = false, const RID &p_skeleton = RID(), const Ref<Material> &p_material = Ref<Material>()); 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, float p_scale = 1); void add_handles(const Vector<Vector3> &p_handles, const Ref<Material> &p_material, bool p_billboard = false, bool p_secondary = false); void add_solid_box(Ref<Material> &p_material, Vector3 p_size, Vector3 p_position = Vector3()); + virtual bool is_handle_highlighted(int p_idx) const; virtual String get_handle_name(int p_idx) const; virtual Variant get_handle_value(int p_idx); virtual void set_handle(int p_idx, Camera *p_camera, const Point2 &p_point); @@ -117,6 +119,7 @@ public: void set_spatial_node(Spatial *p_node); Spatial *get_spatial_node() const { return spatial_node; } + EditorSpatialGizmoPlugin *get_plugin() const { return gizmo_plugin; } Vector3 get_handle_pos(int p_idx) const; bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum); bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false); @@ -482,6 +485,8 @@ public: TOOL_MODE_LIST_SELECT, TOOL_LOCK_SELECTED, TOOL_UNLOCK_SELECTED, + TOOL_GROUP_SELECTED, + TOOL_UNGROUP_SELECTED, TOOL_MAX }; @@ -567,6 +572,8 @@ private: MENU_VIEW_CAMERA_SETTINGS, MENU_LOCK_SELECTED, MENU_UNLOCK_SELECTED, + MENU_GROUP_SELECTED, + MENU_UNGROUP_SELECTED, MENU_SNAP_TO_FLOOR }; @@ -616,6 +623,7 @@ private: void _instance_scene(); void _init_indicators(); void _update_gizmos_menu(); + void _update_gizmos_menu_theme(); void _init_grid(); void _finish_indicators(); void _finish_grid(); @@ -635,7 +643,8 @@ private: static SpatialEditor *singleton; void _node_removed(Node *p_node); - Vector<Ref<EditorSpatialGizmoPlugin> > gizmo_plugins; + Vector<Ref<EditorSpatialGizmoPlugin> > gizmo_plugins_by_priority; + Vector<Ref<EditorSpatialGizmoPlugin> > gizmo_plugins_by_name; void _register_all_gizmos(); @@ -668,9 +677,9 @@ public: ToolMode get_tool_mode() const { return tool_mode; } bool are_local_coords_enabled() const { return tool_option_button[SpatialEditor::TOOL_OPT_LOCAL_COORDS]->is_pressed(); } bool is_snap_enabled() const { return snap_enabled ^ snap_key_enabled; } - float get_translate_snap() const { return snap_translate->get_text().to_double(); } - float get_rotate_snap() const { return snap_rotate->get_text().to_double(); } - float get_scale_snap() const { return snap_scale->get_text().to_double(); } + float get_translate_snap() const; + float get_rotate_snap() const; + float get_scale_snap() const; Ref<ArrayMesh> get_move_gizmo(int idx) const { return move_gizmo[idx]; } Ref<ArrayMesh> get_move_plane_gizmo(int idx) const { return move_plane_gizmo[idx]; } @@ -778,6 +787,7 @@ public: Ref<SpatialMaterial> get_material(const String &p_name, const Ref<EditorSpatialGizmo> &p_gizmo = Ref<EditorSpatialGizmo>()); virtual String get_name() const; + virtual int get_priority() const; virtual bool can_be_hidden() const; virtual bool is_selectable_when_hidden() const; @@ -786,10 +796,11 @@ public: virtual Variant get_handle_value(EditorSpatialGizmo *p_gizmo, int p_idx) const; virtual void set_handle(EditorSpatialGizmo *p_gizmo, int p_idx, Camera *p_camera, const Point2 &p_point); virtual void commit_handle(EditorSpatialGizmo *p_gizmo, int p_idx, const Variant &p_restore, bool p_cancel = false); - virtual bool is_gizmo_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int p_idx) const; + virtual bool is_handle_highlighted(const EditorSpatialGizmo *p_gizmo, int p_idx) const; Ref<EditorSpatialGizmo> get_gizmo(Spatial *p_spatial); void set_state(int p_state); + int get_state() const; void unregister_gizmo(EditorSpatialGizmo *p_gizmo); EditorSpatialGizmoPlugin(); diff --git a/editor/plugins/sprite_editor_plugin.cpp b/editor/plugins/sprite_editor_plugin.cpp index c1718dd8bf..7642bfaf04 100644 --- a/editor/plugins/sprite_editor_plugin.cpp +++ b/editor/plugins/sprite_editor_plugin.cpp @@ -31,7 +31,10 @@ #include "sprite_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "scene/2d/collision_polygon_2d.h" +#include "scene/2d/light_occluder_2d.h" #include "scene/2d/mesh_instance_2d.h" +#include "scene/2d/polygon_2d.h" #include "scene/gui/box_container.h" #include "thirdparty/misc/clipper.hpp" @@ -91,6 +94,8 @@ Vector<Vector2> expand(const Vector<Vector2> &points, const Rect2i &rect, float Vector<Vector2> outPoints; ClipperLib::PolyNode *p2 = out.GetFirst(); + ERR_FAIL_COND_V(!p2, points); + while (p2->IsHole()) { p2 = p2->GetNext(); } @@ -114,8 +119,42 @@ void SpriteEditor::_menu_option(int p_option) { return; } + selected_menu_item = (Menu)p_option; + switch (p_option) { - case MENU_OPTION_CREATE_MESH_2D: { + case MENU_OPTION_CONVERT_TO_MESH_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); + debug_uv_dialog->set_title("Mesh2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + case MENU_OPTION_CONVERT_TO_POLYGON_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create Polygon2D")); + debug_uv_dialog->set_title("Polygon2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + } break; + case MENU_OPTION_CREATE_COLLISION_POLY_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create CollisionPolygon2D")); + debug_uv_dialog->set_title("CollisionPolygon2D Preview"); + + _update_mesh_data(); + debug_uv_dialog->popup_centered(); + debug_uv->update(); + + } break; + case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { + + debug_uv_dialog->get_ok()->set_text(TTR("Create LightOccluder2D")); + debug_uv_dialog->set_title("LightOccluder2D Preview"); _update_mesh_data(); debug_uv_dialog->popup_centered(); @@ -167,47 +206,107 @@ void SpriteEditor::_update_mesh_data() { computed_indices.clear(); Size2 img_size = Vector2(image->get_width(), image->get_height()); - for (int j = 0; j < lines.size(); j++) { - lines.write[j] = expand(lines[j], rect, epsilon); + for (int i = 0; i < lines.size(); i++) { + lines.write[i] = expand(lines[i], rect, epsilon); + } + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D) { + + for (int j = 0; j < lines.size(); j++) { + int index_ofs = computed_vertices.size(); - int index_ofs = computed_vertices.size(); + for (int i = 0; i < lines[j].size(); i++) { + Vector2 vtx = lines[j][i]; + computed_uv.push_back(vtx / img_size); - for (int i = 0; i < lines[j].size(); i++) { - Vector2 vtx = lines[j][i]; - computed_uv.push_back(vtx / img_size); + vtx -= rect.position; //offset by rect position - vtx -= rect.position; //offset by rect position + //flip if flipped + if (node->is_flipped_h()) + vtx.x = rect.size.x - vtx.x - 1.0; + if (node->is_flipped_v()) + vtx.y = rect.size.y - vtx.y - 1.0; - //flip if flipped - if (node->is_flipped_h()) - vtx.x = rect.size.x - vtx.x - 1.0; - if (node->is_flipped_v()) - vtx.y = rect.size.y - vtx.y - 1.0; + if (node->is_centered()) + vtx -= rect.size / 2.0; - if (node->is_centered()) - vtx -= rect.size / 2.0; + computed_vertices.push_back(vtx); + } + + Vector<int> poly = Geometry::triangulate_polygon(lines[j]); - computed_vertices.push_back(vtx); + for (int i = 0; i < poly.size(); i += 3) { + for (int k = 0; k < 3; k++) { + int idx = i + k; + int idxn = i + (k + 1) % 3; + uv_lines.push_back(lines[j][poly[idx]]); + uv_lines.push_back(lines[j][poly[idxn]]); + + computed_indices.push_back(poly[idx] + index_ofs); + } + } } + } - Vector<int> poly = Geometry::triangulate_polygon(lines[j]); + outline_lines.clear(); + computed_outline_lines.clear(); - for (int i = 0; i < poly.size(); i += 3) { - for (int k = 0; k < 3; k++) { - int idx = i + k; - int idxn = i + (k + 1) % 3; - uv_lines.push_back(lines[j][poly[idx]]); - uv_lines.push_back(lines[j][poly[idxn]]); + if (selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) { + outline_lines.resize(lines.size()); + computed_outline_lines.resize(lines.size()); + for (int pi = 0; pi < lines.size(); pi++) { + + Vector<Vector2> ol; + Vector<Vector2> col; + + ol.resize(lines[pi].size()); + col.resize(lines[pi].size()); + + for (int i = 0; i < lines[pi].size(); i++) { + Vector2 vtx = lines[pi][i]; + + ol.write[i] = vtx; + + vtx -= rect.position; //offset by rect position + + //flip if flipped + if (node->is_flipped_h()) + vtx.x = rect.size.x - vtx.x - 1.0; + if (node->is_flipped_v()) + vtx.y = rect.size.y - vtx.y - 1.0; - computed_indices.push_back(poly[idx] + index_ofs); + if (node->is_centered()) + vtx -= rect.size / 2.0; + + col.write[i] = vtx; } + + outline_lines.write[pi] = ol; + computed_outline_lines.write[pi] = col; } } debug_uv->update(); } -void SpriteEditor::_create_mesh_node() { +void SpriteEditor::_create_node() { + switch (selected_menu_item) { + case MENU_OPTION_CONVERT_TO_MESH_2D: { + _convert_to_mesh_2d_node(); + } break; + case MENU_OPTION_CONVERT_TO_POLYGON_2D: { + _convert_to_polygon_2d_node(); + } break; + case MENU_OPTION_CREATE_COLLISION_POLY_2D: { + _create_collision_polygon_2d_node(); + } break; + case MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D: { + _create_light_occluder_2d_node(); + } break; + } +} + +void SpriteEditor::_convert_to_mesh_2d_node() { if (computed_vertices.size() < 3) { err_dialog->set_text(TTR("Invalid geometry, can't replace by mesh.")); @@ -231,6 +330,117 @@ void SpriteEditor::_create_mesh_node() { EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, mesh_instance); } +void SpriteEditor::_convert_to_polygon_2d_node() { + Polygon2D *polygon_2d_instance = memnew(Polygon2D); + + int total_point_count = 0; + for (int i = 0; i < computed_outline_lines.size(); i++) + total_point_count += computed_outline_lines[i].size(); + + PoolVector2Array polygon; + polygon.resize(total_point_count); + PoolVector2Array::Write polygon_write = polygon.write(); + + PoolVector2Array uvs; + uvs.resize(total_point_count); + PoolVector2Array::Write uvs_write = uvs.write(); + + int current_point_index = 0; + + Array polys; + polys.resize(computed_outline_lines.size()); + + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + Vector<Vector2> uv_outline = outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create polygon.")); + err_dialog->popup_centered_minsize(); + return; + } + + PoolIntArray pia; + pia.resize(outline.size()); + PoolIntArray::Write pia_write = pia.write(); + + for (int pi = 0; pi < outline.size(); pi++) { + polygon_write[current_point_index] = outline[pi]; + uvs_write[current_point_index] = uv_outline[pi]; + pia_write[pi] = current_point_index; + current_point_index++; + } + + polys[i] = pia; + } + + polygon_2d_instance->set_uv(uvs); + polygon_2d_instance->set_polygon(polygon); + polygon_2d_instance->set_polygons(polys); + + EditorNode::get_singleton()->get_scene_tree_dock()->replace_node(node, polygon_2d_instance); +} + +void SpriteEditor::_create_collision_polygon_2d_node() { + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create collision polygon.")); + err_dialog->popup_centered_minsize(); + continue; + } + + CollisionPolygon2D *collision_polygon_2d_instance = memnew(CollisionPolygon2D); + collision_polygon_2d_instance->set_polygon(outline); + + _add_as_sibling_or_child(node, collision_polygon_2d_instance); + } +} + +void SpriteEditor::_create_light_occluder_2d_node() { + for (int i = 0; i < computed_outline_lines.size(); i++) { + + Vector<Vector2> outline = computed_outline_lines[i]; + + if (outline.size() < 3) { + err_dialog->set_text(TTR("Invalid geometry, can't create light occluder.")); + err_dialog->popup_centered_minsize(); + continue; + } + + Ref<OccluderPolygon2D> polygon; + polygon.instance(); + + PoolVector2Array a; + a.resize(outline.size()); + PoolVector2Array::Write aw = a.write(); + for (int io = 0; io < outline.size(); io++) { + aw[io] = outline[io]; + } + polygon->set_polygon(a); + + LightOccluder2D *light_occluder_2d_instance = memnew(LightOccluder2D); + light_occluder_2d_instance->set_occluder_polygon(polygon); + + _add_as_sibling_or_child(node, light_occluder_2d_instance); + } +} + +void SpriteEditor::_add_as_sibling_or_child(Node2D *p_own_node, Node2D *p_new_node) { + // Can't make sibling if own node is scene root + if (p_own_node != this->get_tree()->get_edited_scene_root()) { + p_own_node->get_parent()->add_child(p_new_node, true); + p_new_node->set_transform(p_own_node->get_transform()); + } else { + p_own_node->add_child(p_new_node, true); + } + + p_new_node->set_owner(this->get_tree()->get_edited_scene_root()); +} + #if 0 void SpriteEditor::_create_uv_lines() { @@ -296,16 +506,26 @@ void SpriteEditor::_create_uv_lines() { #endif void SpriteEditor::_debug_uv_draw() { - if (uv_lines.size() == 0) - return; - Ref<Texture> tex = node->get_texture(); ERR_FAIL_COND(!tex.is_valid()); debug_uv->set_clip_contents(true); debug_uv->draw_texture(tex, Point2()); debug_uv->set_custom_minimum_size(tex->get_size()); //debug_uv->draw_set_transform(Vector2(), 0, debug_uv->get_size()); - debug_uv->draw_multiline(uv_lines, Color(1.0, 0.8, 0.7)); + + Color color = Color(1.0, 0.8, 0.7); + + if (selected_menu_item == MENU_OPTION_CONVERT_TO_MESH_2D && uv_lines.size() > 0) { + debug_uv->draw_multiline(uv_lines, color); + + } else if ((selected_menu_item == MENU_OPTION_CONVERT_TO_POLYGON_2D || selected_menu_item == MENU_OPTION_CREATE_COLLISION_POLY_2D || selected_menu_item == MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D) && outline_lines.size() > 0) { + for (int i = 0; i < outline_lines.size(); i++) { + Vector<Vector2> outline = outline_lines[i]; + + debug_uv->draw_polyline(outline, color); + debug_uv->draw_line(outline[0], outline[outline.size() - 1], color); + } + } } void SpriteEditor::_bind_methods() { @@ -313,7 +533,7 @@ void SpriteEditor::_bind_methods() { ClassDB::bind_method("_menu_option", &SpriteEditor::_menu_option); ClassDB::bind_method("_debug_uv_draw", &SpriteEditor::_debug_uv_draw); ClassDB::bind_method("_update_mesh_data", &SpriteEditor::_update_mesh_data); - ClassDB::bind_method("_create_mesh_node", &SpriteEditor::_create_mesh_node); + ClassDB::bind_method("_create_node", &SpriteEditor::_create_node); } SpriteEditor::SpriteEditor() { @@ -325,7 +545,11 @@ SpriteEditor::SpriteEditor() { options->set_text(TTR("Sprite")); options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Sprite", "EditorIcons")); - options->get_popup()->add_item(TTR("Convert to 2D Mesh"), MENU_OPTION_CREATE_MESH_2D); + options->get_popup()->add_item(TTR("Convert to Mesh2D"), MENU_OPTION_CONVERT_TO_MESH_2D); + options->get_popup()->add_item(TTR("Convert to Polygon2D"), MENU_OPTION_CONVERT_TO_POLYGON_2D); + options->get_popup()->add_item(TTR("Create CollisionPolygon2D Sibling"), MENU_OPTION_CREATE_COLLISION_POLY_2D); + options->get_popup()->add_item(TTR("Create LightOccluder2D Sibling"), MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D); + options->set_switch_on_hover(true); options->get_popup()->connect("id_pressed", this, "_menu_option"); @@ -333,7 +557,7 @@ SpriteEditor::SpriteEditor() { add_child(err_dialog); debug_uv_dialog = memnew(ConfirmationDialog); - debug_uv_dialog->get_ok()->set_text(TTR("Create 2D Mesh")); + debug_uv_dialog->get_ok()->set_text(TTR("Create Mesh2D")); debug_uv_dialog->set_title("Mesh 2D Preview"); VBoxContainer *vb = memnew(VBoxContainer); debug_uv_dialog->add_child(vb); @@ -345,7 +569,7 @@ SpriteEditor::SpriteEditor() { debug_uv = memnew(Control); debug_uv->connect("draw", this, "_debug_uv_draw"); scroll->add_child(debug_uv); - debug_uv_dialog->connect("confirmed", this, "_create_mesh_node"); + debug_uv_dialog->connect("confirmed", this, "_create_node"); HBoxContainer *hb = memnew(HBoxContainer); hb->add_child(memnew(Label(TTR("Simplification: ")))); diff --git a/editor/plugins/sprite_editor_plugin.h b/editor/plugins/sprite_editor_plugin.h index 7250e3df59..460f5a5707 100644 --- a/editor/plugins/sprite_editor_plugin.h +++ b/editor/plugins/sprite_editor_plugin.h @@ -41,9 +41,14 @@ class SpriteEditor : public Control { GDCLASS(SpriteEditor, Control); enum Menu { - MENU_OPTION_CREATE_MESH_2D, + MENU_OPTION_CONVERT_TO_MESH_2D, + MENU_OPTION_CONVERT_TO_POLYGON_2D, + MENU_OPTION_CREATE_COLLISION_POLY_2D, + MENU_OPTION_CREATE_LIGHT_OCCLUDER_2D }; + Menu selected_menu_item; + Sprite *node; MenuButton *options; @@ -55,7 +60,8 @@ class SpriteEditor : public Control { ConfirmationDialog *debug_uv_dialog; Control *debug_uv; Vector<Vector2> uv_lines; - + Vector<Vector<Vector2> > outline_lines; + Vector<Vector<Vector2> > computed_outline_lines; Vector<Vector2> computed_vertices; Vector<Vector2> computed_uv; Vector<int> computed_indices; @@ -71,7 +77,14 @@ class SpriteEditor : public Control { void _debug_uv_draw(); void _update_mesh_data(); - void _create_mesh_node(); + + void _create_node(); + void _convert_to_mesh_2d_node(); + void _convert_to_polygon_2d_node(); + void _create_collision_polygon_2d_node(); + void _create_light_occluder_2d_node(); + + void _add_as_sibling_or_child(Node2D *p_own_node, Node2D *p_new_node); protected: void _node_removed(Node *p_node); diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index b712cfc9d3..c509202a88 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -38,30 +38,207 @@ void SpriteFramesEditor::_gui_input(Ref<InputEvent> p_event) { } -void SpriteFramesEditor::_notification(int p_what) { +void SpriteFramesEditor::_open_sprite_sheet() { + + file_split_sheet->clear_filters(); + List<String> extensions; + ResourceLoader::get_recognized_extensions_for_type("Texture", &extensions); + for (int i = 0; i < extensions.size(); i++) { + file_split_sheet->add_filter("*." + extensions[i]); + } + + file_split_sheet->popup_centered_ratio(); +} + +void SpriteFramesEditor::_sheet_preview_draw() { + + Size2i size = split_sheet_preview->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; + 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)); + + for (int j = 1; j < v; j++) { + + int y = j * 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)); + } + } + + if (frames_selected.size() == 0) { + split_sheet_dialog->get_ok()->set_disabled(true); + split_sheet_dialog->get_ok()->set_text(TTR("No Frames Selected")); + return; + } + + Color accent = get_color("accent_color", "Editor"); + + 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; + + 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); + split_sheet_preview->draw_rect(Rect2(x + 1, y + 1, width - 2, height - 2), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(x + 2, y + 2, width - 4, height - 4), accent, false); + split_sheet_preview->draw_rect(Rect2(x + 3, y + 3, width - 6, height - 6), accent, false); + split_sheet_preview->draw_rect(Rect2(x + 4, y + 4, width - 8, height - 8), Color(0, 0, 0, 1), false); + split_sheet_preview->draw_rect(Rect2(x + 5, y + 5, width - 10, height - 10), Color(0, 0, 0, 1), false); + } + + split_sheet_dialog->get_ok()->set_disabled(false); + split_sheet_dialog->get_ok()->set_text(vformat(TTR("Add %d Frame(s)"), frames_selected.size())); +} +void SpriteFramesEditor::_sheet_preview_input(const Ref<InputEvent> &p_event) { + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + + int x = CLAMP(int(mb->get_position().x) * h / size.width, 0, h - 1); + int y = CLAMP(int(mb->get_position().y) * v / size.height, 0, v - 1); + + int idx = h * y + x; + + if (mb->get_shift() && 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++) { + if (mb->get_control()) { + frames_selected.erase(i); + } else { + frames_selected.insert(i); + } + } + } else { + if (frames_selected.has(idx)) { + frames_selected.erase(idx); + } else { + frames_selected.insert(idx); + } + } + + last_frame_selected = idx; + split_sheet_preview->update(); + } +} - if (p_what == NOTIFICATION_PHYSICS_PROCESS) { +void SpriteFramesEditor::_sheet_add_frames() { + + Size2i size = split_sheet_preview->get_size(); + int h = split_sheet_h->get_value(); + int v = split_sheet_v->get_value(); + + undo_redo->create_action(TTR("Add Frame")); + + int fc = frames->get_frame_count(edited_anim); + + for (Set<int>::Element *E = frames_selected.front(); E; E = E->next()) { + int idx = E->get(); + int width = size.width / h; + int height = size.height / v; + int xp = idx % h; + int yp = (idx - xp) / h; + int x = xp * width; + int y = yp * height; + + Ref<AtlasTexture> at; + at.instance(); + at->set_atlas(split_sheet_preview->get_texture()); + at->set_region(Rect2(x, y, width, height)); + + undo_redo->add_do_method(frames, "add_frame", edited_anim, at, -1); + undo_redo->add_undo_method(frames, "remove_frame", edited_anim, fc); } - if (p_what == NOTIFICATION_ENTER_TREE) { - load->set_icon(get_icon("Load", "EditorIcons")); - copy->set_icon(get_icon("ActionCopy", "EditorIcons")); - paste->set_icon(get_icon("ActionPaste", "EditorIcons")); - empty->set_icon(get_icon("InsertBefore", "EditorIcons")); - empty2->set_icon(get_icon("InsertAfter", "EditorIcons")); - move_up->set_icon(get_icon("MoveLeft", "EditorIcons")); - move_down->set_icon(get_icon("MoveRight", "EditorIcons")); - _delete->set_icon(get_icon("Remove", "EditorIcons")); - new_anim->set_icon(get_icon("New", "EditorIcons")); - remove_anim->set_icon(get_icon("Remove", "EditorIcons")); + undo_redo->add_do_method(this, "_update_library"); + undo_redo->add_undo_method(this, "_update_library"); + undo_redo->commit_action(); +} + +void SpriteFramesEditor::_sheet_select_clear_all_frames() { + + bool should_clear = true; + for (int i = 0; i < split_sheet_h->get_value() * split_sheet_v->get_value(); i++) { + if (!frames_selected.has(i)) { + frames_selected.insert(i); + should_clear = false; + } } + if (should_clear) { + frames_selected.clear(); + } + + split_sheet_preview->update(); +} + +void SpriteFramesEditor::_sheet_spin_changed(double) { + + frames_selected.clear(); + last_frame_selected = -1; + split_sheet_preview->update(); +} - if (p_what == NOTIFICATION_READY) { +void SpriteFramesEditor::_prepare_sprite_sheet(const String &p_file) { - //NodePath("/root")->connect("node_removed", this,"_node_removed",Vector<Variant>(),true); + Ref<Resource> texture = ResourceLoader::load(p_file); + if (!texture.is_valid()) { + EditorNode::get_singleton()->show_warning("Unable to load images"); + ERR_FAIL_COND(!texture.is_valid()); } + if (texture != split_sheet_preview->get_texture()) { + //different texture, reset to 4x4 + split_sheet_h->set_value(4); + split_sheet_v->set_value(4); + } + frames_selected.clear(); + last_frame_selected = -1; + + split_sheet_preview->set_texture(texture); + split_sheet_dialog->popup_centered_ratio(0.65); +} + +void SpriteFramesEditor::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + load->set_icon(get_icon("Load", "EditorIcons")); + load_sheet->set_icon(get_icon("SpriteSheet", "EditorIcons")); + copy->set_icon(get_icon("ActionCopy", "EditorIcons")); + paste->set_icon(get_icon("ActionPaste", "EditorIcons")); + empty->set_icon(get_icon("InsertBefore", "EditorIcons")); + empty2->set_icon(get_icon("InsertAfter", "EditorIcons")); + move_up->set_icon(get_icon("MoveLeft", "EditorIcons")); + move_down->set_icon(get_icon("MoveRight", "EditorIcons")); + _delete->set_icon(get_icon("Remove", "EditorIcons")); + new_anim->set_icon(get_icon("New", "EditorIcons")); + remove_anim->set_icon(get_icon("Remove", "EditorIcons")); + } // Fallthrough. + case NOTIFICATION_THEME_CHANGED: { + splite_sheet_scroll->add_style_override("bg", get_stylebox("bg", "Tree")); + } break; + case NOTIFICATION_READY: { + add_constant_override("autohide", 1); // Fixes the dragger always showing up. + } break; } } @@ -79,6 +256,7 @@ void SpriteFramesEditor::_file_load_request(const PoolVector<String> &p_path, in if (resource.is_null()) { dialog->set_text(TTR("ERROR: Couldn't load frame resource!")); dialog->set_title(TTR("Error!")); + //dialog->get_cancel()->set_text("Close"); dialog->get_ok()->set_text(TTR("Close")); dialog->popup_centered_minsize(); @@ -285,6 +463,10 @@ void SpriteFramesEditor::_animation_select() { if (updating) return; + double value = anim_speed->get_line_edit()->get_text().to_double(); + if (!Math::is_equal_approx(value, frames->get_animation_speed(edited_anim))) + _animation_fps_changed(value); + TreeItem *selected = animations->get_selected(); ERR_FAIL_COND(!selected); edited_anim = selected->get_text(0); @@ -354,9 +536,6 @@ void SpriteFramesEditor::_animation_name_edited() { for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) { String current = E->get()->call("get_animation"); - if (current != edited_anim) - continue; - undo_redo->add_do_method(E->get(), "set_animation", name); undo_redo->add_undo_method(E->get(), "set_animation", edited_anim); } @@ -389,9 +568,6 @@ void SpriteFramesEditor::_animation_add() { for (List<Node *>::Element *E = nodes.front(); E; E = E->next()) { String current = E->get()->call("get_animation"); - if (frames->has_animation(current)) - continue; - undo_redo->add_do_method(E->get(), "set_animation", name); undo_redo->add_undo_method(E->get(), "set_animation", current); } @@ -649,7 +825,6 @@ void SpriteFramesEditor::drop_data_fw(const Point2 &p_point, const Variant &p_da void SpriteFramesEditor::_bind_methods() { - ClassDB::bind_method(D_METHOD("_gui_input"), &SpriteFramesEditor::_gui_input); ClassDB::bind_method(D_METHOD("_load_pressed"), &SpriteFramesEditor::_load_pressed); ClassDB::bind_method(D_METHOD("_empty_pressed"), &SpriteFramesEditor::_empty_pressed); ClassDB::bind_method(D_METHOD("_empty2_pressed"), &SpriteFramesEditor::_empty2_pressed); @@ -669,35 +844,36 @@ void SpriteFramesEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &SpriteFramesEditor::get_drag_data_fw); ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &SpriteFramesEditor::can_drop_data_fw); ClassDB::bind_method(D_METHOD("drop_data_fw"), &SpriteFramesEditor::drop_data_fw); + ClassDB::bind_method(D_METHOD("_prepare_sprite_sheet"), &SpriteFramesEditor::_prepare_sprite_sheet); + ClassDB::bind_method(D_METHOD("_open_sprite_sheet"), &SpriteFramesEditor::_open_sprite_sheet); + ClassDB::bind_method(D_METHOD("_sheet_preview_draw"), &SpriteFramesEditor::_sheet_preview_draw); + ClassDB::bind_method(D_METHOD("_sheet_preview_input"), &SpriteFramesEditor::_sheet_preview_input); + ClassDB::bind_method(D_METHOD("_sheet_spin_changed"), &SpriteFramesEditor::_sheet_spin_changed); + ClassDB::bind_method(D_METHOD("_sheet_add_frames"), &SpriteFramesEditor::_sheet_add_frames); + ClassDB::bind_method(D_METHOD("_sheet_select_clear_all_frames"), &SpriteFramesEditor::_sheet_select_clear_all_frames); } SpriteFramesEditor::SpriteFramesEditor() { - //add_style_override("panel", EditorNode::get_singleton()->get_gui_base()->get_stylebox("panel","Panel")); - - split = memnew(HSplitContainer); - add_child(split); - VBoxContainer *vbc_animlist = memnew(VBoxContainer); - split->add_child(vbc_animlist); + add_child(vbc_animlist); vbc_animlist->set_custom_minimum_size(Size2(150, 0) * EDSCALE); - //vbc_animlist->set_v_size_flags(SIZE_EXPAND_FILL); VBoxContainer *sub_vb = memnew(VBoxContainer); - vbc_animlist->add_margin_child(TTR("Animations"), sub_vb, true); + vbc_animlist->add_margin_child(TTR("Animations:"), sub_vb, true); sub_vb->set_v_size_flags(SIZE_EXPAND_FILL); HBoxContainer *hbc_animlist = memnew(HBoxContainer); sub_vb->add_child(hbc_animlist); - new_anim = memnew(Button); - new_anim->set_flat(true); + new_anim = memnew(ToolButton); + new_anim->set_tooltip(TTR("New Animation")); hbc_animlist->add_child(new_anim); new_anim->set_h_size_flags(SIZE_EXPAND_FILL); new_anim->connect("pressed", this, "_animation_add"); - remove_anim = memnew(Button); - remove_anim->set_flat(true); + remove_anim = memnew(ToolButton); + remove_anim->set_tooltip(TTR("Remove Animation")); hbc_animlist->add_child(remove_anim); remove_anim->connect("pressed", this, "_animation_remove"); @@ -722,56 +898,55 @@ SpriteFramesEditor::SpriteFramesEditor() { anim_loop->connect("pressed", this, "_animation_loop_changed"); VBoxContainer *vbc = memnew(VBoxContainer); - split->add_child(vbc); + add_child(vbc); vbc->set_h_size_flags(SIZE_EXPAND_FILL); sub_vb = memnew(VBoxContainer); - vbc->add_margin_child(TTR("Animation Frames"), sub_vb, true); + vbc->add_margin_child(TTR("Animation Frames:"), sub_vb, true); HBoxContainer *hbc = memnew(HBoxContainer); sub_vb->add_child(hbc); - //animations = memnew( ItemList ); - - load = memnew(Button); - load->set_flat(true); - load->set_tooltip(TTR("Load Resource")); + load = memnew(ToolButton); + load->set_tooltip(TTR("Add a Texture from File")); hbc->add_child(load); - copy = memnew(Button); - copy->set_flat(true); + load_sheet = memnew(ToolButton); + load_sheet->set_tooltip(TTR("Add Frames from a Sprite Sheet")); + hbc->add_child(load_sheet); + + hbc->add_child(memnew(VSeparator)); + + copy = memnew(ToolButton); copy->set_tooltip(TTR("Copy")); hbc->add_child(copy); - paste = memnew(Button); - paste->set_flat(true); + paste = memnew(ToolButton); paste->set_tooltip(TTR("Paste")); hbc->add_child(paste); - empty = memnew(Button); - empty->set_flat(true); + hbc->add_spacer(false); + + empty = memnew(ToolButton); empty->set_tooltip(TTR("Insert Empty (Before)")); hbc->add_child(empty); - empty2 = memnew(Button); - empty2->set_flat(true); + empty2 = memnew(ToolButton); empty2->set_tooltip(TTR("Insert Empty (After)")); hbc->add_child(empty2); - hbc->add_spacer(false); + hbc->add_child(memnew(VSeparator)); - move_up = memnew(Button); - move_up->set_flat(true); + move_up = memnew(ToolButton); move_up->set_tooltip(TTR("Move (Before)")); hbc->add_child(move_up); - move_down = memnew(Button); - move_down->set_flat(true); + move_down = memnew(ToolButton); move_down->set_tooltip(TTR("Move (After)")); hbc->add_child(move_down); - _delete = memnew(Button); - _delete->set_flat(true); + _delete = memnew(ToolButton); + _delete->set_tooltip(TTR("Delete")); hbc->add_child(_delete); file = memnew(EditorFileDialog); @@ -787,7 +962,6 @@ SpriteFramesEditor::SpriteFramesEditor() { tree->set_fixed_column_width(thumbnail_size * 3 / 2); tree->set_max_text_lines(2); tree->set_fixed_icon_size(Size2(thumbnail_size, thumbnail_size)); - //tree->set_min_icon_size(Size2(thumbnail_size,thumbnail_size)); tree->set_drag_forwarding(this); sub_vb->add_child(tree); @@ -796,6 +970,7 @@ SpriteFramesEditor::SpriteFramesEditor() { add_child(dialog); load->connect("pressed", this, "_load_pressed"); + load_sheet->connect("pressed", this, "_open_sprite_sheet"); _delete->connect("pressed", this, "_delete_pressed"); copy->connect("pressed", this, "_copy_pressed"); paste->connect("pressed", this, "_paste_pressed"); @@ -810,6 +985,66 @@ SpriteFramesEditor::SpriteFramesEditor() { updating = false; edited_anim = "default"; + + split_sheet_dialog = memnew(ConfirmationDialog); + add_child(split_sheet_dialog); + VBoxContainer *split_sheet_vb = memnew(VBoxContainer); + split_sheet_dialog->add_child(split_sheet_vb); + split_sheet_dialog->set_title(TTR("Select Frames")); + split_sheet_dialog->connect("confirmed", this, "_sheet_add_frames"); + + HBoxContainer *split_sheet_hb = memnew(HBoxContainer); + + Label *ss_label = memnew(Label(TTR("Horizontal:"))); + split_sheet_hb->add_child(ss_label); + split_sheet_h = memnew(SpinBox); + split_sheet_h->set_min(1); + split_sheet_h->set_max(128); + split_sheet_h->set_step(1); + split_sheet_hb->add_child(split_sheet_h); + split_sheet_h->connect("value_changed", this, "_sheet_spin_changed"); + + ss_label = memnew(Label(TTR("Vertical:"))); + split_sheet_hb->add_child(ss_label); + split_sheet_v = memnew(SpinBox); + split_sheet_v->set_min(1); + split_sheet_v->set_max(128); + split_sheet_v->set_step(1); + split_sheet_hb->add_child(split_sheet_v); + split_sheet_v->connect("value_changed", this, "_sheet_spin_changed"); + + split_sheet_hb->add_spacer(); + + Button *select_clear_all = memnew(Button); + select_clear_all->set_text(TTR("Select/Clear All Frames")); + select_clear_all->connect("pressed", this, "_sheet_select_clear_all_frames"); + split_sheet_hb->add_child(select_clear_all); + + split_sheet_vb->add_child(split_sheet_hb); + + split_sheet_preview = memnew(TextureRect); + split_sheet_preview->set_expand(false); + split_sheet_preview->set_mouse_filter(MOUSE_FILTER_PASS); + split_sheet_preview->connect("draw", this, "_sheet_preview_draw"); + split_sheet_preview->connect("gui_input", this, "_sheet_preview_input"); + + splite_sheet_scroll = memnew(ScrollContainer); + splite_sheet_scroll->set_enable_h_scroll(true); + splite_sheet_scroll->set_enable_v_scroll(true); + splite_sheet_scroll->set_v_size_flags(SIZE_EXPAND_FILL); + CenterContainer *cc = memnew(CenterContainer); + cc->add_child(split_sheet_preview); + cc->set_h_size_flags(SIZE_EXPAND_FILL); + cc->set_v_size_flags(SIZE_EXPAND_FILL); + splite_sheet_scroll->add_child(cc); + + split_sheet_vb->add_child(splite_sheet_scroll); + + file_split_sheet = memnew(EditorFileDialog); + file_split_sheet->set_title(TTR("Create Frames from Sprite Sheet")); + file_split_sheet->set_mode(EditorFileDialog::MODE_OPEN_FILE); + add_child(file_split_sheet); + file_split_sheet->connect("file_selected", this, "_prepare_sprite_sheet"); } void SpriteFramesEditorPlugin::edit(Object *p_object) { diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index 3ef4ba290b..d64431cde7 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -37,27 +37,29 @@ #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" #include "scene/gui/split_container.h" +#include "scene/gui/texture_rect.h" #include "scene/gui/tree.h" -class SpriteFramesEditor : public PanelContainer { +class SpriteFramesEditor : public HSplitContainer { - GDCLASS(SpriteFramesEditor, PanelContainer); + GDCLASS(SpriteFramesEditor, HSplitContainer); - Button *load; - Button *_delete; - Button *copy; - Button *paste; - Button *empty; - Button *empty2; - Button *move_up; - Button *move_down; + ToolButton *load; + ToolButton *load_sheet; + ToolButton *_delete; + ToolButton *copy; + ToolButton *paste; + ToolButton *empty; + ToolButton *empty2; + ToolButton *move_up; + ToolButton *move_down; ItemList *tree; bool loading_scene; int sel; HSplitContainer *split; - Button *new_anim; - Button *remove_anim; + ToolButton *new_anim; + ToolButton *remove_anim; Tree *animations; SpinBox *anim_speed; @@ -71,6 +73,15 @@ class SpriteFramesEditor : public PanelContainer { StringName edited_anim; + ConfirmationDialog *split_sheet_dialog; + ScrollContainer *splite_sheet_scroll; + TextureRect *split_sheet_preview; + SpinBox *split_sheet_h; + SpinBox *split_sheet_v; + EditorFileDialog *file_split_sheet; + Set<int> frames_selected; + int last_frame_selected; + void _load_pressed(); void _load_scene_pressed(); void _file_load_request(const PoolVector<String> &p_path, int p_at_pos = -1); @@ -99,6 +110,14 @@ class SpriteFramesEditor : 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); + void _open_sprite_sheet(); + void _prepare_sprite_sheet(const String &p_file); + void _sheet_preview_draw(); + void _sheet_spin_changed(double); + void _sheet_preview_input(const Ref<InputEvent> &p_event); + void _sheet_add_frames(); + void _sheet_select_clear_all_frames(); + protected: void _notification(int p_what); void _gui_input(Ref<InputEvent> p_event); diff --git a/editor/plugins/text_editor.cpp b/editor/plugins/text_editor.cpp index fe32c97a64..a0f3c253d1 100644 --- a/editor/plugins/text_editor.cpp +++ b/editor/plugins/text_editor.cpp @@ -66,7 +66,6 @@ void TextEditor::_change_syntax_highlighter(int p_idx) { el = el->next(); } set_syntax_highlighter(highlighters[highlighter_menu->get_item_text(p_idx)]); - EditorSettings::get_singleton()->set_project_metadata("text_editor", "syntax_highlighter", p_idx); } void TextEditor::_load_theme_settings() { @@ -94,7 +93,9 @@ void TextEditor::_load_theme_settings() { Color function_color = EDITOR_GET("text_editor/highlighting/function_color"); Color member_variable_color = EDITOR_GET("text_editor/highlighting/member_variable_color"); Color mark_color = EDITOR_GET("text_editor/highlighting/mark_color"); + Color bookmark_color = EDITOR_GET("text_editor/highlighting/bookmark_color"); Color breakpoint_color = EDITOR_GET("text_editor/highlighting/breakpoint_color"); + Color executing_line_color = EDITOR_GET("text_editor/highlighting/executing_line_color"); Color code_folding_color = EDITOR_GET("text_editor/highlighting/code_folding_color"); Color search_result_color = EDITOR_GET("text_editor/highlighting/search_result_color"); Color search_result_border_color = EDITOR_GET("text_editor/highlighting/search_result_border_color"); @@ -125,7 +126,9 @@ void TextEditor::_load_theme_settings() { text_edit->add_color_override("function_color", function_color); text_edit->add_color_override("member_variable_color", member_variable_color); text_edit->add_color_override("breakpoint_color", breakpoint_color); + text_edit->add_color_override("executing_line_color", executing_line_color); text_edit->add_color_override("mark_color", mark_color); + text_edit->add_color_override("bookmark_color", bookmark_color); text_edit->add_color_override("code_folding_color", code_folding_color); text_edit->add_color_override("search_result_color", search_result_color); text_edit->add_color_override("search_result_border_color", search_result_border_color); @@ -201,7 +204,6 @@ void TextEditor::reload_text() { int v = te->get_v_scroll(); te->set_text(text_file->get_text()); - te->clear_undo_history(); te->cursor_set_line(row); te->cursor_set_column(column); te->set_h_scroll(h); @@ -234,6 +236,14 @@ Variant TextEditor::get_edit_state() { void TextEditor::set_edit_state(const Variant &p_state) { code_editor->set_edit_state(p_state); + + Dictionary state = p_state; + if (state.has("syntax_highlighter")) { + int idx = highlighter_menu->get_item_idx_from_text(state["syntax_highlighter"]); + if (idx >= 0) { + _change_syntax_highlighter(idx); + } + } } void TextEditor::trim_trailing_whitespace() { @@ -261,6 +271,15 @@ void TextEditor::goto_line(int p_line, bool p_with_error) { code_editor->goto_line(p_line); } +void TextEditor::set_executing_line(int p_line) { + + code_editor->set_executing_line(p_line); +} + +void TextEditor::clear_executing_line() { + code_editor->clear_executing_line(); +} + void TextEditor::ensure_focus() { code_editor->get_text_edit()->grab_focus(); @@ -299,7 +318,6 @@ void TextEditor::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: _load_theme_settings(); - _change_syntax_highlighter(EditorSettings::get_singleton()->get_project_metadata("text_editor", "syntax_highlighter", 0)); break; } } @@ -421,6 +439,22 @@ void TextEditor::_edit_option(int p_op) { goto_line_dialog->popup_find_line(tx); } break; + case BOOKMARK_TOGGLE: { + + code_editor->toggle_bookmark(); + } break; + case BOOKMARK_GOTO_NEXT: { + + code_editor->goto_next_bookmark(); + } break; + case BOOKMARK_GOTO_PREV: { + + code_editor->goto_prev_bookmark(); + } break; + case BOOKMARK_REMOVE_ALL: { + + code_editor->remove_all_bookmarks(); + } break; } } @@ -603,5 +637,15 @@ TextEditor::TextEditor() { highlighter_menu->add_radio_check_item(TTR("Standard")); highlighter_menu->connect("id_pressed", this, "_change_syntax_highlighter"); + PopupMenu *bookmarks = memnew(PopupMenu); + bookmarks->set_name("bookmarks"); + edit_menu->get_popup()->add_child(bookmarks); + edit_menu->get_popup()->add_submenu_item(TTR("Bookmarks"), "bookmarks"); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/toggle_bookmark"), BOOKMARK_TOGGLE); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/remove_all_bookmarks"), BOOKMARK_REMOVE_ALL); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_next_bookmark"), BOOKMARK_GOTO_NEXT); + bookmarks->add_shortcut(ED_GET_SHORTCUT("script_text_editor/goto_previous_bookmark"), BOOKMARK_GOTO_PREV); + bookmarks->connect("id_pressed", this, "_edit_option"); + code_editor->get_text_edit()->set_drag_forwarding(this); } diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 3c136277df..2da7474793 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -87,6 +87,10 @@ private: SEARCH_FIND_PREV, SEARCH_REPLACE, SEARCH_GOTO_LINE, + BOOKMARK_TOGGLE, + BOOKMARK_GOTO_NEXT, + BOOKMARK_GOTO_PREV, + BOOKMARK_REMOVE_ALL, }; protected: @@ -123,6 +127,8 @@ public: virtual Vector<String> get_functions(); virtual void get_breakpoints(List<int> *p_breakpoints); virtual void goto_line(int p_line, bool p_with_error = false); + virtual void set_executing_line(int p_line); + virtual void clear_executing_line(); virtual void trim_trailing_whitespace(); virtual void convert_indent_to_spaces(); virtual void convert_indent_to_tabs(); diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index 0482ae86f3..0aa4a7662c 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -138,39 +138,33 @@ TextureEditor::TextureEditor() { set_custom_minimum_size(Size2(1, 150)); } -void TextureEditorPlugin::edit(Object *p_object) { - - Texture *s = Object::cast_to<Texture>(p_object); - if (!s) - return; - - texture_editor->edit(Ref<Texture>(s)); +TextureEditor::~TextureEditor() { + if (!texture.is_null()) { + texture->remove_change_receptor(this); + } } +// +bool EditorInspectorPluginTexture::can_handle(Object *p_object) { -bool TextureEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("Texture"); + return Object::cast_to<ImageTexture>(p_object) != NULL || Object::cast_to<AtlasTexture>(p_object) != NULL || Object::cast_to<StreamTexture>(p_object) != NULL || Object::cast_to<LargeTexture>(p_object) != NULL || Object::cast_to<AnimatedTexture>(p_object) != NULL; } -void TextureEditorPlugin::make_visible(bool p_visible) { +void EditorInspectorPluginTexture::parse_begin(Object *p_object) { - if (p_visible) { - texture_editor->show(); - //texture_editor->set_process(true); - } else { - - texture_editor->hide(); - //texture_editor->set_process(false); + Texture *texture = Object::cast_to<Texture>(p_object); + if (!texture) { + return; } + Ref<Texture> m(texture); + + TextureEditor *editor = memnew(TextureEditor); + editor->edit(m); + add_custom_control(editor); } TextureEditorPlugin::TextureEditorPlugin(EditorNode *p_node) { - editor = p_node; - texture_editor = memnew(TextureEditor); - add_control_to_container(CONTAINER_PROPERTY_EDITOR_BOTTOM, texture_editor); - texture_editor->hide(); -} - -TextureEditorPlugin::~TextureEditorPlugin() { + Ref<EditorInspectorPluginTexture> plugin; + plugin.instance(); + add_inspector_plugin(plugin); } diff --git a/editor/plugins/texture_editor_plugin.h b/editor/plugins/texture_editor_plugin.h index 80ff4d6416..bcbda1fbd7 100644 --- a/editor/plugins/texture_editor_plugin.h +++ b/editor/plugins/texture_editor_plugin.h @@ -50,24 +50,24 @@ protected: public: void edit(Ref<Texture> p_texture); TextureEditor(); + ~TextureEditor(); +}; + +class EditorInspectorPluginTexture : public EditorInspectorPlugin { + GDCLASS(EditorInspectorPluginTexture, EditorInspectorPlugin) +public: + virtual bool can_handle(Object *p_object); + virtual void parse_begin(Object *p_object); }; class TextureEditorPlugin : public EditorPlugin { GDCLASS(TextureEditorPlugin, EditorPlugin); - TextureEditor *texture_editor; - EditorNode *editor; - public: virtual String get_name() const { return "Texture"; } - bool has_main_screen() const { return false; } - virtual void edit(Object *p_object); - virtual bool handles(Object *p_object) const; - virtual void make_visible(bool p_visible); TextureEditorPlugin(EditorNode *p_node); - ~TextureEditorPlugin(); }; #endif // TEXTURE_EDITOR_PLUGIN_H diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 3eeb871380..4e15bd5116 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -64,7 +64,7 @@ void TextureRegionEditor::_region_draw() { return; Transform2D mtx; - mtx.elements[2] = -draw_ofs; + mtx.elements[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), mtx); @@ -128,15 +128,14 @@ void TextureRegionEditor::_region_draw() { }; for (int i = 0; i < 4; i++) { int next = (i + 1) % 4; - edit_draw->draw_line(endpoints[i] - draw_ofs, endpoints[next] - draw_ofs, Color(0.3, 0.7, 1, 1), 2); + edit_draw->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, Color(0.3, 0.7, 1, 1), 2); } } } Ref<Texture> select_handle = get_icon("EditorHandle", "EditorIcons"); - Rect2 scroll_rect(Point2(), mtx.basis_xform(base_tex->get_size())); - scroll_rect.expand_to(mtx.basis_xform(edit_draw->get_size())); + Rect2 scroll_rect; Vector2 endpoints[4] = { mtx.basis_xform(rect.position), @@ -153,21 +152,23 @@ void TextureRegionEditor::_region_draw() { Vector2 ofs = ((endpoints[i] - endpoints[prev]).normalized() + ((endpoints[i] - endpoints[next]).normalized())).normalized(); ofs *= 1.4144 * (select_handle->get_size().width / 2); - edit_draw->draw_line(endpoints[i] - draw_ofs, endpoints[next] - draw_ofs, color, 2); + edit_draw->draw_line(endpoints[i] - draw_ofs * draw_zoom, endpoints[next] - draw_ofs * draw_zoom, color, 2); if (snap_mode != SNAP_AUTOSLICE) - edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs); + edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); ofs = (endpoints[next] - endpoints[i]) / 2; ofs += (endpoints[next] - endpoints[i]).tangent().normalized() * (select_handle->get_size().width / 2); if (snap_mode != SNAP_AUTOSLICE) - edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs); + edit_draw->draw_texture(select_handle, (endpoints[i] + ofs - (select_handle->get_size() / 2)).floor() - draw_ofs * draw_zoom); scroll_rect.expand_to(endpoints[i]); } - scroll_rect = scroll_rect.grow(200); + scroll_rect.position -= edit_draw->get_size(); + scroll_rect.size += edit_draw->get_size() * 2.0; + updating_scroll = true; hscroll->set_min(scroll_rect.position.x); hscroll->set_max(scroll_rect.position.x + scroll_rect.size.x); @@ -205,10 +206,10 @@ void TextureRegionEditor::_region_draw() { margins[3] = obj_styleBox->get_margin_size(MARGIN_RIGHT); } Vector2 pos[4] = { - mtx.basis_xform(Vector2(0, margins[0])) + Vector2(0, endpoints[0].y - draw_ofs.y), - -mtx.basis_xform(Vector2(0, margins[1])) + Vector2(0, endpoints[2].y - draw_ofs.y), - mtx.basis_xform(Vector2(margins[2], 0)) + Vector2(endpoints[0].x - draw_ofs.x, 0), - -mtx.basis_xform(Vector2(margins[3], 0)) + Vector2(endpoints[2].x - draw_ofs.x, 0) + mtx.basis_xform(Vector2(0, margins[0])) + Vector2(0, endpoints[0].y - draw_ofs.y * draw_zoom), + -mtx.basis_xform(Vector2(0, margins[1])) + Vector2(0, endpoints[2].y - draw_ofs.y * draw_zoom), + mtx.basis_xform(Vector2(margins[2], 0)) + Vector2(endpoints[0].x - draw_ofs.x * draw_zoom, 0), + -mtx.basis_xform(Vector2(margins[3], 0)) + Vector2(endpoints[2].x - draw_ofs.x * draw_zoom, 0) }; draw_margin_line(edit_draw, pos[0], pos[0] + Vector2(edit_draw->get_size().x, 0)); @@ -220,7 +221,7 @@ void TextureRegionEditor::_region_draw() { void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { Transform2D mtx; - mtx.elements[2] = -draw_ofs; + mtx.elements[2] = -draw_ofs * draw_zoom; mtx.scale_basis(Vector2(draw_zoom, draw_zoom)); Vector2 endpoints[8] = { @@ -255,10 +256,10 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { margins[3] = obj_styleBox->get_margin_size(MARGIN_RIGHT); } Vector2 pos[4] = { - mtx.basis_xform(rect.position + Vector2(0, margins[0])) - draw_ofs, - mtx.basis_xform(rect.position + rect.size - Vector2(0, margins[1])) - draw_ofs, - mtx.basis_xform(rect.position + Vector2(margins[2], 0)) - draw_ofs, - mtx.basis_xform(rect.position + rect.size - Vector2(margins[3], 0)) - draw_ofs + mtx.basis_xform(rect.position + Vector2(0, margins[0])) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + rect.size - Vector2(0, margins[1])) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + Vector2(margins[2], 0)) - draw_ofs * draw_zoom, + mtx.basis_xform(rect.position + rect.size - Vector2(margins[3], 0)) - draw_ofs * draw_zoom }; if (Math::abs(mb->get_position().y - pos[0].y) < 8) { edited_margin = 0; @@ -415,9 +416,9 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { } } } else if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) { - _zoom_in(); + _zoom_on_position(draw_zoom * ((0.95 + (0.05 * mb->get_factor())) / 0.95), mb->get_position()); } else if (mb->get_button_index() == BUTTON_WHEEL_DOWN && mb->is_pressed()) { - _zoom_out(); + _zoom_on_position(draw_zoom * (1 - (0.05 * mb->get_factor())), mb->get_position()); } } @@ -427,7 +428,7 @@ void TextureRegionEditor::_region_input(const Ref<InputEvent> &p_input) { if (mm->get_button_mask() & BUTTON_MASK_MIDDLE || Input::get_singleton()->is_key_pressed(KEY_SPACE)) { - Vector2 draged(mm->get_relative().x, mm->get_relative().y); + Vector2 draged(mm->get_relative().x / draw_zoom, mm->get_relative().y / draw_zoom); hscroll->set_value(hscroll->get_value() - draged.x); vscroll->set_value(vscroll->get_value() - draged.y); @@ -578,25 +579,30 @@ void TextureRegionEditor::_set_snap_sep_y(float p_val) { edit_draw->update(); } +void TextureRegionEditor::_zoom_on_position(float p_zoom, Point2 p_position) { + if (p_zoom < 0.25 || p_zoom > 8) + return; + + float prev_zoom = draw_zoom; + 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); + + edit_draw->update(); +} + void TextureRegionEditor::_zoom_in() { - if (draw_zoom < 8) { - draw_zoom *= 2; - edit_draw->update(); - } + _zoom_on_position(draw_zoom * 1.5, edit_draw->get_size() / 2.0); } void TextureRegionEditor::_zoom_reset() { - if (draw_zoom == 1) - return; - draw_zoom = 1; - edit_draw->update(); + _zoom_on_position(1.0, edit_draw->get_size() / 2.0); } void TextureRegionEditor::_zoom_out() { - if (draw_zoom > 0.25) { - draw_zoom /= 2; - edit_draw->update(); - } + _zoom_on_position(draw_zoom / 1.5, edit_draw->get_size() / 2.0); } void TextureRegionEditor::apply_rect(const Rect2 &p_rect) { @@ -743,6 +749,7 @@ void TextureRegionEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_snap_step_y"), &TextureRegionEditor::_set_snap_step_y); ClassDB::bind_method(D_METHOD("_set_snap_sep_x"), &TextureRegionEditor::_set_snap_sep_x); ClassDB::bind_method(D_METHOD("_set_snap_sep_y"), &TextureRegionEditor::_set_snap_sep_y); + ClassDB::bind_method(D_METHOD("_zoom_on_position"), &TextureRegionEditor::_zoom_on_position); ClassDB::bind_method(D_METHOD("_zoom_in"), &TextureRegionEditor::_zoom_in); ClassDB::bind_method(D_METHOD("_zoom_reset"), &TextureRegionEditor::_zoom_reset); ClassDB::bind_method(D_METHOD("_zoom_out"), &TextureRegionEditor::_zoom_out); diff --git a/editor/plugins/texture_region_editor_plugin.h b/editor/plugins/texture_region_editor_plugin.h index 19eaef9bc3..a49e0fb96c 100644 --- a/editor/plugins/texture_region_editor_plugin.h +++ b/editor/plugins/texture_region_editor_plugin.h @@ -110,6 +110,7 @@ class TextureRegionEditor : public VBoxContainer { void _set_snap_step_y(float p_val); void _set_snap_sep_x(float p_val); void _set_snap_sep_y(float p_val); + void _zoom_on_position(float p_zoom, Point2 p_position = Point2()); void _zoom_in(); void _zoom_reset(); void _zoom_out(); diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index e9b9c03c1e..5b67d259ba 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -36,7 +36,7 @@ void ThemeEditor::edit(const Ref<Theme> &p_theme) { theme = p_theme; - main_vb->set_theme(p_theme); + main_container->set_theme(p_theme); } void ThemeEditor::_propagate_redraw(Control *p_at) { @@ -53,7 +53,7 @@ void ThemeEditor::_propagate_redraw(Control *p_at) { void ThemeEditor::_refresh_interval() { - _propagate_redraw(main_vb); + _propagate_redraw(main_container); } void ThemeEditor::_type_menu_cbk(int p_option) { @@ -86,7 +86,7 @@ void ThemeEditor::_name_menu_about_to_show() { } name_menu->get_popup()->clear(); - + name_menu->get_popup()->set_size(Size2()); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { name_menu->get_popup()->add_item(E->get()); @@ -574,7 +574,6 @@ void ThemeEditor::_theme_menu_cbk(int p_option) { } } - //types.sort(); types.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = types.front(); E; E = E->next()) { @@ -610,30 +609,14 @@ ThemeEditor::ThemeEditor() { time_left = 0; - scroll = memnew(ScrollContainer); - add_child(scroll); - scroll->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 3); - scroll->set_margin(MARGIN_TOP, 30 * EDSCALE); - //scroll->set_enable_h_scroll(true); - scroll->set_enable_v_scroll(true); - scroll->set_enable_h_scroll(false); - - Panel *panel = memnew(Panel); - scroll->add_child(panel); - panel->set_custom_minimum_size(Size2(500, 800) * EDSCALE); - panel->set_theme(Theme::get_default()); - panel->set_h_size_flags(SIZE_EXPAND_FILL); - - main_vb = memnew(VBoxContainer); - panel->add_child(main_vb); - main_vb->set_anchors_and_margins_preset(Control::PRESET_WIDE, Control::PRESET_MODE_MINSIZE, 4 * EDSCALE); + HBoxContainer *top_menu = memnew(HBoxContainer); + add_child(top_menu); - HBoxContainer *hb_menu = memnew(HBoxContainer); - main_vb->add_child(hb_menu); + top_menu->add_child(memnew(Label(TTR("Preview:")))); + top_menu->add_spacer(false); theme_menu = memnew(MenuButton); - theme_menu->set_text(TTR("Edit theme...")); - theme_menu->set_flat(false); + theme_menu->set_text(TTR("Edit Theme")); theme_menu->set_tooltip(TTR("Theme editing menu.")); theme_menu->get_popup()->add_item(TTR("Add Item"), POPUP_ADD); theme_menu->get_popup()->add_item(TTR("Add Class Items"), POPUP_CLASS_ADD); @@ -643,27 +626,60 @@ ThemeEditor::ThemeEditor() { theme_menu->get_popup()->add_item(TTR("Create Empty Template"), POPUP_CREATE_EMPTY); theme_menu->get_popup()->add_item(TTR("Create Empty Editor Template"), POPUP_CREATE_EDITOR_EMPTY); theme_menu->get_popup()->add_item(TTR("Create From Current Editor Theme"), POPUP_IMPORT_EDITOR_THEME); - add_child(theme_menu); - theme_menu->set_position(Vector2(3, 3) * EDSCALE); + top_menu->add_child(theme_menu); theme_menu->get_popup()->connect("id_pressed", this, "_theme_menu_cbk"); + scroll = memnew(ScrollContainer); + add_child(scroll); + scroll->set_theme(Theme::get_default()); + scroll->set_enable_v_scroll(true); + scroll->set_enable_h_scroll(false); + scroll->set_v_size_flags(SIZE_EXPAND_FILL); + + main_container = memnew(MarginContainer); + scroll->add_child(main_container); + main_container->set_theme(Theme::get_default()); + main_container->set_clip_contents(true); + main_container->set_custom_minimum_size(Size2(700, 0) * EDSCALE); + main_container->set_v_size_flags(SIZE_EXPAND_FILL); + main_container->set_h_size_flags(SIZE_EXPAND_FILL); + + //// Preview Controls //// + + Panel *panel = memnew(Panel); + main_container->add_child(panel); + + MarginContainer *mc = memnew(MarginContainer); + main_container->add_child(mc); + mc->add_constant_override("margin_right", 4 * EDSCALE); + mc->add_constant_override("margin_top", 4 * EDSCALE); + mc->add_constant_override("margin_left", 4 * EDSCALE); + mc->add_constant_override("margin_bottom", 4 * EDSCALE); + HBoxContainer *main_hb = memnew(HBoxContainer); - main_vb->add_child(main_hb); + mc->add_child(main_hb); VBoxContainer *first_vb = memnew(VBoxContainer); - first_vb->set_h_size_flags(SIZE_EXPAND_FILL); main_hb->add_child(first_vb); - - //main_panel->add_child(panel); - //panel->set_anchors_and_margins_preset(Control::PRESET_WIDE); - //panel->set_margin( MARGIN_TOP,20 ); + first_vb->set_h_size_flags(SIZE_EXPAND_FILL); + first_vb->add_constant_override("separation", 10 * EDSCALE); first_vb->add_child(memnew(Label("Label"))); first_vb->add_child(memnew(Button("Button"))); + Button *bt = memnew(Button); + bt->set_text(TTR("Toggle Button")); + bt->set_toggle_mode(true); + bt->set_pressed(true); + first_vb->add_child(bt); + bt = memnew(Button); + bt->set_text(TTR("Disabled Button")); + bt->set_disabled(true); + first_vb->add_child(bt); ToolButton *tb = memnew(ToolButton); tb->set_text("ToolButton"); first_vb->add_child(tb); + CheckButton *cb = memnew(CheckButton); cb->set_text("CheckButton"); first_vb->add_child(cb); @@ -671,23 +687,11 @@ ThemeEditor::ThemeEditor() { cbx->set_text("CheckBox"); first_vb->add_child(cbx); - VBoxContainer *bg = memnew(VBoxContainer); - bg->set_v_size_flags(SIZE_EXPAND_FILL); - VBoxContainer *gbvb = memnew(VBoxContainer); - gbvb->set_v_size_flags(SIZE_EXPAND_FILL); - CheckBox *rbx1 = memnew(CheckBox); - rbx1->set_text(TTR("CheckBox Radio1")); - rbx1->set_pressed(true); - gbvb->add_child(rbx1); - CheckBox *rbx2 = memnew(CheckBox); - rbx2->set_text(TTR("CheckBox Radio2")); - gbvb->add_child(rbx2); - bg->add_child(gbvb); - first_vb->add_child(bg); - MenuButton *test_menu_button = memnew(MenuButton); test_menu_button->set_text("MenuButton"); test_menu_button->get_popup()->add_item(TTR("Item")); + test_menu_button->get_popup()->add_item(TTR("Disabled Item")); + test_menu_button->get_popup()->set_item_disabled(1, true); test_menu_button->get_popup()->add_separator(); test_menu_button->get_popup()->add_check_item(TTR("Check Item")); test_menu_button->get_popup()->add_check_item(TTR("Checked Item")); @@ -696,6 +700,14 @@ ThemeEditor::ThemeEditor() { test_menu_button->get_popup()->add_radio_check_item(TTR("Radio Item")); test_menu_button->get_popup()->add_radio_check_item(TTR("Checked Radio Item")); test_menu_button->get_popup()->set_item_checked(6, true); + test_menu_button->get_popup()->add_separator(TTR("Named Sep.")); + + PopupMenu *test_submenu = memnew(PopupMenu); + test_menu_button->get_popup()->add_child(test_submenu); + test_submenu->set_name("submenu"); + test_menu_button->get_popup()->add_submenu_item(TTR("Submenu"), "submenu"); + test_submenu->add_item(TTR("Item 1")); + test_submenu->add_item(TTR("Item 2")); first_vb->add_child(test_menu_button); OptionButton *test_option_button = memnew(OptionButton); @@ -705,21 +717,7 @@ ThemeEditor::ThemeEditor() { test_option_button->add_item(TTR("Many")); test_option_button->add_item(TTR("Options")); first_vb->add_child(test_option_button); - - ColorPickerButton *cpb = memnew(ColorPickerButton); - first_vb->add_child(cpb); - - first_vb->add_child(memnew(HSeparator)); - first_vb->add_child(memnew(HSlider)); - first_vb->add_child(memnew(HScrollBar)); - first_vb->add_child(memnew(SpinBox)); - ProgressBar *pb = memnew(ProgressBar); - pb->set_value(50); - first_vb->add_child(pb); - Panel *pn = memnew(Panel); - pn->set_custom_minimum_size(Size2(40, 40) * EDSCALE); - first_vb->add_child(pn); - first_vb->add_constant_override("separation", 10 * EDSCALE); + first_vb->add_child(memnew(ColorPickerButton)); VBoxContainer *second_vb = memnew(VBoxContainer); second_vb->set_h_size_flags(SIZE_EXPAND_FILL); @@ -728,50 +726,48 @@ ThemeEditor::ThemeEditor() { LineEdit *le = memnew(LineEdit); le->set_text("LineEdit"); second_vb->add_child(le); + le = memnew(LineEdit); + le->set_text(TTR("Disabled LineEdit")); + le->set_editable(false); + second_vb->add_child(le); TextEdit *te = memnew(TextEdit); te->set_text("TextEdit"); - //te->set_v_size_flags(SIZE_EXPAND_FILL); - te->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + te->set_custom_minimum_size(Size2(0, 100) * EDSCALE); second_vb->add_child(te); + second_vb->add_child(memnew(SpinBox)); - Tree *test_tree = memnew(Tree); - second_vb->add_child(test_tree); - test_tree->set_custom_minimum_size(Size2(0, 160) * EDSCALE); - - TreeItem *item = test_tree->create_item(); - item->set_editable(0, true); - item->set_text(0, "Tree"); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - item->set_editable(0, true); - item->set_text(0, "Check"); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_range_config(0, 0, 20, 0.1); - item->set_range(0, 2); - item = test_tree->create_item(test_tree->get_root()); - item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); - item->set_editable(0, true); - item->set_text(0, TTR("Has,Many,Options")); - item->set_range(0, 2); + HBoxContainer *vhb = memnew(HBoxContainer); + second_vb->add_child(vhb); + vhb->set_custom_minimum_size(Size2(0, 100) * EDSCALE); + vhb->add_child(memnew(VSlider)); + VScrollBar *vsb = memnew(VScrollBar); + vsb->set_page(25); + vhb->add_child(vsb); + vhb->add_child(memnew(VSeparator)); + VBoxContainer *hvb = memnew(VBoxContainer); + vhb->add_child(hvb); + hvb->set_alignment(ALIGN_CENTER); + hvb->set_h_size_flags(SIZE_EXPAND_FILL); + hvb->add_child(memnew(HSlider)); + HScrollBar *hsb = memnew(HScrollBar); + hsb->set_page(25); + hvb->add_child(hsb); + HSlider *hs = memnew(HSlider); + hs->set_editable(false); + hvb->add_child(hs); + hvb->add_child(memnew(HSeparator)); + ProgressBar *pb = memnew(ProgressBar); + pb->set_value(50); + hvb->add_child(pb); VBoxContainer *third_vb = memnew(VBoxContainer); third_vb->set_h_size_flags(SIZE_EXPAND_FILL); - third_vb->add_constant_override("separation", 10); - + third_vb->add_constant_override("separation", 10 * EDSCALE); main_hb->add_child(third_vb); - HBoxContainer *vhb = memnew(HBoxContainer); - vhb->set_custom_minimum_size(Size2(0, 160) * EDSCALE); - vhb->add_child(memnew(VSeparator)); - vhb->add_child(memnew(VSlider)); - vhb->add_child(memnew(VScrollBar)); - third_vb->add_child(vhb); - TabContainer *tc = memnew(TabContainer); third_vb->add_child(tc); - tc->set_custom_minimum_size(Size2(0, 160) * EDSCALE); + tc->set_custom_minimum_size(Size2(0, 135) * EDSCALE); Control *tcc = memnew(Control); tcc->set_name(TTR("Tab 1")); tc->add_child(tcc); @@ -781,9 +777,41 @@ ThemeEditor::ThemeEditor() { tcc = memnew(Control); tcc->set_name(TTR("Tab 3")); tc->add_child(tcc); + tc->set_tab_disabled(2, true); + + Tree *test_tree = memnew(Tree); + third_vb->add_child(test_tree); + test_tree->set_custom_minimum_size(Size2(0, 175) * EDSCALE); + test_tree->add_constant_override("draw_relationship_lines", 1); + + TreeItem *item = test_tree->create_item(); + item->set_text(0, "Tree"); + item = test_tree->create_item(test_tree->get_root()); + item->set_text(0, "Item"); + item = test_tree->create_item(test_tree->get_root()); + item->set_editable(0, true); + item->set_text(0, TTR("Editable Item")); + TreeItem *sub_tree = test_tree->create_item(test_tree->get_root()); + sub_tree->set_text(0, TTR("Subtree")); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_editable(0, true); + item->set_text(0, "Check Item"); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_range_config(0, 0, 20, 0.1); + item->set_range(0, 2); + item = test_tree->create_item(sub_tree); + item->set_cell_mode(0, TreeItem::CELL_MODE_RANGE); + item->set_editable(0, true); + item->set_text(0, TTR("Has,Many,Options")); + item->set_range(0, 2); main_hb->add_constant_override("separation", 20 * EDSCALE); + //////// + add_del_dialog = memnew(ConfirmationDialog); add_del_dialog->hide(); add_child(add_del_dialog); @@ -802,6 +830,7 @@ ThemeEditor::ThemeEditor() { type_edit->set_h_size_flags(SIZE_EXPAND_FILL); type_hbc->add_child(type_edit); type_menu = memnew(MenuButton); + type_menu->set_flat(false); type_menu->set_text(".."); type_hbc->add_child(type_menu); @@ -819,6 +848,7 @@ ThemeEditor::ThemeEditor() { name_edit->set_h_size_flags(SIZE_EXPAND_FILL); name_hbc->add_child(name_edit); name_menu = memnew(MenuButton); + type_menu->set_flat(false); name_menu->set_text(".."); name_hbc->add_child(name_menu); @@ -844,19 +874,14 @@ ThemeEditor::ThemeEditor() { file_dialog->add_filter("*.theme ; Theme File"); add_child(file_dialog); file_dialog->connect("file_selected", this, "_save_template_cbk"); - - //MenuButton *name_menu; - //LineEdit *name_edit; } void ThemeEditorPlugin::edit(Object *p_node) { if (Object::cast_to<Theme>(p_node)) { - theme_editor->show(); theme_editor->edit(Object::cast_to<Theme>(p_node)); } else { theme_editor->edit(Ref<Theme>()); - theme_editor->hide(); } } @@ -871,11 +896,11 @@ void ThemeEditorPlugin::make_visible(bool p_visible) { theme_editor->set_process(true); button->show(); editor->make_bottom_panel_item_visible(theme_editor); - } else { theme_editor->set_process(false); if (theme_editor->is_visible_in_tree()) editor->hide_bottom_panel(); + button->hide(); } } @@ -886,7 +911,6 @@ ThemeEditorPlugin::ThemeEditorPlugin(EditorNode *p_node) { theme_editor = memnew(ThemeEditor); theme_editor->set_custom_minimum_size(Size2(0, 200)); - //p_node->get_viewport()->add_child(theme_editor); button = editor->add_bottom_panel_item(TTR("Theme"), theme_editor); button->hide(); } diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index 352988d69e..cc236907a9 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -33,6 +33,7 @@ #include "scene/gui/check_box.h" #include "scene/gui/file_dialog.h" +#include "scene/gui/margin_container.h" #include "scene/gui/option_button.h" #include "scene/gui/scroll_container.h" #include "scene/gui/texture_rect.h" @@ -40,12 +41,12 @@ #include "editor/editor_node.h" -class ThemeEditor : public Control { +class ThemeEditor : public VBoxContainer { - GDCLASS(ThemeEditor, Control); + GDCLASS(ThemeEditor, VBoxContainer); ScrollContainer *scroll; - VBoxContainer *main_vb; + MarginContainer *main_container; Ref<Theme> theme; EditorFileDialog *file_dialog; diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index a967374eb9..29a54f815d 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -66,6 +66,11 @@ void TileMapEditor::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { + paint_button->set_icon(get_icon("Edit", "EditorIcons")); + bucket_fill_button->set_icon(get_icon("Bucket", "EditorIcons")); + picker_button->set_icon(get_icon("ColorPick", "EditorIcons")); + select_button->set_icon(get_icon("ActionCopy", "EditorIcons")); + rotate_left_button->set_icon(get_icon("Rotate270", "EditorIcons")); rotate_right_button->set_icon(get_icon("Rotate90", "EditorIcons")); flip_horizontal_button->set_icon(get_icon("MirrorX", "EditorIcons")); @@ -76,9 +81,6 @@ void TileMapEditor::_notification(int p_what) { search_box->set_clear_button_enabled(true); PopupMenu *p = options->get_popup(); - p->set_item_icon(p->get_item_index(OPTION_PAINTING), get_icon("Edit", "EditorIcons")); - p->set_item_icon(p->get_item_index(OPTION_PICK_TILE), get_icon("ColorPick", "EditorIcons")); - p->set_item_icon(p->get_item_index(OPTION_SELECT), get_icon("ActionCopy", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_CUT), get_icon("ActionCut", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_COPY), get_icon("Duplicate", "EditorIcons")); p->set_item_icon(p->get_item_index(OPTION_ERASE_SELECTION), get_icon("Remove", "EditorIcons")); @@ -87,37 +89,54 @@ void TileMapEditor::_notification(int p_what) { } } -void TileMapEditor::_menu_option(int p_option) { - - switch (p_option) { - - case OPTION_PAINTING: { - // NOTE: We do not set tool = TOOL_PAINTING as this begins painting - // immediately without pressing the left mouse button first - tool = TOOL_NONE; +void TileMapEditor::_update_button_tool() { - CanvasItemEditor::get_singleton()->update_viewport(); + ToolButton *tb[4] = { paint_button, bucket_fill_button, picker_button, select_button }; + // Unpress all buttons + for (int i = 0; i < 4; i++) { + tb[i]->set_pressed(false); + } + // Press the good button + switch (tool) { + case TOOL_NONE: + case TOOL_PAINTING: { + paint_button->set_pressed(true); } break; - case OPTION_BUCKET: { - - tool = TOOL_BUCKET; - - CanvasItemEditor::get_singleton()->update_viewport(); + case TOOL_BUCKET: { + bucket_fill_button->set_pressed(true); + } break; + case TOOL_PICKING: { + picker_button->set_pressed(true); } break; - case OPTION_PICK_TILE: { + case TOOL_SELECTING: { + select_button->set_pressed(true); + } break; + default: + break; + } - tool = TOOL_PICKING; + if (tool != TOOL_PICKING) + last_tool = tool; +} - CanvasItemEditor::get_singleton()->update_viewport(); - } break; - case OPTION_SELECT: { +void TileMapEditor::_button_tool_select(int p_tool) { + tool = (Tool)p_tool; + _update_button_tool(); + switch (tool) { + case TOOL_SELECTING: { - tool = TOOL_SELECTING; selection_active = false; - - CanvasItemEditor::get_singleton()->update_viewport(); } break; + default: + break; + } + CanvasItemEditor::get_singleton()->update_viewport(); +} + +void TileMapEditor::_menu_option(int p_option) { + + switch (p_option) { case OPTION_COPY: { _update_copydata(); @@ -168,6 +187,7 @@ void TileMapEditor::_menu_option(int p_option) { } } break; } + _update_button_tool(); } void TileMapEditor::_palette_selected(int index) { @@ -220,22 +240,23 @@ void TileMapEditor::set_selected_tiles(Vector<int> p_tiles) { palette->ensure_current_is_visible(); } -void TileMapEditor::_create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new) { +Dictionary TileMapEditor::_create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord) { - Dictionary cell_old; - Dictionary cell_new; + Dictionary cell; - cell_old["id"] = p_cell_old.idx; - cell_old["flip_h"] = p_cell_old.xf; - cell_old["flip_y"] = p_cell_old.yf; - cell_old["transpose"] = p_cell_old.tr; - cell_old["auto_coord"] = p_cell_old.ac; + cell["id"] = tile; + cell["flip_h"] = flip_x; + cell["flip_y"] = flip_y; + cell["transpose"] = transpose; + cell["auto_coord"] = autotile_coord; - cell_new["id"] = p_cell_new.idx; - cell_new["flip_h"] = p_cell_new.xf; - cell_new["flip_y"] = p_cell_new.yf; - cell_new["transpose"] = p_cell_new.tr; - cell_new["auto_coord"] = p_cell_new.ac; + return cell; +} + +void TileMapEditor::_create_set_cell_undo_redo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new) { + + Dictionary cell_old = _create_cell_dictionary(p_cell_old.idx, p_cell_old.xf, p_cell_old.yf, p_cell_old.tr, p_cell_old.ac); + Dictionary cell_new = _create_cell_dictionary(p_cell_new.idx, p_cell_new.xf, p_cell_new.yf, p_cell_new.tr, p_cell_new.ac); undo_redo->add_undo_method(node, "_set_celld", p_vec, cell_old); undo_redo->add_do_method(node, "_set_celld", p_vec, cell_new); @@ -251,7 +272,7 @@ void TileMapEditor::_finish_undo() { if (undo_data.size()) { for (Map<Point2i, CellOp>::Element *E = undo_data.front(); E; E = E->next()) { - _create_set_cell_undo(E->key(), E->get(), _get_op_from_cell(E->key())); + _create_set_cell_undo_redo(E->key(), E->get(), _get_op_from_cell(E->key())); } undo_data.clear(); @@ -299,7 +320,8 @@ void TileMapEditor::_set_cell(const Point2i &p_pos, Vector<int> p_values, bool p } } - node->set_cell(p_pos.x, p_pos.y, p_value, p_flip_h, p_flip_v, p_transpose, p_autotile_coord); + node->_set_celld(p_pos, _create_cell_dictionary(p_value, p_flip_h, p_flip_v, p_transpose, p_autotile_coord)); + if (manual_autotile || (p_value != -1 && node->get_tileset()->tile_get_tile_mode(p_value) == TileSet::ATLAS_TILE)) { if (current != -1) { node->set_cell_autotile_coord(p_pos.x, p_pos.y, position); @@ -477,17 +499,17 @@ void TileMapEditor::_update_palette() { if (sel_tile != TileMap::INVALID_CELL) { if ((manual_autotile && tileset->tile_get_tile_mode(sel_tile) == TileSet::AUTO_TILE) || tileset->tile_get_tile_mode(sel_tile) == TileSet::ATLAS_TILE) { - const Map<Vector2, uint16_t> &tiles = tileset->autotile_get_bitmask_map(sel_tile); + const Map<Vector2, uint32_t> &tiles2 = tileset->autotile_get_bitmask_map(sel_tile); - Vector<Vector2> entries; - for (const Map<Vector2, uint16_t>::Element *E = tiles.front(); E; E = E->next()) { - entries.push_back(E->key()); + Vector<Vector2> entries2; + for (const Map<Vector2, uint32_t>::Element *E = tiles2.front(); E; E = E->next()) { + entries2.push_back(E->key()); } - entries.sort(); + entries2.sort(); Ref<Texture> tex = tileset->tile_get_texture(sel_tile); - for (int i = 0; i < entries.size(); i++) { + for (int i = 0; i < entries2.size(); i++) { manual_palette->add_item(String()); @@ -496,7 +518,7 @@ void TileMapEditor::_update_palette() { Rect2 region = tileset->tile_get_region(sel_tile); int spacing = tileset->autotile_get_spacing(sel_tile); region.size = tileset->autotile_get_size(sel_tile); // !! - region.position += (region.size + Vector2(spacing, spacing)) * entries[i]; + region.position += (region.size + Vector2(spacing, spacing)) * entries2[i]; if (!region.has_no_area()) manual_palette->set_item_icon_region(manual_palette->get_item_count() - 1, region); @@ -504,16 +526,16 @@ void TileMapEditor::_update_palette() { manual_palette->set_item_icon(manual_palette->get_item_count() - 1, tex); } - manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries[i]); + manual_palette->set_item_metadata(manual_palette->get_item_count() - 1, entries2[i]); } } } if (manual_palette->get_item_count() > 0) { // Only show the manual palette if at least tile exists in it - int selected = manual_palette->get_current(); - if (selected == -1) selected = 0; - manual_palette->set_current(selected); + int selected2 = manual_palette->get_current(); + if (selected2 == -1) selected2 = 0; + manual_palette->set_current(selected2); manual_palette->show(); } @@ -928,11 +950,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (mb->get_shift()) { -#ifdef APPLE_STYLE_KEYS if (mb->get_command()) -#else - if (mb->get_control()) -#endif tool = TOOL_RECTANGLE_PAINT; else tool = TOOL_LINE_PAINT; @@ -940,20 +958,20 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { selection_active = false; rectangle_begin = over_tile; + _update_button_tool(); return true; } -#ifdef APPLE_STYLE_KEYS + if (mb->get_command()) { -#else - if (mb->get_control()) { -#endif tool = TOOL_PICKING; _pick_tile(over_tile); + _update_button_tool(); return true; } tool = TOOL_PAINTING; + _update_button_tool(); } if (tool == TOOL_PAINTING) { @@ -975,6 +993,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { rectangle_begin = over_tile; } + _update_button_tool(); return true; } else { @@ -1071,6 +1090,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } tool = TOOL_NONE; + _update_button_tool(); return true; } @@ -1086,6 +1106,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1096,6 +1117,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1108,11 +1130,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _start_undo(TTR("Erase TileMap")); if (mb->get_shift()) { -#ifdef APPLE_STYLE_KEYS if (mb->get_command()) -#else - if (mb->get_control()) -#endif tool = TOOL_RECTANGLE_ERASE; else tool = TOOL_LINE_ERASE; @@ -1126,6 +1144,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { _set_cell(local, invalid_cell); } + _update_button_tool(); return true; } @@ -1140,6 +1159,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; + _update_button_tool(); return true; } else if (tool == TOOL_BUCKET) { @@ -1157,7 +1177,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (points.size() == 0) return false; - undo_redo->create_action("Bucket Fill"); + undo_redo->create_action(TTR("Bucket Fill")); undo_redo->add_do_method(this, "_erase_points", points); undo_redo->add_undo_method(this, "_fill_points", points, pop); @@ -1314,6 +1334,14 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { if (k.is_valid() && k->is_pressed()) { + if (last_tool == TOOL_NONE && tool == TOOL_PICKING && k->get_scancode() == KEY_SHIFT && k->get_command()) { + // trying to draw a rectangle with the painting tool, so change to the correct tool + tool = last_tool; + + CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); + } + if (k->get_scancode() == KEY_ESCAPE) { if (tool == TOOL_PASTING) @@ -1325,6 +1353,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } @@ -1339,17 +1368,20 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_NONE; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/bucket_fill", p_event)) { tool = TOOL_BUCKET; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/erase_selection", p_event)) { _menu_option(OPTION_ERASE_SELECTION); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/select", p_event)) { @@ -1358,6 +1390,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } if (ED_IS_SHORTCUT("tile_map_editor/copy_selection", p_event)) { @@ -1368,6 +1401,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } } @@ -1384,6 +1418,7 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { tool = TOOL_PASTING; CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); return true; } } @@ -1411,8 +1446,30 @@ bool TileMapEditor::forward_gui_input(const Ref<InputEvent> &p_event) { CanvasItemEditor::get_singleton()->update_viewport(); return true; } - } + } else if (k.is_valid()) { // release event + + if (tool == TOOL_NONE) { + + if (k->get_scancode() == KEY_SHIFT && k->get_command()) { + + tool = TOOL_PICKING; + _update_button_tool(); + } + } else if (tool == TOOL_PICKING) { + +#ifdef APPLE_STYLE_KEYS + if (k->get_scancode() == KEY_META) { +#else + if (k->get_scancode() == KEY_CONTROL) { +#endif + // go back to that last tool if KEY_CONTROL was released + tool = last_tool; + CanvasItemEditor::get_singleton()->update_viewport(); + _update_button_tool(); + } + } + } return false; } @@ -1434,9 +1491,9 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { aabb.expand_to(node->world_to_map(xform_inv.xform(screen_size))); Rect2i si = aabb.grow(1.0); - if (node->get_half_offset() != TileMap::HALF_OFFSET_X) { + if (node->get_half_offset() != TileMap::HALF_OFFSET_X && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_X) { - int max_lines = 2000; //avoid crash if size too smal + int max_lines = 2000; //avoid crash if size too small for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) { @@ -1450,7 +1507,7 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { } } else { - int max_lines = 10000; //avoid crash if size too smal + int max_lines = 10000; //avoid crash if size too small for (int i = (si.position.x) - 1; i <= (si.position.x + si.size.x); i++) { @@ -1458,23 +1515,26 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Vector2 ofs; if (ABS(j) & 1) { - ofs = cell_xf[0] * 0.5; + ofs = cell_xf[0] * (node->get_half_offset() == TileMap::HALF_OFFSET_X ? 0.5 : -0.5); } Vector2 from = xform.xform(node->map_to_world(Vector2(i, j), true) + ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(i, j + 1), true) + ofs); + Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); p_overlay->draw_line(from, to, col, 1); - if (max_lines-- == 0) + if (--max_lines == 0) break; } + if (max_lines == 0) + break; } } int max_lines = 10000; //avoid crash if size too smal - if (node->get_half_offset() != TileMap::HALF_OFFSET_Y) { + if (node->get_half_offset() != TileMap::HALF_OFFSET_Y && node->get_half_offset() != TileMap::HALF_OFFSET_NEGATIVE_Y) { for (int i = (si.position.y) - 1; i <= (si.position.y + si.size.y); i++) { @@ -1495,17 +1555,20 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { Vector2 ofs; if (ABS(j) & 1) { - ofs = cell_xf[1] * 0.5; + ofs = cell_xf[1] * (node->get_half_offset() == TileMap::HALF_OFFSET_Y ? 0.5 : -0.5); } Vector2 from = xform.xform(node->map_to_world(Vector2(j, i), true) + ofs); Vector2 to = xform.xform(node->map_to_world(Vector2(j + 1, i), true) + ofs); + Color col = i == 0 ? Color(1, 0.8, 0.2, 0.5) : Color(1, 0.3, 0.1, 0.2); p_overlay->draw_line(from, to, col, 1); - if (max_lines-- == 0) + if (--max_lines == 0) break; } + if (max_lines == 0) + break; } } } @@ -1533,8 +1596,12 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { for (int i = 0; i < 4; i++) { if (node->get_half_offset() == TileMap::HALF_OFFSET_X && ABS(over_tile.y) & 1) endpoints[i] += cell_xf[0] * 0.5; + if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_X && ABS(over_tile.y) & 1) + endpoints[i] += cell_xf[0] * -0.5; if (node->get_half_offset() == TileMap::HALF_OFFSET_Y && ABS(over_tile.x) & 1) endpoints[i] += cell_xf[1] * 0.5; + if (node->get_half_offset() == TileMap::HALF_OFFSET_NEGATIVE_Y && ABS(over_tile.x) & 1) + endpoints[i] += cell_xf[1] * -0.5; endpoints[i] = xform.xform(endpoints[i]); } Color col; @@ -1686,6 +1753,7 @@ void TileMapEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_entered"), &TileMapEditor::_text_entered); ClassDB::bind_method(D_METHOD("_text_changed"), &TileMapEditor::_text_changed); ClassDB::bind_method(D_METHOD("_sbox_input"), &TileMapEditor::_sbox_input); + ClassDB::bind_method(D_METHOD("_button_tool_select"), &TileMapEditor::_button_tool_select); ClassDB::bind_method(D_METHOD("_menu_option"), &TileMapEditor::_menu_option); ClassDB::bind_method(D_METHOD("_canvas_mouse_enter"), &TileMapEditor::_canvas_mouse_enter); ClassDB::bind_method(D_METHOD("_canvas_mouse_exit"), &TileMapEditor::_canvas_mouse_exit); @@ -1866,37 +1934,66 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { // Add menu items toolbar = memnew(HBoxContainer); - toolbar->set_h_size_flags(SIZE_EXPAND_FILL); - toolbar->set_alignment(BoxContainer::ALIGN_END); toolbar->hide(); CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar); + // Separator + toolbar->add_child(memnew(VSeparator)); + + // Tools + paint_button = memnew(ToolButton); + paint_button->set_shortcut(ED_SHORTCUT("tile_map_editor/paint_tile", TTR("Paint Tile"), KEY_P)); + paint_button->set_tooltip(TTR("Shift+RMB: Line Draw\nShift+Ctrl+RMB: Rectangle Paint")); + paint_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_NONE)); + paint_button->set_toggle_mode(true); + toolbar->add_child(paint_button); + + bucket_fill_button = memnew(ToolButton); + bucket_fill_button->set_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G)); + bucket_fill_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_BUCKET)); + bucket_fill_button->set_toggle_mode(true); + toolbar->add_child(bucket_fill_button); + + picker_button = memnew(ToolButton); + picker_button->set_shortcut(ED_SHORTCUT("tile_map_editor/pick_tile", TTR("Pick Tile"), KEY_CONTROL)); + picker_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_PICKING)); + picker_button->set_toggle_mode(true); + toolbar->add_child(picker_button); + + select_button = memnew(ToolButton); + select_button->set_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B)); + select_button->connect("pressed", this, "_button_tool_select", make_binds(TOOL_SELECTING)); + select_button->set_toggle_mode(true); + toolbar->add_child(select_button); + + _update_button_tool(); + + // Container to the right of the toolbar + toolbar_right = memnew(HBoxContainer); + toolbar_right->hide(); + toolbar_right->set_h_size_flags(SIZE_EXPAND_FILL); + toolbar_right->set_alignment(BoxContainer::ALIGN_END); + CanvasItemEditor::get_singleton()->add_control_to_menu_panel(toolbar_right); + // Tile position tile_info = memnew(Label); - toolbar->add_child(tile_info); + toolbar_right->add_child(tile_info); + // Menu options = memnew(MenuButton); options->set_text("TileMap"); options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("TileMap", "EditorIcons")); options->set_process_unhandled_key_input(false); + toolbar_right->add_child(options); PopupMenu *p = options->get_popup(); - - p->add_shortcut(ED_SHORTCUT("tile_map_editor/paint_tile", TTR("Paint Tile"), KEY_P), OPTION_PAINTING); - p->add_shortcut(ED_SHORTCUT("tile_map_editor/bucket_fill", TTR("Bucket Fill"), KEY_G), OPTION_BUCKET); - p->add_separator(); - p->add_item(TTR("Pick Tile"), OPTION_PICK_TILE, KEY_CONTROL); - p->add_separator(); - p->add_shortcut(ED_SHORTCUT("tile_map_editor/select", TTR("Select"), KEY_MASK_CMD + KEY_B), OPTION_SELECT); p->add_shortcut(ED_SHORTCUT("tile_map_editor/cut_selection", TTR("Cut Selection"), KEY_MASK_CMD + KEY_X), OPTION_CUT); p->add_shortcut(ED_SHORTCUT("tile_map_editor/copy_selection", TTR("Copy Selection"), KEY_MASK_CMD + KEY_C), OPTION_COPY); p->add_shortcut(ED_GET_SHORTCUT("tile_map_editor/erase_selection"), OPTION_ERASE_SELECTION); p->add_separator(); p->add_item(TTR("Fix Invalid Tiles"), OPTION_FIX_INVALID); - p->connect("id_pressed", this, "_menu_option"); - toolbar->add_child(options); rotate_left_button = memnew(ToolButton); rotate_left_button->set_tooltip(TTR("Rotate left")); rotate_left_button->set_focus_mode(FOCUS_NONE); @@ -1970,10 +2067,12 @@ void TileMapEditorPlugin::make_visible(bool p_visible) { tile_map_editor->show(); tile_map_editor->get_toolbar()->show(); + tile_map_editor->get_toolbar_right()->show(); } else { tile_map_editor->hide(); tile_map_editor->get_toolbar()->hide(); + tile_map_editor->get_toolbar_right()->hide(); tile_map_editor->edit(NULL); } } diff --git a/editor/plugins/tile_map_editor_plugin.h b/editor/plugins/tile_map_editor_plugin.h index b30426eabe..fcdada1111 100644 --- a/editor/plugins/tile_map_editor_plugin.h +++ b/editor/plugins/tile_map_editor_plugin.h @@ -66,12 +66,8 @@ class TileMapEditor : public VBoxContainer { enum Options { - OPTION_BUCKET, - OPTION_PICK_TILE, - OPTION_SELECT, OPTION_COPY, OPTION_ERASE_SELECTION, - OPTION_PAINTING, OPTION_FIX_INVALID, OPTION_CUT }; @@ -90,10 +86,16 @@ class TileMapEditor : public VBoxContainer { ItemList *manual_palette; HBoxContainer *toolbar; + HBoxContainer *toolbar_right; Label *tile_info; MenuButton *options; + ToolButton *paint_button; + ToolButton *bucket_fill_button; + ToolButton *picker_button; + ToolButton *select_button; + ToolButton *flip_horizontal_button; ToolButton *flip_vertical_button; ToolButton *rotate_left_button; @@ -103,6 +105,7 @@ class TileMapEditor : public VBoxContainer { CheckBox *manual_button; Tool tool; + Tool last_tool; bool selection_active; bool mouse_over; @@ -184,13 +187,16 @@ class TileMapEditor : public VBoxContainer { void _text_changed(const String &p_text); void _sbox_input(const Ref<InputEvent> &p_ie); void _update_palette(); + void _update_button_tool(); + void _button_tool_select(int p_tool); void _menu_option(int p_option); void _palette_selected(int index); void _palette_multi_selected(int index, bool selected); + Dictionary _create_cell_dictionary(int tile, bool flip_x, bool flip_y, bool transpose, Vector2 autotile_coord); void _start_undo(const String &p_action); void _finish_undo(); - void _create_set_cell_undo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new); + void _create_set_cell_undo_redo(const Vector2 &p_vec, const CellOp &p_cell_old, const CellOp &p_cell_new); void _set_cell(const Point2i &p_pos, Vector<int> p_values, bool p_flip_h = false, bool p_flip_v = false, bool p_transpose = false, const Point2i p_autotile_coord = Point2()); void _canvas_mouse_enter(); @@ -210,6 +216,7 @@ protected: public: HBoxContainer *get_toolbar() const { return toolbar; } + HBoxContainer *get_toolbar_right() const { return toolbar_right; } bool forward_gui_input(const Ref<InputEvent> &p_event); void forward_canvas_draw_over_viewport(Control *p_overlay); diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index 1a687a4009..93c6614841 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -201,11 +201,13 @@ void TileSetEditor::_bind_methods() { ClassDB::bind_method("_zoom_out", &TileSetEditor::_zoom_out); ClassDB::bind_method("_zoom_reset", &TileSetEditor::_zoom_reset); ClassDB::bind_method("_select_edited_shape_coord", &TileSetEditor::_select_edited_shape_coord); + ClassDB::bind_method("_sort_tiles", &TileSetEditor::_sort_tiles); ClassDB::bind_method("edit", &TileSetEditor::edit); ClassDB::bind_method("add_texture", &TileSetEditor::add_texture); ClassDB::bind_method("remove_texture", &TileSetEditor::remove_texture); ClassDB::bind_method("update_texture_list_icon", &TileSetEditor::update_texture_list_icon); + ClassDB::bind_method("update_workspace_minsize", &TileSetEditor::update_workspace_minsize); } void TileSetEditor::_notification(int p_what) { @@ -232,6 +234,9 @@ void TileSetEditor::_notification(int p_what) { tools[BITMASK_PASTE]->set_icon(get_icon("Override", "EditorIcons")); tools[BITMASK_CLEAR]->set_icon(get_icon("Clear", "EditorIcons")); tools[SHAPE_NEW_POLYGON]->set_icon(get_icon("CollisionPolygon2D", "EditorIcons")); + tools[SHAPE_NEW_RECTANGLE]->set_icon(get_icon("CollisionShape2D", "EditorIcons")); + tools[SELECT_PREVIOUS]->set_icon(get_icon("ArrowLeft", "EditorIcons")); + tools[SELECT_NEXT]->set_icon(get_icon("ArrowRight", "EditorIcons")); tools[SHAPE_DELETE]->set_icon(get_icon("Remove", "EditorIcons")); tools[SHAPE_KEEP_INSIDE_TILE]->set_icon(get_icon("Snap", "EditorIcons")); tools[TOOL_GRID_SNAP]->set_icon(get_icon("SnapGrid", "EditorIcons")); @@ -239,6 +244,7 @@ void TileSetEditor::_notification(int p_what) { tools[ZOOM_1]->set_icon(get_icon("ZoomReset", "EditorIcons")); tools[ZOOM_IN]->set_icon(get_icon("ZoomMore", "EditorIcons")); tools[VISIBLE_INFO]->set_icon(get_icon("InformationSign", "EditorIcons")); + _update_toggle_shape_button(); tool_editmode[EDITMODE_REGION]->set_icon(get_icon("RegionEdit", "EditorIcons")); tool_editmode[EDITMODE_COLLISION]->set_icon(get_icon("StaticBody2D", "EditorIcons")); @@ -322,11 +328,29 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tool_workspacemode[i]->connect("pressed", this, "_on_workspace_mode_changed", varray(i)); tool_hb->add_child(tool_workspacemode[i]); } + Control *spacer = memnew(Control); spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL); tool_hb->add_child(spacer); tool_hb->move_child(spacer, WORKSPACE_CREATE_SINGLE); + tools[SELECT_NEXT] = memnew(ToolButton); + tool_hb->add_child(tools[SELECT_NEXT]); + tool_hb->move_child(tools[SELECT_NEXT], WORKSPACE_CREATE_SINGLE); + tools[SELECT_NEXT]->set_shortcut(ED_SHORTCUT("tileset_editor/next_shape", TTR("Next Coordinate"), KEY_PAGEDOWN)); + tools[SELECT_NEXT]->connect("pressed", this, "_on_tool_clicked", varray(SELECT_NEXT)); + tools[SELECT_NEXT]->set_tooltip(TTR("Select the next shape, subtile, or Tile.")); + tools[SELECT_PREVIOUS] = memnew(ToolButton); + tool_hb->add_child(tools[SELECT_PREVIOUS]); + tool_hb->move_child(tools[SELECT_PREVIOUS], WORKSPACE_CREATE_SINGLE); + tools[SELECT_PREVIOUS]->set_shortcut(ED_SHORTCUT("tileset_editor/previous_shape", TTR("Previous Coordinate"), KEY_PAGEUP)); + tools[SELECT_PREVIOUS]->set_tooltip(TTR("Select the previous shape, subtile, or Tile.")); + tools[SELECT_PREVIOUS]->connect("pressed", this, "_on_tool_clicked", varray(SELECT_PREVIOUS)); + + VSeparator *separator_shape_selection = memnew(VSeparator); + tool_hb->add_child(separator_shape_selection); + tool_hb->move_child(separator_shape_selection, WORKSPACE_CREATE_SINGLE); + tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); workspace_mode = WORKSPACE_EDIT; @@ -377,12 +401,24 @@ TileSetEditor::TileSetEditor(EditorNode *p_editor) { tools[BITMASK_CLEAR]->connect("pressed", this, "_on_tool_clicked", varray(BITMASK_CLEAR)); toolbar->add_child(tools[BITMASK_CLEAR]); + tools[SHAPE_NEW_RECTANGLE] = memnew(ToolButton); + toolbar->add_child(tools[SHAPE_NEW_RECTANGLE]); + tools[SHAPE_NEW_RECTANGLE]->set_toggle_mode(true); + tools[SHAPE_NEW_RECTANGLE]->set_button_group(tg); + tools[SHAPE_NEW_RECTANGLE]->set_tooltip(TTR("Create a new rectangle.")); + tools[SHAPE_NEW_POLYGON] = memnew(ToolButton); toolbar->add_child(tools[SHAPE_NEW_POLYGON]); tools[SHAPE_NEW_POLYGON]->set_toggle_mode(true); tools[SHAPE_NEW_POLYGON]->set_button_group(tg); tools[SHAPE_NEW_POLYGON]->set_tooltip(TTR("Create a new polygon.")); + separator_shape_toggle = memnew(VSeparator); + toolbar->add_child(separator_shape_toggle); + tools[SHAPE_TOGGLE_TYPE] = memnew(ToolButton); + tools[SHAPE_TOGGLE_TYPE]->connect("pressed", this, "_on_tool_clicked", varray(SHAPE_TOGGLE_TYPE)); + toolbar->add_child(tools[SHAPE_TOGGLE_TYPE]); + separator_delete = memnew(VSeparator); toolbar->add_child(separator_delete); tools[SHAPE_DELETE] = memnew(ToolButton); @@ -583,16 +619,15 @@ void TileSetEditor::_on_texture_list_selected(int p_index) { if (get_current_texture().is_valid()) { current_item_index = p_index; preview->set_texture(get_current_texture()); - workspace->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); - workspace_container->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); - workspace_overlay->set_custom_minimum_size(get_current_texture()->get_size() + WORKSPACE_MARGIN * 2); update_workspace_tile_mode(); + update_workspace_minsize(); } else { current_item_index = -1; preview->set_texture(NULL); workspace->set_custom_minimum_size(Size2i()); update_workspace_tile_mode(); } + set_current_tile(-1); workspace->update(); } @@ -637,6 +672,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->hide(); + tools[SHAPE_NEW_RECTANGLE]->hide(); if (workspace_mode == WORKSPACE_EDIT) { separator_delete->show(); @@ -666,6 +702,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->show(); + tools[SHAPE_NEW_RECTANGLE]->show(); separator_delete->show(); tools[SHAPE_DELETE]->show(); @@ -689,6 +726,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->show(); tools[BITMASK_CLEAR]->show(); tools[SHAPE_NEW_POLYGON]->hide(); + tools[SHAPE_NEW_RECTANGLE]->hide(); separator_delete->hide(); tools[SHAPE_DELETE]->hide(); @@ -696,7 +734,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[SHAPE_KEEP_INSIDE_TILE]->hide(); tools[TOOL_SELECT]->set_pressed(true); - tools[TOOL_SELECT]->set_tooltip(TTR("LMB: Set bit on.\nRMB: Set bit off.\nClick on another Tile to edit it.")); + tools[TOOL_SELECT]->set_tooltip(TTR("LMB: Set bit on.\nRMB: Set bit off.\nShift+LMB: Set wildcard bit.\nClick on another Tile to edit it.")); spin_priority->hide(); } break; case EDITMODE_Z_INDEX: @@ -709,6 +747,7 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { tools[BITMASK_PASTE]->hide(); tools[BITMASK_CLEAR]->hide(); tools[SHAPE_NEW_POLYGON]->hide(); + tools[SHAPE_NEW_RECTANGLE]->hide(); separator_delete->hide(); tools[SHAPE_DELETE]->hide(); @@ -731,8 +770,10 @@ void TileSetEditor::_on_edit_mode_changed(int p_edit_mode) { spin_z_index->show(); } } break; - default: {} + default: { + } } + _update_toggle_shape_button(); workspace->update(); } @@ -778,52 +819,92 @@ void TileSetEditor::_on_workspace_draw() { } break; case EDITMODE_BITMASK: { Color c(1, 0, 0, 0.5); + Color ci(0.3, 0.6, 1, 0.5); for (float x = 0; x < region.size.x / (spacing + size.x); x++) { for (float y = 0; y < region.size.y / (spacing + size.y); y++) { Vector2 coord(x, y); Point2 anchor(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); anchor += WORKSPACE_MARGIN; anchor += region.position; - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), coord); if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { - if (mask & TileSet::BIND_TOPLEFT) { + if (mask & TileSet::BIND_IGNORE_TOPLEFT) { + workspace->draw_rect(Rect2(anchor, size / 4), ci); + workspace->draw_rect(Rect2(anchor + size / 4, size / 4), ci); + } else if (mask & TileSet::BIND_TOPLEFT) { workspace->draw_rect(Rect2(anchor, size / 2), c); } - if (mask & TileSet::BIND_TOPRIGHT) { + if (mask & TileSet::BIND_IGNORE_TOPRIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 4), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 3 / 4, size.y / 4), size / 4), ci); + } else if (mask & TileSet::BIND_TOPRIGHT) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, 0), size / 2), c); } - if (mask & TileSet::BIND_BOTTOMLEFT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 4), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 4, size.y * 3 / 4), size / 4), ci); + } else if (mask & TileSet::BIND_BOTTOMLEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 2), size / 2), c); } - if (mask & TileSet::BIND_BOTTOMRIGHT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) { + workspace->draw_rect(Rect2(anchor + size / 2, size / 4), ci); + workspace->draw_rect(Rect2(anchor + size * 3 / 4, size / 4), ci); + } else if (mask & TileSet::BIND_BOTTOMRIGHT) { workspace->draw_rect(Rect2(anchor + size / 2, size / 2), c); } } else { - if (mask & TileSet::BIND_TOPLEFT) { + if (mask & TileSet::BIND_IGNORE_TOPLEFT) { + workspace->draw_rect(Rect2(anchor, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size / 6, size / 6), ci); + } else if (mask & TileSet::BIND_TOPLEFT) { workspace->draw_rect(Rect2(anchor, size / 3), c); } - if (mask & TileSet::BIND_TOP) { + if (mask & TileSet::BIND_IGNORE_TOP) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y / 6), size / 6), ci); + } else if (mask & TileSet::BIND_TOP) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, 0), size / 3), c); } - if (mask & TileSet::BIND_TOPRIGHT) { + if (mask & TileSet::BIND_IGNORE_TOPRIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, 0), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 6), size / 6), ci); + } else if (mask & TileSet::BIND_TOPRIGHT) { workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, 0), size / 3), c); } - if (mask & TileSet::BIND_LEFT) { + if (mask & TileSet::BIND_IGNORE_LEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y / 2), size / 6), ci); + } else if (mask & TileSet::BIND_LEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_CENTER) { + if (mask & TileSet::BIND_IGNORE_CENTER) { + workspace->draw_rect(Rect2(anchor + size / 3, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size / 2, size / 6), ci); + } else if (mask & TileSet::BIND_CENTER) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_RIGHT) { + if (mask & TileSet::BIND_IGNORE_RIGHT) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 4 / 6, size.y / 3), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x * 5 / 6, size.y / 2), size / 6), ci); + } else if (mask & TileSet::BIND_RIGHT) { workspace->draw_rect(Rect2(anchor + Vector2((size.x / 3) * 2, size.y / 3), size / 3), c); } - if (mask & TileSet::BIND_BOTTOMLEFT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMLEFT) { + workspace->draw_rect(Rect2(anchor + Vector2(0, size.y * 4 / 6), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 6, size.y * 5 / 6), size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOMLEFT) { workspace->draw_rect(Rect2(anchor + Vector2(0, (size.y / 3) * 2), size / 3), c); } - if (mask & TileSet::BIND_BOTTOM) { + if (mask & TileSet::BIND_IGNORE_BOTTOM) { + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, size.y * 4 / 6), size / 6), ci); + workspace->draw_rect(Rect2(anchor + Vector2(size.x / 2, size.y * 5 / 6), size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOM) { workspace->draw_rect(Rect2(anchor + Vector2(size.x / 3, (size.y / 3) * 2), size / 3), c); } - if (mask & TileSet::BIND_BOTTOMRIGHT) { + if (mask & TileSet::BIND_IGNORE_BOTTOMRIGHT) { + workspace->draw_rect(Rect2(anchor + size * 4 / 6, size / 6), ci); + workspace->draw_rect(Rect2(anchor + size * 5 / 6, size / 6), ci); + } else if (mask & TileSet::BIND_BOTTOMRIGHT) { workspace->draw_rect(Rect2(anchor + (size / 3) * 2, size / 3), c); } } @@ -842,10 +923,10 @@ void TileSetEditor::_on_workspace_draw() { } break; case EDITMODE_PRIORITY: { spin_priority->set_value(tileset->autotile_get_subtile_priority(get_current_tile(), edited_shape_coord)); - uint16_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord); + uint32_t mask = tileset->autotile_get_bitmask(get_current_tile(), edited_shape_coord); Vector<Vector2> queue_others; int total = 0; - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { if (E->value() == mask) { total += tileset->autotile_get_subtile_priority(get_current_tile(), E->key()); if (E->key() != edited_shape_coord) { @@ -860,7 +941,8 @@ void TileSetEditor::_on_workspace_draw() { spin_z_index->set_value(tileset->autotile_get_z_index(get_current_tile(), edited_shape_coord)); draw_highlight_subtile(edited_shape_coord); } break; - default: {} + default: { + } } } @@ -956,25 +1038,26 @@ void TileSetEditor::_on_workspace_overlay_draw() { tileset->get_tile_list(tiles); for (List<int>::Element *E = tiles->front(); E; E = E->next()) { int t_id = E->get(); - if (tileset->tile_get_texture(t_id)->get_rid() == current_texture_rid) { - Rect2i region = tileset->tile_get_region(t_id); - region.position += WORKSPACE_MARGIN; - region.position *= workspace->get_scale().x; - Color c; - if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) - c = COLOR_SINGLE; - else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) - c = COLOR_AUTOTILE; - else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) - c = COLOR_ATLAS; - String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); - Ref<Font> font = get_font("font", "Label"); - region.set_size(font->get_string_size(tile_id_name)); - workspace_overlay->draw_rect(region, c); - region.position.y += region.size.y - 2; - c = Color(0.1, 0.1, 0.1); - workspace_overlay->draw_string(font, region.position, tile_id_name, c); - } + if (tileset->tile_get_texture(t_id)->get_rid() != current_texture_rid) + continue; + + Rect2 region = tileset->tile_get_region(t_id); + region.position += WORKSPACE_MARGIN; + region.position *= workspace->get_scale().x; + Color c; + if (tileset->tile_get_tile_mode(t_id) == TileSet::SINGLE_TILE) + c = COLOR_SINGLE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::AUTO_TILE) + c = COLOR_AUTOTILE; + else if (tileset->tile_get_tile_mode(t_id) == TileSet::ATLAS_TILE) + c = COLOR_ATLAS; + String tile_id_name = String::num(t_id, 0) + ": " + tileset->tile_get_name(t_id); + Ref<Font> font = get_font("font", "Label"); + region.set_size(font->get_string_size(tile_id_name)); + workspace_overlay->draw_rect(region, c); + region.position.y += region.size.y - 2; + c = Color(0.1, 0.1, 0.1); + workspace_overlay->draw_string(font, region.position, tile_id_name, c); } } @@ -997,6 +1080,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { static bool dragging; static bool erasing; + static bool alternative; draw_edited_region = false; Rect2 current_tile_region = Rect2(); @@ -1071,7 +1155,23 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { undo_redo->create_action(TTR("Set Tile Region")); undo_redo->add_do_method(tileset.ptr(), "tile_set_region", get_current_tile(), edited_region); undo_redo->add_undo_method(tileset.ptr(), "tile_set_region", get_current_tile(), tileset->tile_get_region(get_current_tile())); + + Size2 tile_workspace_size = edited_region.position + edited_region.size + WORKSPACE_MARGIN * 2; + Size2 workspace_minsize = workspace->get_custom_minimum_size(); + if (tile_workspace_size.x > workspace_minsize.x && tile_workspace_size.y > workspace_minsize.y) { + undo_redo->add_do_method(workspace, "set_custom_minimum_size", tile_workspace_size); + undo_redo->add_undo_method(workspace, "set_custom_minimum_size", workspace_minsize); + undo_redo->add_do_method(workspace_container, "set_custom_minimum_size", tile_workspace_size); + undo_redo->add_undo_method(workspace_container, "set_custom_minimum_size", workspace_minsize); + undo_redo->add_do_method(workspace_overlay, "set_custom_minimum_size", tile_workspace_size); + undo_redo->add_undo_method(workspace_overlay, "set_custom_minimum_size", workspace_minsize); + } else if (workspace_minsize.x > get_current_texture()->get_size().x + WORKSPACE_MARGIN.x * 2 || workspace_minsize.y > get_current_texture()->get_size().y + WORKSPACE_MARGIN.y * 2) { + undo_redo->add_do_method(this, "update_workspace_minsize"); + undo_redo->add_undo_method(this, "update_workspace_minsize"); + } + edited_region = Rect2(); + undo_redo->add_do_method(workspace, "update"); undo_redo->add_undo_method(workspace, "update"); undo_redo->add_do_method(workspace_overlay, "update"); @@ -1095,6 +1195,19 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { tool_workspacemode[WORKSPACE_EDIT]->set_pressed(true); tool_editmode[EDITMODE_COLLISION]->set_pressed(true); edit_mode = EDITMODE_COLLISION; + + Size2 tile_workspace_size = edited_region.position + edited_region.size + WORKSPACE_MARGIN * 2; + Size2 workspace_minsize = workspace->get_custom_minimum_size(); + if (tile_workspace_size.x > workspace_minsize.x || tile_workspace_size.y > workspace_minsize.y) { + Size2 new_workspace_minsize = Size2(MAX(tile_workspace_size.x, workspace_minsize.x), MAX(tile_workspace_size.y, workspace_minsize.y)); + undo_redo->add_do_method(workspace, "set_custom_minimum_size", new_workspace_minsize); + undo_redo->add_undo_method(workspace, "set_custom_minimum_size", workspace_minsize); + undo_redo->add_do_method(workspace_container, "set_custom_minimum_size", new_workspace_minsize); + undo_redo->add_undo_method(workspace_container, "set_custom_minimum_size", workspace_minsize); + undo_redo->add_do_method(workspace_overlay, "set_custom_minimum_size", new_workspace_minsize); + undo_redo->add_undo_method(workspace_overlay, "set_custom_minimum_size", workspace_minsize); + } + edited_region = Rect2(); undo_redo->add_do_method(workspace, "update"); @@ -1151,10 +1264,11 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if ((mb->get_button_index() == BUTTON_RIGHT || mb->get_button_index() == BUTTON_LEFT) && current_tile_region.has_point(mb->get_position())) { dragging = true; erasing = (mb->get_button_index() == BUTTON_RIGHT); + alternative = Input::get_singleton()->is_key_pressed(KEY_SHIFT); Vector2 coord((int)((mb->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mb->get_position().y - current_tile_region.position.y) / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mb->get_position() - (pos + current_tile_region.position); - uint16_t bit = 0; + uint32_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { if (pos.y < size.y / 2) { @@ -1197,13 +1311,19 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } - uint16_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - uint16_t new_mask = old_mask; - if (erasing) { + uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t new_mask = old_mask; + if (alternative) { new_mask &= ~bit; + new_mask |= (bit << 16); + } else if (erasing) { + new_mask &= ~bit; + new_mask &= ~(bit << 16); } else { new_mask |= bit; + new_mask &= ~(bit << 16); } + if (old_mask != new_mask) { undo_redo->create_action(TTR("Edit Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), coord, new_mask); @@ -1217,6 +1337,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { if ((erasing && mb->get_button_index() == BUTTON_RIGHT) || (!erasing && mb->get_button_index() == BUTTON_LEFT)) { dragging = false; erasing = false; + alternative = false; } } } @@ -1225,7 +1346,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { Vector2 coord((int)((mm->get_position().x - current_tile_region.position.x) / (spacing + size.x)), (int)((mm->get_position().y - current_tile_region.position.y) / (spacing + size.y))); Vector2 pos(coord.x * (spacing + size.x), coord.y * (spacing + size.y)); pos = mm->get_position() - (pos + current_tile_region.position); - uint16_t bit = 0; + uint32_t bit = 0; if (tileset->autotile_get_bitmask_mode(get_current_tile()) == TileSet::BITMASK_2X2) { if (pos.x < size.x / 2) { if (pos.y < size.y / 2) { @@ -1268,12 +1389,17 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } } - uint16_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); - uint16_t new_mask = old_mask; - if (erasing) { + uint32_t old_mask = tileset->autotile_get_bitmask(get_current_tile(), coord); + uint32_t new_mask = old_mask; + if (alternative) { new_mask &= ~bit; + new_mask |= (bit << 16); + } else if (erasing) { + new_mask &= ~bit; + new_mask &= ~(bit << 16); } else { new_mask |= bit; + new_mask &= ~(bit << 16); } if (old_mask != new_mask) { undo_redo->create_action(TTR("Edit Tile Bitmask")); @@ -1335,8 +1461,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { } undo_redo->create_action(TTR("Edit Collision Polygon")); - undo_redo->add_do_method(edited_collision_shape.ptr(), "set_points", points); - undo_redo->add_undo_method(edited_collision_shape.ptr(), "set_points", edited_collision_shape->get_points()); + _set_edited_shape_points(points); undo_redo->add_do_method(this, "_select_edited_shape_coord"); undo_redo->add_undo_method(this, "_select_edited_shape_coord"); undo_redo->commit_action(); @@ -1414,6 +1539,7 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { workspace->update(); } else { creating_shape = true; + _set_edited_collision_shape(Ref<ConvexPolygonShape2D>()); current_shape.resize(0); current_shape.push_back(snap_point(pos)); workspace->update(); @@ -1430,9 +1556,54 @@ void TileSetEditor::_on_workspace_input(const Ref<InputEvent> &p_ie) { workspace->update(); } } + } else if (tools[SHAPE_NEW_RECTANGLE]->is_pressed()) { + if (mb.is_valid()) { + if (mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + _set_edited_collision_shape(Ref<ConvexPolygonShape2D>()); + current_shape.resize(0); + Vector2 pos = mb->get_position(); + pos = snap_point(pos); + current_shape.push_back(pos); + current_shape.push_back(pos); + current_shape.push_back(pos); + current_shape.push_back(pos); + creating_shape = true; + workspace->update(); + return; + } else if (mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { + if (creating_shape) { + creating_shape = false; + _select_edited_shape_coord(); + workspace->update(); + } + } else if (!mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { + if (creating_shape) { + if ((current_shape[0] - current_shape[1]).length_squared() <= grab_threshold) { + current_shape.set(0, snap_point(shape_anchor)); + current_shape.set(1, snap_point(shape_anchor + Vector2(current_tile_region.size.x, 0))); + current_shape.set(2, snap_point(shape_anchor + current_tile_region.size)); + current_shape.set(3, snap_point(shape_anchor + Vector2(0, current_tile_region.size.y))); + } + close_shape(shape_anchor); + workspace->update(); + return; + } + } + } else if (mm.is_valid()) { + if (creating_shape) { + Vector2 pos = mm->get_position(); + pos = snap_point(pos); + Vector2 p = current_shape[2]; + current_shape.set(3, snap_point(Vector2(pos.x, p.y))); + current_shape.set(0, snap_point(pos)); + current_shape.set(1, snap_point(Vector2(p.x, pos.y))); + workspace->update(); + } + } } } break; - default: {} + default: { + } } } } @@ -1445,10 +1616,10 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { undo_redo->create_action(TTR("Paste Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); undo_redo->add_undo_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); - for (Map<Vector2, uint16_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = bitmask_map_copy.front(); E; E = E->next()) { undo_redo->add_do_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } undo_redo->add_do_method(workspace, "update"); @@ -1457,12 +1628,55 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { } else if (p_tool == BITMASK_CLEAR) { undo_redo->create_action(TTR("Clear Tile Bitmask")); undo_redo->add_do_method(tileset.ptr(), "autotile_clear_bitmask_map", get_current_tile()); - for (Map<Vector2, uint16_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { + for (Map<Vector2, uint32_t>::Element *E = tileset->autotile_get_bitmask_map(get_current_tile()).front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", get_current_tile(), E->key(), E->value()); } undo_redo->add_do_method(workspace, "update"); undo_redo->add_undo_method(workspace, "update"); undo_redo->commit_action(); + } else if (p_tool == SHAPE_TOGGLE_TYPE) { + if (edited_collision_shape.is_valid()) { + Ref<ConvexPolygonShape2D> convex = edited_collision_shape; + Ref<ConcavePolygonShape2D> concave = edited_collision_shape; + Ref<Shape2D> previous_shape = edited_collision_shape; + Array sd = tileset->call("tile_get_shapes", get_current_tile()); + + if (convex.is_valid()) { + // Make concave + undo_redo->create_action(TTR("Make Polygon Concave")); + Ref<ConcavePolygonShape2D> _concave = memnew(ConcavePolygonShape2D); + edited_collision_shape = _concave; + _set_edited_shape_points(_get_collision_shape_points(convex)); + } else if (concave.is_valid()) { + // Make convex + undo_redo->create_action(TTR("Make Polygon Convex")); + Ref<ConvexPolygonShape2D> _convex = memnew(ConvexPolygonShape2D); + edited_collision_shape = _convex; + _set_edited_shape_points(_get_collision_shape_points(concave)); + } else { + // Shouldn't happen + } + for (int i = 0; i < sd.size(); i++) { + if (sd[i].get("shape") == previous_shape) { + undo_redo->add_undo_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd.duplicate()); + sd.remove(i); + sd.insert(i, edited_collision_shape); + undo_redo->add_do_method(tileset.ptr(), "tile_set_shapes", get_current_tile(), sd); + undo_redo->add_do_method(this, "_select_edited_shape_coord"); + undo_redo->add_undo_method(this, "_select_edited_shape_coord"); + undo_redo->commit_action(); + break; + } + } + _update_toggle_shape_button(); + workspace->update(); + workspace_container->update(); + helper->_change_notify(""); + } + } else if (p_tool == SELECT_NEXT) { + _select_next_shape(); + } else if (p_tool == SELECT_PREVIOUS) { + _select_previous_shape(); } else if (p_tool == SHAPE_DELETE) { if (creating_shape) { creating_shape = false; @@ -1477,6 +1691,14 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { undo_redo->add_do_method(tileset.ptr(), "remove_tile", t_id); _undo_tile_removal(t_id); undo_redo->add_do_method(this, "_validate_current_tile_id"); + + Rect2 tile_region = tileset->tile_get_region(get_current_tile()); + Size2 tile_workspace_size = tile_region.position + tile_region.size; + if (tile_workspace_size.x > get_current_texture()->get_size().x || tile_workspace_size.y > get_current_texture()->get_size().y) { + undo_redo->add_do_method(this, "update_workspace_minsize"); + undo_redo->add_undo_method(this, "update_workspace_minsize"); + } + undo_redo->add_do_method(workspace, "update"); undo_redo->add_undo_method(workspace, "update"); undo_redo->add_do_method(workspace_overlay, "update"); @@ -1535,7 +1757,8 @@ void TileSetEditor::_on_tool_clicked(int p_tool) { undo_redo->commit_action(); } } break; - default: {} + default: { + } } } } else if (p_tool == TOOL_SELECT) { @@ -1577,6 +1800,380 @@ void TileSetEditor::_on_grid_snap_toggled(bool p_val) { workspace->update(); } +Vector<Vector2> TileSetEditor::_get_collision_shape_points(const Ref<Shape2D> &p_shape) { + Ref<ConvexPolygonShape2D> convex = p_shape; + Ref<ConcavePolygonShape2D> concave = p_shape; + if (convex.is_valid()) { + return convex->get_points(); + } else if (concave.is_valid()) { + Vector<Vector2> points; + for (int i = 0; i < concave->get_segments().size(); i += 2) { + points.push_back(concave->get_segments()[i]); + } + return points; + } else { + return Vector<Vector2>(); + } +} + +Vector<Vector2> TileSetEditor::_get_edited_shape_points() { + return _get_collision_shape_points(edited_collision_shape); +} + +void TileSetEditor::_set_edited_shape_points(const Vector<Vector2> points) { + Ref<ConvexPolygonShape2D> convex = edited_collision_shape; + Ref<ConcavePolygonShape2D> concave = edited_collision_shape; + if (convex.is_valid()) { + undo_redo->add_do_method(convex.ptr(), "set_points", points); + undo_redo->add_undo_method(convex.ptr(), "set_points", _get_edited_shape_points()); + } else if (concave.is_valid()) { + PoolVector2Array segments; + for (int i = 0; i < points.size() - 1; i++) { + segments.push_back(points[i]); + segments.push_back(points[i + 1]); + } + segments.push_back(points[points.size() - 1]); + segments.push_back(points[0]); + concave->set_segments(segments); + undo_redo->add_do_method(concave.ptr(), "set_segments", segments); + undo_redo->add_undo_method(concave.ptr(), "set_segments", concave->get_segments()); + } else { + // Invalid shape + } +} + +void TileSetEditor::_update_tile_data() { + current_tile_data.clear(); + if (get_current_tile() < 0) + return; + + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(get_current_tile()); + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + SubtileData data; + for (int i = 0; i < sd.size(); i++) { + data.collisions.push_back(sd[i].shape); + } + data.navigation_shape = tileset->tile_get_navigation_polygon(get_current_tile()); + data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile()); + current_tile_data[Vector2i()] = data; + } else { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->tile_get_region(get_current_tile()).size; + Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing)); + for (int y = 0; y < cell_count.y; y++) { + for (int x = 0; x < cell_count.x; x++) { + SubtileData data; + Vector2i coord(x, y); + for (int i = 0; i < sd.size(); i++) { + if (sd[i].autotile_coord == coord) { + data.collisions.push_back(sd[i].shape); + } + } + data.navigation_shape = tileset->autotile_get_navigation_polygon(get_current_tile(), coord); + data.occlusion_shape = tileset->tile_get_light_occluder(get_current_tile()); + current_tile_data[coord] = data; + } + } + } +} + +void TileSetEditor::_update_toggle_shape_button() { + Ref<ConvexPolygonShape2D> convex = edited_collision_shape; + Ref<ConcavePolygonShape2D> concave = edited_collision_shape; + separator_shape_toggle->show(); + tools[SHAPE_TOGGLE_TYPE]->show(); + if (edit_mode != EDITMODE_COLLISION || !edited_collision_shape.is_valid()) { + separator_shape_toggle->hide(); + tools[SHAPE_TOGGLE_TYPE]->hide(); + } else if (concave.is_valid()) { + tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConvexPolygonShape2D", "EditorIcons")); + tools[SHAPE_TOGGLE_TYPE]->set_text("Make Convex"); + } else if (convex.is_valid()) { + tools[SHAPE_TOGGLE_TYPE]->set_icon(get_icon("ConcavePolygonShape2D", "EditorIcons")); + tools[SHAPE_TOGGLE_TYPE]->set_text("Make Concave"); + } else { + // Shouldn't happen + separator_shape_toggle->hide(); + tools[SHAPE_TOGGLE_TYPE]->hide(); + } +} + +void TileSetEditor::_select_next_tile() { + Array tiles = _get_tiles_in_current_texture(true); + if (tiles.size() == 0) { + set_current_tile(-1); + } else if (get_current_tile() == -1) { + set_current_tile(tiles[0]); + } else { + int index = tiles.find(get_current_tile()); + if (index < 0) { + set_current_tile(tiles[0]); + } else if (index == tiles.size() - 1) { + set_current_tile(tiles[0]); + } else { + set_current_tile(tiles[index + 1]); + } + } + if (get_current_tile() == -1) { + return; + } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + return; + } else { + switch (edit_mode) { + case EDITMODE_COLLISION: + case EDITMODE_OCCLUSION: + case EDITMODE_NAVIGATION: + case EDITMODE_PRIORITY: + case EDITMODE_Z_INDEX: { + edited_shape_coord = Vector2(); + _select_edited_shape_coord(); + } break; + default: { + } + } + } +} + +void TileSetEditor::_select_previous_tile() { + Array tiles = _get_tiles_in_current_texture(true); + if (tiles.size() == 0) { + set_current_tile(-1); + } else if (get_current_tile() == -1) { + set_current_tile(tiles[tiles.size() - 1]); + } else { + int index = tiles.find(get_current_tile()); + if (index <= 0) { + set_current_tile(tiles[tiles.size() - 1]); + } else { + set_current_tile(tiles[index - 1]); + } + } + if (get_current_tile() == -1) { + return; + } else if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + return; + } else { + switch (edit_mode) { + case EDITMODE_COLLISION: + case EDITMODE_OCCLUSION: + case EDITMODE_NAVIGATION: + case EDITMODE_PRIORITY: + case EDITMODE_Z_INDEX: { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->tile_get_region(get_current_tile()).size; + Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing)); + cell_count -= Vector2(1, 1); + edited_shape_coord = cell_count; + _select_edited_shape_coord(); + } break; + default: { + } + } + } +} + +Array TileSetEditor::_get_tiles_in_current_texture(bool sorted) { + Array a; + List<int> all_tiles; + if (!get_current_texture().is_valid()) { + return a; + } + tileset->get_tile_list(&all_tiles); + for (int i = 0; i < all_tiles.size(); i++) { + if (tileset->tile_get_texture(all_tiles[i]) == get_current_texture()) { + a.push_back(all_tiles[i]); + } + } + if (sorted) { + a.sort_custom(this, "_sort_tiles"); + } + return a; +} + +bool TileSetEditor::_sort_tiles(Variant p_a, Variant p_b) { + int a = p_a; + int b = p_b; + + Vector2 pos_a = tileset->tile_get_region(a).position; + Vector2 pos_b = tileset->tile_get_region(b).position; + if (pos_a.y < pos_b.y) { + return true; + + } else if (pos_a.y == pos_b.y) { + if (pos_a.x < pos_b.x) { + return true; + } else { + return false; + } + } else { + return false; + } +} + +void TileSetEditor::_select_next_subtile() { + if (get_current_tile() == -1) { + _select_next_tile(); + return; + } + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + _select_next_tile(); + } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) { + _select_next_tile(); + } else { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->tile_get_region(get_current_tile()).size; + Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing)); + if (edited_shape_coord.x >= cell_count.x - 1 && edited_shape_coord.y >= cell_count.y - 1) { + _select_next_tile(); + } else { + edited_shape_coord.x++; + if (edited_shape_coord.x >= cell_count.x) { + edited_shape_coord.x = 0; + edited_shape_coord.y++; + } + _select_edited_shape_coord(); + } + } +} + +void TileSetEditor::_select_previous_subtile() { + if (get_current_tile() == -1) { + _select_previous_tile(); + return; + } + if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { + _select_previous_tile(); + } else if (edit_mode == EDITMODE_REGION || edit_mode == EDITMODE_BITMASK || edit_mode == EDITMODE_ICON) { + _select_previous_tile(); + } else { + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->tile_get_region(get_current_tile()).size; + Vector2i cell_count = size / (tileset->autotile_get_size(get_current_tile()) + Vector2(spacing, spacing)); + if (edited_shape_coord.x <= 0 && edited_shape_coord.y <= 0) { + _select_previous_tile(); + } else { + edited_shape_coord.x--; + if (edited_shape_coord.x == -1) { + edited_shape_coord.x = cell_count.x - 1; + edited_shape_coord.y--; + } + _select_edited_shape_coord(); + } + } +} + +void TileSetEditor::_select_next_shape() { + if (get_current_tile() == -1) { + _select_next_subtile(); + } else if (edit_mode != EDITMODE_COLLISION) { + _select_next_subtile(); + } else { + Vector2i edited_coord = Vector2(); + if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) { + edited_coord = edited_shape_coord; + } + SubtileData data = current_tile_data[edited_coord]; + if (data.collisions.size() == 0) { + _select_next_subtile(); + } else { + int index = data.collisions.find(edited_collision_shape); + if (index < 0) { + _set_edited_collision_shape(data.collisions[0]); + } else if (index == data.collisions.size() - 1) { + _select_next_subtile(); + } else { + _set_edited_collision_shape(data.collisions[index + 1]); + } + } + current_shape.resize(0); + Rect2 current_tile_region = tileset->tile_get_region(get_current_tile()); + current_tile_region.position += WORKSPACE_MARGIN; + + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); + Vector2 shape_anchor = edited_shape_coord; + shape_anchor.x *= (size.x + spacing); + shape_anchor.y *= (size.y + spacing); + current_tile_region.position += shape_anchor; + + if (edited_collision_shape.is_valid()) { + for (int i = 0; i < _get_edited_shape_points().size(); i++) { + current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position); + } + } + workspace->update(); + workspace_container->update(); + helper->_change_notify(""); + } +} + +void TileSetEditor::_select_previous_shape() { + if (get_current_tile() == -1) { + _select_previous_subtile(); + if (get_current_tile() != -1 && edit_mode == EDITMODE_COLLISION) { + SubtileData data = current_tile_data[Vector2i(edited_shape_coord)]; + if (data.collisions.size() > 1) { + _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]); + } + } else { + return; + } + } else if (edit_mode != EDITMODE_COLLISION) { + _select_previous_subtile(); + } else { + Vector2i edited_coord = Vector2(); + if (tileset->tile_get_tile_mode(get_current_tile()) != TileSet::SINGLE_TILE) { + edited_coord = edited_shape_coord; + } + SubtileData data = current_tile_data[edited_coord]; + if (data.collisions.size() == 0) { + _select_previous_subtile(); + data = current_tile_data[Vector2i(edited_shape_coord)]; + if (data.collisions.size() > 1) { + _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]); + } + } else { + int index = data.collisions.find(edited_collision_shape); + if (index < 0) { + _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]); + } else if (index == 0) { + _select_previous_subtile(); + data = current_tile_data[Vector2i(edited_shape_coord)]; + if (data.collisions.size() > 1) { + _set_edited_collision_shape(data.collisions[data.collisions.size() - 1]); + } + } else { + _set_edited_collision_shape(data.collisions[index - 1]); + } + } + + current_shape.resize(0); + Rect2 current_tile_region = tileset->tile_get_region(get_current_tile()); + current_tile_region.position += WORKSPACE_MARGIN; + + int spacing = tileset->autotile_get_spacing(get_current_tile()); + Vector2 size = tileset->autotile_get_size(get_current_tile()); + Vector2 shape_anchor = edited_shape_coord; + shape_anchor.x *= (size.x + spacing); + shape_anchor.y *= (size.y + spacing); + current_tile_region.position += shape_anchor; + + if (edited_collision_shape.is_valid()) { + for (int i = 0; i < _get_edited_shape_points().size(); i++) { + current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position); + } + } + workspace->update(); + workspace_container->update(); + helper->_change_notify(""); + } +} + +void TileSetEditor::_set_edited_collision_shape(const Ref<Shape2D> &p_shape) { + edited_collision_shape = p_shape; + _update_toggle_shape_button(); +} + void TileSetEditor::_set_snap_step(Vector2 p_val) { snap_step.x = CLAMP(p_val.x, 0, 256); snap_step.y = CLAMP(p_val.y, 0, 256); @@ -1632,8 +2229,8 @@ void TileSetEditor::_undo_tile_removal(int p_id) { for (Map<Vector2, Ref<NavigationPolygon> >::Element *E = navigation_map.front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_navigation_polygon", p_id, E->value(), E->key()); } - Map<Vector2, uint16_t> bitmask_map = tileset->autotile_get_bitmask_map(p_id); - for (Map<Vector2, uint16_t>::Element *E = bitmask_map.front(); E; E = E->next()) { + Map<Vector2, uint32_t> bitmask_map = tileset->autotile_get_bitmask_map(p_id); + for (Map<Vector2, uint32_t>::Element *E = bitmask_map.front(); E; E = E->next()) { undo_redo->add_undo_method(tileset.ptr(), "autotile_set_bitmask", p_id, E->key(), E->value()); } Map<Vector2, int> priority_map = tileset->autotile_get_priority_map(p_id); @@ -1873,16 +2470,29 @@ void TileSetEditor::draw_polygon_shapes() { } anchor += WORKSPACE_MARGIN; anchor += tileset->tile_get_region(t_id).position; - Ref<ConvexPolygonShape2D> shape = sd[i].shape; + Ref<Shape2D> shape = sd[i].shape; if (shape.is_valid()) { Color c_bg; Color c_border; + Ref<ConvexPolygonShape2D> convex = shape; + bool is_convex = convex.is_valid(); if ((tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE || coord == edited_shape_coord) && sd[i].shape == edited_collision_shape) { - c_bg = Color(0, 1, 1, 0.5); - c_border = Color(0, 1, 1); + if (is_convex) { + c_bg = Color(0, 1, 1, 0.5); + c_border = Color(0, 1, 1); + } else { + c_bg = Color(0.8, 0, 1, 0.5); + c_border = Color(0.8, 0, 1); + } } else { - c_bg = Color(0.9, 0.7, 0.07, 0.5); - c_border = Color(0.9, 0.7, 0.07, 1); + if (is_convex) { + c_bg = Color(0.9, 0.7, 0.07, 0.5); + c_border = Color(0.9, 0.7, 0.07, 1); + + } else { + c_bg = Color(0.9, 0.45, 0.075, 0.5); + c_border = Color(0.9, 0.45, 0.075); + } } Vector<Vector2> polygon; Vector<Color> colors; @@ -1892,14 +2502,16 @@ void TileSetEditor::draw_polygon_shapes() { colors.push_back(c_bg); } } else { - for (int j = 0; j < shape->get_points().size(); j++) { - polygon.push_back(shape->get_points()[j] + anchor); + for (int j = 0; j < _get_collision_shape_points(shape).size(); j++) { + polygon.push_back(_get_collision_shape_points(shape)[j] + anchor); colors.push_back(c_bg); } } - if (polygon.size() > 2) { - workspace->draw_polygon(polygon, colors); - } + + if (polygon.size() < 3) + continue; + + workspace->draw_polygon(polygon, colors); if (coord == edited_shape_coord || tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { if (!creating_shape) { @@ -1940,10 +2552,12 @@ void TileSetEditor::draw_polygon_shapes() { workspace->draw_polygon(polygon, colors); if (!creating_shape) { - for (int j = 0; j < polygon.size() - 1; j++) { - workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true); + if (polygon.size() > 1) { + for (int j = 0; j < polygon.size() - 1; j++) { + workspace->draw_line(polygon[j], polygon[j + 1], c_border, 1, true); + } + workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true); } - workspace->draw_line(polygon[polygon.size() - 1], polygon[0], c_border, 1, true); } if (shape == edited_occlusion_shape) { draw_handles = true; @@ -2090,7 +2704,8 @@ void TileSetEditor::draw_polygon_shapes() { } } } break; - default: {} + default: { + } } if (creating_shape) { @@ -2110,11 +2725,11 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { if (current_shape.size() >= 3) { Ref<ConvexPolygonShape2D> shape = memnew(ConvexPolygonShape2D); - Vector<Vector2> segments; + Vector<Vector2> points; float p_total = 0; for (int i = 0; i < current_shape.size(); i++) { - segments.push_back(current_shape[i] - shape_anchor); + points.push_back(current_shape[i] - shape_anchor); if (i != current_shape.size() - 1) p_total += ((current_shape[i + 1].x - current_shape[i].x) * (-current_shape[i + 1].y + (-current_shape[i].y))); @@ -2123,9 +2738,9 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { } if (p_total < 0) - segments.invert(); + points.invert(); - shape->set_points(segments); + shape->set_points(points); undo_redo->create_action(TTR("Create Collision Polygon")); // Necessary to get the version that returns a Array instead of a Vector. @@ -2210,6 +2825,7 @@ void TileSetEditor::close_shape(const Vector2 &shape_anchor) { } void TileSetEditor::select_coord(const Vector2 &coord) { + _update_tile_data(); current_shape = PoolVector2Array(); if (get_current_tile() == -1) return; @@ -2217,7 +2833,7 @@ void TileSetEditor::select_coord(const Vector2 &coord) { current_tile_region.position += WORKSPACE_MARGIN; if (tileset->tile_get_tile_mode(get_current_tile()) == TileSet::SINGLE_TILE) { if (edited_collision_shape != tileset->tile_get_shape(get_current_tile(), 0)) - edited_collision_shape = tileset->tile_get_shape(get_current_tile(), 0); + _set_edited_collision_shape(tileset->tile_get_shape(get_current_tile(), 0)); if (edited_occlusion_shape != tileset->tile_get_light_occluder(get_current_tile())) edited_occlusion_shape = tileset->tile_get_light_occluder(get_current_tile()); if (edited_navigation_shape != tileset->tile_get_navigation_polygon(get_current_tile())) @@ -2226,8 +2842,8 @@ void TileSetEditor::select_coord(const Vector2 &coord) { if (edit_mode == EDITMODE_COLLISION) { current_shape.resize(0); if (edited_collision_shape.is_valid()) { - for (int i = 0; i < edited_collision_shape->get_points().size(); i++) { - current_shape.push_back(edited_collision_shape->get_points()[i] + current_tile_region.position); + for (int i = 0; i < _get_edited_shape_points().size(); i++) { + current_shape.push_back(_get_edited_shape_points()[i] + current_tile_region.position); } } } else if (edit_mode == EDITMODE_OCCLUSION) { @@ -2254,13 +2870,13 @@ void TileSetEditor::select_coord(const Vector2 &coord) { for (int i = 0; i < sd.size(); i++) { if (sd[i].autotile_coord == coord) { if (edited_collision_shape != sd[i].shape) - edited_collision_shape = sd[i].shape; + _set_edited_collision_shape(sd[i].shape); found_collision_shape = true; break; } } if (!found_collision_shape) - edited_collision_shape = Ref<ConvexPolygonShape2D>(NULL); + _set_edited_collision_shape(Ref<ConvexPolygonShape2D>(NULL)); if (edited_occlusion_shape != tileset->autotile_get_light_occluder(get_current_tile(), coord)) edited_occlusion_shape = tileset->autotile_get_light_occluder(get_current_tile(), coord); if (edited_navigation_shape != tileset->autotile_get_navigation_polygon(get_current_tile(), coord)) @@ -2275,8 +2891,8 @@ void TileSetEditor::select_coord(const Vector2 &coord) { if (edit_mode == EDITMODE_COLLISION) { current_shape.resize(0); if (edited_collision_shape.is_valid()) { - for (int j = 0; j < edited_collision_shape->get_points().size(); j++) { - current_shape.push_back(edited_collision_shape->get_points()[j] + shape_anchor); + for (int j = 0; j < _get_edited_shape_points().size(); j++) { + current_shape.push_back(_get_edited_shape_points()[j] + shape_anchor); } } } else if (edit_mode == EDITMODE_OCCLUSION) { @@ -2397,7 +3013,8 @@ void TileSetEditor::update_texture_list_icon() { for (int current_idx = 0; current_idx < texture_list->get_item_count(); current_idx++) { RID rid = texture_list->get_item_metadata(current_idx); texture_list->set_item_icon(current_idx, texture_map[rid]); - texture_list->set_item_icon_region(current_idx, Rect2(0, 0, 150, 100)); + Size2 texture_size = texture_map[rid]->get_size(); + texture_list->set_item_icon_region(current_idx, Rect2(0, 0, MIN(texture_size.x, 150), MIN(texture_size.y, 100))); } texture_list->update(); } @@ -2410,10 +3027,14 @@ void TileSetEditor::update_workspace_tile_mode() { for (int i = 1; i < WORKSPACE_MODE_MAX; i++) { tool_workspacemode[i]->set_disabled(true); } + tools[SELECT_NEXT]->set_disabled(true); + tools[SELECT_PREVIOUS]->set_disabled(true); } else { for (int i = 1; i < WORKSPACE_MODE_MAX; i++) { tool_workspacemode[i]->set_disabled(false); } + tools[SELECT_NEXT]->set_disabled(false); + tools[SELECT_PREVIOUS]->set_disabled(false); } if (workspace_mode != WORKSPACE_EDIT) { @@ -2431,7 +3052,7 @@ void TileSetEditor::update_workspace_tile_mode() { for (int i = 0; i < EDITMODE_MAX; i++) { tool_editmode[i]->hide(); } - for (int i = 0; i < ZOOM_OUT; i++) { + for (int i = TOOL_SELECT; i < ZOOM_OUT; i++) { tools[i]->hide(); } @@ -2479,6 +3100,26 @@ void TileSetEditor::update_workspace_tile_mode() { _on_edit_mode_changed(edit_mode); } +void TileSetEditor::update_workspace_minsize() { + Size2 workspace_min_size = get_current_texture()->get_size(); + RID current_texture_rid = get_current_texture()->get_rid(); + List<int> *tiles = new List<int>(); + tileset->get_tile_list(tiles); + for (List<int>::Element *E = tiles->front(); E; E = E->next()) { + if (tileset->tile_get_texture(E->get())->get_rid() == current_texture_rid) { + Rect2i region = tileset->tile_get_region(E->get()); + if (region.position.x + region.size.x > workspace_min_size.x) + workspace_min_size.x = region.position.x + region.size.x; + if (region.position.y + region.size.y > workspace_min_size.y) + workspace_min_size.y = region.position.y + region.size.y; + } + } + + workspace->set_custom_minimum_size(workspace_min_size + WORKSPACE_MARGIN * 2); + workspace_container->set_custom_minimum_size(workspace_min_size + WORKSPACE_MARGIN * 2); + workspace_overlay->set_custom_minimum_size(workspace_min_size + WORKSPACE_MARGIN * 2); +} + void TileSetEditor::update_edited_region(const Vector2 &end_point) { edited_region = Rect2(region_from, Size2()); if (tools[TOOL_GRID_SNAP]->is_pressed()) { @@ -2511,6 +3152,11 @@ void TileSetEditor::set_current_tile(int p_id) { helper->_change_notify(""); select_coord(Vector2(0, 0)); update_workspace_tile_mode(); + if (p_id == -1) { + editor->get_inspector()->edit(tileset.ptr()); + } else { + editor->get_inspector()->edit(helper); + } } } @@ -2548,20 +3194,20 @@ bool TilesetEditorContext::_set(const StringName &p_name, const Variant &p_value tileset_editor->_set_snap_sep(snap); return true; } else if (p_name.operator String().left(5) == "tile_") { - String name = p_name.operator String().right(5); + String name2 = p_name.operator String().right(5); bool v = false; if (tileset_editor->get_current_tile() < 0 || tileset.is_null()) return false; - if (name == "autotile_bitmask_mode") { + if (name2 == "autotile_bitmask_mode") { tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/bitmask_mode", p_value, &v); - } else if (name == "subtile_size") { + } else if (name2 == "subtile_size") { tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/tile_size", p_value, &v); - } else if (name == "subtile_spacing") { + } else if (name2 == "subtile_spacing") { tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/autotile/spacing", p_value, &v); } else { - tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/" + name, p_value, &v); + tileset->set(String::num(tileset_editor->get_current_tile(), 0) + "/" + name2, p_value, &v); } if (v) { tileset->_change_notify(""); @@ -2572,6 +3218,24 @@ bool TilesetEditorContext::_set(const StringName &p_name, const Variant &p_value } else if (name == "tileset_script") { tileset->set_script(p_value); return true; + } else if (name == "selected_collision_one_way") { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile()); + for (int index = 0; index < sd.size(); index++) { + if (sd[index].shape == tileset_editor->edited_collision_shape) { + tileset->tile_set_shape_one_way(tileset_editor->get_current_tile(), index, p_value); + return true; + } + } + return false; + } else if (name == "selected_collision_one_way_margin") { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile()); + for (int index = 0; index < sd.size(); index++) { + if (sd[index].shape == tileset_editor->edited_collision_shape) { + tileset->tile_set_shape_one_way_margin(tileset_editor->get_current_tile(), index, p_value); + return true; + } + } + return false; } tileset_editor->err_dialog->set_text(TTR("This property can't be changed.")); @@ -2614,6 +3278,24 @@ bool TilesetEditorContext::_get(const StringName &p_name, Variant &r_ret) const } else if (name == "selected_collision") { r_ret = tileset_editor->edited_collision_shape; v = true; + } else if (name == "selected_collision_one_way") { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile()); + for (int index = 0; index < sd.size(); index++) { + if (sd[index].shape == tileset_editor->edited_collision_shape) { + r_ret = sd[index].one_way_collision; + v = true; + break; + } + } + } else if (name == "selected_collision_one_way_margin") { + Vector<TileSet::ShapeData> sd = tileset->tile_get_shapes(tileset_editor->get_current_tile()); + for (int index = 0; index < sd.size(); index++) { + if (sd[index].shape == tileset_editor->edited_collision_shape) { + r_ret = sd[index].one_way_collision_margin; + v = true; + break; + } + } } else if (name == "selected_navigation") { r_ret = tileset_editor->edited_navigation_shape; v = true; @@ -2660,6 +3342,10 @@ void TilesetEditorContext::_get_property_list(List<PropertyInfo> *p_list) const } if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_COLLISION && tileset_editor->edited_collision_shape.is_valid()) { p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_collision", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_collision_shape->get_class())); + if (tileset_editor->edited_collision_shape.is_valid()) { + p_list->push_back(PropertyInfo(Variant::BOOL, "selected_collision_one_way", PROPERTY_HINT_NONE)); + p_list->push_back(PropertyInfo(Variant::REAL, "selected_collision_one_way_margin", PROPERTY_HINT_NONE)); + } } if (tileset_editor->edit_mode == TileSetEditor::EDITMODE_NAVIGATION && tileset_editor->edited_navigation_shape.is_valid()) { p_list->push_back(PropertyInfo(Variant::OBJECT, "selected_navigation", PROPERTY_HINT_RESOURCE_TYPE, tileset_editor->edited_navigation_shape->get_class())); @@ -2680,13 +3366,13 @@ void TilesetEditorContext::_bind_methods() { TilesetEditorContext::TilesetEditorContext(TileSetEditor *p_tileset_editor) { tileset_editor = p_tileset_editor; + snap_options_visible = false; } void TileSetEditorPlugin::edit(Object *p_node) { if (Object::cast_to<TileSet>(p_node)) { tileset_editor->edit(Object::cast_to<TileSet>(p_node)); - editor->get_inspector()->edit(tileset_editor->helper); } } @@ -2715,6 +3401,7 @@ Dictionary TileSetEditorPlugin::get_state() const { state["snap_separation"] = tileset_editor->snap_separation; state["snap_enabled"] = tileset_editor->tools[TileSetEditor::TOOL_GRID_SNAP]->is_pressed(); state["keep_inside_tile"] = tileset_editor->tools[TileSetEditor::SHAPE_KEEP_INSIDE_TILE]->is_pressed(); + state["show_information"] = tileset_editor->tools[TileSetEditor::VISIBLE_INFO]->is_pressed(); return state; } @@ -2735,11 +3422,18 @@ void TileSetEditorPlugin::set_state(const Dictionary &p_state) { if (state.has("snap_enabled")) { tileset_editor->tools[TileSetEditor::TOOL_GRID_SNAP]->set_pressed(state["snap_enabled"]); + if (tileset_editor->helper) { + tileset_editor->_on_grid_snap_toggled(state["snap_enabled"]); + } } if (state.has("keep_inside_tile")) { tileset_editor->tools[TileSetEditor::SHAPE_KEEP_INSIDE_TILE]->set_pressed(state["keep_inside_tile"]); } + + if (state.has("show_information")) { + tileset_editor->tools[TileSetEditor::VISIBLE_INFO]->set_pressed(state["show_information"]); + } } TileSetEditorPlugin::TileSetEditorPlugin(EditorNode *p_node) { diff --git a/editor/plugins/tile_set_editor_plugin.h b/editor/plugins/tile_set_editor_plugin.h index 1a6b51cdd3..1176e1bb92 100644 --- a/editor/plugins/tile_set_editor_plugin.h +++ b/editor/plugins/tile_set_editor_plugin.h @@ -34,6 +34,7 @@ #include "editor/editor_name_dialog.h" #include "editor/editor_node.h" #include "scene/2d/sprite.h" +#include "scene/resources/concave_polygon_shape_2d.h" #include "scene/resources/convex_polygon_shape_2d.h" #include "scene/resources/tile_set.h" @@ -76,11 +77,15 @@ class TileSetEditor : public HSplitContainer { }; enum TileSetTools { + SELECT_PREVIOUS, + SELECT_NEXT, TOOL_SELECT, BITMASK_COPY, BITMASK_PASTE, BITMASK_CLEAR, SHAPE_NEW_POLYGON, + SHAPE_NEW_RECTANGLE, + SHAPE_TOGGLE_TYPE, SHAPE_DELETE, SHAPE_KEEP_INSIDE_TILE, TOOL_GRID_SNAP, @@ -91,6 +96,12 @@ class TileSetEditor : public HSplitContainer { TOOL_MAX }; + struct SubtileData { + Array collisions; + Ref<OccluderPolygon2D> occlusion_shape; + Ref<NavigationPolygon> navigation_shape; + }; + Ref<TileSet> tileset; TilesetEditorContext *helper; EditorNode *editor; @@ -114,13 +125,14 @@ class TileSetEditor : public HSplitContainer { bool draw_edited_region; Vector2 edited_shape_coord; PoolVector2Array current_shape; - Map<Vector2, uint16_t> bitmask_map_copy; + Map<Vector2i, SubtileData> current_tile_data; + Map<Vector2, uint32_t> bitmask_map_copy; Vector2 snap_step; Vector2 snap_offset; Vector2 snap_separation; - Ref<ConvexPolygonShape2D> edited_collision_shape; + Ref<Shape2D> edited_collision_shape; Ref<OccluderPolygon2D> edited_occlusion_shape; Ref<NavigationPolygon> edited_navigation_shape; @@ -136,6 +148,7 @@ class TileSetEditor : public HSplitContainer { HSeparator *separator_editmode; HBoxContainer *toolbar; ToolButton *tools[TOOL_MAX]; + VSeparator *separator_shape_toggle; VSeparator *separator_bitmask; VSeparator *separator_delete; VSeparator *separator_grid; @@ -187,6 +200,20 @@ private: void _on_priority_changed(float val); void _on_z_index_changed(float val); void _on_grid_snap_toggled(bool p_val); + Vector<Vector2> _get_collision_shape_points(const Ref<Shape2D> &p_shape); + Vector<Vector2> _get_edited_shape_points(); + void _set_edited_shape_points(const Vector<Vector2> points); + void _update_tile_data(); + void _update_toggle_shape_button(); + void _select_next_tile(); + void _select_previous_tile(); + Array _get_tiles_in_current_texture(bool sorted = false); + bool _sort_tiles(Variant p_a, Variant p_b); + void _select_next_subtile(); + void _select_previous_subtile(); + void _select_next_shape(); + void _select_previous_shape(); + void _set_edited_collision_shape(const Ref<Shape2D> &p_shape); void _set_snap_step(Vector2 p_val); void _set_snap_off(Vector2 p_val); void _set_snap_sep(Vector2 p_val); @@ -209,6 +236,7 @@ private: void select_coord(const Vector2 &coord); Vector2 snap_point(const Vector2 &point); void update_workspace_tile_mode(); + void update_workspace_minsize(); void update_edited_region(const Vector2 &end_point); int get_current_tile() const; diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index 628c2f714c..c2e847f211 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/io/resource_loader.h" +#include "core/math/math_defs.h" #include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" @@ -39,6 +40,7 @@ #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/main/viewport.h" +#include "scene/resources/visual_shader_nodes.h" Control *VisualShaderNodePlugin::create_editor(const Ref<VisualShaderNode> &p_node) { @@ -57,7 +59,15 @@ void VisualShaderNodePlugin::_bind_methods() { void VisualShaderEditor::edit(VisualShader *p_visual_shader) { + bool changed = false; if (p_visual_shader) { + if (visual_shader.is_null()) { + changed = true; + } else { + if (visual_shader.ptr() != p_visual_shader) { + changed = true; + } + } visual_shader = Ref<VisualShader>(p_visual_shader); } else { visual_shader.unref(); @@ -66,6 +76,9 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) { if (visual_shader.is_null()) { hide(); } else { + if (changed) { // to avoid tree collapse + _update_options_menu(); + } _update_graph(); } } @@ -107,16 +120,187 @@ void VisualShaderEditor::remove_custom_type(const Ref<Script> &p_script) { _update_options_menu(); } +bool VisualShaderEditor::_is_available(int p_mode) { + + int current_mode = edit_type->get_selected(); + + if (p_mode != -1) { + + switch (current_mode) { + case VisualShader::TYPE_VERTEX: + current_mode = 1; + break; + case VisualShader::TYPE_FRAGMENT: + current_mode = 2; + break; + case VisualShader::TYPE_LIGHT: + current_mode = 4; + break; + default: + break; + } + + int temp_mode = 0; + + if (p_mode & VisualShader::TYPE_FRAGMENT) { + temp_mode |= 2; + } + + if (p_mode & VisualShader::TYPE_LIGHT) { + temp_mode |= 4; + } + + if (temp_mode == 0) { + temp_mode |= 1; + } + + p_mode = temp_mode; + } + + if (p_mode != -1 && ((p_mode & current_mode) == 0)) { + return false; + } + return true; +} + void VisualShaderEditor::_update_options_menu() { + node_desc->set_text(""); + members_dialog->get_ok()->set_disabled(true); + String prev_category; - add_node->get_popup()->clear(); - for (int i = 0; i < add_options.size(); i++) { - if (prev_category != add_options[i].category) { - add_node->get_popup()->add_separator(add_options[i].category); + String prev_sub_category; + + members->clear(); + TreeItem *root = members->create_item(); + TreeItem *category = NULL; + TreeItem *sub_category = NULL; + + String filter = node_filter->get_text().strip_edges(); + bool use_filter = !filter.empty(); + + Vector<String> categories; + Vector<String> sub_categories; + + int item_count = 0; + int item_count2 = 0; + bool is_first_item = true; + + int current_func = -1; + + if (!visual_shader.is_null()) { + current_func = visual_shader->get_mode(); + } + + for (int i = 0; i < add_options.size() + 1; i++) { + + if (i == add_options.size()) { + if (sub_category != NULL && item_count2 == 0) { + memdelete(sub_category); + --item_count; + } + if (category != NULL && item_count == 0) { + memdelete(category); + } + break; + } + + if (!use_filter || add_options[i].name.findn(filter) != -1) { + + if (prev_category != add_options[i].category) { + if (category != NULL && item_count == 0) { + memdelete(category); + } + + item_count = 0; + prev_sub_category = ""; + category = members->create_item(root); + category->set_text(0, add_options[i].category); + category->set_selectable(0, false); + if (!use_filter) + category->set_collapsed(true); + } + + if (add_options[i].sub_category != "") { + if (prev_sub_category != add_options[i].sub_category) { + if (category != NULL) { + if (sub_category != NULL && item_count2 == 0) { + memdelete(sub_category); + --item_count; + } + ++item_count; + item_count2 = 0; + sub_category = members->create_item(category); + sub_category->set_text(0, add_options[i].sub_category); + sub_category->set_selectable(0, false); + if (!use_filter) + sub_category->set_collapsed(true); + } + } + if (sub_category != NULL) { + if ((add_options[i].func == current_func || add_options[i].func == -1) && _is_available(add_options[i].mode)) { + ++item_count2; + TreeItem *item = members->create_item(sub_category); + item->set_text(0, add_options[i].name); + if (is_first_item) { + item->select(0); + is_first_item = false; + } + switch (add_options[i].return_type) { + case VisualShaderNode::PORT_TYPE_SCALAR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_VECTOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_COLOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); + break; + default: + break; + } + item->set_meta("id", i); + } + } + } else { + if (category != NULL) { + if ((add_options[i].func == current_func || add_options[i].func == -1) && _is_available(add_options[i].mode)) { + ++item_count; + TreeItem *item = members->create_item(category); + item->set_text(0, add_options[i].name); + switch (add_options[i].return_type) { + case VisualShaderNode::PORT_TYPE_SCALAR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_VECTOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_BOOLEAN: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_TRANSFORM: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons")); + break; + case VisualShaderNode::PORT_TYPE_COLOR: + item->set_icon(0, EditorNode::get_singleton()->get_gui_base()->get_icon("Color", "EditorIcons")); + break; + default: + break; + } + item->set_meta("id", i); + } + } + } + + prev_sub_category = add_options[i].sub_category; + prev_category = add_options[i].category; } - add_node->get_popup()->add_item(add_options[i].name, i); - prev_category = add_options[i].category; } } @@ -144,6 +328,21 @@ static Ref<StyleBoxEmpty> make_empty_stylebox(float p_margin_left = -1, float p_ return style; } +void VisualShaderEditor::_update_created_node(GraphNode *node) { + + if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { + Ref<StyleBoxFlat> sb = node->get_stylebox("frame", "GraphNode"); + Color c = sb->get_border_color(); + Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); + mono_color.a = 0.85; + c = mono_color; + + node->add_color_override("title_color", c); + c.a = 0.7; + node->add_color_override("close_color", c); + } +} + void VisualShaderEditor::_update_graph() { if (updating) @@ -160,15 +359,18 @@ void VisualShaderEditor::_update_graph() { for (int i = 0; i < graph->get_child_count(); i++) { if (Object::cast_to<GraphNode>(graph->get_child(i))) { - memdelete(graph->get_child(i)); + Node *node = graph->get_child(i); + graph->remove_child(node); + memdelete(node); i--; } } - static const Color type_color[3] = { - Color::html("#61daf4"), - Color::html("#d67dee"), - Color::html("#f6a86e") + static const Color type_color[4] = { + Color::html("#61daf4"), // scalar + Color::html("#d67dee"), // vector + Color::html("#8da6f0"), // boolean + Color::html("#f6a86e") // transform }; List<VisualShader::Connection> connections; @@ -178,13 +380,32 @@ void VisualShaderEditor::_update_graph() { Vector<int> nodes = visual_shader->get_node_list(type); + Control *offset; + for (int n_i = 0; n_i < nodes.size(); n_i++) { Vector2 position = visual_shader->get_node_position(type, nodes[n_i]); Ref<VisualShaderNode> vsnode = visual_shader->get_node(type, nodes[n_i]); + Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(vsnode.ptr()); + bool is_group = !group_node.is_null(); + Size2 size = Size2(0, 0); + + Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(group_node.ptr()); + bool is_expression = !expression_node.is_null(); + String expression = ""; + GraphNode *node = memnew(GraphNode); - graph->add_child(node); + + if (is_group) { + size = group_node->get_size(); + + node->set_resizable(true); + node->connect("resize_request", this, "_node_resized", varray((int)type, nodes[n_i])); + } + if (is_expression) { + expression = expression_node->get_expression(); + } /*if (!vsnode->is_connected("changed", this, "_node_changed")) { vsnode->connect("changed", this, "_node_changed", varray(vsnode->get_instance_id()), CONNECT_DEFERRED); @@ -205,8 +426,15 @@ void VisualShaderEditor::_update_graph() { Control *custom_editor = NULL; int port_offset = 0; + if (is_group) { + port_offset++; + } + Ref<VisualShaderNodeUniform> uniform = vsnode; if (uniform.is_valid()) { + graph->add_child(node); + _update_created_node(node); + LineEdit *uniform_name = memnew(LineEdit); uniform_name->set_text(uniform->get_uniform_name()); node->add_child(uniform_name); @@ -237,6 +465,24 @@ void VisualShaderEditor::_update_graph() { custom_editor = NULL; } + if (is_group) { + HBoxContainer *hb2 = memnew(HBoxContainer); + + Button *add_input_btn = memnew(Button); + add_input_btn->set_text(TTR("Add input +")); + add_input_btn->connect("pressed", this, "_add_input_port", varray(nodes[n_i], group_node->get_free_input_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "input" + itos(group_node->get_free_input_port_id())), CONNECT_DEFERRED); + hb2->add_child(add_input_btn); + + hb2->add_spacer(); + + Button *add_output_btn = memnew(Button); + add_output_btn->set_text(TTR("Add output +")); + add_output_btn->connect("pressed", this, "_add_output_port", varray(nodes[n_i], group_node->get_free_output_port_id(), VisualShaderNode::PORT_TYPE_VECTOR, "output" + itos(group_node->get_free_output_port_id())), CONNECT_DEFERRED); + hb2->add_child(add_output_btn); + + node->add_child(hb2); + } + for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) { if (vsnode->is_port_separator(i)) { @@ -293,7 +539,8 @@ void VisualShaderEditor::_update_graph() { Vector3 v = default_value; button->set_text(String::num(v.x, 3) + "," + String::num(v.y, 3) + "," + String::num(v.z, 3)); } break; - default: {} + default: { + } } } @@ -305,25 +552,79 @@ void VisualShaderEditor::_update_graph() { if (valid_left) { - Label *label = memnew(Label); - label->set_text(name_left); - label->add_style_override("normal", label_style); //more compact - hb->add_child(label); + if (is_group) { + + OptionButton *type_box = memnew(OptionButton); + hb->add_child(type_box); + type_box->add_item(TTR("Scalar")); + type_box->add_item(TTR("Vector")); + type_box->add_item(TTR("Boolean")); + type_box->add_item(TTR("Transform")); + type_box->select(group_node->get_input_port_type(i)); + type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + type_box->connect("item_selected", this, "_change_input_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED); + + LineEdit *name_box = memnew(LineEdit); + hb->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(name_left); + name_box->set_expand_to_text_length(true); + name_box->connect("text_entered", this, "_change_input_port_name", varray(name_box, nodes[n_i], i)); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, false)); + + if (is_group) { + Button *remove_btn = memnew(Button); + remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + remove_btn->set_tooltip(TTR("Remove") + " " + name_left); + remove_btn->connect("pressed", this, "_remove_input_port", varray(nodes[n_i], i), CONNECT_DEFERRED); + hb->add_child(remove_btn); + } + } else { + + Label *label = memnew(Label); + label->set_text(name_left); + label->add_style_override("normal", label_style); //more compact + hb->add_child(label); + } } hb->add_spacer(); if (valid_right) { - - Label *label = memnew(Label); - label->set_text(name_right); - label->set_align(Label::ALIGN_RIGHT); - label->add_style_override("normal", label_style); //more compact - hb->add_child(label); + if (is_group) { + Button *remove_btn = memnew(Button); + remove_btn->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Remove", "EditorIcons")); + remove_btn->set_tooltip(TTR("Remove") + " " + name_left); + remove_btn->connect("pressed", this, "_remove_output_port", varray(nodes[n_i], i), CONNECT_DEFERRED); + hb->add_child(remove_btn); + + LineEdit *name_box = memnew(LineEdit); + hb->add_child(name_box); + name_box->set_custom_minimum_size(Size2(60 * EDSCALE, 0)); + name_box->set_text(name_right); + name_box->set_expand_to_text_length(true); + name_box->connect("text_entered", this, "_change_output_port_name", varray(name_box, nodes[n_i], i)); + name_box->connect("focus_exited", this, "_port_name_focus_out", varray(name_box, nodes[n_i], i, true)); + + OptionButton *type_box = memnew(OptionButton); + hb->add_child(type_box); + type_box->add_item(TTR("Scalar")); + type_box->add_item(TTR("Vector")); + type_box->add_item(TTR("Boolean")); + type_box->add_item(TTR("Transform")); + type_box->select(group_node->get_output_port_type(i)); + type_box->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); + type_box->connect("item_selected", this, "_change_output_port_type", varray(nodes[n_i], i), CONNECT_DEFERRED); + } else { + Label *label = memnew(Label); + label->set_text(name_right); + label->add_style_override("normal", label_style); //more compact + hb->add_child(label); + } } } - if (valid_right && edit_type->get_selected() == VisualShader::TYPE_FRAGMENT) { + if (valid_right && edit_type->get_selected() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM) { TextureButton *preview = memnew(TextureButton); preview->set_toggle_mode(true); preview->set_normal_texture(get_icon("GuiVisibilityHidden", "EditorIcons")); @@ -338,30 +639,33 @@ void VisualShaderEditor::_update_graph() { hb->add_child(preview); } + if (is_group) { + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + port_offset++; + } + node->add_child(hb); node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]); - - if (EditorSettings::get_singleton()->get("interface/theme/use_graph_node_headers")) { - Ref<StyleBoxFlat> sb = node->get_stylebox("frame", "GraphNode"); - Color c = sb->get_border_color(MARGIN_TOP); - Color mono_color = ((c.r + c.g + c.b) / 3) < 0.7 ? Color(1.0, 1.0, 1.0) : Color(0.0, 0.0, 0.0); - mono_color.a = 0.85; - c = mono_color; - - node->add_color_override("title_color", c); - c.a = 0.7; - node->add_color_override("close_color", c); - } } - if (vsnode->get_output_port_for_preview() >= 0) { + if (vsnode->get_output_port_for_preview() >= 0 && vsnode->get_output_port_type(vsnode->get_output_port_for_preview()) != VisualShaderNode::PORT_TYPE_TRANSFORM) { + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + VisualShaderNodePortPreview *port_preview = memnew(VisualShaderNodePortPreview); port_preview->setup(visual_shader, type, nodes[n_i], vsnode->get_output_port_for_preview()); port_preview->set_h_size_flags(SIZE_SHRINK_CENTER); node->add_child(port_preview); } + offset = memnew(Control); + offset->set_custom_minimum_size(Size2(0, 5 * EDSCALE)); + node->add_child(offset); + String error = vsnode->get_warning(visual_shader->get_mode(), type); if (error != String()) { Label *error_label = memnew(Label); @@ -369,6 +673,44 @@ void VisualShaderEditor::_update_graph() { error_label->set_text(error); node->add_child(error_label); } + + if (is_expression) { + + TextEdit *expression_box = memnew(TextEdit); + expression_node->set_control(expression_box, 0); + node->add_child(expression_box); + + Color text_color = EDITOR_GET("text_editor/highlighting/text_color"); + Color keyword_color = EDITOR_GET("text_editor/highlighting/keyword_color"); + Color comment_color = EDITOR_GET("text_editor/highlighting/comment_color"); + Color symbol_color = EDITOR_GET("text_editor/highlighting/symbol_color"); + + expression_box->set_syntax_coloring(true); + + for (List<String>::Element *E = keyword_list.front(); E; E = E->next()) { + + expression_box->add_keyword_color(E->get(), keyword_color); + } + + expression_box->add_font_override("font", get_font("expression", "EditorFonts")); + expression_box->add_color_override("font_color", text_color); + expression_box->add_color_override("symbol_color", symbol_color); + expression_box->add_color_region("/*", "*/", comment_color, false); + expression_box->add_color_region("//", "", comment_color, false); + + expression_box->set_text(expression); + expression_box->set_context_menu_enabled(false); + expression_box->set_show_line_numbers(true); + + expression_box->connect("focus_exited", this, "_expression_focus_out", varray(expression_box, nodes[n_i])); + } + + if (!uniform.is_valid()) { + graph->add_child(node); + _update_created_node(node); + if (is_group) + call_deferred("_set_node_size", (int)type, nodes[n_i], size); + } } for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) { @@ -382,6 +724,285 @@ void VisualShaderEditor::_update_graph() { } } +void VisualShaderEditor::_add_input_port(int p_node, int p_port, int p_port_type, const String &p_name) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Add input port")); + undo_redo->add_do_method(node.ptr(), "add_input_port", p_port, p_port_type, p_name); + undo_redo->add_undo_method(node.ptr(), "remove_input_port", p_port); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_add_output_port(int p_node, int p_port, int p_port_type, const String &p_name) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Add output port")); + undo_redo->add_do_method(node.ptr(), "add_output_port", p_port, p_port_type, p_name); + undo_redo->add_undo_method(node.ptr(), "remove_output_port", p_port); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_input_port_type(int p_type, int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Change input port type")); + undo_redo->add_do_method(node.ptr(), "set_input_port_type", p_port, p_type); + undo_redo->add_undo_method(node.ptr(), "set_input_port_type", p_port, node->get_input_port_type(p_port)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_output_port_type(int p_type, int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Change output port type")); + undo_redo->add_do_method(node.ptr(), "set_output_port_type", p_port, p_type); + undo_redo->add_undo_method(node.ptr(), "set_output_port_type", p_port, node->get_output_port_type(p_port)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_input_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + undo_redo->create_action(TTR("Change input port name")); + undo_redo->add_do_method(node.ptr(), "set_input_port_name", p_port_id, p_text); + undo_redo->add_undo_method(node.ptr(), "set_input_port_name", p_port_id, node->get_input_port_name(p_port_id)); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *line_edit, int p_node_id, int p_port_id) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + undo_redo->create_action(TTR("Change output port name")); + undo_redo->add_do_method(node.ptr(), "set_output_port_name", p_port_id, p_text); + undo_redo->add_undo_method(node.ptr(), "set_output_port_name", p_port_id, node->get_output_port_name(p_port_id)); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_remove_input_port(int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Remove input port")); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + + int from_node = E->get().from_node; + int from_port = E->get().from_port; + int to_node = E->get().to_node; + int to_port = E->get().to_port; + + if (to_node == p_node) { + if (to_port == p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + } else if (to_port > p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port - 1); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port - 1); + } + } + } + + undo_redo->add_do_method(node.ptr(), "remove_input_port", p_port); + undo_redo->add_undo_method(node.ptr(), "add_input_port", p_port, (int)node->get_input_port_type(p_port), node->get_input_port_name(p_port)); + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + + undo_redo->commit_action(); +} + +void VisualShaderEditor::_remove_output_port(int p_node, int p_port) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Remove output port")); + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + + int from_node = E->get().from_node; + int from_port = E->get().from_port; + int to_node = E->get().to_node; + int to_port = E->get().to_port; + + if (from_node == p_node) { + if (from_port == p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + } else if (from_port > p_port) { + undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port); + + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port - 1, to_node, to_port); + undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port - 1, to_node, to_port); + } + } + } + + undo_redo->add_do_method(node.ptr(), "remove_output_port", p_port); + undo_redo->add_undo_method(node.ptr(), "add_output_port", p_port, (int)node->get_output_port_type(p_port), node->get_output_port_name(p_port)); + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + + undo_redo->commit_action(); +} + +void VisualShaderEditor::_expression_focus_out(Object *text_edit, int p_node) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNodeExpression> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + TextEdit *expression_box = Object::cast_to<TextEdit>(text_edit); + + if (node->get_expression() == expression_box->get_text()) + return; + + undo_redo->create_action(TTR("Set expression")); + undo_redo->add_do_method(node.ptr(), "set_expression", expression_box->get_text()); + undo_redo->add_undo_method(node.ptr(), "set_expression", node->get_expression()); + undo_redo->add_do_method(this, "_rebuild"); + undo_redo->add_undo_method(this, "_rebuild"); + undo_redo->commit_action(); +} + +void VisualShaderEditor::_rebuild() { + EditorNode::get_singleton()->get_log()->clear(); + visual_shader->rebuild(); +} + +void VisualShaderEditor::_set_node_size(int p_type, int p_node, const Vector2 &p_size) { + + VisualShader::Type type = VisualShader::Type(p_type); + Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + Ref<VisualShaderNodeGroupBase> group_node = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + + if (group_node.is_null()) { + return; + } + + Vector2 size = p_size; + + group_node->set_size(size); + + GraphNode *gn = NULL; + if (edit_type->get_selected() == p_type) { // check - otherwise the error will be emitted + Node *node2 = graph->get_node(itos(p_node)); + gn = Object::cast_to<GraphNode>(node2); + if (!gn) + return; + + gn->set_custom_minimum_size(size); + gn->set_size(Size2(1, 1)); + } + + Ref<VisualShaderNodeExpression> expression_node = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (!expression_node.is_null()) { + Control *text_box = expression_node->get_control(0); + Size2 box_size = size; + if (gn != NULL) { + if (box_size.x < 150 * EDSCALE || box_size.y < 0) { + box_size.x = gn->get_size().x; + } + } + box_size.x -= text_box->get_margin(MARGIN_LEFT); + box_size.x -= 28 * EDSCALE; + box_size.y -= text_box->get_margin(MARGIN_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)); + } +} + +void VisualShaderEditor::_node_resized(const Vector2 &p_new_size, int p_type, int p_node) { + + VisualShader::Type type = VisualShader::Type(p_type); + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node); + if (node.is_null()) { + return; + } + + undo_redo->create_action(TTR("Resize VisualShader node"), UndoRedo::MERGE_ENDS); + undo_redo->add_do_method(this, "_set_node_size", p_type, p_node, p_new_size / EDSCALE); + undo_redo->add_undo_method(this, "_set_node_size", p_type, p_node, node->get_size()); + undo_redo->commit_action(); +} + void VisualShaderEditor::_preview_select_port(int p_node, int p_port) { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); @@ -393,7 +1014,7 @@ void VisualShaderEditor::_preview_select_port(int p_node, int p_port) { if (node->get_output_port_for_preview() == p_port) { p_port = -1; //toggle it } - undo_redo->create_action("Set Uniform Name"); + undo_redo->create_action(TTR("Set Uniform Name")); undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", p_port); undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", node->get_output_port_for_preview()); undo_redo->add_do_method(this, "_update_graph"); @@ -411,7 +1032,7 @@ void VisualShaderEditor::_line_edit_changed(const String &p_text, Object *line_e String validated_name = visual_shader->validate_uniform_name(p_text, node); updating = true; - undo_redo->create_action("Set Uniform Name"); + undo_redo->create_action(TTR("Set Uniform Name")); undo_redo->add_do_method(node.ptr(), "set_uniform_name", validated_name); undo_redo->add_undo_method(node.ptr(), "set_uniform_name", node->get_uniform_name()); undo_redo->add_do_method(this, "_update_graph"); @@ -428,6 +1049,52 @@ void VisualShaderEditor::_line_edit_focus_out(Object *line_edit, int p_node_id) _line_edit_changed(text, line_edit, p_node_id); } +void VisualShaderEditor::_port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output) { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + + Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node_id); + ERR_FAIL_COND(!node.is_valid()); + + String text = Object::cast_to<LineEdit>(line_edit)->get_text(); + + if (!p_output) { + if (node->get_input_port_name(p_port_id) == text) + return; + } else { + if (node->get_output_port_name(p_port_id) == text) + return; + } + + List<String> input_names; + List<String> output_names; + + for (int i = 0; i < node->get_input_port_count(); i++) { + if (!p_output && i == p_port_id) continue; + input_names.push_back(node->get_input_port_name(i)); + } + for (int i = 0; i < node->get_output_port_count(); i++) { + if (p_output && i == p_port_id) continue; + output_names.push_back(node->get_output_port_name(i)); + } + + String validated_name = visual_shader->validate_port_name(text, input_names, output_names); + if (validated_name == "") { + if (!p_output) { + Object::cast_to<LineEdit>(line_edit)->set_text(node->get_input_port_name(p_port_id)); + } else { + Object::cast_to<LineEdit>(line_edit)->set_text(node->get_output_port_name(p_port_id)); + } + return; + } + + if (!p_output) { + _change_input_port_name(validated_name, line_edit, p_node_id, p_port_id); + } else { + _change_output_port_name(validated_name, line_edit, p_node_id, p_port_id); + } +} + void VisualShaderEditor::_port_edited() { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); @@ -436,7 +1103,7 @@ void VisualShaderEditor::_port_edited() { Ref<VisualShaderNode> vsn = visual_shader->get_node(type, editing_node); ERR_FAIL_COND(!vsn.is_valid()); - undo_redo->create_action("Set Input Default Port"); + undo_redo->create_action(TTR("Set Input Default Port")); undo_redo->add_do_method(vsn.ptr(), "set_input_port_default_value", editing_port, value); undo_redo->add_undo_method(vsn.ptr(), "set_input_port_default_value", editing_port, vsn->get_input_port_default_value(editing_port)); undo_redo->add_do_method(this, "_update_graph"); @@ -462,7 +1129,7 @@ void VisualShaderEditor::_edit_port_default_input(Object *p_button, int p_node, editing_port = p_port; } -void VisualShaderEditor::_add_node(int p_idx) { +void VisualShaderEditor::_add_node(int p_idx, int p_op_idx) { ERR_FAIL_INDEX(p_idx, add_options.size()); @@ -471,6 +1138,77 @@ void VisualShaderEditor::_add_node(int p_idx) { if (add_options[p_idx].type != String()) { VisualShaderNode *vsn = Object::cast_to<VisualShaderNode>(ClassDB::instance(add_options[p_idx].type)); ERR_FAIL_COND(!vsn); + + VisualShaderNodeScalarConstant *constant = Object::cast_to<VisualShaderNodeScalarConstant>(vsn); + + if (constant) { + if ((int)add_options[p_idx].value != -1) + constant->set_constant(add_options[p_idx].value); + } + + if (p_op_idx != -1) { + + VisualShaderNodeInput *input = Object::cast_to<VisualShaderNodeInput>(vsn); + + if (input) { + input->set_input_name(add_options[p_idx].sub_func_str); + } + + VisualShaderNodeColorOp *colorOp = Object::cast_to<VisualShaderNodeColorOp>(vsn); + + if (colorOp) { + colorOp->set_operator((VisualShaderNodeColorOp::Operator)p_op_idx); + } + + VisualShaderNodeColorFunc *colorFunc = Object::cast_to<VisualShaderNodeColorFunc>(vsn); + + if (colorFunc) { + colorFunc->set_function((VisualShaderNodeColorFunc::Function)p_op_idx); + } + + VisualShaderNodeScalarOp *scalarOp = Object::cast_to<VisualShaderNodeScalarOp>(vsn); + + if (scalarOp) { + scalarOp->set_operator((VisualShaderNodeScalarOp::Operator)p_op_idx); + } + + VisualShaderNodeScalarFunc *scalarFunc = Object::cast_to<VisualShaderNodeScalarFunc>(vsn); + + if (scalarFunc) { + scalarFunc->set_function((VisualShaderNodeScalarFunc::Function)p_op_idx); + } + + VisualShaderNodeVectorOp *vecOp = Object::cast_to<VisualShaderNodeVectorOp>(vsn); + + if (vecOp) { + vecOp->set_operator((VisualShaderNodeVectorOp::Operator)p_op_idx); + } + + VisualShaderNodeVectorFunc *vecFunc = Object::cast_to<VisualShaderNodeVectorFunc>(vsn); + + if (vecFunc) { + vecFunc->set_function((VisualShaderNodeVectorFunc::Function)p_op_idx); + } + + VisualShaderNodeTransformFunc *matFunc = Object::cast_to<VisualShaderNodeTransformFunc>(vsn); + + if (matFunc) { + matFunc->set_function((VisualShaderNodeTransformFunc::Function)p_op_idx); + } + + VisualShaderNodeScalarDerivativeFunc *sderFunc = Object::cast_to<VisualShaderNodeScalarDerivativeFunc>(vsn); + + if (sderFunc) { + sderFunc->set_function((VisualShaderNodeScalarDerivativeFunc::Function)p_op_idx); + } + + VisualShaderNodeVectorDerivativeFunc *vderFunc = Object::cast_to<VisualShaderNodeVectorDerivativeFunc>(vsn); + + if (vderFunc) { + vderFunc->set_function((VisualShaderNodeVectorDerivativeFunc::Function)p_op_idx); + } + } + vsnode = Ref<VisualShaderNode>(vsn); } else { ERR_FAIL_COND(add_options[p_idx].script.is_null()); @@ -481,15 +1219,29 @@ void VisualShaderEditor::_add_node(int p_idx) { vsnode->set_script(add_options[p_idx].script.get_ref_ptr()); } - Point2 position = (graph->get_scroll_ofs() + graph->get_size() * 0.5) / EDSCALE; + Point2 position = graph->get_scroll_ofs(); + + if (saved_node_pos_dirty) { + position += saved_node_pos; + } else { + position += graph->get_size() * 0.5; + position /= EDSCALE; + } + saved_node_pos_dirty = false; VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); int id_to_use = visual_shader->get_valid_node_id(type); - undo_redo->create_action("Add Node to Visual Shader"); + undo_redo->create_action(TTR("Add Node to Visual Shader")); undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, vsnode, position, id_to_use); undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_to_use); + + VisualShaderNodeExpression *expr = Object::cast_to<VisualShaderNodeExpression>(vsnode.ptr()); + if (expr) { + undo_redo->add_do_method(expr, "set_size", Size2(250 * EDSCALE, 150 * EDSCALE)); + } + undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -500,7 +1252,7 @@ void VisualShaderEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_t VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); updating = true; - undo_redo->create_action("Node Moved"); + undo_redo->create_action(TTR("Node Moved")); undo_redo->add_do_method(visual_shader.ptr(), "set_node_position", type, p_node, p_to); undo_redo->add_undo_method(visual_shader.ptr(), "set_node_position", type, p_node, p_from); undo_redo->add_do_method(this, "_update_graph"); @@ -517,11 +1269,10 @@ void VisualShaderEditor::_connection_request(const String &p_from, int p_from_in int to = p_to.to_int(); if (!visual_shader->can_connect_nodes(type, from, p_from_index, to, p_to_index)) { - EditorNode::get_singleton()->show_warning(TTR("Unable to connect, port may be in use or connection may be invalid.")); return; } - undo_redo->create_action("Nodes Connected"); + undo_redo->create_action(TTR("Nodes Connected")); List<VisualShader::Connection> conns; visual_shader->get_node_connections(type, &conns); @@ -550,7 +1301,7 @@ void VisualShaderEditor::_disconnection_request(const String &p_from, int p_from int to = p_to.to_int(); //updating = true; seems graph edit can handle this, no need to protect - undo_redo->create_action("Nodes Disconnected"); + undo_redo->create_action(TTR("Nodes Disconnected")); undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from, p_from_index, to, p_to_index); undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, from, p_from_index, to, p_to_index); undo_redo->add_do_method(this, "_update_graph"); @@ -565,10 +1316,25 @@ void VisualShaderEditor::_connection_to_empty(const String &p_from, int p_from_s void VisualShaderEditor::_delete_request(int which) { VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + Ref<VisualShaderNode> node = Ref<VisualShaderNode>(visual_shader->get_node(type, which)); - undo_redo->create_action("Delete Node"); + undo_redo->create_action(TTR("Delete Node")); undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, which); - undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, visual_shader->get_node(type, which), visual_shader->get_node_position(type, which), which); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, which), which); + + // restore size, inputs and outputs if node is group + VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + if (group) { + undo_redo->add_undo_method(group, "set_size", group->get_size()); + undo_redo->add_undo_method(group, "set_inputs", group->get_inputs()); + undo_redo->add_undo_method(group, "set_outputs", group->get_outputs()); + } + + // restore expression text if node is expression + VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (expression) { + undo_redo->add_undo_method(expression, "set_expression", expression->get_expression()); + } List<VisualShader::Connection> conns; visual_shader->get_node_connections(type, &conns); @@ -600,29 +1366,98 @@ void VisualShaderEditor::_node_selected(Object *p_node) { //EditorNode::get_singleton()->push_item(vsnode.ptr(), "", true); } -void VisualShaderEditor::_input(const Ref<InputEvent> p_event) { - if (graph->has_focus()) { - Ref<InputEventMouseButton> mb = p_event; +void VisualShaderEditor::_graph_gui_input(const Ref<InputEvent> p_event) { - if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) { - add_node->get_popup()->set_position(get_viewport()->get_mouse_position()); - add_node->get_popup()->show_modal(); - } + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_RIGHT) + _show_members_dialog(true); +} + +void VisualShaderEditor::_show_members_dialog(bool at_mouse_pos) { + + members_dialog->popup(); + + if (at_mouse_pos) { + saved_node_pos_dirty = true; + saved_node_pos = graph->get_local_mouse_position(); + + Point2 gpos = Input::get_singleton()->get_mouse_position(); + members_dialog->popup(); + members_dialog->set_position(gpos); + } else { + saved_node_pos_dirty = false; + members_dialog->set_position(graph->get_global_position() + Point2(5 * EDSCALE, 65 * EDSCALE)); + } + + // keep dialog within window bounds + Size2 window_size = OS::get_singleton()->get_window_size(); + Rect2 dialog_rect = members_dialog->get_global_rect(); + 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)); + } + + node_filter->call_deferred("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_scancode() == KEY_UP || + ie->get_scancode() == KEY_DOWN || + ie->get_scancode() == KEY_ENTER || + ie->get_scancode() == KEY_KP_ENTER)) { + + members->call("_gui_input", ie); + node_filter->accept_event(); } } void VisualShaderEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + + node_filter->set_clear_button_enabled(true); + + // collapse tree by default + + TreeItem *category = members->get_root()->get_children(); + while (category) { + category->set_collapsed(true); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(true); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + } + + if (p_what == NOTIFICATION_DRAG_BEGIN) { + Dictionary dd = get_viewport()->gui_get_drag_data(); + if (members->is_visible_in_tree() && dd.has("id")) { + members->set_drop_mode_flags(Tree::DROP_MODE_ON_ITEM); + } + } else if (p_what == NOTIFICATION_DRAG_END) { + members->set_drop_mode_flags(0); + } if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); + node_filter->set_right_icon(Control::get_icon("Search", "EditorIcons")); + + tools->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Tools", "EditorIcons")); + if (p_what == NOTIFICATION_THEME_CHANGED && is_visible_in_tree()) _update_graph(); - } - - if (p_what == NOTIFICATION_PROCESS) { + } else if (p_what == NOTIFICATION_PROCESS) { } } @@ -671,7 +1506,7 @@ void VisualShaderEditor::_duplicate_nodes() { if (nodes.empty()) return; - undo_redo->create_action("Duplicate Nodes"); + undo_redo->create_action(TTR("Duplicate Nodes")); int base_id = visual_shader->get_valid_node_id(type); int id_from = base_id; @@ -687,6 +1522,19 @@ void VisualShaderEditor::_duplicate_nodes() { undo_redo->add_do_method(visual_shader.ptr(), "add_node", type, dupli, visual_shader->get_node_position(type, E->get()) + Vector2(10, 10) * EDSCALE, id_from); undo_redo->add_undo_method(visual_shader.ptr(), "remove_node", type, id_from); + // 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(dupli.ptr(), "set_inputs", group->get_inputs()); + undo_redo->add_do_method(dupli.ptr(), "set_outputs", group->get_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()); + } + id_from++; } @@ -695,7 +1543,7 @@ void VisualShaderEditor::_duplicate_nodes() { for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { if (connection_remap.has(E->get().from_node) && connection_remap.has(E->get().to_node)) { - undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port); + undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, connection_remap[E->get().from_node], E->get().from_port, connection_remap[E->get().to_node], E->get().to_port); } } @@ -718,7 +1566,77 @@ void VisualShaderEditor::_duplicate_nodes() { } } +void VisualShaderEditor::_on_nodes_delete() { + + VisualShader::Type type = VisualShader::Type(edit_type->get_selected()); + List<int> to_erase; + + for (int i = 0; i < graph->get_child_count(); i++) { + GraphNode *gn = Object::cast_to<GraphNode>(graph->get_child(i)); + if (gn) { + if (gn->is_selected() && gn->is_close_button_visible()) { + to_erase.push_back(gn->get_name().operator String().to_int()); + } + } + } + + if (to_erase.empty()) + return; + + undo_redo->create_action(TTR("Delete Nodes")); + + for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { + + Ref<VisualShaderNode> node = visual_shader->get_node(type, F->get()); + + undo_redo->add_do_method(visual_shader.ptr(), "remove_node", type, F->get()); + undo_redo->add_undo_method(visual_shader.ptr(), "add_node", type, node, visual_shader->get_node_position(type, F->get()), F->get()); + + // restore size, inputs and outputs if node is group + VisualShaderNodeGroupBase *group = Object::cast_to<VisualShaderNodeGroupBase>(node.ptr()); + if (group) { + undo_redo->add_undo_method(group, "set_size", group->get_size()); + undo_redo->add_undo_method(group, "set_inputs", group->get_inputs()); + undo_redo->add_undo_method(group, "set_outputs", group->get_outputs()); + } + + // restore expression text if node is expression + VisualShaderNodeExpression *expression = Object::cast_to<VisualShaderNodeExpression>(node.ptr()); + if (expression) { + undo_redo->add_undo_method(expression, "set_expression", expression->get_expression()); + } + } + + List<VisualShader::Connection> conns; + visual_shader->get_node_connections(type, &conns); + + List<VisualShader::Connection> used_conns; + for (List<int>::Element *F = to_erase.front(); F; F = F->next()) { + for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) { + if (E->get().from_node == F->get() || E->get().to_node == F->get()) { + + bool cancel = false; + for (List<VisualShader::Connection>::Element *R = used_conns.front(); R; R = R->next()) { + if (R->get().from_node == E->get().from_node && R->get().from_port == E->get().from_port && R->get().to_node == E->get().to_node && R->get().to_port == E->get().to_port) { + cancel = true; // to avoid ERR_ALREADY_EXISTS warning + break; + } + } + if (!cancel) { + undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes", type, E->get().from_node, E->get().from_port, E->get().to_node, E->get().to_port); + used_conns.push_back(E->get()); + } + } + } + } + + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); +} + void VisualShaderEditor::_mode_selected(int p_id) { + _update_options_menu(); _update_graph(); } @@ -732,7 +1650,7 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> input, St bool type_changed = input->get_input_type_by_name(name) != input->get_input_type_by_name(prev_name); UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - undo_redo->create_action("Visual Shader Input Type Changed"); + undo_redo->create_action(TTR("Visual Shader Input Type Changed")); undo_redo->add_do_method(input.ptr(), "set_input_name", name); undo_redo->add_undo_method(input.ptr(), "set_input_name", prev_name); @@ -756,9 +1674,133 @@ void VisualShaderEditor::_input_select_item(Ref<VisualShaderNodeInput> input, St undo_redo->commit_action(); } -void VisualShaderEditor::_bind_methods() { +void VisualShaderEditor::_member_filter_changed(const String &p_text) { + _update_options_menu(); +} + +void VisualShaderEditor::_member_selected() { + TreeItem *item = members->get_selected(); + + if (item != NULL && item->has_meta("id")) { + members_dialog->get_ok()->set_disabled(false); + node_desc->set_text(add_options[item->get_meta("id")].description); + } else { + members_dialog->get_ok()->set_disabled(true); + node_desc->set_text(""); + } +} + +void VisualShaderEditor::_member_unselected() { +} + +void VisualShaderEditor::_member_create() { + TreeItem *item = members->get_selected(); + if (item != NULL && item->has_meta("id")) { + int idx = members->get_selected()->get_meta("id"); + _add_node(idx, add_options[idx].sub_func); + members_dialog->hide(); + } +} + +void VisualShaderEditor::_tools_menu_option(int p_idx) { + + TreeItem *category = members->get_root()->get_children(); + + switch (p_idx) { + case EXPAND_ALL: + + while (category) { + category->set_collapsed(false); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(false); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + + break; + case COLLAPSE_ALL: + + while (category) { + category->set_collapsed(true); + TreeItem *sub_category = category->get_children(); + while (sub_category) { + sub_category->set_collapsed(true); + sub_category = sub_category->get_next(); + } + category = category->get_next(); + } + + break; + default: + break; + } +} + +Variant VisualShaderEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) { + + if (p_from == members) { + TreeItem *it = members->get_item_at_position(p_point); + if (!it) + return Variant(); + if (!it->has_meta("id")) + return Variant(); + + int id = it->get_meta("id"); + AddOption op = add_options[id]; + + Dictionary d; + d["id"] = id; + if (op.sub_func == -1) { + d["sub_func"] = op.sub_func_str; + } else { + d["sub_func"] = op.sub_func; + } + + Label *label = memnew(Label); + label->set_text(it->get_text(0)); + set_drag_preview(label); + return d; + } + return Variant(); +} + +bool VisualShaderEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const { + + if (p_from == graph) { + + Dictionary d = p_data; + + if (d.has("id")) { + return true; + } + } + + return false; +} + +void VisualShaderEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) { + + if (p_from == graph) { + + Dictionary d = p_data; + + if (d.has("id")) { + int idx = d["id"]; + saved_node_pos = p_point; + saved_node_pos_dirty = true; + _add_node(idx, add_options[idx].sub_func); + } + } +} + +void VisualShaderEditor::_bind_methods() { + ClassDB::bind_method("_rebuild", &VisualShaderEditor::_rebuild); ClassDB::bind_method("_update_graph", &VisualShaderEditor::_update_graph); + ClassDB::bind_method("_update_options_menu", &VisualShaderEditor::_update_options_menu); + ClassDB::bind_method("_expression_focus_out", &VisualShaderEditor::_expression_focus_out); ClassDB::bind_method("_add_node", &VisualShaderEditor::_add_node); ClassDB::bind_method("_node_dragged", &VisualShaderEditor::_node_dragged); ClassDB::bind_method("_connection_request", &VisualShaderEditor::_connection_request); @@ -766,17 +1808,42 @@ void VisualShaderEditor::_bind_methods() { ClassDB::bind_method("_node_selected", &VisualShaderEditor::_node_selected); ClassDB::bind_method("_scroll_changed", &VisualShaderEditor::_scroll_changed); ClassDB::bind_method("_delete_request", &VisualShaderEditor::_delete_request); + ClassDB::bind_method("_on_nodes_delete", &VisualShaderEditor::_on_nodes_delete); ClassDB::bind_method("_node_changed", &VisualShaderEditor::_node_changed); ClassDB::bind_method("_edit_port_default_input", &VisualShaderEditor::_edit_port_default_input); ClassDB::bind_method("_port_edited", &VisualShaderEditor::_port_edited); ClassDB::bind_method("_connection_to_empty", &VisualShaderEditor::_connection_to_empty); ClassDB::bind_method("_line_edit_focus_out", &VisualShaderEditor::_line_edit_focus_out); ClassDB::bind_method("_line_edit_changed", &VisualShaderEditor::_line_edit_changed); + ClassDB::bind_method("_port_name_focus_out", &VisualShaderEditor::_port_name_focus_out); ClassDB::bind_method("_duplicate_nodes", &VisualShaderEditor::_duplicate_nodes); ClassDB::bind_method("_mode_selected", &VisualShaderEditor::_mode_selected); ClassDB::bind_method("_input_select_item", &VisualShaderEditor::_input_select_item); ClassDB::bind_method("_preview_select_port", &VisualShaderEditor::_preview_select_port); - ClassDB::bind_method("_input", &VisualShaderEditor::_input); + ClassDB::bind_method("_graph_gui_input", &VisualShaderEditor::_graph_gui_input); + ClassDB::bind_method("_add_input_port", &VisualShaderEditor::_add_input_port); + ClassDB::bind_method("_change_input_port_type", &VisualShaderEditor::_change_input_port_type); + ClassDB::bind_method("_change_input_port_name", &VisualShaderEditor::_change_input_port_name); + ClassDB::bind_method("_remove_input_port", &VisualShaderEditor::_remove_input_port); + ClassDB::bind_method("_add_output_port", &VisualShaderEditor::_add_output_port); + ClassDB::bind_method("_change_output_port_type", &VisualShaderEditor::_change_output_port_type); + ClassDB::bind_method("_change_output_port_name", &VisualShaderEditor::_change_output_port_name); + ClassDB::bind_method("_remove_output_port", &VisualShaderEditor::_remove_output_port); + ClassDB::bind_method("_node_resized", &VisualShaderEditor::_node_resized); + ClassDB::bind_method("_set_node_size", &VisualShaderEditor::_set_node_size); + + ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw); + ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw); + ClassDB::bind_method(D_METHOD("drop_data_fw"), &VisualShaderEditor::drop_data_fw); + + ClassDB::bind_method("_is_available", &VisualShaderEditor::_is_available); + ClassDB::bind_method("_tools_menu_option", &VisualShaderEditor::_tools_menu_option); + ClassDB::bind_method("_show_members_dialog", &VisualShaderEditor::_show_members_dialog); + ClassDB::bind_method("_sbox_input", &VisualShaderEditor::_sbox_input); + ClassDB::bind_method("_member_filter_changed", &VisualShaderEditor::_member_filter_changed); + ClassDB::bind_method("_member_selected", &VisualShaderEditor::_member_selected); + ClassDB::bind_method("_member_unselected", &VisualShaderEditor::_member_unselected); + ClassDB::bind_method("_member_create", &VisualShaderEditor::_member_create); } VisualShaderEditor *VisualShaderEditor::singleton = NULL; @@ -785,10 +1852,15 @@ VisualShaderEditor::VisualShaderEditor() { singleton = this; updating = false; + saved_node_pos_dirty = false; + saved_node_pos = Point2(0, 0); + ShaderLanguage::get_keyword_list(&keyword_list); graph = memnew(GraphEdit); add_child(graph); + graph->set_drag_forwarding(this); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_VECTOR); graph->add_valid_right_disconnect_type(VisualShaderNode::PORT_TYPE_TRANSFORM); //graph->add_valid_left_disconnect_type(0); @@ -798,10 +1870,17 @@ VisualShaderEditor::VisualShaderEditor() { graph->connect("node_selected", this, "_node_selected"); graph->connect("scroll_offset_changed", this, "_scroll_changed"); graph->connect("duplicate_nodes_request", this, "_duplicate_nodes"); + graph->connect("delete_nodes_request", this, "_on_nodes_delete"); + graph->connect("gui_input", this, "_graph_gui_input"); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_SCALAR, VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_SCALAR); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_VECTOR, VisualShaderNode::PORT_TYPE_BOOLEAN); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_SCALAR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_VECTOR); + graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_BOOLEAN, VisualShaderNode::PORT_TYPE_BOOLEAN); graph->add_valid_connection_type(VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShaderNode::PORT_TYPE_TRANSFORM); VSeparator *vs = memnew(VSeparator); @@ -817,40 +1896,392 @@ VisualShaderEditor::VisualShaderEditor() { graph->get_zoom_hbox()->add_child(edit_type); graph->get_zoom_hbox()->move_child(edit_type, 0); - add_node = memnew(MenuButton); + add_node = memnew(ToolButton); 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->get_popup()->connect("id_pressed", this, "_add_node"); - - add_options.push_back(AddOption("Scalar", "Constants", "VisualShaderNodeScalarConstant")); - add_options.push_back(AddOption("Vector", "Constants", "VisualShaderNodeVec3Constant")); - add_options.push_back(AddOption("Color", "Constants", "VisualShaderNodeColorConstant")); - add_options.push_back(AddOption("Transform", "Constants", "VisualShaderNodeTransformConstant")); - add_options.push_back(AddOption("Texture", "Constants", "VisualShaderNodeTexture")); - add_options.push_back(AddOption("CubeMap", "Constants", "VisualShaderNodeCubeMap")); - add_options.push_back(AddOption("ScalarOp", "Operators", "VisualShaderNodeScalarOp")); - add_options.push_back(AddOption("VectorOp", "Operators", "VisualShaderNodeVectorOp")); - add_options.push_back(AddOption("ColorOp", "Operators", "VisualShaderNodeColorOp")); - add_options.push_back(AddOption("TransformMult", "Operators", "VisualShaderNodeTransformMult")); - add_options.push_back(AddOption("TransformVectorMult", "Operators", "VisualShaderNodeTransformVecMult")); - add_options.push_back(AddOption("ScalarFunc", "Functions", "VisualShaderNodeScalarFunc")); - add_options.push_back(AddOption("VectorFunc", "Functions", "VisualShaderNodeVectorFunc")); - add_options.push_back(AddOption("DotProduct", "Functions", "VisualShaderNodeDotProduct")); - add_options.push_back(AddOption("VectorLen", "Functions", "VisualShaderNodeVectorLen")); - add_options.push_back(AddOption("ScalarInterp", "Interpolation", "VisualShaderNodeScalarInterp")); - add_options.push_back(AddOption("VectorInterp", "Interpolation", "VisualShaderNodeVectorInterp")); - add_options.push_back(AddOption("VectorCompose", "Compose", "VisualShaderNodeVectorCompose")); - add_options.push_back(AddOption("TransformCompose", "Compose", "VisualShaderNodeTransformCompose")); - add_options.push_back(AddOption("VectorDecompose", "Decompose", "VisualShaderNodeVectorDecompose")); - add_options.push_back(AddOption("TransformDecompose", "Decompose", "VisualShaderNodeTransformDecompose")); - add_options.push_back(AddOption("Scalar", "Uniforms", "VisualShaderNodeScalarUniform")); - add_options.push_back(AddOption("Vector", "Uniforms", "VisualShaderNodeVec3Uniform")); - add_options.push_back(AddOption("Color", "Uniforms", "VisualShaderNodeColorUniform")); - add_options.push_back(AddOption("Transform", "Uniforms", "VisualShaderNodeTransformUniform")); - add_options.push_back(AddOption("Texture", "Uniforms", "VisualShaderNodeTextureUniform")); - add_options.push_back(AddOption("CubeMap", "Uniforms", "VisualShaderNodeCubeMapUniform")); - add_options.push_back(AddOption("Input", "Inputs", "VisualShaderNodeInput")); + add_node->connect("pressed", this, "_show_members_dialog", varray(false)); + + /////////////////////////////////////// + // SHADER NODES TREE + /////////////////////////////////////// + + VBoxContainer *members_vb = memnew(VBoxContainer); + members_vb->set_v_size_flags(SIZE_EXPAND_FILL); + + HBoxContainer *filter_hb = memnew(HBoxContainer); + members_vb->add_child(filter_hb); + + node_filter = memnew(LineEdit); + filter_hb->add_child(node_filter); + node_filter->connect("text_changed", this, "_member_filter_changed"); + node_filter->connect("gui_input", this, "_sbox_input"); + node_filter->set_h_size_flags(SIZE_EXPAND_FILL); + node_filter->set_placeholder(TTR("Search")); + + tools = memnew(MenuButton); + filter_hb->add_child(tools); + tools->set_tooltip(TTR("Options")); + tools->get_popup()->connect("id_pressed", this, "_tools_menu_option"); + tools->get_popup()->add_item(TTR("Expand All"), EXPAND_ALL); + tools->get_popup()->add_item(TTR("Collapse All"), COLLAPSE_ALL); + + members = memnew(Tree); + members_vb->add_child(members); + members->set_drag_forwarding(this); + members->set_h_size_flags(SIZE_EXPAND_FILL); + members->set_v_size_flags(SIZE_EXPAND_FILL); + members->set_hide_root(true); + members->set_allow_reselect(true); + members->set_hide_folding(false); + members->set_custom_minimum_size(Size2(180 * EDSCALE, 200 * EDSCALE)); + members->connect("item_activated", this, "_member_create"); + members->connect("item_selected", this, "_member_selected"); + members->connect("nothing_selected", this, "_member_unselected"); + + Label *desc_label = memnew(Label); + members_vb->add_child(desc_label); + desc_label->set_text(TTR("Description:")); + + node_desc = memnew(RichTextLabel); + members_vb->add_child(node_desc); + node_desc->set_h_size_flags(SIZE_EXPAND_FILL); + node_desc->set_v_size_flags(SIZE_FILL); + node_desc->set_custom_minimum_size(Size2(0, 70 * EDSCALE)); + + members_dialog = memnew(ConfirmationDialog); + members_dialog->set_title(TTR("Create Shader Node")); + members_dialog->add_child(members_vb); + members_dialog->get_ok()->set_text(TTR("Create")); + members_dialog->get_ok()->connect("pressed", this, "_member_create"); + members_dialog->get_ok()->set_disabled(true); + members_dialog->set_resizable(true); + members_dialog->set_as_minsize(); + add_child(members_dialog); + + alert = memnew(AcceptDialog); + alert->set_as_minsize(); + alert->get_label()->set_autowrap(true); + alert->get_label()->set_align(Label::ALIGN_CENTER); + alert->get_label()->set_valign(Label::VALIGN_CENTER); + alert->get_label()->set_custom_minimum_size(Size2(400, 60) * EDSCALE); + add_child(alert); + + /////////////////////////////////////// + // SHADER NODES TREE OPTIONS + /////////////////////////////////////// + + // COLOR + + add_options.push_back(AddOption("ColorFunc", "Color", "Common", "VisualShaderNodeColorFunc", TTR("Color function."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ColorOp", "Color", "Common", "VisualShaderNodeColorOp", TTR("Color operator."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Grayscale", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Grayscale function."), VisualShaderNodeColorFunc::FUNC_GRAYSCALE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("HSV2RGB", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts HSV vector to RGB equivalent."), VisualShaderNodeVectorFunc::FUNC_HSV2RGB, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("RGB2HSV", "Color", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts RGB vector to HSV equivalent."), VisualShaderNodeVectorFunc::FUNC_RGB2HSV, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sepia", "Color", "Functions", "VisualShaderNodeColorFunc", TTR("Sepia function."), VisualShaderNodeColorFunc::FUNC_SEPIA, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Burn", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Burn operator."), VisualShaderNodeColorOp::OP_BURN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Darken", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Darken operator."), VisualShaderNodeColorOp::OP_DARKEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Difference", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Difference operator."), VisualShaderNodeColorOp::OP_DIFFERENCE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Dodge", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Dodge operator."), VisualShaderNodeColorOp::OP_DODGE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("HardLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("HardLight operator"), VisualShaderNodeColorOp::OP_HARD_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Lighten", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Lighten operator."), VisualShaderNodeColorOp::OP_LIGHTEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Overlay", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Overlay operator."), VisualShaderNodeColorOp::OP_OVERLAY, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Screen", "Color", "Operators", "VisualShaderNodeColorOp", TTR("Screen operator."), VisualShaderNodeColorOp::OP_SCREEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SoftLight", "Color", "Operators", "VisualShaderNodeColorOp", TTR("SoftLight operator."), VisualShaderNodeColorOp::OP_SOFT_LIGHT, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("ColorConstant", "Color", "Variables", "VisualShaderNodeColorConstant", TTR("Color constant."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("ColorUniform", "Color", "Variables", "VisualShaderNodeColorUniform", TTR("Color uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + // BOOLEAN + + add_options.push_back(AddOption("If", "Conditional", "Functions", "VisualShaderNodeIf", TTR("Returns an associated vector if the provided scalars are equal, greater or less."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Switch", "Conditional", "Functions", "VisualShaderNodeSwitch", TTR("Returns an associated vector if the provided boolean value is true or false."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("BooleanConstant", "Conditional", "Variables", "VisualShaderNodeBooleanConstant", TTR("Boolean constant."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); + add_options.push_back(AddOption("BooleanUniform", "Conditional", "Variables", "VisualShaderNodeBooleanUniform", TTR("Boolean uniform."), -1, VisualShaderNode::PORT_TYPE_BOOLEAN)); + + // INPUT + + // SPATIAL-FOR-ALL + + add_options.push_back(AddOption("Camera", "Input", "All", "VisualShaderNodeInput", TTR("'camera' input parameter for all shader modes."), "camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("InvCamera", "Input", "All", "VisualShaderNodeInput", TTR("'inv_camera' input parameter for all shader modes."), "inv_camera", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("InvProjection", "Input", "All", "VisualShaderNodeInput", TTR("'inv_projection' input parameter for all shader modes."), "inv_projection", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Normal", "Input", "All", "VisualShaderNodeInput", TTR("'normal' input parameter for all shader modes."), "normal", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Projection", "Input", "All", "VisualShaderNodeInput", TTR("'projection' input parameter for all shader modes."), "projection", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", TTR("'time' input parameter for all shader modes."), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ViewportSize", "Input", "All", "VisualShaderNodeInput", TTR("'viewport_size' input parameter for all shader modes."), "viewport_size", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("World", "Input", "All", "VisualShaderNodeInput", TTR("'world' input parameter for all shader modes."), "world", VisualShaderNode::PORT_TYPE_TRANSFORM, -1, Shader::MODE_SPATIAL)); + + // CANVASITEM-FOR-ALL + + add_options.push_back(AddOption("Alpha", "Input", "All", "VisualShaderNodeInput", TTR("'alpha' input parameter for all shader modes."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Color", "Input", "All", "VisualShaderNodeInput", TTR("'color' input parameter for all shader modes."), "color", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("TexturePixelSize", "Input", "All", "VisualShaderNodeInput", TTR("'texture_pixel_size' input parameter for all shader modes."), "texture_pixel_size", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Time", "Input", "All", "VisualShaderNodeInput", TTR("'time' input parameter for all shader modes."), "time", VisualShaderNode::PORT_TYPE_SCALAR, -1, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("UV", "Input", "All", "VisualShaderNodeInput", TTR("'uv' input parameter for all shader modes."), "uv", VisualShaderNode::PORT_TYPE_VECTOR, -1, Shader::MODE_CANVAS_ITEM)); + + ///////////////// + + add_options.push_back(AddOption("Input", "Input", "Common", "VisualShaderNodeInput", TTR("Input parameter."))); + + // SPATIAL INPUTS + + add_options.push_back(AddOption("Alpha", "Input", "Fragment", "VisualShaderNodeInput", TTR("'alpha' input parameter for vertex and fragment shader modes."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Binormal", "Input", "Fragment", "VisualShaderNodeInput", TTR("'binormal' input parameter for vertex and fragment shader modes."), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Color", "Input", "Fragment", "VisualShaderNodeInput", TTR("'color' input parameter for vertex and fragment shader modes."), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment and light shader modes."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'point_coord' input parameter for fragment shader mode."), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", TTR("'screen_uv' input parameter for fragment shader mode."), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Side", "Input", "Fragment", "VisualShaderNodeInput", TTR("'side' input parameter for fragment shader mode."), "side", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Tangent", "Input", "Fragment", "VisualShaderNodeInput", TTR("'tangent' input parameter for vertex and fragment shader modes."), "tangent", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("UV", "Input", "Fragment", "VisualShaderNodeInput", TTR("'uv' input parameter for vertex and fragment shader modes."), "uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("UV2", "Input", "Fragment", "VisualShaderNodeInput", TTR("'uv2' input parameter for vertex and fragment shader modes."), "uv2", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Vertex", "Input", "Fragment", "VisualShaderNodeInput", TTR("'vertex' input parameter for vertex and fragment shader modes."), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("View", "Input", "Fragment", "VisualShaderNodeInput", TTR("'view' input parameter for fragment and light shader modes."), "view", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_SPATIAL)); + + add_options.push_back(AddOption("Albedo", "Input", "Light", "VisualShaderNodeInput", TTR("'albedo' input parameter for light shader mode."), "albedo", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Attenuation", "Input", "Light", "VisualShaderNodeInput", TTR("'attenuation' input parameter for light shader mode."), "attenuation", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Diffuse", "Input", "Light", "VisualShaderNodeInput", TTR("'diffuse' input parameter for light shader mode."), "diffuse", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment and light shader modes."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Light", "Input", "Light", "VisualShaderNodeInput", TTR("'light' input parameter for light shader mode."), "light", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", TTR("'light_color' input parameter for light shader mode."), "light_color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Roughness", "Input", "Light", "VisualShaderNodeInput", TTR("'roughness' input parameter for light shader mode."), "roughness", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Specular", "Input", "Light", "VisualShaderNodeInput", TTR("'specular' input parameter for light shader mode."), "specular", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Transmission", "Input", "Light", "VisualShaderNodeInput", TTR("'transmission' input parameter for light shader mode."), "transmission", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("View", "Input", "Light", "VisualShaderNodeInput", TTR("'view' input parameter for fragment and light shader modes."), "view", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_SPATIAL)); + + add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", TTR("'alpha' input parameter for vertex and fragment shader modes."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Binormal", "Input", "Vertex", "VisualShaderNodeInput", TTR("'binormal' input parameter for vertex and fragment shader modes."), "binormal", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", TTR("'color' input parameter for vertex and fragment shader modes."), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("ModelView", "Input", "Vertex", "VisualShaderNodeInput", TTR("'modelview' input parameter for vertex shader mode."), "modelview", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", TTR("'point_size' input parameter for vertex shader mode."), "point_size", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Tangent", "Input", "Vertex", "VisualShaderNodeInput", TTR("'tangent' input parameter for vertex and fragment shader mode."), "tangent", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("UV", "Input", "Vertex", "VisualShaderNodeInput", TTR("'uv' input parameter for vertex and fragment shader modes."), "uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("UV2", "Input", "Vertex", "VisualShaderNodeInput", TTR("'uv2' input parameter for vertex and fragment shader modes."), "uv2", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", TTR("'vertex' input parameter for vertex and fragment shader modes."), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_SPATIAL)); + + // CANVASITEM INPUTS + + add_options.push_back(AddOption("FragCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment and light shader modes."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightPass", "Input", "Fragment", "VisualShaderNodeInput", TTR("'light_pass' input parameter for vertex and fragment shader modes."), "light_pass", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("PointCoord", "Input", "Fragment", "VisualShaderNodeInput", TTR("'point_coord' input parameter for fragment and light shader modes."), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("ScreenPixelSize", "Input", "Fragment", "VisualShaderNodeInput", TTR("'screen_pixel_size' input parameter for fragment shader mode."), "screen_pixel_size", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("ScreenUV", "Input", "Fragment", "VisualShaderNodeInput", TTR("'screen_uv' input parameter for fragment and light shader modes."), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT, Shader::MODE_CANVAS_ITEM)); + + add_options.push_back(AddOption("FragCoord", "Input", "Light", "VisualShaderNodeInput", TTR("'fragcoord' input parameter for fragment and light shader modes."), "fragcoord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightAlpha", "Input", "Light", "VisualShaderNodeInput", TTR("'light_alpha' input parameter for light shader mode."), "light_alpha", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightColor", "Input", "Light", "VisualShaderNodeInput", TTR("'light_color' input parameter for light shader mode."), "light_color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightHeight", "Input", "Light", "VisualShaderNodeInput", TTR("'light_height' input parameter for light shader mode."), "light_height", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightUV", "Input", "Light", "VisualShaderNodeInput", TTR("'light_uv' input parameter for light shader mode."), "light_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightVector", "Input", "Light", "VisualShaderNodeInput", TTR("'light_vec' input parameter for light shader mode."), "light_vec", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Normal", "Input", "Light", "VisualShaderNodeInput", TTR("'normal' input parameter for light shader mode."), "normal", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("PointCoord", "Input", "Light", "VisualShaderNodeInput", TTR("'point_coord' input parameter for fragment and light shader modes."), "point_coord", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("ScreenUV", "Input", "Light", "VisualShaderNodeInput", TTR("'screen_uv' input parameter for fragment and light shader modes."), "screen_uv", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("ShadowColor", "Input", "Light", "VisualShaderNodeInput", TTR("'shadow_color' input parameter for light shader mode."), "shadow_color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_LIGHT, Shader::MODE_CANVAS_ITEM)); + + add_options.push_back(AddOption("Extra", "Input", "Vertex", "VisualShaderNodeInput", TTR("'extra' input parameter for vertex shader mode."), "extra", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("LightPass", "Input", "Vertex", "VisualShaderNodeInput", TTR("'light_pass' input parameter for vertex and fragment shader modes."), "light_pass", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("PointSize", "Input", "Vertex", "VisualShaderNodeInput", TTR("'point_size' input parameter for vertex shader mode."), "point_size", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Projection", "Input", "Vertex", "VisualShaderNodeInput", TTR("'projection' input parameter for vertex shader mode."), "projection", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("Vertex", "Input", "Vertex", "VisualShaderNodeInput", TTR("'vertex' input parameter for vertex shader mode."), "vertex", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_CANVAS_ITEM)); + add_options.push_back(AddOption("World", "Input", "Vertex", "VisualShaderNodeInput", TTR("'world' input parameter for vertex shader mode."), "world", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::MODE_CANVAS_ITEM)); + + // PARTICLES INPUTS + + add_options.push_back(AddOption("Active", "Input", "Vertex", "VisualShaderNodeInput", TTR("'active' input parameter for vertex shader mode."), "active", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Alpha", "Input", "Vertex", "VisualShaderNodeInput", TTR("'alpha' input parameter for vertex shader mode."), "alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Color", "Input", "Vertex", "VisualShaderNodeInput", TTR("'color' input parameter for vertex shader mode."), "color", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Custom", "Input", "Vertex", "VisualShaderNodeInput", TTR("'custom' input parameter for vertex shader mode."), "custom", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("CustomAlpha", "Input", "Vertex", "VisualShaderNodeInput", TTR("'custom_alpha' input parameter for vertex shader mode."), "custom_alpha", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Delta", "Input", "Vertex", "VisualShaderNodeInput", TTR("'delta' input parameter for vertex shader mode."), "delta", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("EmissionTransform", "Input", "Vertex", "VisualShaderNodeInput", TTR("'emission_transform' input parameter for vertex shader mode."), "emission_transform", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Index", "Input", "Vertex", "VisualShaderNodeInput", TTR("'index' input parameter for vertex shader mode."), "index", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("LifeTime", "Input", "Vertex", "VisualShaderNodeInput", TTR("'lifetime' input parameter for vertex shader mode."), "lifetime", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Restart", "Input", "Vertex", "VisualShaderNodeInput", TTR("'restart' input parameter for vertex shader mode."), "restart", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Time", "Input", "Vertex", "VisualShaderNodeInput", TTR("'time' input parameter for vertex shader mode."), "time", VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Transform", "Input", "Vertex", "VisualShaderNodeInput", TTR("'transform' input parameter for vertex shader mode."), "transform", VisualShaderNode::PORT_TYPE_TRANSFORM, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + add_options.push_back(AddOption("Velocity", "Input", "Vertex", "VisualShaderNodeInput", TTR("'velocity' input parameter for vertex shader mode."), "velocity", VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_VERTEX, Shader::MODE_PARTICLES)); + + // SCALAR + + add_options.push_back(AddOption("ScalarFunc", "Scalar", "Common", "VisualShaderNodeScalarFunc", TTR("Scalar function."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ScalarOp", "Scalar", "Common", "VisualShaderNodeScalarOp", TTR("Scalar operator."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + + //CONSTANTS + + add_options.push_back(AddOption("E", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("E constant (2.718282). Represents the base of the natural logarithm."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_E)); + add_options.push_back(AddOption("Epsilon", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Epsilon constant (0.00001). Smallest possible scalar number."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, CMP_EPSILON)); + add_options.push_back(AddOption("Phi", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Phi constant (1.618034). Golden ratio."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, 1.618034f)); + add_options.push_back(AddOption("Pi/4", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi/4 constant (0.785398) or 45 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI / 4)); + add_options.push_back(AddOption("Pi/2", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi/2 constant (1.570796) or 90 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI / 2)); + add_options.push_back(AddOption("Pi", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Pi constant (3.141593) or 180 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_PI)); + add_options.push_back(AddOption("Tau", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Tau constant (6.283185) or 360 degrees."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_TAU)); + add_options.push_back(AddOption("Sqrt2", "Scalar", "Constants", "VisualShaderNodeScalarConstant", TTR("Sqrt2 constant (1.414214). Square root of 2."), -1, VisualShaderNode::PORT_TYPE_SCALAR, -1, -1, Math_SQRT2)); + + // FUNCTIONS + + add_options.push_back(AddOption("Abs", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeScalarFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ACos", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ACosH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ASin", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ASIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ASinH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_ASINH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATan", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATan2", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeScalarOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ATanH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Ceil", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeScalarFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Clamp", "Scalar", "Functions", "VisualShaderNodeScalarClamp", TTR("Constrains a value to lie between two further values."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Cos", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_COS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("CosH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic cosine of the parameter."), VisualShaderNodeScalarFunc::FUNC_COSH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Degrees", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Converts a quantity in radians to degrees."), VisualShaderNodeScalarFunc::FUNC_DEGREES, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-e Exponential."), VisualShaderNodeScalarFunc::FUNC_EXP, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp2", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-2 Exponential."), VisualShaderNodeScalarFunc::FUNC_EXP2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Floor", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Finds the nearest integer less than or equal to the parameter."), VisualShaderNodeScalarFunc::FUNC_FLOOR, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Fract", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Computes the fractional part of the argument."), VisualShaderNodeScalarFunc::FUNC_FRAC, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("InverseSqrt", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the inverse of the square root of the parameter."), VisualShaderNodeScalarFunc::FUNC_INVERSE_SQRT, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Natural logarithm."), VisualShaderNodeScalarFunc::FUNC_LOG, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log2", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Base-2 logarithm."), VisualShaderNodeScalarFunc::FUNC_LOG2, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Max", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the greater of two values."), VisualShaderNodeScalarOp::OP_MAX, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Min", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the lesser of two values."), VisualShaderNodeScalarOp::OP_MIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Mix", "Scalar", "Functions", "VisualShaderNodeScalarInterp", TTR("Linear interpolation between two scalars."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Negate", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeScalarFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("OneMinus", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 - scalar"), VisualShaderNodeScalarFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Pow", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeScalarOp::OP_POW, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Radians", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeScalarFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Reciprocal", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("1.0 / scalar"), VisualShaderNodeScalarFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Round", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the nearest integer to the parameter."), VisualShaderNodeScalarFunc::FUNC_ROUND, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("RoundEven", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the nearest even integer to the parameter."), VisualShaderNodeScalarFunc::FUNC_ROUNDEVEN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Saturate", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Clamps the value between 0.0 and 1.0."), VisualShaderNodeScalarFunc::FUNC_SATURATE, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sign", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeScalarFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sin", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("SinH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic sine of the parameter."), VisualShaderNodeScalarFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Sqrt", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeScalarFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("SmoothStep", "Scalar", "Functions", "VisualShaderNodeScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if x is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Step", "Scalar", "Functions", "VisualShaderNodeScalarOp", TTR("Step function( scalar(edge), scalar(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), VisualShaderNodeScalarOp::OP_STEP, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Tan", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("TanH", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Returns the hyperbolic tangent of the parameter."), VisualShaderNodeScalarFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Trunc", "Scalar", "Functions", "VisualShaderNodeScalarFunc", TTR("(GLES3 only) Finds the truncated value of the parameter."), VisualShaderNodeScalarFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("Add", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Adds scalar to scalar."), VisualShaderNodeScalarOp::OP_ADD, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Divide", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Divides scalar by scalar."), VisualShaderNodeScalarOp::OP_DIV, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Multiply", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Multiplies scalar by scalar."), VisualShaderNodeScalarOp::OP_MUL, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Remainder", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Returns the remainder of the two scalars."), VisualShaderNodeScalarOp::OP_MOD, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Subtract", "Scalar", "Operators", "VisualShaderNodeScalarOp", TTR("Subtracts scalar from scalar."), VisualShaderNodeScalarOp::OP_SUB, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("ScalarConstant", "Scalar", "Variables", "VisualShaderNodeScalarConstant", TTR("Scalar constant."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("ScalarUniform", "Scalar", "Variables", "VisualShaderNodeScalarUniform", TTR("Scalar uniform."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + + // TEXTURES + + add_options.push_back(AddOption("CubeMap", "Textures", "Functions", "VisualShaderNodeCubeMap", TTR("Perform the cubic texture lookup."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("Texture", "Textures", "Functions", "VisualShaderNodeTexture", TTR("Perform the texture lookup."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + add_options.push_back(AddOption("CubeMapUniform", "Textures", "Variables", "VisualShaderNodeCubeMapUniform", TTR("Cubic texture uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + add_options.push_back(AddOption("TextureUniform", "Textures", "Variables", "VisualShaderNodeTextureUniform", TTR("2D texture uniform."), -1, VisualShaderNode::PORT_TYPE_COLOR)); + + // TRANSFORM + + add_options.push_back(AddOption("TransformFunc", "Transform", "Common", "VisualShaderNodeTransformFunc", TTR("Transform function."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + add_options.push_back(AddOption("OuterProduct", "Transform", "Composition", "VisualShaderNodeOuterProduct", TTR("(GLES3 only) Calculate the outer product of a pair of vectors.\n\nOuterProduct treats the first parameter 'c' as a column vector (matrix with one column) and the second parameter 'r' as a row vector (matrix with one row) and does a linear algebraic matrix multiply 'c * r', yielding a matrix whose number of rows is the number of components in 'c' and whose number of columns is the number of components in 'r'."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformCompose", "Transform", "Composition", "VisualShaderNodeTransformCompose", TTR("Composes transform from four vectors."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformDecompose", "Transform", "Composition", "VisualShaderNodeTransformDecompose", TTR("Decomposes transform to four vectors."))); + + add_options.push_back(AddOption("Determinant", "Transform", "Functions", "VisualShaderNodeDeterminant", TTR("(GLES3 only) Calculates the determinant of a transform."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Inverse", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("(GLES3 only) Calculates the inverse of a transform."), VisualShaderNodeTransformFunc::FUNC_INVERSE, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("Transpose", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("(GLES3 only) Calculates the transpose of a transform."), VisualShaderNodeTransformFunc::FUNC_TRANSPOSE, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + add_options.push_back(AddOption("TransformMult", "Transform", "Operators", "VisualShaderNodeTransformMult", TTR("Multiplies transform by transform."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformVectorMult", "Transform", "Operators", "VisualShaderNodeTransformVecMult", TTR("Multiplies vector by transform."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("TransformConstant", "Transform", "Variables", "VisualShaderNodeTransformConstant", TTR("Transform constant."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + add_options.push_back(AddOption("TransformUniform", "Transform", "Variables", "VisualShaderNodeTransformUniform", TTR("Transform uniform."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM)); + + // VECTOR + + add_options.push_back(AddOption("VectorFunc", "Vector", "Common", "VisualShaderNodeVectorFunc", TTR("Vector function."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorOp", "Vector", "Common", "VisualShaderNodeVectorOp", TTR("Vector operator."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("VectorCompose", "Vector", "Composition", "VisualShaderNodeVectorCompose", TTR("Composes vector from three scalars."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorDecompose", "Vector", "Composition", "VisualShaderNodeVectorDecompose", TTR("Decomposes vector to three scalars."))); + + add_options.push_back(AddOption("Abs", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the absolute value of the parameter."), VisualShaderNodeVectorFunc::FUNC_ABS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ACos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ACOS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ACosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ACOSH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ASin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ASIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ASinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_ASINH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the arc-tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_ATAN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATan2", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the arc-tangent of the parameters."), VisualShaderNodeVectorOp::OP_ATAN2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("ATanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the inverse hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_ATANH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Ceil", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer that is greater than or equal to the parameter."), VisualShaderNodeVectorFunc::FUNC_CEIL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Clamp", "Vector", "Functions", "VisualShaderNodeVectorClamp", TTR("Constrains a value to lie between two further values."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Cos", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_COS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("CosH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic cosine of the parameter."), VisualShaderNodeVectorFunc::FUNC_COSH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Cross", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Calculates the cross product of two vectors."), VisualShaderNodeVectorOp::OP_CROSS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Degrees", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in radians to degrees."), VisualShaderNodeVectorFunc::FUNC_DEGREES, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Distance", "Vector", "Functions", "VisualShaderNodeVectorDistance", TTR("Returns the distance between two points."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Dot", "Vector", "Functions", "VisualShaderNodeDotProduct", TTR("Calculates the dot product of two vectors."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Exp", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-e Exponential."), VisualShaderNodeVectorFunc::FUNC_EXP, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Exp2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 Exponential."), VisualShaderNodeVectorFunc::FUNC_EXP2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("FaceForward", "Vector", "Functions", "VisualShaderNodeFaceForward", TTR("Returns a vector that points in the same direction as a reference vector. The function has three vector parameters : N, the vector to orient, I, the incident vector, and Nref, the reference vector. If the dot product of I and Nref is smaller than zero the return value is N. Otherwise -N is returned."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Floor", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Finds the nearest integer less than or equal to the parameter."), VisualShaderNodeVectorFunc::FUNC_FLOOR, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Fract", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Computes the fractional part of the argument."), VisualShaderNodeVectorFunc::FUNC_FRAC, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("InverseSqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the inverse of the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_INVERSE_SQRT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Length", "Vector", "Functions", "VisualShaderNodeVectorLen", TTR("Calculates the length of a vector."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + add_options.push_back(AddOption("Log", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Natural logarithm."), VisualShaderNodeVectorFunc::FUNC_LOG, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Log2", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Base-2 logarithm."), VisualShaderNodeVectorFunc::FUNC_LOG2, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Max", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the greater of two values."), VisualShaderNodeVectorOp::OP_MAX, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Min", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the lesser of two values."), VisualShaderNodeVectorOp::OP_MIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Mix", "Vector", "Functions", "VisualShaderNodeVectorInterp", TTR("Linear interpolation between two vectors."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Negate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the opposite value of the parameter."), VisualShaderNodeVectorFunc::FUNC_NEGATE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Normalize", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Calculates the normalize product of vector."), VisualShaderNodeVectorFunc::FUNC_NORMALIZE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("OneMinus", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 - vector"), VisualShaderNodeVectorFunc::FUNC_ONEMINUS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Pow", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns the value of the first parameter raised to the power of the second."), VisualShaderNodeVectorOp::OP_POW, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Radians", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Converts a quantity in degrees to radians."), VisualShaderNodeVectorFunc::FUNC_RADIANS, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Reciprocal", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("1.0 / vector"), VisualShaderNodeVectorFunc::FUNC_RECIPROCAL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Reflect", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Returns a vector that points in the direction of reflection ( a : incident vector, b : normal vector )."), VisualShaderNodeVectorOp::OP_REFLECT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Refract", "Vector", "Functions", "VisualShaderNodeVectorRefract", TTR("Returns a vector that points in the direction of refraction."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Round", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the nearest integer to the parameter."), VisualShaderNodeVectorFunc::FUNC_ROUND, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("RoundEven", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the nearest even integer to the parameter."), VisualShaderNodeVectorFunc::FUNC_ROUNDEVEN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Saturate", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Clamps the value between 0.0 and 1.0."), VisualShaderNodeVectorFunc::FUNC_SATURATE, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sign", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Extracts the sign of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIGN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sin", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SIN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SinH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic sine of the parameter."), VisualShaderNodeVectorFunc::FUNC_SINH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Sqrt", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the square root of the parameter."), VisualShaderNodeVectorFunc::FUNC_SQRT, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SmoothStep", "Vector", "Functions", "VisualShaderNodeVectorSmoothStep", TTR("SmoothStep function( vector(edge0), vector(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("SmoothStepS", "Vector", "Functions", "VisualShaderNodeVectorScalarSmoothStep", TTR("SmoothStep function( scalar(edge0), scalar(edge1), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge0' and 1.0 if 'x' is larger than 'edge1'. Otherwise the return value is interpolated between 0.0 and 1.0 using Hermite polynomials."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Step", "Vector", "Functions", "VisualShaderNodeVectorOp", TTR("Step function( vector(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), VisualShaderNodeVectorOp::OP_STEP, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("StepS", "Vector", "Functions", "VisualShaderNodeVectorScalarStep", TTR("Step function( scalar(edge), vector(x) ).\n\nReturns 0.0 if 'x' is smaller then 'edge' and otherwise 1.0."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Tan", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("Returns the tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TAN, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("TanH", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Returns the hyperbolic tangent of the parameter."), VisualShaderNodeVectorFunc::FUNC_TANH, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Trunc", "Vector", "Functions", "VisualShaderNodeVectorFunc", TTR("(GLES3 only) Finds the truncated value of the parameter."), VisualShaderNodeVectorFunc::FUNC_TRUNC, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("Add", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Adds vector to vector."), VisualShaderNodeVectorOp::OP_ADD, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Divide", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Divides vector by vector."), VisualShaderNodeVectorOp::OP_DIV, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Multiply", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Multiplies vector by vector."), VisualShaderNodeVectorOp::OP_MUL, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Remainder", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Returns the remainder of the two vectors."), VisualShaderNodeVectorOp::OP_MOD, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("Subtract", "Vector", "Operators", "VisualShaderNodeVectorOp", TTR("Subtracts vector from vector."), VisualShaderNodeVectorOp::OP_SUB, VisualShaderNode::PORT_TYPE_VECTOR)); + + add_options.push_back(AddOption("VectorConstant", "Vector", "Variables", "VisualShaderNodeVec3Constant", TTR("Vector constant."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + add_options.push_back(AddOption("VectorUniform", "Vector", "Variables", "VisualShaderNodeVec3Uniform", TTR("Vector uniform."), -1, VisualShaderNode::PORT_TYPE_VECTOR)); + + // SPECIAL + + add_options.push_back(AddOption("Expression", "Special", "", "VisualShaderNodeExpression", TTR("Custom Godot Shader Language expression, with custom amount of input and output ports. This is a direct injection of code into the vertex/fragment/light function, do not use it to write the function declarations inside."))); + add_options.push_back(AddOption("Fresnel", "Special", "", "VisualShaderNodeFresnel", TTR("Returns falloff based on the dot product of surface normal and view direction of camera (pass associated inputs to it)."), -1, VisualShaderNode::PORT_TYPE_SCALAR)); + + add_options.push_back(AddOption("ScalarDerivativeFunc", "Special", "Common", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Scalar derivative function."), -1, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("VectorDerivativeFunc", "Special", "Common", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) Vector derivative function."), -1, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + + add_options.push_back(AddOption("DdX", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Derivative in 'x' using local differencing."), VisualShaderNodeVectorDerivativeFunc::FUNC_X, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdXS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Derivative in 'x' using local differencing."), VisualShaderNodeScalarDerivativeFunc::FUNC_X, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdY", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Derivative in 'y' using local differencing."), VisualShaderNodeVectorDerivativeFunc::FUNC_Y, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("DdYS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Derivative in 'y' using local differencing."), VisualShaderNodeScalarDerivativeFunc::FUNC_Y, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("Sum", "Special", "Derivative", "VisualShaderNodeVectorDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Vector) Sum of absolute derivative in 'x' and 'y'."), VisualShaderNodeVectorDerivativeFunc::FUNC_SUM, VisualShaderNode::PORT_TYPE_VECTOR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + add_options.push_back(AddOption("SumS", "Special", "Derivative", "VisualShaderNodeScalarDerivativeFunc", TTR("(GLES3 only) (Fragment/Light mode only) (Scalar) Sum of absolute derivative in 'x' and 'y'."), VisualShaderNodeScalarDerivativeFunc::FUNC_SUM, VisualShaderNode::PORT_TYPE_SCALAR, VisualShader::TYPE_FRAGMENT | VisualShader::TYPE_LIGHT)); + + ///////////////////////////////////////////////////////////////////// _update_options_menu(); @@ -940,9 +2371,10 @@ public: void setup(const Ref<VisualShaderNodeInput> &p_input) { input = p_input; - Ref<Texture> type_icon[3] = { + Ref<Texture> type_icon[4] = { EditorNode::get_singleton()->get_gui_base()->get_icon("float", "EditorIcons"), EditorNode::get_singleton()->get_gui_base()->get_icon("Vector3", "EditorIcons"), + EditorNode::get_singleton()->get_gui_base()->get_icon("bool", "EditorIcons"), EditorNode::get_singleton()->get_gui_base()->get_icon("Transform", "EditorIcons"), }; @@ -972,7 +2404,7 @@ public: UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); updating = true; - undo_redo->create_action("Edit Visual Property: " + prop, UndoRedo::MERGE_ENDS); + undo_redo->create_action(TTR("Edit Visual Property") + ": " + prop, UndoRedo::MERGE_ENDS); undo_redo->add_do_property(node.ptr(), prop, p_value); undo_redo->add_undo_property(node.ptr(), prop, node->get(prop)); undo_redo->commit_action(); @@ -1065,9 +2497,9 @@ Control *VisualShaderNodePluginDefault::create_editor(const Ref<VisualShaderNode if (Object::cast_to<EditorPropertyResource>(prop)) { Object::cast_to<EditorPropertyResource>(prop)->set_use_sub_inspector(false); prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); - } else if (Object::cast_to<EditorPropertyTransform>(prop)) { + } else if (Object::cast_to<EditorPropertyTransform>(prop) || Object::cast_to<EditorPropertyVector3>(prop)) { prop->set_custom_minimum_size(Size2(250 * EDSCALE, 0)); - } else if (Object::cast_to<EditorPropertyFloat>(prop) || Object::cast_to<EditorPropertyVector3>(prop)) { + } else if (Object::cast_to<EditorPropertyFloat>(prop)) { prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); } else if (Object::cast_to<EditorPropertyEnum>(prop)) { prop->set_custom_minimum_size(Size2(100 * EDSCALE, 0)); @@ -1093,7 +2525,7 @@ void EditorPropertyShaderMode::_option_selected(int p_which) { return; UndoRedo *undo_redo = EditorNode::get_singleton()->get_undo_redo(); - undo_redo->create_action("Visual Shader Mode Changed"); + undo_redo->create_action(TTR("Visual Shader Mode Changed")); //do is easy undo_redo->add_do_method(visual_shader.ptr(), "set_mode", p_which); undo_redo->add_undo_method(visual_shader.ptr(), "set_mode", visual_shader->get_mode()); @@ -1116,8 +2548,8 @@ void EditorPropertyShaderMode::_option_selected(int p_which) { VisualShader::Type type = VisualShader::Type(i); Vector<int> nodes = visual_shader->get_node_list(type); - for (int i = 0; i < nodes.size(); i++) { - Ref<VisualShaderNodeInput> input = visual_shader->get_node(type, nodes[i]); + for (int j = 0; j < nodes.size(); j++) { + Ref<VisualShaderNodeInput> input = visual_shader->get_node(type, nodes[j]); if (!input.is_valid()) { continue; } @@ -1137,6 +2569,9 @@ void EditorPropertyShaderMode::_option_selected(int p_which) { } } + undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_options_menu"); + undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_options_menu"); + //update graph undo_redo->add_do_method(VisualShaderEditor::get_singleton(), "_update_graph"); undo_redo->add_undo_method(VisualShaderEditor::get_singleton(), "_update_graph"); diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h index 2fb8a85d5b..1b009b61d5 100644 --- a/editor/plugins/visual_shader_editor_plugin.h +++ b/editor/plugins/visual_shader_editor_plugin.h @@ -60,7 +60,7 @@ class VisualShaderEditor : public VBoxContainer { Ref<VisualShader> visual_shader; GraphEdit *graph; - MenuButton *add_node; + ToolButton *add_node; OptionButton *edit_type; @@ -68,26 +68,74 @@ class VisualShaderEditor : public VBoxContainer { Label *error_label; UndoRedo *undo_redo; + Point2 saved_node_pos; + bool saved_node_pos_dirty; + + ConfirmationDialog *members_dialog; + MenuButton *tools; + + enum ToolsMenuOptions { + EXPAND_ALL, + COLLAPSE_ALL + }; + + Tree *members; + AcceptDialog *alert; + LineEdit *node_filter; + RichTextLabel *node_desc; + + void _tools_menu_option(int p_idx); + void _show_members_dialog(bool at_mouse_pos); void _update_graph(); struct AddOption { String name; String category; + String sub_category; String type; + String description; + int sub_func; + String sub_func_str; Ref<Script> script; - AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_type = String()) { + int mode; + int return_type; + int func; + float value; + + AddOption(const String &p_name = String(), const String &p_category = String(), const String &p_sub_category = String(), const String &p_type = String(), const String &p_description = String(), int p_sub_func = -1, int p_return_type = -1, int p_mode = -1, int p_func = -1, float p_value = -1) { name = p_name; type = p_type; category = p_category; + sub_category = p_sub_category; + description = p_description; + sub_func = p_sub_func; + return_type = p_return_type; + mode = p_mode; + func = p_func; + value = p_value; + } + + AddOption(const String &p_name, const String &p_category, const String &p_sub_category, const String &p_type, const String &p_description, const String &p_sub_func, int p_return_type = -1, int p_mode = -1, int p_func = -1, float p_value = -1) { + name = p_name; + type = p_type; + category = p_category; + sub_category = p_sub_category; + description = p_description; + sub_func_str = p_sub_func; + return_type = p_return_type; + mode = p_mode; + func = p_func; + value = p_value; } }; Vector<AddOption> add_options; + List<String> keyword_list; void _draw_color_over_button(Object *obj, Color p_color); - void _add_node(int p_idx); + void _add_node(int p_idx, int p_op_idx = -1); void _update_options_menu(); static VisualShaderEditor *singleton; @@ -102,6 +150,7 @@ class VisualShaderEditor : public VBoxContainer { void _node_selected(Object *p_node); void _delete_request(int); + void _on_nodes_delete(); void _removed_from_graph(); @@ -115,16 +164,47 @@ class VisualShaderEditor : public VBoxContainer { void _line_edit_changed(const String &p_text, Object *line_edit, int p_node_id); void _line_edit_focus_out(Object *line_edit, int p_node_id); + void _port_name_focus_out(Object *line_edit, int p_node_id, int p_port_id, bool p_output); + void _duplicate_nodes(); Vector<Ref<VisualShaderNodePlugin> > plugins; void _mode_selected(int p_id); + void _rebuild(); void _input_select_item(Ref<VisualShaderNodeInput> input, String name); + void _add_input_port(int p_node, int p_port, int p_type, const String &p_name); + void _remove_input_port(int p_node, int p_port); + void _change_input_port_type(int p_type, int p_node, int p_port); + void _change_input_port_name(const String &p_text, Object *line_edit, int p_node, int p_port); + + void _add_output_port(int p_node, int p_port, int p_type, const String &p_name); + void _remove_output_port(int p_node, int p_port); + void _change_output_port_type(int p_type, int p_node, int p_port); + void _change_output_port_name(const String &p_text, Object *line_edit, int p_node, int p_port); + + void _expression_focus_out(Object *text_edit, int p_node); + + void _set_node_size(int p_type, int p_node, const Size2 &p_size); + void _node_resized(const Vector2 &p_new_size, int p_type, int p_node); + void _preview_select_port(int p_node, int p_port); - void _input(const Ref<InputEvent> p_event); + void _graph_gui_input(const Ref<InputEvent> p_event); + + void _member_filter_changed(const String &p_text); + void _sbox_input(const Ref<InputEvent> &p_ie); + void _member_selected(); + void _member_unselected(); + void _member_create(); + + 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); + + bool _is_available(int p_flags); + void _update_created_node(GraphNode *node); protected: void _notification(int p_what); |